fteqw/engine/client/snd_win.c

368 lines
7.9 KiB
C

/*
Copyright (C) 1996-1997 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 "quakedef.h"
#include "winquake.h"
// 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
static void WAV_Submit(soundcardinfo_t *sc);
typedef struct {
HWAVEOUT hWaveOut;
HANDLE hData;
HGLOBAL hWaveHdr;
HPSTR lpData;
LPWAVEHDR lpWaveHdr;
// DWORD mmstarttime;
DWORD gSndBufSize;
} wavhandle_t;
/*
==================
S_BlockSound
==================
*/
//all devices
void S_BlockSound (void)
{
soundcardinfo_t *sc;
wavhandle_t *wh;
snd_blocked++;
for (sc = sndcardinfo; sc; sc=sc->next)
{
if (sc->Submit == WAV_Submit && !sc->inactive_sound)
{
wh = sc->handle;
if (snd_blocked == 1)
waveOutReset (wh->hWaveOut);
}
}
}
/*
==================
S_UnblockSound
==================
*/
//all devices
void S_UnblockSound (void)
{
snd_blocked--;
}
static void *WAV_Lock (soundcardinfo_t *sc)
{
return sc->sn.buffer;
}
static void WAV_Unlock (soundcardinfo_t *sc, void *buffer)
{
}
/*
==================
FreeSound
==================
*/
//per device
static void WAV_Shutdown (soundcardinfo_t *sc)
{
int i;
wavhandle_t *wh = sc->handle;
if (!wh)
return;
sc->handle = NULL;
waveOutReset (wh->hWaveOut);
if (wh->lpWaveHdr)
{
for (i=0 ; i< WAV_BUFFERS ; i++)
waveOutUnprepareHeader (wh->hWaveOut, wh->lpWaveHdr+i, sizeof(WAVEHDR));
}
waveOutClose (wh->hWaveOut);
if (wh->hWaveHdr)
{
GlobalUnlock(wh->hWaveHdr);
GlobalFree(wh->hWaveHdr);
}
if (wh->hData)
{
GlobalUnlock(wh->hData);
GlobalFree(wh->hData);
}
wh->hWaveOut = 0;
wh->hData = 0;
wh->hWaveHdr = 0;
wh->lpData = NULL;
wh->lpWaveHdr = NULL;
Z_Free(wh);
}
static void WAV_SetUnderWater(soundcardinfo_t *sc, qboolean underwater)
{
}
/*
==============
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.
===============
*/
static int WAV_GetDMAPos(soundcardinfo_t *sc)
{
int s;
s = sc->snd_sent * WAV_BUFFER_SIZE;
s >>= (sc->sn.samplebits/8) - 1;
// s = (s/shm->numchannels % (shm->samples-1))*shm->numchannels;
return s;
}
/*
==============
WAV_Submit
Send sound to device if buffer isn't really the dma buffer
===============
*/
static void WAV_Submit(soundcardinfo_t *sc)
{
LPWAVEHDR h;
int wResult;
wavhandle_t *wh = sc->handle;
int chunkstosubmit;
//
// find which sound blocks have completed
//
while (1)
{
if ( sc->snd_completed == sc->snd_sent )
{
Con_DPrintf ("Sound overrun\n");
break;
}
if ( ! (wh->lpWaveHdr[ sc->snd_completed & WAV_MASK].dwFlags & WHDR_DONE) )
{
break;
}
sc->snd_completed++; // this buffer has been played
}
//
// submit two new sound blocks
//
if (sc->sn.speed <= 22050)
chunkstosubmit = 4;
else
chunkstosubmit = 4 + (sc->sn.speed/6000);
while (((sc->snd_sent - sc->snd_completed) >> ((sc->sn.samplebits/8) - 1)) < chunkstosubmit)
{
h = wh->lpWaveHdr + ( sc->snd_sent&WAV_MASK );
sc->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(wh->hWaveOut, h, sizeof(WAVEHDR));
if (wResult != MMSYSERR_NOERROR)
{
Con_SafePrintf ("Failed to write block to device\n");
WAV_Shutdown (sc);
return;
}
}
}
/*
==================
SNDDM_InitWav
Crappy windows multimedia base
==================
*/
int WAV_InitCard (soundcardinfo_t *sc, int cardnum)
{
WAVEFORMATEX format;
int i;
HRESULT hr;
wavhandle_t *wh;
if (cardnum != 0)
return 2; //we only support one card, at the moment.
wh = sc->handle = Z_Malloc(sizeof(wavhandle_t));
sc->snd_sent = 0;
sc->snd_completed = 0;
if (sc->sn.speed > 48000) // limit waveout to 48000 until that buffer issue gets solved
sc->sn.speed = 48000;
memset (&format, 0, sizeof(format));
format.wFormatTag = WAVE_FORMAT_PCM;
format.nChannels = sc->sn.numchannels;
format.wBitsPerSample = sc->sn.samplebits;
format.nSamplesPerSec = sc->sn.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. */
while ((hr = waveOutOpen((LPHWAVEOUT)&wh->hWaveOut, WAVE_MAPPER,
&format,
0, 0L, CALLBACK_NULL)) != MMSYSERR_NOERROR)
{
if (hr != MMSYSERR_ALLOCATED)
{
if (hr == WAVERR_BADFORMAT)
Con_SafePrintf (S_ERROR "waveOutOpen failed, format not supported\n");
else
Con_SafePrintf (S_ERROR "waveOutOpen failed, return code %i\n", hr);
WAV_Shutdown (sc);
return false;
}
// 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 Quake with no sound.",
// "Sound not available",
// MB_RETRYCANCEL | MB_SETFOREGROUND | MB_ICONEXCLAMATION) != IDRETRY)
// {
Con_SafePrintf (S_ERROR "waveOutOpen failure;\n"
" hardware already in use\nclose the app, then try using snd_restart\n");
WAV_Shutdown (sc);
return false;
// }
}
/*
* Allocate and lock memory for the waveform data. The memory
* for waveform data must be globally allocated with
* GMEM_MOVEABLE and GMEM_SHARE flags.
*/
wh->gSndBufSize = WAV_BUFFERS*WAV_BUFFER_SIZE;
wh->hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, wh->gSndBufSize);
if (!wh->hData)
{
Con_SafePrintf (S_ERROR "Sound: Out of memory.\n");
WAV_Shutdown (sc);
return false;
}
wh->lpData = GlobalLock(wh->hData);
if (!wh->lpData)
{
Con_SafePrintf (S_ERROR "Sound: Failed to lock.\n");
WAV_Shutdown (sc);
return false;
}
memset (wh->lpData, 0, wh->gSndBufSize);
/*
* Allocate and lock memory for the header. This memory must
* also be globally allocated with GMEM_MOVEABLE and
* GMEM_SHARE flags.
*/
wh->hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR) * WAV_BUFFERS);
if (wh->hWaveHdr == NULL)
{
Con_SafePrintf (S_ERROR "Sound: Failed to Alloc header.\n");
WAV_Shutdown (sc);
return false;
}
wh->lpWaveHdr = (LPWAVEHDR) GlobalLock(wh->hWaveHdr);
if (wh->lpWaveHdr == NULL)
{
Con_SafePrintf (S_ERROR "Sound: Failed to lock header.\n");
WAV_Shutdown (sc);
return false;
}
memset (wh->lpWaveHdr, 0, sizeof(WAVEHDR) * WAV_BUFFERS);
/* After allocation, set up and prepare headers. */
for (i=0 ; i<WAV_BUFFERS ; i++)
{
wh->lpWaveHdr[i].dwBufferLength = WAV_BUFFER_SIZE;
wh->lpWaveHdr[i].lpData = wh->lpData + i*WAV_BUFFER_SIZE;
if (waveOutPrepareHeader(wh->hWaveOut, wh->lpWaveHdr+i, sizeof(WAVEHDR)) !=
MMSYSERR_NOERROR)
{
Con_SafePrintf (S_ERROR "Sound: failed to prepare wave headers\n");
WAV_Shutdown (sc);
return false;
}
}
sc->sn.samples = wh->gSndBufSize/(sc->sn.samplebits/8);
sc->sn.samplepos = 0;
sc->sn.buffer = (unsigned char *) wh->lpData;
sc->Lock = WAV_Lock;
sc->Unlock = WAV_Unlock;
sc->SetWaterDistortion = WAV_SetUnderWater;
sc->Submit = WAV_Submit;
sc->Shutdown = WAV_Shutdown;
sc->GetDMAPos = WAV_GetDMAPos;
return true;
}
int (*pWAV_InitCard) (soundcardinfo_t *sc, int cardnum) = &WAV_InitCard;