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