Serious-Engine/Sources/Engine/Sound/SoundLibrary.cpp
Daniel Gibson 72edf1c720 Commented out unused functions and variables
many unused functions and variables are now commented out

You'll still get tons of warnings, which should mostly fall in one of
the following categories:
1. Unnecessary variables or values generated from .es scripts
2. Pointers assigned to from functions with side-effects: DO NOT REMOVE!
   Like CEntity *penNew = CreateEntity_t(...); - even if penNew isn't
   used, CreateEntity() must be called there!
2016-05-09 18:51:03 +02:00

1761 lines
60 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "Engine/StdH.h"
// !!! FIXME : rcg12162001 This file really needs to be ripped apart and
// !!! FIXME : rcg12162001 into platform/driver specific subdirectories.
// !!! FIXME : rcg10132001 what is this file?
#ifdef PLATFORM_WIN32
#include "initguid.h"
#endif
// !!! FIXME : Move all the SDL stuff to a different file...
#ifdef PLATFORM_UNIX
#include "SDL.h"
#endif
#include <Engine/Engine.h>
#include <Engine/Sound/SoundLibrary.h>
#include <Engine/Base/Translation.h>
#include <Engine/Base/Shell.h>
#include <Engine/Base/Memory.h>
#include <Engine/Base/ErrorReporting.h>
#include <Engine/Base/ListIterator.inl>
#include <Engine/Base/Console.h>
#include <Engine/Base/Console_internal.h>
#include <Engine/Base/Statistics_Internal.h>
#include <Engine/Base/IFeel.h>
#include <Engine/Sound/SoundProfile.h>
#include <Engine/Sound/SoundListener.h>
#include <Engine/Sound/SoundData.h>
#include <Engine/Sound/SoundObject.h>
#include <Engine/Sound/SoundDecoder.h>
#include <Engine/Network/Network.h>
#include <Engine/Templates/StaticArray.cpp>
#include <Engine/Templates/StaticStackArray.cpp>
template class CStaticArray<CSoundListener>;
#ifdef _MSC_VER
#pragma comment(lib, "winmm.lib")
#endif
// pointer to global sound library object
CSoundLibrary *_pSound = NULL;
// console variables
FLOAT snd_tmMixAhead = 0.2f; // mix-ahead in seconds
FLOAT snd_fSoundVolume = 1.0f; // master volume for sound playing [0..1]
FLOAT snd_fMusicVolume = 1.0f; // master volume for music playing [0..1]
// NOTES:
// - these 3d sound parameters have been set carefully, take extreme if changing !
// - ears distance of 20cm causes phase shift of up to 0.6ms which is very noticable
// and is more than enough, too large values cause too much distorsions in other effects
// - pan strength needs not to be very strong, since lrfilter has panning-like influence also
// - if down filter is too large, it makes too much influence even on small elevation changes
// and messes the situation completely
FLOAT snd_fDelaySoundSpeed = 1E10; // sound speed used for delay [m/s]
FLOAT snd_fDopplerSoundSpeed = 330.0f; // sound speed used for doppler [m/s]
FLOAT snd_fEarsDistance = 0.2f; // distance between listener's ears
FLOAT snd_fPanStrength = 0.1f; // panning modifier (0=none, 1= full)
FLOAT snd_fLRFilter = 3.0f; // filter for left-right
FLOAT snd_fBFilter = 5.0f; // filter for back
FLOAT snd_fUFilter = 1.0f; // filter for up
FLOAT snd_fDFilter = 3.0f; // filter for down
ENGINE_API INDEX snd_iFormat = 3;
INDEX snd_bMono = FALSE;
static INDEX snd_iDevice = -1;
static INDEX snd_iInterface = 2; // 0=WaveOut, 1=DirectSound, 2=EAX
static INDEX snd_iMaxOpenRetries = 3;
static INDEX snd_iMaxExtraChannels = 32;
static FLOAT snd_tmOpenFailDelay = 0.5f;
static FLOAT snd_fEAXPanning = 0.0f;
static FLOAT snd_fNormalizer = 0.9f;
static FLOAT _fLastNormalizeValue = 1;
#ifdef PLATFORM_WIN32
extern HWND _hwndMain; // global handle for application window
static HWND _hwndCurrent = NULL;
static HINSTANCE _hInstDS = NULL;
#else
static CTString snd_strDeviceName;
#endif
static BOOL _bMuted = FALSE;
static INDEX _iLastEnvType = 1234;
static FLOAT _fLastEnvSize = 1234;
#ifdef PLATFORM_WIN32
static FLOAT _fLastPanning = 1234;
static INDEX _iWriteOffset = 0;
static INDEX _iWriteOffset2 = 0;
// TEMP! - for writing mixer buffer to file
static FILE *_filMixerBuffer;
static BOOL _bOpened = FALSE;
#endif
#define WAVEOUTBLOCKSIZE 1024
#define MINPAN (1.0f)
#define MAXPAN (9.0f)
/**
* ----------------------------
* Sound Library functions
* ----------------------------
**/
// rcg12162001 Simple Directmedia Layer sound implementation.
#ifdef PLATFORM_UNIX
static Uint8 sdl_silence = 0;
static volatile SLONG sdl_backbuffer_allocation = 0;
static Uint8 *sdl_backbuffer = NULL;
static volatile SLONG sdl_backbuffer_pos = 0;
static volatile SLONG sdl_backbuffer_remain = 0;
static SDL_AudioDeviceID sdl_audio_device = 0;
static void sdl_audio_callback(void *userdata, Uint8 *stream, int len)
{
ASSERT(!_bDedicatedServer);
ASSERT(sdl_backbuffer != NULL);
ASSERT(sdl_backbuffer_remain <= sdl_backbuffer_allocation);
ASSERT(sdl_backbuffer_remain >= 0);
ASSERT(sdl_backbuffer_pos < sdl_backbuffer_allocation);
ASSERT(sdl_backbuffer_pos >= 0);
// "avail" is just the byte count before the end of the buffer.
// "cpysize" is how many bytes can actually be copied.
int avail = sdl_backbuffer_allocation - sdl_backbuffer_pos;
int cpysize = (len < sdl_backbuffer_remain) ? len : sdl_backbuffer_remain;
Uint8 *src = sdl_backbuffer + sdl_backbuffer_pos;
if (avail < cpysize) // Copy would pass end of ring buffer?
cpysize = avail;
if (cpysize > 0) {
memcpy(stream, src, cpysize); // move first block to SDL stream.
sdl_backbuffer_remain -= cpysize;
ASSERT(sdl_backbuffer_remain >= 0);
len -= cpysize;
ASSERT(len >= 0);
stream += cpysize;
sdl_backbuffer_pos += cpysize;
} // if
// See if we need to rotate to start of ring buffer...
ASSERT(sdl_backbuffer_pos <= sdl_backbuffer_allocation);
if (sdl_backbuffer_pos == sdl_backbuffer_allocation) {
sdl_backbuffer_pos = 0;
// we might need to feed SDL more data now...
if (len > 0) {
cpysize = (len < sdl_backbuffer_remain) ? len : sdl_backbuffer_remain;
if (cpysize > 0) {
memcpy(stream, sdl_backbuffer, cpysize); // move 2nd block.
sdl_backbuffer_pos += cpysize;
ASSERT(sdl_backbuffer_pos < sdl_backbuffer_allocation);
sdl_backbuffer_remain -= cpysize;
ASSERT(sdl_backbuffer_remain >= 0);
len -= cpysize;
ASSERT(len >= 0);
stream += cpysize;
} // if
} // if
} // if
// SDL _still_ needs more data than we've got! Fill with silence. (*shrug*)
if (len > 0) {
ASSERT(sdl_backbuffer_remain == 0);
memset(stream, sdl_silence, len);
} // if
} // sdl_audio_callback
// initialize the SDL audio subsystem.
static BOOL StartUp_SDLaudio( CSoundLibrary &sl, BOOL bReport=TRUE)
{
bReport=TRUE; // !!! FIXME ...how do you configure this externally?
// not using DirectSound (obviously)
sl.sl_bUsingDirectSound = FALSE;
sl.sl_bUsingEAX = FALSE;
snd_iDevice = 0;
ASSERT(!_bDedicatedServer);
if (_bDedicatedServer) {
CPrintF("Dedicated server; not initializing audio.\n");
return FALSE;
}
if( bReport) CPrintF(TRANSV("SDL audio initialization ...\n"));
SDL_AudioSpec desired, obtained;
SDL_zero(desired);
SDL_zero(obtained);
Sint16 bps = sl.sl_SwfeFormat.wBitsPerSample;
if (bps <= 8)
desired.format = AUDIO_U8;
else if (bps <= 16)
desired.format = AUDIO_S16LSB;
else if (bps <= 32)
desired.format = AUDIO_S32LSB;
else {
CPrintF(TRANSV("Unsupported bits-per-sample: %d\n"), bps);
return FALSE;
}
desired.freq = sl.sl_SwfeFormat.nSamplesPerSec;
// I dunno if this is the best idea, but I'll give it a try...
// should probably check a cvar for this...
if (desired.freq <= 11025)
desired.samples = 512;
else if (desired.freq <= 22050)
desired.samples = 1024;
else if (desired.freq <= 44100)
desired.samples = 2048;
else
desired.samples = 4096; // (*shrug*)
desired.channels = sl.sl_SwfeFormat.nChannels;
desired.userdata = &sl;
desired.callback = sdl_audio_callback;
// !!! FIXME rcg12162001 We force SDL to convert the audio stream on the
// !!! FIXME rcg12162001 fly to match sl.sl_SwfeFormat, but I'm curious
// !!! FIXME rcg12162001 if the Serious Engine can handle it if we changed
// !!! FIXME rcg12162001 sl.sl_SwfeFormat to match what the audio hardware
// !!! FIXME rcg12162001 can handle. I'll have to check later.
sdl_audio_device = SDL_OpenAudioDevice(snd_strDeviceName.IsEmpty() ? NULL : (const char *) snd_strDeviceName, 0, &desired, &obtained, 0);
if (!sdl_audio_device) {
CPrintF( TRANSV("SDL_OpenAudioDevice() error: %s\n"), SDL_GetError());
return FALSE;
}
sdl_silence = obtained.silence;
sdl_backbuffer_allocation = (obtained.size * 4);
sdl_backbuffer = (Uint8 *)AllocMemory(sdl_backbuffer_allocation);
sdl_backbuffer_remain = 0;
sdl_backbuffer_pos = 0;
// report success
if( bReport) {
STUBBED("Report actual SDL device name?");
CPrintF( TRANSV(" opened device: %s\n"), "SDL audio stream");
CPrintF( TRANSV(" %dHz, %dbit, %s\n"),
sl.sl_SwfeFormat.nSamplesPerSec,
sl.sl_SwfeFormat.wBitsPerSample,
SDL_GetCurrentAudioDriver());
}
// determine whole mixer buffer size from mixahead console variable
sl.sl_slMixerBufferSize = (SLONG)(ceil(snd_tmMixAhead*sl.sl_SwfeFormat.nSamplesPerSec) *
sl.sl_SwfeFormat.wBitsPerSample/8 * sl.sl_SwfeFormat.nChannels);
// align size to be next multiply of WAVEOUTBLOCKSIZE
sl.sl_slMixerBufferSize += WAVEOUTBLOCKSIZE - (sl.sl_slMixerBufferSize % WAVEOUTBLOCKSIZE);
// decoder buffer always works at 44khz
sl.sl_slDecodeBufferSize = sl.sl_slMixerBufferSize *
((44100+sl.sl_SwfeFormat.nSamplesPerSec-1)/sl.sl_SwfeFormat.nSamplesPerSec);
if( bReport) {
CPrintF(TRANSV(" parameters: %d Hz, %d bit, stereo, mix-ahead: %gs\n"),
sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, snd_tmMixAhead);
CPrintF(TRANSV(" output buffers: %d x %d bytes\n"), 2, obtained.size);
CPrintF(TRANSV(" mpx decode: %d bytes\n"), sl.sl_slDecodeBufferSize);
}
// initialize mixing and decoding buffer
sl.sl_pslMixerBuffer = (SLONG*)AllocMemory( sl.sl_slMixerBufferSize *2); // (*2 because of 32-bit buffer)
sl.sl_pswDecodeBuffer = (SWORD*)AllocMemory( sl.sl_slDecodeBufferSize+4); // (+4 because of linear interpolation of last samples)
// the audio callback can now safely fill the audio stream with silence
// until there is actual audio data to mix...
SDL_PauseAudioDevice(sdl_audio_device, 0);
// done
return TRUE;
} // StartUp_SDLaudio
// SDL audio shutdown procedure
static void ShutDown_SDLaudio( CSoundLibrary &sl)
{
SDL_PauseAudioDevice(sdl_audio_device, 1);
if (sdl_backbuffer != NULL) {
FreeMemory(sdl_backbuffer);
sdl_backbuffer = NULL;
}
if (sl.sl_pslMixerBuffer != NULL) {
FreeMemory( sl.sl_pslMixerBuffer);
sl.sl_pslMixerBuffer = NULL;
}
if (sl.sl_pswDecodeBuffer != NULL) {
FreeMemory(sl.sl_pswDecodeBuffer);
sl.sl_pswDecodeBuffer = NULL;
}
SDL_CloseAudioDevice(sdl_audio_device);
sdl_audio_device = 0;
} // ShutDown_SDLaudio
// SDL_LockAudio() must be in effect when calling this!
// ...and stay in effect until after CopyMixerBuffer_SDLaudio() is called!
static SLONG PrepareSoundBuffer_SDLaudio( CSoundLibrary &sl)
{
ASSERT(sdl_backbuffer_remain >= 0);
ASSERT(sdl_backbuffer_remain <= sdl_backbuffer_allocation);
return(sdl_backbuffer_allocation - sdl_backbuffer_remain);
} // PrepareSoundBuffer_SDLaudio
// SDL_LockAudio() must be in effect when calling this!
// ...and have been in effect since PrepareSoundBuffer_SDLaudio was called!
static void CopyMixerBuffer_SDLaudio( CSoundLibrary &sl, SLONG datasize)
{
ASSERT((sdl_backbuffer_allocation - sdl_backbuffer_remain) >= datasize);
SLONG fillpos = sdl_backbuffer_pos + sdl_backbuffer_remain;
if (fillpos > sdl_backbuffer_allocation)
fillpos -= sdl_backbuffer_allocation;
SLONG cpysize = datasize;
if ( (cpysize + fillpos) > sdl_backbuffer_allocation)
cpysize = sdl_backbuffer_allocation - fillpos;
Uint8 *src = sdl_backbuffer + fillpos;
CopyMixerBuffer_stereo(0, src, cpysize);
datasize -= cpysize;
sdl_backbuffer_remain += cpysize;
if (datasize > 0) { // rotate to start of ring buffer?
CopyMixerBuffer_stereo(cpysize, sdl_backbuffer, datasize);
sdl_backbuffer_remain += datasize;
} // if
ASSERT(sdl_backbuffer_remain <= sdl_backbuffer_allocation);
} // CopyMixerBuffer_SDLaudio
#endif // defined PLATFORM_UNIX (SDL audio implementation)
/*
* Construct uninitialized sound library.
*/
CSoundLibrary::CSoundLibrary(void)
{
sl_csSound.cs_iIndex = 3000;
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// clear sound format
memset( &sl_SwfeFormat, 0, sizeof(WAVEFORMATEX));
sl_EsfFormat = SF_NONE;
// reset buffer ptrs
sl_pslMixerBuffer = NULL;
sl_pswDecodeBuffer = NULL;
sl_pubBuffersMemory = NULL;
#ifdef PLATFORM_WIN32
// clear wave out data
sl_hwoWaveOut = NULL;
// clear direct sound data
_hInstDS = NULL;
sl_pDS = NULL;
sl_pKSProperty = NULL;
sl_pDSPrimary = NULL;
sl_pDSSecondary = NULL;
sl_pDSSecondary2 = NULL;
sl_pDSListener = NULL;
sl_pDSSourceLeft = NULL;
sl_pDSSourceRight = NULL;
#endif
sl_bUsingDirectSound = FALSE;
sl_bUsingEAX = FALSE;
}
/*
* Destruct (and clean up).
*/
CSoundLibrary::~CSoundLibrary(void)
{
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// clear sound enviroment
Clear();
// clear any installed sound decoders
CSoundDecoder::EndPlugins();
}
// post sound console variables' functions
static FLOAT _tmLastMixAhead = 1234;
static INDEX _iLastFormat = 1234;
static INDEX _iLastDevice = 1234;
static INDEX _iLastAPI = 1234;
static void SndPostFunc(void *pArgs)
{
// clamp variables
snd_tmMixAhead = Clamp( snd_tmMixAhead, 0.1f, 0.9f);
snd_iFormat = Clamp( snd_iFormat, (INDEX)CSoundLibrary::SF_NONE, (INDEX)CSoundLibrary::SF_44100_16);
snd_iDevice = Clamp( snd_iDevice, -1, 15);
snd_iInterface = Clamp( snd_iInterface, 0, 2);
// if any variable has been changed
if( _tmLastMixAhead!=snd_tmMixAhead || _iLastFormat!=snd_iFormat
|| _iLastDevice!=snd_iDevice || _iLastAPI!=snd_iInterface) {
// reinit sound format
_pSound->SetFormat( (enum CSoundLibrary::SoundFormat)snd_iFormat, TRUE);
}
}
/*
* some internal functions
*/
#ifdef PLATFORM_WIN32
// DirectSound shutdown procedure
static void ShutDown_dsound( CSoundLibrary &sl)
{
// free direct sound buffer(s)
sl.sl_bUsingDirectSound = FALSE;
sl.sl_bUsingEAX = FALSE;
if( sl.sl_pDSSourceRight!=NULL) {
sl.sl_pDSSourceRight->Release();
sl.sl_pDSSourceRight = NULL;
}
if( sl.sl_pDSSourceLeft != NULL) {
sl.sl_pDSSourceLeft->Release();
sl.sl_pDSSourceLeft = NULL;
}
if( sl.sl_pDSListener != NULL) {
sl.sl_pDSListener->Release();
sl.sl_pDSListener = NULL;
}
if( sl.sl_pDSSecondary2 != NULL) {
sl.sl_pDSSecondary2->Stop();
sl.sl_pDSSecondary2->Release();
sl.sl_pDSSecondary2 = NULL;
}
if( sl.sl_pDSSecondary != NULL) {
sl.sl_pDSSecondary->Stop();
sl.sl_pDSSecondary->Release();
sl.sl_pDSSecondary = NULL;
}
if( sl.sl_pDSPrimary!=NULL) {
sl.sl_pDSPrimary->Stop();
sl.sl_pDSPrimary->Release();
sl.sl_pDSPrimary = NULL;
}
if( sl.sl_pKSProperty != NULL) {
sl.sl_pKSProperty->Release();
sl.sl_pKSProperty = NULL;
}
// free direct sound object
if( sl.sl_pDS!=NULL) {
// reset cooperative level
if( _hwndCurrent!=NULL) sl.sl_pDS->SetCooperativeLevel( _hwndCurrent, DSSCL_NORMAL);
sl.sl_pDS->Release();
sl.sl_pDS = NULL;
}
// free direct sound library
if( _hInstDS != NULL) {
FreeLibrary(_hInstDS);
_hInstDS = NULL;
}
// free memory
if( sl.sl_pslMixerBuffer!=NULL) {
FreeMemory( sl.sl_pslMixerBuffer);
sl.sl_pslMixerBuffer = NULL;
}
if( sl.sl_pswDecodeBuffer!=NULL) {
FreeMemory( sl.sl_pswDecodeBuffer);
sl.sl_pswDecodeBuffer = NULL;
}
}
#endif
/*
* Set wave format from library format
*/
static void SetWaveFormat( CSoundLibrary::SoundFormat EsfFormat, WAVEFORMATEX &wfeFormat)
{
// change Library Wave Format
memset( &wfeFormat, 0, sizeof(WAVEFORMATEX));
wfeFormat.wFormatTag = WAVE_FORMAT_PCM;
wfeFormat.nChannels = 2;
wfeFormat.wBitsPerSample = 16;
switch( EsfFormat) {
case CSoundLibrary::SF_11025_16: wfeFormat.nSamplesPerSec = 11025; break;
case CSoundLibrary::SF_22050_16: wfeFormat.nSamplesPerSec = 22050; break;
case CSoundLibrary::SF_44100_16: wfeFormat.nSamplesPerSec = 44100; break;
case CSoundLibrary::SF_NONE: ASSERTALWAYS( "Can't set to NONE format"); break;
default: ASSERTALWAYS( "Unknown Sound format"); break;
}
wfeFormat.nBlockAlign = (wfeFormat.wBitsPerSample / 8) * wfeFormat.nChannels;
wfeFormat.nAvgBytesPerSec = wfeFormat.nSamplesPerSec * wfeFormat.nBlockAlign;
}
/*
* Set library format from wave format
*/
static void SetLibraryFormat( CSoundLibrary &sl)
{
// if library format is none return
if( sl.sl_EsfFormat == CSoundLibrary::SF_NONE) return;
// else check wave format to determine library format
ULONG ulFormat = sl.sl_SwfeFormat.nSamplesPerSec;
// find format
switch( ulFormat) {
case 11025: sl.sl_EsfFormat = CSoundLibrary::SF_11025_16; break;
case 22050: sl.sl_EsfFormat = CSoundLibrary::SF_22050_16; break;
case 44100: sl.sl_EsfFormat = CSoundLibrary::SF_44100_16; break;
// unknown format
default:
ASSERTALWAYS( "Unknown sound format");
FatalError( TRANS("Unknown sound format"));
sl.sl_EsfFormat = CSoundLibrary::SF_ILLEGAL;
}
}
#ifdef PLATFORM_WIN32
static BOOL DSFail( CSoundLibrary &sl, char *strError)
{
CPrintF(strError);
ShutDown_dsound(sl);
snd_iInterface=1; // if EAX failed -> try DirectSound
return FALSE;
}
// some helper functions for DirectSound
static BOOL DSInitSecondary( CSoundLibrary &sl, LPDIRECTSOUNDBUFFER &pBuffer, SLONG slSize)
{
// eventuallt adjust for EAX
DWORD dwFlag3D = NONE;
if( snd_iInterface==2) {
dwFlag3D = DSBCAPS_CTRL3D;
sl.sl_SwfeFormat.nChannels=1; // mono output
sl.sl_SwfeFormat.nBlockAlign/=2;
sl.sl_SwfeFormat.nAvgBytesPerSec/=2;
slSize/=2;
}
DSBUFFERDESC dsBuffer;
memset( &dsBuffer, 0, sizeof(dsBuffer));
dsBuffer.dwSize = sizeof(DSBUFFERDESC);
dsBuffer.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | dwFlag3D;
dsBuffer.dwBufferBytes = slSize;
dsBuffer.lpwfxFormat = &sl.sl_SwfeFormat;
HRESULT hResult = sl.sl_pDS->CreateSoundBuffer( &dsBuffer, &pBuffer, NULL);
if( snd_iInterface==2) {
// revert back to original wave format (stereo)
sl.sl_SwfeFormat.nChannels=2;
sl.sl_SwfeFormat.nBlockAlign*=2;
sl.sl_SwfeFormat.nAvgBytesPerSec*=2;
}
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create secondary buffer.\n"));
return TRUE;
}
static BOOL DSLockBuffer( CSoundLibrary &sl, LPDIRECTSOUNDBUFFER pBuffer, SLONG slSize, LPVOID &lpData, DWORD &dwSize)
{
INDEX ctRetries = 1000; // too much?
if( sl.sl_bUsingEAX) slSize/=2; // buffer is mono in case of EAX
FOREVER {
HRESULT hResult = pBuffer->Lock( 0, slSize, &lpData, &dwSize, NULL, NULL, 0);
if( hResult==DS_OK && slSize==dwSize) return TRUE;
if( hResult!=DSERR_BUFFERLOST) return DSFail( sl, TRANS(" ! DirectSound error: Cannot lock sound buffer.\n"));
if( ctRetries-- == 0) return DSFail( sl, TRANS(" ! DirectSound error: Couldn't restore sound buffer.\n"));
pBuffer->Restore();
}
}
static void DSPlayBuffers( CSoundLibrary &sl)
{
DWORD dw;
BOOL bInitiatePlay = FALSE;
ASSERT( sl.sl_pDSSecondary!=NULL && sl.sl_pDSPrimary!=NULL);
if( sl.sl_bUsingEAX && sl.sl_pDSSecondary2->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
if( sl.sl_pDSSecondary->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
if( sl.sl_pDSPrimary->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
// done if all buffers are already playing
if( !bInitiatePlay) return;
// stop buffers (in case some buffers are playing
sl.sl_pDSPrimary->Stop();
sl.sl_pDSSecondary->Stop();
if( sl.sl_bUsingEAX) sl.sl_pDSSecondary2->Stop();
// check sound buffer lock and clear sound buffer(s)
LPVOID lpData;
DWORD dwSize;
if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
memset( lpData, 0, dwSize);
sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
if( sl.sl_bUsingEAX) {
if( !DSLockBuffer( sl, sl.sl_pDSSecondary2, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
memset( lpData, 0, dwSize);
sl.sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
// start playing EAX additional buffer
sl.sl_pDSSecondary2->Play( 0, 0, DSBPLAY_LOOPING);
}
// start playing standard DirectSound buffers
sl.sl_pDSPrimary->Play( 0, 0, DSBPLAY_LOOPING);
sl.sl_pDSSecondary->Play( 0, 0, DSBPLAY_LOOPING);
_iWriteOffset = 0;
_iWriteOffset2 = 0;
// adjust starting offsets for EAX
if( sl.sl_bUsingEAX) {
DWORD dwCursor1, dwCursor2;
SLONG slMinDelta = MAX_SLONG;
for( INDEX i=0; i<10; i++) { // shoud be enough to screw interrupts
sl.sl_pDSSecondary->GetCurrentPosition( &dwCursor1, NULL);
sl.sl_pDSSecondary2->GetCurrentPosition( &dwCursor2, NULL);
SLONG slDelta1 = dwCursor2-dwCursor1;
sl.sl_pDSSecondary2->GetCurrentPosition( &dwCursor2, NULL);
sl.sl_pDSSecondary->GetCurrentPosition( &dwCursor1, NULL);
SLONG slDelta2 = dwCursor2-dwCursor1;
SLONG slDelta = (slDelta1+slDelta2) /2;
if( slDelta<slMinDelta) slMinDelta = slDelta;
//CPrintF( "D1=%5d, D2=%5d, AD=%5d, MD=%5d\n", slDelta1, slDelta2, slDelta, slMinDelta);
}
if( slMinDelta<0) _iWriteOffset = -slMinDelta*2; // 2 because of offset is stereo
if( slMinDelta>0) _iWriteOffset2 = +slMinDelta*2;
_iWriteOffset += _iWriteOffset & 3; // round to 4 bytes
_iWriteOffset2 += _iWriteOffset2 & 3;
// assure that first writing offsets are inside buffers
if( _iWriteOffset >=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
if( _iWriteOffset2>=sl.sl_slMixerBufferSize) _iWriteOffset2 -= sl.sl_slMixerBufferSize;
ASSERT( _iWriteOffset >=0 && _iWriteOffset <sl.sl_slMixerBufferSize);
ASSERT( _iWriteOffset2>=0 && _iWriteOffset2<sl.sl_slMixerBufferSize);
}
}
// init and set DirectSound format (internal)
static BOOL StartUp_dsound( CSoundLibrary &sl, BOOL bReport=TRUE)
{
// startup
sl.sl_bUsingDirectSound = FALSE;
ASSERT( _hInstDS==NULL && sl.sl_pDS==NULL);
ASSERT( sl.sl_pDSSecondary==NULL && sl.sl_pDSPrimary==NULL);
// update window handle (just in case)
HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
if( bReport) CPrintF(TRANSV("Direct Sound initialization ...\n"));
ASSERT( _hInstDS==NULL);
_hInstDS = LoadLibraryA( "dsound.dll");
if( _hInstDS==NULL) {
CPrintF( TRANS(" ! DirectSound error: Cannot load 'DSOUND.DLL'.\n"));
return FALSE;
}
// get main procedure address
pDirectSoundCreate = (HRESULT(WINAPI *)(GUID FAR *, LPDIRECTSOUND FAR *, IUnknown FAR *))GetProcAddress( _hInstDS, "DirectSoundCreate");
if( pDirectSoundCreate==NULL) return DSFail( sl, TRANS(" ! DirectSound error: Cannot get procedure address.\n"));
// init dsound
HRESULT hResult;
hResult = pDirectSoundCreate( NULL, &sl.sl_pDS, NULL);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create object.\n"));
// get capabilities
DSCAPS dsCaps;
dsCaps.dwSize = sizeof(dsCaps);
hResult = sl.sl_pDS->GetCaps( &dsCaps);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot determine capabilites.\n"));
// fail if in emulation mode
if( dsCaps.dwFlags & DSCAPS_EMULDRIVER) {
CPrintF( TRANS(" ! DirectSound error: No driver installed.\n"));
ShutDown_dsound(sl);
return FALSE;
}
// set cooperative level to priority
_hwndCurrent = _hwndMain;
hResult = sl.sl_pDS->SetCooperativeLevel( _hwndCurrent, DSSCL_PRIORITY);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot set cooperative level.\n"));
// prepare 3D flag if EAX
DWORD dwFlag3D = NONE;
if( snd_iInterface==2) dwFlag3D = DSBCAPS_CTRL3D;
// create primary sound buffer (must have one)
DSBUFFERDESC dsBuffer;
memset( &dsBuffer, 0, sizeof(dsBuffer));
dsBuffer.dwSize = sizeof(dsBuffer);
dsBuffer.dwFlags = DSBCAPS_PRIMARYBUFFER | dwFlag3D;
dsBuffer.dwBufferBytes = 0;
dsBuffer.lpwfxFormat = NULL;
hResult = sl.sl_pDS->CreateSoundBuffer( &dsBuffer, &sl.sl_pDSPrimary, NULL);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create primary sound buffer.\n"));
// set primary buffer format
WAVEFORMATEX wfx = sl.sl_SwfeFormat;
hResult = sl.sl_pDSPrimary->SetFormat(&wfx);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot set primary sound buffer format.\n"));
// startup secondary sound buffer(s)
SLONG slBufferSize = (SLONG)(ceil(snd_tmMixAhead*sl.sl_SwfeFormat.nSamplesPerSec) *
sl.sl_SwfeFormat.wBitsPerSample/8 * sl.sl_SwfeFormat.nChannels);
if( !DSInitSecondary( sl, sl.sl_pDSSecondary, slBufferSize)) return FALSE;
// set some additionals for EAX
if( snd_iInterface==2)
{
// 2nd secondary buffer
if( !DSInitSecondary( sl, sl.sl_pDSSecondary2, slBufferSize)) return FALSE;
// set 3D for all buffers
HRESULT hr1,hr2,hr3,hr4;
hr1 = sl.sl_pDSPrimary->QueryInterface( IID_IDirectSound3DListener, (LPVOID*)&sl.sl_pDSListener);
hr2 = sl.sl_pDSSecondary->QueryInterface( IID_IDirectSound3DBuffer, (LPVOID*)&sl.sl_pDSSourceLeft);
hr3 = sl.sl_pDSSecondary2->QueryInterface( IID_IDirectSound3DBuffer, (LPVOID*)&sl.sl_pDSSourceRight);
if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D sound buffer.\n"));
hr1 = sl.sl_pDSListener->SetPosition( 0,0,0, DS3D_DEFERRED);
hr2 = sl.sl_pDSListener->SetOrientation( 0,0,1, 0,1,0, DS3D_DEFERRED);
hr3 = sl.sl_pDSListener->SetRolloffFactor( 1, DS3D_DEFERRED);
if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D parameters for listener.\n"));
hr1 = sl.sl_pDSSourceLeft->SetMinDistance( MINPAN, DS3D_DEFERRED);
hr2 = sl.sl_pDSSourceLeft->SetMaxDistance( MAXPAN, DS3D_DEFERRED);
hr3 = sl.sl_pDSSourceRight->SetMinDistance( MINPAN, DS3D_DEFERRED);
hr4 = sl.sl_pDSSourceRight->SetMaxDistance( MAXPAN, DS3D_DEFERRED);
if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK || hr4!=DS_OK) {
return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D parameters for sound source.\n"));
}
// apply
hResult = sl.sl_pDSListener->CommitDeferredSettings();
if( hResult!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot apply 3D parameters.\n"));
// reset EAX parameters
_fLastPanning = 1234;
_iLastEnvType = 1234;
_fLastEnvSize = 1234;
// query property interface to EAX
hResult = sl.sl_pDSSourceLeft->QueryInterface( IID_IKsPropertySet, (LPVOID*)&sl.sl_pKSProperty);
if( hResult != DS_OK) return DSFail( sl, TRANS(" ! EAX error: Cannot set property interface.\n"));
// query support
ULONG ulSupport = 0;
hResult = sl.sl_pKSProperty->QuerySupport( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, &ulSupport);
if( hResult != DS_OK || !(ulSupport&KSPROPERTY_SUPPORT_SET)) return DSFail( sl, TRANS(" ! EAX error: Cannot query property support.\n"));
hResult = sl.sl_pKSProperty->QuerySupport( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &ulSupport);
if( hResult != DS_OK || !(ulSupport&KSPROPERTY_SUPPORT_SET)) return DSFail( sl, TRANS(" ! EAX error: Cannot query property support.\n"));
// made it - EAX's on!
sl.sl_bUsingEAX = TRUE;
}
// mark that dsound is operative and set mixer buffer size (decoder buffer always works at 44khz)
_iWriteOffset = 0;
_iWriteOffset2 = 0;
sl.sl_bUsingDirectSound = TRUE;
sl.sl_slMixerBufferSize = slBufferSize;
sl.sl_slDecodeBufferSize = sl.sl_slMixerBufferSize *
((44100+sl.sl_SwfeFormat.nSamplesPerSec-1) /sl.sl_SwfeFormat.nSamplesPerSec);
// allocate mixing and decoding buffers
sl.sl_pslMixerBuffer = (SLONG*)AllocMemory( sl.sl_slMixerBufferSize *2); // (*2 because of 32-bit buffer)
sl.sl_pswDecodeBuffer = (SWORD*)AllocMemory( sl.sl_slDecodeBufferSize+4); // (+4 because of linear interpolation of last samples)
// report success
if( bReport) {
CTString strDevice = TRANS("default device");
if( snd_iDevice>=0) strDevice.PrintF( TRANS("device %d"), snd_iDevice);
CPrintF( TRANS(" %dHz, %dbit, %s, mix-ahead: %gs\n"),
sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, strDevice, snd_tmMixAhead);
CPrintF(TRANSV(" mixer buffer size: %d KB\n"), sl.sl_slMixerBufferSize /1024);
CPrintF(TRANSV(" decode buffer size: %d KB\n"), sl.sl_slDecodeBufferSize/1024);
// EAX?
CTString strEAX = TRANS("Disabled");
if( sl.sl_bUsingEAX) strEAX = TRANS("Enabled");
CPrintF( TRANS(" EAX: %s\n"), strEAX);
}
// done
return TRUE;
}
// set WaveOut format (internal)
static INDEX _ctChannelsOpened = 0;
static BOOL StartUp_waveout( CSoundLibrary &sl, BOOL bReport=TRUE)
{
// not using DirectSound (obviously)
sl.sl_bUsingDirectSound = FALSE;
sl.sl_bUsingEAX = FALSE;
if( bReport) CPrintF(TRANSV("WaveOut initialization ...\n"));
// set maximum total number of retries for device opening
INDEX ctMaxRetries = snd_iMaxOpenRetries;
_ctChannelsOpened = 0;
MMRESULT res;
// repeat
FOREVER {
// try to open wave device
HWAVEOUT hwo;
res = waveOutOpen( &hwo, (snd_iDevice<0)?WAVE_MAPPER:snd_iDevice, &sl.sl_SwfeFormat, NULL, NULL, NONE);
// if opened
if( res == MMSYSERR_NOERROR) {
_ctChannelsOpened++;
// if first one
if (_ctChannelsOpened==1) {
// remember as used waveout
sl.sl_hwoWaveOut = hwo;
// if extra channel
} else {
// remember under extra
sl.sl_ahwoExtra.Push() = hwo;
}
// if no extra channels should be taken
if (_ctChannelsOpened>=snd_iMaxExtraChannels+1) {
// no more tries
break;
}
// if cannot open
} else {
// decrement retry counter
ctMaxRetries--;
// if no more retries
if (ctMaxRetries<0) {
// quit trying
break;
// if more retries left
} else {
// wait a bit (probably sound-scheme is playing)
Sleep(int(snd_tmOpenFailDelay*1000));
}
}
}
// if couldn't set format
if( _ctChannelsOpened==0 && res != MMSYSERR_NOERROR) {
// report error
CTString strError;
switch (res) {
case MMSYSERR_ALLOCATED: strError = TRANS("Device already in use."); break;
case MMSYSERR_BADDEVICEID: strError = TRANS("Bad device number."); break;
case MMSYSERR_NODRIVER: strError = TRANS("No driver installed."); break;
case MMSYSERR_NOMEM: strError = TRANS("Memory allocation problem."); break;
case WAVERR_BADFORMAT: strError = TRANS("Unsupported data format."); break;
case WAVERR_SYNC: strError = TRANS("Wrong flag?"); break;
default: strError.PrintF( "%d", res);
};
CPrintF( TRANS(" ! WaveOut error: %s\n"), strError);
return FALSE;
}
// get waveout capabilities
WAVEOUTCAPS woc;
memset( &woc, 0, sizeof(woc));
res = waveOutGetDevCaps((int)sl.sl_hwoWaveOut, &woc, sizeof(woc));
// report success
if( bReport) {
CTString strDevice = TRANS("default device");
if( snd_iDevice>=0) strDevice.PrintF( TRANS("device %d"), snd_iDevice);
CPrintF( TRANS(" opened device: %s\n"), woc.szPname);
CPrintF( TRANS(" %dHz, %dbit, %s\n"),
sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, strDevice);
}
// determine whole mixer buffer size from mixahead console variable
sl.sl_slMixerBufferSize = (SLONG)(ceil(snd_tmMixAhead*sl.sl_SwfeFormat.nSamplesPerSec) *
sl.sl_SwfeFormat.wBitsPerSample/8 * sl.sl_SwfeFormat.nChannels);
// align size to be next multiply of WAVEOUTBLOCKSIZE
sl.sl_slMixerBufferSize += WAVEOUTBLOCKSIZE - (sl.sl_slMixerBufferSize % WAVEOUTBLOCKSIZE);
// determine number of WaveOut buffers
const INDEX ctWOBuffers = sl.sl_slMixerBufferSize / WAVEOUTBLOCKSIZE;
// decoder buffer always works at 44khz
sl.sl_slDecodeBufferSize = sl.sl_slMixerBufferSize *
((44100+sl.sl_SwfeFormat.nSamplesPerSec-1)/sl.sl_SwfeFormat.nSamplesPerSec);
if( bReport) {
CPrintF(TRANSV(" parameters: %d Hz, %d bit, stereo, mix-ahead: %gs\n"),
sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, snd_tmMixAhead);
CPrintF(TRANSV(" output buffers: %d x %d bytes\n"), ctWOBuffers, WAVEOUTBLOCKSIZE),
CPrintF(TRANSV(" mpx decode: %d bytes\n"), sl.sl_slDecodeBufferSize),
CPrintF(TRANSV(" extra sound channels taken: %d\n"), _ctChannelsOpened-1);
}
// initialise waveout sound buffers
sl.sl_pubBuffersMemory = (UBYTE*)AllocMemory( sl.sl_slMixerBufferSize);
memset( sl.sl_pubBuffersMemory, 0, sl.sl_slMixerBufferSize);
sl.sl_awhWOBuffers.New(ctWOBuffers);
for( INDEX iBuffer = 0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++) {
WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
wh.lpData = (char*)(sl.sl_pubBuffersMemory + iBuffer*WAVEOUTBLOCKSIZE);
wh.dwBufferLength = WAVEOUTBLOCKSIZE;
wh.dwFlags = 0;
}
// initialize mixing and decoding buffer
sl.sl_pslMixerBuffer = (SLONG*)AllocMemory( sl.sl_slMixerBufferSize *2); // (*2 because of 32-bit buffer)
sl.sl_pswDecodeBuffer = (SWORD*)AllocMemory( sl.sl_slDecodeBufferSize+4); // (+4 because of linear interpolation of last samples)
// done
return TRUE;
}
#endif
/*
* set sound format
*/
static void SetFormat_internal( CSoundLibrary &sl, CSoundLibrary::SoundFormat EsfNew, BOOL bReport)
{
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl.sl_csSound, TRUE);
// remember library format
sl.sl_EsfFormat = EsfNew;
// release library
sl.ClearLibrary();
// if none skip initialization
_fLastNormalizeValue = 1;
if( bReport) CPrintF(TRANSV("Setting sound format ...\n"));
if( sl.sl_EsfFormat == CSoundLibrary::SF_NONE) {
if( bReport) CPrintF(TRANSV(" (no sound)\n"));
return;
}
// set wave format from library format
SetWaveFormat( EsfNew, sl.sl_SwfeFormat);
snd_iDevice = Clamp( snd_iDevice, -1, (INDEX)(sl.sl_ctWaveDevices-1));
snd_tmMixAhead = Clamp( snd_tmMixAhead, 0.1f, 0.9f);
snd_iInterface = Clamp( snd_iInterface, 0, 2);
BOOL bSoundOK = FALSE;
#ifdef PLATFORM_WIN32
if( snd_iInterface==2) {
// if wanted, 1st try to set EAX
bSoundOK = StartUp_dsound( sl, bReport);
}
if( !bSoundOK && snd_iInterface==1) {
// if wanted, 2nd try to set DirectSound
bSoundOK = StartUp_dsound( sl, bReport);
}
// if DirectSound failed or not wanted
if( !bSoundOK) {
// try waveout
bSoundOK = StartUp_waveout( sl, bReport);
snd_iInterface = 0; // mark that DirectSound didn't make it
}
#else
bSoundOK = StartUp_SDLaudio(sl, bReport);
#endif
// if didn't make it by now
if( bReport) CPrintF("\n");
if( !bSoundOK) {
// revert to none in case sound init was unsuccessful
sl.sl_EsfFormat = CSoundLibrary::SF_NONE;
return;
}
// set library format from wave format
SetLibraryFormat(sl);
// add timer handler
_pTimer->AddHandler(&sl.sl_thTimerHandler);
}
/*
* Initialization
*/
void CSoundLibrary::Init(void)
{
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
_pShell->DeclareSymbol( "void SndPostFunc(INDEX);", (void *) &SndPostFunc);
_pShell->DeclareSymbol( " user INDEX snd_bMono;", (void *) &snd_bMono);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fEarsDistance;", (void *) &snd_fEarsDistance);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fDelaySoundSpeed;", (void *) &snd_fDelaySoundSpeed);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fDopplerSoundSpeed;", (void *) &snd_fDopplerSoundSpeed);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fPanStrength;", (void *) &snd_fPanStrength);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fLRFilter;", (void *) &snd_fLRFilter);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fBFilter;", (void *) &snd_fBFilter);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fUFilter;", (void *) &snd_fUFilter);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fDFilter;", (void *) &snd_fDFilter);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fSoundVolume;", (void *) &snd_fSoundVolume);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fMusicVolume;", (void *) &snd_fMusicVolume);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fNormalizer;", (void *) &snd_fNormalizer);
_pShell->DeclareSymbol( "persistent user FLOAT snd_tmMixAhead post:SndPostFunc;", (void *) &snd_tmMixAhead);
_pShell->DeclareSymbol( "persistent user INDEX snd_iInterface post:SndPostFunc;", (void *) &snd_iInterface);
_pShell->DeclareSymbol( "persistent user INDEX snd_iDevice post:SndPostFunc;", (void *) &snd_iDevice);
_pShell->DeclareSymbol( "persistent user INDEX snd_iFormat post:SndPostFunc;", (void *) &snd_iFormat);
_pShell->DeclareSymbol( "persistent user INDEX snd_iMaxExtraChannels;", (void *) &snd_iMaxExtraChannels);
_pShell->DeclareSymbol( "persistent user INDEX snd_iMaxOpenRetries;", (void *) &snd_iMaxOpenRetries);
_pShell->DeclareSymbol( "persistent user FLOAT snd_tmOpenFailDelay;", (void *) &snd_tmOpenFailDelay);
_pShell->DeclareSymbol( "persistent user FLOAT snd_fEAXPanning;", (void *) &snd_fEAXPanning);
#ifdef PLATFORM_UNIX
_pShell->DeclareSymbol( "persistent user CTString snd_strDeviceName;", (void *) &snd_strDeviceName);
#endif
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer) {
CPrintF(TRANSV("Dedicated server; not initializing sound.\n"));
return;
}
#endif
// print header
CPrintF(TRANSV("Initializing sound...\n"));
// initialize sound library and set no-sound format
SetFormat(SF_NONE);
// initialize any installed sound decoders
CSoundDecoder::InitPlugins();
sl_ctWaveDevices = 0; // rcg11012005 valgrind fix.
#ifdef PLATFORM_WIN32
// get number of devices
const INDEX ctDevices = waveOutGetNumDevs();
CPrintF(TRANSV(" Detected devices: %d\n"), ctDevices);
sl_ctWaveDevices = ctDevices;
// for each device
for(INDEX iDevice=0; iDevice<ctDevices; iDevice++) {
// get description
WAVEOUTCAPS woc;
memset( &woc, 0, sizeof(woc));
MMRESULT res = waveOutGetDevCaps(iDevice, &woc, sizeof(woc));
CPrintF(TRANSV(" device %d: %s\n"),
iDevice, woc.szPname);
CPrintF(TRANSV(" ver: %d, id: %d.%d\n"),
woc.vDriverVersion, woc.wMid, woc.wPid);
CPrintF(TRANSV(" form: 0x%08x, ch: %d, support: 0x%08x\n"),
woc.dwFormats, woc.wChannels, woc.dwSupport);
}
// done
#else
const int ctDevices = SDL_GetNumAudioDevices(0);
CPrintF(TRANSV(" Detected devices: %d\n"), ctDevices);
sl_ctWaveDevices = ctDevices;
for (int iDevice = 0; iDevice < ctDevices; iDevice++) {
CPrintF(TRANSV(" device %d: %s\n"),
iDevice, SDL_GetAudioDeviceName(iDevice, 0));
}
#endif
CPrintF("\n");
}
/*
* Clear Sound Library
*/
void CSoundLibrary::Clear(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// clear all sounds and datas buffers
{FOREACHINLIST(CSoundData, sd_Node, sl_ClhAwareList, itCsdStop) {
FOREACHINLIST(CSoundObject, so_Node, (itCsdStop->sd_ClhLinkList), itCsoStop) {
itCsoStop->Stop();
}
itCsdStop->ClearBuffer();
}}
// clear wave out data
ClearLibrary();
_fLastNormalizeValue = 1;
}
/* Clear Library WaveOut */
void CSoundLibrary::ClearLibrary(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// remove timer handler if added
if (sl_thTimerHandler.th_Node.IsLinked()) {
_pTimer->RemHandler(&sl_thTimerHandler);
}
sl_bUsingDirectSound = FALSE;
sl_bUsingEAX = FALSE;
#ifdef PLATFORM_WIN32
// shut down direct sound buffers (if needed)
ShutDown_dsound(*this);
// shut down wave out player buffers (if needed)
if( sl_hwoWaveOut!=NULL)
{ // reset wave out play buffers (stop playing)
MMRESULT res;
res = waveOutReset(sl_hwoWaveOut);
ASSERT(res == MMSYSERR_NOERROR);
// clear buffers
for( INDEX iBuffer = 0; iBuffer<sl_awhWOBuffers.Count(); iBuffer++) {
res = waveOutUnprepareHeader( sl_hwoWaveOut, &sl_awhWOBuffers[iBuffer],
sizeof(sl_awhWOBuffers[iBuffer]));
ASSERT(res == MMSYSERR_NOERROR);
}
sl_awhWOBuffers.Clear();
// close waveout device
res = waveOutClose( sl_hwoWaveOut);
ASSERT(res == MMSYSERR_NOERROR);
sl_hwoWaveOut = NULL;
}
// for each extra taken channel
for(INDEX iChannel=0; iChannel<sl_ahwoExtra.Count(); iChannel++) {
// close its device
MMRESULT res = waveOutClose( sl_ahwoExtra[iChannel]);
ASSERT(res == MMSYSERR_NOERROR);
}
// free extra channel handles
sl_ahwoExtra.PopAll();
#else
ShutDown_SDLaudio(*this);
#endif
// free memory
if( sl_pslMixerBuffer!=NULL) {
FreeMemory( sl_pslMixerBuffer);
sl_pslMixerBuffer = NULL;
}
if( sl_pswDecodeBuffer!=NULL) {
FreeMemory( sl_pswDecodeBuffer);
sl_pswDecodeBuffer = NULL;
}
if( sl_pubBuffersMemory!=NULL) {
FreeMemory( sl_pubBuffersMemory);
sl_pubBuffersMemory = NULL;
}
}
// set listener enviroment properties (EAX)
BOOL CSoundLibrary::SetEnvironment( INDEX iEnvNo, FLOAT fEnvSize/*=0*/)
{
if( !sl_bUsingEAX) return FALSE;
#ifdef PLATFORM_WIN32
// trim values
if( iEnvNo<0 || iEnvNo>25) iEnvNo=1;
if( fEnvSize<1 || fEnvSize>99) fEnvSize=8;
HRESULT hResult;
hResult = sl_pKSProperty->Set( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, NULL, 0, &iEnvNo, sizeof(DWORD));
if( hResult != DS_OK) return DSFail( *this, TRANS(" ! EAX error: Cannot set environment.\n"));
hResult = sl_pKSProperty->Set( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, NULL, 0, &fEnvSize, sizeof(FLOAT));
if( hResult != DS_OK) return DSFail( *this, TRANS(" ! EAX error: Cannot set environment size.\n"));
#endif
return TRUE;
}
// mute all sounds (erase playing buffer(s) and supress mixer)
void CSoundLibrary::Mute(void)
{
// stop all IFeel effects
IFeel_StopEffect(NULL);
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
#ifdef PLATFORM_WIN32
// erase direct sound buffer (waveout will shut-up by itself), but skip if there's no more sound library
if( this==NULL || !sl_bUsingDirectSound) return;
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// supress future mixing and erase sound buffer
_bMuted = TRUE;
static LPVOID lpData;
static DWORD dwSize;
// flush one secondary buffer
if( !DSLockBuffer( *this, sl_pDSSecondary, sl_slMixerBufferSize, lpData, dwSize)) return;
memset( lpData, 0, dwSize);
sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
// if EAX is in use
if( sl_bUsingEAX) {
// flush right buffer, too
if( !DSLockBuffer( *this, sl_pDSSecondary2, sl_slMixerBufferSize, lpData, dwSize)) return;
memset( lpData, 0, dwSize);
sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
}
#else
SDL_LockAudioDevice(sdl_audio_device);
_bMuted = TRUE;
sdl_backbuffer_remain = 0; // ditch pending audio data...
sdl_backbuffer_pos = 0;
SDL_UnlockAudioDevice(sdl_audio_device);
#endif
}
/*
* set sound format
*/
CSoundLibrary::SoundFormat CSoundLibrary::SetFormat( CSoundLibrary::SoundFormat EsfNew, BOOL bReport/*=FALSE*/)
{
// !!! FIXME : rcg12162001 Do this for all platforms?
#ifdef PLATFORM_UNIX
if (_bDedicatedServer) {
sl_EsfFormat = SF_NONE;
return(sl_EsfFormat);
}
#endif
// access to the list of handlers must be locked
CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
// synchronize access to sounds
CTSingleLock slSounds(&sl_csSound, TRUE);
// pause playing all sounds
{FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdStop) {
itCsdStop->PausePlayingObjects();
}}
// change format and keep console variable states
SetFormat_internal( *this, EsfNew, bReport);
_tmLastMixAhead = snd_tmMixAhead;
_iLastFormat = snd_iFormat;
_iLastDevice = snd_iDevice;
_iLastAPI = snd_iInterface;
// continue playing all sounds
CListHead lhToReload;
lhToReload.MoveList(sl_ClhAwareList);
{FORDELETELIST( CSoundData, sd_Node, lhToReload, itCsdContinue) {
CSoundData &sd = *itCsdContinue;
if( !(sd.sd_ulFlags&SDF_ENCODED)) {
sd.Reload();
} else {
sd.sd_Node.Remove();
sl_ClhAwareList.AddTail(sd.sd_Node);
}
sd.ResumePlayingObjects();
}}
// done
return sl_EsfFormat;
}
/* Update all 3d effects and copy internal data. */
void CSoundLibrary::UpdateSounds(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// see if we have valid handle for direct sound and eventually reinit sound
#ifdef PLATFORM_WIN32
if( sl_bUsingDirectSound && _hwndCurrent!=_hwndMain) {
_hwndCurrent = _hwndMain;
SetFormat( sl_EsfFormat);
}
#endif
_bMuted = FALSE; // enable mixer
_sfStats.StartTimer(CStatForm::STI_SOUNDUPDATE);
_pfSoundProfile.StartTimer(CSoundProfile::PTI_UPDATESOUNDS);
// synchronize access to sounds
CTSingleLock slSounds( &sl_csSound, TRUE);
#ifdef PLATFORM_WIN32
// make sure that the buffers are playing
if( sl_bUsingDirectSound) DSPlayBuffers(*this);
#endif
// determine number of listeners and get listener
INDEX ctListeners=0;
CSoundListener *sli;
{FOREACHINLIST( CSoundListener, sli_lnInActiveListeners, _pSound->sl_lhActiveListeners, itsli) {
sli = itsli;
ctListeners++;
}}
// if there's only one listener environment properties have been changed (in split-screen EAX is not supported)
if( ctListeners==1 && (_iLastEnvType!=sli->sli_iEnvironmentType || _fLastEnvSize!=sli->sli_fEnvironmentSize)) {
// keep new properties and eventually update environment (EAX)
_iLastEnvType = sli->sli_iEnvironmentType;
_fLastEnvSize = sli->sli_fEnvironmentSize;
SetEnvironment( _iLastEnvType, _fLastEnvSize);
}
// if there are no listeners - reset environment properties
if( ctListeners<1 && (_iLastEnvType!=1 || _fLastEnvSize!=1.4f)) {
// keep new properties and update environment
_iLastEnvType = 1;
_fLastEnvSize = 1.4f;
SetEnvironment( _iLastEnvType, _fLastEnvSize);
}
// adjust panning if needed
#ifdef PLATFORM_WIN32
snd_fEAXPanning = Clamp( snd_fEAXPanning, -1.0f, +1.0f);
if( sl_bUsingEAX && _fLastPanning!=snd_fEAXPanning)
{ // determine new panning
_fLastPanning = snd_fEAXPanning;
FLOAT fPanLeft = -1.0f;
FLOAT fPanRight = +1.0f;
if( snd_fEAXPanning<0) fPanRight = MINPAN + Abs(snd_fEAXPanning)*MAXPAN; // pan left
if( snd_fEAXPanning>0) fPanLeft = MINPAN + Abs(snd_fEAXPanning)*MAXPAN; // pan right
// set and apply
HRESULT hr1,hr2,hr3;
hr1 = sl_pDSSourceLeft->SetPosition( fPanLeft, 0,0, DS3D_DEFERRED);
hr2 = sl_pDSSourceRight->SetPosition( fPanRight,0,0, DS3D_DEFERRED);
hr3 = sl_pDSListener->CommitDeferredSettings();
if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) DSFail( *this, TRANS(" ! DirectSound3D error: Cannot set 3D position.\n"));
}
#endif
// for each sound
{FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
_sfStats.IncrementCounter(CStatForm::SCI_SOUNDSACTIVE);
itCsoSoundObject->Update3DEffects();
}
}}
// for each sound
{FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
CSoundObject &so = *itCsoSoundObject;
// if sound is playing
if( so.so_slFlags&SOF_PLAY) {
// copy parameters
so.so_sp = so.so_spNew;
// prepare sound if not prepared already
if ( !(so.so_slFlags&SOF_PREPARE)) {
so.PrepareSound();
so.so_slFlags |= SOF_PREPARE;
}
// if it is not playing
} else {
// remove it from list
so.so_Node.Remove();
}
}
}}
// remove all listeners
{FORDELETELIST( CSoundListener, sli_lnInActiveListeners, sl_lhActiveListeners, itsli) {
itsli->sli_lnInActiveListeners.Remove();
}}
_pfSoundProfile.StopTimer(CSoundProfile::PTI_UPDATESOUNDS);
_sfStats.StopTimer(CStatForm::STI_SOUNDUPDATE);
}
/*
* This is called every TickQuantum seconds.
*/
void CSoundTimerHandler::HandleTimer(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
/* memory leak checking routines
ASSERT( _CrtCheckMemory());
ASSERT( _CrtIsMemoryBlock( (void*)_pSound->sl_pswDecodeBuffer,
(ULONG)_pSound->sl_slDecodeBufferSize, NULL, NULL, NULL));
ASSERT( _CrtIsValidPointer( (void*)_pSound->sl_pswDecodeBuffer,
(ULONG)_pSound->sl_slDecodeBufferSize, TRUE)); */
// mix all needed sounds
_pSound->MixSounds();
}
/*
* MIXER helper functions
*/
// copying of mixer buffer to sound buffer(s)
#ifdef PLATFORM_WIN32
static LPVOID _lpData, _lpData2;
static DWORD _dwSize, _dwSize2;
static void CopyMixerBuffer_dsound( CSoundLibrary &sl, SLONG slMixedSize)
{
LPVOID lpData;
DWORD dwSize;
SLONG slPart1Size, slPart2Size;
// if EAX is in use
if( sl.sl_bUsingEAX)
{
// lock left buffer and copy first part of 1st mono block
if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset, slMixedSize);
CopyMixerBuffer_mono( 0, ((UBYTE*)lpData)+_iWriteOffset/2, slPart1Size);
// copy second part of 1st mono block
slPart2Size = slMixedSize - slPart1Size;
CopyMixerBuffer_mono( slPart1Size, lpData, slPart2Size);
_iWriteOffset += slMixedSize;
if( _iWriteOffset>=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
ASSERT( _iWriteOffset>=0 && _iWriteOffset<sl.sl_slMixerBufferSize);
sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
// lock right buffer and copy first part of 2nd mono block
if( !DSLockBuffer( sl, sl.sl_pDSSecondary2, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset2, slMixedSize);
CopyMixerBuffer_mono( 2, ((UBYTE*)lpData)+_iWriteOffset2/2, slPart1Size);
// copy second part of 2nd mono block
slPart2Size = slMixedSize - slPart1Size;
CopyMixerBuffer_mono( slPart1Size+2, lpData, slPart2Size);
_iWriteOffset2 += slMixedSize;
if( _iWriteOffset2>=sl.sl_slMixerBufferSize) _iWriteOffset2 -= sl.sl_slMixerBufferSize;
ASSERT( _iWriteOffset2>=0 && _iWriteOffset2<sl.sl_slMixerBufferSize);
sl.sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
}
// if only standard DSound (no EAX)
else
{
// lock stereo buffer and copy first part of block
if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset, slMixedSize);
CopyMixerBuffer_stereo( 0, ((UBYTE*)lpData)+_iWriteOffset, slPart1Size);
// copy second part of block
slPart2Size = slMixedSize - slPart1Size;
CopyMixerBuffer_stereo( slPart1Size, lpData, slPart2Size);
_iWriteOffset += slMixedSize;
if( _iWriteOffset>=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
ASSERT( _iWriteOffset>=0 && _iWriteOffset<sl.sl_slMixerBufferSize);
sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
}
}
static void CopyMixerBuffer_waveout( CSoundLibrary &sl)
{
MMRESULT res;
SLONG slOffset = 0;
for( INDEX iBuffer = 0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++)
{ // skip prepared buffer
WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
if( wh.dwFlags&WHDR_PREPARED) continue;
// copy part of a mixer buffer to wave buffer
CopyMixerBuffer_stereo( slOffset, wh.lpData, WAVEOUTBLOCKSIZE);
slOffset += WAVEOUTBLOCKSIZE;
// write wave buffer (ready for playing)
res = waveOutPrepareHeader( sl.sl_hwoWaveOut, &wh, sizeof(wh));
ASSERT( res==MMSYSERR_NOERROR);
res = waveOutWrite( sl.sl_hwoWaveOut, &wh, sizeof(wh));
ASSERT( res==MMSYSERR_NOERROR);
}
}
// finds room in sound buffer to copy in next crop of samples
static SLONG PrepareSoundBuffer_dsound( CSoundLibrary &sl)
{
// determine writable block size (difference between write and play pointers)
HRESULT hr1,hr2;
DWORD dwCurrentCursor, dwCurrentCursor2;
SLONG slDataToMix;
ASSERT( sl.sl_pDSSecondary!=NULL && sl.sl_pDSPrimary!=NULL);
// if EAX is in use
if( sl.sl_bUsingEAX)
{
hr1 = sl.sl_pDSSecondary->GetCurrentPosition( &dwCurrentCursor, NULL);
hr2 = sl.sl_pDSSecondary2->GetCurrentPosition( &dwCurrentCursor2, NULL);
if( hr1!=DS_OK || hr2!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot obtain sound buffer write position.\n"));
dwCurrentCursor *=2; // stereo mixer
dwCurrentCursor2*=2; // stereo mixer
// store pointers and wrapped block sizes
SLONG slDataToMix1 = dwCurrentCursor - _iWriteOffset;
if( slDataToMix1<0) slDataToMix1 += sl.sl_slMixerBufferSize;
ASSERT( slDataToMix1>=0 && slDataToMix1<=sl.sl_slMixerBufferSize);
slDataToMix1 = Min( slDataToMix1, sl.sl_slMixerBufferSize);
SLONG slDataToMix2 = dwCurrentCursor2 - _iWriteOffset2;
if( slDataToMix2<0) slDataToMix2 += sl.sl_slMixerBufferSize;
ASSERT( slDataToMix2>=0 && slDataToMix2<=sl.sl_slMixerBufferSize);
slDataToMix = Min( slDataToMix1, slDataToMix2);
}
// if only standard DSound (no EAX)
else
{
hr1 = sl.sl_pDSSecondary->GetCurrentPosition( &dwCurrentCursor, NULL);
if( hr1!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot obtain sound buffer write position.\n"));
// store pointer and wrapped block size
slDataToMix = dwCurrentCursor - _iWriteOffset;
if( slDataToMix<0) slDataToMix += sl.sl_slMixerBufferSize;
ASSERT( slDataToMix>=0 && slDataToMix<=sl.sl_slMixerBufferSize);
slDataToMix = Min( slDataToMix, sl.sl_slMixerBufferSize);
}
// done
//CPrintF( "LP/LW: %5d / %5d, RP/RW: %5d / %5d, MIX: %5d\n", dwCurrentCursor, _iWriteOffset, dwCurrentCursor2, _iWriteOffset2, slDataToMix); // grgr
return slDataToMix;
}
static SLONG PrepareSoundBuffer_waveout( CSoundLibrary &sl)
{
// scan waveout buffers to find all that are ready to receive sound data (i.e. not playing)
SLONG slDataToMix=0;
for( INDEX iBuffer=0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++)
{ // if done playing
WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
if( wh.dwFlags&WHDR_DONE) {
// unprepare buffer
MMRESULT res = waveOutUnprepareHeader( sl.sl_hwoWaveOut, &wh, sizeof(wh));
ASSERT( res == MMSYSERR_NOERROR);
}
// if unprepared
if( !(wh.dwFlags&WHDR_PREPARED)) {
// increase mix-in data size
slDataToMix += WAVEOUTBLOCKSIZE;
}
}
// done
ASSERT( slDataToMix <= sl.sl_slMixerBufferSize);
return slDataToMix;
}
#endif
/* Update Mixer */
void CSoundLibrary::MixSounds(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// synchronize access to sounds
CTSingleLock slSounds( &sl_csSound, TRUE);
// do nothing if no sound
if( sl_EsfFormat==SF_NONE || _bMuted) return;
_sfStats.StartTimer(CStatForm::STI_SOUNDMIXING);
_pfSoundProfile.IncrementAveragingCounter();
_pfSoundProfile.StartTimer(CSoundProfile::PTI_MIXSOUNDS);
// seek available buffer(s) for next crop of samples
SLONG slDataToMix;
#ifdef PLATFORM_WIN32
if( sl_bUsingDirectSound) { // using direct sound
slDataToMix = PrepareSoundBuffer_dsound( *this);
} else { // using wave out
slDataToMix = PrepareSoundBuffer_waveout(*this);
}
#else
SDL_LockAudioDevice(sdl_audio_device);
slDataToMix = PrepareSoundBuffer_SDLaudio(*this);
#endif
// skip mixing if all sound buffers are still busy playing
ASSERT( slDataToMix>=0);
if( slDataToMix<=0) {
#ifdef PLATFORM_UNIX
SDL_UnlockAudioDevice(sdl_audio_device);
#endif
_pfSoundProfile.StopTimer(CSoundProfile::PTI_MIXSOUNDS);
_sfStats.StopTimer(CStatForm::STI_SOUNDMIXING);
return;
}
// prepare mixer buffer
_pfSoundProfile.IncrementCounter(CSoundProfile::PCI_MIXINGS, 1);
ResetMixer( sl_pslMixerBuffer, slDataToMix);
BOOL bGamePaused = _pNetwork->IsPaused() || _pNetwork->IsServer() && _pNetwork->GetLocalPause();
// for each sound
FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
CSoundObject &so = *itCsoSoundObject;
// if the sound is in-game sound, and the game paused
if (!(so.so_slFlags&SOF_NONGAME) && bGamePaused) {
// don't mix it it
continue;
}
// if sound is prepared and playing
if( so.so_slFlags&SOF_PLAY &&
so.so_slFlags&SOF_PREPARE &&
!(so.so_slFlags&SOF_PAUSED)) {
// mix it
MixSound(&so);
}
}
}
// eventually normalize mixed sounds
snd_fNormalizer = Clamp( snd_fNormalizer, 0.0f, 1.0f);
NormalizeMixerBuffer( snd_fNormalizer, slDataToMix, _fLastNormalizeValue);
// write mixer buffer to file
// if( !_bOpened) _filMixerBuffer = fopen( "d:\\MixerBufferDump.raw", "wb");
// fwrite( (void*)sl_pslMixerBuffer, 1, slDataToMix, _filMixerBuffer);
// _bOpened = TRUE;
// copy mixer buffer to buffers buffer(s)
#ifdef PLATFORM_WIN32
if( sl_bUsingDirectSound) { // using direct sound
CopyMixerBuffer_dsound( *this, slDataToMix);
} else { // using wave out
CopyMixerBuffer_waveout(*this);
}
#else
CopyMixerBuffer_SDLaudio(*this, slDataToMix);
SDL_UnlockAudioDevice(sdl_audio_device);
#endif
// all done
_pfSoundProfile.StopTimer(CSoundProfile::PTI_MIXSOUNDS);
_sfStats.StopTimer(CStatForm::STI_SOUNDMIXING);
}
//
// Sound mode awareness functions
//
/*
* Add sound in sound aware list
*/
void CSoundLibrary::AddSoundAware(CSoundData &CsdAdd)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// add sound to list tail
sl_ClhAwareList.AddTail(CsdAdd.sd_Node);
};
/*
* Remove a display mode aware object.
*/
void CSoundLibrary::RemoveSoundAware(CSoundData &CsdRemove)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// remove it from list
CsdRemove.sd_Node.Remove();
};
// listen from this listener this frame
void CSoundLibrary::Listen(CSoundListener &sl)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
// just add it to list
if (sl.sli_lnInActiveListeners.IsLinked()) {
sl.sli_lnInActiveListeners.Remove();
}
sl_lhActiveListeners.AddTail(sl.sli_lnInActiveListeners);
}