# Home    # nevrax.com   
Nevrax
Nevrax.org
#News
#Mailing-list
#Documentation
#CVS
#Bugs
#License
Docs
 
Documentation  
Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages   Search  

ambiant_source.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 2000, 2001 Nevrax Ltd.
00008  *
00009  * This file is part of NEVRAX NEL.
00010  * NEVRAX NEL is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2, or (at your option)
00013  * any later version.
00014 
00015  * NEVRAX NEL is distributed in the hope that it will be useful, but
00016  * WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00018  * General Public License for more details.
00019 
00020  * You should have received a copy of the GNU General Public License
00021  * along with NEVRAX NEL; see the file COPYING. If not, write to the
00022  * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023  * MA 02111-1307, USA.
00024  */
00025 
00026 #error "Deprecated"
00027 
00028 #include "stdsound.h"
00029 
00030 #include "ambiant_source.h"
00031 #include "sound.h"
00032 
00033 using namespace NLMISC;
00034 using namespace std;
00035 
00036 
00037 namespace NLSOUND {
00038 
00039 
00040 // If this flag is defined, one ambiant sound cannot be the next one to itself
00041 #define ENVSOUND_DONT_DUPLICATE_AMBIANT
00042 
00043 
00044 /*
00045  * Constructor
00046  */
00047 CAmbiantSource::CAmbiantSource() : _Play(false),
00048                                                                    _StereoGain(0.0f),
00049                                                                    _Sustain(false),
00050                                                                    _RandomSoundChosen(false),
00051                                                                    _NextSparseSoundTime(0),
00052                                                                    _CrossfadeTime(4000),
00053                                                                    _SustainTime(8000),
00054                                                                    _SparseAvgPeriod(20000)
00055 {
00056         _StereoChannels[AMBIANT_CH1].setLooping( true );
00057         _StereoChannels[AMBIANT_CH2].setLooping( true );
00058         //_StereoChannels[SPARSE_CH].setLooping( false );
00059 
00060         srand( (uint32)CTime::getLocalTime() );
00061 }
00062 
00063 
00064 /*
00065  * Destructor
00066  */
00067 CAmbiantSource::~CAmbiantSource()
00068 {
00069         CAudioMixerUser::instance()->removeMySource( &_StereoChannels[AMBIANT_CH1] );
00070         CAudioMixerUser::instance()->removeMySource( &_StereoChannels[AMBIANT_CH2] );
00071         CAudioMixerUser::instance()->removeMySource( &_StereoChannels[SPARSE_CH] );
00072 
00073         // Delete sounds: now done in CAudioMixerUser::~CAudioMixerUser()
00074         /*vector<CSound*>::iterator ipsnds;
00075         for ( ipsnds=_AmbiantSounds.begin(); ipsnds!=_AmbiantSounds.end(); ++ipsnds )
00076         {
00077                 nldebug( "Deleting ambiant sound" );
00078                 delete (*ipsnds);
00079         }
00080         for ( ipsnds=_SparseSounds.begin(); ipsnds!=_SparseSounds.end(); ++ipsnds )
00081         {
00082                 nldebug( "Deleting sparse sound" );
00083                 delete (*ipsnds);
00084         }
00085         */
00086 }
00087 
00088 
00089 /* Init. You can pass a position vector to link to (if the playable has stereo source(s))
00090  * When reading from a stream, call init() *after* serial().
00091  */
00092 void CAmbiantSource::initPos( const CVector *posvector )
00093 {
00094         // Initialize ambiant sound channels
00095         if ( ! _AmbiantSounds.empty()  )
00096         {
00097                 _StereoChannels[AMBIANT_CH1].set3DPositionVector( posvector );
00098                 _StereoChannels[AMBIANT_CH1].setSound( getRandomSound( _AmbiantSounds ) );
00099                 _StereoChannels[AMBIANT_CH1].setGain( 0.0f );
00100                 _StereoChannels[AMBIANT_CH1].setPriority( LowPri);
00101                 CAudioMixerUser::instance()->addSource( &_StereoChannels[AMBIANT_CH1] );
00102                 CAudioMixerUser::instance()->giveTrack( &_StereoChannels[AMBIANT_CH1] );
00103                 
00104                 if ( _AmbiantSounds.size() > 1 )
00105                 {
00106                         _StereoChannels[AMBIANT_CH2].set3DPositionVector( posvector );
00107                         _StereoChannels[AMBIANT_CH2].setSound( getRandomSound( _AmbiantSounds ) );
00108                         _StereoChannels[AMBIANT_CH2].setGain( 0.0f );
00109                         _StereoChannels[AMBIANT_CH2].setPriority( LowPri);
00110                         CAudioMixerUser::instance()->addSource( &_StereoChannels[AMBIANT_CH2] );
00111                         CAudioMixerUser::instance()->giveTrack( &_StereoChannels[AMBIANT_CH2] );
00112                 }
00113         }
00114 
00115         // Initialize sparse sounds channel
00116         if ( ! _SparseSounds.empty() )
00117         {
00118                 _StereoChannels[SPARSE_CH].set3DPositionVector( posvector );
00119                 _StereoChannels[SPARSE_CH].setPriority( LowPri );
00120                 _StereoChannels[SPARSE_CH].setSound( getRandomSound( _SparseSounds ) );
00121                 CAudioMixerUser::instance()->addSource( &_StereoChannels[SPARSE_CH] );
00122                 CAudioMixerUser::instance()->giveTrack( &_StereoChannels[SPARSE_CH] );
00123         }
00124 }
00125 
00126 
00127 /*
00128  * Enable (play with high priority) and set general gain, or disable (stop and set low priority).
00129  */
00130 void                    CAmbiantSource::enable( bool toplay, float gain )
00131 {
00132         // Calc position in cycle
00133         bool crossfade;
00134         uint32 leadchannel;
00135         if ( _AmbiantSounds.size() > 1 )
00136         {
00137                 calcPosInCycle( crossfade, leadchannel );
00138         }
00139         else
00140         {
00141                 crossfade = false;
00142                 leadchannel = AMBIANT_CH1;
00143         }
00144 
00145         // Enable/disable
00146         if ( toplay )
00147         {
00148                 _StereoGain = gain;
00149 
00150                 if ( ! _Play )
00151                 {
00152                         // Start lead channel
00153                         if ( _StereoChannels[leadchannel].getSound() != NULL )
00154                         {
00155                                 nldebug( "AM: Envsound: Switch on channel %u", leadchannel );
00156                                 _StereoChannels[leadchannel].setPriority( HighPri ) ;
00157                                 _StereoChannels[leadchannel].play();
00158                         }
00159 
00160                         // If crossfading, start back channel
00161                         if ( crossfade && (_StereoChannels[1-leadchannel].getSound() != NULL) )
00162                         {
00163                                 nldebug( "AM: Envsound: Switch on channel %u", 1-leadchannel );
00164                                 _StereoChannels[1-leadchannel].setPriority( HighPri ) ;
00165                                 _StereoChannels[1-leadchannel].play();
00166                         }
00167 
00168                         // Calc when the next spare sound will play
00169                         calcRandomSparseSoundTime( NULL );
00170 
00171                         _Play = true;
00172                 }
00173                 // The SPARSE_CH is added only when needed
00174         }
00175         else
00176         {
00177                 if ( _Play )
00178                 {
00179                         // Stop lead channel
00180                         if ( _StereoChannels[leadchannel].getSound() != NULL )
00181                         {
00182                                 nldebug( "AM: Envsound: Switch off channel %u", leadchannel );
00183                                 _StereoChannels[leadchannel].stop();
00184                                 _StereoChannels[leadchannel].setPriority( LowPri );
00185                         }
00186 
00187                         // If crossfading, stop back channel
00188                         if ( crossfade && (_StereoChannels[1-leadchannel].getSound() != NULL) )
00189                         {
00190                                 nldebug( "AM: Envsound: Switch off channel %u", 1-leadchannel );
00191                                 _StereoChannels[1-leadchannel].stop();
00192                                 _StereoChannels[1-leadchannel].setPriority( LowPri );
00193                         }
00194 
00195                         // Stop spare channel, anyway
00196                         if ( _StereoChannels[SPARSE_CH].getSound() != NULL )
00197                         {
00198                                 nldebug( "AM: Envsound: Switch off sparse channel" );
00199                                 _StereoChannels[SPARSE_CH].stop();
00200                                 _StereoChannels[SPARSE_CH].setPriority( LowPri );
00201                         }
00202 
00203                         _Play = false;
00204                 }
00205         }
00206 }
00207 
00208 
00209 /*
00210  * Calc pos in cycle
00211  */
00212 TTime CAmbiantSource::calcPosInCycle( bool& crossfade, uint32& leadchannel )
00213 {
00214         TTime pos = CTime::getLocalTime();
00215         uint32 cycletime = _CrossfadeTime + _SustainTime;
00216         pos = pos % (cycletime*2);
00217 
00218         // Calc which channel will be the lead one
00219         if ( pos < cycletime )
00220         {
00221                 leadchannel = 0;
00222         }
00223         else
00224         {
00225                 leadchannel = 1;
00226                 pos = pos - (cycletime);
00227         }
00228 
00229         // Calc if the pos is in the first part (crossfade) or in the other part (sustain)
00230         crossfade = ( pos < _CrossfadeTime );
00231         
00232         return pos;
00233 }
00234 
00235 
00236 /*
00237  * Update the stereo mix (call evenly)
00238  */
00239 void CAmbiantSource::update()
00240 {
00241         if ( (!_Play) || (_StereoGain==0.0f) )
00242         {
00243                 return;
00244         }
00245 
00246         // Calc pos in cycle
00247         bool crossfade;
00248         uint32 leadchannel, backchannel=0;
00249         TTime posInCycle=0;
00250         if ( _AmbiantSounds.size() > 1 )
00251         {
00252                 posInCycle = calcPosInCycle( crossfade, leadchannel );
00253                 backchannel = 1 - leadchannel;
00254         }
00255         else
00256         {
00257                 // If there is less than 2 sounds, no crossfade, always sustain (a single sound must be looping)
00258                 crossfade = false;
00259                 leadchannel = AMBIANT_CH1;
00260                 _Sustain = true;
00261         }
00262         
00263         // Crossfade the first two sources
00264         if ( crossfade )
00265         {
00266                 // Attack
00267                 float ratio = (float)posInCycle / (float)_CrossfadeTime;
00268                 _StereoChannels[leadchannel].setRelativeGain( ratio*_StereoGain );
00269                 _StereoChannels[backchannel].setRelativeGain( (1.0f - ratio)*_StereoGain );
00270 
00271                 // Start next sound
00272                 if ( _Sustain )
00273                 {
00274                         nldebug( "AM: EnvSound: Beginning crossfade: channel #%u rising", leadchannel );
00275                         _StereoChannels[leadchannel].setPriority( HighPri ) ;
00276                         _StereoChannels[leadchannel].play();
00277                         _Sustain = false;
00278                 }
00279         }
00280         else
00281         {
00282                 // Set sustain gain (takes into account the possible changes to _StereoGain)
00283                 _StereoChannels[leadchannel].setRelativeGain( _StereoGain );
00284 
00285                 // Prepare next ambiant sound
00286                 if ( ! _Sustain )
00287                 {
00288                         _Sustain = true;
00289                         TSoundId nextsound = getRandomSound( _AmbiantSounds );
00290 #ifdef ENVSOUND_DONT_DUPLICATE_AMBIANT
00291                         nlassert( _AmbiantSounds.size() > 1 ); // or infinite loop
00292                         while ( nextsound == _StereoChannels[leadchannel].getSound() )
00293                         {
00294                                 nldebug( "AM: EnvSound: Avoiding ambiant sound duplication..." );
00295                                 nextsound = getRandomSound( _AmbiantSounds );
00296                         }
00297 #endif
00298                         nldebug( "AM: EnvSound: Sustain: channel #1" );
00299                         _StereoChannels[backchannel].setRelativeGain( 0.0f );
00300                         _StereoChannels[backchannel].stop(); // we don't set the priority to LowPri
00301                         _StereoChannels[backchannel].setSound( nextsound );
00302                 }
00303         }
00304 
00305         // Add a short random sound into the third source
00306         if ( ! _SparseSounds.empty() )
00307         {
00308                 // Set sustain gain (takes into account the possible changes to _StereoGain)
00309                 _StereoChannels[SPARSE_CH].setRelativeGain( _StereoGain );
00310 
00311                 // Start next sparse sound
00312                 TTime now = CTime::getLocalTime();
00313                 if ( now > _NextSparseSoundTime )
00314                 {
00315                         TSoundId nextsound = getRandomSound( _SparseSounds );
00316                         _StereoChannels[SPARSE_CH].stop();
00317                         _StereoChannels[SPARSE_CH].setSound( nextsound );
00318                         _StereoChannels[SPARSE_CH].setPriority( MidPri );
00319                         _StereoChannels[SPARSE_CH].play();
00320                         nldebug( "AM: EnvSound: Playing sparse sound" );
00321                         if ( _StereoChannels[SPARSE_CH].getTrack() == NULL )
00322                         {
00323                                 nldebug( "AM: Ensound: Switch on sparse channel" );
00324                                 nlassert( _StereoChannels[SPARSE_CH].getSound() != NULL );
00325                         }
00326                         // Does not leave the track
00327                         calcRandomSparseSoundTime( nextsound );
00328                 }
00329         }
00330 }
00331 
00332 
00333 /*
00334  * Select a random sound in a bank
00335  */
00336 TSoundId        CAmbiantSource::getRandomSound( const std::vector<CSound*>& bank ) const
00337 {
00338         nlassert( ! bank.empty() );
00339         // Note: does not work with a very big size (rand()*bank.size() would overflow)
00340         uint32 r = rand()*bank.size()/(RAND_MAX+1);
00341         nlassert( r < bank.size() );
00342         nldebug( "AM: EnvSound: Prepared random sound number %u of %u", r, bank.size()-1 );
00343 
00344         return bank[r];
00345 }
00346 
00347 
00348 
00349 
00350 /*
00351  * Calculate the next time a sparse sound plays
00352  */
00353 void            CAmbiantSource::calcRandomSparseSoundTime( TSoundId currentsparesound )
00354 {
00355         uint32 delay = (uint)((float)rand() * (float)(_SparseAvgPeriod*2) / (float)RAND_MAX);
00356 
00357         // Check the next sound will play after the current one
00358         if ( currentsparesound != NULL )
00359         {
00360                 uint32 soundlength = currentsparesound->getDuration();
00361                 if ( delay <= soundlength )
00362                 {
00363                         delay = soundlength+1;
00364                 }
00365         }
00366 
00367         nldebug( "AM: EnvSound: Next sparse sound will play in %u ms", delay );
00368         _NextSparseSoundTime = CTime::getLocalTime() + delay;
00369 }
00370 
00371 
00372 /*
00373  * Serialize
00374  */
00375 /*
00376 void                                    CAmbiantSource::serial( NLMISC::IStream& s )
00377 {
00378         // If you change this, increment the version number in CEnvSoundUser::load() !
00379 
00380         // Constants
00381         s.serial( _CrossfadeTime );
00382         s.serial( _SustainTime );
00383         s.serial( _SparseAvgPeriod );
00384 
00385         // Stereo sound banks (sounds allocated here once per pointer, deleted by CAudioMixerUser::~CAudioMixerUser())
00386         s.serialContPtr( _AmbiantSounds );
00387         s.serialContPtr( _SparseSounds );
00388 
00389         // Register within the audio mixer for deletion at the end
00390         vector<CSound*>::iterator ips;
00391         for ( ips=_AmbiantSounds.begin(); ips!=_AmbiantSounds.end(); ++ips )
00392         {
00393                 CAudioMixerUser::instance()->addAmbiantSound( (*ips) );
00394         }
00395         for ( ips=_SparseSounds.begin(); ips!=_SparseSounds.end(); ++ips )
00396         {
00397                 CAudioMixerUser::instance()->addAmbiantSound( (*ips) );
00398         }
00399 }
00400 
00401 */
00402 
00403 /*
00404  * Set properties (EDIT)
00405  */
00406 void            CAmbiantSource::setProperties( std::vector<TSoundId>& ambiantsounds,
00407                                                                                    std::vector<TSoundId>& sparsesounds,
00408                                                                                    uint32 crossfadeTimeMs, uint32 sustainTimeMs,
00409                                                                                    uint32 sparseAvgPeriodMs )
00410 {
00411         _AmbiantSounds = ambiantsounds;
00412         _SparseSounds = sparsesounds;
00413         _CrossfadeTime = crossfadeTimeMs;
00414         _SustainTime = sustainTimeMs;
00415         _SparseAvgPeriod = sparseAvgPeriodMs;
00416 }
00417 
00418 
00419 } // NLSOUND