summaryrefslogtreecommitdiff
path: root/source/snd_wave.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_wave.c
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/snd_wave.c')
-rw-r--r--source/snd_wave.c352
1 files changed, 352 insertions, 0 deletions
diff --git a/source/snd_wave.c b/source/snd_wave.c
new file mode 100644
index 0000000..eadd895
--- /dev/null
+++ b/source/snd_wave.c
@@ -0,0 +1,352 @@
+/*
+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.
+
+*/
+
+#include "win_local.h"
+#include <mmsystem.h>
+#include "snd_local.h"
+
+// 64K is > 1 second at 16-bit, 22050 Hz
+#define WAV_BUFFERS 64
+#define WAV_MASK ( WAV_BUFFERS - 1 )
+#define WAV_BUFFER_SIZE 0x0400
+
+static qboolean wav_init;
+
+// starts at 0 for disabled
+static int sample16;
+static int snd_sent, snd_completed;
+
+static HANDLE hData;
+static HPSTR lpData;
+
+static HGLOBAL hWaveHdr;
+static LPWAVEHDR lpWaveHdr;
+
+static HWAVEOUT hWaveOut;
+
+static DWORD gSndBufSize;
+
+/*
+==============
+WAVE_Shutdown
+
+Reset the sound device for exiting
+===============
+*/
+static void WAVE_Shutdown( void ) {
+ int i;
+
+ Com_Printf( "Shutting down wave sound\n" );
+
+ if( hWaveOut ) {
+ Com_DPrintf( "...resetting waveOut\n" );
+ waveOutReset (hWaveOut);
+
+ if (lpWaveHdr) {
+ Com_DPrintf( "...unpreparing headers\n" );
+ for (i=0 ; i< WAV_BUFFERS ; i++)
+ waveOutUnprepareHeader (hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR));
+ }
+
+ Com_DPrintf( "...closing waveOut\n" );
+ waveOutClose (hWaveOut);
+
+ if (hWaveHdr) {
+ Com_DPrintf( "...freeing WAV header\n" );
+ GlobalUnlock(hWaveHdr);
+ GlobalFree(hWaveHdr);
+ }
+
+ if (hData) {
+ Com_DPrintf( "...freeing WAV buffer\n" );
+ GlobalUnlock(hData);
+ GlobalFree(hData);
+ }
+
+ }
+
+ hWaveOut = 0;
+ hData = 0;
+ hWaveHdr = 0;
+ lpData = NULL;
+ lpWaveHdr = NULL;
+ wav_init = qfalse;
+}
+
+
+/*
+==================
+WAVE_Init
+
+Crappy windows multimedia base
+==================
+*/
+static sndinitstat WAVE_Init (void) {
+ WAVEFORMATEX format;
+ int i;
+ HRESULT hr;
+
+ Com_Printf( "Initializing wave sound\n" );
+
+ snd_sent = 0;
+ snd_completed = 0;
+
+ memset (&dma, 0, sizeof (dma));
+ dma.channels = 2;
+ dma.samplebits = 16;
+
+ if (s_khz->integer == 44)
+ dma.speed = 44100;
+ else if (s_khz->integer == 22)
+ dma.speed = 22050;
+ else
+ dma.speed = 11025;
+
+ 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 = 0;
+ format.nAvgBytesPerSec = format.nSamplesPerSec*format.nBlockAlign;
+
+ /* Open a waveform device for output using window callback. */
+ Com_DPrintf ("...opening waveform device: ");
+ while ((hr = waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
+ &format,
+ 0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
+ {
+ if (hr != MMSYSERR_ALLOCATED)
+ {
+ Com_DPrintf ("failed\n");
+ return SIS_FAILURE;
+ }
+
+ if (MessageBox (NULL,
+ __TEXT("The sound hardware is in use by another app.\n\n")
+ __TEXT("Select Retry to try to start sound again or Cancel to run ") __TEXT("q2pro") __TEXT(" with no sound."),
+ __TEXT("Sound not available"),
+ MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
+ {
+ Com_DPrintf ("hw in use\n" );
+ return SIS_NOTAVAIL;
+ }
+ }
+ Com_DPrintf( "ok\n" );
+
+ /*
+ * Allocate and lock memory for the waveform data. The memory
+ * for waveform data must be globally allocated with
+ * GMEM_MOVEABLE and GMEM_SHARE flags.
+
+ */
+ Com_DPrintf ("...allocating waveform buffer: ");
+ gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
+ hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, gSndBufSize);
+ if (!hData)
+ {
+ Com_DPrintf( " failed\n" );
+ WAVE_Shutdown();
+ return SIS_FAILURE;
+ }
+ Com_DPrintf( "ok\n" );
+
+ Com_DPrintf ("...locking waveform buffer: ");
+ lpData = GlobalLock(hData);
+ if (!lpData)
+ {
+ Com_DPrintf( " failed\n" );
+ WAVE_Shutdown();
+ return SIS_FAILURE;
+ }
+ memset (lpData, 0, gSndBufSize);
+ Com_DPrintf( "ok\n" );
+
+ /*
+ * Allocate and lock memory for the header. This memory must
+ * also be globally allocated with GMEM_MOVEABLE and
+ * GMEM_SHARE flags.
+ */
+ Com_DPrintf ("...allocating waveform header: ");
+ hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
+ (DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
+
+ if (hWaveHdr == NULL)
+ {
+ Com_DPrintf( "failed\n" );
+ WAVE_Shutdown();
+ return SIS_FAILURE;
+ }
+ Com_DPrintf( "ok\n" );
+
+ Com_DPrintf ("...locking waveform header: ");
+ lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
+
+ if (lpWaveHdr == NULL)
+ {
+ Com_DPrintf( "failed\n" );
+ WAVE_Shutdown();
+ return SIS_FAILURE;
+ }
+ memset (lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
+ Com_DPrintf( "ok\n" );
+
+ /* After allocation, set up and prepare headers. */
+ Com_DPrintf ("...preparing headers: ");
+ for (i=0 ; i<WAV_BUFFERS ; i++)
+ {
+ lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
+ lpWaveHdr[i].lpData = lpData + i*WAV_BUFFER_SIZE;
+
+ if (waveOutPrepareHeader(hWaveOut, lpWaveHdr+i, sizeof(WAVEHDR)) !=
+ MMSYSERR_NOERROR)
+ {
+ Com_DPrintf ("failed\n");
+ WAVE_Shutdown();
+ return SIS_FAILURE;
+ }
+ }
+ Com_DPrintf ("ok\n");
+
+ dma.samples = gSndBufSize/(dma.samplebits/8);
+ dma.samplepos = 0;
+ dma.submission_chunk = 512;
+ dma.buffer = (byte *) lpData;
+ sample16 = (dma.samplebits/8) - 1;
+
+ wav_init = qtrue;
+
+ return SIS_SUCCESS;
+}
+
+
+/*
+==============
+WAVE_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 WAVE_GetDMAPos(void) {
+ int s;
+
+ if( !wav_init ) {
+ return 0;
+ }
+
+ s = snd_sent * WAV_BUFFER_SIZE;
+
+ s >>= sample16;
+ s &= (dma.samples-1);
+
+ return s;
+}
+
+/*
+==============
+WAVE_BeginPainting
+
+Makes sure dma.buffer is valid
+===============
+*/
+static void WAVE_BeginPainting (void) {
+}
+
+/*
+==============
+WAVE_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+Also unlocks the dsound buffer
+===============
+*/
+static void WAVE_Submit(void) {
+ LPWAVEHDR h;
+ int wResult;
+
+ if (!dma.buffer)
+ return;
+
+ if (!wav_init)
+ return;
+
+ //
+ // find which sound blocks have completed
+ //
+ while (1) {
+ if ( snd_completed == snd_sent ) {
+ Com_DPrintf ("WAVE_Submit: Sound overrun\n");
+ break;
+ }
+
+ if ( !(lpWaveHdr[snd_completed & WAV_MASK].dwFlags & WHDR_DONE) ) {
+ break;
+ }
+
+ snd_completed++; // this buffer has been played
+ }
+
+ //
+ // submit a few new sound blocks
+ //
+ while (((snd_sent - snd_completed) >> sample16) < 8) {
+ h = lpWaveHdr + ( snd_sent & WAV_MASK );
+ if (paintedtime/256 <= snd_sent)
+ break;
+ snd_sent++;
+ /*
+ * Now the data block can be sent to the output device. The
+ * waveOutWrite function returns immediately and waveform
+ * data is sent to the output device in the background.
+ */
+ wResult = waveOutWrite(hWaveOut, h, sizeof(WAVEHDR));
+
+ if (wResult != MMSYSERR_NOERROR) {
+ Com_EPrintf ("WAVE_Submit: Failed to write block to device\n");
+ WAVE_Shutdown ();
+ return;
+ }
+ }
+}
+
+
+/*
+===========
+WAVE_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 WAVE_Activate (qboolean active) {
+}
+
+void WAVE_FillAPI( snddmaAPI_t *api ) {
+ api->Init = WAVE_Init;
+ api->GetDMAPos = WAVE_GetDMAPos;
+ api->Shutdown = WAVE_Shutdown;
+ api->BeginPainting = WAVE_BeginPainting;
+ api->Submit = WAVE_Submit;
+ api->Activate = WAVE_Activate;
+}