]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/sound/snd_world.cpp
Various Mac OS X tweaks to get this to build. Probably breaking things.
[icculus/iodoom3.git] / neo / sound / snd_world.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 idSoundWorldLocal::Init
37 ==================
38 */
39 void idSoundWorldLocal::Init( idRenderWorld *renderWorld ) {
40         rw = renderWorld;
41         writeDemo = NULL;
42
43         listenerAxis.Identity();
44         listenerPos.Zero();
45         listenerPrivateId = 0;
46         listenerQU.Zero();
47         listenerArea = 0;
48         listenerAreaName = "Undefined";
49         listenerEnvironmentID = -2;
50
51         gameMsec = 0;
52         game44kHz = 0;
53         pause44kHz = -1;
54         lastAVI44kHz = 0;
55
56         for ( int i = 0 ; i < SOUND_MAX_CLASSES ; i++ ) {
57                 soundClassFade[i].Clear();
58         }
59
60         // fill in the 0 index spot
61         idSoundEmitterLocal     *placeHolder = new idSoundEmitterLocal;
62         emitters.Append( placeHolder );
63
64         fpa[0] = fpa[1] = fpa[2] = fpa[3] = fpa[4] = fpa[5] = NULL;
65
66         aviDemoPath = "";
67         aviDemoName = "";
68
69         localSound = NULL;
70
71         slowmoActive            = false;
72         slowmoSpeed                     = 0;
73         enviroSuitActive        = false;
74 }
75
76 /*
77 ===============
78 idSoundWorldLocal::idSoundWorldLocal
79 ===============
80 */
81 idSoundWorldLocal::idSoundWorldLocal() {
82 }
83
84 /*
85 ===============
86 idSoundWorldLocal::~idSoundWorldLocal
87 ===============
88 */
89 idSoundWorldLocal::~idSoundWorldLocal() {
90         Shutdown();
91 }
92
93 /*
94 ===============
95 idSoundWorldLocal::Shutdown
96
97   this is called from the main thread
98 ===============
99 */
100 void idSoundWorldLocal::Shutdown() {
101         int i;
102
103         if ( soundSystemLocal.currentSoundWorld == this ) {
104                 soundSystemLocal.currentSoundWorld = NULL;
105         }
106
107         AVIClose();
108
109         for ( i = 0; i < emitters.Num(); i++ ) {
110                 if ( emitters[i] ) {
111                         delete emitters[i];
112                         emitters[i] = NULL;
113                 }
114         }
115         localSound = NULL;
116 }
117
118 /*
119 ===================
120 idSoundWorldLocal::ClearAllSoundEmitters
121 ===================
122 */
123 void idSoundWorldLocal::ClearAllSoundEmitters() {
124         int i;
125
126         Sys_EnterCriticalSection();
127
128         AVIClose();
129
130         for ( i = 0; i < emitters.Num(); i++ ) {
131                 idSoundEmitterLocal *sound = emitters[i];
132                 sound->Clear();
133         }
134         localSound = NULL;
135
136         Sys_LeaveCriticalSection();
137 }
138
139 /*
140 ===================
141 idSoundWorldLocal::AllocLocalSoundEmitter
142 ===================
143 */
144 idSoundEmitterLocal *idSoundWorldLocal::AllocLocalSoundEmitter() {
145         int i, index;
146         idSoundEmitterLocal *def = NULL;
147
148         index = -1;
149
150         // never use the 0 index spot
151
152         for ( i = 1 ; i < emitters.Num() ; i++ ) {
153                 def = emitters[i];
154
155                 // check for a completed and freed spot
156                 if ( def->removeStatus >= REMOVE_STATUS_SAMPLEFINISHED ) {
157                         index = i;
158                         if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
159                                 common->Printf( "sound: recycling sound def %d\n", i );
160                         }
161                         break;
162                 }
163         }
164
165         if ( index == -1 ) {
166                 // append a brand new one
167                 def = new idSoundEmitterLocal;
168
169                 // we need to protect this from the async thread
170                 Sys_EnterCriticalSection();
171                 index = emitters.Append( def );
172                 Sys_LeaveCriticalSection();
173
174                 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
175                         common->Printf( "sound: appended new sound def %d\n", index );
176                 }
177         }
178
179         def->Clear();
180         def->index = index;
181         def->removeStatus = REMOVE_STATUS_ALIVE;
182         def->soundWorld = this;
183
184         return def;
185 }
186
187 /*
188 ===================
189 idSoundWorldLocal::AllocSoundEmitter
190
191   this is called from the main thread
192 ===================
193 */
194 idSoundEmitter *idSoundWorldLocal::AllocSoundEmitter() {
195         idSoundEmitterLocal *emitter = AllocLocalSoundEmitter();
196
197         if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
198                 common->Printf( "AllocSoundEmitter = %i\n",  emitter->index );
199         }
200         if ( writeDemo ) {
201                 writeDemo->WriteInt( DS_SOUND );
202                 writeDemo->WriteInt( SCMD_ALLOC_EMITTER );
203                 writeDemo->WriteInt( emitter->index );
204         }
205
206         return emitter;
207 }
208
209 /*
210 ===================
211 idSoundWorldLocal::StartWritingDemo
212
213   this is called from the main thread
214 ===================
215 */
216 void idSoundWorldLocal::StartWritingDemo( idDemoFile *demo ) {
217         writeDemo = demo;
218
219         writeDemo->WriteInt( DS_SOUND );
220         writeDemo->WriteInt( SCMD_STATE );
221
222         // use the normal save game code to archive all the emitters
223         WriteToSaveGame( writeDemo );
224 }
225
226 /*
227 ===================
228 idSoundWorldLocal::StopWritingDemo
229
230   this is called from the main thread
231 ===================
232 */
233 void idSoundWorldLocal::StopWritingDemo() {
234         writeDemo = NULL;
235 }
236
237 /*
238 ===================
239 idSoundWorldLocal::ProcessDemoCommand
240
241   this is called from the main thread
242 ===================
243 */
244 void idSoundWorldLocal::ProcessDemoCommand( idDemoFile *readDemo ) {
245         int     index;
246         idSoundEmitterLocal     *def;
247
248         if ( !readDemo ) {
249                 return;
250         }
251
252         soundDemoCommand_t      dc;
253
254         if ( !readDemo->ReadInt( (int&)dc ) ) {
255                 return;
256         }
257
258         switch( dc ) {
259         case SCMD_STATE:
260                 // we need to protect this from the async thread
261                 // other instances of calling idSoundWorldLocal::ReadFromSaveGame do this while the sound code is muted
262                 // setting muted and going right in may not be good enough here, as we async thread may already be in an async tick (in which case we could still race to it)
263                 Sys_EnterCriticalSection();
264                 ReadFromSaveGame( readDemo );
265                 Sys_LeaveCriticalSection();
266                 UnPause();
267                 break;
268         case SCMD_PLACE_LISTENER:
269                 {
270                         idVec3  origin;
271                         idMat3  axis;
272                         int             listenerId;
273                         int             gameTime;
274
275                         readDemo->ReadVec3( origin );
276                         readDemo->ReadMat3( axis );
277                         readDemo->ReadInt( listenerId );
278                         readDemo->ReadInt( gameTime );
279                         
280                         PlaceListener( origin, axis, listenerId, gameTime, "" );
281                 };
282                 break;
283         case SCMD_ALLOC_EMITTER:
284                 readDemo->ReadInt( index );
285                 if ( index < 1 || index > emitters.Num() ) {
286                         common->Error( "idSoundWorldLocal::ProcessDemoCommand: bad emitter number" );
287                 }
288                 if ( index == emitters.Num() ) {
289                         // append a brand new one
290                         def = new idSoundEmitterLocal;
291                         emitters.Append( def );
292                 }
293                 def = emitters[ index ];
294                 def->Clear();
295                 def->index = index;
296                 def->removeStatus = REMOVE_STATUS_ALIVE;
297                 def->soundWorld = this;
298                 break;
299         case SCMD_FREE:
300                 {
301                         int     immediate;
302
303                         readDemo->ReadInt( index );
304                         readDemo->ReadInt( immediate );
305                         EmitterForIndex( index )->Free( immediate != 0 );
306                 }
307                 break;
308         case SCMD_UPDATE:
309                 {
310                         idVec3 origin;
311                         int listenerId;
312                         soundShaderParms_t parms;
313
314                         readDemo->ReadInt( index );
315                         readDemo->ReadVec3( origin );
316                         readDemo->ReadInt( listenerId );
317                         readDemo->ReadFloat( parms.minDistance );
318                         readDemo->ReadFloat( parms.maxDistance );
319                         readDemo->ReadFloat( parms.volume );
320                         readDemo->ReadFloat( parms.shakes );
321                         readDemo->ReadInt( parms.soundShaderFlags );
322                         readDemo->ReadInt( parms.soundClass );
323                         EmitterForIndex( index )->UpdateEmitter( origin, listenerId, &parms );
324                 }
325                 break;
326         case SCMD_START:
327                 {
328                         const idSoundShader *shader;
329                         int                     channel;
330                         float           diversity;
331                         int                     shaderFlags;
332
333                         readDemo->ReadInt( index );
334                         shader = declManager->FindSound( readDemo->ReadHashString() );
335                         readDemo->ReadInt( channel );
336                         readDemo->ReadFloat( diversity );
337                         readDemo->ReadInt( shaderFlags );
338                         EmitterForIndex( index )->StartSound( shader, (s_channelType)channel, diversity, shaderFlags );
339                 }
340                 break;
341         case SCMD_MODIFY:
342                 {
343                         int             channel;
344                         soundShaderParms_t parms;
345
346                         readDemo->ReadInt( index );
347                         readDemo->ReadInt( channel );
348                         readDemo->ReadFloat( parms.minDistance );
349                         readDemo->ReadFloat( parms.maxDistance );
350                         readDemo->ReadFloat( parms.volume );
351                         readDemo->ReadFloat( parms.shakes );
352                         readDemo->ReadInt( parms.soundShaderFlags );
353                         readDemo->ReadInt( parms.soundClass );
354                         EmitterForIndex( index )->ModifySound( (s_channelType)channel, &parms );
355                 }
356                 break;
357         case SCMD_STOP:
358                 {
359                         int             channel;
360
361                         readDemo->ReadInt( index );
362                         readDemo->ReadInt( channel );
363                         EmitterForIndex( index )->StopSound( (s_channelType)channel );
364                 }
365                 break;
366         case SCMD_FADE:
367                 {
368                         int             channel;
369                         float   to, over;
370
371                         readDemo->ReadInt( index );
372                         readDemo->ReadInt( channel );
373                         readDemo->ReadFloat( to );
374                         readDemo->ReadFloat( over );
375                         EmitterForIndex( index )->FadeSound((s_channelType)channel, to, over );
376                 }
377                 break;
378         }
379 }
380
381 /*
382 ===================
383 idSoundWorldLocal::CurrentShakeAmplitudeForPosition
384
385   this is called from the main thread
386 ===================
387 */
388 float idSoundWorldLocal::CurrentShakeAmplitudeForPosition( const int time, const idVec3 &listererPosition ) {
389         float amp = 0.0f;
390         int localTime;
391
392         if ( idSoundSystemLocal::s_constantAmplitude.GetFloat() >= 0.0f ) {
393                 return 0.0f;
394         }
395
396         localTime = soundSystemLocal.GetCurrent44kHzTime();
397
398         for ( int i = 1; i < emitters.Num(); i++ ) {
399                 idSoundEmitterLocal *sound = emitters[i];
400                 if ( !sound->hasShakes ) {
401                         continue;
402                 }
403                 amp += FindAmplitude( sound, localTime, &listererPosition, SCHANNEL_ANY, true );
404         }
405         return amp;
406 }
407
408 /*
409 ===================
410 idSoundWorldLocal::MixLoop
411
412 Sum all sound contributions into finalMixBuffer, an unclamped float buffer holding
413 all output channels.  MIXBUFFER_SAMPLES samples will be created, with each sample consisting
414 of 2 or 6 floats depending on numSpeakers.
415
416 this is normally called from the sound thread, but also from the main thread
417 for AVIdemo writing
418 ===================
419 */
420 void idSoundWorldLocal::MixLoop( int current44kHz, int numSpeakers, float *finalMixBuffer ) {
421         int i, j;
422         idSoundEmitterLocal *sound;
423
424         // if noclip flying outside the world, leave silence
425         if ( listenerArea == -1 ) {
426                 if ( idSoundSystemLocal::useOpenAL )
427                         alListenerf( AL_GAIN, 0.0f );
428                 return;
429         } 
430
431         // update the listener position and orientation
432         if ( idSoundSystemLocal::useOpenAL ) {
433                 ALfloat listenerPosition[3];
434
435                 listenerPosition[0] = -listenerPos.y;
436                 listenerPosition[1] =  listenerPos.z;
437                 listenerPosition[2] = -listenerPos.x;
438
439                 ALfloat listenerOrientation[6];
440
441                 listenerOrientation[0] = -listenerAxis[0].y;
442                 listenerOrientation[1] =  listenerAxis[0].z;
443                 listenerOrientation[2] = -listenerAxis[0].x;
444
445                 listenerOrientation[3] = -listenerAxis[2].y;
446                 listenerOrientation[4] =  listenerAxis[2].z;
447                 listenerOrientation[5] = -listenerAxis[2].x;
448
449                 alListenerf( AL_GAIN, 1.0f );
450                 alListenerfv( AL_POSITION, listenerPosition );
451                 alListenerfv( AL_ORIENTATION, listenerOrientation );
452
453 #if ID_OPENAL
454                 if ( soundSystemLocal.s_useEAXReverb.GetBool() ) {
455                         if ( soundSystemLocal.efxloaded ) {
456                                 idSoundEffect *effect = NULL;
457                                 int EnvironmentID = -1;
458                                 idStr defaultStr( "default" );
459                                 idStr listenerAreaStr( listenerArea );
460                                 
461                                 soundSystemLocal.EFXDatabase.FindEffect( listenerAreaStr, &effect, &EnvironmentID );
462                                 if (!effect)
463                                         soundSystemLocal.EFXDatabase.FindEffect( listenerAreaName, &effect, &EnvironmentID );
464                                 if (!effect)
465                                         soundSystemLocal.EFXDatabase.FindEffect( defaultStr, &effect, &EnvironmentID );
466                                 
467                                 // only update if change in settings 
468                                 if ( soundSystemLocal.s_muteEAXReverb.GetBool() || ( listenerEnvironmentID != EnvironmentID ) ) {
469                                         EAXREVERBPROPERTIES EnvironmentParameters;
470                                         
471                                         // get area reverb setting from EAX Manager
472                                         if ( ( effect ) && ( effect->data) && ( memcpy( &EnvironmentParameters, effect->data, effect->datasize ) ) ) {
473                                                 if ( soundSystemLocal.s_muteEAXReverb.GetBool() ) {
474                                                         EnvironmentParameters.lRoom = -10000;
475                                                         EnvironmentID = -2;
476                                                 }
477                                                 if ( soundSystemLocal.alEAXSet ) {
478                                                         soundSystemLocal.alEAXSet( &EAXPROPERTYID_EAX_FXSlot0, EAXREVERB_ALLPARAMETERS, 0, &EnvironmentParameters, sizeof( EnvironmentParameters ) );
479                                                 }
480                                         }
481                                         listenerEnvironmentID = EnvironmentID;
482                                 }
483                         }
484                 }
485 #endif
486         }
487
488         // debugging option to mute all but a single soundEmitter
489         if ( idSoundSystemLocal::s_singleEmitter.GetInteger() > 0 && idSoundSystemLocal::s_singleEmitter.GetInteger() < emitters.Num() ) {
490                 sound = emitters[idSoundSystemLocal::s_singleEmitter.GetInteger()];
491
492                 if ( sound && sound->playing ) {
493                         // run through all the channels
494                         for ( j = 0; j < SOUND_MAX_CHANNELS ; j++ ) {
495                                 idSoundChannel  *chan = &sound->channels[j];
496
497                                 // see if we have a sound triggered on this channel
498                                 if ( !chan->triggerState ) {
499                                         chan->ALStop();
500                                         continue;
501                                 }
502
503                                 AddChannelContribution( sound, chan, current44kHz, numSpeakers, finalMixBuffer );
504                         }
505                 }
506                 return;
507         }
508
509         for ( i = 1; i < emitters.Num(); i++ ) {
510                 sound = emitters[i];
511
512                 if ( !sound ) {
513                         continue;
514                 }
515                 // if no channels are active, do nothing
516                 if ( !sound->playing ) {
517                         continue;
518                 }
519                 // run through all the channels
520                 for ( j = 0; j < SOUND_MAX_CHANNELS ; j++ ) {
521                         idSoundChannel  *chan = &sound->channels[j];
522
523                         // see if we have a sound triggered on this channel
524                         if ( !chan->triggerState ) {
525                                 chan->ALStop();
526                                 continue;
527                         }
528
529                         AddChannelContribution( sound, chan, current44kHz, numSpeakers, finalMixBuffer );
530                 }
531         }
532
533         if ( !idSoundSystemLocal::useOpenAL && enviroSuitActive ) {
534                 soundSystemLocal.DoEnviroSuit( finalMixBuffer, MIXBUFFER_SAMPLES, numSpeakers );
535         }
536 }
537
538 //==============================================================================
539
540 /*
541 ===================
542 idSoundWorldLocal::AVIOpen
543
544         this is called by the main thread
545 ===================
546 */
547 void idSoundWorldLocal::AVIOpen( const char *path, const char *name ) {
548         aviDemoPath = path;
549         aviDemoName = name;
550
551         lastAVI44kHz = game44kHz - game44kHz % MIXBUFFER_SAMPLES;
552
553         if ( soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers() == 6 ) {
554                 fpa[0] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_left.raw" );
555                 fpa[1] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_right.raw" );
556                 fpa[2] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_center.raw" );
557                 fpa[3] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_lfe.raw" );
558                 fpa[4] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_backleft.raw" );
559                 fpa[5] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_backright.raw" );
560         } else {
561                 fpa[0] = fileSystem->OpenFileWrite( aviDemoPath + "channel_left.raw" );
562                 fpa[1] = fileSystem->OpenFileWrite( aviDemoPath + "channel_right.raw" );
563         }
564
565         soundSystemLocal.SetMute( true );
566 }
567
568 /*
569 ===================
570 idSoundWorldLocal::AVIUpdate
571
572 this is called by the main thread
573 writes one block of sound samples if enough time has passed
574 This can be used to write wave files even if no sound hardware exists
575 ===================
576 */
577 void idSoundWorldLocal::AVIUpdate() {
578         int             numSpeakers;
579         
580         if ( game44kHz - lastAVI44kHz < MIXBUFFER_SAMPLES ) {
581                 return;
582         }
583
584         if ( !soundSystemLocal.snd_audio_hw ) {
585                 numSpeakers = 2;
586         } else {
587                 numSpeakers = soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers();
588         }
589
590         float   mix[MIXBUFFER_SAMPLES*6+16];
591         float   *mix_p = (float *)((( int)mix + 15 ) & ~15);    // SIMD align
592
593         SIMDProcessor->Memset( mix_p, 0, MIXBUFFER_SAMPLES*sizeof(float)*numSpeakers );
594
595         MixLoop( lastAVI44kHz, numSpeakers, mix_p );
596
597         for ( int i = 0; i < numSpeakers; i++ ) {
598                 short outD[MIXBUFFER_SAMPLES];
599
600                 for( int j = 0; j < MIXBUFFER_SAMPLES; j++ ) {
601                         float s = mix_p[ j*numSpeakers + i];
602                         if ( s < -32768.0f ) {
603                                 outD[j] = -32768;
604                         } else if ( s > 32767.0f ) {
605                                 outD[j] = 32767;
606                         } else {
607                                 outD[j] = idMath::FtoiFast( s );
608                         }
609                 }
610                 // write to file
611                 fpa[i]->Write( outD, MIXBUFFER_SAMPLES*sizeof(short) );
612         }
613
614         lastAVI44kHz += MIXBUFFER_SAMPLES;
615
616         return;
617 }
618
619 /*
620 ===================
621 idSoundWorldLocal::AVIClose
622 ===================
623 */
624 void idSoundWorldLocal::AVIClose( void ) {
625         int i;
626
627         if ( !fpa[0] ) {
628                 return;
629         }
630
631         // make sure the final block is written
632         game44kHz += MIXBUFFER_SAMPLES;
633         AVIUpdate();
634         game44kHz -= MIXBUFFER_SAMPLES;
635
636         for ( i = 0; i < 6; i++ ) {
637                 if ( fpa[i] != NULL ) {
638                         fileSystem->CloseFile( fpa[i] );
639                         fpa[i] = NULL;
640                 }
641         }
642         if ( soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers() == 2 ) {
643                 // convert it to a wave file
644                 idFile *rL, *lL, *wO;
645                 idStr   name;
646
647                 name = aviDemoPath + aviDemoName + ".wav";
648                 wO = fileSystem->OpenFileWrite( name );
649                 if ( !wO ) {
650                         common->Error( "Couldn't write %s", name.c_str() );
651                 }
652
653                 name = aviDemoPath + "channel_right.raw";
654                 rL = fileSystem->OpenFileRead( name );
655                 if ( !rL ) {
656                         common->Error( "Couldn't open %s", name.c_str() );
657                 }
658
659                 name = aviDemoPath + "channel_left.raw";
660                 lL = fileSystem->OpenFileRead( name );
661                 if ( !lL ) {
662                         common->Error( "Couldn't open %s", name.c_str() );
663                 }
664
665                 int numSamples = rL->Length()/2;
666                 mminfo_t        info;
667                 pcmwaveformat_t format;
668                 
669                 info.ckid = fourcc_riff;
670                 info.fccType = mmioFOURCC( 'W', 'A', 'V', 'E' );
671                 info.cksize = (rL->Length()*2) - 8 + 4 + 16 + 8 + 8;
672                 info.dwDataOffset = 12;
673                 
674                 wO->Write( &info, 12 );
675
676                 info.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
677                 info.cksize = 16;
678
679                 wO->Write( &info, 8 );
680
681                 format.wBitsPerSample = 16;
682                 format.wf.nAvgBytesPerSec = 44100*4;            // sample rate * block align
683                 format.wf.nChannels = 2;
684                 format.wf.nSamplesPerSec = 44100;
685                 format.wf.wFormatTag = WAVE_FORMAT_TAG_PCM;
686                 format.wf.nBlockAlign = 4;                                      // channels * bits/sample / 8
687
688                 wO->Write( &format, 16 );
689
690                 info.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
691                 info.cksize = rL->Length() * 2;
692
693                 wO->Write( &info, 8 );
694
695                 short s0, s1;
696                 for( i = 0; i < numSamples; i++ ) {
697                         lL->Read( &s0, 2 );
698                         rL->Read( &s1, 2 );
699                         wO->Write( &s0, 2 );
700                         wO->Write( &s1, 2 );
701                 }
702
703                 fileSystem->CloseFile( wO );
704                 fileSystem->CloseFile( lL );
705                 fileSystem->CloseFile( rL );
706
707                 fileSystem->RemoveFile( aviDemoPath + "channel_right.raw" );
708                 fileSystem->RemoveFile( aviDemoPath + "channel_left.raw" );
709         }
710
711         soundSystemLocal.SetMute( false );
712 }
713
714 //==============================================================================
715
716
717 /*
718 ===================
719 idSoundWorldLocal::ResolveOrigin
720
721 Find out of the sound is completely occluded by a closed door portal, or
722 the virtual sound origin position at the portal closest to the listener.
723   this is called by the main thread
724
725 dist is the distance from the orignial sound origin to the current portal that enters soundArea
726 def->distance is the distance we are trying to reduce.
727
728 If there is no path through open portals from the sound to the listener, def->distance will remain
729 set at maxDistance
730 ===================
731 */
732 static const int MAX_PORTAL_TRACE_DEPTH = 10;
733
734 void idSoundWorldLocal::ResolveOrigin( const int stackDepth, const soundPortalTrace_t *prevStack, const int soundArea, const float dist, const idVec3& soundOrigin, idSoundEmitterLocal *def ) {
735
736         if ( dist >= def->distance ) {
737                 // we can't possibly hear the sound through this chain of portals
738                 return;
739         }
740
741         if ( soundArea == listenerArea ) {
742                 float   fullDist = dist + (soundOrigin - listenerQU).LengthFast();
743                 if ( fullDist < def->distance ) {
744                         def->distance = fullDist;
745                         def->spatializedOrigin = soundOrigin;
746                 }
747                 return;
748         }
749
750         if ( stackDepth == MAX_PORTAL_TRACE_DEPTH ) {
751                 // don't spend too much time doing these calculations in big maps
752                 return;
753         }
754
755         soundPortalTrace_t newStack;
756         newStack.portalArea = soundArea;
757         newStack.prevStack = prevStack;
758
759         int numPortals = rw->NumPortalsInArea( soundArea );
760         for( int p = 0; p < numPortals; p++ ) {
761                 exitPortal_t re = rw->GetPortal( soundArea, p );
762
763                 float   occlusionDistance = 0;
764
765                 // air blocking windows will block sound like closed doors
766                 if ( (re.blockingBits & ( PS_BLOCK_VIEW | PS_BLOCK_AIR ) ) ) {
767                         // we could just completely cut sound off, but reducing the volume works better
768                         // continue;
769                         occlusionDistance = idSoundSystemLocal::s_doorDistanceAdd.GetFloat();
770                 }
771
772                 // what area are we about to go look at
773                 int otherArea = re.areas[0];
774                 if ( re.areas[0] == soundArea ) {
775                         otherArea = re.areas[1];
776                 }
777
778                 // if this area is already in our portal chain, don't bother looking into it
779                 const soundPortalTrace_t *prev;
780                 for ( prev = prevStack ; prev ; prev = prev->prevStack ) {
781                         if ( prev->portalArea == otherArea ) {
782                                 break;
783                         }
784                 }
785                 if ( prev ) {
786                         continue;
787                 }
788
789                 // pick a point on the portal to serve as our virtual sound origin
790 #if 1
791                 idVec3  source;
792
793                 idPlane pl;
794                 re.w->GetPlane( pl );
795
796                 float   scale;
797                 idVec3  dir = listenerQU - soundOrigin;
798                 if ( !pl.RayIntersection( soundOrigin, dir, scale ) ) {
799                         source = re.w->GetCenter();
800                 } else {
801                         source = soundOrigin + scale * dir;
802
803                         // if this point isn't inside the portal edges, slide it in
804                         for ( int i = 0 ; i < re.w->GetNumPoints() ; i++ ) {
805                                 int j = ( i + 1 ) % re.w->GetNumPoints();
806                                 idVec3  edgeDir = (*(re.w))[j].ToVec3() - (*(re.w))[i].ToVec3();
807                                 idVec3  edgeNormal;
808
809                                 edgeNormal.Cross( pl.Normal(), edgeDir );
810
811                                 idVec3  fromVert = source - (*(re.w))[j].ToVec3();
812
813                                 float   d = edgeNormal * fromVert;
814                                 if ( d > 0 ) {
815                                         // move it in
816                                         float div = edgeNormal.Normalize();
817                                         d /= div;
818
819                                         source -= d * edgeNormal;
820                                 }
821                         }
822                 }
823 #else
824                 // clip the ray from the listener to the center of the portal by
825                 // all the portal edge planes, then project that point (or the original if not clipped)
826                 // onto the portal plane to get the spatialized origin
827
828                 idVec3  start = listenerQU;
829                 idVec3  mid = re.w->GetCenter();
830                 bool    wasClipped = false;
831
832                 for ( int i = 0 ; i < re.w->GetNumPoints() ; i++ ) {
833                         int j = ( i + 1 ) % re.w->GetNumPoints();
834                         idVec3  v1 = (*(re.w))[j].ToVec3() - soundOrigin;
835                         idVec3  v2 = (*(re.w))[i].ToVec3() - soundOrigin;
836
837                         v1.Normalize();
838                         v2.Normalize();
839
840                         idVec3  edgeNormal;
841
842                         edgeNormal.Cross( v1, v2 );
843
844                         idVec3  fromVert = start - soundOrigin;
845                         float   d1 = edgeNormal * fromVert;
846
847                         if ( d1 > 0.0f ) {
848                                 fromVert = mid - (*(re.w))[j].ToVec3();
849                                 float d2 = edgeNormal * fromVert;
850
851                                 // move it in
852                                 float   f = d1 / ( d1 - d2 );
853
854                                 idVec3  clipped = start * ( 1.0f - f ) + mid * f;
855                                 start = clipped;
856                                 wasClipped = true;
857                         }
858                 }
859
860                 idVec3  source;
861                 if ( wasClipped ) {
862                         // now project it onto the portal plane
863                         idPlane pl;
864                         re.w->GetPlane( pl );
865
866                         float   f1 = pl.Distance( start );
867                         float   f2 = pl.Distance( soundOrigin );
868
869                         float   f = f1 / ( f1 - f2 );
870                         source = start * ( 1.0f - f ) + soundOrigin * f;
871                 } else {
872                         source = soundOrigin;
873                 }
874 #endif
875
876                 idVec3 tlen = source - soundOrigin;
877                 float tlenLength = tlen.LengthFast();
878
879                 ResolveOrigin( stackDepth+1, &newStack, otherArea, dist+tlenLength+occlusionDistance, source, def );
880         }
881 }
882
883
884 /*
885 ===================
886 idSoundWorldLocal::PlaceListener
887
888   this is called by the main thread
889 ===================
890 */
891 void idSoundWorldLocal::PlaceListener( const idVec3& origin, const idMat3& axis, 
892                                                                         const int listenerId, const int gameTime, const idStr& areaName  ) {
893
894         int current44kHzTime;
895
896         if ( !soundSystemLocal.isInitialized ) {
897                 return;
898         }
899
900         if ( pause44kHz >= 0 ){
901                 return;
902         }
903
904         if ( writeDemo ) {
905                 writeDemo->WriteInt( DS_SOUND );
906                 writeDemo->WriteInt( SCMD_PLACE_LISTENER );
907                 writeDemo->WriteVec3( origin );
908                 writeDemo->WriteMat3( axis );
909                 writeDemo->WriteInt( listenerId );
910                 writeDemo->WriteInt( gameTime );
911         }
912
913         current44kHzTime = soundSystemLocal.GetCurrent44kHzTime();
914
915         // we usually expect gameTime to be increasing by 16 or 32 msec, but when
916         // a cinematic is fast-forward skipped through, it can jump by a significant
917         // amount, while the hardware 44kHz position will not have changed accordingly,
918         // which would make sounds (like long character speaches) continue from the
919         // old time.  Fix this by killing all non-looping sounds
920         if ( gameTime > gameMsec + 500 ) {
921                 OffsetSoundTime( - ( gameTime - gameMsec ) * 0.001f * 44100.0f );
922         }
923
924         gameMsec = gameTime;
925         if ( fpa[0] ) {
926                 // exactly 30 fps so the wave file can be used for exact video frames
927                 game44kHz = idMath::FtoiFast( gameMsec * ( ( 1000.0f / 60.0f ) / 16.0f ) * 0.001f * 44100.0f );
928         } else {
929                 // the normal 16 msec / frame
930                 game44kHz = idMath::FtoiFast( gameMsec * 0.001f * 44100.0f );
931         }
932
933         listenerPrivateId = listenerId;
934
935         listenerQU = origin;                                                    // Doom units
936         listenerPos = origin * DOOM_TO_METERS;                  // meters
937         listenerAxis = axis;
938         listenerAreaName = areaName;
939         listenerAreaName.ToLower();
940
941         if ( rw ) {
942                 listenerArea = rw->PointInArea( listenerQU );   // where are we?
943         } else {
944                 listenerArea = 0;
945         }
946
947         if ( listenerArea < 0 ) {
948                 return;
949         }
950
951         ForegroundUpdate( current44kHzTime );
952 }
953
954 /*
955 ==================
956 idSoundWorldLocal::ForegroundUpdate
957 ==================
958 */
959 void idSoundWorldLocal::ForegroundUpdate( int current44kHzTime ) {
960         int j, k;
961         idSoundEmitterLocal     *def;
962
963         if ( !soundSystemLocal.isInitialized ) {
964                 return;
965         }
966
967         Sys_EnterCriticalSection();
968
969         // if we are recording an AVI demo, don't use hardware time
970         if ( fpa[0] ) {
971                 current44kHzTime = lastAVI44kHz;
972         }
973
974         //
975         // check to see if each sound is visible or not
976         // speed up by checking maxdistance to origin
977         // although the sound may still need to play if it has
978         // just become occluded so it can ramp down to 0
979         //
980         for ( j = 1; j < emitters.Num(); j++ ) {
981                 def = emitters[j];
982
983                 if ( def->removeStatus >= REMOVE_STATUS_SAMPLEFINISHED ) {
984                         continue;
985                 }
986
987                 // see if our last channel just finished
988                 def->CheckForCompletion( current44kHzTime );
989
990                 if ( !def->playing ) {
991                         continue;
992                 }
993
994                 // update virtual origin / distance, etc
995                 def->Spatialize( listenerPos, listenerArea, rw );
996
997                 // per-sound debug options
998                 if ( idSoundSystemLocal::s_drawSounds.GetInteger() && rw ) {
999                         if ( def->distance < def->maxDistance || idSoundSystemLocal::s_drawSounds.GetInteger() > 1 ) {
1000                                 idBounds ref;
1001                                 ref.Clear();
1002                                 ref.AddPoint( idVec3( -10, -10, -10 ) );
1003                                 ref.AddPoint( idVec3(  10,  10,  10 ) );
1004                                 float vis = (1.0f - (def->distance / def->maxDistance));
1005
1006                                 // draw a box
1007                                 rw->DebugBounds( idVec4( vis, 0.25f, vis, vis ), ref, def->origin );
1008
1009                                 // draw an arrow to the audible position, possible a portal center
1010                                 if ( def->origin != def->spatializedOrigin ) {
1011                                         rw->DebugArrow( colorRed, def->origin, def->spatializedOrigin, 4 );
1012                                 }
1013
1014                                 // draw the index
1015                                 idVec3  textPos = def->origin;
1016                                 textPos[2] -= 8;
1017                                 rw->DrawText( va("%i", def->index), textPos, 0.1f, idVec4(1,0,0,1), listenerAxis );
1018                                 textPos[2] += 8;
1019
1020                                 // run through all the channels
1021                                 for ( k = 0; k < SOUND_MAX_CHANNELS ; k++ ) {
1022                                         idSoundChannel  *chan = &def->channels[k];
1023
1024                                         // see if we have a sound triggered on this channel
1025                                         if ( !chan->triggerState ) {
1026                                                 continue;
1027                                         }
1028
1029                                         char    text[1024];
1030                                         float   min = chan->parms.minDistance;
1031                                         float   max = chan->parms.maxDistance;
1032                                         const char      *defaulted = chan->leadinSample->defaultSound ? "(DEFAULTED)" : "";
1033                                         sprintf( text, "%s (%i/%i %i/%i)%s", chan->soundShader->GetName(), (int)def->distance,
1034                                                 (int)def->realDistance, (int)min, (int)max, defaulted );
1035                                         rw->DrawText( text, textPos, 0.1f, idVec4(1,0,0,1), listenerAxis );
1036                                         textPos[2] += 8;
1037                                 }
1038                         }
1039                 }
1040         }
1041
1042         Sys_LeaveCriticalSection();
1043
1044         //
1045         // the sound meter
1046         //
1047         if ( idSoundSystemLocal::s_showLevelMeter.GetInteger() ) {
1048                 const idMaterial *gui = declManager->FindMaterial( "guis/assets/soundmeter/audiobg", false );
1049                 if ( gui ) {
1050                         const shaderStage_t *foo = gui->GetStage(0);
1051                         if ( !foo->texture.cinematic ) {
1052                                 ((shaderStage_t *)foo)->texture.cinematic = new idSndWindow;
1053                         }
1054                 }
1055         }
1056
1057         //
1058         // optionally dump out the generated sound
1059         //
1060         if ( fpa[0] ) {
1061                 AVIUpdate();
1062         }
1063 }
1064
1065 /*
1066 ===================
1067 idSoundWorldLocal::OffsetSoundTime
1068 ===================
1069 */
1070 void idSoundWorldLocal::OffsetSoundTime( int offset44kHz ) {
1071         int i, j;
1072
1073         for ( i = 0; i < emitters.Num(); i++ ) {
1074                 if ( emitters[i] == NULL ) {
1075                         continue;
1076                 }
1077                 for ( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
1078                         idSoundChannel *chan = &emitters[i]->channels[ j ];
1079
1080                         if ( !chan->triggerState ) {
1081                                 continue;
1082                         }
1083
1084                         chan->trigger44kHzTime += offset44kHz;
1085                 }
1086         }
1087 }
1088
1089 /*
1090 ===================
1091 idSoundWorldLocal::WriteToSaveGame
1092 ===================
1093 */
1094 void idSoundWorldLocal::WriteToSaveGame( idFile *savefile ) {
1095         int i, j, num, currentSoundTime;
1096         const char *name;
1097
1098         // the game soundworld is always paused at this point, save that time down
1099         if ( pause44kHz > 0 ) {
1100                 currentSoundTime = pause44kHz;
1101         } else {
1102                 currentSoundTime = soundSystemLocal.GetCurrent44kHzTime();
1103         }
1104
1105         // write listener data
1106         savefile->WriteVec3(listenerQU);
1107         savefile->WriteMat3(listenerAxis);
1108         savefile->WriteInt(listenerPrivateId);
1109         savefile->WriteInt(gameMsec);
1110         savefile->WriteInt(game44kHz);
1111         savefile->WriteInt(currentSoundTime);
1112
1113         num = emitters.Num();
1114         savefile->WriteInt(num);
1115
1116         for ( i = 1; i < emitters.Num(); i++ ) {
1117                 idSoundEmitterLocal *def = emitters[i];
1118
1119                 if ( def->removeStatus != REMOVE_STATUS_ALIVE ) {
1120                         int skip = -1;
1121                         savefile->Write( &skip, sizeof( skip ) );
1122                         continue;
1123                 }
1124
1125                 savefile->WriteInt(i);
1126
1127                 // Write the emitter data
1128                 savefile->WriteVec3( def->origin );
1129                 savefile->WriteInt( def->listenerId );
1130                 WriteToSaveGameSoundShaderParams( savefile, &def->parms );
1131                 savefile->WriteFloat( def->amplitude );
1132                 savefile->WriteInt( def->ampTime );
1133                 for (int k = 0; k < SOUND_MAX_CHANNELS; k++) 
1134                         WriteToSaveGameSoundChannel( savefile, &def->channels[k] );
1135                 savefile->WriteFloat( def->distance );
1136                 savefile->WriteBool( def->hasShakes );
1137                 savefile->WriteInt( def->lastValidPortalArea );
1138                 savefile->WriteFloat( def->maxDistance );
1139                 savefile->WriteBool( def->playing );
1140                 savefile->WriteFloat( def->realDistance );
1141                 savefile->WriteInt( def->removeStatus );
1142                 savefile->WriteVec3( def->spatializedOrigin );
1143
1144                 // write the channel data
1145                 for( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
1146                         idSoundChannel *chan = &def->channels[ j ];
1147
1148                         // Write out any sound commands for this def
1149                         if ( chan->triggerState && chan->soundShader && chan->leadinSample ) {
1150
1151                                 savefile->WriteInt( j );
1152
1153                                 // write the pointers out separately
1154                                 name = chan->soundShader->GetName();
1155                                 savefile->WriteString( name );
1156                                 
1157                                 name = chan->leadinSample->name;
1158                                 savefile->WriteString( name );
1159                         }
1160                 }
1161
1162                 // End active channels with -1
1163                 int end = -1;
1164                 savefile->WriteInt( end );
1165         }
1166
1167         // new in Doom3 v1.2
1168         savefile->Write( &slowmoActive, sizeof( slowmoActive ) );
1169         savefile->Write( &slowmoSpeed, sizeof( slowmoSpeed ) );
1170         savefile->Write( &enviroSuitActive, sizeof( enviroSuitActive ) );
1171 }
1172
1173 /*
1174  ===================
1175  idSoundWorldLocal::WriteToSaveGameSoundShaderParams
1176  ===================
1177  */
1178 void idSoundWorldLocal::WriteToSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params ) {
1179         saveGame->WriteFloat(params->minDistance);
1180         saveGame->WriteFloat(params->maxDistance);
1181         saveGame->WriteFloat(params->volume);
1182         saveGame->WriteFloat(params->shakes);
1183         saveGame->WriteInt(params->soundShaderFlags);
1184         saveGame->WriteInt(params->soundClass);
1185 }
1186
1187 /*
1188  ===================
1189  idSoundWorldLocal::WriteToSaveGameSoundChannel
1190  ===================
1191  */
1192 void idSoundWorldLocal::WriteToSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch ) {
1193         saveGame->WriteBool( ch->triggerState );
1194         saveGame->WriteUnsignedChar( 0 );
1195         saveGame->WriteUnsignedChar( 0 );
1196         saveGame->WriteUnsignedChar( 0 );
1197         saveGame->WriteInt( ch->trigger44kHzTime );
1198         saveGame->WriteInt( ch->triggerGame44kHzTime );
1199         WriteToSaveGameSoundShaderParams( saveGame, &ch->parms );
1200         saveGame->WriteInt( (int)ch->leadinSample );
1201         saveGame->WriteInt( ch->triggerChannel );
1202         saveGame->WriteInt( (int)ch->soundShader );
1203         saveGame->WriteInt( (int)ch->decoder );
1204         saveGame->WriteFloat(ch->diversity );
1205         saveGame->WriteFloat(ch->lastVolume );
1206         for (int m = 0; m < 6; m++)
1207                 saveGame->WriteFloat( ch->lastV[m] );
1208         saveGame->WriteInt( ch->channelFade.fadeStart44kHz );
1209         saveGame->WriteInt( ch->channelFade.fadeEnd44kHz );
1210         saveGame->WriteFloat( ch->channelFade.fadeStartVolume );
1211         saveGame->WriteFloat( ch->channelFade.fadeEndVolume );
1212 }
1213
1214 /*
1215 ===================
1216 idSoundWorldLocal::ReadFromSaveGame
1217 ===================
1218 */
1219 void idSoundWorldLocal::ReadFromSaveGame( idFile *savefile ) {
1220         int i, num, handle, listenerId, gameTime, channel;
1221         int savedSoundTime, currentSoundTime, soundTimeOffset;
1222         idSoundEmitterLocal *def;
1223         idVec3 origin;
1224         idMat3 axis;
1225         idStr soundShader;
1226
1227         ClearAllSoundEmitters();
1228
1229         savefile->ReadVec3( origin );
1230         savefile->ReadMat3( axis );
1231         savefile->ReadInt( listenerId );
1232         savefile->ReadInt( gameTime );
1233         savefile->ReadInt( game44kHz );
1234         savefile->ReadInt( savedSoundTime );
1235
1236         // we will adjust the sound starting times from those saved with the demo
1237         currentSoundTime = soundSystemLocal.GetCurrent44kHzTime();
1238         soundTimeOffset = currentSoundTime - savedSoundTime;
1239
1240         // at the end of the level load we unpause the sound world and adjust the sound starting times once more
1241         pause44kHz = currentSoundTime;
1242
1243         // place listener
1244         PlaceListener( origin, axis, listenerId, gameTime, "Undefined" );
1245
1246         // make sure there are enough
1247         // slots to read the saveGame in.  We don't shrink the list
1248         // if there are extras.
1249         savefile->ReadInt( num );
1250
1251         while( emitters.Num() < num ) {
1252                 def = new idSoundEmitterLocal;
1253                 def->index = emitters.Append( def );
1254                 def->soundWorld = this;
1255         }
1256
1257         // read in the state
1258         for ( i = 1; i < num; i++ ) {
1259
1260                 savefile->ReadInt( handle );
1261                 if ( handle < 0 ) {
1262                         continue;
1263                 }
1264                 if ( handle != i ) {
1265                         common->Error( "idSoundWorldLocal::ReadFromSaveGame: index mismatch" );
1266                 }
1267                 def = emitters[i];
1268
1269                 def->removeStatus = REMOVE_STATUS_ALIVE;
1270                 def->playing = true;            // may be reset by the first UpdateListener
1271
1272                 savefile->ReadVec3( def->origin );
1273                 savefile->ReadInt( def->listenerId );
1274                 ReadFromSaveGameSoundShaderParams( savefile, &def->parms );
1275                 savefile->ReadFloat( def->amplitude );
1276                 savefile->ReadInt( def->ampTime );
1277                 for (int k = 0; k < SOUND_MAX_CHANNELS; k++) 
1278                         ReadFromSaveGameSoundChannel( savefile, &def->channels[k] );
1279                 savefile->ReadFloat( def->distance );
1280                 savefile->ReadBool( def->hasShakes );
1281                 savefile->ReadInt( def->lastValidPortalArea );
1282                 savefile->ReadFloat( def->maxDistance );
1283                 savefile->ReadBool( def->playing );
1284                 savefile->ReadFloat( def->realDistance );
1285                 savefile->ReadInt( (int&)def->removeStatus );
1286                 savefile->ReadVec3( def->spatializedOrigin );
1287
1288                 // read the individual channels
1289                 savefile->ReadInt( channel );
1290
1291                 while ( channel >= 0 ) {
1292                         if ( channel > SOUND_MAX_CHANNELS ) {
1293                                 common->Error( "idSoundWorldLocal::ReadFromSaveGame: channel > SOUND_MAX_CHANNELS" );
1294                         }
1295
1296                         idSoundChannel *chan = &def->channels[channel];
1297
1298                         if ( chan->decoder != NULL ) {
1299                                 // The pointer in the save file is not valid, so we grab a new one
1300                                 chan->decoder = idSampleDecoder::Alloc();
1301                         }
1302
1303                         savefile->ReadString( soundShader );
1304                         chan->soundShader = declManager->FindSound( soundShader );
1305
1306                         savefile->ReadString( soundShader );
1307                         // load savegames with s_noSound 1
1308                         if ( soundSystemLocal.soundCache ) {
1309                                 chan->leadinSample = soundSystemLocal.soundCache->FindSound( soundShader, false );
1310                         } else {
1311                                 chan->leadinSample = NULL;
1312                         }
1313
1314                         // adjust the hardware start time
1315                         chan->trigger44kHzTime += soundTimeOffset;
1316
1317                         // make sure we start up the hardware voice if needed
1318                         chan->triggered = chan->triggerState;
1319                         chan->openalStreamingOffset = currentSoundTime - chan->trigger44kHzTime;
1320
1321                         // adjust the hardware fade time
1322                         if ( chan->channelFade.fadeStart44kHz != 0 ) {
1323                                 chan->channelFade.fadeStart44kHz += soundTimeOffset;
1324                                 chan->channelFade.fadeEnd44kHz += soundTimeOffset;
1325                         }
1326
1327                         // next command
1328                         savefile->ReadInt( channel );
1329                 }
1330         }
1331
1332         if ( session->GetSaveGameVersion() >= 17 ) {
1333                 savefile->Read( &slowmoActive, sizeof( slowmoActive ) );
1334                 savefile->Read( &slowmoSpeed, sizeof( slowmoSpeed ) );
1335                 savefile->Read( &enviroSuitActive, sizeof( enviroSuitActive ) );
1336         } else {
1337                 slowmoActive            = false;
1338                 slowmoSpeed                     = 0;
1339                 enviroSuitActive        = false;
1340         }
1341 }
1342
1343 /*
1344  ===================
1345  idSoundWorldLocal::ReadFromSaveGameSoundShaderParams
1346  ===================
1347  */
1348 void idSoundWorldLocal::ReadFromSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params ) {
1349         saveGame->ReadFloat(params->minDistance);
1350         saveGame->ReadFloat(params->maxDistance);
1351         saveGame->ReadFloat(params->volume);
1352         saveGame->ReadFloat(params->shakes);
1353         saveGame->ReadInt(params->soundShaderFlags);
1354         saveGame->ReadInt(params->soundClass);
1355 }
1356
1357 /*
1358  ===================
1359  idSoundWorldLocal::ReadFromSaveGameSoundChannel
1360  ===================
1361  */
1362 void idSoundWorldLocal::ReadFromSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch ) {
1363         saveGame->ReadBool( ch->triggerState );
1364         char tmp;
1365         saveGame->ReadChar( tmp );
1366         saveGame->ReadChar( tmp );
1367         saveGame->ReadChar( tmp );
1368         saveGame->ReadInt( ch->trigger44kHzTime );
1369         saveGame->ReadInt( ch->triggerGame44kHzTime );
1370         ReadFromSaveGameSoundShaderParams( saveGame, &ch->parms );
1371         saveGame->ReadInt( (int&)ch->leadinSample );
1372         saveGame->ReadInt( ch->triggerChannel );
1373         saveGame->ReadInt( (int&)ch->soundShader );
1374         saveGame->ReadInt( (int&)ch->decoder );
1375         saveGame->ReadFloat(ch->diversity );
1376         saveGame->ReadFloat(ch->lastVolume );
1377         for (int m = 0; m < 6; m++)
1378                 saveGame->ReadFloat( ch->lastV[m] );
1379         saveGame->ReadInt( ch->channelFade.fadeStart44kHz );
1380         saveGame->ReadInt( ch->channelFade.fadeEnd44kHz );
1381         saveGame->ReadFloat( ch->channelFade.fadeStartVolume );
1382         saveGame->ReadFloat( ch->channelFade.fadeEndVolume );
1383 }
1384
1385 /*
1386 ===================
1387 idSoundWorldLocal::EmitterForIndex
1388 ===================
1389 */
1390 idSoundEmitter  *idSoundWorldLocal::EmitterForIndex( int index ) {
1391         if ( index == 0 ) {
1392                 return NULL;
1393         }
1394         if ( index >= emitters.Num() ) {
1395                 common->Error( "idSoundWorldLocal::EmitterForIndex: %i > %i", index, emitters.Num() );
1396         }
1397         return emitters[index];
1398 }
1399
1400 /*
1401 ===============
1402 idSoundWorldLocal::StopAllSounds
1403
1404   this is called from the main thread
1405 ===============
1406 */
1407 void idSoundWorldLocal::StopAllSounds() {
1408
1409         for ( int i = 0; i < emitters.Num(); i++ ) {
1410                 idSoundEmitterLocal * def = emitters[i];
1411                 def->StopSound( SCHANNEL_ANY );
1412         }
1413 }
1414
1415 /*
1416 ===============
1417 idSoundWorldLocal::Pause
1418 ===============
1419 */
1420 void idSoundWorldLocal::Pause( void ) {
1421         if ( pause44kHz >= 0 ) {
1422                 common->Warning( "idSoundWorldLocal::Pause: already paused" );
1423                 return;
1424         }
1425
1426         pause44kHz = soundSystemLocal.GetCurrent44kHzTime();
1427 }
1428
1429 /*
1430 ===============
1431 idSoundWorldLocal::UnPause
1432 ===============
1433 */
1434 void idSoundWorldLocal::UnPause( void ) {
1435         int offset44kHz;
1436
1437         if ( pause44kHz < 0 ) {
1438                 common->Warning( "idSoundWorldLocal::UnPause: not paused" );
1439                 return;
1440         }
1441
1442         offset44kHz = soundSystemLocal.GetCurrent44kHzTime() - pause44kHz;
1443         OffsetSoundTime( offset44kHz );
1444
1445         pause44kHz = -1;
1446 }
1447
1448 /*
1449 ===============
1450 idSoundWorldLocal::IsPaused
1451 ===============
1452 */
1453 bool idSoundWorldLocal::IsPaused( void ) {
1454         return ( pause44kHz >= 0 );
1455 }
1456
1457 /*
1458 ===============
1459 idSoundWorldLocal::PlayShaderDirectly
1460
1461 start a music track
1462
1463   this is called from the main thread
1464 ===============
1465 */
1466 void idSoundWorldLocal::PlayShaderDirectly( const char *shaderName, int channel ) {
1467
1468         if ( localSound && channel == -1 ) {
1469                 localSound->StopSound( SCHANNEL_ANY );
1470         } else if ( localSound ) {
1471                 localSound->StopSound( channel );
1472         }
1473
1474         if ( !shaderName || !shaderName[0] ) {
1475                 return;
1476         }
1477
1478         const idSoundShader *shader = declManager->FindSound( shaderName );
1479         if ( !shader ) {
1480                 return;
1481         }
1482
1483         if ( !localSound ) {
1484                 localSound = AllocLocalSoundEmitter();
1485         }
1486
1487         static idRandom rnd;
1488         float   diversity = rnd.RandomFloat();
1489
1490         localSound->StartSound( shader, ( channel == -1 ) ? SCHANNEL_ONE : channel , diversity, SSF_GLOBAL );
1491
1492         // in case we are at the console without a game doing updates, force an update
1493         ForegroundUpdate( soundSystemLocal.GetCurrent44kHzTime() );
1494 }
1495
1496 /*
1497 ===============
1498 idSoundWorldLocal::CalcEars
1499
1500 Determine the volumes from each speaker for a given sound emitter
1501 ===============
1502 */
1503 void idSoundWorldLocal::CalcEars( int numSpeakers, idVec3 spatializedOrigin, idVec3 listenerPos,
1504                                                                  idMat3 listenerAxis, float ears[6], float spatialize ) {
1505         idVec3 svec = spatializedOrigin - listenerPos;
1506         idVec3 ovec;
1507
1508         ovec[0] = svec * listenerAxis[0];
1509         ovec[1] = svec * listenerAxis[1];
1510         ovec[2] = svec * listenerAxis[2];
1511
1512         ovec.Normalize();
1513
1514         if ( numSpeakers == 6 ) {
1515                 static idVec3   speakerVector[6] = {
1516                         idVec3(  0.707f,  0.707f, 0.0f ),       // front left
1517                         idVec3(  0.707f, -0.707f, 0.0f ),       // front right
1518                         idVec3(  0.707f,  0.0f,   0.0f ),       // front center
1519                         idVec3(  0.0f,    0.0f,   0.0f ),       // sub
1520                         idVec3( -0.707f,  0.707f, 0.0f ),       // rear left
1521                         idVec3( -0.707f, -0.707f, 0.0f )        // rear right
1522                 };
1523                 for ( int i = 0 ; i < 6 ; i++ ) {
1524                         if ( i == 3 ) {
1525                                 ears[i] = idSoundSystemLocal::s_subFraction.GetFloat();         // subwoofer
1526                                 continue;
1527                         }
1528                         float dot = ovec * speakerVector[i];
1529                         ears[i] = (idSoundSystemLocal::s_dotbias6.GetFloat() + dot) / ( 1.0f + idSoundSystemLocal::s_dotbias6.GetFloat() );
1530                         if ( ears[i] < idSoundSystemLocal::s_minVolume6.GetFloat() ) {
1531                                 ears[i] = idSoundSystemLocal::s_minVolume6.GetFloat();
1532                         }
1533                 }
1534         } else {
1535                 float dot = ovec.y;
1536                 float dotBias = idSoundSystemLocal::s_dotbias2.GetFloat();
1537
1538                 // when we are inside the minDistance, start reducing the amount of spatialization
1539                 // so NPC voices right in front of us aren't quieter that off to the side
1540                 dotBias += ( idSoundSystemLocal::s_spatializationDecay.GetFloat() - dotBias ) * ( 1.0f - spatialize );
1541
1542                 ears[0] = (idSoundSystemLocal::s_dotbias2.GetFloat() + dot) / ( 1.0f + dotBias );
1543                 ears[1] = (idSoundSystemLocal::s_dotbias2.GetFloat() - dot) / ( 1.0f + dotBias );
1544
1545                 if ( ears[0] < idSoundSystemLocal::s_minVolume2.GetFloat() ) {
1546                         ears[0] = idSoundSystemLocal::s_minVolume2.GetFloat();
1547                 }
1548                 if ( ears[1] < idSoundSystemLocal::s_minVolume2.GetFloat() ) {
1549                         ears[1] = idSoundSystemLocal::s_minVolume2.GetFloat();
1550                 }
1551
1552                 ears[2] = 
1553                 ears[3] = 
1554                 ears[4] = 
1555                 ears[5] = 0.0f;
1556         }
1557 }
1558
1559 /*
1560 ===============
1561 idSoundWorldLocal::AddChannelContribution
1562
1563 Adds the contribution of a single sound channel to finalMixBuffer
1564 this is called from the async thread
1565
1566 Mixes MIXBUFFER_SAMPLES samples starting at current44kHz sample time into
1567 finalMixBuffer
1568 ===============
1569 */
1570 void idSoundWorldLocal::AddChannelContribution( idSoundEmitterLocal *sound, idSoundChannel *chan,
1571                                    int current44kHz, int numSpeakers, float *finalMixBuffer ) {
1572         int j;
1573         float volume;
1574
1575         //
1576         // get the sound definition and parameters from the entity
1577         //
1578         soundShaderParms_t *parms = &chan->parms;
1579
1580         // assume we have a sound triggered on this channel
1581         assert( chan->triggerState );
1582
1583         // fetch the actual wave file and see if it's valid
1584         idSoundSample *sample = chan->leadinSample;
1585         if ( sample == NULL ) {
1586                 return;
1587         }
1588
1589         // if you don't want to hear all the beeps from missing sounds
1590         if ( sample->defaultSound && !idSoundSystemLocal::s_playDefaultSound.GetBool() ) {
1591                 return;
1592         }
1593
1594         // get the actual shader
1595         const idSoundShader *shader = chan->soundShader;
1596
1597         // this might happen if the foreground thread just deleted the sound emitter
1598         if ( !shader ) {
1599                 return;
1600         }
1601
1602         float maxd = parms->maxDistance;
1603         float mind = parms->minDistance;
1604         
1605         int  mask = shader->speakerMask;
1606         bool omni = ( parms->soundShaderFlags & SSF_OMNIDIRECTIONAL) != 0;
1607         bool looping = ( parms->soundShaderFlags & SSF_LOOPING ) != 0;
1608         bool global = ( parms->soundShaderFlags & SSF_GLOBAL ) != 0;
1609         bool noOcclusion = ( parms->soundShaderFlags & SSF_NO_OCCLUSION ) || !idSoundSystemLocal::s_useOcclusion.GetBool();
1610
1611         // speed goes from 1 to 0.2
1612         if ( idSoundSystemLocal::s_slowAttenuate.GetBool() && slowmoActive && !chan->disallowSlow ) {
1613                 maxd *= slowmoSpeed;
1614         }
1615
1616         // stereo samples are always omni
1617         if ( sample->objectInfo.nChannels == 2 ) {
1618                 omni = true;
1619         }
1620
1621         // if the sound is playing from the current listener, it will not be spatialized at all
1622         if ( sound->listenerId == listenerPrivateId ) {
1623                 global = true;
1624         }
1625
1626         //
1627         // see if it's in range
1628         //
1629         
1630         // convert volumes from decibels to float scale
1631
1632         // leadin volume scale for shattering lights
1633         // this isn't exactly correct, because the modified volume will get applied to
1634         // some initial chunk of the loop as well, because the volume is scaled for the
1635         // entire mix buffer
1636         if ( shader->leadinVolume && current44kHz - chan->trigger44kHzTime < sample->LengthIn44kHzSamples() ) {
1637                 volume = soundSystemLocal.dB2Scale( shader->leadinVolume );
1638         } else {
1639                 volume = soundSystemLocal.dB2Scale( parms->volume );
1640         }
1641
1642         // global volume scale
1643         volume *= soundSystemLocal.dB2Scale( idSoundSystemLocal::s_volume.GetFloat() );
1644
1645
1646         // volume fading
1647         float   fadeDb = chan->channelFade.FadeDbAt44kHz( current44kHz );
1648         volume *= soundSystemLocal.dB2Scale( fadeDb );
1649
1650         fadeDb = soundClassFade[parms->soundClass].FadeDbAt44kHz( current44kHz );
1651         volume *= soundSystemLocal.dB2Scale( fadeDb );
1652         
1653
1654         //
1655         // if it's a global sound then
1656         // it's not affected by distance or occlusion
1657         //
1658         float   spatialize = 1;
1659         idVec3 spatializedOriginInMeters;
1660         if ( !global ) {
1661                 float   dlen;
1662
1663                 if ( noOcclusion ) {
1664                         // use the real origin and distance
1665                         spatializedOriginInMeters = sound->origin * DOOM_TO_METERS;
1666                         dlen = sound->realDistance;
1667                 } else {
1668                         // use the possibly portal-occluded origin and distance
1669                         spatializedOriginInMeters = sound->spatializedOrigin * DOOM_TO_METERS;
1670                         dlen = sound->distance;
1671                 }
1672
1673                 // reduce volume based on distance
1674                 if ( dlen >= maxd ) {
1675                         volume = 0.0f;
1676                 } else if ( dlen > mind ) {
1677                         float frac = idMath::ClampFloat( 0.0f, 1.0f, 1.0f - ((dlen - mind) / (maxd - mind)));
1678                         if ( idSoundSystemLocal::s_quadraticFalloff.GetBool() ) {
1679                                 frac *= frac;
1680                         }
1681                         volume *= frac;
1682                 } else if ( mind > 0.0f ) {
1683                         // we tweak the spatialization bias when you are inside the minDistance
1684                         spatialize = dlen / mind;
1685                 }
1686         }
1687
1688         //
1689         // if it is a private sound, set the volume to zero
1690         // unless we match the listenerId
1691         //
1692         if ( parms->soundShaderFlags & SSF_PRIVATE_SOUND ) {
1693                 if ( sound->listenerId != listenerPrivateId ) {
1694                         volume = 0;
1695                 }
1696         }
1697         if ( parms->soundShaderFlags & SSF_ANTI_PRIVATE_SOUND ) {
1698                 if ( sound->listenerId == listenerPrivateId ) {
1699                         volume = 0;
1700                 }
1701         }
1702
1703         //
1704         // do we have anything to add?
1705         //
1706         if ( volume < SND_EPSILON && chan->lastVolume < SND_EPSILON ) {
1707                 return;
1708         }
1709         chan->lastVolume = volume;
1710
1711         //
1712         // fetch the sound from the cache as 44kHz, 16 bit samples
1713         //
1714         int offset = current44kHz - chan->trigger44kHzTime;
1715         float inputSamples[MIXBUFFER_SAMPLES*2+16];
1716         float *alignedInputSamples = (float *) ( ( ( (int)inputSamples ) + 15 ) & ~15 );
1717
1718         //
1719         // allocate and initialize hardware source
1720         // 
1721         if ( idSoundSystemLocal::useOpenAL && sound->removeStatus < REMOVE_STATUS_SAMPLEFINISHED ) {
1722                 if ( !alIsSource( chan->openalSource ) ) {
1723                         chan->openalSource = soundSystemLocal.AllocOpenALSource( chan, !chan->leadinSample->hardwareBuffer || !chan->soundShader->entries[0]->hardwareBuffer || looping, chan->leadinSample->objectInfo.nChannels == 2 );
1724                 }
1725
1726                 if ( alIsSource( chan->openalSource ) ) {
1727                         
1728                         // stop source if needed..
1729                         if ( chan->triggered ) {
1730                                 alSourceStop( chan->openalSource );
1731                         }
1732
1733                         // update source parameters
1734                         if ( global || omni ) {
1735                                 alSourcei( chan->openalSource, AL_SOURCE_RELATIVE, AL_TRUE);
1736                                 alSource3f( chan->openalSource, AL_POSITION, 0.0f, 0.0f, 0.0f );
1737                                 alSourcef( chan->openalSource, AL_GAIN, ( volume ) < ( 1.0f ) ? ( volume ) : ( 1.0f ) );
1738                         } else {
1739                                 alSourcei( chan->openalSource, AL_SOURCE_RELATIVE, AL_FALSE);
1740                                 alSource3f( chan->openalSource, AL_POSITION, -spatializedOriginInMeters.y, spatializedOriginInMeters.z, -spatializedOriginInMeters.x );
1741                                 alSourcef( chan->openalSource, AL_GAIN, ( volume ) < ( 1.0f ) ? ( volume ) : ( 1.0f ) );
1742                         }
1743                         alSourcei( chan->openalSource, AL_LOOPING, ( looping && chan->soundShader->entries[0]->hardwareBuffer ) ? AL_TRUE : AL_FALSE );
1744 #if !defined(MACOS_X)           
1745                         alSourcef( chan->openalSource, AL_REFERENCE_DISTANCE, mind );
1746                         alSourcef( chan->openalSource, AL_MAX_DISTANCE, maxd );
1747 #endif
1748                         alSourcef( chan->openalSource, AL_PITCH, ( slowmoActive && !chan->disallowSlow ) ? ( slowmoSpeed ) : ( 1.0f ) );
1749 #if ID_OPENAL
1750                         long lOcclusion = ( enviroSuitActive ? -1150 : 0);
1751                         if ( soundSystemLocal.alEAXSet ) {
1752                                 soundSystemLocal.alEAXSet( &EAXPROPERTYID_EAX_Source, EAXSOURCE_OCCLUSION, chan->openalSource, &lOcclusion, sizeof(lOcclusion) );
1753                         }
1754 #endif
1755                         if ( ( !looping && chan->leadinSample->hardwareBuffer ) || ( looping && chan->soundShader->entries[0]->hardwareBuffer ) ) {
1756                                 // handle uncompressed (non streaming) single shot and looping sounds
1757                                 if ( chan->triggered ) {
1758                                         alSourcei( chan->openalSource, AL_BUFFER, looping ? chan->soundShader->entries[0]->openalBuffer : chan->leadinSample->openalBuffer );
1759                                 }
1760                         } else {
1761                                 ALint finishedbuffers;
1762                                 ALuint buffers[3];
1763
1764                                 // handle streaming sounds (decode on the fly) both single shot AND looping
1765                                 if ( chan->triggered ) {
1766                                         alSourcei( chan->openalSource, AL_BUFFER, NULL );
1767                                         alDeleteBuffers( 3, &chan->lastopenalStreamingBuffer[0] );
1768                                         chan->lastopenalStreamingBuffer[0] = chan->openalStreamingBuffer[0];
1769                                         chan->lastopenalStreamingBuffer[1] = chan->openalStreamingBuffer[1];
1770                                         chan->lastopenalStreamingBuffer[2] = chan->openalStreamingBuffer[2];
1771                                         alGenBuffers( 3, &chan->openalStreamingBuffer[0] );
1772                                         if ( soundSystemLocal.alEAXSetBufferMode ) {
1773                                                 soundSystemLocal.alEAXSetBufferMode( 3, &chan->openalStreamingBuffer[0], alGetEnumValue( ID_ALCHAR "AL_STORAGE_ACCESSIBLE" ) );
1774                                         }
1775                                         buffers[0] = chan->openalStreamingBuffer[0];
1776                                         buffers[1] = chan->openalStreamingBuffer[1];
1777                                         buffers[2] = chan->openalStreamingBuffer[2];
1778                                         finishedbuffers = 3;
1779                                 } else {
1780                                         alGetSourcei( chan->openalSource, AL_BUFFERS_PROCESSED, &finishedbuffers );
1781                                         alSourceUnqueueBuffers( chan->openalSource, finishedbuffers, &buffers[0] );
1782                                         if ( finishedbuffers == 3 ) {
1783                                                 chan->triggered = true;
1784                                         }
1785                                 }
1786
1787                                 for ( j = 0; j < finishedbuffers; j++ ) {
1788                                         chan->GatherChannelSamples( chan->openalStreamingOffset * sample->objectInfo.nChannels, MIXBUFFER_SAMPLES * sample->objectInfo.nChannels, alignedInputSamples );
1789                                         for ( int i = 0; i < ( MIXBUFFER_SAMPLES * sample->objectInfo.nChannels ); i++ ) {
1790                                                 if ( alignedInputSamples[i] < -32768.0f )
1791                                                         ((short *)alignedInputSamples)[i] = -32768;
1792                                                 else if ( alignedInputSamples[i] > 32767.0f )
1793                                                         ((short *)alignedInputSamples)[i] = 32767;
1794                                                 else
1795                                                         ((short *)alignedInputSamples)[i] = idMath::FtoiFast( alignedInputSamples[i] );
1796                                         }
1797                                         alBufferData( buffers[j], chan->leadinSample->objectInfo.nChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, alignedInputSamples, MIXBUFFER_SAMPLES * sample->objectInfo.nChannels * sizeof( short ), 44100 );
1798                                         chan->openalStreamingOffset += MIXBUFFER_SAMPLES;
1799                                 }
1800
1801                                 if ( finishedbuffers ) {
1802                                         alSourceQueueBuffers( chan->openalSource, finishedbuffers, &buffers[0] );
1803                                 }
1804                         }
1805                         
1806                         // (re)start if needed..
1807                         if ( chan->triggered ) {
1808                                 alSourcePlay( chan->openalSource );
1809                                 chan->triggered = false;
1810                         }
1811                 }
1812         } else {
1813
1814                 if ( slowmoActive && !chan->disallowSlow ) {
1815                         idSlowChannel slow = sound->GetSlowChannel( chan );
1816
1817                         slow.AttachSoundChannel( chan );
1818
1819                                 if ( sample->objectInfo.nChannels == 2 ) {
1820                                         // need to add a stereo path, but very few samples go through this
1821                                         memset( alignedInputSamples, 0, sizeof( alignedInputSamples[0] ) * MIXBUFFER_SAMPLES * 2 );
1822                                 } else {
1823                                         slow.GatherChannelSamples( offset, MIXBUFFER_SAMPLES, alignedInputSamples );
1824                                 }
1825
1826                         sound->SetSlowChannel( chan, slow );
1827                 } else {
1828                         sound->ResetSlowChannel( chan );
1829
1830                         // if we are getting a stereo sample adjust accordingly
1831                         if ( sample->objectInfo.nChannels == 2 ) {
1832                                 // we should probably check to make sure any looping is also to a stereo sample...
1833                                 chan->GatherChannelSamples( offset*2, MIXBUFFER_SAMPLES*2, alignedInputSamples );
1834                         } else {
1835                                 chan->GatherChannelSamples( offset, MIXBUFFER_SAMPLES, alignedInputSamples );
1836                         }
1837                 }
1838
1839                 //
1840                 // work out the left / right ear values
1841                 //
1842                 float   ears[6];
1843                 if ( global || omni ) {
1844                         // same for all speakers
1845                         for ( int i = 0 ; i < 6 ; i++ ) {
1846                                 ears[i] = idSoundSystemLocal::s_globalFraction.GetFloat() * volume;
1847                         }
1848                         ears[3] = idSoundSystemLocal::s_subFraction.GetFloat() * volume;                // subwoofer
1849
1850                 } else {
1851                         CalcEars( numSpeakers, spatializedOriginInMeters, listenerPos, listenerAxis, ears, spatialize );
1852
1853                         for ( int i = 0 ; i < 6 ; i++ ) {
1854                                 ears[i] *= volume;
1855                         }
1856                 }
1857
1858                 // if the mask is 0, it really means do every channel
1859                 if ( !mask ) {
1860                         mask = 255;
1861                 }
1862                 // cleared mask bits set the mix volume to zero
1863                 for ( int i = 0 ; i < 6 ; i++ ) {
1864                         if ( !(mask & ( 1 << i ) ) ) {
1865                                 ears[i] = 0;
1866                         }
1867                 }
1868
1869                 // if sounds are generally normalized, using a mixing volume over 1.0 will
1870                 // almost always cause clipping noise.  If samples aren't normalized, there
1871                 // is a good call to allow overvolumes
1872                 if ( idSoundSystemLocal::s_clipVolumes.GetBool() && !( parms->soundShaderFlags & SSF_UNCLAMPED )  ) {
1873                         for ( int i = 0 ; i < 6 ; i++ ) {
1874                                 if ( ears[i] > 1.0f ) {
1875                                         ears[i] = 1.0f;
1876                                 }
1877                         }
1878                 }
1879
1880                 // if this is the very first mixing block, set the lastV
1881                 // to the current volume
1882                 if ( current44kHz == chan->trigger44kHzTime ) {
1883                         for ( j = 0 ; j < 6 ; j++ ) {
1884                                 chan->lastV[j] = ears[j];
1885                         }
1886                 }
1887
1888                 if ( numSpeakers == 6 ) {
1889                         if ( sample->objectInfo.nChannels == 1 ) {
1890                                 SIMDProcessor->MixSoundSixSpeakerMono( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1891                         } else {
1892                                 SIMDProcessor->MixSoundSixSpeakerStereo( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1893                         }
1894                 } else {
1895                         if ( sample->objectInfo.nChannels == 1 ) {
1896                                 SIMDProcessor->MixSoundTwoSpeakerMono( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1897                         } else {
1898                                 SIMDProcessor->MixSoundTwoSpeakerStereo( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1899                         }
1900                 }
1901
1902                 for ( j = 0 ; j < 6 ; j++ ) {
1903                         chan->lastV[j] = ears[j];
1904                 }
1905
1906         }
1907
1908         soundSystemLocal.soundStats.activeSounds++;
1909
1910 }
1911
1912 /*
1913 ===============
1914 idSoundWorldLocal::FindAmplitude
1915
1916   this is called from the main thread
1917
1918   if listenerPosition is NULL, this is being used for shader parameters,
1919   like flashing lights and glows based on sound level.  Otherwise, it is being used for
1920   the screen-shake on a player.
1921
1922   This doesn't do the portal-occlusion currently, because it would have to reset all the defs
1923   which would be problematic in multiplayer
1924 ===============
1925 */
1926 float idSoundWorldLocal::FindAmplitude( idSoundEmitterLocal *sound, const int localTime, const idVec3 *listenerPosition, 
1927                                                                            const s_channelType channel, bool shakesOnly ) {
1928         int             i, j;
1929         soundShaderParms_t *parms;
1930         float   volume;
1931         int             activeChannelCount;
1932         static const int AMPLITUDE_SAMPLES = MIXBUFFER_SAMPLES/8;
1933         float   sourceBuffer[AMPLITUDE_SAMPLES];
1934         float   sumBuffer[AMPLITUDE_SAMPLES];
1935         // work out the distance from the listener to the emitter
1936         float   dlen;
1937
1938         if ( !sound->playing ) {
1939                 return 0;
1940         }
1941
1942         if ( listenerPosition ) {
1943                 // this doesn't do the portal spatialization
1944                 idVec3 dist = sound->origin - *listenerPosition;
1945                 dlen = dist.Length();
1946                 dlen *= DOOM_TO_METERS;
1947         } else {
1948                 dlen = 1;
1949         }
1950
1951         activeChannelCount = 0;
1952
1953         for ( i = 0; i < SOUND_MAX_CHANNELS ; i++ ) {
1954                 idSoundChannel  *chan = &sound->channels[ i ];
1955
1956                 if ( !chan->triggerState ) {
1957                         continue;
1958                 }
1959
1960                 if ( channel != SCHANNEL_ANY && chan->triggerChannel != channel) {
1961                         continue;
1962                 }
1963
1964                 parms = &chan->parms;
1965
1966                 int     localTriggerTimes = chan->trigger44kHzTime;
1967
1968                 bool looping = ( parms->soundShaderFlags & SSF_LOOPING ) != 0;
1969
1970                 // check for screen shakes
1971                 float shakes = parms->shakes;
1972                 if ( shakesOnly && shakes <= 0.0f ) {
1973                         continue;
1974                 }
1975
1976                 //
1977                 // calculate volume
1978                 //
1979                 if ( !listenerPosition ) {
1980                         // just look at the raw wav data for light shader evaluation
1981                         volume = 1.0;
1982                 } else {
1983                         volume = parms->volume;
1984                         volume = soundSystemLocal.dB2Scale( volume );
1985                         if ( shakesOnly ) {
1986                                 volume *= shakes;
1987                         }
1988
1989                         if ( listenerPosition && !( parms->soundShaderFlags & SSF_GLOBAL )  ) {                 
1990                                 // check for overrides
1991                                 float maxd = parms->maxDistance;
1992                                 float mind = parms->minDistance;
1993
1994                                 if ( dlen >= maxd ) {
1995                                         volume = 0.0f;
1996                                 } else if ( dlen > mind ) {
1997                                         float frac = idMath::ClampFloat( 0, 1, 1.0f - ((dlen - mind) / (maxd - mind)));
1998                                         if ( idSoundSystemLocal::s_quadraticFalloff.GetBool() ) {
1999                                                 frac *= frac;
2000                                         }
2001                                         volume *= frac;
2002                                 }
2003                         }
2004                 }
2005
2006                 if ( volume <= 0 ) {
2007                         continue;
2008                 }
2009
2010                 //
2011                 // fetch the sound from the cache
2012                 // this doesn't handle stereo samples correctly...
2013                 //
2014                 if ( !listenerPosition && chan->parms.soundShaderFlags & SSF_NO_FLICKER ) {
2015                         // the NO_FLICKER option is to allow a light to still play a sound, but
2016                         // not have it effect the intensity
2017                         for ( j = 0 ; j < (AMPLITUDE_SAMPLES); j++ ) {
2018                                 sourceBuffer[j] = j & 1 ? 32767.0f : -32767.0f;
2019                         }
2020                 } else {
2021                         int offset = (localTime - localTriggerTimes);   // offset in samples
2022                         int size = ( looping ? chan->soundShader->entries[0]->LengthIn44kHzSamples() : chan->leadinSample->LengthIn44kHzSamples() );
2023                         short *amplitudeData = (short *)( looping ? chan->soundShader->entries[0]->amplitudeData : chan->leadinSample->amplitudeData );
2024         
2025                         if ( amplitudeData ) {
2026                                 // when the amplitudeData is present use that fill a dummy sourceBuffer
2027                                 // this is to allow for amplitude based effect on hardware audio solutions
2028                                 if ( looping ) offset %= size;
2029                                 if ( offset < size ) {
2030                                         for ( j = 0 ; j < (AMPLITUDE_SAMPLES); j++ ) {
2031                                                 sourceBuffer[j] = j & 1 ? amplitudeData[ ( offset / 512 ) * 2 ] : amplitudeData[ ( offset / 512 ) * 2 + 1 ];
2032                                         }
2033                                 }
2034                         } else {
2035                                 // get actual sample data
2036                                 chan->GatherChannelSamples( offset, AMPLITUDE_SAMPLES, sourceBuffer );
2037                         }
2038                 }
2039                 activeChannelCount++;
2040                 if ( activeChannelCount == 1 ) {
2041                         // store to the buffer
2042                         for( j = 0; j < AMPLITUDE_SAMPLES; j++ ) {
2043                                 sumBuffer[ j ] = volume * sourceBuffer[ j ];
2044                         }
2045                 } else {
2046                         // add to the buffer
2047                         for( j = 0; j < AMPLITUDE_SAMPLES; j++ ) {
2048                                 sumBuffer[ j ] += volume * sourceBuffer[ j ];
2049                         }
2050                 }
2051         }
2052
2053         if ( activeChannelCount == 0 ) {
2054                 return 0.0;
2055         }
2056
2057         float high = -32767.0f;
2058         float low = 32767.0f;
2059
2060         // use a 20th of a second
2061         for( i = 0; i < (AMPLITUDE_SAMPLES); i++ ) {
2062                 float fabval = sumBuffer[i];
2063                 if ( high < fabval ) {
2064                         high = fabval;
2065                 }
2066                 if ( low > fabval ) {
2067                         low = fabval;
2068                 }
2069         }
2070
2071         float sout;
2072         sout = atan( (high - low) / 32767.0f) / DEG2RAD(45);
2073
2074         return sout;
2075 }
2076
2077 /*
2078 =================
2079 idSoundWorldLocal::FadeSoundClasses
2080
2081 fade all sounds in the world with a given shader soundClass
2082 to is in Db (sigh), over is in seconds
2083 =================
2084 */
2085 void    idSoundWorldLocal::FadeSoundClasses( const int soundClass, const float to, const float over ) {
2086         if ( soundClass < 0 || soundClass >= SOUND_MAX_CLASSES ) {
2087                 common->Error( "idSoundWorldLocal::FadeSoundClasses: bad soundClass %i", soundClass );
2088         }
2089
2090         idSoundFade     *fade = &soundClassFade[ soundClass ];
2091
2092         int     length44kHz = soundSystemLocal.MillisecondsToSamples( over * 1000 );
2093
2094         // if it is already fading to this volume at this rate, don't change it
2095         if ( fade->fadeEndVolume == to && 
2096                 fade->fadeEnd44kHz - fade->fadeStart44kHz == length44kHz ) {
2097                 return;
2098         }
2099
2100         int     start44kHz;
2101
2102         if ( fpa[0] ) {
2103                 // if we are recording an AVI demo, don't use hardware time
2104                 start44kHz = lastAVI44kHz + MIXBUFFER_SAMPLES;
2105         } else {
2106                 start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
2107         }
2108
2109         // fade it
2110         fade->fadeStartVolume = fade->FadeDbAt44kHz( start44kHz );
2111         fade->fadeStart44kHz = start44kHz;
2112         fade->fadeEnd44kHz = start44kHz + length44kHz;
2113         fade->fadeEndVolume = to;
2114 }
2115
2116 /*
2117 =================
2118 idSoundWorldLocal::SetSlowmo
2119 =================
2120 */
2121 void idSoundWorldLocal::SetSlowmo( bool active ) {
2122         slowmoActive = active;
2123 }
2124
2125 /*
2126 =================
2127 idSoundWorldLocal::SetSlowmoSpeed
2128 =================
2129 */
2130 void idSoundWorldLocal::SetSlowmoSpeed( float speed ) {
2131         slowmoSpeed = speed;
2132 }
2133
2134 /*
2135 =================
2136 idSoundWorldLocal::SetEnviroSuit
2137 =================
2138 */
2139 void idSoundWorldLocal::SetEnviroSuit( bool active ) {
2140         enviroSuitActive = active;
2141 }