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