mirror of
https://github.com/YGGverse/xash3d-fwgs.git
synced 2025-01-30 00:34:56 +00:00
Add SDL sound backend
This commit is contained in:
parent
fc7ed1ab75
commit
5744aa8d9e
@ -1,483 +0,0 @@
|
||||
/*
|
||||
s_backend.c - sound hardware output
|
||||
Copyright (C) 2009 Uncle Mike
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "sound.h"
|
||||
#include <dsound.h>
|
||||
|
||||
#define iDirectSoundCreate( a, b, c ) pDirectSoundCreate( a, b, c )
|
||||
|
||||
static HRESULT ( _stdcall *pDirectSoundCreate)(GUID* lpGUID, LPDIRECTSOUND* lpDS, IUnknown* pUnkOuter );
|
||||
|
||||
static dllfunc_t dsound_funcs[] =
|
||||
{
|
||||
{ "DirectSoundCreate", (void **) &pDirectSoundCreate },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
dll_info_t dsound_dll = { "dsound.dll", dsound_funcs, false };
|
||||
|
||||
#define SAMPLE_16BIT_SHIFT 1
|
||||
#define SECONDARY_BUFFER_SIZE 0x10000
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SIS_SUCCESS,
|
||||
SIS_FAILURE,
|
||||
SIS_NOTAVAIL
|
||||
} si_state_t;
|
||||
|
||||
static qboolean snd_firsttime = true;
|
||||
static qboolean primary_format_set;
|
||||
static HWND snd_hwnd;
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
Global variables. Must be visible to window-procedure function
|
||||
so it can unlock and free the data block after it has been played.
|
||||
=======================================================================
|
||||
*/
|
||||
static DWORD locksize;
|
||||
static HPSTR lpData, lpData2;
|
||||
static DWORD gSndBufSize;
|
||||
static MMTIME mmstarttime;
|
||||
static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf;
|
||||
static LPDIRECTSOUND pDS;
|
||||
|
||||
qboolean SNDDMA_InitDirect( void *hInst );
|
||||
void SNDDMA_FreeSound( void );
|
||||
|
||||
static const char *DSoundError( int error )
|
||||
{
|
||||
switch( error )
|
||||
{
|
||||
case DSERR_BUFFERLOST:
|
||||
return "buffer is lost";
|
||||
case DSERR_INVALIDCALL:
|
||||
return "invalid call";
|
||||
case DSERR_INVALIDPARAM:
|
||||
return "invalid param";
|
||||
case DSERR_PRIOLEVELNEEDED:
|
||||
return "invalid priority level";
|
||||
}
|
||||
|
||||
return "unknown error";
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
DS_CreateBuffers
|
||||
==================
|
||||
*/
|
||||
static qboolean DS_CreateBuffers( void *hInst )
|
||||
{
|
||||
WAVEFORMATEX pformat, format;
|
||||
DSBCAPS dsbcaps;
|
||||
DSBUFFERDESC dsbuf;
|
||||
|
||||
memset( &format, 0, sizeof( format ));
|
||||
format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
format.nChannels = 2;
|
||||
format.wBitsPerSample = 16;
|
||||
format.nSamplesPerSec = SOUND_DMA_SPEED;
|
||||
format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
|
||||
format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
|
||||
format.cbSize = 0;
|
||||
|
||||
if( pDS->lpVtbl->SetCooperativeLevel( pDS, hInst, DSSCL_EXCLUSIVE ) != DS_OK )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to set EXCLUSIVE coop level\n" );
|
||||
SNDDMA_FreeSound();
|
||||
return false;
|
||||
}
|
||||
|
||||
// get access to the primary buffer, if possible, so we can set the sound hardware format
|
||||
memset( &dsbuf, 0, sizeof( dsbuf ));
|
||||
dsbuf.dwSize = sizeof( DSBUFFERDESC );
|
||||
dsbuf.dwFlags = DSBCAPS_PRIMARYBUFFER;
|
||||
dsbuf.dwBufferBytes = 0;
|
||||
dsbuf.lpwfxFormat = NULL;
|
||||
|
||||
memset( &dsbcaps, 0, sizeof( dsbcaps ));
|
||||
dsbcaps.dwSize = sizeof( dsbcaps );
|
||||
primary_format_set = false;
|
||||
|
||||
if( pDS->lpVtbl->CreateSoundBuffer( pDS, &dsbuf, &pDSPBuf, NULL ) == DS_OK )
|
||||
{
|
||||
pformat = format;
|
||||
|
||||
if( pDSPBuf->lpVtbl->SetFormat( pDSPBuf, &pformat ) != DS_OK )
|
||||
{
|
||||
if( snd_firsttime )
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to set primary sound format\n" );
|
||||
}
|
||||
else
|
||||
{
|
||||
primary_format_set = true;
|
||||
}
|
||||
}
|
||||
|
||||
// create the secondary buffer we'll actually work with
|
||||
memset( &dsbuf, 0, sizeof( dsbuf ));
|
||||
dsbuf.dwSize = sizeof( DSBUFFERDESC );
|
||||
dsbuf.dwFlags = (DSBCAPS_CTRLFREQUENCY|DSBCAPS_LOCSOFTWARE);
|
||||
dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE;
|
||||
dsbuf.lpwfxFormat = &format;
|
||||
|
||||
memset( &dsbcaps, 0, sizeof( dsbcaps ));
|
||||
dsbcaps.dwSize = sizeof( dsbcaps );
|
||||
|
||||
if( pDS->lpVtbl->CreateSoundBuffer( pDS, &dsbuf, &pDSBuf, NULL ) != DS_OK )
|
||||
{
|
||||
// couldn't get hardware, fallback to software.
|
||||
dsbuf.dwFlags = (DSBCAPS_LOCSOFTWARE|DSBCAPS_GETCURRENTPOSITION2);
|
||||
|
||||
if( pDS->lpVtbl->CreateSoundBuffer( pDS, &dsbuf, &pDSBuf, NULL ) != DS_OK )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to create secondary buffer\n" );
|
||||
SNDDMA_FreeSound ();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if( pDSBuf->lpVtbl->GetCaps( pDSBuf, &dsbcaps ) != DS_OK )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to get capabilities\n" );
|
||||
SNDDMA_FreeSound ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure mixer is active
|
||||
if( pDSBuf->lpVtbl->Play( pDSBuf, 0, 0, DSBPLAY_LOOPING ) != DS_OK )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to create circular buffer\n" );
|
||||
SNDDMA_FreeSound ();
|
||||
return false;
|
||||
}
|
||||
|
||||
// we don't want anyone to access the buffer directly w/o locking it first
|
||||
lpData = NULL;
|
||||
dma.samplepos = 0;
|
||||
snd_hwnd = (HWND)hInst;
|
||||
gSndBufSize = dsbcaps.dwBufferBytes;
|
||||
dma.samples = gSndBufSize / 2;
|
||||
dma.buffer = (byte *)lpData;
|
||||
|
||||
SNDDMA_BeginPainting();
|
||||
if( dma.buffer ) memset( dma.buffer, 0, dma.samples * 2 );
|
||||
SNDDMA_Submit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
DS_DestroyBuffers
|
||||
==================
|
||||
*/
|
||||
static void DS_DestroyBuffers( void )
|
||||
{
|
||||
if( pDS ) pDS->lpVtbl->SetCooperativeLevel( pDS, snd_hwnd, DSSCL_NORMAL );
|
||||
|
||||
if( pDSBuf )
|
||||
{
|
||||
pDSBuf->lpVtbl->Stop( pDSBuf );
|
||||
pDSBuf->lpVtbl->Release( pDSBuf );
|
||||
}
|
||||
|
||||
// only release primary buffer if it's not also the mixing buffer we just released
|
||||
if( pDSPBuf && ( pDSBuf != pDSPBuf ))
|
||||
pDSPBuf->lpVtbl->Release( pDSPBuf );
|
||||
|
||||
dma.buffer = NULL;
|
||||
pDSPBuf = NULL;
|
||||
pDSBuf = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_LockSound
|
||||
==================
|
||||
*/
|
||||
void SNDDMA_LockSound( void )
|
||||
{
|
||||
if( pDSBuf != NULL )
|
||||
pDSBuf->lpVtbl->Stop( pDSBuf );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_LockSound
|
||||
==================
|
||||
*/
|
||||
void SNDDMA_UnlockSound( void )
|
||||
{
|
||||
if( pDSBuf != NULL )
|
||||
pDSBuf->lpVtbl->Play( pDSBuf, 0, 0, DSBPLAY_LOOPING );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_FreeSound
|
||||
==================
|
||||
*/
|
||||
void SNDDMA_FreeSound( void )
|
||||
{
|
||||
if( pDS )
|
||||
{
|
||||
DS_DestroyBuffers();
|
||||
pDS->lpVtbl->Release( pDS );
|
||||
Sys_FreeLibrary( &dsound_dll );
|
||||
}
|
||||
|
||||
lpData = NULL;
|
||||
pDSPBuf = NULL;
|
||||
pDSBuf = NULL;
|
||||
pDS = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_InitDirect
|
||||
|
||||
Direct-Sound support
|
||||
==================
|
||||
*/
|
||||
si_state_t SNDDMA_InitDirect( void *hInst )
|
||||
{
|
||||
DSCAPS dscaps;
|
||||
HRESULT hresult;
|
||||
|
||||
if( !dsound_dll.link )
|
||||
{
|
||||
if( !Sys_LoadLibrary( &dsound_dll ))
|
||||
return SIS_FAILURE;
|
||||
}
|
||||
|
||||
if(( hresult = iDirectSoundCreate( NULL, &pDS, NULL )) != DS_OK )
|
||||
{
|
||||
if( hresult != DSERR_ALLOCATED )
|
||||
return SIS_FAILURE;
|
||||
|
||||
Con_DPrintf( S_ERROR "DirectSound: hardware already in use\n" );
|
||||
return SIS_NOTAVAIL;
|
||||
}
|
||||
|
||||
dscaps.dwSize = sizeof( dscaps );
|
||||
|
||||
if( pDS->lpVtbl->GetCaps( pDS, &dscaps ) != DS_OK )
|
||||
Con_DPrintf( S_ERROR "DirectSound: failed to get capabilities\n" );
|
||||
|
||||
if( FBitSet( dscaps.dwFlags, DSCAPS_EMULDRIVER ))
|
||||
{
|
||||
Con_DPrintf( S_ERROR "DirectSound: driver not installed\n" );
|
||||
SNDDMA_FreeSound();
|
||||
return SIS_FAILURE;
|
||||
}
|
||||
|
||||
if( !DS_CreateBuffers( hInst ))
|
||||
return SIS_FAILURE;
|
||||
|
||||
return SIS_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_Init
|
||||
|
||||
Try to find a sound device to mix for.
|
||||
Returns false if nothing is found.
|
||||
==================
|
||||
*/
|
||||
int SNDDMA_Init( void *hInst )
|
||||
{
|
||||
// already initialized
|
||||
if( dma.initialized ) return true;
|
||||
|
||||
memset( &dma, 0, sizeof( dma ));
|
||||
|
||||
// init DirectSound
|
||||
if( SNDDMA_InitDirect( hInst ) != SIS_SUCCESS )
|
||||
return false;
|
||||
|
||||
if( snd_firsttime )
|
||||
Con_Printf( "Audio: DirectSound\n" );
|
||||
dma.initialized = true;
|
||||
snd_firsttime = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetDMAPos
|
||||
|
||||
return the current sample position (in mono samples read)
|
||||
inside the recirculating dma buffer, so the mixing code will know
|
||||
how many sample are required to fill it up.
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetDMAPos( void )
|
||||
{
|
||||
int s;
|
||||
MMTIME mmtime;
|
||||
DWORD dwWrite;
|
||||
|
||||
if( !dma.initialized )
|
||||
return 0;
|
||||
|
||||
mmtime.wType = TIME_SAMPLES;
|
||||
pDSBuf->lpVtbl->GetCurrentPosition( pDSBuf, &mmtime.u.sample, &dwWrite );
|
||||
s = mmtime.u.sample - mmstarttime.u.sample;
|
||||
|
||||
s >>= SAMPLE_16BIT_SHIFT;
|
||||
s &= (dma.samples - 1);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetSoundtime
|
||||
|
||||
update global soundtime
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetSoundtime( void )
|
||||
{
|
||||
static int buffers, oldsamplepos;
|
||||
int samplepos, fullsamples;
|
||||
|
||||
fullsamples = dma.samples / 2;
|
||||
|
||||
// it is possible to miscount buffers
|
||||
// if it has wrapped twice between
|
||||
// calls to S_Update. Oh well.
|
||||
samplepos = SNDDMA_GetDMAPos();
|
||||
|
||||
if( samplepos < oldsamplepos )
|
||||
{
|
||||
buffers++; // buffer wrapped
|
||||
|
||||
if( paintedtime > 0x40000000 )
|
||||
{
|
||||
// time to chop things off to avoid 32 bit limits
|
||||
buffers = 0;
|
||||
paintedtime = fullsamples;
|
||||
S_StopAllSounds( true );
|
||||
}
|
||||
}
|
||||
|
||||
oldsamplepos = samplepos;
|
||||
|
||||
return (buffers * fullsamples + samplepos / 2);
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_BeginPainting
|
||||
|
||||
Makes sure dma.buffer is valid
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_BeginPainting( void )
|
||||
{
|
||||
int reps;
|
||||
DWORD dwSize2;
|
||||
DWORD *pbuf, *pbuf2;
|
||||
HRESULT hr;
|
||||
DWORD dwStatus;
|
||||
|
||||
if( !pDSBuf ) return;
|
||||
|
||||
// if the buffer was lost or stopped, restore it and/or restart it
|
||||
if( pDSBuf->lpVtbl->GetStatus( pDSBuf, &dwStatus ) != DS_OK )
|
||||
Con_DPrintf( S_ERROR "BeginPainting: couldn't get sound buffer status\n" );
|
||||
|
||||
if( dwStatus & DSBSTATUS_BUFFERLOST )
|
||||
pDSBuf->lpVtbl->Restore( pDSBuf );
|
||||
|
||||
if( !FBitSet( dwStatus, DSBSTATUS_PLAYING ))
|
||||
pDSBuf->lpVtbl->Play( pDSBuf, 0, 0, DSBPLAY_LOOPING );
|
||||
|
||||
// lock the dsound buffer
|
||||
dma.buffer = NULL;
|
||||
reps = 0;
|
||||
|
||||
while(( hr = pDSBuf->lpVtbl->Lock( pDSBuf, 0, gSndBufSize, &pbuf, &locksize, &pbuf2, &dwSize2, 0 )) != DS_OK )
|
||||
{
|
||||
if( hr != DSERR_BUFFERLOST )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "BeginPainting: %s\n", DSoundError( hr ));
|
||||
S_Shutdown ();
|
||||
return;
|
||||
}
|
||||
else pDSBuf->lpVtbl->Restore( pDSBuf );
|
||||
if( ++reps > 2 ) return;
|
||||
}
|
||||
|
||||
dma.buffer = (byte *)pbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Submit
|
||||
|
||||
Send sound to device if buffer isn't really the dma buffer
|
||||
Also unlocks the dsound buffer
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Submit( void )
|
||||
{
|
||||
if( !dma.buffer ) return;
|
||||
// unlock the dsound buffer
|
||||
if( pDSBuf ) pDSBuf->lpVtbl->Unlock( pDSBuf, dma.buffer, locksize, NULL, 0 );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Shutdown
|
||||
|
||||
Reset the sound device for exiting
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Shutdown( void )
|
||||
{
|
||||
if( !dma.initialized ) return;
|
||||
dma.initialized = false;
|
||||
SNDDMA_FreeSound();
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
S_Activate
|
||||
|
||||
Called when the main window gains or loses focus.
|
||||
The window have been destroyed and recreated
|
||||
between a deactivate and an activate.
|
||||
===========
|
||||
*/
|
||||
void S_Activate( qboolean active, void *hInst )
|
||||
{
|
||||
if( !dma.initialized ) return;
|
||||
snd_hwnd = (HWND)hInst;
|
||||
|
||||
if( !pDS || !snd_hwnd )
|
||||
return;
|
||||
|
||||
if( active )
|
||||
DS_CreateBuffers( snd_hwnd );
|
||||
else DS_DestroyBuffers();
|
||||
}
|
@ -50,11 +50,13 @@ convar_t *snd_foliage_db_loss;
|
||||
convar_t *snd_gain;
|
||||
convar_t *snd_gain_max;
|
||||
convar_t *snd_gain_min;
|
||||
convar_t *snd_mute_losefocus;
|
||||
convar_t *s_refdist;
|
||||
convar_t *s_refdb;
|
||||
convar_t *s_cull; // cull sounds by geometry
|
||||
convar_t *s_test; // cvar for testing new effects
|
||||
convar_t *s_phs;
|
||||
convar_t *s_samplecount;
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
@ -2172,12 +2174,14 @@ qboolean S_Init( void )
|
||||
snd_foliage_db_loss = Cvar_Get( "snd_foliage_db_loss", "4", 0, "foliage loss factor" );
|
||||
snd_gain_max = Cvar_Get( "snd_gain_max", "1", 0, "gain maximal threshold" );
|
||||
snd_gain_min = Cvar_Get( "snd_gain_min", "0.01", 0, "gain minimal threshold" );
|
||||
snd_mute_losefocus = Cvar_Get( "snd_mute_losefocus", "1", FCVAR_ARCHIVE, "silence the audio when game window loses focus" );
|
||||
s_refdist = Cvar_Get( "s_refdist", "36", 0, "soundlevel reference distance" );
|
||||
s_refdb = Cvar_Get( "s_refdb", "60", 0, "soundlevel refernce dB" );
|
||||
snd_gain = Cvar_Get( "snd_gain", "1", 0, "sound default gain" );
|
||||
s_cull = Cvar_Get( "s_cull", "0", FCVAR_ARCHIVE, "cull sounds by geometry" );
|
||||
s_test = Cvar_Get( "s_test", "0", 0, "engine developer cvar for quick testing new features" );
|
||||
s_phs = Cvar_Get( "s_phs", "0", FCVAR_ARCHIVE, "cull sounds by PHS" );
|
||||
s_samplecount = Cvar_Get( "s_samplecount", "0", FCVAR_ARCHIVE, "sample count (0 for default value)" );
|
||||
|
||||
Cmd_AddCommand( "play", S_Play_f, "playing a specified sound file" );
|
||||
Cmd_AddCommand( "playvol", S_PlayVol_f, "playing a specified sound file with specified volume" );
|
||||
@ -2241,4 +2245,4 @@ void S_Shutdown( void )
|
||||
SNDDMA_Shutdown ();
|
||||
MIX_FreeAllPaintbuffers ();
|
||||
Mem_FreePool( &sndpool );
|
||||
}
|
||||
}
|
||||
|
@ -127,8 +127,16 @@ typedef struct
|
||||
float percent;
|
||||
} musicfade_t;
|
||||
|
||||
typedef struct snd_format_s
|
||||
{
|
||||
unsigned int speed;
|
||||
unsigned int width;
|
||||
unsigned int channels;
|
||||
} snd_format_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
snd_format_t format;
|
||||
int samples; // mono samples in buffer
|
||||
int samplepos; // in mono samples
|
||||
byte *buffer;
|
||||
@ -266,6 +274,7 @@ extern convar_t *s_mixahead;
|
||||
extern convar_t *s_lerping;
|
||||
extern convar_t *dsp_off;
|
||||
extern convar_t *s_test; // cvar to testify new effects
|
||||
extern convar_t *s_samplecount;
|
||||
|
||||
void S_InitScaletable( void );
|
||||
wavdata_t *S_LoadSound( sfx_t *sfx );
|
||||
@ -303,7 +312,7 @@ void DSP_ClearState( void );
|
||||
|
||||
qboolean S_Init( void );
|
||||
void S_Shutdown( void );
|
||||
void S_Activate( qboolean active, void *hInst );
|
||||
void S_Activate( qboolean active );
|
||||
void S_SoundList_f( void );
|
||||
void S_SoundInfo_f( void );
|
||||
|
||||
@ -356,4 +365,4 @@ void VOX_LoadSound( channel_t *pchan, const char *psz );
|
||||
float VOX_ModifyPitch( channel_t *ch, float pitch );
|
||||
int VOX_MixDataToDevice( channel_t *pChannel, int sampleCount, int outputRate, int outputOffset );
|
||||
|
||||
#endif//SOUND_H
|
||||
#endif//SOUND_H
|
||||
|
261
engine/platform/sdl/s_backend.c
Normal file
261
engine/platform/sdl/s_backend.c
Normal file
@ -0,0 +1,261 @@
|
||||
/*
|
||||
s_backend.c - sound hardware output
|
||||
Copyright (C) 2009 Uncle Mike
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#if XASH_SOUND == SOUND_SDL
|
||||
|
||||
#include "sound.h"
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#define SAMPLE_16BIT_SHIFT 1
|
||||
#define SECONDARY_BUFFER_SIZE 0x10000
|
||||
|
||||
/*
|
||||
=======================================================================
|
||||
Global variables. Must be visible to window-procedure function
|
||||
so it can unlock and free the data block after it has been played.
|
||||
=======================================================================
|
||||
*/
|
||||
static int sdl_dev;
|
||||
|
||||
//static qboolean snd_firsttime = true;
|
||||
//static qboolean primary_format_set;
|
||||
|
||||
void SDL_SoundCallback( void *userdata, Uint8 *stream, int len )
|
||||
{
|
||||
int size = dma.samples << 1;
|
||||
int pos = dma.samplepos << 1;
|
||||
int wrapped = pos + len - size;
|
||||
|
||||
if( wrapped < 0 )
|
||||
{
|
||||
memcpy( stream, dma.buffer + pos, len );
|
||||
dma.samplepos += len >> 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int remaining = size - pos;
|
||||
memcpy( stream, dma.buffer + pos, remaining );
|
||||
memcpy( stream + remaining, dma.buffer, wrapped );
|
||||
dma.samplepos = wrapped >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SNDDMA_Init
|
||||
|
||||
Try to find a sound device to mix for.
|
||||
Returns false if nothing is found.
|
||||
==================
|
||||
*/
|
||||
qboolean SNDDMA_Init( void *hInst )
|
||||
{
|
||||
SDL_AudioSpec desired, obtained;
|
||||
int samplecount;
|
||||
|
||||
if( SDL_Init( SDL_INIT_AUDIO ) )
|
||||
{
|
||||
MsgDev( D_ERROR, "Audio: SDL: %s \n", SDL_GetError( ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
setenv( "PULSE_PROP_application.name", GI->title, 1 );
|
||||
setenv( "PULSE_PROP_media.role", "game", 1 );
|
||||
#endif
|
||||
|
||||
memset( &desired, 0, sizeof( desired ) );
|
||||
desired.freq = SOUND_DMA_SPEED;
|
||||
desired.format = AUDIO_S16LSB;
|
||||
desired.samples = 1024;
|
||||
desired.channels = 2;
|
||||
desired.callback = SDL_SoundCallback;
|
||||
|
||||
sdl_dev = SDL_OpenAudioDevice( NULL, 0, &desired, &obtained, 0 );
|
||||
|
||||
if( !sdl_dev )
|
||||
{
|
||||
Con_Printf( "Couldn't open SDL audio: %s\n", SDL_GetError( ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( obtained.format != AUDIO_S16LSB )
|
||||
{
|
||||
Con_Printf( "SDL audio format %d unsupported.\n", obtained.format );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if( obtained.channels != 1 && obtained.channels != 2 )
|
||||
{
|
||||
Con_Printf( "SDL audio channels %d unsupported.\n", obtained.channels );
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dma.format.speed = obtained.freq;
|
||||
dma.format.channels = obtained.channels;
|
||||
dma.format.width = 2;
|
||||
samplecount = s_samplecount->value;
|
||||
if( !samplecount )
|
||||
samplecount = 0x8000;
|
||||
dma.samples = samplecount * obtained.channels;
|
||||
dma.buffer = Z_Malloc( dma.samples * 2 );
|
||||
dma.samplepos = 0;
|
||||
|
||||
Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq );
|
||||
|
||||
SDL_PauseAudioDevice( sdl_dev, 0 );
|
||||
|
||||
dma.initialized = true;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
SNDDMA_Shutdown( );
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetDMAPos
|
||||
|
||||
return the current sample position (in mono samples read)
|
||||
inside the recirculating dma buffer, so the mixing code will know
|
||||
how many sample are required to fill it up.
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetDMAPos( void )
|
||||
{
|
||||
return dma.samplepos;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_GetSoundtime
|
||||
|
||||
update global soundtime
|
||||
===============
|
||||
*/
|
||||
int SNDDMA_GetSoundtime( void )
|
||||
{
|
||||
static int buffers, oldsamplepos;
|
||||
int samplepos, fullsamples;
|
||||
|
||||
fullsamples = dma.samples / 2;
|
||||
|
||||
// it is possible to miscount buffers
|
||||
// if it has wrapped twice between
|
||||
// calls to S_Update. Oh well.
|
||||
samplepos = SNDDMA_GetDMAPos( );
|
||||
|
||||
if( samplepos < oldsamplepos )
|
||||
{
|
||||
buffers++; // buffer wrapped
|
||||
|
||||
if( paintedtime > 0x40000000 )
|
||||
{
|
||||
// time to chop things off to avoid 32 bit limits
|
||||
buffers = 0;
|
||||
paintedtime = fullsamples;
|
||||
S_StopAllSounds( true );
|
||||
}
|
||||
}
|
||||
|
||||
oldsamplepos = samplepos;
|
||||
|
||||
return ( buffers * fullsamples + samplepos / 2 );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_BeginPainting
|
||||
|
||||
Makes sure dma.buffer is valid
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_BeginPainting( void )
|
||||
{
|
||||
SDL_LockAudio( );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Submit
|
||||
|
||||
Send sound to device if buffer isn't really the dma buffer
|
||||
Also unlocks the dsound buffer
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Submit( void )
|
||||
{
|
||||
SDL_UnlockAudio( );
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
SNDDMA_Shutdown
|
||||
|
||||
Reset the sound device for exiting
|
||||
===============
|
||||
*/
|
||||
void SNDDMA_Shutdown( void )
|
||||
{
|
||||
Con_Printf( "Shutting down audio.\n" );
|
||||
dma.initialized = false;
|
||||
|
||||
if( sdl_dev )
|
||||
{
|
||||
SDL_PauseAudioDevice( sdl_dev, 1 );
|
||||
#ifndef __EMSCRIPTEN__
|
||||
SDL_CloseAudioDevice( sdl_dev );
|
||||
SDL_CloseAudio( );
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
if( SDL_WasInit( SDL_INIT_AUDIO ) )
|
||||
SDL_QuitSubSystem( SDL_INIT_AUDIO );
|
||||
#endif
|
||||
|
||||
if( dma.buffer )
|
||||
{
|
||||
Mem_Free( dma.buffer );
|
||||
dma.buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
S_PrintDeviceName
|
||||
===========
|
||||
*/
|
||||
void S_PrintDeviceName( void )
|
||||
{
|
||||
Msg( "Audio: SDL (driver: %s)\n", SDL_GetCurrentAudioDriver( ) );
|
||||
}
|
||||
|
||||
/*
|
||||
===========
|
||||
S_Activate
|
||||
Called when the main window gains or loses focus.
|
||||
The window have been destroyed and recreated
|
||||
between a deactivate and an activate.
|
||||
===========
|
||||
*/
|
||||
void S_Activate( qboolean active )
|
||||
{
|
||||
SDL_PauseAudioDevice( sdl_dev, !active );
|
||||
}
|
||||
#endif // XASH_SOUND == SOUND_SDL
|
Loading…
x
Reference in New Issue
Block a user