summaryrefslogtreecommitdiff
path: root/source/snd_dx.c
diff options
context:
space:
mode:
authorAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
committerAndrey Nazarov <skuller@skuller.net>2007-08-14 20:18:08 +0000
commitf294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch)
treee8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/snd_dx.c
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/snd_dx.c')
-rw-r--r--source/snd_dx.c429
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