diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/snd_dx.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/snd_dx.c')
-rw-r--r-- | source/snd_dx.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/source/snd_dx.c b/source/snd_dx.c new file mode 100644 index 0000000..7fdbbbf --- /dev/null +++ b/source/snd_dx.c @@ -0,0 +1,429 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +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 2 +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#if USE_DSOUND + +#include "win_local.h" +#include <mmsystem.h> +#include <dsound.h> +#include "snd_local.h" + +typedef HRESULT (WINAPI *LPDIRECTSOUNDCREATE)( LPCGUID, LPDIRECTSOUND *, LPUNKNOWN ); + +// 64K is > 1 second at 16-bit, 22050 Hz +#define WAV_BUFFERS 64 +#define WAV_MASK 0x3F +#define WAV_BUFFER_SIZE 0x0400 +#define SECONDARY_BUFFER_SIZE 0x10000 + +// starts at 0 for disabled +static int sample16; + +static DWORD locksize; + +static MMTIME mmstarttime; + +static LPDIRECTSOUND pDS; +static LPDIRECTSOUNDBUFFER pDSBuf, pDSPBuf; + +static HINSTANCE hInstDS; + +static DWORD gSndBufSize; + +static const char *DSoundError( int error ) { + switch ( error ) { + case DSERR_BUFFERLOST: + return "DSERR_BUFFERLOST"; + case DSERR_INVALIDCALL: + return "DSERR_INVALIDCALL"; + case DSERR_INVALIDPARAM: + return "DSERR_INVALIDPARAM"; + case DSERR_PRIOLEVELNEEDED: + return "DSERR_PRIOLEVELNEEDED"; + } + + return "<unknown error>"; +} + +/* +** DS_DestroyBuffers +*/ +static void DS_DestroyBuffers( void ) { + Com_DPrintf( "Destroying DS buffers\n" ); + if( pDS ) { + Com_DPrintf( "...setting NORMAL coop level\n" ); + IDirectSound_SetCooperativeLevel( pDS, win.wnd, DSSCL_NORMAL ); + } + + if( pDSBuf ) { + Com_DPrintf( "...stopping and releasing sound buffer\n" ); + IDirectSoundBuffer_Stop( pDSBuf ); + IDirectSoundBuffer_Release( pDSBuf ); + } + + // only release primary buffer if it's not also the mixing buffer we just released + if( pDSPBuf && ( pDSBuf != pDSPBuf ) ) { + Com_DPrintf( "...releasing primary buffer\n" ); + IDirectSoundBuffer_Release( pDSPBuf ); + } + pDSBuf = NULL; + pDSPBuf = NULL; + + dma.buffer = NULL; +} + +/* +============== +DS_Shutdown + +Reset the sound device for exiting +=============== +*/ +static void DS_Shutdown(void) { + Com_Printf( "Shutting down DirectSound\n" ); + + if( pDS ) { + DS_DestroyBuffers(); + + Com_DPrintf( "...releasing DS object\n" ); + IDirectSound_Release( pDS ); + } + + if ( hInstDS ) { + Com_DPrintf( "...freeing DSOUND.DLL\n" ); + FreeLibrary( hInstDS ); + hInstDS = NULL; + } + + pDS = NULL; + pDSBuf = NULL; + pDSPBuf = NULL; +} + +/* +** DS_CreateBuffers +*/ +static qboolean DS_CreateBuffers( void ) { + DSBUFFERDESC dsbuf; + DSBCAPS dsbcaps; + WAVEFORMATEX format; + DWORD dwWrite; + + memset (&format, 0, sizeof(format)); + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = dma.channels; + format.wBitsPerSample = dma.samplebits; + format.nSamplesPerSec = dma.speed; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.cbSize = sizeof( format ); + format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign; + + Com_DPrintf( "Creating DS buffer\n" ); + + Com_DPrintf("...setting PRIORITY coop level: " ); + if ( DS_OK != IDirectSound_SetCooperativeLevel( pDS, win.wnd, DSSCL_PRIORITY ) ) { + Com_DPrintf ("failed\n"); + return qfalse; + } + Com_DPrintf("ok\n" ); + +// create the secondary buffer we'll actually work with + memset (&dsbuf, 0, sizeof(dsbuf)); + dsbuf.dwSize = sizeof(DSBUFFERDESC); + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCHARDWARE; + dsbuf.dwBufferBytes = SECONDARY_BUFFER_SIZE; + dsbuf.lpwfxFormat = &format; + + memset(&dsbcaps, 0, sizeof(dsbcaps)); + dsbcaps.dwSize = sizeof(dsbcaps); + + Com_DPrintf( "...creating secondary buffer: " ); + if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) { + dsbuf.dwFlags = DSBCAPS_CTRLFREQUENCY | DSBCAPS_LOCSOFTWARE; + if (DS_OK != IDirectSound_CreateSoundBuffer(pDS, &dsbuf, &pDSBuf, NULL)) { + Com_DPrintf( "failed\n" ); + return qfalse; + } + + Com_DPrintf( "ok\n...forced to software\n" ); + } else { + Com_DPrintf( "ok\n...locked hardware\n" ); + } + + dma.channels = format.nChannels; + dma.samplebits = format.wBitsPerSample; + dma.speed = format.nSamplesPerSec; + + if (DS_OK != IDirectSoundBuffer_GetCaps (pDSBuf, &dsbcaps)) { + Com_DPrintf ("*** GetCaps failed ***\n"); + return qfalse; + } + + // Make sure mixer is active + if (DS_OK != IDirectSoundBuffer_Play (pDSBuf, 0, 0, DSBPLAY_LOOPING)) { + Com_DPrintf ("*** Play failed ***\n"); + return qfalse; + } + + Com_DPrintf( " %d channel(s)\n" + " %d bits/sample\n" + " %d bytes/sec\n", + dma.channels, dma.samplebits, dma.speed); + + gSndBufSize = dsbcaps.dwBufferBytes; + + IDirectSoundBuffer_Stop(pDSBuf); + IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmstarttime.u.sample, &dwWrite); + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + dma.samples = gSndBufSize/(dma.samplebits/8); + dma.samplepos = 0; + dma.submission_chunk = 1; + dma.buffer = NULL; + sample16 = (dma.samplebits/8) - 1; + + return qtrue; +} + + + +/* +================== +DS_Init + +Direct-Sound support +================== +*/ +static sndinitstat DS_Init (void) { + DSCAPS dscaps; + HRESULT hresult; + LPDIRECTSOUNDCREATE pDirectSoundCreate; + + memset (&dma, 0, sizeof (dma)); + dma.channels = 2; + dma.samplebits = 16; + + switch( s_khz->integer ) { + case 48: + dma.speed = 48000; + break; + case 44: + dma.speed = 44100; + break; + case 22: + dma.speed = 22050; + break; + default: + dma.speed = 11025; + break; + } + + Com_Printf( "Initializing DirectSound\n"); + + if ( !hInstDS ) { + Com_DPrintf( "...loading dsound.dll: " ); + + hInstDS = LoadLibrary("dsound.dll"); + if (hInstDS == NULL) { + Com_DPrintf ("failed\n"); + return SIS_FAILURE; + } + Com_DPrintf ("ok\n"); + } + + pDirectSoundCreate = ( LPDIRECTSOUNDCREATE ) + GetProcAddress(hInstDS,"DirectSoundCreate"); + if (!pDirectSoundCreate) { + Com_DPrintf ("...couldn't get DS proc addr\n"); + return SIS_FAILURE; + } + + Com_DPrintf( "...creating DS object: " ); + while ( ( hresult = pDirectSoundCreate( NULL, &pDS, NULL ) ) != DS_OK ) { + if (hresult != DSERR_ALLOCATED) { + Com_DPrintf( "failed\n" ); + return SIS_FAILURE; + } + + if (MessageBox (NULL, + "The sound hardware is in use by another app.\n\n" + "Select Retry to try to start sound again or Cancel to run " APPLICATION " with no sound.", + "Sound not available", + MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY) + { + Com_DPrintf ("failed, hardware already in use\n" ); + return SIS_NOTAVAIL; + } + } + Com_DPrintf( "ok\n" ); + + dscaps.dwSize = sizeof(dscaps); + + if( DS_OK != IDirectSound_GetCaps( pDS, &dscaps ) ) { + Com_DPrintf ("...couldn't get DS caps\n"); + DS_Shutdown(); + return SIS_FAILURE; + } + + if( dscaps.dwFlags & DSCAPS_EMULDRIVER ) { + Com_DPrintf ("...no DSound driver found\n" ); + DS_Shutdown(); + return SIS_FAILURE; + } + + if( !DS_CreateBuffers() ) { + DS_Shutdown (); + return SIS_FAILURE; + } + + Com_DPrintf("...completed successfully\n" ); + + return SIS_SUCCESS; +} + +/* +============== +DS_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. +=============== +*/ +static int DS_GetDMAPos(void) { + MMTIME mmtime; + int s; + DWORD dwWrite; + + if (!pDSBuf) { + return 0; + } + + mmtime.wType = TIME_SAMPLES; + IDirectSoundBuffer_GetCurrentPosition(pDSBuf, &mmtime.u.sample, &dwWrite); + s = mmtime.u.sample - mmstarttime.u.sample; + + s >>= sample16; + s &= (dma.samples-1); + + return s; +} + +/* +============== +DS_BeginPainting + +Makes sure dma.buffer is valid +=============== +*/ +static void DS_BeginPainting (void) { + int reps; + DWORD dwSize2; + DWORD *pbuf, *pbuf2; + HRESULT hresult; + DWORD dwStatus; + + if (!pDSBuf) + return; + + // if the buffer was lost or stopped, restore it and/or restart it + if (IDirectSoundBuffer_GetStatus (pDSBuf, &dwStatus) != DS_OK) { + Com_EPrintf ("DS_BeginPainting: Couldn't get sound buffer status\n"); + DS_Shutdown (); + return; + } + + if (dwStatus & DSBSTATUS_BUFFERLOST) + IDirectSoundBuffer_Restore (pDSBuf); + + if (!(dwStatus & DSBSTATUS_PLAYING)) + IDirectSoundBuffer_Play(pDSBuf, 0, 0, DSBPLAY_LOOPING); + + // lock the dsound buffer + + reps = 0; + dma.buffer = NULL; + + while ((hresult = IDirectSoundBuffer_Lock(pDSBuf, 0, gSndBufSize, ( void ** )&pbuf, &locksize, + ( void ** )&pbuf2, &dwSize2, 0)) != DS_OK) + { + if (hresult != DSERR_BUFFERLOST) { + Com_EPrintf( "DS_BeginPainting: Lock failed with error '%s'\n", DSoundError( hresult ) ); + DS_Shutdown (); + return; + } + + IDirectSoundBuffer_Restore( pDSBuf ); + + if (++reps > 2) + return; + } + dma.buffer = (byte *)pbuf; +} + +/* +============== +DS_Submit + +Send sound to device if buffer isn't really the dma buffer +Also unlocks the dsound buffer +=============== +*/ +static void DS_Submit(void) { + if (!pDSBuf) + return; + + // unlock the dsound buffer + IDirectSoundBuffer_Unlock(pDSBuf, dma.buffer, locksize, NULL, 0); +} + +/* +=========== +DS_Activate + +Called when the main window gains or loses focus. +The window have been destroyed and recreated +between a deactivate and an activate. +=========== +*/ +static void DS_Activate (qboolean active) { + if( !pDS ) { + return; + } + if( active ) { + if( !DS_CreateBuffers() ) { + Com_EPrintf( "DS_Activate: DS_CreateBuffers failed\n" ); + DS_Shutdown(); + } + } else { + DS_DestroyBuffers(); + } +} + +void DS_FillAPI( snddmaAPI_t *api ) { + api->Init = DS_Init; + api->GetDMAPos = DS_GetDMAPos; + api->Shutdown = DS_Shutdown; + api->BeginPainting = DS_BeginPainting; + api->Submit = DS_Submit; + api->Activate = DS_Activate; +} + +#endif // USE_DSOUND
\ No newline at end of file |