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"
40 void idSoundFade::Clear() {
49 idSoundFade::FadeDbAt44kHz
52 float idSoundFade::FadeDbAt44kHz( int current44kHz ) {
55 if ( current44kHz >= fadeEnd44kHz ) {
56 fadeDb = fadeEndVolume;
57 } else if ( current44kHz > fadeStart44kHz ) {
58 float fraction = ( fadeEnd44kHz - fadeStart44kHz );
59 float over = ( current44kHz - fadeStart44kHz );
60 fadeDb = fadeStartVolume + ( fadeEndVolume - fadeStartVolume ) * over / fraction;
62 fadeDb = fadeStartVolume;
67 //========================================================================
71 =======================
74 Fills in elements[0] .. elements[numElements-1] with a permutation of
75 0 .. numElements-1 based on the permute parameter
85 =======================
87 void PermuteList_r( int *list, int listLength, int permute, int maxPermute ) {
88 if ( listLength < 2 ) {
91 permute %= maxPermute;
92 int swap = permute * listLength / maxPermute;
97 maxPermute /= listLength;
98 PermuteList_r( list + 1, listLength - 1, permute, maxPermute );
101 int Factorial( int val ) {
110 void GeneratePermutedList( int *list, int listLength, int permute ) {
111 for ( int i = 0 ; i < listLength ; i++ ) {
115 // we can't calculate > 12 factorial, so we can't easily build a permuted list
116 if ( listLength > 12 ) {
120 // calculate listLength factorial
121 int maxPermute = Factorial( listLength );
123 // recursively permute
124 PermuteList_r( list, listLength, permute, maxPermute );
127 void TestPermutations( void ) {
128 int list[SOUND_MAX_LIST_WAVS];
130 for ( int len = 1 ; len < 5 ; len++ ) {
131 common->Printf( "list length: %i\n", len );
133 int max = Factorial( len );
134 for ( int j = 0 ; j < max * 2 ; j++ ) {
135 GeneratePermutedList( list, len, j );
136 common->Printf( "%4i : ", j );
137 for ( int k = 0 ; k < len ; k++ ) {
138 common->Printf( "%i", list[k] );
140 common->Printf( "\n" );
145 //=====================================================================================
149 idSoundChannel::idSoundChannel
152 idSoundChannel::idSoundChannel( void ) {
159 idSoundChannel::~idSoundChannel
162 idSoundChannel::~idSoundChannel( void ) {
168 idSoundChannel::Clear
171 void idSoundChannel::Clear( void ) {
177 triggerChannel = SCHANNEL_ANY;
181 trigger44kHzTime = 0;
182 for( j = 0; j < 6; j++ ) {
185 memset( &parms, 0, sizeof(parms) );
189 openalStreamingOffset = 0;
190 openalStreamingBuffer[0] = openalStreamingBuffer[1] = openalStreamingBuffer[2] = 0;
191 lastopenalStreamingBuffer[0] = lastopenalStreamingBuffer[1] = lastopenalStreamingBuffer[2] = 0;
196 idSoundChannel::Start
199 void idSoundChannel::Start( void ) {
201 if ( decoder == NULL ) {
202 decoder = idSampleDecoder::Alloc();
211 void idSoundChannel::Stop( void ) {
212 triggerState = false;
213 if ( decoder != NULL ) {
214 idSampleDecoder::Free( decoder );
221 idSoundChannel::ALStop
224 void idSoundChannel::ALStop( void ) {
225 if ( idSoundSystemLocal::useOpenAL ) {
227 if ( alIsSource( openalSource ) ) {
228 alSourceStop( openalSource );
229 alSourcei( openalSource, AL_BUFFER, 0 );
230 soundSystemLocal.FreeOpenALSource( openalSource );
233 if ( openalStreamingBuffer[0] && openalStreamingBuffer[1] && openalStreamingBuffer[2] ) {
235 alDeleteBuffers( 3, &openalStreamingBuffer[0] );
236 if ( alGetError() == AL_NO_ERROR ) {
237 openalStreamingBuffer[0] = openalStreamingBuffer[1] = openalStreamingBuffer[2] = 0;
241 if ( lastopenalStreamingBuffer[0] && lastopenalStreamingBuffer[1] && lastopenalStreamingBuffer[2] ) {
243 alDeleteBuffers( 3, &lastopenalStreamingBuffer[0] );
244 if ( alGetError() == AL_NO_ERROR ) {
245 lastopenalStreamingBuffer[0] = lastopenalStreamingBuffer[1] = lastopenalStreamingBuffer[2] = 0;
253 idSoundChannel::GatherChannelSamples
255 Will always return 44kHz samples for the given range, even if it deeply looped or
256 out of the range of the unlooped samples. Handles looping between multiple different
260 void idSoundChannel::GatherChannelSamples( int sampleOffset44k, int sampleCount44k, float *dest ) const {
261 float *dest_p = dest;
264 //Sys_DebugPrintf( "msec:%i sample:%i : %i : %i\n", Sys_Milliseconds(), soundSystemLocal.GetCurrent44kHzTime(), sampleOffset44k, sampleCount44k ); //!@#
266 // negative offset times will just zero fill
267 if ( sampleOffset44k < 0 ) {
268 len = -sampleOffset44k;
269 if ( len > sampleCount44k ) {
270 len = sampleCount44k;
272 memset( dest_p, 0, len * sizeof( dest_p[0] ) );
274 sampleCount44k -= len;
275 sampleOffset44k += len;
278 // grab part of the leadin sample
279 idSoundSample *leadin = leadinSample;
280 if ( !leadin || sampleOffset44k < 0 || sampleCount44k <= 0 ) {
281 memset( dest_p, 0, sampleCount44k * sizeof( dest_p[0] ) );
285 if ( sampleOffset44k < leadin->LengthIn44kHzSamples() ) {
286 len = leadin->LengthIn44kHzSamples() - sampleOffset44k;
287 if ( len > sampleCount44k ) {
288 len = sampleCount44k;
292 decoder->Decode( leadin, sampleOffset44k, len, dest_p );
295 sampleCount44k -= len;
296 sampleOffset44k += len;
299 // if not looping, zero fill any remaining spots
300 if ( !soundShader || !( parms.soundShaderFlags & SSF_LOOPING ) ) {
301 memset( dest_p, 0, sampleCount44k * sizeof( dest_p[0] ) );
305 // fill the remainder with looped samples
306 idSoundSample *loop = soundShader->entries[0];
309 memset( dest_p, 0, sampleCount44k * sizeof( dest_p[0] ) );
313 sampleOffset44k -= leadin->LengthIn44kHzSamples();
315 while( sampleCount44k > 0 ) {
316 int totalLen = loop->LengthIn44kHzSamples();
318 sampleOffset44k %= totalLen;
320 len = totalLen - sampleOffset44k;
321 if ( len > sampleCount44k ) {
322 len = sampleCount44k;
326 decoder->Decode( loop, sampleOffset44k, len, dest_p );
329 sampleCount44k -= len;
330 sampleOffset44k += len;
335 //=====================================================================================
339 idSoundEmitterLocal::idSoundEmitterLocal
343 idSoundEmitterLocal::idSoundEmitterLocal( void ) {
350 idSoundEmitterLocal::~idSoundEmitterLocal
353 idSoundEmitterLocal::~idSoundEmitterLocal( void ) {
359 idSoundEmitterLocal::Clear
362 void idSoundEmitterLocal::Clear( void ) {
365 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
366 channels[i].ALStop();
370 removeStatus = REMOVE_STATUS_SAMPLEFINISHED;
373 lastValidPortalArea = -1;
377 ampTime = 0; // last time someone queried
379 maxDistance = 10.0f; // meters
380 spatializedOrigin.Zero();
382 memset( &parms, 0, sizeof( parms ) );
387 idSoundEmitterLocal::OverrideParms
390 void idSoundEmitterLocal::OverrideParms( const soundShaderParms_t *base,
391 const soundShaderParms_t *over, soundShaderParms_t *out ) {
396 if ( over->minDistance ) {
397 out->minDistance = over->minDistance;
399 out->minDistance = base->minDistance;
401 if ( over->maxDistance ) {
402 out->maxDistance = over->maxDistance;
404 out->maxDistance = base->maxDistance;
406 if ( over->shakes ) {
407 out->shakes = over->shakes;
409 out->shakes = base->shakes;
411 if ( over->volume ) {
412 out->volume = over->volume;
414 out->volume = base->volume;
416 if ( over->soundClass ) {
417 out->soundClass = over->soundClass;
419 out->soundClass = base->soundClass;
421 out->soundShaderFlags = base->soundShaderFlags | over->soundShaderFlags;
426 idSoundEmitterLocal::CheckForCompletion
428 Checks to see if all the channels have completed, clearing the playing flag if necessary.
429 Sets the playing and shakes bools.
432 void idSoundEmitterLocal::CheckForCompletion( int current44kHzTime ) {
440 for ( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
441 idSoundChannel *chan = &channels[i];
443 if ( !chan->triggerState ) {
446 const idSoundShader *shader = chan->soundShader;
451 // see if this channel has completed
452 if ( !( chan->parms.soundShaderFlags & SSF_LOOPING ) ) {
453 ALint state = AL_PLAYING;
455 if ( idSoundSystemLocal::useOpenAL && alIsSource( chan->openalSource ) ) {
456 alGetSourcei( chan->openalSource, AL_SOURCE_STATE, &state );
458 idSlowChannel slow = GetSlowChannel( chan );
460 if ( soundWorld->slowmoActive && slow.IsActive() ) {
461 if ( slow.GetCurrentPosition().time >= chan->leadinSample->LengthIn44kHzSamples() / 2 ) {
463 // if this was an onDemand sound, purge the sample now
464 if ( chan->leadinSample->onDemand ) {
465 chan->leadinSample->PurgeSoundSample();
469 } else if ( ( chan->trigger44kHzTime + chan->leadinSample->LengthIn44kHzSamples() < current44kHzTime ) || ( state == AL_STOPPED ) ) {
472 // free hardware resources
475 // if this was an onDemand sound, purge the sample now
476 if ( chan->leadinSample->onDemand ) {
477 chan->leadinSample->PurgeSoundSample();
483 // free decoder memory if no sound was decoded for a while
484 if ( chan->decoder != NULL && chan->decoder->GetLastDecodeTime() < current44kHzTime - SOUND_DECODER_FREE_DELAY ) {
485 chan->decoder->ClearDecoder();
490 if ( chan->parms.shakes > 0.0f ) {
496 // mark the entire sound emitter as non-playing if there aren't any active channels
499 if ( removeStatus == REMOVE_STATUS_WAITSAMPLEFINISHED ) {
500 // this can now be reused by the next request for a new soundEmitter
501 removeStatus = REMOVE_STATUS_SAMPLEFINISHED;
508 idSoundEmitterLocal::Spatialize
510 Called once each sound frame by the main thread from idSoundWorldLocal::PlaceOrigin
513 void idSoundEmitterLocal::Spatialize( idVec3 listenerPos, int listenerArea, idRenderWorld *rw ) {
515 bool hasActive = false;
518 // work out the maximum distance of all the playing channels
522 for ( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
523 idSoundChannel *chan = &channels[i];
525 if ( !chan->triggerState ) {
528 if ( chan->parms.maxDistance > maxDistance ) {
529 maxDistance = chan->parms.maxDistance;
534 // work out where the sound comes from
536 idVec3 realOrigin = origin * DOOM_TO_METERS;
537 idVec3 len = listenerPos - realOrigin;
538 realDistance = len.LengthFast();
540 if ( realDistance >= maxDistance ) {
541 // no way to possibly hear it
542 distance = realDistance;
547 // work out virtual origin and distance, which may be from a portal instead of the actual origin
549 distance = maxDistance * METERS_TO_DOOM;
550 if ( listenerArea == -1 ) { // listener is outside the world
554 // we have a valid renderWorld
555 int soundInArea = rw->PointInArea( origin );
556 if ( soundInArea == -1 ) {
557 if ( lastValidPortalArea == -1 ) { // sound is outside the world
558 distance = realDistance;
559 spatializedOrigin = origin; // sound is in our area
562 soundInArea = lastValidPortalArea;
564 lastValidPortalArea = soundInArea;
565 if ( soundInArea == listenerArea ) {
566 distance = realDistance;
567 spatializedOrigin = origin; // sound is in our area
571 soundWorld->ResolveOrigin( 0, NULL, soundInArea, 0.0f, origin, this );
572 distance /= METERS_TO_DOOM;
574 // no portals available
575 distance = realDistance;
576 spatializedOrigin = origin; // sound is in our area
581 ===========================================================================================
585 ===========================================================================================
589 =====================
590 idSoundEmitterLocal::UpdateEmitter
591 =====================
593 void idSoundEmitterLocal::UpdateEmitter( const idVec3 &origin, int listenerId, const soundShaderParms_t *parms ) {
595 common->Error( "idSoundEmitterLocal::UpdateEmitter: NULL parms" );
597 if ( soundWorld && soundWorld->writeDemo ) {
598 soundWorld->writeDemo->WriteInt( DS_SOUND );
599 soundWorld->writeDemo->WriteInt( SCMD_UPDATE );
600 soundWorld->writeDemo->WriteInt( index );
601 soundWorld->writeDemo->WriteVec3( origin );
602 soundWorld->writeDemo->WriteInt( listenerId );
603 soundWorld->writeDemo->WriteFloat( parms->minDistance );
604 soundWorld->writeDemo->WriteFloat( parms->maxDistance );
605 soundWorld->writeDemo->WriteFloat( parms->volume );
606 soundWorld->writeDemo->WriteFloat( parms->shakes );
607 soundWorld->writeDemo->WriteInt( parms->soundShaderFlags );
608 soundWorld->writeDemo->WriteInt( parms->soundClass );
611 this->origin = origin;
612 this->listenerId = listenerId;
613 this->parms = *parms;
615 // FIXME: change values on all channels?
619 =====================
620 idSoundEmitterLocal::Free
622 They are never truly freed, just marked so they can be reused by the soundWorld
623 =====================
625 void idSoundEmitterLocal::Free( bool immediate ) {
626 if ( removeStatus != REMOVE_STATUS_ALIVE ) {
630 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
631 common->Printf( "FreeSound (%i,%i)\n", index, (int)immediate );
633 if ( soundWorld && soundWorld->writeDemo ) {
634 soundWorld->writeDemo->WriteInt( DS_SOUND );
635 soundWorld->writeDemo->WriteInt( SCMD_FREE );
636 soundWorld->writeDemo->WriteInt( index );
637 soundWorld->writeDemo->WriteInt( immediate );
641 removeStatus = REMOVE_STATUS_WAITSAMPLEFINISHED;
648 =====================
649 idSoundEmitterLocal::StartSound
651 returns the length of the started sound in msec
652 =====================
654 int idSoundEmitterLocal::StartSound( const idSoundShader *shader, const s_channelType channel, float diversity, int soundShaderFlags, bool allowSlow ) {
661 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
662 common->Printf( "StartSound %ims (%i,%i,%s) = ", soundWorld->gameMsec, index, (int)channel, shader->GetName() );
665 if ( soundWorld && soundWorld->writeDemo ) {
666 soundWorld->writeDemo->WriteInt( DS_SOUND );
667 soundWorld->writeDemo->WriteInt( SCMD_START );
668 soundWorld->writeDemo->WriteInt( index );
670 soundWorld->writeDemo->WriteHashString( shader->GetName() );
672 soundWorld->writeDemo->WriteInt( channel );
673 soundWorld->writeDemo->WriteFloat( diversity );
674 soundWorld->writeDemo->WriteInt( soundShaderFlags );
677 // build the channel parameters by taking the shader parms and optionally overriding
678 soundShaderParms_t chanParms;
680 chanParms = shader->parms;
681 OverrideParms( &chanParms, &this->parms, &chanParms );
682 chanParms.soundShaderFlags |= soundShaderFlags;
684 if ( chanParms.shakes > 0.0f ) {
685 shader->CheckShakesAndOgg();
688 // this is the sample time it will be first mixed
691 if ( soundWorld->fpa[0] ) {
692 // if we are recording an AVI demo, don't use hardware time
693 start44kHz = soundWorld->lastAVI44kHz + MIXBUFFER_SAMPLES;
695 start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
699 // pick which sound to play from the shader
701 if ( !shader->numEntries ) {
702 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
703 common->Printf( "no samples in sound shader\n" );
705 return 0; // no sounds
709 // pick a sound from the list based on the passed diversity
710 choice = (int)(diversity * shader->numEntries);
711 if ( choice < 0 || choice >= shader->numEntries ) {
715 // bump the choice if the exact sound was just played and we are NO_DUPS
716 if ( chanParms.soundShaderFlags & SSF_NO_DUPS ) {
717 idSoundSample *sample;
718 if ( shader->leadins[ choice ] ) {
719 sample = shader->leadins[ choice ];
721 sample = shader->entries[ choice ];
723 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
724 idSoundChannel *chan = &channels[i];
725 if ( chan->leadinSample == sample ) {
726 choice = ( choice + 1 ) % shader->numEntries;
732 // PLAY_ONCE sounds will never be restarted while they are running
733 if ( chanParms.soundShaderFlags & SSF_PLAY_ONCE ) {
734 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
735 idSoundChannel *chan = &channels[i];
736 if ( chan->triggerState && chan->soundShader == shader ) {
737 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
738 common->Printf( "PLAY_ONCE not restarting\n" );
745 // never play the same sound twice with the same starting time, even
746 // if they are on different channels
747 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
748 idSoundChannel *chan = &channels[i];
749 if ( chan->triggerState && chan->soundShader == shader && chan->trigger44kHzTime == start44kHz ) {
750 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
751 common->Printf( "already started this frame\n" );
757 Sys_EnterCriticalSection();
759 // kill any sound that is currently playing on this channel
760 if ( channel != SCHANNEL_ANY ) {
761 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
762 idSoundChannel *chan = &channels[i];
763 if ( chan->triggerState && chan->soundShader && chan->triggerChannel == channel ) {
764 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
765 common->Printf( "(override %s)", chan->soundShader->base->GetName() );
770 // if this was an onDemand sound, purge the sample now
771 if ( chan->leadinSample->onDemand ) {
773 chan->leadinSample->PurgeSoundSample();
780 // find a free channel to play the sound on
781 idSoundChannel *chan;
782 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
784 if ( !chan->triggerState ) {
789 if ( i == SOUND_MAX_CHANNELS ) {
790 // we couldn't find a channel for it
791 Sys_LeaveCriticalSection();
792 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
793 common->Printf( "no channels available\n" );
800 if ( shader->leadins[ choice ] ) {
801 chan->leadinSample = shader->leadins[ choice ];
803 chan->leadinSample = shader->entries[ choice ];
806 // if the sample is onDemand (voice mails, etc), load it now
807 if ( chan->leadinSample->purged ) {
808 int start = Sys_Milliseconds();
809 chan->leadinSample->Load();
810 int end = Sys_Milliseconds();
811 session->TimeHitch( end - start );
812 // recalculate start44kHz, because loading may have taken a fair amount of time
813 if ( !soundWorld->fpa[0] ) {
814 start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
818 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
819 common->Printf( "'%s'\n", chan->leadinSample->name.c_str() );
822 if ( idSoundSystemLocal::s_skipHelltimeFX.GetBool() ) {
823 chan->disallowSlow = true;
825 chan->disallowSlow = !allowSlow;
828 ResetSlowChannel( chan );
830 // the sound will start mixing in the next async mix block
831 chan->triggered = true;
832 chan->openalStreamingOffset = 0;
833 chan->trigger44kHzTime = start44kHz;
834 chan->parms = chanParms;
835 chan->triggerGame44kHzTime = soundWorld->game44kHz;
836 chan->soundShader = shader;
837 chan->triggerChannel = channel;
840 // we need to start updating the def and mixing it in
843 // spatialize it immediately, so it will start the next mix block
844 // even if that happens before the next PlaceOrigin()
845 Spatialize( soundWorld->listenerPos, soundWorld->listenerArea, soundWorld->rw );
847 // return length of sound in milliseconds
848 int length = chan->leadinSample->LengthIn44kHzSamples();
850 if ( chan->leadinSample->objectInfo.nChannels == 2 ) {
851 length /= 2; // stereo samples
854 // adjust the start time based on diversity for looping sounds, so they don't all start
856 if ( chan->parms.soundShaderFlags & SSF_LOOPING && !chan->leadinSample->LengthIn44kHzSamples() ) {
857 chan->trigger44kHzTime -= diversity * length;
858 chan->trigger44kHzTime &= ~7; // so we don't have to worry about the 22kHz and 11kHz expansions
859 // starting in fractional samples
860 chan->triggerGame44kHzTime -= diversity * length;
861 chan->triggerGame44kHzTime &= ~7;
864 length *= 1000 / (float)PRIMARYFREQ;
866 Sys_LeaveCriticalSection();
873 idSoundEmitterLocal::ModifySound
876 void idSoundEmitterLocal::ModifySound( const s_channelType channel, const soundShaderParms_t *parms ) {
878 common->Error( "idSoundEmitterLocal::ModifySound: NULL parms" );
880 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
881 common->Printf( "ModifySound(%i,%i)\n", index, channel );
883 if ( soundWorld && soundWorld->writeDemo ) {
884 soundWorld->writeDemo->WriteInt( DS_SOUND );
885 soundWorld->writeDemo->WriteInt( SCMD_MODIFY );
886 soundWorld->writeDemo->WriteInt( index );
887 soundWorld->writeDemo->WriteInt( channel );
888 soundWorld->writeDemo->WriteFloat( parms->minDistance );
889 soundWorld->writeDemo->WriteFloat( parms->maxDistance );
890 soundWorld->writeDemo->WriteFloat( parms->volume );
891 soundWorld->writeDemo->WriteFloat( parms->shakes );
892 soundWorld->writeDemo->WriteInt( parms->soundShaderFlags );
893 soundWorld->writeDemo->WriteInt( parms->soundClass );
896 for ( int i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
897 idSoundChannel *chan = &channels[i];
899 if ( !chan->triggerState ) {
902 if ( channel != SCHANNEL_ANY && chan->triggerChannel != channel ) {
906 OverrideParms( &chan->parms, parms, &chan->parms );
908 if ( chan->parms.shakes > 0.0f && chan->soundShader != NULL ) {
909 chan->soundShader->CheckShakesAndOgg();
916 idSoundEmitterLocal::StopSound
918 can pass SCHANNEL_ANY
921 void idSoundEmitterLocal::StopSound( const s_channelType channel ) {
924 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
925 common->Printf( "StopSound(%i,%i)\n", index, channel );
928 if ( soundWorld && soundWorld->writeDemo ) {
929 soundWorld->writeDemo->WriteInt( DS_SOUND );
930 soundWorld->writeDemo->WriteInt( SCMD_STOP );
931 soundWorld->writeDemo->WriteInt( index );
932 soundWorld->writeDemo->WriteInt( channel );
935 Sys_EnterCriticalSection();
937 for( i = 0; i < SOUND_MAX_CHANNELS; i++ ) {
938 idSoundChannel *chan = &channels[i];
940 if ( !chan->triggerState ) {
943 if ( channel != SCHANNEL_ANY && chan->triggerChannel != channel ) {
950 // free hardware resources
953 // if this was an onDemand sound, purge the sample now
954 if ( chan->leadinSample->onDemand ) {
955 chan->leadinSample->PurgeSoundSample();
958 chan->leadinSample = NULL;
959 chan->soundShader = NULL;
962 Sys_LeaveCriticalSection();
967 idSoundEmitterLocal::FadeSound
969 to is in Db (sigh), over is in seconds
972 void idSoundEmitterLocal::FadeSound( const s_channelType channel, float to, float over ) {
973 if ( idSoundSystemLocal::s_showStartSound.GetInteger() ) {
974 common->Printf( "FadeSound(%i,%i,%f,%f )\n", index, channel, to, over );
979 if ( soundWorld->writeDemo ) {
980 soundWorld->writeDemo->WriteInt( DS_SOUND );
981 soundWorld->writeDemo->WriteInt( SCMD_FADE );
982 soundWorld->writeDemo->WriteInt( index );
983 soundWorld->writeDemo->WriteInt( channel );
984 soundWorld->writeDemo->WriteFloat( to );
985 soundWorld->writeDemo->WriteFloat( over );
990 if ( soundWorld->fpa[0] ) {
991 // if we are recording an AVI demo, don't use hardware time
992 start44kHz = soundWorld->lastAVI44kHz + MIXBUFFER_SAMPLES;
994 start44kHz = soundSystemLocal.GetCurrent44kHzTime() + MIXBUFFER_SAMPLES;
997 int length44kHz = soundSystemLocal.MillisecondsToSamples( over * 1000 );
999 for( int i = 0; i < SOUND_MAX_CHANNELS ; i++ ) {
1000 idSoundChannel *chan = &channels[i];
1002 if ( !chan->triggerState ) {
1005 if ( channel != SCHANNEL_ANY && chan->triggerChannel != channel ) {
1009 // if it is already fading to this volume at this rate, don't change it
1010 if ( chan->channelFade.fadeEndVolume == to &&
1011 chan->channelFade.fadeEnd44kHz - chan->channelFade.fadeStart44kHz == length44kHz ) {
1016 chan->channelFade.fadeStartVolume = chan->channelFade.FadeDbAt44kHz( start44kHz );
1017 chan->channelFade.fadeStart44kHz = start44kHz;
1018 chan->channelFade.fadeEnd44kHz = start44kHz + length44kHz;
1019 chan->channelFade.fadeEndVolume = to;
1025 idSoundEmitterLocal::CurrentlyPlaying
1028 bool idSoundEmitterLocal::CurrentlyPlaying( void ) const {
1034 idSoundEmitterLocal::Index
1037 int idSoundEmitterLocal::Index( void ) const {
1043 idSoundEmitterLocal::CurrentAmplitude
1045 this is called from the main thread by the material shader system
1046 to allow lights and surface flares to vary with the sound amplitude
1049 float idSoundEmitterLocal::CurrentAmplitude( void ) {
1050 if ( idSoundSystemLocal::s_constantAmplitude.GetFloat() >= 0.0f ) {
1051 return idSoundSystemLocal::s_constantAmplitude.GetFloat();
1054 if ( removeStatus > REMOVE_STATUS_WAITSAMPLEFINISHED ) {
1058 int localTime = soundSystemLocal.GetCurrent44kHzTime();
1060 // see if we can use our cached value
1061 if ( ampTime == localTime ) {
1065 // calculate a new value
1066 ampTime = localTime;
1067 amplitude = soundWorld->FindAmplitude( this, localTime, NULL, SCHANNEL_ANY, false );
1074 idSoundEmitterLocal::GetSlowChannel
1077 idSlowChannel idSoundEmitterLocal::GetSlowChannel( const idSoundChannel *chan ) {
1078 return slowChannels[chan - channels];
1083 idSoundEmitterLocal::SetSlowChannel
1086 void idSoundEmitterLocal::SetSlowChannel( const idSoundChannel *chan, idSlowChannel slow ) {
1087 slowChannels[chan - channels] = slow;
1092 idSoundEmitterLocal::ResetSlowChannel
1095 void idSoundEmitterLocal::ResetSlowChannel( const idSoundChannel *chan ) {
1096 int index = chan - channels;
1097 slowChannels[index].Reset();
1102 idSlowChannel::Reset
1105 void idSlowChannel::Reset() {
1106 memset( this, 0, sizeof( *this ) );
1110 curPosition.Set( 0 );
1111 newPosition.Set( 0 );
1113 curSampleOffset = -10000;
1114 newSampleOffset = -10000;
1121 idSlowChannel::AttachSoundChannel
1124 void idSlowChannel::AttachSoundChannel( const idSoundChannel *chan ) {
1130 idSlowChannel::GetSlowmoSpeed
1133 float idSlowChannel::GetSlowmoSpeed() {
1134 idSoundWorldLocal *sw = static_cast<idSoundWorldLocal*>( soundSystemLocal.GetPlayingSoundWorld() );
1137 return sw->slowmoSpeed;
1145 idSlowChannel::GenerateSlowChannel
1148 void idSlowChannel::GenerateSlowChannel( FracTime& playPos, int sampleCount44k, float* finalBuffer ) {
1149 idSoundWorldLocal *sw = static_cast<idSoundWorldLocal*>( soundSystemLocal.GetPlayingSoundWorld() );
1150 float in[MIXBUFFER_SAMPLES+3], out[MIXBUFFER_SAMPLES+3], *src, *spline, slowmoSpeed;
1151 int i, neededSamples, orgTime, zeroedPos, count = 0;
1157 slowmoSpeed = sw->slowmoSpeed;
1163 neededSamples = sampleCount44k * slowmoSpeed + 4;
1164 orgTime = playPos.time;
1166 // get the channel's samples
1167 chan->GatherChannelSamples( playPos.time * 2, neededSamples, src );
1168 for ( i = 0; i < neededSamples >> 1; i++ ) {
1169 spline[i] = src[i*2];
1172 // interpolate channel
1173 zeroedPos = playPos.time;
1176 for ( i = 0; i < sampleCount44k >> 1; i++, count += 2 ) {
1178 val = spline[playPos.time];
1180 playPos.Increment( slowmoSpeed );
1184 float *in_p = in + 2, *out_p = out + 2;
1185 int numSamples = sampleCount44k >> 1;
1187 lowpass.GetContinuitySamples( in_p[-1], in_p[-2], out_p[-1], out_p[-2] );
1188 lowpass.SetParms( slowmoSpeed * 15000, 1.2f );
1190 for ( int i = 0, count = 0; i < numSamples; i++, count += 2 ) {
1191 lowpass.ProcessSample( in_p + i, out_p + i );
1192 finalBuffer[count] = finalBuffer[count+1] = out[i];
1195 lowpass.SetContinuitySamples( in_p[numSamples-2], in_p[numSamples-3], out_p[numSamples-2], out_p[numSamples-3] );
1197 playPos.time += zeroedPos;
1202 idSlowChannel::GatherChannelSamples
1205 void idSlowChannel::GatherChannelSamples( int sampleOffset44k, int sampleCount44k, float *dest ) {
1210 newSampleOffset = sampleOffset44k >> 1;
1213 if ( newSampleOffset < curSampleOffset ) {
1214 state = PLAYBACK_RESET;
1215 } else if ( newSampleOffset > curSampleOffset ) {
1216 state = PLAYBACK_ADVANCING;
1219 if ( state == PLAYBACK_RESET ) {
1220 curPosition.Set( newSampleOffset );
1224 curSampleOffset = newSampleOffset;
1225 newPosition = curPosition;
1227 // do the slow processing
1228 GenerateSlowChannel( newPosition, sampleCount44k, dest );
1231 if ( state == PLAYBACK_ADVANCING )
1232 curPosition = newPosition;