# 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  

env_sound_user.cpp

Go to the documentation of this file.
00001 
00007 /* Copyright, 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 #include "stdsound.h"
00027 
00028 #include "env_sound_user.h"
00029 #include "sound.h"
00030 #include "bounding_shape.h"
00031 #include <stdlib.h>
00032 
00033 using namespace std;
00034 using namespace NLMISC;
00035 
00036 
00037 namespace NLSOUND {
00038 
00039 
00040 /*
00041  * Constructor
00042  */
00043 CEnvSoundUser::CEnvSoundUser() : _Play(false), _Source(NULL), _BoundingShape(NULL), _Transition(false), _Parent(NULL), _Mark(false), _Gain(1.0f)
00044 {
00045 }
00046 
00047 
00048 /*
00049  * Destructor
00050  */
00051 CEnvSoundUser::~CEnvSoundUser()
00052 {
00053         vector<CEnvSoundUser*>::iterator ipe;
00054         for ( ipe=_Children.begin(); ipe!=_Children.end(); ++ipe )
00055         {
00056                 delete (*ipe);
00057         }
00058 
00059         vector<IPlayable*>::iterator ipp;
00060         for ( ipp=_SrcBank.begin(); ipp!=_SrcBank.end(); ++ipp )
00061         {
00062                 // An IPlayable object can be a CAmbiantSource or a CSourceUser.
00063                 // A CAmbiantSource removes its source (channels) in destructor, but not a CSourceUser
00064                 if ( dynamic_cast<CSourceUser*>(*ipp) )
00065                 {
00066                         CAudioMixerUser::instance()->removeSource( static_cast<CSourceUser*>(*ipp) );
00067                 }
00068                 else
00069                 {
00070                         if ( (*ipp) != NULL )
00071                         {
00072                                 delete (*ipp);
00073                         }
00074                 }
00075         }
00076 
00077         if ( _BoundingShape != NULL )
00078         {
00079                 delete _BoundingShape;
00080         }
00081 }
00082 
00083 
00084 /*
00085  * Serialize (recursive)
00086  */
00087 void CEnvSoundUser::serial( NLMISC::IStream& s )
00088 {
00089         // If you change this, increment the version number in CEnvSoundUser::load() !
00090 
00091         s.serial( _Transition );
00092         s.serialPolyPtr( _BoundingShape );
00093         s.serialContPolyPtr( _SrcBank ); // serializes sound and looping state only
00094 
00095         uint16 srcindex;
00096 
00097         if ( s.isReading() )
00098         {
00099                 // Select the source in the bank
00100                 s.serial( srcindex );
00101                 if ( ! _SrcBank.empty() )
00102                 {
00103                         _Source = _SrcBank[srcindex];
00104                 }
00105                 else
00106                 {
00107                         _Source = NULL;
00108                 }
00109 
00110                 // Set the position which was not serialized
00111                 if ( (_Source!=NULL) && (_BoundingShape != NULL) )
00112                 {
00113                         vector<IPlayable*>::iterator ipp;
00114                         for ( ipp=_SrcBank.begin(); ipp!=_SrcBank.end(); ++ipp )
00115                         {
00116                                 (*ipp)->moveTo( _BoundingShape->getCenter() );
00117                         }
00118                 }
00119 
00120                 // Init the source (not transition)
00121                 if ( ! _Transition )
00122                 {
00123                         if ( (_Source != NULL) )
00124                         {
00125                                 vector<IPlayable*>::iterator ipp;
00126                                 if ( _BoundingShape != NULL )
00127                                 {
00128                                         for ( ipp=_SrcBank.begin(); ipp!=_SrcBank.end(); ++ipp )
00129                                         {
00130                                                 (*ipp)->initPos( const_cast<CVector*>(&(_BoundingShape->getCenter())) );
00131                                         }
00132                                 }
00133                                 else
00134                                 {
00135                                         // The world envsound will be heard at the listener
00136                                         for ( ipp=_SrcBank.begin(); ipp!=_SrcBank.end(); ++ipp )
00137                                         {
00138                                                 (*ipp)->initPos( &CAudioMixerUser::instance()->getListenPosVector() );
00139                                         }
00140                                 }
00141                         }
00142                 }
00143         }
00144         else
00145         {
00146                 uint i;
00147                 // Find the index of the current source in the bank and serialize it out
00148                 if ( _SrcBank.size() != 0 )
00149                 {
00150                         for ( i=0; i!=_SrcBank.size(); i++ )
00151                         {
00152                                 if ( _SrcBank[i] == _Source )
00153                                 {
00154                                         break;
00155                                 }
00156                         }
00157                         nlassert( i != _SrcBank.size() );
00158                         nlassert( i < 0x10000 ); // 16-bit value
00159                         srcindex = i;
00160                 }
00161                 else
00162                 {
00163                         srcindex = 0;
00164                 }
00165                 s.serial( srcindex );
00166         }
00167 
00168         // Tags
00169         s.serialCont( _Tags );
00170 #ifdef NL_DEBUG
00171         DebugLog->display( "Envsound tags:");
00172         vector<string>::iterator ist;
00173         for ( ist=_Tags.begin(); ist!=_Tags.end(); ++ist )
00174         {
00175                 DebugLog->displayRaw( (" " + (*ist)).c_str() );
00176         }
00177         DebugLog->displayRawNL("");
00178 #endif
00179 
00180         // Children envsounds
00181         s.serialPtr( _Parent );
00182         s.serialContPtr( _Children );
00183 }
00184 
00185 
00186 /*
00187  * Select current env
00188  */
00189 void                    CEnvSoundUser::selectEnv( const char *tag, bool children_too )
00190 {
00191         uint i;
00192         for ( i=0; i!= _Tags.size(); i++ )
00193         {
00194                 if ( _Tags[i] == string(tag) )
00195                 {
00196                         if ( _Source != NULL )
00197                         {
00198                                 _Source->enable( false, 1.0f );
00199                         }
00200                         _Source = _SrcBank[i];
00201                         nldebug( "AM: EnvSound: Environment changed to %s", tag );
00202                         CAudioMixerUser::instance()->getEnvSounds()->recompute();
00203                         break;
00204                 }
00205         }
00206         //nldebug( "AM: EnvSound: Environment %s not found", tag );
00207         // Don't change _Source if not found
00208 
00209         // Apply to descendants
00210         if ( children_too )
00211         {
00212                 vector<CEnvSoundUser*>::iterator ipc;
00213                 for ( ipc=_Children.begin(); ipc!=_Children.end(); ++ipc )
00214                 {
00215                         (*ipc)->selectEnv( tag, children_too );
00216                 }
00217         }
00218 }
00219 
00220 
00221 /*
00222  * Serialize file header
00223  */
00224 void    CEnvSoundUser::serialFileHeader( NLMISC::IStream& s )
00225 {
00226         // Envsounds file header
00227         s.serialCheck( (uint32)'SEN' ); // NeL Environment Sounds
00228         if ( s.serialVersion( 2 ) < 2 )
00229         {
00230                 throw EOlderStream(s);
00231         }
00232 
00233         // Check CSound version (and prepare CSound::serial() backward compatibility)
00234         CSound::FileVersion = s.serialVersion( CSound::CurrentVersion );
00235         if ( CSound::FileVersion == 0 ) // warning: not multithread-compliant
00236         {
00237                 // Not supporting version 0 anymore
00238                 throw EOlderStream(s);
00239         }
00240 }
00241 
00242 
00243 /*
00244  * Count the envs in the tree (call on the root)
00245  */
00246 uint32  CEnvSoundUser::getCount() const
00247 {
00248         uint32 cnt=1;
00249         vector<CEnvSoundUser*>::const_iterator ipe;
00250         for ( ipe=_Children.begin(); ipe!=_Children.end(); ++ipe )
00251         {
00252                 cnt += (*ipe)->getCount();
00253         }
00254         return cnt;
00255 }
00256 
00257 
00258 /*
00259  * Load several envsounds and return the number of envsounds loaded
00260  */
00261 uint32 CEnvSoundUser::load( CEnvSoundUser* &envSoundTreeRoot, NLMISC::IStream& s )
00262 {
00263         if ( s.isReading() )
00264         {
00265                 serialFileHeader( s );
00266                 s.serialPtr( envSoundTreeRoot );
00267                 return envSoundTreeRoot->getCount();
00268 
00269                 // Reset CSound version
00270                 CSound::FileVersion = CSound::CurrentVersion; // warning: not multithread-compliant : do not serialize in different threads !
00271         }
00272         else
00273         {
00274                 nlstop;
00275                 return 0;
00276         }
00277 }
00278 
00279 
00280 /*
00281  * Update the stereo mixes (call evenly on the root) (recursive)
00282  */
00283 void                    CEnvSoundUser::update()
00284 {
00285         if ( _Source != NULL )
00286         {
00287                 _Source->update();
00288         }
00289         vector<CEnvSoundUser*>::iterator ipe;
00290         for ( ipe=_Children.begin(); ipe!=_Children.end(); ++ipe )
00291         {
00292                 (*ipe)->update();
00293         }
00294 }
00295 
00296 
00297 /*
00298  * Find the area where the listener is located (recursive)
00299  */
00300 CEnvSoundUser *CEnvSoundUser::findCurrentEnv( const NLMISC::CVector& listenerpos )
00301 {
00302         // Find in children first (check from leaves to root)
00303         vector<CEnvSoundUser*>::iterator ipe = _Children.begin();
00304         CEnvSoundUser *found = NULL;
00305         while ( ! ( (found) || (ipe==_Children.end()) ) )
00306         {
00307                 found = (*ipe)->findCurrentEnv( listenerpos );
00308                 ipe++;
00309         }
00310         if ( found )
00311                 return found;
00312         else if ( (_BoundingShape == NULL) || (_BoundingShape->include( listenerpos )) )
00313                 return this;
00314         else
00315                 return NULL;
00316 }
00317 
00318 
00319 /*
00320  * Return the position
00321  */
00322 void CEnvSoundUser::getPos( NLMISC::CVector& pos ) const
00323 {
00324         if ( _BoundingShape == NULL )
00325         {
00326                 pos = CVector::Null;
00327         }
00328         else
00329         {
00330                 pos = _BoundingShape->getCenter();
00331         }
00332 }
00333 
00334 
00335 /*
00336  * Moves the envsound (and its transition envsound if it has one)
00337  */
00338 void CEnvSoundUser::setPos( const NLMISC::CVector& pos )
00339 {
00340         if ( _BoundingShape != NULL )
00341         {
00342                 // Get the vector between the pos of this envsound and the pos of its transition envsound
00343                 CVector newpos;
00344                 if ( (_Parent != NULL) && ( _Parent->_Transition ) )
00345                 {
00346                         newpos = pos + _Parent->_BoundingShape->getCenter() - _BoundingShape->getCenter();
00347                 }
00348                 else
00349                 {
00350                         newpos = pos;
00351                 }
00352 
00353                 // Set the new pos
00354                 _BoundingShape->setCenter( pos );
00355                 if ( (_Parent != NULL) && ( _Parent->_Transition ) )
00356                 {
00357                         _Parent->_BoundingShape->setCenter( newpos );
00358                         if ( _Parent->_Source != NULL )
00359                         {
00360                                 _Parent->_Source->moveTo( newpos );
00361                         }
00362                 }
00363 
00364                 // Recompute the entire tree
00365                 CAudioMixerUser::instance()->getEnvSounds()->recompute();
00366         }
00367 }
00368 
00369 
00370 /*
00371  * Return the children envsounds
00372  */
00373 std::vector<UEnvSound*>& CEnvSoundUser::getChildren()
00374 {
00375         return (vector<UEnvSound*>&)(_Children);
00376 }
00377 
00378 
00379 /*
00380  * Play or stop the sources (call only on the root env)
00381  */
00382 void CEnvSoundUser::recompute()
00383 {
00384         nlassert( isRoot() );
00385 
00386         // Find the area of the listener
00387         CVector listenerpos;
00388         CAudioMixerUser::instance()->getListener()->getPos( listenerpos );
00389         CEnvSoundUser *current = findCurrentEnv( listenerpos );
00390 
00391         // Mark the envs that have to play their source
00392         if ( current != NULL )
00393         {
00394                 current->markSources( listenerpos, 1.0f );
00395         }
00396         
00397         // Enable/disable the sources in the hierarchy, and reset the marks
00398         applySourcesMarks();
00399 }
00400 
00401 
00402 /* Prepare the related sources to play (recursive).
00403  * In each children branch, there must be an env which is not a transition, for the recursion to stop
00404  */
00405 void CEnvSoundUser::markSources( const NLMISC::CVector& listenerpos, float gain )
00406 {
00407         // Is the listener in a transition area ?
00408         if ( _Transition )
00409         {
00410                 //nldebug( "AM: EnvSound: Marking sources for transition between child and parent" );
00411 
00412                 // Compute the listener position to find the ratio between up and down envs
00413                 nlassert( (_Children.size() == 1) && (_Children[0] != NULL) && (_Parent != NULL) );
00414                 nlassert( _BoundingShape && _Children[0]->_BoundingShape );
00415                 float ratio = _BoundingShape->getRatio( listenerpos, _Children[0]->_BoundingShape );
00416                 nlassert( ratio >= 0.0f && ratio <= 1.0f );
00417 
00418                 // The child env plays at gain*ratio
00419                 // The recursion stops because the child env is not a transition area
00420                 _Children[0]->markSources( listenerpos, gain * ratio );
00421 
00422                 // The parent env (therefore the 3d source of the current env as well) plays at gain*(1-ratio)
00423                 // The recursion stops because the parent env is not a transition area
00424                 _Parent->markSources( listenerpos, gain * (1.0f-ratio) );
00425         }
00426         else
00427         {
00428                 //nldebug( "AM: EnvSound: Marking sources for environnement" );
00429 
00430                 // The listener in an environment, containing other environments (e.g. a town) or not (e.g. a room).
00431                 // The current env plays
00432                 _Mark = true;
00433                 _Gain = gain;
00434 
00435                 // The children env (next level only) play
00436                 vector<CEnvSoundUser*>::iterator ipe;
00437                 for( ipe=_Children.begin(); ipe!=_Children.end(); ++ipe )
00438                 {
00439                         (*ipe)->_Mark = true;
00440                         (*ipe)->_Gain = gain;
00441                 }
00442         }
00443 }
00444 
00445 
00446 /*
00447  * Enable/disable the source and set general gain if enabled, and reset the source mark (recursive)
00448  */
00449 void CEnvSoundUser::applySourcesMarks()
00450 {
00451         if ( ! _Play )
00452         {
00453                 _Mark = false;
00454         }
00455         if ( _Source != NULL )
00456         {
00457                 _Source->enable( _Mark, _Gain );
00458         }
00459         _Mark = false;
00460 
00461         // Apply on children
00462         vector<CEnvSoundUser*>::iterator ipe;
00463         for ( ipe=_Children.begin(); ipe!=_Children.end(); ++ipe )
00464         {
00465                 (*ipe)->applySourcesMarks();
00466         }
00467 }
00468 
00469 
00470 /*
00471  * Set properties (EDIT)
00472  */
00473 void            CEnvSoundUser::setProperties( bool transition, IBoundingShape *bshape )
00474 {
00475         _Transition = transition;
00476         _BoundingShape = bshape;
00477 }
00478 
00479 
00480 /*
00481  * Add an environment source/tag (EDIT) (set a NULL source for no source at all)
00482  * The current source always becomes the first one.
00483  */
00484 void            CEnvSoundUser::addEnvTag( IPlayable *source, const std::string& tag )
00485 {
00486         _SrcBank.push_back( source );
00487         _Tags.push_back( tag );
00488 
00489         if ( _Source == NULL ) // becomes the first one and stays there
00490         {
00491                 _Source = source;
00492         }
00493 }
00494 
00495 
00496 /*
00497  * Save (output stream only) (EDIT)
00498  */
00499 void CEnvSoundUser::save( CEnvSoundUser *envSoundTreeRoot, NLMISC::IStream& s )
00500 {
00501         nlassert( ! s.isReading() );
00502         nlassert( envSoundTreeRoot->isRoot() );
00503 
00504         serialFileHeader( s );
00505         s.serialPtr( envSoundTreeRoot );
00506 }
00507 
00508 
00509 /*
00510  * Play
00511  */
00512 void CEnvSoundUser::play( bool children_too )
00513 {
00514         playSub( children_too );
00515 
00516         CAudioMixerUser::instance()->getEnvSounds()->recompute();
00517 }
00518 
00519 
00520 /*
00521  * Stop playing
00522  */
00523 void CEnvSoundUser::stop( bool children_too )
00524 {
00525         stopSub( children_too );
00526         
00527         CAudioMixerUser::instance()->getEnvSounds()->recompute();
00528 }
00529 
00530 
00531 /*
00532  * Play this node, and all descendants if children_too is true, but do not recompute
00533  */
00534 void    CEnvSoundUser::playSub( bool children_too )
00535 {
00536         _Play = true;
00537 
00538         // Start transition as well
00539         if ( (_Parent != NULL) && ( _Parent->_Transition ) )
00540         {
00541                 _Parent->_Play = true;
00542         }
00543 
00544         // Apply to descendants
00545         if ( children_too )
00546         {
00547                 vector<CEnvSoundUser*>::iterator ipc;
00548                 for ( ipc=_Children.begin(); ipc!=_Children.end(); ++ipc )
00549                 {
00550                         (*ipc)->playSub( children_too );
00551                 }
00552         }
00553 }
00554 
00555 
00556 /*
00557  * Stop playing this node, and all descendants if children_too is true, but do not recompute
00558  */
00559 void    CEnvSoundUser::stopSub( bool children_too )
00560 {
00561         _Play = false;
00562 
00563         // Stop transition as well
00564         if ( (_Parent != NULL) && ( _Parent->_Transition ) )
00565         {
00566                 _Parent->_Play = false;
00567         }
00568 
00569         // Apply to descendants
00570         if ( children_too )
00571         {
00572                 vector<CEnvSoundUser*>::iterator ipc;
00573                 for ( ipc=_Children.begin(); ipc!=_Children.end(); ++ipc )
00574                 {
00575                         (*ipc)->stopSub( children_too );
00576                 }
00577         }
00578 }
00579 
00580 
00581 /*
00582  * Add a child (EDIT)
00583  */
00584 void CEnvSoundUser::addChild( CEnvSoundUser *child ) 
00585 {
00586         child->_Parent = this;
00587         _Children.push_back( child );
00588 }
00589 
00590 
00591 } // NLSOUND