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