snd_system.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #pragma hdrstop
  21. #include "../idlib/precompiled.h"
  22. #include "snd_local.h"
  23. idCVar s_noSound( "s_noSound", "0", CVAR_BOOL, "returns NULL for all sounds loaded and does not update the sound rendering" );
  24. #ifdef ID_RETAIL
  25. idCVar s_useCompression( "s_useCompression", "1", CVAR_BOOL, "Use compressed sound files (mp3/xma)" );
  26. idCVar s_playDefaultSound( "s_playDefaultSound", "0", CVAR_BOOL, "play a beep for missing sounds" );
  27. idCVar s_maxSamples( "s_maxSamples", "5", CVAR_INTEGER, "max samples to load per shader" );
  28. #else
  29. idCVar s_useCompression( "s_useCompression", "1", CVAR_BOOL, "Use compressed sound files (mp3/xma)" );
  30. idCVar s_playDefaultSound( "s_playDefaultSound", "1", CVAR_BOOL, "play a beep for missing sounds" );
  31. idCVar s_maxSamples( "s_maxSamples", "5", CVAR_INTEGER, "max samples to load per shader" );
  32. #endif
  33. idCVar preLoad_Samples( "preLoad_Samples", "1", CVAR_SYSTEM | CVAR_BOOL, "preload samples during beginlevelload" );
  34. idSoundSystemLocal soundSystemLocal;
  35. idSoundSystem * soundSystem = &soundSystemLocal;
  36. /*
  37. ================================================================================================
  38. idSoundSystemLocal
  39. ================================================================================================
  40. */
  41. /*
  42. ========================
  43. TestSound_f
  44. This is called from the main thread.
  45. ========================
  46. */
  47. void TestSound_f( const idCmdArgs & args ) {
  48. if ( args.Argc() != 2 ) {
  49. idLib::Printf( "Usage: testSound <file>\n" );
  50. return;
  51. }
  52. if ( soundSystemLocal.currentSoundWorld ) {
  53. soundSystemLocal.currentSoundWorld->PlayShaderDirectly( args.Argv( 1 ) );
  54. }
  55. }
  56. /*
  57. ========================
  58. RestartSound_f
  59. ========================
  60. */
  61. void RestartSound_f( const idCmdArgs & args ) {
  62. soundSystemLocal.Restart();
  63. }
  64. /*
  65. ========================
  66. ListSamples_f
  67. ========================
  68. */
  69. void ListSamples_f( const idCmdArgs & args ) {
  70. idLib::Printf( "Sound samples\n-------------\n" );
  71. int totSize = 0;
  72. for ( int i = 0; i < soundSystemLocal.samples.Num(); i++ ) {
  73. idLib::Printf( "%05dkb\t%s\n", soundSystemLocal.samples[ i ]->BufferSize() / 1024, soundSystemLocal.samples[ i ]->GetName() );
  74. totSize += soundSystemLocal.samples[ i ]->BufferSize();
  75. }
  76. idLib::Printf( "--------------------------\n" );
  77. idLib::Printf( "%05dkb total size\n", totSize / 1024 );
  78. }
  79. /*
  80. ========================
  81. idSoundSystemLocal::Restart
  82. ========================
  83. */
  84. void idSoundSystemLocal::Restart() {
  85. // Mute all channels in all worlds
  86. for ( int i = 0; i < soundWorlds.Num(); i++ ) {
  87. idSoundWorldLocal * sw = soundWorlds[i];
  88. for ( int e = 0; e < sw->emitters.Num(); e++ ) {
  89. idSoundEmitterLocal * emitter = sw->emitters[e];
  90. for ( int c = 0; c < emitter->channels.Num(); c++ ) {
  91. emitter->channels[c]->Mute();
  92. }
  93. }
  94. }
  95. // Shutdown sound hardware
  96. hardware.Shutdown();
  97. // Reinitialize sound hardware
  98. if ( !s_noSound.GetBool() ) {
  99. hardware.Init();
  100. }
  101. InitStreamBuffers();
  102. }
  103. /*
  104. ========================
  105. idSoundSystemLocal::Init
  106. Initialize the SoundSystem.
  107. ========================
  108. */
  109. void idSoundSystemLocal::Init() {
  110. idLib::Printf( "----- Initializing Sound System ------\n" );
  111. soundTime = Sys_Milliseconds();
  112. random.SetSeed( soundTime );
  113. if ( !s_noSound.GetBool() ) {
  114. hardware.Init();
  115. InitStreamBuffers();
  116. }
  117. cmdSystem->AddCommand( "testSound", TestSound_f, 0, "tests a sound", idCmdSystem::ArgCompletion_SoundName );
  118. cmdSystem->AddCommand( "s_restart", RestartSound_f, 0, "restart sound system" );
  119. cmdSystem->AddCommand( "listSamples", ListSamples_f, 0, "lists all loaded sound samples" );
  120. idLib::Printf( "sound system initialized.\n" );
  121. idLib::Printf( "--------------------------------------\n" );
  122. }
  123. /*
  124. ========================
  125. idSoundSystemLocal::InitStreamBuffers
  126. ========================
  127. */
  128. void idSoundSystemLocal::InitStreamBuffers() {
  129. streamBufferMutex.Lock();
  130. const bool empty = ( bufferContexts.Num() == 0 );
  131. if ( empty ) {
  132. bufferContexts.SetNum( MAX_SOUND_BUFFERS );
  133. for ( int i = 0; i < MAX_SOUND_BUFFERS; i++ ) {
  134. freeStreamBufferContexts.Append( &( bufferContexts[ i ] ) );
  135. }
  136. } else {
  137. for ( int i = 0; i < activeStreamBufferContexts.Num(); i++ ) {
  138. freeStreamBufferContexts.Append( activeStreamBufferContexts[ i ] );
  139. }
  140. activeStreamBufferContexts.Clear();
  141. }
  142. assert( bufferContexts.Num() == MAX_SOUND_BUFFERS );
  143. assert( freeStreamBufferContexts.Num() == MAX_SOUND_BUFFERS );
  144. assert( activeStreamBufferContexts.Num() == 0 );
  145. streamBufferMutex.Unlock();
  146. }
  147. /*
  148. ========================
  149. idSoundSystemLocal::FreeStreamBuffers
  150. ========================
  151. */
  152. void idSoundSystemLocal::FreeStreamBuffers() {
  153. streamBufferMutex.Lock();
  154. bufferContexts.Clear();
  155. freeStreamBufferContexts.Clear();
  156. activeStreamBufferContexts.Clear();
  157. streamBufferMutex.Unlock();
  158. }
  159. /*
  160. ========================
  161. idSoundSystemLocal::Shutdown
  162. ========================
  163. */
  164. void idSoundSystemLocal::Shutdown() {
  165. hardware.Shutdown();
  166. FreeStreamBuffers();
  167. samples.DeleteContents( true );
  168. sampleHash.Free();
  169. }
  170. /*
  171. ========================
  172. idSoundSystemLocal::ObtainStreamBuffer
  173. Get a stream buffer from the free pool, returns NULL if none are available
  174. ========================
  175. */
  176. idSoundSystemLocal::bufferContext_t * idSoundSystemLocal::ObtainStreamBufferContext() {
  177. bufferContext_t * bufferContext = NULL;
  178. streamBufferMutex.Lock();
  179. if ( freeStreamBufferContexts.Num() != 0 ) {
  180. bufferContext = freeStreamBufferContexts[ freeStreamBufferContexts.Num() - 1 ];
  181. freeStreamBufferContexts.SetNum( freeStreamBufferContexts.Num() - 1 );
  182. activeStreamBufferContexts.Append( bufferContext );
  183. }
  184. streamBufferMutex.Unlock();
  185. return bufferContext;
  186. }
  187. /*
  188. ========================
  189. idSoundSystemLocal::ReleaseStreamBuffer
  190. Releases a stream buffer back to the free pool
  191. ========================
  192. */
  193. void idSoundSystemLocal::ReleaseStreamBufferContext( bufferContext_t * bufferContext ) {
  194. streamBufferMutex.Lock();
  195. if ( activeStreamBufferContexts.Remove( bufferContext ) ) {
  196. freeStreamBufferContexts.Append( bufferContext );
  197. }
  198. streamBufferMutex.Unlock();
  199. }
  200. /*
  201. ========================
  202. idSoundSystemLocal::AllocSoundWorld
  203. ========================
  204. */
  205. idSoundWorld * idSoundSystemLocal::AllocSoundWorld( idRenderWorld *rw ) {
  206. idSoundWorldLocal * local = new (TAG_AUDIO) idSoundWorldLocal;
  207. local->renderWorld = rw;
  208. soundWorlds.Append( local );
  209. return local;
  210. }
  211. /*
  212. ========================
  213. idSoundSystemLocal::FreeSoundWorld
  214. ========================
  215. */
  216. void idSoundSystemLocal::FreeSoundWorld( idSoundWorld *sw ) {
  217. idSoundWorldLocal *local = static_cast<idSoundWorldLocal*>( sw );
  218. soundWorlds.Remove( local );
  219. delete local;
  220. }
  221. /*
  222. ========================
  223. idSoundSystemLocal::SetPlayingSoundWorld
  224. Specifying NULL will cause silence to be played.
  225. ========================
  226. */
  227. void idSoundSystemLocal::SetPlayingSoundWorld( idSoundWorld *soundWorld ) {
  228. if ( currentSoundWorld == soundWorld ) {
  229. return;
  230. }
  231. idSoundWorldLocal * oldSoundWorld = currentSoundWorld;
  232. currentSoundWorld = static_cast<idSoundWorldLocal *>( soundWorld );
  233. if ( oldSoundWorld != NULL ) {
  234. oldSoundWorld->Update();
  235. }
  236. }
  237. /*
  238. ========================
  239. idSoundSystemLocal::GetPlayingSoundWorld
  240. ========================
  241. */
  242. idSoundWorld * idSoundSystemLocal::GetPlayingSoundWorld() {
  243. return currentSoundWorld;
  244. }
  245. /*
  246. ========================
  247. idSoundSystemLocal::Render
  248. ========================
  249. */
  250. void idSoundSystemLocal::Render() {
  251. if ( s_noSound.GetBool() ) {
  252. return;
  253. }
  254. if ( needsRestart ) {
  255. needsRestart = false;
  256. Restart();
  257. }
  258. SCOPED_PROFILE_EVENT( "SoundSystem::Render" );
  259. if ( currentSoundWorld != NULL ) {
  260. currentSoundWorld->Update();
  261. }
  262. hardware.Update();
  263. // The sound system doesn't use game time or anything like that because the sounds are decoded in real time.
  264. soundTime = Sys_Milliseconds();
  265. }
  266. /*
  267. ========================
  268. idSoundSystemLocal::OnReloadSound
  269. ========================
  270. */
  271. void idSoundSystemLocal::OnReloadSound( const idDecl* sound ) {
  272. for ( int i = 0; i < soundWorlds.Num(); i++ ) {
  273. soundWorlds[i]->OnReloadSound( sound );
  274. }
  275. }
  276. /*
  277. ========================
  278. idSoundSystemLocal::StopAllSounds
  279. ========================
  280. */
  281. void idSoundSystemLocal::StopAllSounds() {
  282. for ( int i = 0; i < soundWorlds.Num(); i++ ) {
  283. idSoundWorld *sw = soundWorlds[i];
  284. if ( sw ) {
  285. sw->StopAllSounds();
  286. }
  287. }
  288. hardware.Update();
  289. }
  290. /*
  291. ========================
  292. idSoundSystemLocal::GetIXAudio2
  293. ========================
  294. */
  295. void * idSoundSystemLocal::GetIXAudio2() const {
  296. return (void *)hardware.GetIXAudio2();
  297. }
  298. /*
  299. ========================
  300. idSoundSystemLocal::SoundTime
  301. ========================
  302. */
  303. int idSoundSystemLocal::SoundTime() const {
  304. return soundTime;
  305. }
  306. /*
  307. ========================
  308. idSoundSystemLocal::AllocateVoice
  309. ========================
  310. */
  311. idSoundVoice * idSoundSystemLocal::AllocateVoice( const idSoundSample * leadinSample, const idSoundSample * loopingSample ) {
  312. return hardware.AllocateVoice( leadinSample, loopingSample );
  313. }
  314. /*
  315. ========================
  316. idSoundSystemLocal::FreeVoice
  317. ========================
  318. */
  319. void idSoundSystemLocal::FreeVoice( idSoundVoice * voice ) {
  320. hardware.FreeVoice( voice );
  321. }
  322. /*
  323. ========================
  324. idSoundSystemLocal::LoadSample
  325. ========================
  326. */
  327. idSoundSample * idSoundSystemLocal::LoadSample( const char * name ) {
  328. idStrStatic< MAX_OSPATH > canonical = name;
  329. canonical.ToLower();
  330. canonical.BackSlashesToSlashes();
  331. canonical.StripFileExtension();
  332. int hashKey = idStr::Hash( canonical );
  333. for ( int i = sampleHash.First( hashKey ); i != -1; i = sampleHash.Next( i ) ) {
  334. if ( idStr::Cmp( samples[i]->GetName(), canonical ) == 0 ) {
  335. samples[i]->SetLevelLoadReferenced();
  336. return samples[i];
  337. }
  338. }
  339. idSoundSample * sample = new (TAG_AUDIO) idSoundSample;
  340. sample->SetName( canonical );
  341. sampleHash.Add( hashKey, samples.Append( sample ) );
  342. if ( !insideLevelLoad ) {
  343. // Sound sample referenced before any map is loaded
  344. sample->SetNeverPurge();
  345. sample->LoadResource();
  346. } else {
  347. sample->SetLevelLoadReferenced();
  348. }
  349. if ( cvarSystem->GetCVarBool( "fs_buildgame" ) ) {
  350. fileSystem->AddSamplePreload( canonical );
  351. }
  352. return sample;
  353. }
  354. /*
  355. ========================
  356. idSoundSystemLocal::StopVoicesWithSample
  357. A sample is about to be freed, make sure the hardware isn't mixing from it.
  358. ========================
  359. */
  360. void idSoundSystemLocal::StopVoicesWithSample( const idSoundSample * const sample ) {
  361. for ( int w = 0; w < soundWorlds.Num(); w++ ) {
  362. idSoundWorldLocal * sw = soundWorlds[w];
  363. if ( sw == NULL ) {
  364. continue;
  365. }
  366. for ( int e = 0; e < sw->emitters.Num(); e++ ) {
  367. idSoundEmitterLocal * emitter = sw->emitters[e];
  368. if ( emitter == NULL ) {
  369. continue;
  370. }
  371. for ( int i = 0; i < emitter->channels.Num(); i++ ) {
  372. if ( emitter->channels[i]->leadinSample == sample || emitter->channels[i]->loopingSample == sample ) {
  373. emitter->channels[i]->Mute();
  374. }
  375. }
  376. }
  377. }
  378. }
  379. /*
  380. ========================
  381. idSoundSystemLocal::FreeVoice
  382. ========================
  383. */
  384. cinData_t idSoundSystemLocal::ImageForTime( const int milliseconds, const bool waveform ) {
  385. cinData_t cd;
  386. cd.imageY = NULL;
  387. cd.imageCr = NULL;
  388. cd.imageCb = NULL;
  389. cd.imageWidth = 0;
  390. cd.imageHeight = 0;
  391. cd.status = FMV_IDLE;
  392. return cd;
  393. }
  394. /*
  395. ========================
  396. idSoundSystemLocal::BeginLevelLoad
  397. ========================
  398. */
  399. void idSoundSystemLocal::BeginLevelLoad() {
  400. insideLevelLoad = true;
  401. for ( int i = 0; i < samples.Num(); i++ ) {
  402. if ( samples[i]->GetNeverPurge() ) {
  403. continue;
  404. }
  405. samples[i]->FreeData();
  406. samples[i]->ResetLevelLoadReferenced();
  407. }
  408. }
  409. /*
  410. ========================
  411. idSoundSystemLocal::Preload
  412. ========================
  413. */
  414. void idSoundSystemLocal::Preload( idPreloadManifest & manifest ) {
  415. idStrStatic< MAX_OSPATH > filename;
  416. int start = Sys_Milliseconds();
  417. int numLoaded = 0;
  418. idList< preloadSort_t > preloadSort;
  419. preloadSort.Resize( manifest.NumResources() );
  420. for ( int i = 0; i < manifest.NumResources(); i++ ) {
  421. const preloadEntry_s & p = manifest.GetPreloadByIndex( i );
  422. idResourceCacheEntry rc;
  423. // FIXME: write these out sorted
  424. if ( p.resType == PRELOAD_SAMPLE ) {
  425. if ( p.resourceName.Find( "/vo/", false ) >= 0 ) {
  426. continue;
  427. }
  428. filename = "generated/";
  429. filename += p.resourceName;
  430. filename.SetFileExtension( "idwav" );
  431. if ( fileSystem->GetResourceCacheEntry( filename, rc ) ) {
  432. preloadSort_t ps = {};
  433. ps.idx = i;
  434. ps.ofs = rc.offset;
  435. preloadSort.Append( ps );
  436. }
  437. }
  438. }
  439. preloadSort.SortWithTemplate( idSort_Preload() );
  440. for ( int i = 0; i < preloadSort.Num(); i++ ) {
  441. const preloadSort_t & ps = preloadSort[ i ];
  442. const preloadEntry_s & p = manifest.GetPreloadByIndex( ps.idx );
  443. filename = p.resourceName;
  444. filename.Replace( "generated/", "" );
  445. numLoaded++;
  446. idSoundSample *sample = LoadSample( filename );
  447. if ( sample != NULL && !sample->IsLoaded() ) {
  448. sample->LoadResource();
  449. sample->SetLevelLoadReferenced();
  450. }
  451. }
  452. int end = Sys_Milliseconds();
  453. common->Printf( "%05d sounds preloaded in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 );
  454. common->Printf( "----------------------------------------\n" );
  455. }
  456. /*
  457. ========================
  458. idSoundSystemLocal::EndLevelLoad
  459. ========================
  460. */
  461. void idSoundSystemLocal::EndLevelLoad() {
  462. insideLevelLoad = false;
  463. common->Printf( "----- idSoundSystemLocal::EndLevelLoad -----\n" );
  464. int start = Sys_Milliseconds();
  465. int keepCount = 0;
  466. int loadCount = 0;
  467. idList< preloadSort_t > preloadSort;
  468. preloadSort.Resize( samples.Num() );
  469. for ( int i = 0; i < samples.Num(); i++ ) {
  470. common->UpdateLevelLoadPacifier();
  471. if ( samples[i]->GetNeverPurge() ) {
  472. continue;
  473. }
  474. if ( samples[i]->IsLoaded() ) {
  475. keepCount++;
  476. continue;
  477. }
  478. if ( samples[i]->GetLevelLoadReferenced() ) {
  479. idStrStatic< MAX_OSPATH > filename = "generated/";
  480. filename += samples[ i ]->GetName();
  481. filename.SetFileExtension( "idwav" );
  482. preloadSort_t ps = {};
  483. ps.idx = i;
  484. idResourceCacheEntry rc;
  485. if ( fileSystem->GetResourceCacheEntry( filename, rc ) ) {
  486. ps.ofs = rc.offset;
  487. } else {
  488. ps.ofs = 0;
  489. }
  490. preloadSort.Append( ps );
  491. loadCount++;
  492. }
  493. }
  494. preloadSort.SortWithTemplate( idSort_Preload() );
  495. for ( int i = 0; i < preloadSort.Num(); i++ ) {
  496. common->UpdateLevelLoadPacifier();
  497. samples[ preloadSort[ i ].idx ]->LoadResource();
  498. }
  499. int end = Sys_Milliseconds();
  500. common->Printf( "%5i sounds loaded in %5.1f seconds\n", loadCount, (end-start) * 0.001 );
  501. common->Printf( "----------------------------------------\n" );
  502. }
  503. /*
  504. ========================
  505. idSoundSystemLocal::FreeVoice
  506. ========================
  507. */
  508. void idSoundSystemLocal::PrintMemInfo( MemInfo_t *mi ) {
  509. }