]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sound/snd_shader.cpp
hello world
[icculus/iodoom3.git] / neo / sound / snd_shader.cpp
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 "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "snd_local.h"
33
34
35 /*
36 ===============
37 idSoundShader::Init
38 ===============
39 */
40 void idSoundShader::Init( void ) {
41         desc = "<no description>";
42         errorDuringParse = false;
43         onDemand = false;
44         numEntries = 0;
45         numLeadins = 0;
46         leadinVolume = 0;
47         altSound = NULL;
48 }
49
50 /*
51 ===============
52 idSoundShader::idSoundShader
53 ===============
54 */
55 idSoundShader::idSoundShader( void ) {
56         Init();
57 }
58
59 /*
60 ===============
61 idSoundShader::~idSoundShader
62 ===============
63 */
64 idSoundShader::~idSoundShader( void ) {
65 }
66
67 /*
68 =================
69 idSoundShader::Size
70 =================
71 */
72 size_t idSoundShader::Size( void ) const {
73         return sizeof( idSoundShader );
74 }
75
76 /*
77 ===============
78 idSoundShader::idSoundShader::FreeData
79 ===============
80 */
81 void idSoundShader::FreeData() {
82         numEntries = 0;
83         numLeadins = 0;
84 }
85
86 /*
87 ===================
88 idSoundShader::SetDefaultText
89 ===================
90 */
91 bool idSoundShader::SetDefaultText( void ) {
92         idStr wavname;
93
94         wavname = GetName();
95         wavname.DefaultFileExtension( ".wav" );         // if the name has .ogg in it, that will stay
96
97         // if there exists a wav file with the same name
98         if ( 1 ) { //fileSystem->ReadFile( wavname, NULL ) != -1 ) {
99                 char generated[2048];
100                 idStr::snPrintf( generated, sizeof( generated ), 
101                                                 "sound %s // IMPLICITLY GENERATED\n"
102                                                 "{\n"
103                                                 "%s\n"
104                                                 "}\n", GetName(), wavname.c_str() );
105                 SetText( generated );
106                 return true;
107         } else {
108                 return false;
109         }
110 }
111
112 /*
113 ===================
114 DefaultDefinition
115 ===================
116 */
117 const char *idSoundShader::DefaultDefinition() const {
118         return
119                 "{\n"
120         "\t"    "_default.wav\n"
121                 "}";
122 }
123
124 /*
125 ===============
126 idSoundShader::Parse
127
128   this is called by the declManager
129 ===============
130 */
131 bool idSoundShader::Parse( const char *text, const int textLength ) {
132         idLexer src;
133
134         src.LoadMemory( text, textLength, GetFileName(), GetLineNum() );
135         src.SetFlags( DECL_LEXER_FLAGS );
136         src.SkipUntilString( "{" );
137
138         // deeper functions can set this, which will cause MakeDefault() to be called at the end
139         errorDuringParse = false;
140
141         if ( !ParseShader( src ) || errorDuringParse ) {
142                 MakeDefault();
143                 return false;
144         }
145         return true;
146 }
147
148 /*
149 ===============
150 idSoundShader::ParseShader
151 ===============
152 */
153 bool idSoundShader::ParseShader( idLexer &src ) {
154         int                     i;
155         idToken         token;
156
157         parms.minDistance = 1;
158         parms.maxDistance = 10;
159         parms.volume = 1;
160         parms.shakes = 0;
161         parms.soundShaderFlags = 0;
162         parms.soundClass = 0;
163
164         speakerMask = 0;
165         altSound = NULL;
166
167         for( i = 0; i < SOUND_MAX_LIST_WAVS; i++ ) {
168                 leadins[i] = NULL;
169                 entries[i] = NULL;
170         }
171         numEntries = 0;
172         numLeadins = 0;
173
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;
177         }
178
179         while ( 1 ) {
180                 if ( !src.ExpectAnyToken( &token ) ) {
181                         return false;
182                 }
183                 // end of definition
184                 else if ( token == "}" ) {
185                         break;
186                 }
187                 // minimum number of sounds
188                 else if ( !token.Icmp( "minSamples" ) ) {
189                         maxSamples = idMath::ClampInt( src.ParseInt(), SOUND_MAX_LIST_WAVS, maxSamples );
190                 }
191                 // description
192                 else if ( !token.Icmp( "description" ) ) {
193                         src.ReadTokenOnLine( &token );
194                         desc = token.c_str();
195                 }
196                 // mindistance
197                 else if ( !token.Icmp( "mindistance" ) ) {
198                         parms.minDistance = src.ParseFloat();
199                 }
200                 // maxdistance
201                 else if ( !token.Icmp( "maxdistance" ) ) {
202                         parms.maxDistance = src.ParseFloat();
203                 }
204                 // shakes screen
205                 else if ( !token.Icmp( "shakes" ) ) {
206                         src.ExpectAnyToken( &token );
207                         if ( token.type == TT_NUMBER ) {
208                                 parms.shakes = token.GetFloatValue();
209                         } else {
210                                 src.UnreadToken( &token );
211                                 parms.shakes = 1.0f;
212                         }
213                 }
214                 // reverb
215                 else if ( !token.Icmp( "reverb" ) ) {
216                         int reg0 = src.ParseFloat();
217                         if ( !src.ExpectTokenString( "," ) ) {
218                                 src.FreeSource();
219                                 return false;
220                         }
221                         int reg1 = src.ParseFloat();
222                         // no longer supported
223                 }
224                 // volume
225                 else if ( !token.Icmp( "volume" ) ) {
226                         parms.volume = src.ParseFloat();
227                 }
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();
231                 }
232                 // speaker mask
233                 else if ( !token.Icmp( "mask_center" ) ) {
234                         speakerMask |= 1<<SPEAKER_CENTER;
235                 }
236                 // speaker mask
237                 else if ( !token.Icmp( "mask_left" ) ) {
238                         speakerMask |= 1<<SPEAKER_LEFT;
239                 }
240                 // speaker mask
241                 else if ( !token.Icmp( "mask_right" ) ) {
242                         speakerMask |= 1<<SPEAKER_RIGHT;
243                 }
244                 // speaker mask
245                 else if ( !token.Icmp( "mask_backright" ) ) {
246                         speakerMask |= 1<<SPEAKER_BACKRIGHT;
247                 }
248                 // speaker mask
249                 else if ( !token.Icmp( "mask_backleft" ) ) {
250                         speakerMask |= 1<<SPEAKER_BACKLEFT;
251                 }
252                 // speaker mask
253                 else if ( !token.Icmp( "mask_lfe" ) ) {
254                         speakerMask |= 1<<SPEAKER_LFE;
255                 }
256                 // soundClass
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" );
261                                 return false;
262                         }
263                 }
264                 // altSound
265                 else if ( !token.Icmp( "altSound" ) ) {
266                         if ( !src.ExpectAnyToken( &token ) ) {
267                                 return false;
268                         }
269                         altSound = declManager->FindSound( token.c_str() );
270                 }
271                 // ordered
272                 else if ( !token.Icmp( "ordered" ) ) {
273                         // no longer supported
274                 }
275                 // no_dups
276                 else if ( !token.Icmp( "no_dups" ) ) {
277                         parms.soundShaderFlags |= SSF_NO_DUPS;
278                 }
279                 // no_flicker
280                 else if ( !token.Icmp( "no_flicker" ) ) {
281                         parms.soundShaderFlags |= SSF_NO_FLICKER;
282                 }
283                 // plain
284                 else if ( !token.Icmp( "plain" ) ) {    
285                         // no longer supported
286                 }
287                 // looping
288                 else if ( !token.Icmp( "looping" ) ) {
289                         parms.soundShaderFlags |= SSF_LOOPING;
290                 }
291                 // no occlusion
292                 else if ( !token.Icmp( "no_occlusion" ) ) {
293                         parms.soundShaderFlags |= SSF_NO_OCCLUSION;
294                 }
295                 // private
296                 else if ( !token.Icmp( "private" ) ) {
297                         parms.soundShaderFlags |= SSF_PRIVATE_SOUND;
298                 }
299                 // antiPrivate
300                 else if ( !token.Icmp( "antiPrivate" ) ) {
301                         parms.soundShaderFlags |= SSF_ANTI_PRIVATE_SOUND;
302                 }
303                 // once
304                 else if ( !token.Icmp( "playonce" ) ) {
305                         parms.soundShaderFlags |= SSF_PLAY_ONCE;
306                 }
307                 // global
308                 else if ( !token.Icmp( "global" ) ) {
309                         parms.soundShaderFlags |= SSF_GLOBAL;
310                 }
311                 // unclamped
312                 else if ( !token.Icmp( "unclamped" ) ) {
313                         parms.soundShaderFlags |= SSF_UNCLAMPED;
314                 }
315                 // omnidirectional
316                 else if ( !token.Icmp( "omnidirectional" ) ) {
317                         parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
318                 }
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
322                         //onDemand = true;
323                 }
324
325                 // the wave files
326                 else if ( !token.Icmp( "leadin" ) ) {
327                         // add to the leadin list
328                         if ( !src.ReadToken( &token ) ) {
329                                 src.Warning( "Expected sound after leadin" );
330                                 return false;
331                         }
332                         if ( soundSystemLocal.soundCache && numLeadins < maxSamples ) {
333                                 leadins[ numLeadins ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
334                                 numLeadins++;
335                         }
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 ) {
342                                         idStr work = token;
343                                         work.ToLower();
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 ) {
347                                                 token = work;
348                                         } else {
349                                                 // also try to find it with the .ogg extension
350                                                 work.SetFileExtension( ".ogg" );
351                                                 if ( fileSystem->ReadFile( work, NULL, NULL ) > 0 ) {
352                                                         token = work;
353                                                 }
354                                         }
355                                 }                                       
356                                 entries[ numEntries ] = soundSystemLocal.soundCache->FindSound( token.c_str(), onDemand );
357                                 numEntries++;
358                         }
359                 } else {
360                         src.Warning( "unknown token '%s'", token.c_str() );
361                         return false;
362                 }
363         }
364
365         if ( parms.shakes > 0.0f ) {
366                 CheckShakesAndOgg();
367         }
368
369         return true;
370 }
371
372 /*
373 ===============
374 idSoundShader::CheckShakesAndOgg
375 ===============
376 */
377 bool idSoundShader::CheckShakesAndOgg( void ) const {
378         int i;
379         bool ret = false;
380
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() );
385                         ret = true;
386                 }
387         }
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() );
392                         ret = true;
393                 }
394         }
395         return ret;
396 }
397
398 /*
399 ===============
400 idSoundShader::List
401 ===============
402 */
403 void idSoundShader::List() const {
404         idStrList       shaders;
405
406         common->Printf( "%4i: %s\n", Index(), GetName() );
407         if ( idStr::Icmp( GetDescription(), "<no description>" ) != 0 ) {
408                 common->Printf( "      description: %s\n", GetDescription() );
409         }
410         for( int k = 0; k < numLeadins ; k++ ) {
411                 const idSoundSample *objectp = leadins[k];
412                 if ( objectp ) {
413                         common->Printf( "      %5dms %4dKb %s (LEADIN)\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
414                                 ,objectp->name.c_str() );
415                 }
416         }
417         for( int k = 0; k < numEntries; k++ ) {
418                 const idSoundSample *objectp = entries[k];
419                 if ( objectp ) {
420                         common->Printf( "      %5dms %4dKb %s\n", soundSystemLocal.SamplesToMilliseconds(objectp->LengthIn44kHzSamples()), (objectp->objectMemSize/1024)
421                                 ,objectp->name.c_str() );
422                 }
423         }
424 }
425
426 /*
427 ===============
428 idSoundShader::GetAltSound
429 ===============
430 */
431 const idSoundShader *idSoundShader::GetAltSound( void ) const {
432         return altSound;
433 }
434
435 /*
436 ===============
437 idSoundShader::GetMinDistance
438 ===============
439 */
440 float idSoundShader::GetMinDistance() const {
441         return parms.minDistance;
442 }
443
444 /*
445 ===============
446 idSoundShader::GetMaxDistance
447 ===============
448 */
449 float idSoundShader::GetMaxDistance() const {
450         return parms.maxDistance;
451 }
452
453 /*
454 ===============
455 idSoundShader::GetDescription
456 ===============
457 */
458 const char *idSoundShader::GetDescription() const {
459         return desc;
460 }
461
462 /*
463 ===============
464 idSoundShader::HasDefaultSound
465 ===============
466 */
467 bool idSoundShader::HasDefaultSound() const {
468         for ( int i = 0; i < numEntries; i++ ) {
469                 if ( entries[i] && entries[i]->defaultSound ) {
470                         return true;
471                 }
472         }
473         return false;
474 }
475
476 /*
477 ===============
478 idSoundShader::GetParms
479 ===============
480 */
481 const soundShaderParms_t *idSoundShader::GetParms() const {
482         return &parms;
483 }
484
485 /*
486 ===============
487 idSoundShader::GetNumSounds
488 ===============
489 */
490 int idSoundShader::GetNumSounds() const {
491         return numLeadins + numEntries;
492 }
493
494 /*
495 ===============
496 idSoundShader::GetSound
497 ===============
498 */
499 const char *idSoundShader::GetSound( int index ) const {
500         if ( index >= 0 ) {
501                 if ( index < numLeadins ) {
502                         return leadins[index]->name.c_str();
503                 }
504                 index -= numLeadins;
505                 if ( index < numEntries ) {
506                         return entries[index]->name.c_str();
507                 }
508         }
509         return "";
510 }