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 
31 #include "sound/snd_local.h"
32 
33 #ifdef ID_DEDICATED
34 idCVar idSoundSystemLocal::s_noSound( "s_noSound", "1", CVAR_SOUND | CVAR_BOOL | CVAR_ROM, "" );
35 #else
36 idCVar idSoundSystemLocal::s_noSound( "s_noSound", "0", CVAR_SOUND | CVAR_BOOL | CVAR_NOCHEAT, "" );
37 #endif
38 idCVar idSoundSystemLocal::s_device( "s_device", "default", CVAR_SOUND | CVAR_NOCHEAT | CVAR_ARCHIVE, "the audio device to use ('default' for the default audio device)" );
39 idCVar idSoundSystemLocal::s_quadraticFalloff( "s_quadraticFalloff", "1", CVAR_SOUND | CVAR_BOOL, "" );
40 idCVar idSoundSystemLocal::s_drawSounds( "s_drawSounds", "0", CVAR_SOUND | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
41 idCVar idSoundSystemLocal::s_showStartSound( "s_showStartSound", "0", CVAR_SOUND | CVAR_BOOL, "" );
42 idCVar idSoundSystemLocal::s_useOcclusion( "s_useOcclusion", "1", CVAR_SOUND | CVAR_BOOL, "" );
43 idCVar idSoundSystemLocal::s_maxSoundsPerShader( "s_maxSoundsPerShader", "0", CVAR_SOUND | CVAR_ARCHIVE, "", 0, 10, idCmdSystem::ArgCompletion_Integer<0,10> );
44 idCVar idSoundSystemLocal::s_showLevelMeter( "s_showLevelMeter", "0", CVAR_SOUND | CVAR_BOOL, "" );
45 idCVar idSoundSystemLocal::s_constantAmplitude( "s_constantAmplitude", "-1", CVAR_SOUND | CVAR_FLOAT, "" );
46 idCVar idSoundSystemLocal::s_minVolume6( "s_minVolume6", "0", CVAR_SOUND | CVAR_FLOAT, "" );
47 idCVar idSoundSystemLocal::s_dotbias6( "s_dotbias6", "0.8", CVAR_SOUND | CVAR_FLOAT, "" );
48 idCVar idSoundSystemLocal::s_minVolume2( "s_minVolume2", "0.25", CVAR_SOUND | CVAR_FLOAT, "" );
49 idCVar idSoundSystemLocal::s_dotbias2( "s_dotbias2", "1.1", CVAR_SOUND | CVAR_FLOAT, "" );
50 idCVar idSoundSystemLocal::s_spatializationDecay( "s_spatializationDecay", "2", CVAR_SOUND | CVAR_ARCHIVE | CVAR_FLOAT, "" );
51 idCVar idSoundSystemLocal::s_reverse( "s_reverse", "0", CVAR_SOUND | CVAR_ARCHIVE | CVAR_BOOL, "" );
52 idCVar idSoundSystemLocal::s_meterTopTime( "s_meterTopTime", "2000", CVAR_SOUND | CVAR_ARCHIVE | CVAR_INTEGER, "" );
53 idCVar idSoundSystemLocal::s_volume( "s_volume_dB", "0", CVAR_SOUND | CVAR_ARCHIVE | CVAR_FLOAT, "volume in dB" );
54 idCVar idSoundSystemLocal::s_playDefaultSound( "s_playDefaultSound", "1", CVAR_SOUND | CVAR_ARCHIVE | CVAR_BOOL, "play a beep for missing sounds" );
55 idCVar idSoundSystemLocal::s_subFraction( "s_subFraction", "0.75", CVAR_SOUND | CVAR_ARCHIVE | CVAR_FLOAT, "volume to subwoofer in 5.1" );
56 idCVar idSoundSystemLocal::s_globalFraction( "s_globalFraction", "0.8", CVAR_SOUND | CVAR_ARCHIVE | CVAR_FLOAT, "volume to all speakers when not spatialized" );
57 idCVar idSoundSystemLocal::s_doorDistanceAdd( "s_doorDistanceAdd", "150", CVAR_SOUND | CVAR_ARCHIVE | CVAR_FLOAT, "reduce sound volume with this distance when going through a door" );
58 idCVar idSoundSystemLocal::s_singleEmitter( "s_singleEmitter", "0", CVAR_SOUND | CVAR_INTEGER, "mute all sounds but this emitter" );
59 idCVar idSoundSystemLocal::s_numberOfSpeakers( "s_numberOfSpeakers", "2", CVAR_SOUND | CVAR_ARCHIVE, "number of speakers" );
60 idCVar idSoundSystemLocal::s_force22kHz( "s_force22kHz", "0", CVAR_SOUND | CVAR_BOOL, ""  );
61 idCVar idSoundSystemLocal::s_clipVolumes( "s_clipVolumes", "1", CVAR_SOUND | CVAR_BOOL, ""  );
62 idCVar idSoundSystemLocal::s_realTimeDecoding( "s_realTimeDecoding", "1", CVAR_SOUND | CVAR_BOOL | CVAR_INIT, "" );
63 
64 idCVar idSoundSystemLocal::s_slowAttenuate( "s_slowAttenuate", "1", CVAR_SOUND | CVAR_BOOL, "slowmo sounds attenuate over shorted distance" );
65 idCVar idSoundSystemLocal::s_enviroSuitCutoffFreq( "s_enviroSuitCutoffFreq", "2000", CVAR_SOUND | CVAR_FLOAT, "" );
66 idCVar idSoundSystemLocal::s_enviroSuitCutoffQ( "s_enviroSuitCutoffQ", "2", CVAR_SOUND | CVAR_FLOAT, "" );
67 idCVar idSoundSystemLocal::s_reverbTime( "s_reverbTime", "1000", CVAR_SOUND | CVAR_FLOAT, "" );
68 idCVar idSoundSystemLocal::s_reverbFeedback( "s_reverbFeedback", "0.333", CVAR_SOUND | CVAR_FLOAT, "" );
69 idCVar idSoundSystemLocal::s_enviroSuitVolumeScale( "s_enviroSuitVolumeScale", "0.9", CVAR_SOUND | CVAR_FLOAT, "" );
70 idCVar idSoundSystemLocal::s_skipHelltimeFX( "s_skipHelltimeFX", "0", CVAR_SOUND | CVAR_BOOL, "" );
71 
72 #if !defined(ID_DEDICATED)
73 idCVar idSoundSystemLocal::s_useEAXReverb( "s_useEAXReverb", "1", CVAR_SOUND | CVAR_BOOL | CVAR_ARCHIVE, "use EFX reverb" );
74 idCVar idSoundSystemLocal::s_decompressionLimit( "s_decompressionLimit", "6", CVAR_SOUND | CVAR_INTEGER | CVAR_ARCHIVE, "specifies maximum uncompressed sample length in seconds" );
75 #else
76 idCVar idSoundSystemLocal::s_useEAXReverb( "s_useEAXReverb", "0", CVAR_SOUND | CVAR_BOOL | CVAR_ROM, "EFX not available in this build" );
77 idCVar idSoundSystemLocal::s_decompressionLimit( "s_decompressionLimit", "6", CVAR_SOUND | CVAR_INTEGER | CVAR_ROM, "specifies maximum uncompressed sample length in seconds" );
78 #endif
79 
80 bool idSoundSystemLocal::useEFXReverb = false;
81 int idSoundSystemLocal::EFXAvailable = -1;
82 
83 idSoundSystemLocal	soundSystemLocal;
84 idSoundSystem	*soundSystem  = &soundSystemLocal;
85 
86 /*
87 ===============
88 SoundReloadSounds_f
89 
90   this is called from the main thread
91 ===============
92 */
SoundReloadSounds_f(const idCmdArgs & args)93 void SoundReloadSounds_f( const idCmdArgs &args ) {
94 	if ( !soundSystemLocal.soundCache ) {
95 		return;
96 	}
97 	bool force = false;
98 	if ( args.Argc() == 2 ) {
99 		force = true;
100 	}
101 	soundSystem->SetMute( true );
102 	soundSystemLocal.soundCache->ReloadSounds( force );
103 	soundSystem->SetMute( false );
104 	common->Printf( "sound: changed sounds reloaded\n" );
105 }
106 
107 /*
108 ===============
109 ListSounds_f
110 
111 Optional parameter to only list sounds containing that string
112 ===============
113 */
ListSounds_f(const idCmdArgs & args)114 void ListSounds_f( const idCmdArgs &args ) {
115 	int i;
116 	const char	*snd = args.Argv( 1 );
117 
118 	if ( !soundSystemLocal.soundCache ) {
119 		common->Printf( "No sound.\n" );
120 		return;
121 	}
122 
123 	int	totalSounds = 0;
124 	int totalSamples = 0;
125 	int totalMemory = 0;
126 	int totalPCMMemory = 0;
127 	for( i = 0; i < soundSystemLocal.soundCache->GetNumObjects(); i++ ) {
128 		const idSoundSample *sample = soundSystemLocal.soundCache->GetObject(i);
129 		if ( !sample ) {
130 			continue;
131 		}
132 		if ( snd && sample->name.Find( snd, false ) < 0 ) {
133 			continue;
134 		}
135 
136 		const waveformatex_t &info = sample->objectInfo;
137 
138 		const char *stereo = ( info.nChannels == 2 ? "ST" : "  " );
139 		const char *format = ( info.wFormatTag == WAVE_FORMAT_TAG_OGG ) ? "OGG" : "WAV";
140 		const char *defaulted = ( sample->defaultSound ? "(DEFAULTED)" : sample->purged ? "(PURGED)" : "" );
141 
142 		common->Printf( "%s %dkHz %6dms %5dkB %4s %s%s\n", stereo, sample->objectInfo.nSamplesPerSec / 1000,
143 					soundSystemLocal.SamplesToMilliseconds( sample->LengthIn44kHzSamples() ),
144 					sample->objectMemSize >> 10, format, sample->name.c_str(), defaulted );
145 
146 		if ( !sample->purged ) {
147 			totalSamples += sample->objectSize;
148 			if ( info.wFormatTag != WAVE_FORMAT_TAG_OGG )
149 				totalPCMMemory += sample->objectMemSize;
150 			if ( !sample->hardwareBuffer )
151 				totalMemory += sample->objectMemSize;
152 		}
153 		totalSounds++;
154 	}
155 	common->Printf( "%8d total sounds\n", totalSounds );
156 	common->Printf( "%8d total samples loaded\n", totalSamples );
157 	common->Printf( "%8d kB total system memory used\n", totalMemory >> 10 );
158 }
159 
160 /*
161 ===============
162 ListSoundDecoders_f
163 ===============
164 */
ListSoundDecoders_f(const idCmdArgs & args)165 void ListSoundDecoders_f( const idCmdArgs &args ) {
166 	int i, j, numActiveDecoders, numWaitingDecoders;
167 	idSoundWorldLocal *sw = soundSystemLocal.currentSoundWorld;
168 
169 	numActiveDecoders = numWaitingDecoders = 0;
170 
171 	for ( i = 0; i < sw->emitters.Num(); i++ ) {
172 		idSoundEmitterLocal *sound = sw->emitters[i];
173 
174 		if ( !sound ) {
175 			continue;
176 		}
177 
178 		// run through all the channels
179 		for ( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
180 			idSoundChannel	*chan = &sound->channels[j];
181 
182 			if ( chan->decoder == NULL ) {
183 				continue;
184 			}
185 
186 			idSoundSample *sample = chan->decoder->GetSample();
187 
188 			if ( sample != NULL ) {
189 				continue;
190 			}
191 
192 			const char *format = ( chan->leadinSample->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) ? "OGG" : "WAV";
193 			common->Printf( "%3d waiting %s: %s\n", numWaitingDecoders, format, chan->leadinSample->name.c_str() );
194 
195 			numWaitingDecoders++;
196 		}
197 	}
198 
199 	for ( i = 0; i < sw->emitters.Num(); i++ ) {
200 		idSoundEmitterLocal *sound = sw->emitters[i];
201 
202 		if ( !sound ) {
203 			continue;
204 		}
205 
206 		// run through all the channels
207 		for ( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
208 			idSoundChannel	*chan = &sound->channels[j];
209 
210 			if ( chan->decoder == NULL ) {
211 				continue;
212 			}
213 
214 			idSoundSample *sample = chan->decoder->GetSample();
215 
216 			if ( sample == NULL ) {
217 				continue;
218 			}
219 
220 			const char *format = ( sample->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) ? "OGG" : "WAV";
221 
222 			int localTime = soundSystemLocal.GetCurrent44kHzTime() - chan->trigger44kHzTime;
223 			int sampleTime = sample->LengthIn44kHzSamples() * sample->objectInfo.nChannels;
224 			int percent;
225 			if ( localTime > sampleTime ) {
226 				if ( chan->parms.soundShaderFlags & SSF_LOOPING ) {
227 					percent = ( localTime % sampleTime ) * 100 / sampleTime;
228 				} else {
229 					percent = 100;
230 				}
231 			} else {
232 				percent = localTime * 100 / sampleTime;
233 			}
234 
235 			common->Printf( "%3d decoding %3d%% %s: %s\n", numActiveDecoders, percent, format, sample->name.c_str() );
236 
237 			numActiveDecoders++;
238 		}
239 	}
240 
241 	common->Printf( "%d decoders\n", numWaitingDecoders + numActiveDecoders );
242 	common->Printf( "%d waiting decoders\n", numWaitingDecoders );
243 	common->Printf( "%d active decoders\n", numActiveDecoders );
244 	common->Printf( "%d kB decoder memory in %d blocks\n", idSampleDecoder::GetUsedBlockMemory() >> 10, idSampleDecoder::GetNumUsedBlocks() );
245 }
246 
247 /*
248 ===============
249 TestSound_f
250 
251   this is called from the main thread
252 ===============
253 */
TestSound_f(const idCmdArgs & args)254 void TestSound_f( const idCmdArgs &args ) {
255 	if ( args.Argc() != 2 ) {
256 		common->Printf( "Usage: testSound <file>\n" );
257 		return;
258 	}
259 	if ( soundSystemLocal.currentSoundWorld ) {
260 		soundSystemLocal.currentSoundWorld->PlayShaderDirectly( args.Argv( 1 ) );
261 	}
262 }
263 
264 /*
265 ===============
266 SoundSystemRestart_f
267 
268 restart the sound thread
269 
270   this is called from the main thread
271 ===============
272 */
SoundSystemRestart_f(const idCmdArgs & args)273 void SoundSystemRestart_f( const idCmdArgs &args ) {
274 	soundSystem->SetMute( true );
275 	soundSystemLocal.ShutdownHW();
276 	soundSystemLocal.InitHW();
277 	soundSystem->SetMute( false );
278 }
279 
280 // DG: make this function callable from idSessionLocal::Frame() without having to
281 // change the public idSoundSystem interface - that would break mod DLL compat,
282 // and this is not relevant for gamecode.
CheckOpenALDeviceAndRecoverIfNeeded()283 bool CheckOpenALDeviceAndRecoverIfNeeded()
284 {
285 	if(soundSystemLocal.isInitialized)
286 		return soundSystemLocal.CheckDeviceAndRecoverIfNeeded();
287 
288 	return true;
289 }
290 
291 /*
292 ===============
293 idSoundSystemLocal::Init
294 
295 initialize the sound system
296 ===============
297 */
Init()298 void idSoundSystemLocal::Init() {
299 	common->Printf( "----- Initializing OpenAL -----\n" );
300 
301 	isInitialized = false;
302 	muted = false;
303 	shutdown = false;
304 
305 	currentSoundWorld = NULL;
306 	soundCache = NULL;
307 
308 	olddwCurrentWritePos = 0;
309 	buffers = 0;
310 	CurrentSoundTime = 0;
311 
312 	nextWriteBlock = 0xffffffff;
313 
314 	memset( meterTops, 0, sizeof( meterTops ) );
315 	memset( meterTopsTime, 0, sizeof( meterTopsTime ) );
316 
317 	for( int i = -600; i < 600; i++ ) {
318 		float pt = i * 0.1f;
319 		volumesDB[i+600] = pow( 2.0f,( pt * ( 1.0f / 6.0f ) ) );
320 	}
321 
322 	// make a 16 byte aligned finalMixBuffer
323 	finalMixBuffer = (float *) ( ( ( (intptr_t)realAccum ) + 15 ) & ~15 );
324 
325 	graph = NULL;
326 
327 	// DG: added these for CheckDeviceAndRecoverIfNeeded()
328 	alcResetDeviceSOFT = NULL;
329 	resetRetryCount = 0;
330 	lastCheckTime = 0;
331 
332 	// DG: no point in initializing OpenAL if sound is disabled with s_noSound
333 	if ( s_noSound.GetBool() ) {
334 		common->Printf( "Sound disabled with s_noSound 1 !\n" );
335 		openalDevice = NULL;
336 		openalContext = NULL;
337 	} else {
338 		// set up openal device and context
339 		common->Printf( "Setup OpenAL device and context\n" );
340 
341 		const char *device = s_device.GetString();
342 		if (strlen(device) < 1)
343 			device = NULL;
344 		else if (!idStr::Icmp(device, "default"))
345 			device = NULL;
346 
347 		if ( alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") ) {
348 			const char *devs = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
349 			bool found = false;
350 
351 			while (devs && *devs) {
352 				common->Printf("OpenAL: found device '%s'", devs);
353 
354 				if ( device && !idStr::Icmp(devs, device) ) {
355 					common->Printf(" (ACTIVE)\n");
356 					found = true;
357 				} else {
358 					common->Printf("\n");
359 				}
360 
361 				devs += strlen(devs) + 1;
362 			}
363 
364 			if ( device && !found ) {
365 				common->Printf("OpenAL: device %s not found, using default\n", device);
366 				device = NULL;
367 			}
368 		}
369 
370 		openalDevice = alcOpenDevice(device);
371 		if ( !openalDevice && device ) {
372 			common->Printf( "OpenAL: failed to open device '%s' (0x%x), trying default...\n", device, alGetError() );
373 			openalDevice = alcOpenDevice( NULL );
374 		}
375 
376 		// DG: handle the possibility that opening the default device or creating context failed
377 		if ( openalDevice == NULL ) {
378 			common->Printf( "OpenAL: failed to open default device (0x%x), disabling sound\n", alGetError() );
379 			openalContext = NULL;
380 		} else {
381 			openalContext = alcCreateContext( openalDevice, NULL );
382 			if ( openalContext == NULL ) {
383 				common->Printf( "OpenAL: failed to create context (0x%x), disabling sound\n", alcGetError(openalDevice) );
384 				alcCloseDevice( openalDevice );
385 				openalDevice = NULL;
386 			}
387 		}
388 	}
389 
390 	// DG: only do these things if opening device and creating context succeeded and sound is enabled
391 	//     (if sound is disabled with s_noSound, openalContext is NULL)
392 	if ( openalContext != NULL )
393 	{
394 		idSampleDecoder::Init();
395 		soundCache = new idSoundCache();
396 
397 		alcMakeContextCurrent( openalContext );
398 
399 		// log openal info
400 		common->Printf( "OpenAL vendor: %s\n", alGetString(AL_VENDOR) );
401 		common->Printf( "OpenAL renderer: %s\n", alGetString(AL_RENDERER) );
402 		common->Printf( "OpenAL version: %s\n", alGetString(AL_VERSION) );
403 
404 		// DG: extensions needed for CheckDeviceAndRecoverIfNeeded()
405 		bool hasAlcExtDisconnect = alcIsExtensionPresent( openalDevice, "ALC_EXT_disconnect" ) != AL_FALSE;
406 		bool hasAlcSoftHrtf = alcIsExtensionPresent( openalDevice, "ALC_SOFT_HRTF" ) != AL_FALSE;
407 		if ( hasAlcExtDisconnect && hasAlcSoftHrtf ) {
408 			common->Printf( "OpenAL: found extensions for resetting disconnected devices\n" );
409 			alcResetDeviceSOFT = (LPALCRESETDEVICESOFT)alcGetProcAddress( openalDevice, "alcResetDeviceSOFT" );
410 		}
411 
412 		// try to obtain EFX extensions
413 		if (alcIsExtensionPresent(openalDevice, "ALC_EXT_EFX")) {
414 			common->Printf( "OpenAL: found EFX extension\n" );
415 			EFXAvailable = 1;
416 
417 			alGenEffects = (LPALGENEFFECTS)alGetProcAddress("alGenEffects");
418 			alDeleteEffects = (LPALDELETEEFFECTS)alGetProcAddress("alDeleteEffects");
419 			alIsEffect = (LPALISEFFECT)alGetProcAddress("alIsEffect");
420 			alEffecti = (LPALEFFECTI)alGetProcAddress("alEffecti");
421 			alEffectf = (LPALEFFECTF)alGetProcAddress("alEffectf");
422 			alEffectfv = (LPALEFFECTFV)alGetProcAddress("alEffectfv");
423 			alGenFilters = (LPALGENFILTERS)alGetProcAddress("alGenFilters");
424 			alDeleteFilters = (LPALDELETEFILTERS)alGetProcAddress("alDeleteFilters");
425 			alIsFilter = (LPALISFILTER)alGetProcAddress("alIsFilter");
426 			alFilteri = (LPALFILTERI)alGetProcAddress("alFilteri");
427 			alFilterf = (LPALFILTERF)alGetProcAddress("alFilterf");
428 			alGenAuxiliaryEffectSlots = (LPALGENAUXILIARYEFFECTSLOTS)alGetProcAddress("alGenAuxiliaryEffectSlots");
429 			alDeleteAuxiliaryEffectSlots = (LPALDELETEAUXILIARYEFFECTSLOTS)alGetProcAddress("alDeleteAuxiliaryEffectSlots");
430 			alIsAuxiliaryEffectSlot = (LPALISAUXILIARYEFFECTSLOT)alGetProcAddress("alIsAuxiliaryEffectSlot");;
431 			alAuxiliaryEffectSloti = (LPALAUXILIARYEFFECTSLOTI)alGetProcAddress("alAuxiliaryEffectSloti");
432 		} else {
433 			common->Printf( "OpenAL: EFX extension not found\n" );
434 			EFXAvailable = 0;
435 			idSoundSystemLocal::s_useEAXReverb.SetBool( false );
436 
437 			alGenEffects = NULL;
438 			alDeleteEffects = NULL;
439 			alIsEffect = NULL;
440 			alEffecti = NULL;
441 			alEffectf = NULL;
442 			alEffectfv = NULL;
443 			alGenFilters = NULL;
444 			alDeleteFilters = NULL;
445 			alIsFilter = NULL;
446 			alFilteri = NULL;
447 			alFilterf = NULL;
448 			alGenAuxiliaryEffectSlots = NULL;
449 			alDeleteAuxiliaryEffectSlots = NULL;
450 			alIsAuxiliaryEffectSlot = NULL;
451 			alAuxiliaryEffectSloti = NULL;
452 		}
453 
454 		ALuint handle;
455 		openalSourceCount = 0;
456 
457 		while ( openalSourceCount < 256 ) {
458 			alGetError();
459 			alGenSources( 1, &handle );
460 			if ( alGetError() != AL_NO_ERROR ) {
461 				break;
462 			} else {
463 				// store in source array
464 				openalSources[openalSourceCount].handle = handle;
465 				openalSources[openalSourceCount].startTime = 0;
466 				openalSources[openalSourceCount].chan = NULL;
467 				openalSources[openalSourceCount].inUse = false;
468 				openalSources[openalSourceCount].looping = false;
469 
470 				// initialise sources
471 				alSourcef( handle, AL_ROLLOFF_FACTOR, 0.0f );
472 
473 				// found one source
474 				openalSourceCount++;
475 			}
476 		}
477 
478 		common->Printf( "OpenAL: found %d hardware voices\n", openalSourceCount );
479 
480 		// adjust source count to allow for at least eight stereo sounds to play
481 		openalSourceCount -= 8;
482 
483 		useEFXReverb = idSoundSystemLocal::s_useEAXReverb.GetBool();
484 		efxloaded = false;
485 
486 	}
487 
488 	cmdSystem->AddCommand( "listSounds", ListSounds_f, CMD_FL_SOUND, "lists all sounds" );
489 	cmdSystem->AddCommand( "listSoundDecoders", ListSoundDecoders_f, CMD_FL_SOUND, "list active sound decoders" );
490 	cmdSystem->AddCommand( "reloadSounds", SoundReloadSounds_f, CMD_FL_SOUND|CMD_FL_CHEAT, "reloads all sounds" );
491 	cmdSystem->AddCommand( "testSound", TestSound_f, CMD_FL_SOUND | CMD_FL_CHEAT, "tests a sound", idCmdSystem::ArgCompletion_SoundName );
492 	cmdSystem->AddCommand( "s_restart", SoundSystemRestart_f, CMD_FL_SOUND, "restarts the sound system" );
493 }
494 
495 /*
496 ===============
497 idSoundSystemLocal::Shutdown
498 ===============
499 */
Shutdown()500 void idSoundSystemLocal::Shutdown() {
501 	ShutdownHW();
502 
503 	// EFX or not, the list needs to be cleared
504 	EFXDatabase.Clear();
505 
506 	efxloaded = false;
507 
508 	// adjust source count back up to allow for freeing of all resources
509 	openalSourceCount += 8;
510 
511 	for ( ALsizei i = 0; i < openalSourceCount; i++ ) {
512 		// stop source
513 		alSourceStop( openalSources[i].handle );
514 		alSourcei( openalSources[i].handle, AL_BUFFER, 0 );
515 
516 		// delete source
517 		alDeleteSources( 1, &openalSources[i].handle );
518 
519 		// clear entry in source array
520 		openalSources[i].handle = 0;
521 		openalSources[i].startTime = 0;
522 		openalSources[i].chan = NULL;
523 		openalSources[i].inUse = false;
524 		openalSources[i].looping = false;
525 	}
526 
527 	// destroy all the sounds (hardware buffers as well)
528 	delete soundCache;
529 	soundCache = NULL;
530 
531 	// destroy openal device and context
532 	alcMakeContextCurrent( NULL );
533 
534 	alcDestroyContext( openalContext );
535 	openalContext = NULL;
536 
537 	alcCloseDevice( openalDevice );
538 	openalDevice = NULL;
539 
540 	idSampleDecoder::Shutdown();
541 }
542 
543 /*
544 ===============
545 idSoundSystemLocal::InitHW
546 ===============
547 */
InitHW()548 bool idSoundSystemLocal::InitHW() {
549 	int numSpeakers = s_numberOfSpeakers.GetInteger();
550 
551 	if (numSpeakers != 2 && numSpeakers != 6) {
552 		common->Warning("invalid value for s_numberOfSpeakers. Use either 2 or 6");
553 		numSpeakers = 2;
554 		s_numberOfSpeakers.SetInteger(numSpeakers);
555 	}
556 
557 	// DG: if OpenAL context couldn't be created (maybe there were no
558 	//      audio devices), keep audio disabled.
559 	if ( s_noSound.GetBool() || openalContext == NULL ) {
560 		return false;
561 	}
562 
563 	// put the real number in there
564 	s_numberOfSpeakers.SetInteger(numSpeakers);
565 
566 	isInitialized = true;
567 	shutdown = false;
568 
569 	return true;
570 }
571 
572 /*
573 ===============
574 idSoundSystemLocal::ShutdownHW
575 ===============
576 */
ShutdownHW()577 bool idSoundSystemLocal::ShutdownHW() {
578 	if ( !isInitialized ) {
579 		return false;
580 	}
581 
582 	shutdown = true;		// don't do anything at AsyncUpdate() time
583 	Sys_Sleep( 100 );		// sleep long enough to make sure any async sound talking to hardware has returned
584 
585 	common->Printf( "Shutting down sound hardware\n" );
586 
587 	isInitialized = false;
588 
589 	if ( graph ) {
590 		Mem_Free( graph );
591 		graph = NULL;
592 	}
593 
594 	return true;
595 }
596 
597 
598 /*
599 ===============
600 idSoundSystemLocal::CheckDeviceAndRecoverIfNeeded
601 
602  DG: returns true if openalDevice is still available,
603      otherwise it will try to recover the device and return false while it's gone
604      (display audio sound devices sometimes disappear for a few seconds when switching resolution)
605 ===============
606 */
CheckDeviceAndRecoverIfNeeded()607 bool idSoundSystemLocal::CheckDeviceAndRecoverIfNeeded()
608 {
609 	static const int maxRetries = 20;
610 
611 	if ( alcResetDeviceSOFT == NULL ) {
612 		return true; // we can't check or reset, just pretend everything is fine..
613 	}
614 
615 	unsigned int curTime = Sys_Milliseconds();
616 	if ( curTime - lastCheckTime >= 1000 ) // check once per second
617 	{
618 		lastCheckTime = curTime;
619 
620 		ALCint connected; // ALC_CONNECTED needs ALC_EXT_disconnect (we check for that in Init())
621 		alcGetIntegerv( openalDevice, ALC_CONNECTED, 1, &connected );
622 		if ( connected ) {
623 			resetRetryCount = 0;
624 			return true;
625 		}
626 
627 		if ( resetRetryCount == 0 ) {
628 			common->Warning( "OpenAL device disconnected! Will try to reconnect.." );
629 			resetRetryCount = 1;
630 		} else if ( resetRetryCount > maxRetries ) { // give up after 20 seconds
631 			if ( resetRetryCount == maxRetries+1 ) {
632 				common->Warning( "OpenAL device still disconnected! Giving up!" );
633 				++resetRetryCount; // this makes sure the warning is only shown once
634 
635 				// TODO: can we shut down sound without things blowing up?
636 				//       if we can, we could do that if we don't have alcResetDeviceSOFT but ALC_EXT_disconnect
637 			}
638 			return false;
639 		}
640 
641 		if ( alcResetDeviceSOFT( openalDevice, NULL ) ) {
642 			common->Printf( "OpenAL: resetting device succeeded!\n" );
643 			resetRetryCount = 0;
644 			return true;
645 		}
646 
647 		++resetRetryCount;
648 		return false;
649 	}
650 
651 	return resetRetryCount == 0; // if it's 0, state on last check was ok
652 }
653 
654 /*
655 ===============
656 idSoundSystemLocal::GetCurrent44kHzTime
657 ===============
658 */
GetCurrent44kHzTime(void) const659 int idSoundSystemLocal::GetCurrent44kHzTime( void ) const {
660 	if ( isInitialized ) {
661 		return CurrentSoundTime;
662 	} else {
663 		// NOTE: this would overflow 31bits within about 1h20
664 		//return ( ( Sys_Milliseconds()*441 ) / 10 ) * 4;
665 		return idMath::FtoiFast( (float)Sys_Milliseconds() * 176.4f );
666 	}
667 }
668 
669 /*
670 ===================
671 idSoundSystemLocal::AsyncMix
672 Mac OSX version. The system uses it's own thread and an IOProc callback
673 ===================
674 */
AsyncMix(int soundTime,float * mixBuffer)675 int idSoundSystemLocal::AsyncMix( int soundTime, float *mixBuffer ) {
676 	int	inTime, numSpeakers;
677 
678 	if ( !isInitialized || shutdown ) {
679 		return 0;
680 	}
681 
682 	inTime = Sys_Milliseconds();
683 	numSpeakers = s_numberOfSpeakers.GetInteger();
684 
685 	// let the active sound world mix all the channels in unless muted or avi demo recording
686 	if ( !muted && currentSoundWorld && !currentSoundWorld->fpa[0] ) {
687 		currentSoundWorld->MixLoop( soundTime, numSpeakers, mixBuffer );
688 	}
689 
690 	CurrentSoundTime = soundTime;
691 
692 	return Sys_Milliseconds() - inTime;
693 }
694 
695 /*
696 ===================
697 idSoundSystemLocal::AsyncUpdate
698 called from async sound thread when com_asyncSound == 2
699 DG: using this for the "traditional" sound updates that
700     only happen about every 100ms (and lead to delays between 1 and 110ms between
701     starting a sound in gamecode and it being played), for people who like that..
702 ===================
703 */
AsyncUpdate(int inTime)704 int idSoundSystemLocal::AsyncUpdate( int inTime ) {
705 
706 	if ( !isInitialized || shutdown ) {
707 		return 0;
708 	}
709 
710 	ulong dwCurrentWritePos;
711 	dword dwCurrentBlock;
712 
713 	// here we do it in samples ( overflows in 27 hours or so )
714 	dwCurrentWritePos = idMath::Ftol( (float)Sys_Milliseconds() * 44.1f ) % ( MIXBUFFER_SAMPLES * ROOM_SLICES_IN_BUFFER );
715 	dwCurrentBlock = dwCurrentWritePos / MIXBUFFER_SAMPLES;
716 
717 	if ( nextWriteBlock == 0xffffffff ) {
718 		nextWriteBlock = dwCurrentBlock;
719 	}
720 
721 	if ( dwCurrentBlock != nextWriteBlock ) {
722 		return 0;
723 	}
724 
725 	soundStats.runs++;
726 	soundStats.activeSounds = 0;
727 
728 	int	numSpeakers = s_numberOfSpeakers.GetInteger();
729 
730 	nextWriteBlock++;
731 	nextWriteBlock %= ROOM_SLICES_IN_BUFFER;
732 
733 	int newPosition = nextWriteBlock * MIXBUFFER_SAMPLES;
734 
735 	if ( newPosition < olddwCurrentWritePos ) {
736 		buffers++;					// buffer wrapped
737 	}
738 
739 	// nextWriteSample is in multi-channel samples inside the buffer
740 	int	nextWriteSamples = nextWriteBlock * MIXBUFFER_SAMPLES;
741 
742 	olddwCurrentWritePos = newPosition;
743 
744 	// newSoundTime is in multi-channel samples since the sound system was started
745 	int newSoundTime = ( buffers * MIXBUFFER_SAMPLES * ROOM_SLICES_IN_BUFFER ) + nextWriteSamples;
746 
747 	// check for impending overflow
748 	// FIXME: we don't handle sound wrap-around correctly yet
749 	if ( newSoundTime > 0x6fffffff ) {
750 		buffers = 0;
751 	}
752 
753 	if ( (newSoundTime - CurrentSoundTime) > (int)MIXBUFFER_SAMPLES ) {
754 		soundStats.missedWindow++;
755 	}
756 
757 	// enable audio hardware caching
758 	alcSuspendContext( openalContext );
759 
760 	// let the active sound world mix all the channels in unless muted or avi demo recording
761 	if ( !muted && currentSoundWorld && !currentSoundWorld->fpa[0] ) {
762 		currentSoundWorld->MixLoop( newSoundTime, numSpeakers, finalMixBuffer );
763 	}
764 
765 	// disable audio hardware caching (this updates ALL settings since last alcSuspendContext)
766 	alcProcessContext( openalContext );
767 
768 	CurrentSoundTime = newSoundTime;
769 
770 	soundStats.timeinprocess = Sys_Milliseconds() - inTime;
771 
772 	return soundStats.timeinprocess;
773 }
774 
775 /*
776 ===================
777 idSoundSystemLocal::AsyncUpdateWrite
778 DG: using this now for 60Hz sound updates
779 called from async sound thread when com_asyncSound is 3 or 1
780 also called from main thread if com_asyncSound == 0
781 (those were the default values used in dhewm3 on unix-likes (except mac) or rest)
782 with this, once every async tic new sounds are started and existing ones updated,
783 instead of once every ~100ms.
784 ===================
785 */
AsyncUpdateWrite(int inTime)786 int idSoundSystemLocal::AsyncUpdateWrite( int inTime ) {
787 
788 	if ( !isInitialized || shutdown ) {
789 		return 0;
790 	}
791 
792 	int sampleTime = inTime * 44.1f;
793 	int numSpeakers = s_numberOfSpeakers.GetInteger();
794 
795 	// enable audio hardware caching
796 	alcSuspendContext( openalContext );
797 
798 	// let the active sound world mix all the channels in unless muted or avi demo recording
799 	if ( !muted && currentSoundWorld && !currentSoundWorld->fpa[0] ) {
800 		currentSoundWorld->MixLoop( sampleTime, numSpeakers, finalMixBuffer );
801 	}
802 
803 	// disable audio hardware caching (this updates ALL settings since last alcSuspendContext)
804 	alcProcessContext( openalContext );
805 
806 	CurrentSoundTime = sampleTime;
807 
808 	return Sys_Milliseconds() - inTime;
809 }
810 
811 /*
812 ===================
813 idSoundSystemLocal::dB2Scale
814 ===================
815 */
dB2Scale(const float val) const816 float idSoundSystemLocal::dB2Scale( const float val ) const {
817 	if ( val == 0.0f ) {
818 		return 1.0f;				// most common
819 	} else if ( val <= -60.0f ) {
820 		return 0.0f;
821 	} else if ( val >= 60.0f ) {
822 		return powf( 2.0f, val * ( 1.0f / 6.0f ) );
823 	}
824 	int ival = (int)( ( val + 60.0f ) * 10.0f );
825 	return volumesDB[ival];
826 }
827 
828 /*
829 ===================
830 idSoundSystemLocal::ImageForTime
831 ===================
832 */
ImageForTime(const int milliseconds,const bool waveform)833 cinData_t idSoundSystemLocal::ImageForTime( const int milliseconds, const bool waveform ) {
834 	cinData_t ret;
835 	int i, j;
836 
837 	if ( !isInitialized ) {
838 		memset( &ret, 0, sizeof( ret ) );
839 		return ret;
840 	}
841 
842 	Sys_EnterCriticalSection();
843 
844 	if ( !graph ) {
845 		graph = (dword *)Mem_Alloc( 256*128 * 4);
846 	}
847 	memset( graph, 0, 256*128 * 4 );
848 	float *accum = finalMixBuffer;	// unfortunately, these are already clamped
849 	int time = Sys_Milliseconds();
850 
851 	int numSpeakers = s_numberOfSpeakers.GetInteger();
852 
853 	if ( !waveform ) {
854 		for( j = 0; j < numSpeakers; j++ ) {
855 			int meter = 0;
856 			for( i = 0; i < MIXBUFFER_SAMPLES; i++ ) {
857 				float result = idMath::Fabs(accum[i*numSpeakers+j]);
858 				if ( result > meter ) {
859 					meter = result;
860 				}
861 			}
862 
863 			meter /= 256;		// 32768 becomes 128
864 			if ( meter > 128 ) {
865 				meter = 128;
866 			}
867 			int offset;
868 			int xsize;
869 			if ( numSpeakers == 6 ) {
870 				offset = j * 40;
871 				xsize = 20;
872 			} else {
873 				offset = j * 128;
874 				xsize = 63;
875 			}
876 			int x,y;
877 			dword color = 0xff00ff00;
878 			for ( y = 0; y < 128; y++ ) {
879 				for ( x = 0; x < xsize; x++ ) {
880 					graph[(127-y)*256 + offset + x ] = color;
881 				}
882 #if 0
883 				if ( y == 80 ) {
884 					color = 0xff00ffff;
885 				} else if ( y == 112 ) {
886 					color = 0xff0000ff;
887 				}
888 #endif
889 				if ( y > meter ) {
890 					break;
891 				}
892 			}
893 
894 			if ( meter > meterTops[j] ) {
895 				meterTops[j] = meter;
896 				meterTopsTime[j] = time + s_meterTopTime.GetInteger();
897 			} else if ( time > meterTopsTime[j] && meterTops[j] > 0 ) {
898 				meterTops[j]--;
899 				if (meterTops[j]) {
900 					meterTops[j]--;
901 				}
902 			}
903 		}
904 
905 		for( j = 0; j < numSpeakers; j++ ) {
906 			int meter = meterTops[j];
907 
908 			int offset;
909 			int xsize;
910 			if ( numSpeakers == 6 ) {
911 				offset = j*40;
912 				xsize = 20;
913 			} else {
914 				offset = j*128;
915 				xsize = 63;
916 			}
917 			int x,y;
918 			dword color;
919 			if ( meter <= 80 ) {
920 				color = 0xff007f00;
921 			} else if ( meter <= 112 ) {
922 				color = 0xff007f7f;
923 			} else {
924 				color = 0xff00007f;
925 			}
926 			for ( y = meter; y < 128 && y < meter + 4; y++ ) {
927 				for ( x = 0; x < xsize; x++ ) {
928 					graph[(127-y)*256 + offset + x ] = color;
929 				}
930 			}
931 		}
932 	} else {
933 		dword colors[] = { 0xff007f00, 0xff007f7f, 0xff00007f, 0xff00ff00, 0xff00ffff, 0xff0000ff };
934 
935 		for( j = 0; j < numSpeakers; j++ ) {
936 			int xx = 0;
937 			float fmeter;
938 			int step = MIXBUFFER_SAMPLES / 256;
939 			for( i = 0; i < MIXBUFFER_SAMPLES; i += step ) {
940 				fmeter = 0.0f;
941 				for( int x = 0; x < step; x++ ) {
942 					float result = accum[(i+x)*numSpeakers+j];
943 					result = result / 32768.0f;
944 					fmeter += result;
945 				}
946 				fmeter /= 4.0f;
947 				if ( fmeter < -1.0f ) {
948 					fmeter = -1.0f;
949 				} else if ( fmeter > 1.0f ) {
950 					fmeter = 1.0f;
951 				}
952 				int meter = (fmeter * 63.0f);
953 				graph[ (meter + 64) * 256 + xx ] = colors[j];
954 
955 				if ( meter < 0 ) {
956 					meter = -meter;
957 				}
958 				if ( meter > meterTops[xx] ) {
959 					meterTops[xx] = meter;
960 					meterTopsTime[xx] = time + 100;
961 				} else if ( time>meterTopsTime[xx] && meterTops[xx] > 0 ) {
962 					meterTops[xx]--;
963 					if ( meterTops[xx] ) {
964 						meterTops[xx]--;
965 					}
966 				}
967 				xx++;
968 			}
969 		}
970 		for( i = 0; i < 256; i++ ) {
971 			int meter = meterTops[i];
972 			for ( int y = -meter; y < meter; y++ ) {
973 				graph[ (y+64)*256 + i ] = colors[j];
974 			}
975 		}
976 	}
977 	ret.imageHeight = 128;
978 	ret.imageWidth = 256;
979 	ret.image = (unsigned char *)graph;
980 
981 	Sys_LeaveCriticalSection();
982 
983 	return ret;
984 }
985 
986 /*
987 ===================
988 idSoundSystemLocal::GetSoundDecoderInfo
989 ===================
990 */
GetSoundDecoderInfo(int index,soundDecoderInfo_t & decoderInfo)991 int idSoundSystemLocal::GetSoundDecoderInfo( int index, soundDecoderInfo_t &decoderInfo ) {
992 	int i, j, firstEmitter, firstChannel;
993 	idSoundWorldLocal *sw = soundSystemLocal.currentSoundWorld;
994 
995 	if ( index < 0 ) {
996 		firstEmitter = 0;
997 		firstChannel = 0;
998 	} else {
999 		firstEmitter = index / SOUND_MAX_CHANNELS;
1000 		firstChannel = index - firstEmitter * SOUND_MAX_CHANNELS + 1;
1001 	}
1002 
1003 	for ( i = firstEmitter; i < sw->emitters.Num(); i++ ) {
1004 		idSoundEmitterLocal *sound = sw->emitters[i];
1005 
1006 		if ( !sound ) {
1007 			continue;
1008 		}
1009 
1010 		// run through all the channels
1011 		for ( j = firstChannel; j < SOUND_MAX_CHANNELS; j++ ) {
1012 			idSoundChannel	*chan = &sound->channels[j];
1013 
1014 			if ( chan->decoder == NULL ) {
1015 				continue;
1016 			}
1017 
1018 			idSoundSample *sample = chan->decoder->GetSample();
1019 
1020 			if ( sample == NULL ) {
1021 				continue;
1022 			}
1023 
1024 			decoderInfo.name = sample->name;
1025 			decoderInfo.format = ( sample->objectInfo.wFormatTag == WAVE_FORMAT_TAG_OGG ) ? "OGG" : "WAV";
1026 			decoderInfo.numChannels = sample->objectInfo.nChannels;
1027 			decoderInfo.numSamplesPerSecond = sample->objectInfo.nSamplesPerSec;
1028 			decoderInfo.num44kHzSamples = sample->LengthIn44kHzSamples();
1029 			decoderInfo.numBytes = sample->objectMemSize;
1030 			decoderInfo.looping = ( chan->parms.soundShaderFlags & SSF_LOOPING ) != 0;
1031 			decoderInfo.lastVolume = chan->lastVolume;
1032 			decoderInfo.start44kHzTime = chan->trigger44kHzTime;
1033 			decoderInfo.current44kHzTime = soundSystemLocal.GetCurrent44kHzTime();
1034 
1035 			return ( i * SOUND_MAX_CHANNELS + j );
1036 		}
1037 
1038 		firstChannel = 0;
1039 	}
1040 	return -1;
1041 }
1042 
1043 /*
1044 ===================
1045 idSoundSystemLocal::AllocSoundWorld
1046 ===================
1047 */
AllocSoundWorld(idRenderWorld * rw)1048 idSoundWorld *idSoundSystemLocal::AllocSoundWorld( idRenderWorld *rw ) {
1049 	idSoundWorldLocal	*local = new idSoundWorldLocal;
1050 
1051 	local->Init( rw );
1052 
1053 	return local;
1054 }
1055 
1056 /*
1057 ===================
1058 idSoundSystemLocal::SetMute
1059 ===================
1060 */
SetMute(bool muteOn)1061 void idSoundSystemLocal::SetMute( bool muteOn ) {
1062 	muted = muteOn;
1063 }
1064 
1065 /*
1066 ===================
1067 idSoundSystemLocal::SamplesToMilliseconds
1068 ===================
1069 */
SamplesToMilliseconds(int samples) const1070 int idSoundSystemLocal::SamplesToMilliseconds( int samples ) const {
1071 	return ( samples / (PRIMARYFREQ/1000) );
1072 }
1073 
1074 /*
1075 ===================
1076 idSoundSystemLocal::SamplesToMilliseconds
1077 ===================
1078 */
MillisecondsToSamples(int ms) const1079 int idSoundSystemLocal::MillisecondsToSamples( int ms ) const {
1080 	return ( ms * (PRIMARYFREQ/1000) );
1081 }
1082 
1083 /*
1084 ===================
1085 idSoundSystemLocal::SetPlayingSoundWorld
1086 
1087 specifying NULL will cause silence to be played
1088 ===================
1089 */
SetPlayingSoundWorld(idSoundWorld * soundWorld)1090 void idSoundSystemLocal::SetPlayingSoundWorld( idSoundWorld *soundWorld ) {
1091 	currentSoundWorld = static_cast<idSoundWorldLocal *>(soundWorld);
1092 }
1093 
1094 /*
1095 ===================
1096 idSoundSystemLocal::GetPlayingSoundWorld
1097 ===================
1098 */
GetPlayingSoundWorld(void)1099 idSoundWorld *idSoundSystemLocal::GetPlayingSoundWorld( void ) {
1100 	return currentSoundWorld;
1101 }
1102 
1103 /*
1104 ===================
1105 idSoundSystemLocal::BeginLevelLoad
1106 ===================
1107 */
1108 
BeginLevelLoad()1109 void idSoundSystemLocal::BeginLevelLoad() {
1110 	if ( !isInitialized ) {
1111 		return;
1112 	}
1113 	soundCache->BeginLevelLoad();
1114 
1115 	if ( efxloaded ) {
1116 		EFXDatabase.Clear();
1117 		efxloaded = false;
1118 	}
1119 }
1120 
1121 /*
1122 ===================
1123 idSoundSystemLocal::EndLevelLoad
1124 ===================
1125 */
EndLevelLoad(const char * mapstring)1126 void idSoundSystemLocal::EndLevelLoad( const char *mapstring ) {
1127 	if ( !isInitialized ) {
1128 		return;
1129 	}
1130 	soundCache->EndLevelLoad();
1131 
1132 	if (!useEFXReverb)
1133 		return;
1134 
1135 	idStr efxname( "efxs/" );
1136 	idStr mapname( mapstring );
1137 
1138 	mapname.SetFileExtension( ".efx" );
1139 	mapname.StripPath();
1140 	efxname += mapname;
1141 
1142 	efxloaded = EFXDatabase.LoadFile( efxname );
1143 
1144 	if ( efxloaded ) {
1145 		common->Printf("sound: found %s\n", efxname.c_str() );
1146 	} else {
1147 		common->Printf("sound: missing %s\n", efxname.c_str() );
1148 	}
1149 }
1150 
1151 /*
1152 ===================
1153 idSoundSystemLocal::AllocOpenALSource
1154 ===================
1155 */
AllocOpenALSource(idSoundChannel * chan,bool looping,bool stereo)1156 ALuint idSoundSystemLocal::AllocOpenALSource( idSoundChannel *chan, bool looping, bool stereo ) {
1157 	int timeOldestZeroVolSingleShot = Sys_Milliseconds();
1158 	int timeOldestZeroVolLooping = Sys_Milliseconds();
1159 	int timeOldestSingle = Sys_Milliseconds();
1160 	int iOldestZeroVolSingleShot = -1;
1161 	int iOldestZeroVolLooping = -1;
1162 	int iOldestSingle = -1;
1163 	int iUnused = -1;
1164 	int index = -1;
1165 	ALsizei i;
1166 
1167 	// Grab current msec time
1168 	int time = Sys_Milliseconds();
1169 
1170 	// Cycle through all sources
1171 	for ( i = 0; i < openalSourceCount; i++ ) {
1172 		// Use any unused source first,
1173 		// Then find oldest single shot quiet source,
1174 		// Then find oldest looping quiet source and
1175 		// Lastly find oldest single shot non quiet source..
1176 		if ( !openalSources[i].inUse ) {
1177 			iUnused = i;
1178 			break;
1179 		}  else if ( !openalSources[i].looping && openalSources[i].chan->lastVolume < SND_EPSILON ) {
1180 			if ( openalSources[i].startTime < timeOldestZeroVolSingleShot ) {
1181 				timeOldestZeroVolSingleShot = openalSources[i].startTime;
1182 				iOldestZeroVolSingleShot = i;
1183 			}
1184 		} else if ( openalSources[i].looping && openalSources[i].chan->lastVolume < SND_EPSILON ) {
1185 			if ( openalSources[i].startTime < timeOldestZeroVolLooping ) {
1186 				timeOldestZeroVolLooping = openalSources[i].startTime;
1187 				iOldestZeroVolLooping = i;
1188 			}
1189 		} else if ( !openalSources[i].looping ) {
1190 			if ( openalSources[i].startTime < timeOldestSingle ) {
1191 				timeOldestSingle = openalSources[i].startTime;
1192 				iOldestSingle = i;
1193 			}
1194 		}
1195 	}
1196 
1197 	if ( iUnused != -1 ) {
1198 		index = iUnused;
1199 	} else if ( iOldestZeroVolSingleShot != - 1 ) {
1200 		index = iOldestZeroVolSingleShot;
1201 	} else if ( iOldestZeroVolLooping != -1 ) {
1202 		index = iOldestZeroVolLooping;
1203 	} else if ( iOldestSingle != -1 ) {
1204 		index = iOldestSingle;
1205 	}
1206 
1207 	if ( index != -1 ) {
1208 		// stop the channel that is being ripped off
1209 		if ( openalSources[index].chan ) {
1210 			// stop the channel only when not looping
1211 			if ( !openalSources[index].looping ) {
1212 				openalSources[index].chan->Stop();
1213 			} else {
1214 				openalSources[index].chan->triggered = true;
1215 			}
1216 
1217 			// Free hardware resources
1218 			openalSources[index].chan->ALStop();
1219 		}
1220 
1221 		// Initialize structure
1222 		openalSources[index].startTime = time;
1223 		openalSources[index].chan = chan;
1224 		openalSources[index].inUse = true;
1225 		openalSources[index].looping = looping;
1226 		openalSources[index].stereo = stereo;
1227 
1228 		return openalSources[index].handle;
1229 	} else {
1230 		return 0;
1231 	}
1232 }
1233 
1234 /*
1235 ===================
1236 idSoundSystemLocal::FreeOpenALSource
1237 ===================
1238 */
FreeOpenALSource(ALuint handle)1239 void idSoundSystemLocal::FreeOpenALSource( ALuint handle ) {
1240 	ALsizei i;
1241 	for ( i = 0; i < openalSourceCount; i++ ) {
1242 		if ( openalSources[i].handle == handle ) {
1243 			if ( openalSources[i].chan ) {
1244 				openalSources[i].chan->openalSource = 0;
1245 			}
1246 
1247 			// Initialize structure
1248 			openalSources[i].startTime = 0;
1249 			openalSources[i].chan = NULL;
1250 			openalSources[i].inUse = false;
1251 			openalSources[i].looping = false;
1252 			openalSources[i].stereo = false;
1253 		}
1254 	}
1255 }
1256 
1257 /*
1258 ============================================================
1259 SoundFX and misc effects
1260 ============================================================
1261 */
1262 
1263 /*
1264 ===================
1265 idSoundSystemLocal::ProcessSample
1266 ===================
1267 */
ProcessSample(float * in,float * out)1268 void SoundFX_Lowpass::ProcessSample( float* in, float* out ) {
1269 	float c, a1, a2, a3, b1, b2;
1270 	float resonance = idSoundSystemLocal::s_enviroSuitCutoffQ.GetFloat();
1271 	float cutoffFrequency = idSoundSystemLocal::s_enviroSuitCutoffFreq.GetFloat();
1272 
1273 	Initialize();
1274 
1275 	c = 1.0 / idMath::Tan16( idMath::PI * cutoffFrequency / 44100 );
1276 
1277 	// compute coefs
1278 	a1 = 1.0 / ( 1.0 + resonance * c + c * c );
1279 	a2 = 2* a1;
1280 	a3 = a1;
1281 	b1 = 2.0 * ( 1.0 - c * c) * a1;
1282 	b2 = ( 1.0 - resonance * c + c * c ) * a1;
1283 
1284 	// compute output value
1285 	out[0] = a1 * in[0] + a2 * in[-1] + a3 * in[-2] - b1 * out[-1] - b2 * out[-2];
1286 }
1287 
ProcessSample(float * in,float * out)1288 void SoundFX_LowpassFast::ProcessSample( float* in, float* out ) {
1289 	// compute output value
1290 	out[0] = a1 * in[0] + a2 * in[-1] + a3 * in[-2] - b1 * out[-1] - b2 * out[-2];
1291 }
1292 
SetParms(float p1,float p2,float p3)1293 void SoundFX_LowpassFast::SetParms( float p1, float p2, float p3 ) {
1294 	float c;
1295 
1296 	// set the vars
1297 	freq = p1;
1298 	res = p2;
1299 
1300 	// precompute the coefs
1301 	c = 1.0 / idMath::Tan( idMath::PI * freq / 44100 );
1302 
1303 	// compute coefs
1304 	a1 = 1.0 / ( 1.0 + res * c + c * c );
1305 	a2 = 2* a1;
1306 	a3 = a1;
1307 
1308 	b1 = 2.0 * ( 1.0 - c * c) * a1;
1309 	b2 = ( 1.0 - res * c + c * c ) * a1;
1310 }
1311 
Initialize()1312 void SoundFX_Comb::Initialize() {
1313 	if ( initialized )
1314 		return;
1315 
1316 	initialized = true;
1317 	maxlen = 50000;
1318 	buffer = new float[maxlen];
1319 	currentTime = 0;
1320 }
1321 
ProcessSample(float * in,float * out)1322 void SoundFX_Comb::ProcessSample( float* in, float* out ) {
1323 	float gain = idSoundSystemLocal::s_reverbFeedback.GetFloat();
1324 	int len = idSoundSystemLocal::s_reverbTime.GetFloat() + param;
1325 
1326 	Initialize();
1327 
1328 	// sum up and output
1329 	out[0] = buffer[currentTime];
1330 	buffer[currentTime] = buffer[currentTime] * gain + in[0];
1331 
1332 	// increment current time
1333 	currentTime++;
1334 	if ( currentTime >= len )
1335 		currentTime -= len;
1336 }
1337 
1338 /*
1339 ===================
1340 idSoundSystemLocal::DoEnviroSuit
1341 ===================
1342 */
DoEnviroSuit(float * samples,int numSamples,int numSpeakers)1343 void idSoundSystemLocal::DoEnviroSuit( float* samples, int numSamples, int numSpeakers ) {
1344 	float out[10000], *out_p = out + 2;
1345 	float in[10000], *in_p = in + 2;
1346 
1347 	// TODO port to OpenAL
1348 	assert( false );
1349 
1350 	if ( !fxList.Num() ) {
1351 		for ( int i = 0; i < 6; i++ ) {
1352 			SoundFX* fx;
1353 
1354 			// lowpass filter
1355 			fx = new SoundFX_Lowpass();
1356 			fx->SetChannel( i );
1357 			fxList.Append( fx );
1358 
1359 			// comb
1360 			fx = new SoundFX_Comb();
1361 			fx->SetChannel( i );
1362 			fx->SetParameter( i * 100 );
1363 			fxList.Append( fx );
1364 
1365 			// comb
1366 			fx = new SoundFX_Comb();
1367 			fx->SetChannel( i );
1368 			fx->SetParameter( i * 100 + 5 );
1369 			fxList.Append( fx );
1370 		}
1371 	}
1372 
1373 	for ( int i = 0; i < numSpeakers; i++ ) {
1374 		int j;
1375 
1376 		// restore previous samples
1377 		memset( in, 0, 10000 * sizeof( float ) );
1378 		memset( out, 0, 10000 * sizeof( float ) );
1379 
1380 		// fx loop
1381 		for ( int k = 0; k < fxList.Num(); k++ ) {
1382 			SoundFX* fx = fxList[k];
1383 
1384 			// skip if we're not the right channel
1385 			if ( fx->GetChannel() != i )
1386 				continue;
1387 
1388 			// get samples and continuity
1389 			fx->GetContinuitySamples( in_p[-1], in_p[-2], out_p[-1], out_p[-2] );
1390 			for ( j = 0; j < numSamples; j++ ) {
1391 				in_p[j] = samples[j * numSpeakers + i] * s_enviroSuitVolumeScale.GetFloat();
1392 			}
1393 
1394 			// process fx loop
1395 			for ( j = 0; j < numSamples; j++ ) {
1396 				fx->ProcessSample( in_p + j, out_p + j );
1397 			}
1398 
1399 			// store samples and continuity
1400 			fx->SetContinuitySamples( in_p[numSamples-2], in_p[numSamples-3], out_p[numSamples-2], out_p[numSamples-3] );
1401 
1402 			for ( j = 0; j < numSamples; j++ ) {
1403 				samples[j * numSpeakers + i] = out_p[j];
1404 			}
1405 		}
1406 	}
1407 }
1408 
1409 /*
1410 =================
1411 idSoundSystemLocal::PrintMemInfo
1412 =================
1413 */
PrintMemInfo(MemInfo_t * mi)1414 void idSoundSystemLocal::PrintMemInfo( MemInfo_t *mi ) {
1415 	soundCache->PrintMemInfo( mi );
1416 }
1417 
1418 /*
1419 ===============
1420 idSoundSystemLocal::IsEFXAvailable
1421 ===============
1422 */
IsEFXAvailable(void)1423 int idSoundSystemLocal::IsEFXAvailable( void ) {
1424 #if defined(ID_DEDICATED)
1425 	return -1;
1426 #else
1427 	return EFXAvailable;
1428 #endif
1429 }
1430