2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "snd_local.h"
36 idSoundWorldLocal::Init
39 void idSoundWorldLocal::Init( idRenderWorld *renderWorld ) {
43 listenerAxis.Identity();
45 listenerPrivateId = 0;
48 listenerAreaName = "Undefined";
49 listenerEnvironmentID = -2;
56 for ( int i = 0 ; i < SOUND_MAX_CLASSES ; i++ ) {
57 soundClassFade[i].Clear();
60 // fill in the 0 index spot
61 idSoundEmitterLocal *placeHolder = new idSoundEmitterLocal;
62 emitters.Append( placeHolder );
64 fpa[0] = fpa[1] = fpa[2] = fpa[3] = fpa[4] = fpa[5] = NULL;
73 enviroSuitActive = false;
78 idSoundWorldLocal::idSoundWorldLocal
81 idSoundWorldLocal::idSoundWorldLocal() {
86 idSoundWorldLocal::~idSoundWorldLocal
89 idSoundWorldLocal::~idSoundWorldLocal() {
95 idSoundWorldLocal::Shutdown
97 this is called from the main thread
100 void idSoundWorldLocal::Shutdown() {
103 if ( soundSystemLocal.currentSoundWorld == this ) {
104 soundSystemLocal.currentSoundWorld = NULL;
109 for ( i = 0; i < emitters.Num(); i++ ) {
120 idSoundWorldLocal::ClearAllSoundEmitters
123 void idSoundWorldLocal::ClearAllSoundEmitters() {
126 Sys_EnterCriticalSection();
130 for ( i = 0; i < emitters.Num(); i++ ) {
131 idSoundEmitterLocal *sound = emitters[i];
136 Sys_LeaveCriticalSection();
141 idSoundWorldLocal::AllocLocalSoundEmitter
144 idSoundEmitterLocal *idSoundWorldLocal::AllocLocalSoundEmitter() {
146 idSoundEmitterLocal *def = NULL;
150 // never use the 0 index spot
152 for ( i = 1 ; i < emitters.Num() ; i++ ) {
155 // check for a completed and freed spot
156 if ( def->removeStatus >= REMOVE_STATUS_SAMPLEFINISHED ) {
158 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
159 common->Printf( "sound: recycling sound def %d\n", i );
166 // append a brand new one
167 def = new idSoundEmitterLocal;
169 // we need to protect this from the async thread
170 Sys_EnterCriticalSection();
171 index = emitters.Append( def );
172 Sys_LeaveCriticalSection();
174 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
175 common->Printf( "sound: appended new sound def %d\n", index );
181 def->removeStatus = REMOVE_STATUS_ALIVE;
182 def->soundWorld = this;
189 idSoundWorldLocal::AllocSoundEmitter
191 this is called from the main thread
194 idSoundEmitter *idSoundWorldLocal::AllocSoundEmitter() {
195 idSoundEmitterLocal *emitter = AllocLocalSoundEmitter();
197 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
198 common->Printf( "AllocSoundEmitter = %i\n", emitter->index );
201 writeDemo->WriteInt( DS_SOUND );
202 writeDemo->WriteInt( SCMD_ALLOC_EMITTER );
203 writeDemo->WriteInt( emitter->index );
211 idSoundWorldLocal::StartWritingDemo
213 this is called from the main thread
216 void idSoundWorldLocal::StartWritingDemo( idDemoFile *demo ) {
219 writeDemo->WriteInt( DS_SOUND );
220 writeDemo->WriteInt( SCMD_STATE );
222 // use the normal save game code to archive all the emitters
223 WriteToSaveGame( writeDemo );
228 idSoundWorldLocal::StopWritingDemo
230 this is called from the main thread
233 void idSoundWorldLocal::StopWritingDemo() {
239 idSoundWorldLocal::ProcessDemoCommand
241 this is called from the main thread
244 void idSoundWorldLocal::ProcessDemoCommand( idDemoFile *readDemo ) {
246 idSoundEmitterLocal *def;
252 soundDemoCommand_t dc;
254 if ( !readDemo->ReadInt( (int&)dc ) ) {
260 // we need to protect this from the async thread
261 // other instances of calling idSoundWorldLocal::ReadFromSaveGame do this while the sound code is muted
262 // setting muted and going right in may not be good enough here, as we async thread may already be in an async tick (in which case we could still race to it)
263 Sys_EnterCriticalSection();
264 ReadFromSaveGame( readDemo );
265 Sys_LeaveCriticalSection();
268 case SCMD_PLACE_LISTENER:
275 readDemo->ReadVec3( origin );
276 readDemo->ReadMat3( axis );
277 readDemo->ReadInt( listenerId );
278 readDemo->ReadInt( gameTime );
280 PlaceListener( origin, axis, listenerId, gameTime, "" );
283 case SCMD_ALLOC_EMITTER:
284 readDemo->ReadInt( index );
285 if ( index < 1 || index > emitters.Num() ) {
286 common->Error( "idSoundWorldLocal::ProcessDemoCommand: bad emitter number" );
288 if ( index == emitters.Num() ) {
289 // append a brand new one
290 def = new idSoundEmitterLocal;
291 emitters.Append( def );
293 def = emitters[ index ];
296 def->removeStatus = REMOVE_STATUS_ALIVE;
297 def->soundWorld = this;
303 readDemo->ReadInt( index );
304 readDemo->ReadInt( immediate );
305 EmitterForIndex( index )->Free( immediate != 0 );
312 soundShaderParms_t parms;
314 readDemo->ReadInt( index );
315 readDemo->ReadVec3( origin );
316 readDemo->ReadInt( listenerId );
317 readDemo->ReadFloat( parms.minDistance );
318 readDemo->ReadFloat( parms.maxDistance );
319 readDemo->ReadFloat( parms.volume );
320 readDemo->ReadFloat( parms.shakes );
321 readDemo->ReadInt( parms.soundShaderFlags );
322 readDemo->ReadInt( parms.soundClass );
323 EmitterForIndex( index )->UpdateEmitter( origin, listenerId, &parms );
328 const idSoundShader *shader;
333 readDemo->ReadInt( index );
334 shader = declManager->FindSound( readDemo->ReadHashString() );
335 readDemo->ReadInt( channel );
336 readDemo->ReadFloat( diversity );
337 readDemo->ReadInt( shaderFlags );
338 EmitterForIndex( index )->StartSound( shader, (s_channelType)channel, diversity, shaderFlags );
344 soundShaderParms_t parms;
346 readDemo->ReadInt( index );
347 readDemo->ReadInt( channel );
348 readDemo->ReadFloat( parms.minDistance );
349 readDemo->ReadFloat( parms.maxDistance );
350 readDemo->ReadFloat( parms.volume );
351 readDemo->ReadFloat( parms.shakes );
352 readDemo->ReadInt( parms.soundShaderFlags );
353 readDemo->ReadInt( parms.soundClass );
354 EmitterForIndex( index )->ModifySound( (s_channelType)channel, &parms );
361 readDemo->ReadInt( index );
362 readDemo->ReadInt( channel );
363 EmitterForIndex( index )->StopSound( (s_channelType)channel );
371 readDemo->ReadInt( index );
372 readDemo->ReadInt( channel );
373 readDemo->ReadFloat( to );
374 readDemo->ReadFloat( over );
375 EmitterForIndex( index )->FadeSound((s_channelType)channel, to, over );
383 idSoundWorldLocal::CurrentShakeAmplitudeForPosition
385 this is called from the main thread
388 float idSoundWorldLocal::CurrentShakeAmplitudeForPosition( const int time, const idVec3 &listererPosition ) {
392 if ( idSoundSystemLocal::s_constantAmplitude.GetFloat() >= 0.0f ) {
396 localTime = soundSystemLocal.GetCurrent44kHzTime();
398 for ( int i = 1; i < emitters.Num(); i++ ) {
399 idSoundEmitterLocal *sound = emitters[i];
400 if ( !sound->hasShakes ) {
403 amp += FindAmplitude( sound, localTime, &listererPosition, SCHANNEL_ANY, true );
410 idSoundWorldLocal::MixLoop
412 Sum all sound contributions into finalMixBuffer, an unclamped float buffer holding
413 all output channels. MIXBUFFER_SAMPLES samples will be created, with each sample consisting
414 of 2 or 6 floats depending on numSpeakers.
416 this is normally called from the sound thread, but also from the main thread
420 void idSoundWorldLocal::MixLoop( int current44kHz, int numSpeakers, float *finalMixBuffer ) {
422 idSoundEmitterLocal *sound;
424 // if noclip flying outside the world, leave silence
425 if ( listenerArea == -1 ) {
426 if ( idSoundSystemLocal::useOpenAL )
427 alListenerf( AL_GAIN, 0.0f );
431 // update the listener position and orientation
432 if ( idSoundSystemLocal::useOpenAL ) {
433 ALfloat listenerPosition[3];
435 listenerPosition[0] = -listenerPos.y;
436 listenerPosition[1] = listenerPos.z;
437 listenerPosition[2] = -listenerPos.x;
439 ALfloat listenerOrientation[6];
441 listenerOrientation[0] = -listenerAxis[0].y;
442 listenerOrientation[1] = listenerAxis[0].z;
443 listenerOrientation[2] = -listenerAxis[0].x;
445 listenerOrientation[3] = -listenerAxis[2].y;
446 listenerOrientation[4] = listenerAxis[2].z;
447 listenerOrientation[5] = -listenerAxis[2].x;
449 alListenerf( AL_GAIN, 1.0f );
450 alListenerfv( AL_POSITION, listenerPosition );
451 alListenerfv( AL_ORIENTATION, listenerOrientation );
454 if ( soundSystemLocal.s_useEAXReverb.GetBool() ) {
455 if ( soundSystemLocal.efxloaded ) {
456 idSoundEffect *effect = NULL;
457 int EnvironmentID = -1;
458 idStr defaultStr( "default" );
459 idStr listenerAreaStr( listenerArea );
461 soundSystemLocal.EFXDatabase.FindEffect( listenerAreaStr, &effect, &EnvironmentID );
463 soundSystemLocal.EFXDatabase.FindEffect( listenerAreaName, &effect, &EnvironmentID );
465 soundSystemLocal.EFXDatabase.FindEffect( defaultStr, &effect, &EnvironmentID );
467 // only update if change in settings
468 if ( soundSystemLocal.s_muteEAXReverb.GetBool() || ( listenerEnvironmentID != EnvironmentID ) ) {
469 EAXREVERBPROPERTIES EnvironmentParameters;
471 // get area reverb setting from EAX Manager
472 if ( ( effect ) && ( effect->data) && ( memcpy( &EnvironmentParameters, effect->data, effect->datasize ) ) ) {
473 if ( soundSystemLocal.s_muteEAXReverb.GetBool() ) {
474 EnvironmentParameters.lRoom = -10000;
477 if ( soundSystemLocal.alEAXSet ) {
478 soundSystemLocal.alEAXSet( &EAXPROPERTYID_EAX_FXSlot0, EAXREVERB_ALLPARAMETERS, 0, &EnvironmentParameters, sizeof( EnvironmentParameters ) );
481 listenerEnvironmentID = EnvironmentID;
488 // debugging option to mute all but a single soundEmitter
489 if ( idSoundSystemLocal::s_singleEmitter.GetInteger() > 0 && idSoundSystemLocal::s_singleEmitter.GetInteger() < emitters.Num() ) {
490 sound = emitters[idSoundSystemLocal::s_singleEmitter.GetInteger()];
492 if ( sound && sound->playing ) {
493 // run through all the channels
494 for ( j = 0; j < SOUND_MAX_CHANNELS ; j++ ) {
495 idSoundChannel *chan = &sound->channels[j];
497 // see if we have a sound triggered on this channel
498 if ( !chan->triggerState ) {
503 AddChannelContribution( sound, chan, current44kHz, numSpeakers, finalMixBuffer );
509 for ( i = 1; i < emitters.Num(); i++ ) {
515 // if no channels are active, do nothing
516 if ( !sound->playing ) {
519 // run through all the channels
520 for ( j = 0; j < SOUND_MAX_CHANNELS ; j++ ) {
521 idSoundChannel *chan = &sound->channels[j];
523 // see if we have a sound triggered on this channel
524 if ( !chan->triggerState ) {
529 AddChannelContribution( sound, chan, current44kHz, numSpeakers, finalMixBuffer );
533 if ( !idSoundSystemLocal::useOpenAL && enviroSuitActive ) {
534 soundSystemLocal.DoEnviroSuit( finalMixBuffer, MIXBUFFER_SAMPLES, numSpeakers );
538 //==============================================================================
542 idSoundWorldLocal::AVIOpen
544 this is called by the main thread
547 void idSoundWorldLocal::AVIOpen( const char *path, const char *name ) {
551 lastAVI44kHz = game44kHz - game44kHz % MIXBUFFER_SAMPLES;
553 if ( soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers() == 6 ) {
554 fpa[0] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_left.raw" );
555 fpa[1] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_right.raw" );
556 fpa[2] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_center.raw" );
557 fpa[3] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_lfe.raw" );
558 fpa[4] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_backleft.raw" );
559 fpa[5] = fileSystem->OpenFileWrite( aviDemoPath + "channel_51_backright.raw" );
561 fpa[0] = fileSystem->OpenFileWrite( aviDemoPath + "channel_left.raw" );
562 fpa[1] = fileSystem->OpenFileWrite( aviDemoPath + "channel_right.raw" );
565 soundSystemLocal.SetMute( true );
570 idSoundWorldLocal::AVIUpdate
572 this is called by the main thread
573 writes one block of sound samples if enough time has passed
574 This can be used to write wave files even if no sound hardware exists
577 void idSoundWorldLocal::AVIUpdate() {
580 if ( game44kHz - lastAVI44kHz < MIXBUFFER_SAMPLES ) {
584 if ( !soundSystemLocal.snd_audio_hw ) {
587 numSpeakers = soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers();
590 float mix[MIXBUFFER_SAMPLES*6+16];
591 float *mix_p = (float *)((( int)mix + 15 ) & ~15); // SIMD align
593 SIMDProcessor->Memset( mix_p, 0, MIXBUFFER_SAMPLES*sizeof(float)*numSpeakers );
595 MixLoop( lastAVI44kHz, numSpeakers, mix_p );
597 for ( int i = 0; i < numSpeakers; i++ ) {
598 short outD[MIXBUFFER_SAMPLES];
600 for( int j = 0; j < MIXBUFFER_SAMPLES; j++ ) {
601 float s = mix_p[ j*numSpeakers + i];
602 if ( s < -32768.0f ) {
604 } else if ( s > 32767.0f ) {
607 outD[j] = idMath::FtoiFast( s );
611 fpa[i]->Write( outD, MIXBUFFER_SAMPLES*sizeof(short) );
614 lastAVI44kHz += MIXBUFFER_SAMPLES;
621 idSoundWorldLocal::AVIClose
624 void idSoundWorldLocal::AVIClose( void ) {
631 // make sure the final block is written
632 game44kHz += MIXBUFFER_SAMPLES;
634 game44kHz -= MIXBUFFER_SAMPLES;
636 for ( i = 0; i < 6; i++ ) {
637 if ( fpa[i] != NULL ) {
638 fileSystem->CloseFile( fpa[i] );
642 if ( soundSystemLocal.snd_audio_hw->GetNumberOfSpeakers() == 2 ) {
643 // convert it to a wave file
644 idFile *rL, *lL, *wO;
647 name = aviDemoPath + aviDemoName + ".wav";
648 wO = fileSystem->OpenFileWrite( name );
650 common->Error( "Couldn't write %s", name.c_str() );
653 name = aviDemoPath + "channel_right.raw";
654 rL = fileSystem->OpenFileRead( name );
656 common->Error( "Couldn't open %s", name.c_str() );
659 name = aviDemoPath + "channel_left.raw";
660 lL = fileSystem->OpenFileRead( name );
662 common->Error( "Couldn't open %s", name.c_str() );
665 int numSamples = rL->Length()/2;
667 pcmwaveformat_t format;
669 info.ckid = fourcc_riff;
670 info.fccType = mmioFOURCC( 'W', 'A', 'V', 'E' );
671 info.cksize = (rL->Length()*2) - 8 + 4 + 16 + 8 + 8;
672 info.dwDataOffset = 12;
674 wO->Write( &info, 12 );
676 info.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
679 wO->Write( &info, 8 );
681 format.wBitsPerSample = 16;
682 format.wf.nAvgBytesPerSec = 44100*4; // sample rate * block align
683 format.wf.nChannels = 2;
684 format.wf.nSamplesPerSec = 44100;
685 format.wf.wFormatTag = WAVE_FORMAT_TAG_PCM;
686 format.wf.nBlockAlign = 4; // channels * bits/sample / 8
688 wO->Write( &format, 16 );
690 info.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
691 info.cksize = rL->Length() * 2;
693 wO->Write( &info, 8 );
696 for( i = 0; i < numSamples; i++ ) {
703 fileSystem->CloseFile( wO );
704 fileSystem->CloseFile( lL );
705 fileSystem->CloseFile( rL );
707 fileSystem->RemoveFile( aviDemoPath + "channel_right.raw" );
708 fileSystem->RemoveFile( aviDemoPath + "channel_left.raw" );
711 soundSystemLocal.SetMute( false );
714 //==============================================================================
719 idSoundWorldLocal::ResolveOrigin
721 Find out of the sound is completely occluded by a closed door portal, or
722 the virtual sound origin position at the portal closest to the listener.
723 this is called by the main thread
725 dist is the distance from the orignial sound origin to the current portal that enters soundArea
726 def->distance is the distance we are trying to reduce.
728 If there is no path through open portals from the sound to the listener, def->distance will remain
732 static const int MAX_PORTAL_TRACE_DEPTH = 10;
734 void idSoundWorldLocal::ResolveOrigin( const int stackDepth, const soundPortalTrace_t *prevStack, const int soundArea, const float dist, const idVec3& soundOrigin, idSoundEmitterLocal *def ) {
736 if ( dist >= def->distance ) {
737 // we can't possibly hear the sound through this chain of portals
741 if ( soundArea == listenerArea ) {
742 float fullDist = dist + (soundOrigin - listenerQU).LengthFast();
743 if ( fullDist < def->distance ) {
744 def->distance = fullDist;
745 def->spatializedOrigin = soundOrigin;
750 if ( stackDepth == MAX_PORTAL_TRACE_DEPTH ) {
751 // don't spend too much time doing these calculations in big maps
755 soundPortalTrace_t newStack;
756 newStack.portalArea = soundArea;
757 newStack.prevStack = prevStack;
759 int numPortals = rw->NumPortalsInArea( soundArea );
760 for( int p = 0; p < numPortals; p++ ) {
761 exitPortal_t re = rw->GetPortal( soundArea, p );
763 float occlusionDistance = 0;
765 // air blocking windows will block sound like closed doors
766 if ( (re.blockingBits & ( PS_BLOCK_VIEW | PS_BLOCK_AIR ) ) ) {
767 // we could just completely cut sound off, but reducing the volume works better
769 occlusionDistance = idSoundSystemLocal::s_doorDistanceAdd.GetFloat();
772 // what area are we about to go look at
773 int otherArea = re.areas[0];
774 if ( re.areas[0] == soundArea ) {
775 otherArea = re.areas[1];
778 // if this area is already in our portal chain, don't bother looking into it
779 const soundPortalTrace_t *prev;
780 for ( prev = prevStack ; prev ; prev = prev->prevStack ) {
781 if ( prev->portalArea == otherArea ) {
789 // pick a point on the portal to serve as our virtual sound origin
794 re.w->GetPlane( pl );
797 idVec3 dir = listenerQU - soundOrigin;
798 if ( !pl.RayIntersection( soundOrigin, dir, scale ) ) {
799 source = re.w->GetCenter();
801 source = soundOrigin + scale * dir;
803 // if this point isn't inside the portal edges, slide it in
804 for ( int i = 0 ; i < re.w->GetNumPoints() ; i++ ) {
805 int j = ( i + 1 ) % re.w->GetNumPoints();
806 idVec3 edgeDir = (*(re.w))[j].ToVec3() - (*(re.w))[i].ToVec3();
809 edgeNormal.Cross( pl.Normal(), edgeDir );
811 idVec3 fromVert = source - (*(re.w))[j].ToVec3();
813 float d = edgeNormal * fromVert;
816 float div = edgeNormal.Normalize();
819 source -= d * edgeNormal;
824 // clip the ray from the listener to the center of the portal by
825 // all the portal edge planes, then project that point (or the original if not clipped)
826 // onto the portal plane to get the spatialized origin
828 idVec3 start = listenerQU;
829 idVec3 mid = re.w->GetCenter();
830 bool wasClipped = false;
832 for ( int i = 0 ; i < re.w->GetNumPoints() ; i++ ) {
833 int j = ( i + 1 ) % re.w->GetNumPoints();
834 idVec3 v1 = (*(re.w))[j].ToVec3() - soundOrigin;
835 idVec3 v2 = (*(re.w))[i].ToVec3() - soundOrigin;
842 edgeNormal.Cross( v1, v2 );
844 idVec3 fromVert = start - soundOrigin;
845 float d1 = edgeNormal * fromVert;
848 fromVert = mid - (*(re.w))[j].ToVec3();
849 float d2 = edgeNormal * fromVert;
852 float f = d1 / ( d1 - d2 );
854 idVec3 clipped = start * ( 1.0f - f ) + mid * f;
862 // now project it onto the portal plane
864 re.w->GetPlane( pl );
866 float f1 = pl.Distance( start );
867 float f2 = pl.Distance( soundOrigin );
869 float f = f1 / ( f1 - f2 );
870 source = start * ( 1.0f - f ) + soundOrigin * f;
872 source = soundOrigin;
876 idVec3 tlen = source - soundOrigin;
877 float tlenLength = tlen.LengthFast();
879 ResolveOrigin( stackDepth+1, &newStack, otherArea, dist+tlenLength+occlusionDistance, source, def );
886 idSoundWorldLocal::PlaceListener
888 this is called by the main thread
891 void idSoundWorldLocal::PlaceListener( const idVec3& origin, const idMat3& axis,
892 const int listenerId, const int gameTime, const idStr& areaName ) {
894 int current44kHzTime;
896 if ( !soundSystemLocal.isInitialized ) {
900 if ( pause44kHz >= 0 ){
905 writeDemo->WriteInt( DS_SOUND );
906 writeDemo->WriteInt( SCMD_PLACE_LISTENER );
907 writeDemo->WriteVec3( origin );
908 writeDemo->WriteMat3( axis );
909 writeDemo->WriteInt( listenerId );
910 writeDemo->WriteInt( gameTime );
913 current44kHzTime = soundSystemLocal.GetCurrent44kHzTime();
915 // we usually expect gameTime to be increasing by 16 or 32 msec, but when
916 // a cinematic is fast-forward skipped through, it can jump by a significant
917 // amount, while the hardware 44kHz position will not have changed accordingly,
918 // which would make sounds (like long character speaches) continue from the
919 // old time. Fix this by killing all non-looping sounds
920 if ( gameTime > gameMsec + 500 ) {
921 OffsetSoundTime( - ( gameTime - gameMsec ) * 0.001f * 44100.0f );
926 // exactly 30 fps so the wave file can be used for exact video frames
927 game44kHz = idMath::FtoiFast( gameMsec * ( ( 1000.0f / 60.0f ) / 16.0f ) * 0.001f * 44100.0f );
929 // the normal 16 msec / frame
930 game44kHz = idMath::FtoiFast( gameMsec * 0.001f * 44100.0f );
933 listenerPrivateId = listenerId;
935 listenerQU = origin; // Doom units
936 listenerPos = origin * DOOM_TO_METERS; // meters
938 listenerAreaName = areaName;
939 listenerAreaName.ToLower();
942 listenerArea = rw->PointInArea( listenerQU ); // where are we?
947 if ( listenerArea < 0 ) {
951 ForegroundUpdate( current44kHzTime );
956 idSoundWorldLocal::ForegroundUpdate
959 void idSoundWorldLocal::ForegroundUpdate( int current44kHzTime ) {
961 idSoundEmitterLocal *def;
963 if ( !soundSystemLocal.isInitialized ) {
967 Sys_EnterCriticalSection();
969 // if we are recording an AVI demo, don't use hardware time
971 current44kHzTime = lastAVI44kHz;
975 // check to see if each sound is visible or not
976 // speed up by checking maxdistance to origin
977 // although the sound may still need to play if it has
978 // just become occluded so it can ramp down to 0
980 for ( j = 1; j < emitters.Num(); j++ ) {
983 if ( def->removeStatus >= REMOVE_STATUS_SAMPLEFINISHED ) {
987 // see if our last channel just finished
988 def->CheckForCompletion( current44kHzTime );
990 if ( !def->playing ) {
994 // update virtual origin / distance, etc
995 def->Spatialize( listenerPos, listenerArea, rw );
997 // per-sound debug options
998 if ( idSoundSystemLocal::s_drawSounds.GetInteger() && rw ) {
999 if ( def->distance < def->maxDistance || idSoundSystemLocal::s_drawSounds.GetInteger() > 1 ) {
1002 ref.AddPoint( idVec3( -10, -10, -10 ) );
1003 ref.AddPoint( idVec3( 10, 10, 10 ) );
1004 float vis = (1.0f - (def->distance / def->maxDistance));
1007 rw->DebugBounds( idVec4( vis, 0.25f, vis, vis ), ref, def->origin );
1009 // draw an arrow to the audible position, possible a portal center
1010 if ( def->origin != def->spatializedOrigin ) {
1011 rw->DebugArrow( colorRed, def->origin, def->spatializedOrigin, 4 );
1015 idVec3 textPos = def->origin;
1017 rw->DrawText( va("%i", def->index), textPos, 0.1f, idVec4(1,0,0,1), listenerAxis );
1020 // run through all the channels
1021 for ( k = 0; k < SOUND_MAX_CHANNELS ; k++ ) {
1022 idSoundChannel *chan = &def->channels[k];
1024 // see if we have a sound triggered on this channel
1025 if ( !chan->triggerState ) {
1030 float min = chan->parms.minDistance;
1031 float max = chan->parms.maxDistance;
1032 const char *defaulted = chan->leadinSample->defaultSound ? "(DEFAULTED)" : "";
1033 sprintf( text, "%s (%i/%i %i/%i)%s", chan->soundShader->GetName(), (int)def->distance,
1034 (int)def->realDistance, (int)min, (int)max, defaulted );
1035 rw->DrawText( text, textPos, 0.1f, idVec4(1,0,0,1), listenerAxis );
1042 Sys_LeaveCriticalSection();
1047 if ( idSoundSystemLocal::s_showLevelMeter.GetInteger() ) {
1048 const idMaterial *gui = declManager->FindMaterial( "guis/assets/soundmeter/audiobg", false );
1050 const shaderStage_t *foo = gui->GetStage(0);
1051 if ( !foo->texture.cinematic ) {
1052 ((shaderStage_t *)foo)->texture.cinematic = new idSndWindow;
1058 // optionally dump out the generated sound
1067 idSoundWorldLocal::OffsetSoundTime
1070 void idSoundWorldLocal::OffsetSoundTime( int offset44kHz ) {
1073 for ( i = 0; i < emitters.Num(); i++ ) {
1074 if ( emitters[i] == NULL ) {
1077 for ( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
1078 idSoundChannel *chan = &emitters[i]->channels[ j ];
1080 if ( !chan->triggerState ) {
1084 chan->trigger44kHzTime += offset44kHz;
1091 idSoundWorldLocal::WriteToSaveGame
1094 void idSoundWorldLocal::WriteToSaveGame( idFile *savefile ) {
1095 int i, j, num, currentSoundTime;
1098 // the game soundworld is always paused at this point, save that time down
1099 if ( pause44kHz > 0 ) {
1100 currentSoundTime = pause44kHz;
1102 currentSoundTime = soundSystemLocal.GetCurrent44kHzTime();
1105 // write listener data
1106 savefile->WriteVec3(listenerQU);
1107 savefile->WriteMat3(listenerAxis);
1108 savefile->WriteInt(listenerPrivateId);
1109 savefile->WriteInt(gameMsec);
1110 savefile->WriteInt(game44kHz);
1111 savefile->WriteInt(currentSoundTime);
1113 num = emitters.Num();
1114 savefile->WriteInt(num);
1116 for ( i = 1; i < emitters.Num(); i++ ) {
1117 idSoundEmitterLocal *def = emitters[i];
1119 if ( def->removeStatus != REMOVE_STATUS_ALIVE ) {
1121 savefile->Write( &skip, sizeof( skip ) );
1125 savefile->WriteInt(i);
1127 // Write the emitter data
1128 savefile->WriteVec3( def->origin );
1129 savefile->WriteInt( def->listenerId );
1130 WriteToSaveGameSoundShaderParams( savefile, &def->parms );
1131 savefile->WriteFloat( def->amplitude );
1132 savefile->WriteInt( def->ampTime );
1133 for (int k = 0; k < SOUND_MAX_CHANNELS; k++)
1134 WriteToSaveGameSoundChannel( savefile, &def->channels[k] );
1135 savefile->WriteFloat( def->distance );
1136 savefile->WriteBool( def->hasShakes );
1137 savefile->WriteInt( def->lastValidPortalArea );
1138 savefile->WriteFloat( def->maxDistance );
1139 savefile->WriteBool( def->playing );
1140 savefile->WriteFloat( def->realDistance );
1141 savefile->WriteInt( def->removeStatus );
1142 savefile->WriteVec3( def->spatializedOrigin );
1144 // write the channel data
1145 for( j = 0; j < SOUND_MAX_CHANNELS; j++ ) {
1146 idSoundChannel *chan = &def->channels[ j ];
1148 // Write out any sound commands for this def
1149 if ( chan->triggerState && chan->soundShader && chan->leadinSample ) {
1151 savefile->WriteInt( j );
1153 // write the pointers out separately
1154 name = chan->soundShader->GetName();
1155 savefile->WriteString( name );
1157 name = chan->leadinSample->name;
1158 savefile->WriteString( name );
1162 // End active channels with -1
1164 savefile->WriteInt( end );
1167 // new in Doom3 v1.2
1168 savefile->Write( &slowmoActive, sizeof( slowmoActive ) );
1169 savefile->Write( &slowmoSpeed, sizeof( slowmoSpeed ) );
1170 savefile->Write( &enviroSuitActive, sizeof( enviroSuitActive ) );
1175 idSoundWorldLocal::WriteToSaveGameSoundShaderParams
1178 void idSoundWorldLocal::WriteToSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params ) {
1179 saveGame->WriteFloat(params->minDistance);
1180 saveGame->WriteFloat(params->maxDistance);
1181 saveGame->WriteFloat(params->volume);
1182 saveGame->WriteFloat(params->shakes);
1183 saveGame->WriteInt(params->soundShaderFlags);
1184 saveGame->WriteInt(params->soundClass);
1189 idSoundWorldLocal::WriteToSaveGameSoundChannel
1192 void idSoundWorldLocal::WriteToSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch ) {
1193 saveGame->WriteBool( ch->triggerState );
1194 saveGame->WriteUnsignedChar( 0 );
1195 saveGame->WriteUnsignedChar( 0 );
1196 saveGame->WriteUnsignedChar( 0 );
1197 saveGame->WriteInt( ch->trigger44kHzTime );
1198 saveGame->WriteInt( ch->triggerGame44kHzTime );
1199 WriteToSaveGameSoundShaderParams( saveGame, &ch->parms );
1200 saveGame->WriteInt( (int)ch->leadinSample );
1201 saveGame->WriteInt( ch->triggerChannel );
1202 saveGame->WriteInt( (int)ch->soundShader );
1203 saveGame->WriteInt( (int)ch->decoder );
1204 saveGame->WriteFloat(ch->diversity );
1205 saveGame->WriteFloat(ch->lastVolume );
1206 for (int m = 0; m < 6; m++)
1207 saveGame->WriteFloat( ch->lastV[m] );
1208 saveGame->WriteInt( ch->channelFade.fadeStart44kHz );
1209 saveGame->WriteInt( ch->channelFade.fadeEnd44kHz );
1210 saveGame->WriteFloat( ch->channelFade.fadeStartVolume );
1211 saveGame->WriteFloat( ch->channelFade.fadeEndVolume );
1216 idSoundWorldLocal::ReadFromSaveGame
1219 void idSoundWorldLocal::ReadFromSaveGame( idFile *savefile ) {
1220 int i, num, handle, listenerId, gameTime, channel;
1221 int savedSoundTime, currentSoundTime, soundTimeOffset;
1222 idSoundEmitterLocal *def;
1227 ClearAllSoundEmitters();
1229 savefile->ReadVec3( origin );
1230 savefile->ReadMat3( axis );
1231 savefile->ReadInt( listenerId );
1232 savefile->ReadInt( gameTime );
1233 savefile->ReadInt( game44kHz );
1234 savefile->ReadInt( savedSoundTime );
1236 // we will adjust the sound starting times from those saved with the demo
1237 currentSoundTime = soundSystemLocal.GetCurrent44kHzTime();
1238 soundTimeOffset = currentSoundTime - savedSoundTime;
1240 // at the end of the level load we unpause the sound world and adjust the sound starting times once more
1241 pause44kHz = currentSoundTime;
1244 PlaceListener( origin, axis, listenerId, gameTime, "Undefined" );
1246 // make sure there are enough
1247 // slots to read the saveGame in. We don't shrink the list
1248 // if there are extras.
1249 savefile->ReadInt( num );
1251 while( emitters.Num() < num ) {
1252 def = new idSoundEmitterLocal;
1253 def->index = emitters.Append( def );
1254 def->soundWorld = this;
1257 // read in the state
1258 for ( i = 1; i < num; i++ ) {
1260 savefile->ReadInt( handle );
1264 if ( handle != i ) {
1265 common->Error( "idSoundWorldLocal::ReadFromSaveGame: index mismatch" );
1269 def->removeStatus = REMOVE_STATUS_ALIVE;
1270 def->playing = true; // may be reset by the first UpdateListener
1272 savefile->ReadVec3( def->origin );
1273 savefile->ReadInt( def->listenerId );
1274 ReadFromSaveGameSoundShaderParams( savefile, &def->parms );
1275 savefile->ReadFloat( def->amplitude );
1276 savefile->ReadInt( def->ampTime );
1277 for (int k = 0; k < SOUND_MAX_CHANNELS; k++)
1278 ReadFromSaveGameSoundChannel( savefile, &def->channels[k] );
1279 savefile->ReadFloat( def->distance );
1280 savefile->ReadBool( def->hasShakes );
1281 savefile->ReadInt( def->lastValidPortalArea );
1282 savefile->ReadFloat( def->maxDistance );
1283 savefile->ReadBool( def->playing );
1284 savefile->ReadFloat( def->realDistance );
1285 savefile->ReadInt( (int&)def->removeStatus );
1286 savefile->ReadVec3( def->spatializedOrigin );
1288 // read the individual channels
1289 savefile->ReadInt( channel );
1291 while ( channel >= 0 ) {
1292 if ( channel > SOUND_MAX_CHANNELS ) {
1293 common->Error( "idSoundWorldLocal::ReadFromSaveGame: channel > SOUND_MAX_CHANNELS" );
1296 idSoundChannel *chan = &def->channels[channel];
1298 if ( chan->decoder != NULL ) {
1299 // The pointer in the save file is not valid, so we grab a new one
1300 chan->decoder = idSampleDecoder::Alloc();
1303 savefile->ReadString( soundShader );
1304 chan->soundShader = declManager->FindSound( soundShader );
1306 savefile->ReadString( soundShader );
1307 // load savegames with s_noSound 1
1308 if ( soundSystemLocal.soundCache ) {
1309 chan->leadinSample = soundSystemLocal.soundCache->FindSound( soundShader, false );
1311 chan->leadinSample = NULL;
1314 // adjust the hardware start time
1315 chan->trigger44kHzTime += soundTimeOffset;
1317 // make sure we start up the hardware voice if needed
1318 chan->triggered = chan->triggerState;
1319 chan->openalStreamingOffset = currentSoundTime - chan->trigger44kHzTime;
1321 // adjust the hardware fade time
1322 if ( chan->channelFade.fadeStart44kHz != 0 ) {
1323 chan->channelFade.fadeStart44kHz += soundTimeOffset;
1324 chan->channelFade.fadeEnd44kHz += soundTimeOffset;
1328 savefile->ReadInt( channel );
1332 if ( session->GetSaveGameVersion() >= 17 ) {
1333 savefile->Read( &slowmoActive, sizeof( slowmoActive ) );
1334 savefile->Read( &slowmoSpeed, sizeof( slowmoSpeed ) );
1335 savefile->Read( &enviroSuitActive, sizeof( enviroSuitActive ) );
1337 slowmoActive = false;
1339 enviroSuitActive = false;
1345 idSoundWorldLocal::ReadFromSaveGameSoundShaderParams
1348 void idSoundWorldLocal::ReadFromSaveGameSoundShaderParams( idFile *saveGame, soundShaderParms_t *params ) {
1349 saveGame->ReadFloat(params->minDistance);
1350 saveGame->ReadFloat(params->maxDistance);
1351 saveGame->ReadFloat(params->volume);
1352 saveGame->ReadFloat(params->shakes);
1353 saveGame->ReadInt(params->soundShaderFlags);
1354 saveGame->ReadInt(params->soundClass);
1359 idSoundWorldLocal::ReadFromSaveGameSoundChannel
1362 void idSoundWorldLocal::ReadFromSaveGameSoundChannel( idFile *saveGame, idSoundChannel *ch ) {
1363 saveGame->ReadBool( ch->triggerState );
1365 saveGame->ReadChar( tmp );
1366 saveGame->ReadChar( tmp );
1367 saveGame->ReadChar( tmp );
1368 saveGame->ReadInt( ch->trigger44kHzTime );
1369 saveGame->ReadInt( ch->triggerGame44kHzTime );
1370 ReadFromSaveGameSoundShaderParams( saveGame, &ch->parms );
1371 saveGame->ReadInt( (int&)ch->leadinSample );
1372 saveGame->ReadInt( ch->triggerChannel );
1373 saveGame->ReadInt( (int&)ch->soundShader );
1374 saveGame->ReadInt( (int&)ch->decoder );
1375 saveGame->ReadFloat(ch->diversity );
1376 saveGame->ReadFloat(ch->lastVolume );
1377 for (int m = 0; m < 6; m++)
1378 saveGame->ReadFloat( ch->lastV[m] );
1379 saveGame->ReadInt( ch->channelFade.fadeStart44kHz );
1380 saveGame->ReadInt( ch->channelFade.fadeEnd44kHz );
1381 saveGame->ReadFloat( ch->channelFade.fadeStartVolume );
1382 saveGame->ReadFloat( ch->channelFade.fadeEndVolume );
1387 idSoundWorldLocal::EmitterForIndex
1390 idSoundEmitter *idSoundWorldLocal::EmitterForIndex( int index ) {
1394 if ( index >= emitters.Num() ) {
1395 common->Error( "idSoundWorldLocal::EmitterForIndex: %i > %i", index, emitters.Num() );
1397 return emitters[index];
1402 idSoundWorldLocal::StopAllSounds
1404 this is called from the main thread
1407 void idSoundWorldLocal::StopAllSounds() {
1409 for ( int i = 0; i < emitters.Num(); i++ ) {
1410 idSoundEmitterLocal * def = emitters[i];
1411 def->StopSound( SCHANNEL_ANY );
1417 idSoundWorldLocal::Pause
1420 void idSoundWorldLocal::Pause( void ) {
1421 if ( pause44kHz >= 0 ) {
1422 common->Warning( "idSoundWorldLocal::Pause: already paused" );
1426 pause44kHz = soundSystemLocal.GetCurrent44kHzTime();
1431 idSoundWorldLocal::UnPause
1434 void idSoundWorldLocal::UnPause( void ) {
1437 if ( pause44kHz < 0 ) {
1438 common->Warning( "idSoundWorldLocal::UnPause: not paused" );
1442 offset44kHz = soundSystemLocal.GetCurrent44kHzTime() - pause44kHz;
1443 OffsetSoundTime( offset44kHz );
1450 idSoundWorldLocal::IsPaused
1453 bool idSoundWorldLocal::IsPaused( void ) {
1454 return ( pause44kHz >= 0 );
1459 idSoundWorldLocal::PlayShaderDirectly
1463 this is called from the main thread
1466 void idSoundWorldLocal::PlayShaderDirectly( const char *shaderName, int channel ) {
1468 if ( localSound && channel == -1 ) {
1469 localSound->StopSound( SCHANNEL_ANY );
1470 } else if ( localSound ) {
1471 localSound->StopSound( channel );
1474 if ( !shaderName || !shaderName[0] ) {
1478 const idSoundShader *shader = declManager->FindSound( shaderName );
1483 if ( !localSound ) {
1484 localSound = AllocLocalSoundEmitter();
1487 static idRandom rnd;
1488 float diversity = rnd.RandomFloat();
1490 localSound->StartSound( shader, ( channel == -1 ) ? SCHANNEL_ONE : channel , diversity, SSF_GLOBAL );
1492 // in case we are at the console without a game doing updates, force an update
1493 ForegroundUpdate( soundSystemLocal.GetCurrent44kHzTime() );
1498 idSoundWorldLocal::CalcEars
1500 Determine the volumes from each speaker for a given sound emitter
1503 void idSoundWorldLocal::CalcEars( int numSpeakers, idVec3 spatializedOrigin, idVec3 listenerPos,
1504 idMat3 listenerAxis, float ears[6], float spatialize ) {
1505 idVec3 svec = spatializedOrigin - listenerPos;
1508 ovec[0] = svec * listenerAxis[0];
1509 ovec[1] = svec * listenerAxis[1];
1510 ovec[2] = svec * listenerAxis[2];
1514 if ( numSpeakers == 6 ) {
1515 static idVec3 speakerVector[6] = {
1516 idVec3( 0.707f, 0.707f, 0.0f ), // front left
1517 idVec3( 0.707f, -0.707f, 0.0f ), // front right
1518 idVec3( 0.707f, 0.0f, 0.0f ), // front center
1519 idVec3( 0.0f, 0.0f, 0.0f ), // sub
1520 idVec3( -0.707f, 0.707f, 0.0f ), // rear left
1521 idVec3( -0.707f, -0.707f, 0.0f ) // rear right
1523 for ( int i = 0 ; i < 6 ; i++ ) {
1525 ears[i] = idSoundSystemLocal::s_subFraction.GetFloat(); // subwoofer
1528 float dot = ovec * speakerVector[i];
1529 ears[i] = (idSoundSystemLocal::s_dotbias6.GetFloat() + dot) / ( 1.0f + idSoundSystemLocal::s_dotbias6.GetFloat() );
1530 if ( ears[i] < idSoundSystemLocal::s_minVolume6.GetFloat() ) {
1531 ears[i] = idSoundSystemLocal::s_minVolume6.GetFloat();
1536 float dotBias = idSoundSystemLocal::s_dotbias2.GetFloat();
1538 // when we are inside the minDistance, start reducing the amount of spatialization
1539 // so NPC voices right in front of us aren't quieter that off to the side
1540 dotBias += ( idSoundSystemLocal::s_spatializationDecay.GetFloat() - dotBias ) * ( 1.0f - spatialize );
1542 ears[0] = (idSoundSystemLocal::s_dotbias2.GetFloat() + dot) / ( 1.0f + dotBias );
1543 ears[1] = (idSoundSystemLocal::s_dotbias2.GetFloat() - dot) / ( 1.0f + dotBias );
1545 if ( ears[0] < idSoundSystemLocal::s_minVolume2.GetFloat() ) {
1546 ears[0] = idSoundSystemLocal::s_minVolume2.GetFloat();
1548 if ( ears[1] < idSoundSystemLocal::s_minVolume2.GetFloat() ) {
1549 ears[1] = idSoundSystemLocal::s_minVolume2.GetFloat();
1561 idSoundWorldLocal::AddChannelContribution
1563 Adds the contribution of a single sound channel to finalMixBuffer
1564 this is called from the async thread
1566 Mixes MIXBUFFER_SAMPLES samples starting at current44kHz sample time into
1570 void idSoundWorldLocal::AddChannelContribution( idSoundEmitterLocal *sound, idSoundChannel *chan,
1571 int current44kHz, int numSpeakers, float *finalMixBuffer ) {
1576 // get the sound definition and parameters from the entity
1578 soundShaderParms_t *parms = &chan->parms;
1580 // assume we have a sound triggered on this channel
1581 assert( chan->triggerState );
1583 // fetch the actual wave file and see if it's valid
1584 idSoundSample *sample = chan->leadinSample;
1585 if ( sample == NULL ) {
1589 // if you don't want to hear all the beeps from missing sounds
1590 if ( sample->defaultSound && !idSoundSystemLocal::s_playDefaultSound.GetBool() ) {
1594 // get the actual shader
1595 const idSoundShader *shader = chan->soundShader;
1597 // this might happen if the foreground thread just deleted the sound emitter
1602 float maxd = parms->maxDistance;
1603 float mind = parms->minDistance;
1605 int mask = shader->speakerMask;
1606 bool omni = ( parms->soundShaderFlags & SSF_OMNIDIRECTIONAL) != 0;
1607 bool looping = ( parms->soundShaderFlags & SSF_LOOPING ) != 0;
1608 bool global = ( parms->soundShaderFlags & SSF_GLOBAL ) != 0;
1609 bool noOcclusion = ( parms->soundShaderFlags & SSF_NO_OCCLUSION ) || !idSoundSystemLocal::s_useOcclusion.GetBool();
1611 // speed goes from 1 to 0.2
1612 if ( idSoundSystemLocal::s_slowAttenuate.GetBool() && slowmoActive && !chan->disallowSlow ) {
1613 maxd *= slowmoSpeed;
1616 // stereo samples are always omni
1617 if ( sample->objectInfo.nChannels == 2 ) {
1621 // if the sound is playing from the current listener, it will not be spatialized at all
1622 if ( sound->listenerId == listenerPrivateId ) {
1627 // see if it's in range
1630 // convert volumes from decibels to float scale
1632 // leadin volume scale for shattering lights
1633 // this isn't exactly correct, because the modified volume will get applied to
1634 // some initial chunk of the loop as well, because the volume is scaled for the
1635 // entire mix buffer
1636 if ( shader->leadinVolume && current44kHz - chan->trigger44kHzTime < sample->LengthIn44kHzSamples() ) {
1637 volume = soundSystemLocal.dB2Scale( shader->leadinVolume );
1639 volume = soundSystemLocal.dB2Scale( parms->volume );
1642 // global volume scale
1643 volume *= soundSystemLocal.dB2Scale( idSoundSystemLocal::s_volume.GetFloat() );
1647 float fadeDb = chan->channelFade.FadeDbAt44kHz( current44kHz );
1648 volume *= soundSystemLocal.dB2Scale( fadeDb );
1650 fadeDb = soundClassFade[parms->soundClass].FadeDbAt44kHz( current44kHz );
1651 volume *= soundSystemLocal.dB2Scale( fadeDb );
1655 // if it's a global sound then
1656 // it's not affected by distance or occlusion
1658 float spatialize = 1;
1659 idVec3 spatializedOriginInMeters;
1663 if ( noOcclusion ) {
1664 // use the real origin and distance
1665 spatializedOriginInMeters = sound->origin * DOOM_TO_METERS;
1666 dlen = sound->realDistance;
1668 // use the possibly portal-occluded origin and distance
1669 spatializedOriginInMeters = sound->spatializedOrigin * DOOM_TO_METERS;
1670 dlen = sound->distance;
1673 // reduce volume based on distance
1674 if ( dlen >= maxd ) {
1676 } else if ( dlen > mind ) {
1677 float frac = idMath::ClampFloat( 0.0f, 1.0f, 1.0f - ((dlen - mind) / (maxd - mind)));
1678 if ( idSoundSystemLocal::s_quadraticFalloff.GetBool() ) {
1682 } else if ( mind > 0.0f ) {
1683 // we tweak the spatialization bias when you are inside the minDistance
1684 spatialize = dlen / mind;
1689 // if it is a private sound, set the volume to zero
1690 // unless we match the listenerId
1692 if ( parms->soundShaderFlags & SSF_PRIVATE_SOUND ) {
1693 if ( sound->listenerId != listenerPrivateId ) {
1697 if ( parms->soundShaderFlags & SSF_ANTI_PRIVATE_SOUND ) {
1698 if ( sound->listenerId == listenerPrivateId ) {
1704 // do we have anything to add?
1706 if ( volume < SND_EPSILON && chan->lastVolume < SND_EPSILON ) {
1709 chan->lastVolume = volume;
1712 // fetch the sound from the cache as 44kHz, 16 bit samples
1714 int offset = current44kHz - chan->trigger44kHzTime;
1715 float inputSamples[MIXBUFFER_SAMPLES*2+16];
1716 float *alignedInputSamples = (float *) ( ( ( (int)inputSamples ) + 15 ) & ~15 );
1719 // allocate and initialize hardware source
1721 if ( idSoundSystemLocal::useOpenAL && sound->removeStatus < REMOVE_STATUS_SAMPLEFINISHED ) {
1722 if ( !alIsSource( chan->openalSource ) ) {
1723 chan->openalSource = soundSystemLocal.AllocOpenALSource( chan, !chan->leadinSample->hardwareBuffer || !chan->soundShader->entries[0]->hardwareBuffer || looping, chan->leadinSample->objectInfo.nChannels == 2 );
1726 if ( alIsSource( chan->openalSource ) ) {
1728 // stop source if needed..
1729 if ( chan->triggered ) {
1730 alSourceStop( chan->openalSource );
1733 // update source parameters
1734 if ( global || omni ) {
1735 alSourcei( chan->openalSource, AL_SOURCE_RELATIVE, AL_TRUE);
1736 alSource3f( chan->openalSource, AL_POSITION, 0.0f, 0.0f, 0.0f );
1737 alSourcef( chan->openalSource, AL_GAIN, ( volume ) < ( 1.0f ) ? ( volume ) : ( 1.0f ) );
1739 alSourcei( chan->openalSource, AL_SOURCE_RELATIVE, AL_FALSE);
1740 alSource3f( chan->openalSource, AL_POSITION, -spatializedOriginInMeters.y, spatializedOriginInMeters.z, -spatializedOriginInMeters.x );
1741 alSourcef( chan->openalSource, AL_GAIN, ( volume ) < ( 1.0f ) ? ( volume ) : ( 1.0f ) );
1743 alSourcei( chan->openalSource, AL_LOOPING, ( looping && chan->soundShader->entries[0]->hardwareBuffer ) ? AL_TRUE : AL_FALSE );
1744 #if !defined(MACOS_X)
1745 alSourcef( chan->openalSource, AL_REFERENCE_DISTANCE, mind );
1746 alSourcef( chan->openalSource, AL_MAX_DISTANCE, maxd );
1748 alSourcef( chan->openalSource, AL_PITCH, ( slowmoActive && !chan->disallowSlow ) ? ( slowmoSpeed ) : ( 1.0f ) );
1750 long lOcclusion = ( enviroSuitActive ? -1150 : 0);
1751 if ( soundSystemLocal.alEAXSet ) {
1752 soundSystemLocal.alEAXSet( &EAXPROPERTYID_EAX_Source, EAXSOURCE_OCCLUSION, chan->openalSource, &lOcclusion, sizeof(lOcclusion) );
1755 if ( ( !looping && chan->leadinSample->hardwareBuffer ) || ( looping && chan->soundShader->entries[0]->hardwareBuffer ) ) {
1756 // handle uncompressed (non streaming) single shot and looping sounds
1757 if ( chan->triggered ) {
1758 alSourcei( chan->openalSource, AL_BUFFER, looping ? chan->soundShader->entries[0]->openalBuffer : chan->leadinSample->openalBuffer );
1761 ALint finishedbuffers;
1764 // handle streaming sounds (decode on the fly) both single shot AND looping
1765 if ( chan->triggered ) {
1766 alSourcei( chan->openalSource, AL_BUFFER, NULL );
1767 alDeleteBuffers( 3, &chan->lastopenalStreamingBuffer[0] );
1768 chan->lastopenalStreamingBuffer[0] = chan->openalStreamingBuffer[0];
1769 chan->lastopenalStreamingBuffer[1] = chan->openalStreamingBuffer[1];
1770 chan->lastopenalStreamingBuffer[2] = chan->openalStreamingBuffer[2];
1771 alGenBuffers( 3, &chan->openalStreamingBuffer[0] );
1772 if ( soundSystemLocal.alEAXSetBufferMode ) {
1773 soundSystemLocal.alEAXSetBufferMode( 3, &chan->openalStreamingBuffer[0], alGetEnumValue( ID_ALCHAR "AL_STORAGE_ACCESSIBLE" ) );
1775 buffers[0] = chan->openalStreamingBuffer[0];
1776 buffers[1] = chan->openalStreamingBuffer[1];
1777 buffers[2] = chan->openalStreamingBuffer[2];
1778 finishedbuffers = 3;
1780 alGetSourcei( chan->openalSource, AL_BUFFERS_PROCESSED, &finishedbuffers );
1781 alSourceUnqueueBuffers( chan->openalSource, finishedbuffers, &buffers[0] );
1782 if ( finishedbuffers == 3 ) {
1783 chan->triggered = true;
1787 for ( j = 0; j < finishedbuffers; j++ ) {
1788 chan->GatherChannelSamples( chan->openalStreamingOffset * sample->objectInfo.nChannels, MIXBUFFER_SAMPLES * sample->objectInfo.nChannels, alignedInputSamples );
1789 for ( int i = 0; i < ( MIXBUFFER_SAMPLES * sample->objectInfo.nChannels ); i++ ) {
1790 if ( alignedInputSamples[i] < -32768.0f )
1791 ((short *)alignedInputSamples)[i] = -32768;
1792 else if ( alignedInputSamples[i] > 32767.0f )
1793 ((short *)alignedInputSamples)[i] = 32767;
1795 ((short *)alignedInputSamples)[i] = idMath::FtoiFast( alignedInputSamples[i] );
1797 alBufferData( buffers[j], chan->leadinSample->objectInfo.nChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, alignedInputSamples, MIXBUFFER_SAMPLES * sample->objectInfo.nChannels * sizeof( short ), 44100 );
1798 chan->openalStreamingOffset += MIXBUFFER_SAMPLES;
1801 if ( finishedbuffers ) {
1802 alSourceQueueBuffers( chan->openalSource, finishedbuffers, &buffers[0] );
1806 // (re)start if needed..
1807 if ( chan->triggered ) {
1808 alSourcePlay( chan->openalSource );
1809 chan->triggered = false;
1814 if ( slowmoActive && !chan->disallowSlow ) {
1815 idSlowChannel slow = sound->GetSlowChannel( chan );
1817 slow.AttachSoundChannel( chan );
1819 if ( sample->objectInfo.nChannels == 2 ) {
1820 // need to add a stereo path, but very few samples go through this
1821 memset( alignedInputSamples, 0, sizeof( alignedInputSamples[0] ) * MIXBUFFER_SAMPLES * 2 );
1823 slow.GatherChannelSamples( offset, MIXBUFFER_SAMPLES, alignedInputSamples );
1826 sound->SetSlowChannel( chan, slow );
1828 sound->ResetSlowChannel( chan );
1830 // if we are getting a stereo sample adjust accordingly
1831 if ( sample->objectInfo.nChannels == 2 ) {
1832 // we should probably check to make sure any looping is also to a stereo sample...
1833 chan->GatherChannelSamples( offset*2, MIXBUFFER_SAMPLES*2, alignedInputSamples );
1835 chan->GatherChannelSamples( offset, MIXBUFFER_SAMPLES, alignedInputSamples );
1840 // work out the left / right ear values
1843 if ( global || omni ) {
1844 // same for all speakers
1845 for ( int i = 0 ; i < 6 ; i++ ) {
1846 ears[i] = idSoundSystemLocal::s_globalFraction.GetFloat() * volume;
1848 ears[3] = idSoundSystemLocal::s_subFraction.GetFloat() * volume; // subwoofer
1851 CalcEars( numSpeakers, spatializedOriginInMeters, listenerPos, listenerAxis, ears, spatialize );
1853 for ( int i = 0 ; i < 6 ; i++ ) {
1858 // if the mask is 0, it really means do every channel
1862 // cleared mask bits set the mix volume to zero
1863 for ( int i = 0 ; i < 6 ; i++ ) {
1864 if ( !(mask & ( 1 << i ) ) ) {
1869 // if sounds are generally normalized, using a mixing volume over 1.0 will
1870 // almost always cause clipping noise. If samples aren't normalized, there
1871 // is a good call to allow overvolumes
1872 if ( idSoundSystemLocal::s_clipVolumes.GetBool() && !( parms->soundShaderFlags & SSF_UNCLAMPED ) ) {
1873 for ( int i = 0 ; i < 6 ; i++ ) {
1874 if ( ears[i] > 1.0f ) {
1880 // if this is the very first mixing block, set the lastV
1881 // to the current volume
1882 if ( current44kHz == chan->trigger44kHzTime ) {
1883 for ( j = 0 ; j < 6 ; j++ ) {
1884 chan->lastV[j] = ears[j];
1888 if ( numSpeakers == 6 ) {
1889 if ( sample->objectInfo.nChannels == 1 ) {
1890 SIMDProcessor->MixSoundSixSpeakerMono( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1892 SIMDProcessor->MixSoundSixSpeakerStereo( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1895 if ( sample->objectInfo.nChannels == 1 ) {
1896 SIMDProcessor->MixSoundTwoSpeakerMono( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1898 SIMDProcessor->MixSoundTwoSpeakerStereo( finalMixBuffer, alignedInputSamples, MIXBUFFER_SAMPLES, chan->lastV, ears );
1902 for ( j = 0 ; j < 6 ; j++ ) {
1903 chan->lastV[j] = ears[j];
1908 soundSystemLocal.soundStats.activeSounds++;
1914 idSoundWorldLocal::FindAmplitude
1916 this is called from the main thread
1918 if listenerPosition is NULL, this is being used for shader parameters,
1919 like flashing lights and glows based on sound level. Otherwise, it is being used for
1920 the screen-shake on a player.
1922 This doesn't do the portal-occlusion currently, because it would have to reset all the defs
1923 which would be problematic in multiplayer
1926 float idSoundWorldLocal::FindAmplitude( idSoundEmitterLocal *sound, const int localTime, const idVec3 *listenerPosition,
1927 const s_channelType channel, bool shakesOnly ) {
1929 soundShaderParms_t *parms;
1931 int activeChannelCount;
1932 static const int AMPLITUDE_SAMPLES = MIXBUFFER_SAMPLES/8;
1933 float sourceBuffer[AMPLITUDE_SAMPLES];
1934 float sumBuffer[AMPLITUDE_SAMPLES];
1935 // work out the distance from the listener to the emitter
1938 if ( !sound->playing ) {
1942 if ( listenerPosition ) {
1943 // this doesn't do the portal spatialization
1944 idVec3 dist = sound->origin - *listenerPosition;
1945 dlen = dist.Length();
1946 dlen *= DOOM_TO_METERS;
1951 activeChannelCount = 0;
1953 for ( i = 0; i < SOUND_MAX_CHANNELS ; i++ ) {
1954 idSoundChannel *chan = &sound->channels[ i ];
1956 if ( !chan->triggerState ) {
1960 if ( channel != SCHANNEL_ANY && chan->triggerChannel != channel) {
1964 parms = &chan->parms;
1966 int localTriggerTimes = chan->trigger44kHzTime;
1968 bool looping = ( parms->soundShaderFlags & SSF_LOOPING ) != 0;
1970 // check for screen shakes
1971 float shakes = parms->shakes;
1972 if ( shakesOnly && shakes <= 0.0f ) {
1979 if ( !listenerPosition ) {
1980 // just look at the raw wav data for light shader evaluation
1983 volume = parms->volume;
1984 volume = soundSystemLocal.dB2Scale( volume );
1989 if ( listenerPosition && !( parms->soundShaderFlags & SSF_GLOBAL ) ) {
1990 // check for overrides
1991 float maxd = parms->maxDistance;
1992 float mind = parms->minDistance;
1994 if ( dlen >= maxd ) {
1996 } else if ( dlen > mind ) {
1997 float frac = idMath::ClampFloat( 0, 1, 1.0f - ((dlen - mind) / (maxd - mind)));
1998 if ( idSoundSystemLocal::s_quadraticFalloff.GetBool() ) {
2006 if ( volume <= 0 ) {
2011 // fetch the sound from the cache
2012 // this doesn't handle stereo samples correctly...
2014 if ( !listenerPosition && chan->parms.soundShaderFlags & SSF_NO_FLICKER ) {
2015 // the NO_FLICKER option is to allow a light to still play a sound, but
2016 // not have it effect the intensity
2017 for ( j = 0 ; j < (AMPLITUDE_SAMPLES); j++ ) {
2018 sourceBuffer[j] = j & 1 ? 32767.0f : -32767.0f;
2021 int offset = (localTime - localTriggerTimes); // offset in samples
2022 int size = ( looping ? chan->soundShader->entries[0]->LengthIn44kHzSamples() : chan->leadinSample->LengthIn44kHzSamples() );
2023 short *amplitudeData = (short *)( looping ? chan->soundShader->entries[0]->amplitudeData : chan->leadinSample->amplitudeData );
2025 if ( amplitudeData ) {
2026 // when the amplitudeData is present use that fill a dummy sourceBuffer
2027 // this is to allow for amplitude based effect on hardware audio solutions
2028 if ( looping ) offset %= size;
2029 if ( offset < size ) {
2030 for ( j = 0 ; j < (AMPLITUDE_SAMPLES); j++ ) {
2031 sourceBuffer[j] = j & 1 ? amplitudeData[ ( offset / 512 ) * 2 ] : amplitudeData[ ( offset / 512 ) * 2 + 1 ];
2035 // get actual sample data
2036 chan->GatherChannelSamples( offset, AMPLITUDE_SAMPLES, sourceBuffer );
2039 activeChannelCount++;
2040 if ( activeChannelCount == 1 ) {
2041 // store to the buffer
2042 for( j = 0; j < AMPLITUDE_SAMPLES; j++ ) {
2043 sumBuffer[ j ] = volume * sourceBuffer[ j ];
2046 // add to the buffer
2047 for( j = 0; j < AMPLITUDE_SAMPLES; j++ ) {
2048 sumBuffer[ j ] += volume * sourceBuffer[ j ];
2053 if ( activeChannelCount == 0 ) {
2057 float high = -32767.0f;
2058 float low = 32767.0f;
2060 // use a 20th of a second
2061 for( i = 0; i < (AMPLITUDE_SAMPLES); i++ ) {
2062 float fabval = sumBuffer[i];
2063 if ( high < fabval ) {
2066 if ( low > fabval ) {
2072 sout = atan( (high - low) / 32767.0f) / DEG2RAD(45);
2079 idSoundWorldLocal::FadeSoundClasses
2081 fade all sounds in the world with a given shader soundClass
2082 to is in Db (sigh), over is in seconds
2085 void idSoundWorldLocal::FadeSoundClasses( const int soundClass, const float to, const float over ) {
2086 if ( soundClass < 0 || soundClass >= SOUND_MAX_CLASSES ) {
2087 common->Error( "idSoundWorldLocal::FadeSoundClasses: bad soundClass %i", soundClass );
2090 idSoundFade *fade = &soundClassFade[ soundClass ];
2092 int length44kHz = soundSystemLocal.MillisecondsToSamples( over * 1000 );
2094 // if it is already fading to this volume at this rate, don't change it
2095 if ( fade->fadeEndVolume == to &&
2096 fade->fadeEnd44kHz - fade->fadeStart44kHz == length44kHz ) {
2103 // if we are recording an AVI demo, don't use hardware time
2104 start44kHz = lastAVI44kHz + MIXBUFFER_SAMPLES;
2106 start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
2110 fade->fadeStartVolume = fade->FadeDbAt44kHz( start44kHz );
2111 fade->fadeStart44kHz = start44kHz;
2112 fade->fadeEnd44kHz = start44kHz + length44kHz;
2113 fade->fadeEndVolume = to;
2118 idSoundWorldLocal::SetSlowmo
2121 void idSoundWorldLocal::SetSlowmo( bool active ) {
2122 slowmoActive = active;
2127 idSoundWorldLocal::SetSlowmoSpeed
2130 void idSoundWorldLocal::SetSlowmoSpeed( float speed ) {
2131 slowmoSpeed = speed;
2136 idSoundWorldLocal::SetEnviroSuit
2139 void idSoundWorldLocal::SetEnviroSuit( bool active ) {
2140 enviroSuitActive = active;