SoundLibrary.cpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356
  1. /* Copyright (c) 2002-2012 Croteam Ltd.
  2. This program is free software; you can redistribute it and/or modify
  3. it under the terms of version 2 of the GNU General Public License as published by
  4. the Free Software Foundation
  5. This program is distributed in the hope that it will be useful,
  6. but WITHOUT ANY WARRANTY; without even the implied warranty of
  7. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  8. GNU General Public License for more details.
  9. You should have received a copy of the GNU General Public License along
  10. with this program; if not, write to the Free Software Foundation, Inc.,
  11. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
  12. #include "stdh.h"
  13. #include "initguid.h"
  14. #include <Engine/Sound/SoundLibrary.h>
  15. #include <Engine/Base/Translation.h>
  16. #include <Engine/Base/Shell.h>
  17. #include <Engine/Base/Memory.h>
  18. #include <Engine/Base/ErrorReporting.h>
  19. #include <Engine/Base/ListIterator.inl>
  20. #include <Engine/Base/Console.h>
  21. #include <Engine/Base/Console_internal.h>
  22. #include <Engine/Base/Statistics_Internal.h>
  23. #include <Engine/Base/IFeel.h>
  24. #include <Engine/Sound/SoundProfile.h>
  25. #include <Engine/Sound/SoundListener.h>
  26. #include <Engine/Sound/SoundData.h>
  27. #include <Engine/Sound/SoundObject.h>
  28. #include <Engine/Sound/SoundDecoder.h>
  29. #include <Engine/Network/Network.h>
  30. #include <Engine/Templates/StaticArray.cpp>
  31. #include <Engine/Templates/StaticStackArray.cpp>
  32. template CStaticArray<CSoundListener>;
  33. #pragma comment(lib, "winmm.lib")
  34. // pointer to global sound library object
  35. CSoundLibrary *_pSound = NULL;
  36. // console variables
  37. extern FLOAT snd_tmMixAhead = 0.2f; // mix-ahead in seconds
  38. extern FLOAT snd_fSoundVolume = 1.0f; // master volume for sound playing [0..1]
  39. extern FLOAT snd_fMusicVolume = 1.0f; // master volume for music playing [0..1]
  40. // NOTES:
  41. // - these 3d sound parameters have been set carefully, take extreme if changing !
  42. // - ears distance of 20cm causes phase shift of up to 0.6ms which is very noticable
  43. // and is more than enough, too large values cause too much distorsions in other effects
  44. // - pan strength needs not to be very strong, since lrfilter has panning-like influence also
  45. // - if down filter is too large, it makes too much influence even on small elevation changes
  46. // and messes the situation completely
  47. extern FLOAT snd_fDelaySoundSpeed = 1E10; // sound speed used for delay [m/s]
  48. extern FLOAT snd_fDopplerSoundSpeed = 330.0f; // sound speed used for doppler [m/s]
  49. extern FLOAT snd_fEarsDistance = 0.2f; // distance between listener's ears
  50. extern FLOAT snd_fPanStrength = 0.1f; // panning modifier (0=none, 1= full)
  51. extern FLOAT snd_fLRFilter = 3.0f; // filter for left-right
  52. extern FLOAT snd_fBFilter = 5.0f; // filter for back
  53. extern FLOAT snd_fUFilter = 1.0f; // filter for up
  54. extern FLOAT snd_fDFilter = 3.0f; // filter for down
  55. ENGINE_API extern INDEX snd_iFormat = 3;
  56. extern INDEX snd_bMono = FALSE;
  57. static INDEX snd_iDevice = -1;
  58. static INDEX snd_iInterface = 2; // 0=WaveOut, 1=DirectSound, 2=EAX
  59. static INDEX snd_iMaxOpenRetries = 3;
  60. static INDEX snd_iMaxExtraChannels = 32;
  61. static FLOAT snd_tmOpenFailDelay = 0.5f;
  62. static FLOAT snd_fEAXPanning = 0.0f;
  63. static FLOAT snd_fNormalizer = 0.9f;
  64. static FLOAT _fLastNormalizeValue = 1;
  65. extern HWND _hwndMain; // global handle for application window
  66. static HWND _hwndCurrent = NULL;
  67. static HINSTANCE _hInstDS = NULL;
  68. static INDEX _iWriteOffset = 0;
  69. static INDEX _iWriteOffset2 = 0;
  70. static BOOL _bMuted = FALSE;
  71. static INDEX _iLastEnvType = 1234;
  72. static FLOAT _fLastEnvSize = 1234;
  73. static FLOAT _fLastPanning = 1234;
  74. // TEMP! - for writing mixer buffer to file
  75. static FILE *_filMixerBuffer;
  76. static BOOL _bOpened = FALSE;
  77. #define WAVEOUTBLOCKSIZE 1024
  78. #define MINPAN (1.0f)
  79. #define MAXPAN (9.0f)
  80. /**
  81. * ----------------------------
  82. * Sound Library functions
  83. * ----------------------------
  84. **/
  85. /*
  86. * Construct uninitialized sound library.
  87. */
  88. CSoundLibrary::CSoundLibrary(void)
  89. {
  90. sl_csSound.cs_iIndex = 3000;
  91. // access to the list of handlers must be locked
  92. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  93. // synchronize access to sounds
  94. CTSingleLock slSounds(&sl_csSound, TRUE);
  95. // clear sound format
  96. memset( &sl_SwfeFormat, 0, sizeof(WAVEFORMATEX));
  97. sl_EsfFormat = SF_NONE;
  98. // reset buffer ptrs
  99. sl_pslMixerBuffer = NULL;
  100. sl_pswDecodeBuffer = NULL;
  101. sl_pubBuffersMemory = NULL;
  102. // clear wave out data
  103. sl_hwoWaveOut = NULL;
  104. // clear direct sound data
  105. _hInstDS = NULL;
  106. sl_pDS = NULL;
  107. sl_pKSProperty = NULL;
  108. sl_pDSPrimary = NULL;
  109. sl_pDSSecondary = NULL;
  110. sl_pDSSecondary2 = NULL;
  111. sl_pDSListener = NULL;
  112. sl_pDSSourceLeft = NULL;
  113. sl_pDSSourceRight = NULL;
  114. sl_bUsingDirectSound = FALSE;
  115. sl_bUsingEAX = FALSE;
  116. }
  117. /*
  118. * Destruct (and clean up).
  119. */
  120. CSoundLibrary::~CSoundLibrary(void)
  121. {
  122. // access to the list of handlers must be locked
  123. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  124. // synchronize access to sounds
  125. CTSingleLock slSounds(&sl_csSound, TRUE);
  126. // clear sound enviroment
  127. Clear();
  128. // clear any installed sound decoders
  129. CSoundDecoder::EndPlugins();
  130. }
  131. // post sound console variables' functions
  132. static FLOAT _tmLastMixAhead = 1234;
  133. static INDEX _iLastFormat = 1234;
  134. static INDEX _iLastDevice = 1234;
  135. static INDEX _iLastAPI = 1234;
  136. static void SndPostFunc(void *pArgs)
  137. {
  138. // clamp variables
  139. snd_tmMixAhead = Clamp( snd_tmMixAhead, 0.1f, 0.9f);
  140. snd_iFormat = Clamp( snd_iFormat, (INDEX)CSoundLibrary::SF_NONE, (INDEX)CSoundLibrary::SF_44100_16);
  141. snd_iDevice = Clamp( snd_iDevice, -1L, 15L);
  142. snd_iInterface = Clamp( snd_iInterface, 0L, 2L);
  143. // if any variable has been changed
  144. if( _tmLastMixAhead!=snd_tmMixAhead || _iLastFormat!=snd_iFormat
  145. || _iLastDevice!=snd_iDevice || _iLastAPI!=snd_iInterface) {
  146. // reinit sound format
  147. _pSound->SetFormat( (enum CSoundLibrary::SoundFormat)snd_iFormat, TRUE);
  148. }
  149. }
  150. /*
  151. * some internal functions
  152. */
  153. // DirectSound shutdown procedure
  154. static void ShutDown_dsound( CSoundLibrary &sl)
  155. {
  156. // free direct sound buffer(s)
  157. sl.sl_bUsingDirectSound = FALSE;
  158. sl.sl_bUsingEAX = FALSE;
  159. if( sl.sl_pDSSourceRight!=NULL) {
  160. sl.sl_pDSSourceRight->Release();
  161. sl.sl_pDSSourceRight = NULL;
  162. }
  163. if( sl.sl_pDSSourceLeft != NULL) {
  164. sl.sl_pDSSourceLeft->Release();
  165. sl.sl_pDSSourceLeft = NULL;
  166. }
  167. if( sl.sl_pDSListener != NULL) {
  168. sl.sl_pDSListener->Release();
  169. sl.sl_pDSListener = NULL;
  170. }
  171. if( sl.sl_pDSSecondary2 != NULL) {
  172. sl.sl_pDSSecondary2->Stop();
  173. sl.sl_pDSSecondary2->Release();
  174. sl.sl_pDSSecondary2 = NULL;
  175. }
  176. if( sl.sl_pDSSecondary != NULL) {
  177. sl.sl_pDSSecondary->Stop();
  178. sl.sl_pDSSecondary->Release();
  179. sl.sl_pDSSecondary = NULL;
  180. }
  181. if( sl.sl_pDSPrimary!=NULL) {
  182. sl.sl_pDSPrimary->Stop();
  183. sl.sl_pDSPrimary->Release();
  184. sl.sl_pDSPrimary = NULL;
  185. }
  186. if( sl.sl_pKSProperty != NULL) {
  187. sl.sl_pKSProperty->Release();
  188. sl.sl_pKSProperty = NULL;
  189. }
  190. // free direct sound object
  191. if( sl.sl_pDS!=NULL) {
  192. // reset cooperative level
  193. if( _hwndCurrent!=NULL) sl.sl_pDS->SetCooperativeLevel( _hwndCurrent, DSSCL_NORMAL);
  194. sl.sl_pDS->Release();
  195. sl.sl_pDS = NULL;
  196. }
  197. // free direct sound library
  198. if( _hInstDS != NULL) {
  199. FreeLibrary(_hInstDS);
  200. _hInstDS = NULL;
  201. }
  202. // free memory
  203. if( sl.sl_pslMixerBuffer!=NULL) {
  204. FreeMemory( sl.sl_pslMixerBuffer);
  205. sl.sl_pslMixerBuffer = NULL;
  206. }
  207. if( sl.sl_pswDecodeBuffer!=NULL) {
  208. FreeMemory( sl.sl_pswDecodeBuffer);
  209. sl.sl_pswDecodeBuffer = NULL;
  210. }
  211. }
  212. /*
  213. * Set wave format from library format
  214. */
  215. static void SetWaveFormat( CSoundLibrary::SoundFormat EsfFormat, WAVEFORMATEX &wfeFormat)
  216. {
  217. // change Library Wave Format
  218. memset( &wfeFormat, 0, sizeof(WAVEFORMATEX));
  219. wfeFormat.wFormatTag = WAVE_FORMAT_PCM;
  220. wfeFormat.nChannels = 2;
  221. wfeFormat.wBitsPerSample = 16;
  222. switch( EsfFormat) {
  223. case CSoundLibrary::SF_11025_16: wfeFormat.nSamplesPerSec = 11025; break;
  224. case CSoundLibrary::SF_22050_16: wfeFormat.nSamplesPerSec = 22050; break;
  225. case CSoundLibrary::SF_44100_16: wfeFormat.nSamplesPerSec = 44100; break;
  226. case CSoundLibrary::SF_NONE: ASSERTALWAYS( "Can't set to NONE format"); break;
  227. default: ASSERTALWAYS( "Unknown Sound format"); break;
  228. }
  229. wfeFormat.nBlockAlign = (wfeFormat.wBitsPerSample / 8) * wfeFormat.nChannels;
  230. wfeFormat.nAvgBytesPerSec = wfeFormat.nSamplesPerSec * wfeFormat.nBlockAlign;
  231. }
  232. /*
  233. * Set library format from wave format
  234. */
  235. static void SetLibraryFormat( CSoundLibrary &sl)
  236. {
  237. // if library format is none return
  238. if( sl.sl_EsfFormat == CSoundLibrary::SF_NONE) return;
  239. // else check wave format to determine library format
  240. ULONG ulFormat = sl.sl_SwfeFormat.nSamplesPerSec;
  241. // find format
  242. switch( ulFormat) {
  243. case 11025: sl.sl_EsfFormat = CSoundLibrary::SF_11025_16; break;
  244. case 22050: sl.sl_EsfFormat = CSoundLibrary::SF_22050_16; break;
  245. case 44100: sl.sl_EsfFormat = CSoundLibrary::SF_44100_16; break;
  246. // unknown format
  247. default:
  248. ASSERTALWAYS( "Unknown sound format");
  249. FatalError( TRANS("Unknown sound format"));
  250. sl.sl_EsfFormat = CSoundLibrary::SF_ILLEGAL;
  251. }
  252. }
  253. static BOOL DSFail( CSoundLibrary &sl, char *strError)
  254. {
  255. CPrintF(strError);
  256. ShutDown_dsound(sl);
  257. snd_iInterface=1; // if EAX failed -> try DirectSound
  258. return FALSE;
  259. }
  260. // some helper functions for DirectSound
  261. static BOOL DSInitSecondary( CSoundLibrary &sl, LPDIRECTSOUNDBUFFER &pBuffer, SLONG slSize)
  262. {
  263. // eventuallt adjust for EAX
  264. DWORD dwFlag3D = NONE;
  265. if( snd_iInterface==2) {
  266. dwFlag3D = DSBCAPS_CTRL3D;
  267. sl.sl_SwfeFormat.nChannels=1; // mono output
  268. sl.sl_SwfeFormat.nBlockAlign/=2;
  269. sl.sl_SwfeFormat.nAvgBytesPerSec/=2;
  270. slSize/=2;
  271. }
  272. DSBUFFERDESC dsBuffer;
  273. memset( &dsBuffer, 0, sizeof(dsBuffer));
  274. dsBuffer.dwSize = sizeof(DSBUFFERDESC);
  275. dsBuffer.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | dwFlag3D;
  276. dsBuffer.dwBufferBytes = slSize;
  277. dsBuffer.lpwfxFormat = &sl.sl_SwfeFormat;
  278. HRESULT hResult = sl.sl_pDS->CreateSoundBuffer( &dsBuffer, &pBuffer, NULL);
  279. if( snd_iInterface==2) {
  280. // revert back to original wave format (stereo)
  281. sl.sl_SwfeFormat.nChannels=2;
  282. sl.sl_SwfeFormat.nBlockAlign*=2;
  283. sl.sl_SwfeFormat.nAvgBytesPerSec*=2;
  284. }
  285. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create secondary buffer.\n"));
  286. return TRUE;
  287. }
  288. static BOOL DSLockBuffer( CSoundLibrary &sl, LPDIRECTSOUNDBUFFER pBuffer, SLONG slSize, LPVOID &lpData, DWORD &dwSize)
  289. {
  290. INDEX ctRetries = 1000; // too much?
  291. if( sl.sl_bUsingEAX) slSize/=2; // buffer is mono in case of EAX
  292. FOREVER {
  293. HRESULT hResult = pBuffer->Lock( 0, slSize, &lpData, &dwSize, NULL, NULL, 0);
  294. if( hResult==DS_OK && slSize==dwSize) return TRUE;
  295. if( hResult!=DSERR_BUFFERLOST) return DSFail( sl, TRANS(" ! DirectSound error: Cannot lock sound buffer.\n"));
  296. if( ctRetries-- == 0) return DSFail( sl, TRANS(" ! DirectSound error: Couldn't restore sound buffer.\n"));
  297. pBuffer->Restore();
  298. }
  299. }
  300. static void DSPlayBuffers( CSoundLibrary &sl)
  301. {
  302. DWORD dw;
  303. BOOL bInitiatePlay = FALSE;
  304. ASSERT( sl.sl_pDSSecondary!=NULL && sl.sl_pDSPrimary!=NULL);
  305. if( sl.sl_bUsingEAX && sl.sl_pDSSecondary2->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
  306. if( sl.sl_pDSSecondary->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
  307. if( sl.sl_pDSPrimary->GetStatus(&dw)==DS_OK && !(dw&DSBSTATUS_PLAYING)) bInitiatePlay = TRUE;
  308. // done if all buffers are already playing
  309. if( !bInitiatePlay) return;
  310. // stop buffers (in case some buffers are playing
  311. sl.sl_pDSPrimary->Stop();
  312. sl.sl_pDSSecondary->Stop();
  313. if( sl.sl_bUsingEAX) sl.sl_pDSSecondary2->Stop();
  314. // check sound buffer lock and clear sound buffer(s)
  315. LPVOID lpData;
  316. DWORD dwSize;
  317. if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
  318. memset( lpData, 0, dwSize);
  319. sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
  320. if( sl.sl_bUsingEAX) {
  321. if( !DSLockBuffer( sl, sl.sl_pDSSecondary2, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
  322. memset( lpData, 0, dwSize);
  323. sl.sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
  324. // start playing EAX additional buffer
  325. sl.sl_pDSSecondary2->Play( 0, 0, DSBPLAY_LOOPING);
  326. }
  327. // start playing standard DirectSound buffers
  328. sl.sl_pDSPrimary->Play( 0, 0, DSBPLAY_LOOPING);
  329. sl.sl_pDSSecondary->Play( 0, 0, DSBPLAY_LOOPING);
  330. _iWriteOffset = 0;
  331. _iWriteOffset2 = 0;
  332. // adjust starting offsets for EAX
  333. if( sl.sl_bUsingEAX) {
  334. DWORD dwCursor1, dwCursor2;
  335. SLONG slMinDelta = MAX_SLONG;
  336. for( INDEX i=0; i<10; i++) { // shoud be enough to screw interrupts
  337. sl.sl_pDSSecondary->GetCurrentPosition( &dwCursor1, NULL);
  338. sl.sl_pDSSecondary2->GetCurrentPosition( &dwCursor2, NULL);
  339. SLONG slDelta1 = dwCursor2-dwCursor1;
  340. sl.sl_pDSSecondary2->GetCurrentPosition( &dwCursor2, NULL);
  341. sl.sl_pDSSecondary->GetCurrentPosition( &dwCursor1, NULL);
  342. SLONG slDelta2 = dwCursor2-dwCursor1;
  343. SLONG slDelta = (slDelta1+slDelta2) /2;
  344. if( slDelta<slMinDelta) slMinDelta = slDelta;
  345. //CPrintF( "D1=%5d, D2=%5d, AD=%5d, MD=%5d\n", slDelta1, slDelta2, slDelta, slMinDelta);
  346. }
  347. if( slMinDelta<0) _iWriteOffset = -slMinDelta*2; // 2 because of offset is stereo
  348. if( slMinDelta>0) _iWriteOffset2 = +slMinDelta*2;
  349. _iWriteOffset += _iWriteOffset & 3; // round to 4 bytes
  350. _iWriteOffset2 += _iWriteOffset2 & 3;
  351. // assure that first writing offsets are inside buffers
  352. if( _iWriteOffset >=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
  353. if( _iWriteOffset2>=sl.sl_slMixerBufferSize) _iWriteOffset2 -= sl.sl_slMixerBufferSize;
  354. ASSERT( _iWriteOffset >=0 && _iWriteOffset <sl.sl_slMixerBufferSize);
  355. ASSERT( _iWriteOffset2>=0 && _iWriteOffset2<sl.sl_slMixerBufferSize);
  356. }
  357. }
  358. // init and set DirectSound format (internal)
  359. static BOOL StartUp_dsound( CSoundLibrary &sl, BOOL bReport=TRUE)
  360. {
  361. // startup
  362. sl.sl_bUsingDirectSound = FALSE;
  363. ASSERT( _hInstDS==NULL && sl.sl_pDS==NULL);
  364. ASSERT( sl.sl_pDSSecondary==NULL && sl.sl_pDSPrimary==NULL);
  365. // update window handle (just in case)
  366. HRESULT (WINAPI *pDirectSoundCreate)(GUID FAR *lpGUID, LPDIRECTSOUND FAR *lplpDS, IUnknown FAR *pUnkOuter);
  367. if( bReport) CPrintF(TRANS("Direct Sound initialization ...\n"));
  368. ASSERT( _hInstDS==NULL);
  369. _hInstDS = LoadLibraryA( "dsound.dll");
  370. if( _hInstDS==NULL) {
  371. CPrintF( TRANS(" ! DirectSound error: Cannot load 'DSOUND.DLL'.\n"));
  372. return FALSE;
  373. }
  374. // get main procedure address
  375. pDirectSoundCreate = (HRESULT(WINAPI *)(GUID FAR *, LPDIRECTSOUND FAR *, IUnknown FAR *))GetProcAddress( _hInstDS, "DirectSoundCreate");
  376. if( pDirectSoundCreate==NULL) return DSFail( sl, TRANS(" ! DirectSound error: Cannot get procedure address.\n"));
  377. // init dsound
  378. HRESULT hResult;
  379. hResult = pDirectSoundCreate( NULL, &sl.sl_pDS, NULL);
  380. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create object.\n"));
  381. // get capabilities
  382. DSCAPS dsCaps;
  383. dsCaps.dwSize = sizeof(dsCaps);
  384. hResult = sl.sl_pDS->GetCaps( &dsCaps);
  385. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot determine capabilites.\n"));
  386. // fail if in emulation mode
  387. if( dsCaps.dwFlags & DSCAPS_EMULDRIVER) {
  388. CPrintF( TRANS(" ! DirectSound error: No driver installed.\n"));
  389. ShutDown_dsound(sl);
  390. return FALSE;
  391. }
  392. // set cooperative level to priority
  393. _hwndCurrent = _hwndMain;
  394. hResult = sl.sl_pDS->SetCooperativeLevel( _hwndCurrent, DSSCL_PRIORITY);
  395. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot set cooperative level.\n"));
  396. // prepare 3D flag if EAX
  397. DWORD dwFlag3D = NONE;
  398. if( snd_iInterface==2) dwFlag3D = DSBCAPS_CTRL3D;
  399. // create primary sound buffer (must have one)
  400. DSBUFFERDESC dsBuffer;
  401. memset( &dsBuffer, 0, sizeof(dsBuffer));
  402. dsBuffer.dwSize = sizeof(dsBuffer);
  403. dsBuffer.dwFlags = DSBCAPS_PRIMARYBUFFER | dwFlag3D;
  404. dsBuffer.dwBufferBytes = 0;
  405. dsBuffer.lpwfxFormat = NULL;
  406. hResult = sl.sl_pDS->CreateSoundBuffer( &dsBuffer, &sl.sl_pDSPrimary, NULL);
  407. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot create primary sound buffer.\n"));
  408. // set primary buffer format
  409. WAVEFORMATEX wfx = sl.sl_SwfeFormat;
  410. hResult = sl.sl_pDSPrimary->SetFormat(&wfx);
  411. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot set primary sound buffer format.\n"));
  412. // startup secondary sound buffer(s)
  413. SLONG slBufferSize = (SLONG)(ceil(snd_tmMixAhead*sl.sl_SwfeFormat.nSamplesPerSec) *
  414. sl.sl_SwfeFormat.wBitsPerSample/8 * sl.sl_SwfeFormat.nChannels);
  415. if( !DSInitSecondary( sl, sl.sl_pDSSecondary, slBufferSize)) return FALSE;
  416. // set some additionals for EAX
  417. if( snd_iInterface==2)
  418. {
  419. // 2nd secondary buffer
  420. if( !DSInitSecondary( sl, sl.sl_pDSSecondary2, slBufferSize)) return FALSE;
  421. // set 3D for all buffers
  422. HRESULT hr1,hr2,hr3,hr4;
  423. hr1 = sl.sl_pDSPrimary->QueryInterface( IID_IDirectSound3DListener, (LPVOID*)&sl.sl_pDSListener);
  424. hr2 = sl.sl_pDSSecondary->QueryInterface( IID_IDirectSound3DBuffer, (LPVOID*)&sl.sl_pDSSourceLeft);
  425. hr3 = sl.sl_pDSSecondary2->QueryInterface( IID_IDirectSound3DBuffer, (LPVOID*)&sl.sl_pDSSourceRight);
  426. if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D sound buffer.\n"));
  427. hr1 = sl.sl_pDSListener->SetPosition( 0,0,0, DS3D_DEFERRED);
  428. hr2 = sl.sl_pDSListener->SetOrientation( 0,0,1, 0,1,0, DS3D_DEFERRED);
  429. hr3 = sl.sl_pDSListener->SetRolloffFactor( 1, DS3D_DEFERRED);
  430. if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D parameters for listener.\n"));
  431. hr1 = sl.sl_pDSSourceLeft->SetMinDistance( MINPAN, DS3D_DEFERRED);
  432. hr2 = sl.sl_pDSSourceLeft->SetMaxDistance( MAXPAN, DS3D_DEFERRED);
  433. hr3 = sl.sl_pDSSourceRight->SetMinDistance( MINPAN, DS3D_DEFERRED);
  434. hr4 = sl.sl_pDSSourceRight->SetMaxDistance( MAXPAN, DS3D_DEFERRED);
  435. if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK || hr4!=DS_OK) {
  436. return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot set 3D parameters for sound source.\n"));
  437. }
  438. // apply
  439. hResult = sl.sl_pDSListener->CommitDeferredSettings();
  440. if( hResult!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound3D error: Cannot apply 3D parameters.\n"));
  441. // reset EAX parameters
  442. _fLastPanning = 1234;
  443. _iLastEnvType = 1234;
  444. _fLastEnvSize = 1234;
  445. // query property interface to EAX
  446. hResult = sl.sl_pDSSourceLeft->QueryInterface( IID_IKsPropertySet, (LPVOID*)&sl.sl_pKSProperty);
  447. if( hResult != DS_OK) return DSFail( sl, TRANS(" ! EAX error: Cannot set property interface.\n"));
  448. // query support
  449. ULONG ulSupport = 0;
  450. hResult = sl.sl_pKSProperty->QuerySupport( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, &ulSupport);
  451. if( hResult != DS_OK || !(ulSupport&KSPROPERTY_SUPPORT_SET)) return DSFail( sl, TRANS(" ! EAX error: Cannot query property support.\n"));
  452. hResult = sl.sl_pKSProperty->QuerySupport( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, &ulSupport);
  453. if( hResult != DS_OK || !(ulSupport&KSPROPERTY_SUPPORT_SET)) return DSFail( sl, TRANS(" ! EAX error: Cannot query property support.\n"));
  454. // made it - EAX's on!
  455. sl.sl_bUsingEAX = TRUE;
  456. }
  457. // mark that dsound is operative and set mixer buffer size (decoder buffer always works at 44khz)
  458. _iWriteOffset = 0;
  459. _iWriteOffset2 = 0;
  460. sl.sl_bUsingDirectSound = TRUE;
  461. sl.sl_slMixerBufferSize = slBufferSize;
  462. sl.sl_slDecodeBufferSize = sl.sl_slMixerBufferSize *
  463. ((44100+sl.sl_SwfeFormat.nSamplesPerSec-1) /sl.sl_SwfeFormat.nSamplesPerSec);
  464. // allocate mixing and decoding buffers
  465. sl.sl_pslMixerBuffer = (SLONG*)AllocMemory( sl.sl_slMixerBufferSize *2); // (*2 because of 32-bit buffer)
  466. sl.sl_pswDecodeBuffer = (SWORD*)AllocMemory( sl.sl_slDecodeBufferSize+4); // (+4 because of linear interpolation of last samples)
  467. // report success
  468. if( bReport) {
  469. CTString strDevice = TRANS("default device");
  470. if( snd_iDevice>=0) strDevice.PrintF( TRANS("device %d"), snd_iDevice);
  471. CPrintF( TRANS(" %dHz, %dbit, %s, mix-ahead: %gs\n"),
  472. sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, strDevice, snd_tmMixAhead);
  473. CPrintF(TRANS(" mixer buffer size: %d KB\n"), sl.sl_slMixerBufferSize /1024);
  474. CPrintF(TRANS(" decode buffer size: %d KB\n"), sl.sl_slDecodeBufferSize/1024);
  475. // EAX?
  476. CTString strEAX = TRANS("Disabled");
  477. if( sl.sl_bUsingEAX) strEAX = TRANS("Enabled");
  478. CPrintF( TRANS(" EAX: %s\n"), strEAX);
  479. }
  480. // done
  481. return TRUE;
  482. }
  483. // set WaveOut format (internal)
  484. static INDEX _ctChannelsOpened = 0;
  485. static BOOL StartUp_waveout( CSoundLibrary &sl, BOOL bReport=TRUE)
  486. {
  487. // not using DirectSound (obviously)
  488. sl.sl_bUsingDirectSound = FALSE;
  489. sl.sl_bUsingEAX = FALSE;
  490. if( bReport) CPrintF(TRANS("WaveOut initialization ...\n"));
  491. // set maximum total number of retries for device opening
  492. INDEX ctMaxRetries = snd_iMaxOpenRetries;
  493. _ctChannelsOpened = 0;
  494. MMRESULT res;
  495. // repeat
  496. FOREVER {
  497. // try to open wave device
  498. HWAVEOUT hwo;
  499. res = waveOutOpen( &hwo, (snd_iDevice<0)?WAVE_MAPPER:snd_iDevice, &sl.sl_SwfeFormat, NULL, NULL, NONE);
  500. // if opened
  501. if( res == MMSYSERR_NOERROR) {
  502. _ctChannelsOpened++;
  503. // if first one
  504. if (_ctChannelsOpened==1) {
  505. // remember as used waveout
  506. sl.sl_hwoWaveOut = hwo;
  507. // if extra channel
  508. } else {
  509. // remember under extra
  510. sl.sl_ahwoExtra.Push() = hwo;
  511. }
  512. // if no extra channels should be taken
  513. if (_ctChannelsOpened>=snd_iMaxExtraChannels+1) {
  514. // no more tries
  515. break;
  516. }
  517. // if cannot open
  518. } else {
  519. // decrement retry counter
  520. ctMaxRetries--;
  521. // if no more retries
  522. if (ctMaxRetries<0) {
  523. // quit trying
  524. break;
  525. // if more retries left
  526. } else {
  527. // wait a bit (probably sound-scheme is playing)
  528. Sleep(int(snd_tmOpenFailDelay*1000));
  529. }
  530. }
  531. }
  532. // if couldn't set format
  533. if( _ctChannelsOpened==0 && res != MMSYSERR_NOERROR) {
  534. // report error
  535. CTString strError;
  536. switch (res) {
  537. case MMSYSERR_ALLOCATED: strError = TRANS("Device already in use."); break;
  538. case MMSYSERR_BADDEVICEID: strError = TRANS("Bad device number."); break;
  539. case MMSYSERR_NODRIVER: strError = TRANS("No driver installed."); break;
  540. case MMSYSERR_NOMEM: strError = TRANS("Memory allocation problem."); break;
  541. case WAVERR_BADFORMAT: strError = TRANS("Unsupported data format."); break;
  542. case WAVERR_SYNC: strError = TRANS("Wrong flag?"); break;
  543. default: strError.PrintF( "%d", res);
  544. };
  545. CPrintF( TRANS(" ! WaveOut error: %s\n"), strError);
  546. return FALSE;
  547. }
  548. // get waveout capabilities
  549. WAVEOUTCAPS woc;
  550. memset( &woc, 0, sizeof(woc));
  551. res = waveOutGetDevCaps((int)sl.sl_hwoWaveOut, &woc, sizeof(woc));
  552. // report success
  553. if( bReport) {
  554. CTString strDevice = TRANS("default device");
  555. if( snd_iDevice>=0) strDevice.PrintF( TRANS("device %d"), snd_iDevice);
  556. CPrintF( TRANS(" opened device: %s\n"), woc.szPname);
  557. CPrintF( TRANS(" %dHz, %dbit, %s\n"),
  558. sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, strDevice);
  559. }
  560. // determine whole mixer buffer size from mixahead console variable
  561. sl.sl_slMixerBufferSize = (SLONG)(ceil(snd_tmMixAhead*sl.sl_SwfeFormat.nSamplesPerSec) *
  562. sl.sl_SwfeFormat.wBitsPerSample/8 * sl.sl_SwfeFormat.nChannels);
  563. // align size to be next multiply of WAVEOUTBLOCKSIZE
  564. sl.sl_slMixerBufferSize += WAVEOUTBLOCKSIZE - (sl.sl_slMixerBufferSize % WAVEOUTBLOCKSIZE);
  565. // determine number of WaveOut buffers
  566. const INDEX ctWOBuffers = sl.sl_slMixerBufferSize / WAVEOUTBLOCKSIZE;
  567. // decoder buffer always works at 44khz
  568. sl.sl_slDecodeBufferSize = sl.sl_slMixerBufferSize *
  569. ((44100+sl.sl_SwfeFormat.nSamplesPerSec-1)/sl.sl_SwfeFormat.nSamplesPerSec);
  570. if( bReport) {
  571. CPrintF(TRANS(" parameters: %d Hz, %d bit, stereo, mix-ahead: %gs\n"),
  572. sl.sl_SwfeFormat.nSamplesPerSec, sl.sl_SwfeFormat.wBitsPerSample, snd_tmMixAhead);
  573. CPrintF(TRANS(" output buffers: %d x %d bytes\n"), ctWOBuffers, WAVEOUTBLOCKSIZE),
  574. CPrintF(TRANS(" mpx decode: %d bytes\n"), sl.sl_slDecodeBufferSize),
  575. CPrintF(TRANS(" extra sound channels taken: %d\n"), _ctChannelsOpened-1);
  576. }
  577. // initialise waveout sound buffers
  578. sl.sl_pubBuffersMemory = (UBYTE*)AllocMemory( sl.sl_slMixerBufferSize);
  579. memset( sl.sl_pubBuffersMemory, 0, sl.sl_slMixerBufferSize);
  580. sl.sl_awhWOBuffers.New(ctWOBuffers);
  581. for( INDEX iBuffer = 0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++) {
  582. WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
  583. wh.lpData = (char*)(sl.sl_pubBuffersMemory + iBuffer*WAVEOUTBLOCKSIZE);
  584. wh.dwBufferLength = WAVEOUTBLOCKSIZE;
  585. wh.dwFlags = 0;
  586. }
  587. // initialize mixing and decoding buffer
  588. sl.sl_pslMixerBuffer = (SLONG*)AllocMemory( sl.sl_slMixerBufferSize *2); // (*2 because of 32-bit buffer)
  589. sl.sl_pswDecodeBuffer = (SWORD*)AllocMemory( sl.sl_slDecodeBufferSize+4); // (+4 because of linear interpolation of last samples)
  590. // done
  591. return TRUE;
  592. }
  593. /*
  594. * set sound format
  595. */
  596. static void SetFormat_internal( CSoundLibrary &sl, CSoundLibrary::SoundFormat EsfNew, BOOL bReport)
  597. {
  598. // access to the list of handlers must be locked
  599. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  600. // synchronize access to sounds
  601. CTSingleLock slSounds(&sl.sl_csSound, TRUE);
  602. // remember library format
  603. sl.sl_EsfFormat = EsfNew;
  604. // release library
  605. sl.ClearLibrary();
  606. // if none skip initialization
  607. _fLastNormalizeValue = 1;
  608. if( bReport) CPrintF(TRANS("Setting sound format ...\n"));
  609. if( sl.sl_EsfFormat == CSoundLibrary::SF_NONE) {
  610. if( bReport) CPrintF(TRANS(" (no sound)\n"));
  611. return;
  612. }
  613. // set wave format from library format
  614. SetWaveFormat( EsfNew, sl.sl_SwfeFormat);
  615. snd_iDevice = Clamp( snd_iDevice, -1L, (INDEX)(sl.sl_ctWaveDevices-1));
  616. snd_tmMixAhead = Clamp( snd_tmMixAhead, 0.1f, 0.9f);
  617. snd_iInterface = Clamp( snd_iInterface, 0L, 2L);
  618. BOOL bSoundOK = FALSE;
  619. if( snd_iInterface==2) {
  620. // if wanted, 1st try to set EAX
  621. bSoundOK = StartUp_dsound( sl, bReport);
  622. }
  623. if( !bSoundOK && snd_iInterface==1) {
  624. // if wanted, 2nd try to set DirectSound
  625. bSoundOK = StartUp_dsound( sl, bReport);
  626. }
  627. // if DirectSound failed or not wanted
  628. if( !bSoundOK) {
  629. // try waveout
  630. bSoundOK = StartUp_waveout( sl, bReport);
  631. snd_iInterface = 0; // mark that DirectSound didn't make it
  632. }
  633. // if didn't make it by now
  634. if( bReport) CPrintF("\n");
  635. if( !bSoundOK) {
  636. // revert to none in case sound init was unsuccessful
  637. sl.sl_EsfFormat = CSoundLibrary::SF_NONE;
  638. return;
  639. }
  640. // set library format from wave format
  641. SetLibraryFormat(sl);
  642. // add timer handler
  643. _pTimer->AddHandler(&sl.sl_thTimerHandler);
  644. }
  645. /*
  646. * Initialization
  647. */
  648. void CSoundLibrary::Init(void)
  649. {
  650. // access to the list of handlers must be locked
  651. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  652. // synchronize access to sounds
  653. CTSingleLock slSounds(&sl_csSound, TRUE);
  654. _pShell->DeclareSymbol( "void SndPostFunc(INDEX);", &SndPostFunc);
  655. _pShell->DeclareSymbol( " user INDEX snd_bMono;", &snd_bMono);
  656. _pShell->DeclareSymbol( "persistent user FLOAT snd_fEarsDistance;", &snd_fEarsDistance);
  657. _pShell->DeclareSymbol( "persistent user FLOAT snd_fDelaySoundSpeed;", &snd_fDelaySoundSpeed);
  658. _pShell->DeclareSymbol( "persistent user FLOAT snd_fDopplerSoundSpeed;", &snd_fDopplerSoundSpeed);
  659. _pShell->DeclareSymbol( "persistent user FLOAT snd_fPanStrength;", &snd_fPanStrength);
  660. _pShell->DeclareSymbol( "persistent user FLOAT snd_fLRFilter;", &snd_fLRFilter);
  661. _pShell->DeclareSymbol( "persistent user FLOAT snd_fBFilter;", &snd_fBFilter);
  662. _pShell->DeclareSymbol( "persistent user FLOAT snd_fUFilter;", &snd_fUFilter);
  663. _pShell->DeclareSymbol( "persistent user FLOAT snd_fDFilter;", &snd_fDFilter);
  664. _pShell->DeclareSymbol( "persistent user FLOAT snd_fSoundVolume;", &snd_fSoundVolume);
  665. _pShell->DeclareSymbol( "persistent user FLOAT snd_fMusicVolume;", &snd_fMusicVolume);
  666. _pShell->DeclareSymbol( "persistent user FLOAT snd_fNormalizer;", &snd_fNormalizer);
  667. _pShell->DeclareSymbol( "persistent user FLOAT snd_tmMixAhead post:SndPostFunc;", &snd_tmMixAhead);
  668. _pShell->DeclareSymbol( "persistent user INDEX snd_iInterface post:SndPostFunc;", &snd_iInterface);
  669. _pShell->DeclareSymbol( "persistent user INDEX snd_iDevice post:SndPostFunc;", &snd_iDevice);
  670. _pShell->DeclareSymbol( "persistent user INDEX snd_iFormat post:SndPostFunc;", &snd_iFormat);
  671. _pShell->DeclareSymbol( "persistent user INDEX snd_iMaxExtraChannels;", &snd_iMaxExtraChannels);
  672. _pShell->DeclareSymbol( "persistent user INDEX snd_iMaxOpenRetries;", &snd_iMaxOpenRetries);
  673. _pShell->DeclareSymbol( "persistent user FLOAT snd_tmOpenFailDelay;", &snd_tmOpenFailDelay);
  674. _pShell->DeclareSymbol( "persistent user FLOAT snd_fEAXPanning;", &snd_fEAXPanning);
  675. // print header
  676. CPrintF(TRANS("Initializing sound...\n"));
  677. // initialize sound library and set no-sound format
  678. SetFormat(SF_NONE);
  679. // initialize any installed sound decoders
  680. CSoundDecoder::InitPlugins();
  681. // get number of devices
  682. INDEX ctDevices = waveOutGetNumDevs();
  683. CPrintF(TRANS(" Detected devices: %d\n"), ctDevices);
  684. sl_ctWaveDevices = ctDevices;
  685. // for each device
  686. for(INDEX iDevice=0; iDevice<ctDevices; iDevice++) {
  687. // get description
  688. WAVEOUTCAPS woc;
  689. memset( &woc, 0, sizeof(woc));
  690. MMRESULT res = waveOutGetDevCaps(iDevice, &woc, sizeof(woc));
  691. CPrintF(TRANS(" device %d: %s\n"),
  692. iDevice, woc.szPname);
  693. CPrintF(TRANS(" ver: %d, id: %d.%d\n"),
  694. woc.vDriverVersion, woc.wMid, woc.wPid);
  695. CPrintF(TRANS(" form: 0x%08x, ch: %d, support: 0x%08x\n"),
  696. woc.dwFormats, woc.wChannels, woc.dwSupport);
  697. }
  698. // done
  699. CPrintF("\n");
  700. }
  701. /*
  702. * Clear Sound Library
  703. */
  704. void CSoundLibrary::Clear(void) {
  705. // access to the list of handlers must be locked
  706. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  707. // synchronize access to sounds
  708. CTSingleLock slSounds(&sl_csSound, TRUE);
  709. // clear all sounds and datas buffers
  710. {FOREACHINLIST(CSoundData, sd_Node, sl_ClhAwareList, itCsdStop) {
  711. FOREACHINLIST(CSoundObject, so_Node, (itCsdStop->sd_ClhLinkList), itCsoStop) {
  712. itCsoStop->Stop();
  713. }
  714. itCsdStop->ClearBuffer();
  715. }}
  716. // clear wave out data
  717. ClearLibrary();
  718. _fLastNormalizeValue = 1;
  719. }
  720. /* Clear Library WaveOut */
  721. void CSoundLibrary::ClearLibrary(void)
  722. {
  723. // access to the list of handlers must be locked
  724. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  725. // synchronize access to sounds
  726. CTSingleLock slSounds(&sl_csSound, TRUE);
  727. // remove timer handler if added
  728. if (sl_thTimerHandler.th_Node.IsLinked()) {
  729. _pTimer->RemHandler(&sl_thTimerHandler);
  730. }
  731. // shut down direct sound buffers (if needed)
  732. ShutDown_dsound(*this);
  733. // shut down wave out player buffers (if needed)
  734. if( sl_hwoWaveOut!=NULL)
  735. { // reset wave out play buffers (stop playing)
  736. MMRESULT res;
  737. res = waveOutReset(sl_hwoWaveOut);
  738. ASSERT(res == MMSYSERR_NOERROR);
  739. // clear buffers
  740. for( INDEX iBuffer = 0; iBuffer<sl_awhWOBuffers.Count(); iBuffer++) {
  741. res = waveOutUnprepareHeader( sl_hwoWaveOut, &sl_awhWOBuffers[iBuffer],
  742. sizeof(sl_awhWOBuffers[iBuffer]));
  743. ASSERT(res == MMSYSERR_NOERROR);
  744. }
  745. sl_awhWOBuffers.Clear();
  746. // close waveout device
  747. res = waveOutClose( sl_hwoWaveOut);
  748. ASSERT(res == MMSYSERR_NOERROR);
  749. sl_hwoWaveOut = NULL;
  750. }
  751. // for each extra taken channel
  752. for(INDEX iChannel=0; iChannel<sl_ahwoExtra.Count(); iChannel++) {
  753. // close its device
  754. MMRESULT res = waveOutClose( sl_ahwoExtra[iChannel]);
  755. ASSERT(res == MMSYSERR_NOERROR);
  756. }
  757. // free extra channel handles
  758. sl_ahwoExtra.PopAll();
  759. // free memory
  760. if( sl_pslMixerBuffer!=NULL) {
  761. FreeMemory( sl_pslMixerBuffer);
  762. sl_pslMixerBuffer = NULL;
  763. }
  764. if( sl_pswDecodeBuffer!=NULL) {
  765. FreeMemory( sl_pswDecodeBuffer);
  766. sl_pswDecodeBuffer = NULL;
  767. }
  768. if( sl_pubBuffersMemory!=NULL) {
  769. FreeMemory( sl_pubBuffersMemory);
  770. sl_pubBuffersMemory = NULL;
  771. }
  772. }
  773. // set listener enviroment properties (EAX)
  774. BOOL CSoundLibrary::SetEnvironment( INDEX iEnvNo, FLOAT fEnvSize/*=0*/)
  775. {
  776. if( !sl_bUsingEAX) return FALSE;
  777. // trim values
  778. if( iEnvNo<0 || iEnvNo>25) iEnvNo=1;
  779. if( fEnvSize<1 || fEnvSize>99) fEnvSize=8;
  780. HRESULT hResult;
  781. hResult = sl_pKSProperty->Set( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENT, NULL, 0, &iEnvNo, sizeof(DWORD));
  782. if( hResult != DS_OK) return DSFail( *this, TRANS(" ! EAX error: Cannot set environment.\n"));
  783. hResult = sl_pKSProperty->Set( DSPROPSETID_EAX_ListenerProperties, DSPROPERTY_EAXLISTENER_ENVIRONMENTSIZE, NULL, 0, &fEnvSize, sizeof(FLOAT));
  784. if( hResult != DS_OK) return DSFail( *this, TRANS(" ! EAX error: Cannot set environment size.\n"));
  785. return TRUE;
  786. }
  787. // mute all sounds (erase playing buffer(s) and supress mixer)
  788. void CSoundLibrary::Mute(void)
  789. {
  790. // stop all IFeel effects
  791. IFeel_StopEffect(NULL);
  792. // erase direct sound buffer (waveout will shut-up by itself), but skip if there's no more sound library
  793. if( this==NULL || !sl_bUsingDirectSound) return;
  794. // synchronize access to sounds
  795. CTSingleLock slSounds(&sl_csSound, TRUE);
  796. // supress future mixing and erase sound buffer
  797. _bMuted = TRUE;
  798. static LPVOID lpData;
  799. static DWORD dwSize;
  800. // flush one secondary buffer
  801. if( !DSLockBuffer( *this, sl_pDSSecondary, sl_slMixerBufferSize, lpData, dwSize)) return;
  802. memset( lpData, 0, dwSize);
  803. sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
  804. // if EAX is in use
  805. if( sl_bUsingEAX) {
  806. // flush right buffer, too
  807. if( !DSLockBuffer( *this, sl_pDSSecondary2, sl_slMixerBufferSize, lpData, dwSize)) return;
  808. memset( lpData, 0, dwSize);
  809. sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
  810. }
  811. }
  812. /*
  813. * set sound format
  814. */
  815. CSoundLibrary::SoundFormat CSoundLibrary::SetFormat( CSoundLibrary::SoundFormat EsfNew, BOOL bReport/*=FALSE*/)
  816. {
  817. // access to the list of handlers must be locked
  818. CTSingleLock slHooks(&_pTimer->tm_csHooks, TRUE);
  819. // synchronize access to sounds
  820. CTSingleLock slSounds(&sl_csSound, TRUE);
  821. // pause playing all sounds
  822. {FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdStop) {
  823. itCsdStop->PausePlayingObjects();
  824. }}
  825. // change format and keep console variable states
  826. SetFormat_internal( *this, EsfNew, bReport);
  827. _tmLastMixAhead = snd_tmMixAhead;
  828. _iLastFormat = snd_iFormat;
  829. _iLastDevice = snd_iDevice;
  830. _iLastAPI = snd_iInterface;
  831. // continue playing all sounds
  832. CListHead lhToReload;
  833. lhToReload.MoveList(sl_ClhAwareList);
  834. {FORDELETELIST( CSoundData, sd_Node, lhToReload, itCsdContinue) {
  835. CSoundData &sd = *itCsdContinue;
  836. if( !(sd.sd_ulFlags&SDF_ENCODED)) {
  837. sd.Reload();
  838. } else {
  839. sd.sd_Node.Remove();
  840. sl_ClhAwareList.AddTail(sd.sd_Node);
  841. }
  842. sd.ResumePlayingObjects();
  843. }}
  844. // done
  845. return sl_EsfFormat;
  846. }
  847. /* Update all 3d effects and copy internal data. */
  848. void CSoundLibrary::UpdateSounds(void)
  849. {
  850. // see if we have valid handle for direct sound and eventually reinit sound
  851. if( sl_bUsingDirectSound && _hwndCurrent!=_hwndMain) {
  852. _hwndCurrent = _hwndMain;
  853. SetFormat( sl_EsfFormat);
  854. }
  855. _bMuted = FALSE; // enable mixer
  856. _sfStats.StartTimer(CStatForm::STI_SOUNDUPDATE);
  857. _pfSoundProfile.StartTimer(CSoundProfile::PTI_UPDATESOUNDS);
  858. // synchronize access to sounds
  859. CTSingleLock slSounds( &sl_csSound, TRUE);
  860. // make sure that the buffers are playing
  861. if( sl_bUsingDirectSound) DSPlayBuffers(*this);
  862. // determine number of listeners and get listener
  863. INDEX ctListeners=0;
  864. CSoundListener *sli;
  865. {FOREACHINLIST( CSoundListener, sli_lnInActiveListeners, _pSound->sl_lhActiveListeners, itsli) {
  866. sli = itsli;
  867. ctListeners++;
  868. }}
  869. // if there's only one listener environment properties have been changed (in split-screen EAX is not supported)
  870. if( ctListeners==1 && (_iLastEnvType!=sli->sli_iEnvironmentType || _fLastEnvSize!=sli->sli_fEnvironmentSize)) {
  871. // keep new properties and eventually update environment (EAX)
  872. _iLastEnvType = sli->sli_iEnvironmentType;
  873. _fLastEnvSize = sli->sli_fEnvironmentSize;
  874. SetEnvironment( _iLastEnvType, _fLastEnvSize);
  875. }
  876. // if there are no listeners - reset environment properties
  877. if( ctListeners<1 && (_iLastEnvType!=1 || _fLastEnvSize!=1.4f)) {
  878. // keep new properties and update environment
  879. _iLastEnvType = 1;
  880. _fLastEnvSize = 1.4f;
  881. SetEnvironment( _iLastEnvType, _fLastEnvSize);
  882. }
  883. // adjust panning if needed
  884. snd_fEAXPanning = Clamp( snd_fEAXPanning, -1.0f, +1.0f);
  885. if( sl_bUsingEAX && _fLastPanning!=snd_fEAXPanning)
  886. { // determine new panning
  887. _fLastPanning = snd_fEAXPanning;
  888. FLOAT fPanLeft = -1.0f;
  889. FLOAT fPanRight = +1.0f;
  890. if( snd_fEAXPanning<0) fPanRight = MINPAN + Abs(snd_fEAXPanning)*MAXPAN; // pan left
  891. if( snd_fEAXPanning>0) fPanLeft = MINPAN + Abs(snd_fEAXPanning)*MAXPAN; // pan right
  892. // set and apply
  893. HRESULT hr1,hr2,hr3;
  894. hr1 = sl_pDSSourceLeft->SetPosition( fPanLeft, 0,0, DS3D_DEFERRED);
  895. hr2 = sl_pDSSourceRight->SetPosition( fPanRight,0,0, DS3D_DEFERRED);
  896. hr3 = sl_pDSListener->CommitDeferredSettings();
  897. if( hr1!=DS_OK || hr2!=DS_OK || hr3!=DS_OK) DSFail( *this, TRANS(" ! DirectSound3D error: Cannot set 3D position.\n"));
  898. }
  899. // for each sound
  900. {FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
  901. FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
  902. _sfStats.IncrementCounter(CStatForm::SCI_SOUNDSACTIVE);
  903. itCsoSoundObject->Update3DEffects();
  904. }
  905. }}
  906. // for each sound
  907. {FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
  908. FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
  909. CSoundObject &so = *itCsoSoundObject;
  910. // if sound is playing
  911. if( so.so_slFlags&SOF_PLAY) {
  912. // copy parameters
  913. so.so_sp = so.so_spNew;
  914. // prepare sound if not prepared already
  915. if ( !(so.so_slFlags&SOF_PREPARE)) {
  916. so.PrepareSound();
  917. so.so_slFlags |= SOF_PREPARE;
  918. }
  919. // if it is not playing
  920. } else {
  921. // remove it from list
  922. so.so_Node.Remove();
  923. }
  924. }
  925. }}
  926. // remove all listeners
  927. {FORDELETELIST( CSoundListener, sli_lnInActiveListeners, sl_lhActiveListeners, itsli) {
  928. itsli->sli_lnInActiveListeners.Remove();
  929. }}
  930. _pfSoundProfile.StopTimer(CSoundProfile::PTI_UPDATESOUNDS);
  931. _sfStats.StopTimer(CStatForm::STI_SOUNDUPDATE);
  932. }
  933. /*
  934. * This is called every TickQuantum seconds.
  935. */
  936. void CSoundTimerHandler::HandleTimer(void)
  937. {
  938. /* memory leak checking routines
  939. ASSERT( _CrtCheckMemory());
  940. ASSERT( _CrtIsMemoryBlock( (void*)_pSound->sl_pswDecodeBuffer,
  941. (ULONG)_pSound->sl_slDecodeBufferSize, NULL, NULL, NULL));
  942. ASSERT( _CrtIsValidPointer( (void*)_pSound->sl_pswDecodeBuffer,
  943. (ULONG)_pSound->sl_slDecodeBufferSize, TRUE)); */
  944. // mix all needed sounds
  945. _pSound->MixSounds();
  946. }
  947. /*
  948. * MIXER helper functions
  949. */
  950. // copying of mixer buffer to sound buffer(s)
  951. static LPVOID _lpData, _lpData2;
  952. static DWORD _dwSize, _dwSize2;
  953. static void CopyMixerBuffer_dsound( CSoundLibrary &sl, SLONG slMixedSize)
  954. {
  955. LPVOID lpData;
  956. DWORD dwSize;
  957. SLONG slPart1Size, slPart2Size;
  958. // if EAX is in use
  959. if( sl.sl_bUsingEAX)
  960. {
  961. // lock left buffer and copy first part of 1st mono block
  962. if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
  963. slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset, slMixedSize);
  964. CopyMixerBuffer_mono( 0, ((UBYTE*)lpData)+_iWriteOffset/2, slPart1Size);
  965. // copy second part of 1st mono block
  966. slPart2Size = slMixedSize - slPart1Size;
  967. CopyMixerBuffer_mono( slPart1Size, lpData, slPart2Size);
  968. _iWriteOffset += slMixedSize;
  969. if( _iWriteOffset>=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
  970. ASSERT( _iWriteOffset>=0 && _iWriteOffset<sl.sl_slMixerBufferSize);
  971. sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
  972. // lock right buffer and copy first part of 2nd mono block
  973. if( !DSLockBuffer( sl, sl.sl_pDSSecondary2, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
  974. slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset2, slMixedSize);
  975. CopyMixerBuffer_mono( 2, ((UBYTE*)lpData)+_iWriteOffset2/2, slPart1Size);
  976. // copy second part of 2nd mono block
  977. slPart2Size = slMixedSize - slPart1Size;
  978. CopyMixerBuffer_mono( slPart1Size+2, lpData, slPart2Size);
  979. _iWriteOffset2 += slMixedSize;
  980. if( _iWriteOffset2>=sl.sl_slMixerBufferSize) _iWriteOffset2 -= sl.sl_slMixerBufferSize;
  981. ASSERT( _iWriteOffset2>=0 && _iWriteOffset2<sl.sl_slMixerBufferSize);
  982. sl.sl_pDSSecondary2->Unlock( lpData, dwSize, NULL, 0);
  983. }
  984. // if only standard DSound (no EAX)
  985. else
  986. {
  987. // lock stereo buffer and copy first part of block
  988. if( !DSLockBuffer( sl, sl.sl_pDSSecondary, sl.sl_slMixerBufferSize, lpData, dwSize)) return;
  989. slPart1Size = Min( sl.sl_slMixerBufferSize-_iWriteOffset, slMixedSize);
  990. CopyMixerBuffer_stereo( 0, ((UBYTE*)lpData)+_iWriteOffset, slPart1Size);
  991. // copy second part of block
  992. slPart2Size = slMixedSize - slPart1Size;
  993. CopyMixerBuffer_stereo( slPart1Size, lpData, slPart2Size);
  994. _iWriteOffset += slMixedSize;
  995. if( _iWriteOffset>=sl.sl_slMixerBufferSize) _iWriteOffset -= sl.sl_slMixerBufferSize;
  996. ASSERT( _iWriteOffset>=0 && _iWriteOffset<sl.sl_slMixerBufferSize);
  997. sl.sl_pDSSecondary->Unlock( lpData, dwSize, NULL, 0);
  998. }
  999. }
  1000. static void CopyMixerBuffer_waveout( CSoundLibrary &sl)
  1001. {
  1002. MMRESULT res;
  1003. SLONG slOffset = 0;
  1004. for( INDEX iBuffer = 0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++)
  1005. { // skip prepared buffer
  1006. WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
  1007. if( wh.dwFlags&WHDR_PREPARED) continue;
  1008. // copy part of a mixer buffer to wave buffer
  1009. CopyMixerBuffer_stereo( slOffset, wh.lpData, WAVEOUTBLOCKSIZE);
  1010. slOffset += WAVEOUTBLOCKSIZE;
  1011. // write wave buffer (ready for playing)
  1012. res = waveOutPrepareHeader( sl.sl_hwoWaveOut, &wh, sizeof(wh));
  1013. ASSERT( res==MMSYSERR_NOERROR);
  1014. res = waveOutWrite( sl.sl_hwoWaveOut, &wh, sizeof(wh));
  1015. ASSERT( res==MMSYSERR_NOERROR);
  1016. }
  1017. }
  1018. // finds room in sound buffer to copy in next crop of samples
  1019. static SLONG PrepareSoundBuffer_dsound( CSoundLibrary &sl)
  1020. {
  1021. // determine writable block size (difference between write and play pointers)
  1022. HRESULT hr1,hr2;
  1023. DWORD dwCurrentCursor, dwCurrentCursor2;
  1024. SLONG slDataToMix;
  1025. ASSERT( sl.sl_pDSSecondary!=NULL && sl.sl_pDSPrimary!=NULL);
  1026. // if EAX is in use
  1027. if( sl.sl_bUsingEAX)
  1028. {
  1029. hr1 = sl.sl_pDSSecondary->GetCurrentPosition( &dwCurrentCursor, NULL);
  1030. hr2 = sl.sl_pDSSecondary2->GetCurrentPosition( &dwCurrentCursor2, NULL);
  1031. if( hr1!=DS_OK || hr2!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot obtain sound buffer write position.\n"));
  1032. dwCurrentCursor *=2; // stereo mixer
  1033. dwCurrentCursor2*=2; // stereo mixer
  1034. // store pointers and wrapped block sizes
  1035. SLONG slDataToMix1 = dwCurrentCursor - _iWriteOffset;
  1036. if( slDataToMix1<0) slDataToMix1 += sl.sl_slMixerBufferSize;
  1037. ASSERT( slDataToMix1>=0 && slDataToMix1<=sl.sl_slMixerBufferSize);
  1038. slDataToMix1 = Min( slDataToMix1, sl.sl_slMixerBufferSize);
  1039. SLONG slDataToMix2 = dwCurrentCursor2 - _iWriteOffset2;
  1040. if( slDataToMix2<0) slDataToMix2 += sl.sl_slMixerBufferSize;
  1041. ASSERT( slDataToMix2>=0 && slDataToMix2<=sl.sl_slMixerBufferSize);
  1042. slDataToMix = Min( slDataToMix1, slDataToMix2);
  1043. }
  1044. // if only standard DSound (no EAX)
  1045. else
  1046. {
  1047. hr1 = sl.sl_pDSSecondary->GetCurrentPosition( &dwCurrentCursor, NULL);
  1048. if( hr1!=DS_OK) return DSFail( sl, TRANS(" ! DirectSound error: Cannot obtain sound buffer write position.\n"));
  1049. // store pointer and wrapped block size
  1050. slDataToMix = dwCurrentCursor - _iWriteOffset;
  1051. if( slDataToMix<0) slDataToMix += sl.sl_slMixerBufferSize;
  1052. ASSERT( slDataToMix>=0 && slDataToMix<=sl.sl_slMixerBufferSize);
  1053. slDataToMix = Min( slDataToMix, sl.sl_slMixerBufferSize);
  1054. }
  1055. // done
  1056. //CPrintF( "LP/LW: %5d / %5d, RP/RW: %5d / %5d, MIX: %5d\n", dwCurrentCursor, _iWriteOffset, dwCurrentCursor2, _iWriteOffset2, slDataToMix); // grgr
  1057. return slDataToMix;
  1058. }
  1059. static SLONG PrepareSoundBuffer_waveout( CSoundLibrary &sl)
  1060. {
  1061. // scan waveout buffers to find all that are ready to receive sound data (i.e. not playing)
  1062. SLONG slDataToMix=0;
  1063. for( INDEX iBuffer=0; iBuffer<sl.sl_awhWOBuffers.Count(); iBuffer++)
  1064. { // if done playing
  1065. WAVEHDR &wh = sl.sl_awhWOBuffers[iBuffer];
  1066. if( wh.dwFlags&WHDR_DONE) {
  1067. // unprepare buffer
  1068. MMRESULT res = waveOutUnprepareHeader( sl.sl_hwoWaveOut, &wh, sizeof(wh));
  1069. ASSERT( res == MMSYSERR_NOERROR);
  1070. }
  1071. // if unprepared
  1072. if( !(wh.dwFlags&WHDR_PREPARED)) {
  1073. // increase mix-in data size
  1074. slDataToMix += WAVEOUTBLOCKSIZE;
  1075. }
  1076. }
  1077. // done
  1078. ASSERT( slDataToMix <= sl.sl_slMixerBufferSize);
  1079. return slDataToMix;
  1080. }
  1081. /* Update Mixer */
  1082. void CSoundLibrary::MixSounds(void)
  1083. {
  1084. // synchronize access to sounds
  1085. CTSingleLock slSounds( &sl_csSound, TRUE);
  1086. // do nothing if no sound
  1087. if( sl_EsfFormat==SF_NONE || _bMuted) return;
  1088. _sfStats.StartTimer(CStatForm::STI_SOUNDMIXING);
  1089. _pfSoundProfile.IncrementAveragingCounter();
  1090. _pfSoundProfile.StartTimer(CSoundProfile::PTI_MIXSOUNDS);
  1091. // seek available buffer(s) for next crop of samples
  1092. SLONG slDataToMix;
  1093. if( sl_bUsingDirectSound) { // using direct sound
  1094. slDataToMix = PrepareSoundBuffer_dsound( *this);
  1095. } else { // using wave out
  1096. slDataToMix = PrepareSoundBuffer_waveout(*this);
  1097. }
  1098. // skip mixing if all sound buffers are still busy playing
  1099. ASSERT( slDataToMix>=0);
  1100. if( slDataToMix<=0) {
  1101. _pfSoundProfile.StopTimer(CSoundProfile::PTI_MIXSOUNDS);
  1102. _sfStats.StopTimer(CStatForm::STI_SOUNDMIXING);
  1103. return;
  1104. }
  1105. // prepare mixer buffer
  1106. _pfSoundProfile.IncrementCounter(CSoundProfile::PCI_MIXINGS, 1);
  1107. ResetMixer( sl_pslMixerBuffer, slDataToMix);
  1108. BOOL bGamePaused = _pNetwork->IsPaused() || _pNetwork->IsServer() && _pNetwork->GetLocalPause();
  1109. // for each sound
  1110. FOREACHINLIST( CSoundData, sd_Node, sl_ClhAwareList, itCsdSoundData) {
  1111. FORDELETELIST( CSoundObject, so_Node, itCsdSoundData->sd_ClhLinkList, itCsoSoundObject) {
  1112. CSoundObject &so = *itCsoSoundObject;
  1113. // if the sound is in-game sound, and the game paused
  1114. if (!(so.so_slFlags&SOF_NONGAME) && bGamePaused) {
  1115. // don't mix it it
  1116. continue;
  1117. }
  1118. // if sound is prepared and playing
  1119. if( so.so_slFlags&SOF_PLAY &&
  1120. so.so_slFlags&SOF_PREPARE &&
  1121. !(so.so_slFlags&SOF_PAUSED)) {
  1122. // mix it
  1123. MixSound(&so);
  1124. }
  1125. }
  1126. }
  1127. // eventually normalize mixed sounds
  1128. snd_fNormalizer = Clamp( snd_fNormalizer, 0.0f, 1.0f);
  1129. NormalizeMixerBuffer( snd_fNormalizer, slDataToMix, _fLastNormalizeValue);
  1130. // write mixer buffer to file
  1131. // if( !_bOpened) _filMixerBuffer = fopen( "d:\\MixerBufferDump.raw", "wb");
  1132. // fwrite( (void*)sl_pslMixerBuffer, 1, slDataToMix, _filMixerBuffer);
  1133. // _bOpened = TRUE;
  1134. // copy mixer buffer to buffers buffer(s)
  1135. if( sl_bUsingDirectSound) { // using direct sound
  1136. CopyMixerBuffer_dsound( *this, slDataToMix);
  1137. } else { // using wave out
  1138. CopyMixerBuffer_waveout(*this);
  1139. }
  1140. // all done
  1141. _pfSoundProfile.StopTimer(CSoundProfile::PTI_MIXSOUNDS);
  1142. _sfStats.StopTimer(CStatForm::STI_SOUNDMIXING);
  1143. }
  1144. //
  1145. // Sound mode awareness functions
  1146. //
  1147. /*
  1148. * Add sound in sound aware list
  1149. */
  1150. void CSoundLibrary::AddSoundAware(CSoundData &CsdAdd) {
  1151. // add sound to list tail
  1152. sl_ClhAwareList.AddTail(CsdAdd.sd_Node);
  1153. };
  1154. /*
  1155. * Remove a display mode aware object.
  1156. */
  1157. void CSoundLibrary::RemoveSoundAware(CSoundData &CsdRemove) {
  1158. // remove it from list
  1159. CsdRemove.sd_Node.Remove();
  1160. };
  1161. // listen from this listener this frame
  1162. void CSoundLibrary::Listen(CSoundListener &sl)
  1163. {
  1164. // just add it to list
  1165. if (sl.sli_lnInActiveListeners.IsLinked()) {
  1166. sl.sli_lnInActiveListeners.Remove();
  1167. }
  1168. sl_lhActiveListeners.AddTail(sl.sli_lnInActiveListeners);
  1169. }