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