Torque3D Documentation / _generateds / sfxSoundscape.cpp

sfxSoundscape.cpp

Engine/source/sfx/sfxSoundscape.cpp

More...

Detailed Description

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "sfx/sfxSoundscape.h"
 25#include "sfx/sfxAmbience.h"
 26#include "sfx/sfxEnvironment.h"
 27#include "sfx/sfxState.h"
 28#include "sfx/sfxSource.h"
 29#include "sfx/sfxSystem.h"
 30
 31
 32//#define DEBUG_SPEW
 33
 34//FIXME: fix reverb resetting
 35
 36//=============================================================================
 37//    SFXSoundscape.
 38//=============================================================================
 39// MARK: ---- SFXSoundscape ----
 40
 41//-----------------------------------------------------------------------------
 42
 43SFXSoundscape::SFXSoundscape( SFXAmbience* ambience )
 44   : mAmbience( ambience )
 45{
 46   mDirtyBits.set( AmbienceDirty );
 47   mFlags.set( FlagUnique );
 48   
 49   dMemset( mStates, 0, sizeof( mStates ) );
 50}
 51
 52//-----------------------------------------------------------------------------
 53
 54void SFXSoundscape::setAmbience( SFXAmbience* ambience )
 55{
 56   AssertFatal( ambience != NULL, "SFXSoundscape::setAmbience - ambience cannot be NULL!" );
 57   mDirtyBits.set( AmbienceDirty );
 58   mAmbience = ambience;
 59}
 60
 61//=============================================================================
 62//    SFXSoundscapeManager.
 63//=============================================================================
 64// MARK: ---- SFXSoundscapeManager ----
 65
 66//-----------------------------------------------------------------------------
 67
 68SFXSoundscapeManager::SFXSoundscapeManager()
 69   : mCurrentReverbIndex( -1 )
 70{
 71   VECTOR_SET_ASSOCIATION( mStack );
 72   VECTOR_SET_ASSOCIATION( mFadeStack );
 73   
 74#ifndef TORQUE_SHIPPING
 75
 76   // Hook on to the ambience change signal (used
 77   // to respond to editing of ambiences).
 78   
 79   SFXAmbience::getChangeSignal().notify
 80      ( this, &SFXSoundscapeManager::_notifyAmbienceChanged );
 81   
 82#endif
 83
 84   // Push the global ambience.
 85   
 86   mDefaultGlobalAmbience = new SFXAmbience;
 87   SFXSoundscape* global = mChunker.alloc();
 88   constructInPlace( global, mDefaultGlobalAmbience );
 89   mStack.push_back( global );
 90}
 91
 92//-----------------------------------------------------------------------------
 93
 94SFXSoundscapeManager::~SFXSoundscapeManager()
 95{
 96#ifndef TORQUE_SHIPPING
 97
 98   // Remove the hook on the ambience change signal.
 99   
100   SFXAmbience::getChangeSignal().remove
101      ( this, &SFXSoundscapeManager::_notifyAmbienceChanged );
102      
103#endif
104
105   mDefaultGlobalAmbience->deleteObject();
106}
107
108//-----------------------------------------------------------------------------
109
110void SFXSoundscapeManager::update()
111{
112   // Make sure the topmost reverb on the stack is active.
113   
114   S32 reverbIndex = _findTopmostReverbOnStack( mStack );
115   if( mCurrentReverbIndex != reverbIndex )
116   {
117      if( reverbIndex == -1 )
118      {
119         // No ambience on the stack has reverb settings so reset
120         // to default.
121         SFX->setReverb( SFXReverbProperties() );
122      }
123      else
124      {
125         SFXAmbience* ambience = mStack[ reverbIndex ]->getAmbience();
126         AssertFatal( ambience->getEnvironment(), "SFXSoundscapeManager::update - Reverb lookup return ambience without reverb!" );
127
128         SFX->setRolloffFactor( ambience->getRolloffFactor() );
129         SFX->setDopplerFactor( ambience->getDopplerFactor() );
130         SFX->setReverb( ambience->getEnvironment()->getReverb() );
131      }
132
133      mCurrentReverbIndex = reverbIndex;
134   }
135   
136   // Update the active soundscapes.
137   
138   for( U32 i = 0; i < mStack.size(); ++ i )
139   {
140      SFXSoundscape* soundscape = mStack[ i ];
141      
142      // If the soundscape's associated ambience has changed
143         
144      if( soundscape->mDirtyBits.test( SFXSoundscape::AmbienceDirty ) )
145      {
146         SFXAmbience* ambience = soundscape->getAmbience();
147         
148         // Start playing the ambient audio track if it isn't
149         // already playing and if the soundscape isn't overridden
150         // by an instance lower down the stack.
151         
152         if( !soundscape->_isOverridden() )
153         {
154            SFXTrack* track = ambience->getSoundTrack();
155            if( !soundscape->mSource || soundscape->mSource->getTrack() != track )
156            {
157               if( soundscape->mSource != NULL )
158               {
159                  soundscape->mSource->stop();
160                  soundscape->mSource = NULL;
161               }
162               
163               if( track )
164                  soundscape->mSource = SFX->playOnce( track );
165            }
166            else if( soundscape->mSource != NULL )
167            {
168               // Make sure to revert a fade-out running on the source
169               // if it has been taken from the fade stack.
170               
171               soundscape->mSource->play();
172            }
173         }
174         
175         // Activate SFXStates on the ambience.  For state slots that
176         // have changed, deactivate states that we have already activated.
177         
178         for( U32 ambState = 0; ambState < SFXAmbience::MaxStates; ++ambState)
179         {
180            SFXState* state = ambience->getState(ambState);
181            if( soundscape->mStates[ambState] != state )
182            {
183               if( soundscape->mStates[ambState] )
184                  soundscape->mStates[ambState]->deactivate();
185
186               if( state )
187                  state->activate();
188                  
189               soundscape->mStates[ambState] = state;
190            }
191         }
192         
193         soundscape->mDirtyBits.clear( SFXSoundscape::AmbienceDirty );
194      }
195   }
196   
197   // Clean out the fade stack.
198   
199   for( U32 i = 0; i < mFadeStack.size(); )
200      if( mFadeStack[ i ]->mSource == NULL )
201      {
202         SFXSoundscape* soundscape = mFadeStack[ i ];
203         mFadeStack.erase_fast( i );
204         
205         #ifdef DEBUG_SPEW
206         Platform::outputDebugString( "[SFXSoundscapeManager] Deleting faded instance of '%s'",
207            soundscape->getAmbience()->getName() );
208         #endif
209         
210         // Free the soundscape.
211         
212         destructInPlace( soundscape );
213         mChunker.free( soundscape );
214      }
215      else
216         ++ i;
217}
218
219//-----------------------------------------------------------------------------
220
221SFXSoundscape* SFXSoundscapeManager::insertSoundscape( U32 index, SFXAmbience* ambience )
222{
223   AssertFatal( index <= mStack.size(), "SFXSoundscapeManager::insertSoundscape - index out of range" );
224   AssertFatal( index != 0, "SFXSoundscapeManager::insertSoundscape - cannot insert before global soundscape" );
225   AssertFatal( ambience != NULL, "SFXSoundscapeManager::insertSoundscape - got a NULL ambience" );
226   
227   // Look for an existing soundscape that is assigned the
228   // same ambience.
229   
230   S32 ambientInstanceIndex = _findOnStack( ambience, mStack );
231   
232   // Push a soundscape unto the stack.  If there is an instance
233   // on the fade stack that is tied to the given ambience, bring that
234   // soundscape over to the active stack.  Otherwise create a new
235   // soundscape instance.
236   
237   SFXSoundscape* soundscape = NULL;
238   if( ambientInstanceIndex == -1 )
239   {
240      S32 fadeIndex = _findOnStack( ambience, mFadeStack );
241      if( fadeIndex != -1 )
242      {
243         soundscape = mFadeStack[ fadeIndex ];
244         mFadeStack.erase_fast( fadeIndex );
245         
246         // Make sure the soundscape will get re-evaluated
247         // on the next update.
248         
249         soundscape->mDirtyBits.set( SFXSoundscape::AmbienceDirty );
250
251         #ifdef DEBUG_SPEW
252         Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' from fade stack to #%i (total: %i)",
253            ambience->getName(), index, mStack.size() + 1 );
254         #endif
255      }
256   }
257   
258   if( !soundscape )
259   {
260      #ifdef DEBUG_SPEW
261      Platform::outputDebugString( "[SFXSoundscapeManager] Adding new instance for '%s' at #%i (total: %i)",
262         ambience->getName(), index, mStack.size() + 1 );
263      #endif
264      
265      soundscape = mChunker.alloc();
266      constructInPlace( soundscape, ambience );
267   }
268   
269   mStack.insert( index, soundscape );
270   
271   // If there is an existing soundscape that is assigned the
272   // same ambience and it is lower on the stack, steal its sound
273   // source if it has one.  If it is higher up the stack, simply
274   // mark this soundscape as being overridden.
275   
276   if( ambientInstanceIndex != -1 )
277   {
278      SFXSoundscape* existingSoundscape = mStack[ ambientInstanceIndex ];
279      
280      existingSoundscape->mFlags.clear( SFXSoundscape::FlagUnique );
281      soundscape->mFlags.clear( SFXSoundscape::FlagUnique );
282      
283      if( ambientInstanceIndex < index )
284      {
285         existingSoundscape->mFlags.set( SFXSoundscape::FlagOverridden );
286
287         SFXSource* source = existingSoundscape->mSource;
288         existingSoundscape->mSource = NULL;
289         
290         if( source && source->isPlaying() )
291            soundscape->mSource = source;
292      }
293      else
294      {
295         soundscape->mFlags.set( SFXSoundscape::FlagOverridden );
296      }
297   }
298      
299   return soundscape;
300}
301
302//-----------------------------------------------------------------------------
303
304void SFXSoundscapeManager::removeSoundscape( SFXSoundscape* soundscape )
305{
306   AssertFatal( soundscape != getGlobalSoundscape(),
307      "SFXSoundscapeManager::removeSoundscape() - trying to remove the global soundscape" );
308      
309   // Find the soundscape on the stack.
310            
311   U32 index = 1;
312   for( ; index < mStack.size(); ++ index )
313      if( mStack[ index ] == soundscape )
314         break;
315         
316   AssertFatal( index < mStack.size(),
317      "SFXSoundscapeManager::removeSoundscape() - soundscape not on stack" );
318      
319   // Find out if the soundscape has the current reverb
320   // environment.  If so, we need to change the reverb to
321   // the next one higher up the stack.
322
323   const bool isCurrentReverb = ( _findTopmostReverbOnStack( mStack ) == index );
324   
325   // Remove the soundscape from the stack.
326
327   mStack.erase( index );
328   
329   // Update reverb, if necessary.
330   
331   if( isCurrentReverb )
332   {
333      S32 reverbIndex = _findTopmostReverbOnStack( mStack );
334      if( reverbIndex != -1 )
335         SFX->setReverb( mStack[ reverbIndex ]->getAmbience()->getEnvironment()->getReverb() );
336   }
337   
338   // Deactivate states.
339   
340   for( U32 i = 0; i < SFXAmbience::MaxStates; ++ i )
341      if( soundscape->mStates[ i ] )
342      {
343         soundscape->mStates[ i ]->deactivate();
344         soundscape->mStates[ i ] = NULL;
345      }
346   
347   // If the soundscape is the only instance of its ambience, move
348   // it to the fade stack.  Otherwise delete the soundscape.
349   
350   if( soundscape->_isUnique() && soundscape->mSource != NULL )
351   {
352      #ifdef DEBUG_SPEW
353      Platform::outputDebugString( "[SFXSoundscapeManager] Moving ambience '%s' at index #%i to fade stack",
354         soundscape->getAmbience()->getName(), index );
355      #endif
356      
357      soundscape->mSource->stop();
358      mFadeStack.push_back( soundscape );      
359   }
360   else
361   {
362      if( !soundscape->_isUnique() )
363      {
364         // If this is the overriding soundscape, transfer its state
365         // to the ambient instance lower down the stack.
366         
367         if( !soundscape->_isOverridden() )
368         {
369            S32 overrideeIndex = index - 1;
370            for( ; overrideeIndex >= 0; -- overrideeIndex )
371               if( soundscape->getAmbience() == mStack[ overrideeIndex ]->getAmbience() )
372                  break;
373                  
374            AssertFatal( overrideeIndex >= 0,
375               "SFXSoundscapeManager::removeSoundscape() - could not find ambience being overridden on stack" );
376               
377            // Pass the source on to the previous soundscape.
378               
379            mStack[ overrideeIndex ]->mSource = soundscape->mSource;
380            soundscape->mSource = NULL;
381         }
382         
383         #ifdef DEBUG_SPEW
384         Platform::outputDebugString( "[SFXSoundscapeManager] Removing instance of '%s' at index #%i",
385            soundscape->getAmbience()->getName(), index );
386         #endif
387         
388         // If there's only one instance of this ambience is
389         // left, mark it as being unique now.
390         
391         U32 numInstances = 0;
392         for( U32 i = 0; i < mStack.size(); ++ i )
393            if( mStack[ i ]->getAmbience() == soundscape->getAmbience() )
394               ++ numInstances;
395               
396         if( numInstances == 1 )
397            mStack[ _findOnStack( soundscape->getAmbience(), mStack ) ]->mFlags.set( SFXSoundscape::FlagUnique );
398      }
399         
400      // Free the soundscape.
401         
402      destructInPlace( soundscape );
403      mChunker.free( soundscape );
404   }
405}
406
407//-----------------------------------------------------------------------------
408
409S32 SFXSoundscapeManager::_findOnStack( SFXAmbience* ambience, const Vector< SFXSoundscape*>& stack )
410{
411   // Search the stack top to bottom so we always find
412   // the uppermost instance of the ambience on the stack.
413   
414   for( S32 i = stack.size() - 1; i >= 0; -- i )
415      if( stack[ i ]->getAmbience() == ambience )
416         return i;
417         
418   return -1;
419}
420
421//-----------------------------------------------------------------------------
422
423S32 SFXSoundscapeManager::_findTopmostReverbOnStack( const Vector< SFXSoundscape*>& stack )
424{
425   for( S32 i = stack.size() - 1; i >= 0; -- i )
426      if( stack[ i ]->getAmbience()->getEnvironment() )
427         return i;
428         
429   return -1;
430}
431
432//-----------------------------------------------------------------------------
433
434void SFXSoundscapeManager::_notifyAmbienceChanged( SFXAmbience* ambience )
435{
436   //RDTODO: fade stack?
437   
438   // Set the ambience dirty bit on all soundscapes
439   // tied to the given ambience.
440   
441   for( U32 i = 0; i < mStack.size(); ++ i )
442      if( mStack[ i ]->getAmbience() == ambience )
443      {
444         mStack[ i ]->mDirtyBits.set( SFXSoundscape::AmbienceDirty );
445         
446         #ifdef DEBUG_SPEW
447         Platform::outputDebugString( "[SFXSoundscapeManager] Ambience '%s' at #%i changed",
448            ambience->getName(), i );
449         #endif
450      }
451}
452