2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "snd_local.h"
40 void idSoundShader::Init( void ) {
41 desc = "<no description>";
42 errorDuringParse = false;
52 idSoundShader::idSoundShader
55 idSoundShader::idSoundShader( void ) {
61 idSoundShader::~idSoundShader
64 idSoundShader::~idSoundShader( void ) {
72 size_t idSoundShader::Size( void ) const {
73 return sizeof( idSoundShader );
78 idSoundShader::idSoundShader::FreeData
81 void idSoundShader::FreeData() {
88 idSoundShader::SetDefaultText
91 bool idSoundShader::SetDefaultText( void ) {
95 wavname.DefaultFileExtension( ".wav" ); // if the name has .ogg in it, that will stay
97 // if there exists a wav file with the same name
98 if ( 1 ) { //fileSystem->ReadFile( wavname, NULL ) != -1 ) {
100 idStr::snPrintf( generated, sizeof( generated ),
101 "sound %s // IMPLICITLY GENERATED\n"
104 "}\n", GetName(), wavname.c_str() );
105 SetText( generated );
117 const char *idSoundShader::DefaultDefinition() const {
120 "\t" "_default.wav\n"
128 this is called by the declManager
131 bool idSoundShader::Parse( const char *text, const int textLength ) {
134 src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
135 src.SetFlags( DECL_LEXER_FLAGS );
136 src.SkipUntilString( "{" );
138 // deeper functions can set this, which will cause MakeDefault() to be called at the end
139 errorDuringParse = false;
141 if ( !ParseShader( src ) || errorDuringParse ) {
150 idSoundShader::ParseShader
153 bool idSoundShader::ParseShader( idLexer &src ) {
157 parms.minDistance = 1;
158 parms.maxDistance = 10;
161 parms.soundShaderFlags = 0;
162 parms.soundClass = 0;
167 for( i = 0; i < SOUND_MAX_LIST_WAVS; i++ ) {
174 int maxSamples = idSoundSystemLocal::s_maxSoundsPerShader.GetInteger();
175 if ( com_makingBuild.GetBool() || maxSamples <= 0 || maxSamples > SOUND_MAX_LIST_WAVS ) {
176 maxSamples = SOUND_MAX_LIST_WAVS;
180 if ( !src.ExpectAnyToken( &token ) ) {
184 else if ( token == "}" ) {
187 // minimum number of sounds
188 else if ( !token.Icmp( "minSamples" ) ) {
189 maxSamples = idMath::ClampInt( src.ParseInt(), SOUND_MAX_LIST_WAVS, maxSamples );
192 else if ( !token.Icmp( "description" ) ) {
193 src.ReadTokenOnLine( &token );
194 desc = token.c_str();
197 else if ( !token.Icmp( "mindistance" ) ) {
198 parms.minDistance = src.ParseFloat();
201 else if ( !token.Icmp( "maxdistance" ) ) {
202 parms.maxDistance = src.ParseFloat();
205 else if ( !token.Icmp( "shakes" ) ) {
206 src.ExpectAnyToken( &token );
207 if ( token.type == TT_NUMBER ) {
208 parms.shakes = token.GetFloatValue();
210 src.UnreadToken( &token );
215 else if ( !token.Icmp( "reverb" ) ) {
216 int reg0 = src.ParseFloat();
217 if ( !src.ExpectTokenString( "," ) ) {
221 int reg1 = src.ParseFloat();
222 // no longer supported
225 else if ( !token.Icmp( "volume" ) ) {
226 parms.volume = src.ParseFloat();
228 // leadinVolume is used to allow light breaking leadin sounds to be much louder than the broken loop
229 else if ( !token.Icmp( "leadinVolume" ) ) {
230 leadinVolume = src.ParseFloat();
233 else if ( !token.Icmp( "mask_center" ) ) {
234 speakerMask |= 1<<SPEAKER_CENTER;
237 else if ( !token.Icmp( "mask_left" ) ) {
238 speakerMask |= 1<<SPEAKER_LEFT;
241 else if ( !token.Icmp( "mask_right" ) ) {
242 speakerMask |= 1<<SPEAKER_RIGHT;
245 else if ( !token.Icmp( "mask_backright" ) ) {
246 speakerMask |= 1<<SPEAKER_BACKRIGHT;
249 else if ( !token.Icmp( "mask_backleft" ) ) {
250 speakerMask |= 1<<SPEAKER_BACKLEFT;
253 else if ( !token.Icmp( "mask_lfe" ) ) {
254 speakerMask |= 1<<SPEAKER_LFE;
257 else if ( !token.Icmp( "soundClass" ) ) {
258 parms.soundClass = src.ParseInt();
259 if ( parms.soundClass < 0 || parms.soundClass >= SOUND_MAX_CLASSES ) {
260 src.Warning( "SoundClass out of range" );
265 else if ( !token.Icmp( "altSound" ) ) {
266 if ( !src.ExpectAnyToken( &token ) ) {
269 altSound = declManager->FindSound( token.c_str() );
272 else if ( !token.Icmp( "ordered" ) ) {
273 // no longer supported
276 else if ( !token.Icmp( "no_dups" ) ) {
277 parms.soundShaderFlags |= SSF_NO_DUPS;
280 else if ( !token.Icmp( "no_flicker" ) ) {
281 parms.soundShaderFlags |= SSF_NO_FLICKER;
284 else if ( !token.Icmp( "plain" ) ) {
285 // no longer supported
288 else if ( !token.Icmp( "looping" ) ) {
289 parms.soundShaderFlags |= SSF_LOOPING;
292 else if ( !token.Icmp( "no_occlusion" ) ) {
293 parms.soundShaderFlags |= SSF_NO_OCCLUSION;
296 else if ( !token.Icmp( "private" ) ) {
297 parms.soundShaderFlags |= SSF_PRIVATE_SOUND;
300 else if ( !token.Icmp( "antiPrivate" ) ) {
301 parms.soundShaderFlags |= SSF_ANTI_PRIVATE_SOUND;
304 else if ( !token.Icmp( "playonce" ) ) {
305 parms.soundShaderFlags |= SSF_PLAY_ONCE;
308 else if ( !token.Icmp( "global" ) ) {
309 parms.soundShaderFlags |= SSF_GLOBAL;
312 else if ( !token.Icmp( "unclamped" ) ) {
313 parms.soundShaderFlags |= SSF_UNCLAMPED;
316 else if ( !token.Icmp( "omnidirectional" ) ) {
317 parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
319 // onDemand can't be a parms, because we must track all references and overrides would confuse it
320 else if ( !token.Icmp( "onDemand" ) ) {
321 // no longer loading sounds on demand
326 else if ( !token.Icmp( "leadin" ) ) {
327 // add to the leadin list
328 if ( !src.ReadToken( &token ) ) {
329 src.Warning( "Expected sound after leadin" );
332 if ( soundSystemLocal.soundCache && numLeadins < maxSamples ) {
333 leadins[ numLeadins ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
336 } else if ( token.Find( ".wav", false ) != -1 || token.Find( ".ogg", false ) != -1 ) {
337 // add to the wav list
338 if ( soundSystemLocal.soundCache && numEntries < maxSamples ) {
339 token.BackSlashesToSlashes();
340 idStr lang = cvarSystem->GetCVarString( "sys_lang" );
341 if ( lang.Icmp( "english" ) != 0 && token.Find( "sound/vo/", false ) >= 0 ) {
344 work.StripLeading( "sound/vo/" );
345 work = va( "sound/vo/%s/%s", lang.c_str(), work.c_str() );
346 if ( fileSystem->ReadFile( work, NULL, NULL ) > 0 ) {
349 // also try to find it with the .ogg extension
350 work.SetFileExtension( ".ogg" );
351 if ( fileSystem->ReadFile( work, NULL, NULL ) > 0 ) {
356 entries[ numEntries ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
360 src.Warning( "unknown token '%s'", token.c_str() );
365 if ( parms.shakes > 0.0f ) {
374 idSoundShader::CheckShakesAndOgg
377 bool idSoundShader::CheckShakesAndOgg( void ) const {
381 for ( i = 0; i < numLeadins; i++ ) {
382 if ( leadins[ i ]->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) {
383 common->Warning( "sound shader '%s' has shakes and uses OGG file '%s'",
384 GetName(), leadins[ i ]->name.c_str() );
388 for ( i = 0; i < numEntries; i++ ) {
389 if ( entries[ i ]->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) {
390 common->Warning( "sound shader '%s' has shakes and uses OGG file '%s'",
391 GetName(), entries[ i ]->name.c_str() );
403 void idSoundShader::List() const {
406 common->Printf( "%4i: %s\n", Index(), GetName() );
407 if ( idStr::Icmp( GetDescription(), "<no description>" ) != 0 ) {
408 common->Printf( " description: %s\n", GetDescription() );
410 for( int k = 0; k < numLeadins ; k++ ) {
411 const idSoundSample *objectp = leadins[k];
413 common->Printf( " %5dms %4dKb %s (LEADIN)\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
414 ,objectp->name.c_str() );
417 for( int k = 0; k < numEntries; k++ ) {
418 const idSoundSample *objectp = entries[k];
420 common->Printf( " %5dms %4dKb %s\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
421 ,objectp->name.c_str() );
428 idSoundShader::GetAltSound
431 const idSoundShader *idSoundShader::GetAltSound( void ) const {
437 idSoundShader::GetMinDistance
440 float idSoundShader::GetMinDistance() const {
441 return parms.minDistance;
446 idSoundShader::GetMaxDistance
449 float idSoundShader::GetMaxDistance() const {
450 return parms.maxDistance;
455 idSoundShader::GetDescription
458 const char *idSoundShader::GetDescription() const {
464 idSoundShader::HasDefaultSound
467 bool idSoundShader::HasDefaultSound() const {
468 for ( int i = 0; i < numEntries; i++ ) {
469 if ( entries[i] && entries[i]->defaultSound ) {
478 idSoundShader::GetParms
481 const soundShaderParms_t *idSoundShader::GetParms() const {
487 idSoundShader::GetNumSounds
490 int idSoundShader::GetNumSounds() const {
491 return numLeadins + numEntries;
496 idSoundShader::GetSound
499 const char *idSoundShader::GetSound( int index ) const {
501 if ( index < numLeadins ) {
502 return leadins[index]->name.c_str();
505 if ( index < numEntries ) {
506 return entries[index]->name.c_str();