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