snd_shader.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 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 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. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "snd_local.h"
  23. /*
  24. ===============
  25. idSoundShader::Init
  26. ===============
  27. */
  28. void idSoundShader::Init( void ) {
  29. desc = "<no description>";
  30. errorDuringParse = false;
  31. onDemand = false;
  32. numEntries = 0;
  33. numLeadins = 0;
  34. leadinVolume = 0;
  35. altSound = NULL;
  36. }
  37. /*
  38. ===============
  39. idSoundShader::idSoundShader
  40. ===============
  41. */
  42. idSoundShader::idSoundShader( void ) {
  43. Init();
  44. }
  45. /*
  46. ===============
  47. idSoundShader::~idSoundShader
  48. ===============
  49. */
  50. idSoundShader::~idSoundShader( void ) {
  51. }
  52. /*
  53. =================
  54. idSoundShader::Size
  55. =================
  56. */
  57. size_t idSoundShader::Size( void ) const {
  58. return sizeof( idSoundShader );
  59. }
  60. /*
  61. ===============
  62. idSoundShader::idSoundShader::FreeData
  63. ===============
  64. */
  65. void idSoundShader::FreeData() {
  66. numEntries = 0;
  67. numLeadins = 0;
  68. }
  69. /*
  70. ===================
  71. idSoundShader::SetDefaultText
  72. ===================
  73. */
  74. bool idSoundShader::SetDefaultText( void ) {
  75. idStr wavname;
  76. wavname = GetName();
  77. wavname.DefaultFileExtension( ".wav" ); // if the name has .ogg in it, that will stay
  78. // if there exists a wav file with the same name
  79. if ( 1 ) { //fileSystem->ReadFile( wavname, NULL ) != -1 ) {
  80. char generated[2048];
  81. idStr::snPrintf( generated, sizeof( generated ),
  82. "sound %s // IMPLICITLY GENERATED\n"
  83. "{\n"
  84. "%s\n"
  85. "}\n", GetName(), wavname.c_str() );
  86. SetText( generated );
  87. return true;
  88. } else {
  89. return false;
  90. }
  91. }
  92. /*
  93. ===================
  94. DefaultDefinition
  95. ===================
  96. */
  97. const char *idSoundShader::DefaultDefinition() const {
  98. return
  99. "{\n"
  100. "\t" "_default.wav\n"
  101. "}";
  102. }
  103. /*
  104. ===============
  105. idSoundShader::Parse
  106. this is called by the declManager
  107. ===============
  108. */
  109. bool idSoundShader::Parse( const char *text, const int textLength ) {
  110. idLexer src;
  111. src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
  112. src.SetFlags( DECL_LEXER_FLAGS );
  113. src.SkipUntilString( "{" );
  114. // deeper functions can set this, which will cause MakeDefault() to be called at the end
  115. errorDuringParse = false;
  116. if ( !ParseShader( src ) || errorDuringParse ) {
  117. MakeDefault();
  118. return false;
  119. }
  120. return true;
  121. }
  122. /*
  123. ===============
  124. idSoundShader::ParseShader
  125. ===============
  126. */
  127. bool idSoundShader::ParseShader( idLexer &src ) {
  128. int i;
  129. idToken token;
  130. parms.minDistance = 1;
  131. parms.maxDistance = 10;
  132. parms.volume = 1;
  133. parms.shakes = 0;
  134. parms.soundShaderFlags = 0;
  135. parms.soundClass = 0;
  136. speakerMask = 0;
  137. altSound = NULL;
  138. for( i = 0; i < SOUND_MAX_LIST_WAVS; i++ ) {
  139. leadins[i] = NULL;
  140. entries[i] = NULL;
  141. }
  142. numEntries = 0;
  143. numLeadins = 0;
  144. int maxSamples = idSoundSystemLocal::s_maxSoundsPerShader.GetInteger();
  145. if ( com_makingBuild.GetBool() || maxSamples <= 0 || maxSamples > SOUND_MAX_LIST_WAVS ) {
  146. maxSamples = SOUND_MAX_LIST_WAVS;
  147. }
  148. while ( 1 ) {
  149. if ( !src.ExpectAnyToken( &token ) ) {
  150. return false;
  151. }
  152. // end of definition
  153. else if ( token == "}" ) {
  154. break;
  155. }
  156. // minimum number of sounds
  157. else if ( !token.Icmp( "minSamples" ) ) {
  158. maxSamples = idMath::ClampInt( src.ParseInt(), SOUND_MAX_LIST_WAVS, maxSamples );
  159. }
  160. // description
  161. else if ( !token.Icmp( "description" ) ) {
  162. src.ReadTokenOnLine( &token );
  163. desc = token.c_str();
  164. }
  165. // mindistance
  166. else if ( !token.Icmp( "mindistance" ) ) {
  167. parms.minDistance = src.ParseFloat();
  168. }
  169. // maxdistance
  170. else if ( !token.Icmp( "maxdistance" ) ) {
  171. parms.maxDistance = src.ParseFloat();
  172. }
  173. // shakes screen
  174. else if ( !token.Icmp( "shakes" ) ) {
  175. src.ExpectAnyToken( &token );
  176. if ( token.type == TT_NUMBER ) {
  177. parms.shakes = token.GetFloatValue();
  178. } else {
  179. src.UnreadToken( &token );
  180. parms.shakes = 1.0f;
  181. }
  182. }
  183. // reverb
  184. else if ( !token.Icmp( "reverb" ) ) {
  185. int reg0 = src.ParseFloat();
  186. if ( !src.ExpectTokenString( "," ) ) {
  187. src.FreeSource();
  188. return false;
  189. }
  190. int reg1 = src.ParseFloat();
  191. // no longer supported
  192. }
  193. // volume
  194. else if ( !token.Icmp( "volume" ) ) {
  195. parms.volume = src.ParseFloat();
  196. }
  197. // leadinVolume is used to allow light breaking leadin sounds to be much louder than the broken loop
  198. else if ( !token.Icmp( "leadinVolume" ) ) {
  199. leadinVolume = src.ParseFloat();
  200. }
  201. // speaker mask
  202. else if ( !token.Icmp( "mask_center" ) ) {
  203. speakerMask |= 1<<SPEAKER_CENTER;
  204. }
  205. // speaker mask
  206. else if ( !token.Icmp( "mask_left" ) ) {
  207. speakerMask |= 1<<SPEAKER_LEFT;
  208. }
  209. // speaker mask
  210. else if ( !token.Icmp( "mask_right" ) ) {
  211. speakerMask |= 1<<SPEAKER_RIGHT;
  212. }
  213. // speaker mask
  214. else if ( !token.Icmp( "mask_backright" ) ) {
  215. speakerMask |= 1<<SPEAKER_BACKRIGHT;
  216. }
  217. // speaker mask
  218. else if ( !token.Icmp( "mask_backleft" ) ) {
  219. speakerMask |= 1<<SPEAKER_BACKLEFT;
  220. }
  221. // speaker mask
  222. else if ( !token.Icmp( "mask_lfe" ) ) {
  223. speakerMask |= 1<<SPEAKER_LFE;
  224. }
  225. // soundClass
  226. else if ( !token.Icmp( "soundClass" ) ) {
  227. parms.soundClass = src.ParseInt();
  228. if ( parms.soundClass < 0 || parms.soundClass >= SOUND_MAX_CLASSES ) {
  229. src.Warning( "SoundClass out of range" );
  230. return false;
  231. }
  232. }
  233. // altSound
  234. else if ( !token.Icmp( "altSound" ) ) {
  235. if ( !src.ExpectAnyToken( &token ) ) {
  236. return false;
  237. }
  238. altSound = declManager->FindSound( token.c_str() );
  239. }
  240. // ordered
  241. else if ( !token.Icmp( "ordered" ) ) {
  242. // no longer supported
  243. }
  244. // no_dups
  245. else if ( !token.Icmp( "no_dups" ) ) {
  246. parms.soundShaderFlags |= SSF_NO_DUPS;
  247. }
  248. // no_flicker
  249. else if ( !token.Icmp( "no_flicker" ) ) {
  250. parms.soundShaderFlags |= SSF_NO_FLICKER;
  251. }
  252. // plain
  253. else if ( !token.Icmp( "plain" ) ) {
  254. // no longer supported
  255. }
  256. // looping
  257. else if ( !token.Icmp( "looping" ) ) {
  258. parms.soundShaderFlags |= SSF_LOOPING;
  259. }
  260. // no occlusion
  261. else if ( !token.Icmp( "no_occlusion" ) ) {
  262. parms.soundShaderFlags |= SSF_NO_OCCLUSION;
  263. }
  264. // private
  265. else if ( !token.Icmp( "private" ) ) {
  266. parms.soundShaderFlags |= SSF_PRIVATE_SOUND;
  267. }
  268. // antiPrivate
  269. else if ( !token.Icmp( "antiPrivate" ) ) {
  270. parms.soundShaderFlags |= SSF_ANTI_PRIVATE_SOUND;
  271. }
  272. // once
  273. else if ( !token.Icmp( "playonce" ) ) {
  274. parms.soundShaderFlags |= SSF_PLAY_ONCE;
  275. }
  276. // global
  277. else if ( !token.Icmp( "global" ) ) {
  278. parms.soundShaderFlags |= SSF_GLOBAL;
  279. }
  280. // unclamped
  281. else if ( !token.Icmp( "unclamped" ) ) {
  282. parms.soundShaderFlags |= SSF_UNCLAMPED;
  283. }
  284. // omnidirectional
  285. else if ( !token.Icmp( "omnidirectional" ) ) {
  286. parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
  287. }
  288. // onDemand can't be a parms, because we must track all references and overrides would confuse it
  289. else if ( !token.Icmp( "onDemand" ) ) {
  290. // no longer loading sounds on demand
  291. onDemand = true;
  292. }
  293. // the wave files
  294. else if ( !token.Icmp( "leadin" ) ) {
  295. // add to the leadin list
  296. if ( !src.ReadToken( &token ) ) {
  297. src.Warning( "Expected sound after leadin" );
  298. return false;
  299. }
  300. if ( soundSystemLocal.soundCache && numLeadins < maxSamples ) {
  301. leadins[ numLeadins ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
  302. numLeadins++;
  303. }
  304. } else if ( token.Find( ".wav", false ) != -1 || token.Find( ".ogg", false ) != -1 ) {
  305. // add to the wav list
  306. if ( soundSystemLocal.soundCache && numEntries < maxSamples ) {
  307. token.BackSlashesToSlashes();
  308. idStr lang = cvarSystem->GetCVarString( "sys_lang" );
  309. if ( lang.Icmp( "english" ) != 0 && token.Find( "sound/vo/", false ) >= 0 ) {
  310. idStr work = token;
  311. work.ToLower();
  312. work.StripLeading( "sound/vo/" );
  313. work = va( "sound/vo/%s/%s", lang.c_str(), work.c_str() );
  314. if ( fileSystem->ReadFile( work, NULL, NULL ) > 0 ) {
  315. token = work;
  316. } else {
  317. // also try to find it with the .ogg extension
  318. work.SetFileExtension( ".ogg" );
  319. if ( fileSystem->ReadFile( work, NULL, NULL ) > 0 ) {
  320. token = work;
  321. }
  322. }
  323. }
  324. entries[ numEntries ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
  325. numEntries++;
  326. }
  327. } else {
  328. src.Warning( "unknown token '%s'", token.c_str() );
  329. return false;
  330. }
  331. }
  332. if ( parms.shakes > 0.0f ) {
  333. CheckShakesAndOgg();
  334. }
  335. return true;
  336. }
  337. /*
  338. ===============
  339. idSoundShader::CheckShakesAndOgg
  340. ===============
  341. */
  342. bool idSoundShader::CheckShakesAndOgg( void ) const {
  343. int i;
  344. bool ret = false;
  345. for ( i = 0; i < numLeadins; i++ ) {
  346. if ( leadins[ i ]->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) {
  347. common->Warning( "sound shader '%s' has shakes and uses OGG file '%s'",
  348. GetName(), leadins[ i ]->name.c_str() );
  349. ret = true;
  350. }
  351. }
  352. for ( i = 0; i < numEntries; i++ ) {
  353. if ( entries[ i ]->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) {
  354. common->Warning( "sound shader '%s' has shakes and uses OGG file '%s'",
  355. GetName(), entries[ i ]->name.c_str() );
  356. ret = true;
  357. }
  358. }
  359. return ret;
  360. }
  361. /*
  362. ===============
  363. idSoundShader::List
  364. ===============
  365. */
  366. void idSoundShader::List() const {
  367. idStrList shaders;
  368. common->Printf( "%4i: %s\n", Index(), GetName() );
  369. if ( idStr::Icmp( GetDescription(), "<no description>" ) != 0 ) {
  370. common->Printf( " description: %s\n", GetDescription() );
  371. }
  372. for( int k = 0; k < numLeadins ; k++ ) {
  373. const idSoundSample *objectp = leadins[k];
  374. if ( objectp ) {
  375. common->Printf( " %5dms %4dKb %s (LEADIN)\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
  376. ,objectp->name.c_str() );
  377. }
  378. }
  379. for( int k = 0; k < numEntries; k++ ) {
  380. const idSoundSample *objectp = entries[k];
  381. if ( objectp ) {
  382. common->Printf( " %5dms %4dKb %s\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
  383. ,objectp->name.c_str() );
  384. }
  385. }
  386. }
  387. /*
  388. ===============
  389. idSoundShader::GetAltSound
  390. ===============
  391. */
  392. const idSoundShader *idSoundShader::GetAltSound( void ) const {
  393. return altSound;
  394. }
  395. /*
  396. ===============
  397. idSoundShader::GetMinDistance
  398. ===============
  399. */
  400. float idSoundShader::GetMinDistance() const {
  401. return parms.minDistance;
  402. }
  403. /*
  404. ===============
  405. idSoundShader::GetMaxDistance
  406. ===============
  407. */
  408. float idSoundShader::GetMaxDistance() const {
  409. return parms.maxDistance;
  410. }
  411. /*
  412. ===============
  413. idSoundShader::GetDescription
  414. ===============
  415. */
  416. const char *idSoundShader::GetDescription() const {
  417. return desc;
  418. }
  419. /*
  420. ===============
  421. idSoundShader::HasDefaultSound
  422. ===============
  423. */
  424. bool idSoundShader::HasDefaultSound() const {
  425. for ( int i = 0; i < numEntries; i++ ) {
  426. if ( entries[i] && entries[i]->defaultSound ) {
  427. return true;
  428. }
  429. }
  430. return false;
  431. }
  432. /*
  433. ===============
  434. idSoundShader::GetParms
  435. ===============
  436. */
  437. const soundShaderParms_t *idSoundShader::GetParms() const {
  438. return &parms;
  439. }
  440. /*
  441. ===============
  442. idSoundShader::GetNumSounds
  443. ===============
  444. */
  445. int idSoundShader::GetNumSounds() const {
  446. return numLeadins + numEntries;
  447. }
  448. /*
  449. ===============
  450. idSoundShader::GetSound
  451. ===============
  452. */
  453. const char *idSoundShader::GetSound( int index ) const {
  454. if ( index >= 0 ) {
  455. if ( index < numLeadins ) {
  456. return leadins[index]->name.c_str();
  457. }
  458. index -= numLeadins;
  459. if ( index < numEntries ) {
  460. return entries[index]->name.c_str();
  461. }
  462. }
  463. return "";
  464. }