Serious-Engine/Sources/Engine/Sound/SoundLibrary.cpp

1760 lines
60 KiB
C++
Raw Normal View History

2016-03-12 01:20:51 +01:00
/* 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. */
2016-03-11 14:57:17 +01:00
#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
2016-03-11 14:57:17 +01:00
#include "initguid.h"
#endif
// !!! FIXME : Move all the SDL stuff to a different file...
#ifdef PLATFORM_UNIX
#include "SDL.h"
#endif
2016-03-11 14:57:17 +01:00
#include <Engine/Engine.h>
2016-03-11 14:57:17 +01:00
#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>;
2016-03-11 14:57:17 +01:00
#ifdef _MSC_VER
2016-03-11 14:57:17 +01:00
#pragma comment(lib, "winmm.lib")
#endif
2016-03-11 14:57:17 +01:00
// 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]
2016-03-11 14:57:17 +01:00
// 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;
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
extern HWND _hwndMain; // global handle for application window
static HWND _hwndCurrent = NULL;
static HINSTANCE _hInstDS = NULL;
#else
static CTString snd_strDeviceName;
#endif
2016-03-11 14:57:17 +01:00
static INDEX _iWriteOffset = 0;
static INDEX _iWriteOffset2 = 0;
static BOOL _bMuted = FALSE;
static INDEX _iLastEnvType = 1234;
static FLOAT _fLastEnvSize = 1234;
static FLOAT _fLastPanning = 1234;
// TEMP! - for writing mixer buffer to file
static FILE *_filMixerBuffer;
static BOOL _bOpened = FALSE;
#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)
2016-03-11 14:57:17 +01:00
/*
* 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
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);
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
/*
* 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
2016-03-11 14:57:17 +01:00
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"));
2016-03-11 14:57:17 +01:00
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);
2016-03-11 14:57:17 +01:00
// 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"));
2016-03-11 14:57:17 +01:00
// 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"),
2016-03-11 14:57:17 +01:00
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);
2016-03-11 14:57:17 +01:00
}
// 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
2016-03-11 14:57:17 +01:00
/*
* 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"));
2016-03-11 14:57:17 +01:00
if( sl.sl_EsfFormat == CSoundLibrary::SF_NONE) {
if( bReport) CPrintF(TRANSV(" (no sound)\n"));
2016-03-11 14:57:17 +01:00
return;
}
// set wave format from library format
SetWaveFormat( EsfNew, sl.sl_SwfeFormat);
snd_iDevice = Clamp( snd_iDevice, -1, (INDEX)(sl.sl_ctWaveDevices-1));
2016-03-11 14:57:17 +01:00
snd_tmMixAhead = Clamp( snd_tmMixAhead, 0.1f, 0.9f);
snd_iInterface = Clamp( snd_iInterface, 0, 2);
2016-03-11 14:57:17 +01:00
BOOL bSoundOK = FALSE;
#ifdef PLATFORM_WIN32
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// print header
CPrintF(TRANSV("Initializing sound...\n"));
2016-03-11 14:57:17 +01:00
// initialize sound library and set no-sound format
SetFormat(SF_NONE);
// initialize any installed sound decoders
2016-03-11 14:57:17 +01:00
CSoundDecoder::InitPlugins();
sl_ctWaveDevices = 0; // rcg11012005 valgrind fix.
#ifdef PLATFORM_WIN32
2016-03-11 14:57:17 +01:00
// get number of devices
const INDEX ctDevices = waveOutGetNumDevs();
CPrintF(TRANSV(" Detected devices: %d\n"), ctDevices);
2016-03-11 14:57:17 +01:00
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"),
2016-03-11 14:57:17 +01:00
iDevice, woc.szPname);
CPrintF(TRANSV(" ver: %d, id: %d.%d\n"),
2016-03-11 14:57:17 +01:00
woc.vDriverVersion, woc.wMid, woc.wPid);
CPrintF(TRANSV(" form: 0x%08x, ch: %d, support: 0x%08x\n"),
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
}
/*
* 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// see if we have valid handle for direct sound and eventually reinit sound
#ifdef PLATFORM_WIN32
2016-03-11 14:57:17 +01:00
if( sl_bUsingDirectSound && _hwndCurrent!=_hwndMain) {
_hwndCurrent = _hwndMain;
SetFormat( sl_EsfFormat);
}
#endif
2016-03-11 14:57:17 +01:00
_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
2016-03-11 14:57:17 +01:00
// make sure that the buffers are playing
if( sl_bUsingDirectSound) DSPlayBuffers(*this);
#endif
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
/* 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)
static LPVOID _lpData, _lpData2;
static DWORD _dwSize, _dwSize2;
#ifdef PLATFORM_WIN32
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
/* Update Mixer */
void CSoundLibrary::MixSounds(void)
{
// !!! FIXME : rcg12162001 This should probably be done everywhere, honestly.
#ifdef PLATFORM_UNIX
if (_bDedicatedServer)
return;
#endif
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
_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
2016-03-11 14:57:17 +01:00
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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// 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
2016-03-11 14:57:17 +01:00
// just add it to list
if (sl.sli_lnInActiveListeners.IsLinked()) {
sl.sli_lnInActiveListeners.Remove();
}
sl_lhActiveListeners.AddTail(sl.sli_lnInActiveListeners);
}