# 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  

audio_mixer_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 "env_effect.h"
00030 #include "simple_sound.h"
00031 #include "complex_sound.h"
00032 //#include "ambiant_source.h"
00033 //#include "bounding_sphere.h"
00034 //#include "bounding_box.h"
00035 #include "driver/buffer.h"
00036 #include "sample_bank.h"
00037 #include "sound_bank.h"
00038 #include "background_sound_manager.h"
00039 #include "simple_source.h"
00040 #include "complex_source.h"
00041 #include "background_source.h"
00042 
00043 
00044 #include "nel/misc/file.h"
00045 #include "nel/misc/path.h"
00046 #include "nel/misc/time_nl.h"
00047 #include "nel/misc/command.h"
00048 
00049 #include "context_sound.h"
00050 #include <iomanip.h>
00051 
00052 //#include <crtdbg.h>
00053 
00054 using namespace NLMISC;
00055 
00056 using namespace std;
00057 
00058 
00059 namespace NLSOUND {
00060 
00061 
00062 #ifdef _DEBUG
00063 CAudioMixerUser::IMixerEvent    *CurrentEvent = 0;
00064 #endif
00065 
00066 
00067 #define NL_TRACE_MIXER 0
00068 
00069 #if NL_TRACE_MIXER
00070 #define _profile(_a) nldebug ## _a
00071 #else
00072 #define _profile(_a) 
00073 #endif
00074 
00075 // The audio mixer singleton instance
00076 CAudioMixerUser         *CAudioMixerUser::_Instance = NULL;
00077 
00078 // Return the priority cstring (debug info)
00079 const char *PriToCStr [NbSoundPriorities] = { "XH", "HI", "MD", "LO" };
00080 
00081 
00082 // ******************************************************************
00083 
00084 const char *getPriorityStr( TSoundPriority p )
00085 {
00086         nlassert( ((uint)p) < NbSoundPriorities );
00087         return PriToCStr[p];
00088 }
00089 
00090 
00091 // ******************************************************************
00092 
00093 UAudioMixer     *UAudioMixer::createAudioMixer()
00094 {
00095         return new CAudioMixerUser();
00096 }
00097 
00098 
00099 // ******************************************************************
00100 
00101 CAudioMixerUser::CAudioMixerUser() : _SoundDriver(NULL),
00102                                                                          _ListenPosition(CVector::Null),
00103 //                                                                       _EnvSounds(NULL),
00104 //                                                                       _BalancePeriod(0),
00105                                                                          _CurEnvEffect(NULL),
00106                                                                          _NbTracks(0),
00107                                                                          _MaxNbTracks(0),
00108                                                                          _Leaving(false),
00109                                                                          _BackgroundSoundManager(0),
00110                                                                          _PlayingSources(0)
00111 {
00112         if ( _Instance == NULL )
00113         {
00114                 _Instance = this;
00115 
00116 #if NL_PROFILE_MIXER
00117                 _UpdateTime = 0.0;
00118                 _CreateTime = 0.0;
00119                 _UpdateCount = 0;
00120                 _CreateCount = 0;
00121 #endif
00122 
00123         }
00124         else
00125         {
00126                 nlerror( "Audio mixer singleton instanciated twice" );
00127         }
00128 }
00129 
00130 
00131 // ******************************************************************
00132 
00133 CAudioMixerUser::~CAudioMixerUser()
00134 {
00135         nldebug( "AM: Releasing..." );
00136 
00137         if (_BackgroundSoundManager != 0)
00138                 delete _BackgroundSoundManager;
00139 //      CBackgroundSoundManager::release();
00140 
00141         reset();
00142 
00143         _Leaving = true;
00144 
00145         // Release the sound bank
00146         CSoundBank::release();
00147         // Release all the SampleBanks
00148         CSampleBank::releaseAll();
00149 
00150         // Tracks
00151         uint i;
00152         for ( i=0; i!=_NbTracks; i++ )
00153         {
00154                 if ( _Tracks[i] )
00155                         delete _Tracks[i];
00156         }
00157 
00158         // Sound driver
00159         if ( _SoundDriver != NULL )
00160                 delete _SoundDriver;
00161 
00162         _Instance = NULL;
00163 
00164         nldebug( "AM: Released" );
00165 }
00166 
00167 
00168 void CAudioMixerUser::setPriorityReserve(TSoundPriority priorityChannel, uint reserve)
00169 {
00170         _PriorityReserve[priorityChannel] = min(_NbTracks, reserve);
00171 }
00172 
00173 void CAudioMixerUser::setLowWaterMark(uint value)
00174 {
00175         _LowWaterMark = min(_NbTracks, value);
00176 }
00177 
00178 
00179 // ******************************************************************
00180 
00181 void                            CAudioMixerUser::writeProfile(std::ostream& out)
00182 {
00183         // compute number of muted source
00184         uint nb = 0;    
00185         
00186 /*      TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
00187         for (; first != last; ++first)
00188         {
00189                 CSimpleSource *psu = *first;
00190                 if (psu->getTrack() == NULL)
00191                 {
00192                         ++nb;
00193                 }
00194         }
00195 */
00196 /*      hash_set<CSimpleSource*>::const_iterator ips;
00197         for ( ips=_Sources.begin(); ips!=_Sources.end(); ++ips )
00198         {
00199                 CSimpleSource *psu = *ips;
00200                 if (psu->getTrack() == NULL)
00201                 {
00202                         ++nb;
00203                 }
00204         }
00205 */
00206         out << "Mixer: \n";
00207         out << "Playing sources: " << getPlayingSourcesNumber() << " \n";
00208         out << "Available tracks: " << getNumberAvailableTracks() << " \n";
00209 //      out << "Muted sources: " << nb << " \n";
00210 //      out << "Muted sources: " << max(0, sint(_PlayingSources.size())-sint(_NbTracks)) << " \n";
00211         out << "Muted sources: " << max(0, sint(_PlayingSources)-sint(_NbTracks)) << " \n";
00212         out << "Sources waiting for play: " << _SourceWaitingForPlay.size() << " \n";
00213         out << "HighestPri: " << _ReserveUsage[HighestPri] << " \n";
00214         out << "HighPri: " << _ReserveUsage[HighPri] << " \n";
00215         out << "MidPri: " << _ReserveUsage[MidPri] << " \n";
00216         out << "LowPri: " << _ReserveUsage[LowPri] << " \n";
00217         out << "Average update time: " << std::setw(10) << (1000.0 * _UpdateTime / _UpdateCount) << " msec\n";
00218         out << "Average create time: " << std::setw(10) <<(1000.0 * _CreateTime / _CreateCount) << " msec\n";
00219         out << "Estimated CPU: " << std::setiosflags(ios::right) << std::setprecision(6) << std::setw(10) << (100.0 * 1000.0 * (_UpdateTime + _CreateTime) / curTime()) << "%\n";
00220 
00221         if (_SoundDriver)
00222         {
00223                 out << "\n";
00224                 out << "Driver: \n";
00225                 _SoundDriver->writeProfile(out);
00226         }
00227 }
00228 
00229 // ******************************************************************
00230 
00231 void    CAudioMixerUser::addSourceWaitingForPlay(CSimpleSource *source)
00232 {
00233         _SourceWaitingForPlay.push_back(source);
00234 }
00235 
00236 
00237 // ******************************************************************
00238 
00239 void                            CAudioMixerUser::reset()
00240 {
00241         _Leaving = true;
00242 
00243         _SourceWaitingForPlay.clear();
00244 
00245         // Stop tracks
00246         uint i;
00247         for ( i=0; i!=_NbTracks; i++ )
00248         {
00249                 if ( _Tracks[i] )
00250                 {
00251                         CSimpleSource* src = _Tracks[i]->getSource();
00252 
00253                         if (src && src->isPlaying())
00254                         {
00255                                 src->stop();
00256                         }
00257                 }
00258         }
00259 
00260         // Sources
00261         while (!_Sources.empty())
00262         {
00263                 //removeSource( _Sources.begin(), true ); // 3D sources, the envsounds were removed above
00264                 CSourceCommon *source = *(_Sources.begin());
00265                 if (source->isPlaying())
00266                         source->stop();
00267                 else
00268                         delete source;
00269         }
00270 
00271         _Leaving = false;
00272 }
00273 
00274 // ******************************************************************
00275 
00276 void                            CAudioMixerUser::init( /*uint32 balance_period */)
00277 {
00278         nldebug( "AM: Init..." );
00279 
00280         _profile(( "AM: ---------------------------------------------------------------" ));
00281         _profile(( "AM: DRIVER: %s", NLSOUND_DLL_NAME ));
00282         
00283         // Init sound driver
00284         try
00285         {
00286                 _SoundDriver = ISoundDriver::createDriver();
00287         }
00288         catch(...)
00289         {
00290                 delete this;
00291                 _Instance = NULL;
00292                 throw;
00293         }
00294 
00295         uint i;
00296 
00297 
00298         // Init registrable classes
00299         static bool initialized = false;
00300         if (!initialized)
00301         {
00302 //              CSimpleSource::init();
00303                 initialized = true;
00304         }
00305 
00306         // Init listener
00307         _Listener.init( _SoundDriver );
00308 
00309         // Init tracks (physical sources)
00310         _NbTracks = MAX_TRACKS; // could be chosen by the user, or according to the capabilities of the sound card
00311         for ( i=0; i<MAX_TRACKS; i++ )
00312         {
00313                 _Tracks[i] = NULL;
00314         }
00315         try
00316         {
00317                 for ( i=0; i!=_NbTracks; i++ )
00318                 {
00319                         _Tracks[i] = new CTrack();
00320                         _Tracks[i]->init( _SoundDriver );
00321                         _FreeTracks.push_back(_Tracks[i]);
00322                 }
00323         }
00324         catch ( ESoundDriver & )
00325         {
00326                 // If the source generation failed, keep only the generated number of sources
00327                 _NbTracks = i;
00328                 //delete _Tracks[i]; // Bug: the desctructor would not work because the source's name is invalid
00329         }
00330 
00331         _MaxNbTracks = _NbTracks;
00332 
00333         // Init the reserve stuff.
00334         _LowWaterMark = 0;
00335         for (i=0; i<NbSoundPriorities; ++i)
00336         {
00337                 _PriorityReserve[i] = _NbTracks;
00338                 _ReserveUsage[i] = 0;
00339         }
00340         
00341         _StartTime = CTime::getLocalTime();
00342 
00343         // Create the background sound manager.
00344         _BackgroundSoundManager = new CBackgroundSoundManager();
00345 
00346         // Load the sound bank singleton
00347         CSoundBank::instance()->load();
00348         nlinfo( "Initialized audio mixer with %u voices", _NbTracks );
00349 }
00350 
00351 
00352 // ******************************************************************
00353 void    CAudioMixerUser::bufferUnloaded(IBuffer *buffer)
00354 {
00355         // check all track to find a track playing this buffer.
00356         uint i;
00357         for ( i=0; i!=_NbTracks; ++i )
00358         {
00359                 CTrack  *track = _Tracks[i];
00360                 if ( track && track->getSource())
00361                 {
00362                         if (track->getSource()->getBuffer() == buffer)
00363                         {
00364                                 track->getSource()->stop();
00365                         }
00366                 }
00367         }
00368 
00369 }
00370 
00371 
00372 // ******************************************************************
00373 
00374 void                            CAudioMixerUser::enable( bool b )
00375 {
00376         // TODO :  rewrite this method
00377 
00378         nlassert(false);
00379 /*      if ( b )
00380         {
00381                 // Reenable
00382                 _NbTracks = _MaxNbTracks;
00383         }
00384         else
00385         {
00386                 // Disable
00387                 uint i;
00388                 for ( i=0; i!=_NbTracks; i++ )
00389                 {
00390                         if ( _Tracks[i] && ! _Tracks[i]->isAvailable() )
00391                         {
00392                                 _Tracks[i]->getSource()->leaveTrack();
00393 //                              nlassert(_PlayingSources.find(_Tracks[i]->getSource()) != _PlayingSources.end());
00394 //                              _PlayingSources.erase(_Tracks[i]->getSource());
00395                         }
00396                 }
00397                 _NbTracks = 0;
00398         }
00399 */
00400 }
00401 
00402 // ******************************************************************
00403 
00404 ISoundDriver*           CAudioMixerUser::getSoundDriver()
00405 {
00406         return _SoundDriver;
00407 }
00408 
00409 // ******************************************************************
00410 
00411 /*void                          CAudioMixerUser::computeEnvEffect( const CVector& listenerpos, bool force )
00412 {
00413         // Find the first matching, linear search
00414         vector<CEnvEffect*>::iterator ipe;
00415         for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
00416         {
00417                 if ( (*ipe)->include( listenerpos ) )
00418                 {
00419                         // Set the effect only if it has changed
00420                         if ( (_CurEnvEffect != *ipe) || force )
00421                         {
00422                                 _CurEnvEffect = *ipe;
00423                                 _Listener.getListener()->setEnvironment( _CurEnvEffect->getEnvNum(), _CurEnvEffect->getEnvSize() );
00424                                 nldebug( "AM: Listener environmental effect changed to %u", _CurEnvEffect->getEnvNum() );
00425                         }
00426                         return;
00427                 }
00428         }
00429 
00430         // If not found, set the default (only if it wasn't the default before)
00431         if ( _CurEnvEffect != NULL )
00432         {
00433                 _Listener.getListener()->setEnvironment( ENVFX_DEFAULT_NUM );
00434                 _CurEnvEffect = NULL;
00435                 nldebug( "AM: Listener environmental effect reset" );
00436         }
00437 
00438 }
00439 */
00440 
00441 
00442 // ******************************************************************
00443 
00444 void                            CAudioMixerUser::getFreeTracks( uint nb, CTrack **tracks )
00445 {
00446         std::vector<CTrack*>::iterator first(_FreeTracks.begin()), last(_FreeTracks.end());
00447         for (nb =0; first != last; ++first, ++nb)
00448         {
00449                 tracks[nb] = *first;
00450         }
00451 }
00452 
00453  
00454 // ******************************************************************
00455 
00456 void                            CAudioMixerUser::applyListenerMove( const NLMISC::CVector& listenerpos )
00457 {
00458         // Store position
00459         _ListenPosition = listenerpos;
00460 
00461         // Environmental effect
00462 //      computeEnvEffect( listenerpos );
00463 
00464 /*      // Environment sounds
00465         if ( _EnvSounds != NULL )
00466         {
00467                 _EnvSounds->recompute();
00468         }
00469 */
00470 }
00471 
00472 // ******************************************************************
00473 
00474 void                            CAudioMixerUser::reloadSampleBanks(bool async)
00475 {
00476         CSampleBank::reload(async);
00477 }
00478 
00479 // ******************************************************************
00480 
00481 CTrack *CAudioMixerUser::getFreeTrack(CSimpleSource *source)
00482 {
00483         // at least some track free ?
00484         if      (!_FreeTracks.empty())
00485         {
00486                 // under the low water mark or  under the reserve
00487                 if (_FreeTracks.size() > _LowWaterMark          
00488                                 || _ReserveUsage[source->getPriority()] < _PriorityReserve[source->getPriority()] )
00489                 {
00490                         // non discardable track  or not too many waiting source
00491                         if (source->getPriority() == HighestPri         
00492                                 || _FreeTracks.size() > _SourceWaitingForPlay.size())
00493                         {
00494                                 CTrack *ret = _FreeTracks.back();
00495                                 _FreeTracks.pop_back();
00496                                 ret->setSource(source);
00497                                 _ReserveUsage[source->getPriority()]++;
00498 //                              nldebug("Track %p assign to source %p", ret, ret->getSource());
00499                                 return ret;
00500                         }
00501                 }
00502         }
00503         // try to find a track with a source cuttable
00504         {
00505                 float d1, d2, t1, t2;
00506                 d1 = (source->getPos() - _ListenPosition).norm();
00507                 t1 = max(0.0f, 1-((d1-source->getSimpleSound()->getMinDistance()) / (source->getSimpleSound()->getMaxDistance() - source->getSimpleSound()->getMinDistance())));
00508 
00509                 for (uint i=0; i<_NbTracks; ++i)
00510                 {
00511                         CSimpleSource *src2 = _Tracks[i]->getSource();
00512                         if (src2 != 0)
00513                         {
00514                                 d2 = (src2->getPos() - _ListenPosition).norm();
00515                                 t2 = max(0.0f, 1-((d2-src2->getSimpleSound()->getMinDistance()) / (src2->getSimpleSound()->getMaxDistance() - src2->getSimpleSound()->getMinDistance())));
00516 
00517                                 const float tfactor = 1.3f;
00518                                 if (t1 > t2 * tfactor)
00519 //                              if (d1 < d2)
00520                                 {
00521                                         nldebug("Cutting source %p with source %p (%f > %f*%f)", src2, source, t1, tfactor, t2);
00522                                         // on peut cuter cette voie !
00523                                         src2->stop();
00524                                         if (_FreeTracks.empty())
00525                                         {
00526                                                 nlwarning("No free track after cutting a playing sound source !");
00527                                         }
00528                                         else
00529                                         {
00530                                                 CTrack *ret = _FreeTracks.back();
00531                                                 _FreeTracks.pop_back();
00532                                                 ret->setSource(source);
00533                                                 nldebug("Track %p assign to source %p", ret, ret->getSource());
00534                                                 return ret;
00535                                         }
00536                                 }
00537                         }
00538                 }
00539         }
00540 
00541         return 0;
00542 }
00543 
00544 void CAudioMixerUser::freeTrack(CTrack *track)
00545 {
00546         nlassert(track != 0);
00547         nlassert(track->getSource() != 0);
00548 
00549 //      nldebug("Track %p free by source %p", track, track->getSource());
00550 
00551         _ReserveUsage[track->getSource()->getPriority()]--;
00552         track->setSource(0);
00553         _FreeTracks.push_back(track);
00554 }
00555 
00556 
00557 void CAudioMixerUser::getPlayingSoundsPos(std::vector<std::pair<bool, NLMISC::CVector> > &pos)
00558 {
00559         int nbplay = 0;
00560         int     nbmute = 0;
00561         int     nbsrc = 0;
00562 
00563         TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
00564         for (; first != last; ++first)
00565         {
00566                 CSourceCommon *ps = *first;
00567                 if (ps->getType() == CSourceCommon::SOURCE_SIMPLE)
00568                 {
00569                         CSimpleSource *source = static_cast<CSimpleSource*>(*first);
00570                         nbsrc++;
00571 
00572                         if (source->isPlaying())
00573                         {
00574                                 pos.push_back(make_pair(source->getTrack() == 0, source->getPos()));
00575 
00576                                 if (source->getTrack() == 0)
00577                                         nbmute++;
00578                                 else
00579                                 {
00580 //                                      nldebug ("Source %p playing on track %p", source, source->getTrack());
00581                                         nbplay ++;
00582                                 }
00583                         }
00584                 }
00585         }
00586 
00587 //      nldebug("Total source : %d, playing : %d, muted : %d", nbsrc, nbplay, nbmute);
00588 }
00589 
00590 
00591 
00592 void                            CAudioMixerUser::update()
00593 {
00594 #if NL_PROFILE_MIXER
00595         TTicks start = CTime::getPerformanceTime();
00596 #endif
00597 
00598         // update the object.
00599         {
00600                 // 1st, update the event list
00601                 {
00602                         std::vector<std::pair<IMixerUpdate*, bool> >::iterator first(_UpdateEventList.begin()), last(_UpdateEventList.end());
00603                         for (; first != last; ++first)
00604                         {
00605                                 if (first->second)
00606                                         _UpdateList.insert(first->first);
00607                                 else
00608                                         _UpdateList.erase(first->first);
00609                         }
00610                         _UpdateEventList.clear();
00611                 }
00612                 // 2nd, do the update
00613                 {
00614                         TMixerUpdateContainer::iterator first(_UpdateList.begin()), last(_UpdateList.end());
00615                         for (; first != last; ++first)
00616                         {
00617                                 // call the update method.
00618                                 const_cast<IMixerUpdate*>(*first)->onUpdate();
00619                         }
00620                 }
00621         }
00622         // send the event.
00623         {
00624                 // 1st, update the event list
00625                 {
00626                         std::vector<std::pair<NLMISC::TTime, IMixerEvent*> >::iterator first(_EventListUpdate.begin()), last(_EventListUpdate.end());
00627                         for (; first != last; ++first)
00628                         {
00629                                 if (first->first != 0)
00630                                 {
00631                                         // add an event
00632 //                                      nldebug ("Add event %p", first->second);
00633                                         TTimedEventContainer::iterator it(_EventList.insert(*first));
00634                                         _Events.insert(make_pair(first->second, it));
00635                                 }
00636                                 else
00637                                 {
00638                                         // remove the events
00639                                         pair<TEventContainer::iterator, TEventContainer::iterator> range = _Events.equal_range(first->second);
00640                                         TEventContainer::iterator first2(range.first), last2(range.second);
00641                                         for (; first2 != last2; ++first2)
00642                                         {
00643                                                 // remove the event
00644                                                 nldebug("Remove event %p", first2->second->second);
00645                                                 _EventList.erase(first2->second);
00646                                         }
00647                                         _Events.erase(range.first, range.second);
00648                                 }
00649                         }
00650 
00651                         _EventListUpdate.clear();
00652                 }
00653                 // 2nd, call the events
00654                 TTime now = NLMISC::CTime::getLocalTime();
00655                 while (!_EventList.empty() && _EventList.begin()->first <= now)
00656                 {
00657 #ifdef _DEBUG
00658                         CurrentEvent = _EventList.begin()->second;
00659 #endif
00660 //                      nldebug("Sending Event %p", _EventList.begin()->second);
00661                         _EventList.begin()->second->onEvent();
00662                         TEventContainer::iterator it(_Events.lower_bound(_EventList.begin()->second));
00663                         while (it->first == _EventList.begin()->second)
00664                         {
00665                                 if (it->second == _EventList.begin())
00666                                 {
00667                                         _Events.erase(it);
00668                                         break;
00669                                 }
00670                                 it++;
00671                         }
00672                         _EventList.erase(_EventList.begin());
00673 #ifdef _DEBUG
00674                         CurrentEvent = 0;
00675 #endif
00676                 }
00677         }
00678 
00679         // update the background sound
00680 //      _BackgroundSoundManager->update();
00681 
00682         // Check all playing track and stop any terminated buffer.
00683         for (uint i=0; i<_NbTracks; ++i)
00684         {
00685                 if (!_Tracks[i]->isPlaying())
00686                 {
00687                         if (_Tracks[i]->getSource() != 0)
00688                         {
00689                                 CSimpleSource *source = _Tracks[i]->getSource();
00690                                 source->stop();
00691                         }
00692         
00693                         // try to play any waiting source.
00694                         if (!_SourceWaitingForPlay.empty())
00695                         {
00696                                 // check if the source still exist before trying to play it
00697                                 if (_Sources.find(_SourceWaitingForPlay.front()) != _Sources.end())
00698                                         _SourceWaitingForPlay.front()->play();
00699                                 nldebug("Before POP Sources waiting : %u", _SourceWaitingForPlay.size());
00700                                 _SourceWaitingForPlay.pop_front();
00701                                 nldebug("After POP Sources waiting : %u", _SourceWaitingForPlay.size());
00702                         }
00703                 }
00704         }
00705 
00706         // Debug info
00707         /*uint32 i;
00708         nldebug( "List of the %u tracks", _NbTracks );
00709         for ( i=0; i!=_NbTracks; i++ )
00710         {
00711                 CSimpleSource *su;
00712                 if ( su = _Tracks[i]->getSource() )
00713                 {
00714                         nldebug( "%u: %p %s %s %s %s, vol %u",
00715                                     i, &_Tracks[i]->DrvSource, _Tracks[i]->isAvailable()?"FREE":"USED",
00716                                         _Tracks[i]->isAvailable()?"":(su->isPlaying()?"PLAYING":"STOPPED"),
00717                                         _Tracks[i]->isAvailable()?"":PriToCStr[su->getPriority()],
00718                                         _Tracks[i]->isAvailable()?"":(su->getSound()?su->getSound()->getFilename().c_str():""),
00719                                         (uint)(su->getGain()*100.0f) );
00720                 }
00721         }*/
00722 
00723         _SoundDriver->commit3DChanges();
00724 
00725 #if NL_PROFILE_MIXER
00726         _UpdateTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
00727         _UpdateCount++;
00728 #endif
00729 
00730 /*      // display the track using...
00731         {
00732                 char tmp[2048] = "";
00733                 string str;
00734 
00735                 for (uint i=0; i<_NbTracks/2; ++i)
00736                 {
00737                         sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
00738                         str += tmp;
00739                 }
00740                 nldebug((string("Status1: ")+str).c_str());
00741                 str = "";
00742                 for (i=_NbTracks/2; i<_NbTracks; ++i)
00743                 {
00744                         sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
00745                         str += tmp;
00746                 }
00747 //              nldebug((string("Status2: ")+str).c_str());
00748         }
00749 */
00750 }
00751 
00752 
00753 // ******************************************************************
00754 
00755 TSoundId                        CAudioMixerUser::getSoundId( const std::string &name )
00756 {
00757         return CSoundBank::instance()->getSound(name);
00758 }
00759 
00760 // ******************************************************************
00761 
00762 void                            CAudioMixerUser::addSource( CSourceCommon *source )
00763 { 
00764         nlassert(_Sources.find(source) == _Sources.end());
00765         _Sources.insert( source ); 
00766 
00767 //      _profile(( "AM: ADDSOURCE, SOUND: %d, TRACK: %p, NAME=%s", source->getSound(), source->getTrack(),
00768 //                      source->getSound() && (source->getSound()->getName()!="") ? source->getSound()->getName().c_str() : "" ));
00769 
00770 }
00771 
00772 
00773 // ******************************************************************
00774 
00775 USource                         *CAudioMixerUser::createSource( TSoundId id, bool spawn, TSpawnEndCallback cb, void *userParam, CSoundContext *context )
00776 {
00777 #if NL_PROFILE_MIXER
00778         TTicks start = CTime::getPerformanceTime();
00779 #endif
00780 
00781         _profile(( "AM: [%u]---------------------------------------------------------------", curTime() ));
00782         _profile(( "AM: CREATESOURCE: SOUND=%p, NAME=%s, TIME=%d", id, id->getName().c_str(), curTime() ));
00783         _profile(( "AM: SOURCES: %d, PLAYING: %d, TRACKS: %d", getSourcesNumber(), getPlayingSourcesNumber(), getNumberAvailableTracks() ));
00784 
00785         if ( id == NULL )
00786         {
00787                 _profile(("AM: FAILED CREATESOURCE"));
00788                 nldebug( "AM: Sound not created: invalid sound id" );
00789                 return NULL;
00790         }
00791 
00792         USource *ret = NULL;
00793 
00794         if (id->getSoundType() == CSound::SOUND_SIMPLE)
00795         {
00796                 CSimpleSound    *simpleSound = static_cast<CSimpleSound *>(id);
00797                 // This is a simple sound
00798                 if (simpleSound->getBuffer() == NULL)
00799                 {
00800                         nlwarning ("Can't create the sound '%s'", simpleSound->getBuffername().c_str());
00801                         return NULL;
00802                 }
00803 
00804                 // Create source
00805                 CSimpleSource *source = new CSimpleSource( simpleSound, spawn, cb, userParam);
00806 
00807 //              nldebug("Mixer : source %p created", source);
00808 
00809                 if (source->getBuffer() != 0)
00810                 {
00811                         // Link the position to the listener position if it'a stereo source
00812                         if ( source->getBuffer()->isStereo() )
00813                         {
00814                                 source->set3DPositionVector( &_ListenPosition );
00815                         }
00816                 }
00817                 else
00818                 {
00819                         nlassert(false); // FIXME
00820                 }
00821                 ret = source;
00822         }
00823         else if (id->getSoundType() == CSound::SOUND_COMPLEX)
00824         {
00825                 CComplexSound   *complexSound = static_cast<CComplexSound*>(id);
00826                 // This is a pattern sound.
00827                 ret =  new CComplexSource(complexSound, spawn, cb, userParam);
00828         }
00829         else if (id->getSoundType() == CSound::SOUND_BACKGROUND)
00830         {
00831                 // This is a background sound.
00832                 CBackgroundSound        *bgSound = static_cast<CBackgroundSound *>(id);
00833                 ret = new CBackgroundSource(bgSound, spawn, cb, userParam);
00834         }
00835         else if (id->getSoundType() == CSound::SOUND_CONTEXT)
00836         {
00837                 // This is a context sound.
00838                 if (context != 0)
00839                 {
00840                         CContextSound   *ctxSound = static_cast<CContextSound   *>(id);
00841         //              nlassert(context != 0);
00842                         CSound                  *sound = ctxSound->getContextSound(*context);
00843                         if (sound != 0)
00844                         {
00845                                 ret = createSource(sound, spawn, cb, userParam);
00846                                 // Set the volume of the source according to the context volume
00847                                 if (ret != 0)
00848                                         ret->setGain(ret->getGain() * ctxSound->getGain());
00849                         }
00850                         else 
00851                                 ret = 0;
00852                 }
00853                 else
00854                         ret = 0;
00855         }
00856         else
00857         {
00858 //              nlassertex(false, ("Unknown sound class !"));
00859                 nlwarning("Unknow sound class : %u", id->getSoundType());
00860         }
00861 
00862 #if NL_PROFILE_MIXER
00863         _CreateTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
00864         _CreateCount++;
00865 #endif
00866 
00867         //nldebug( "AM: Source created" ); 
00868         return ret;                                             
00869 }
00870 
00871 
00872 // ******************************************************************
00873 
00874 USource                         *CAudioMixerUser::createSource( const std::string &name, bool spawn, TSpawnEndCallback cb, void *userParam, CSoundContext *context)
00875 {
00876         return createSource( getSoundId( name ), spawn, cb, userParam, context );
00877 }
00878 
00879 
00880 // ******************************************************************
00881 
00882 void                            CAudioMixerUser::removeSource( CSourceCommon *source )
00883 {
00884         nlassert( source != NULL );
00885         
00886         size_t n = _Sources.erase(source);
00887         nlassert(n == 1);
00888 }
00889 
00890 
00891 // ******************************************************************
00892 
00893 void                            CAudioMixerUser::selectEnvEffects( const std::string &tag )
00894 {
00895         nlassertex(false, ("Not implemented yet"));
00896 /*      // Select Env
00897         vector<CEnvEffect*>::iterator ipe;
00898         for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
00899         {
00900                 (*ipe)->selectEnv( tag );
00901         }
00902 
00903         // Compute
00904         CVector pos;
00905         _Listener.getPos( pos );
00906         computeEnvEffect( pos, true );
00907 */
00908 }
00909 
00910 
00911 // ******************************************************************
00912 
00913 /*
00914 void                            CAudioMixerUser::loadEnvEffects( const char *filename )
00915 {
00916         nlassert( filename != NULL );
00917         nlinfo( "Loading environmental effects from %s...", filename );
00918 
00919         // Unload previous env effects
00920         vector<CEnvEffect*>::iterator ipe;
00921         for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
00922         {
00923                 delete (*ipe);
00924         }
00925         _EnvEffects.clear();
00926 
00927         string str = CPath::lookup( filename, false );
00928 
00929         // Load env effects
00930         CIFile file;
00931         if ( !str.empty() && file.open(str) )
00932         {
00933                 uint32 n = CEnvEffect::load( _EnvEffects, file );
00934                 nldebug( "AM: Loaded %u environmental effects", n );
00935         }
00936         else
00937         {
00938                 nlwarning( "AM: Environmental effects file not found" );
00939         }
00940 }
00941 */
00942 
00943 // ******************************************************************
00944 
00945 uint32                  CAudioMixerUser::loadSampleBank(bool async, const std::string &filename, std::vector<std::string> *notfoundfiles )
00946 {
00947 //      nlassert( filename != NULL );
00948 
00949         string path = _SamplePath;
00950         path.append("/").append(filename);
00951 
00952         nldebug( "Loading samples from %s...", path.c_str() );
00953 
00954         CSampleBank* bank = CSampleBank::findSampleBank(path);
00955         if (bank == NULL)
00956         {
00957                 // create a new sample bank
00958 //_CrtCheckMemory();
00959                 bank = new CSampleBank(path, _SoundDriver);
00960 //_CrtCheckMemory();
00961         }
00962 
00963         try 
00964         {
00965                 bank->load(async);
00966         }
00967         catch (Exception& e)
00968         {
00969                 if (notfoundfiles) {
00970                         notfoundfiles->push_back(path);
00971                 }
00972                 string reason = e.what();
00973                 nlwarning( "AM: Failed to load the samples: %s", reason.c_str() );
00974         }
00975 
00976 
00977         return bank->countSamples();
00978 }
00979 
00980 bool CAudioMixerUser::unloadSampleBank( const std::string &filename)
00981 {
00982         string path = _SamplePath;
00983         path.append("/").append(filename);
00984 
00985         nldebug( "Unloading samples from %s...", path.c_str() );
00986         CSampleBank *pbank = CSampleBank::findSampleBank(path);
00987 
00988         if (pbank != NULL)
00989         {
00990                 // ok, the bank exist.
00991                 return pbank->unload();
00992         }
00993         else
00994                 return false;
00995 
00996 }
00997 
00998 // ******************************************************************
00999 
01000 void                    CAudioMixerUser::getSoundNames( std::vector<std::string>& names ) const
01001 {
01002         CSoundBank::instance()->getNames(names);
01003 }
01004 
01005 
01006 // ******************************************************************
01007 
01008 uint                    CAudioMixerUser::getPlayingSourcesNumber() const
01009 {
01010         return _PlayingSources;
01011 }
01012 
01013 // ******************************************************************
01014 
01015 uint                    CAudioMixerUser::getNumberAvailableTracks() const
01016 {
01017         return _FreeTracks.size();
01018 }
01019 
01020 
01021 // ******************************************************************
01022 
01023 string                  CAudioMixerUser::getSourcesStats() const
01024 {
01025         // TODO : rewrite log output
01026 
01027         string s;
01028         TSourceContainer::iterator ips;
01029         for ( ips=_Sources.begin(); ips!=_Sources.end(); ++ips )
01030         {
01031                 if ( (*ips)->isPlaying() )
01032                 {
01033 //                      char line [80];
01034 
01035 /*                      nlassert( (*ips)->getSound() && (*ips)->getSimpleSound()->getBuffer() );
01036                         smprintf( line, 80, "%s: %u%% %s %s",
01037                                           (*ips)->getSound()->getName().c_str(),
01038                                           (uint32)((*ips)->getGain()*100.0f),
01039                                           (*ips)->getBuffer()->isStereo()?"ST":"MO",
01040                                           PriToCStr[(*ips)->getPriority()] );
01041                         s += string(line) + "\n";
01042 */              }
01043         }
01044         return s;
01045 
01046 }
01047 
01048 // ******************************************************************
01049 /*
01050 void                    CAudioMixerUser::loadEnvSounds( const char *filename, UEnvSound **treeRoot )
01051 {
01052         nlassert( filename != NULL );
01053         nlinfo( "Loading environment sounds from %s...", filename );
01054 
01055         string str = CPath::lookup( filename, false );
01056 
01057         CIFile file;
01058         if ( !str.empty() && file.open( str ) )
01059         {
01060                 uint32 n = 0; //CEnvSoundUser::load( _EnvSounds, file );
01061                 nldebug( "AM: Loaded %u environment sounds", n );
01062         }
01063         else
01064         {
01065                 nlwarning( "AM: Environment sounds file not found: %s", filename );
01066         }
01067         if ( treeRoot != NULL )
01068         {
01069                 *treeRoot = _EnvSounds;
01070         }
01071 }
01072 */
01073 
01074 // ******************************************************************
01075 
01076 struct CompareSources : public binary_function<const CSimpleSource*, const CSimpleSource*, bool>
01077 {
01078         // Constructor
01079         CompareSources( const CVector &pos ) : _Pos(pos) {}
01080 
01081         // Operator()
01082         bool operator()( const CSimpleSource *s1, const CSimpleSource *s2 )
01083         {
01084                 if (s1->getPriority() < s2->getPriority())
01085                 {
01086                         return true;
01087                 }
01088                 else if (s1->getPriority() == s2->getPriority())
01089                 {
01090                         // Equal priority, test distances to the listener
01091                         const CVector &src1pos = s1->getPos();
01092                         const CVector &src2pos = s2->getPos();;
01093                         return ( (src1pos-_Pos).sqrnorm() < (src2pos-_Pos).sqrnorm() );
01094                 }
01095                 else
01096                 {
01097                         return false;
01098                 }
01099         }
01100 
01101         // Listener pos
01102         const CVector &_Pos;
01103 };
01104 
01105 
01106 // ******************************************************************
01107 uint32                  CAudioMixerUser::getLoadedSampleSize()
01108 {
01109         return CSampleBank::getTotalByteSize();
01110 }
01111 
01112 
01113 // ******************************************************************
01114 /*
01115 void                    CAudioMixerUser::redispatchSourcesToTrack()
01116 {
01117 */      // TODO : rewrite ?
01118 /*
01119         if ( _NbTracks == 0 )
01120         {
01121                 return;
01122         }
01123 
01124         _profile(( "AM: [%u]---------------------------------------------------------------", curTime() ));
01125         _profile(( "AM: Redispatching sources" ));
01126         
01127         const CVector &listenerpos = _Listener.getPos();
01128 
01129         // Get a copy of the sources set (we will modify it)
01130         static TSourceContainer sources_copy; 
01131         sources_copy = _PlayingSources;
01132         // FIXME: SWAPTEST
01133         //nlassert( sources_copy.size() >= _NbTracks );
01134 
01135         // Select the nbtracks "smallest" sources (the ones that have the higher priorities)
01136         TSourceContainer::iterator ips;
01137         static TSourceContainer selected_sources;
01138         uint32 i;
01139 
01140         selected_sources.clear();
01141 
01142         // Select the sources
01143 
01144         // Select the nbtracks "smallest" sources (the ones that have the higher priorities)
01145         // FIXME: SWAPTEST
01146         //for ( i=0; i!=_NbTracks; i++ )
01147         // TODO : optimize : this is a very BAD PERFORMANCE code
01148         while (!sources_copy.empty() && (selected_sources.size() < _NbTracks))
01149         {
01150                 ips = min_element( sources_copy.begin(), sources_copy.end(), CompareSources( listenerpos ) );
01151 
01152                 if ((*ips)->isPlaying())
01153                 {
01154                         selected_sources.insert( *ips );
01155                 }
01156 
01157                 sources_copy.erase( ips );
01158         }
01159 
01160         // Clear the current tracks where the sources are not selected anymore
01161         _profile(( "AM: Total sources: %u", _PlayingSources.size() ));
01162         _profile(( "AM: Selected sources: %u", selected_sources.size() ));
01163         for ( i=0; i!=_NbTracks; i++ )
01164         {
01165                 // FIXME: SWAPTEST
01166                 if ( _Tracks[i] && ! _Tracks[i]->isAvailable() )
01167                 {
01168                         // Optimization note: instead of searching the source in selected_sources, we could have
01169                         // set a boolean in the source object and tested it.
01170                         if ( (ips = selected_sources.find( _Tracks[i]->getSource() )) == selected_sources.end() )
01171                         {
01172                                 // There will be a new source in this track
01173                                 _profile(( "AM: TRACK: %p: REPLACED, SOURCE: %p", _Tracks[i], _Tracks[i]->getSource() ));
01174                                 if (_Tracks[i]->getSource() != 0)
01175                                 {
01176                                         _Tracks[i]->getSource()->leaveTrack();
01177 //                                      nlassert(_PlayingSources.find(_Tracks[i]->getSource()) != _PlayingSources.end());
01178                                         _PlayingSources.erase(_Tracks[i]->getSource());
01179                                 }
01180                         }
01181                         else
01182                         {
01183                                 // The track will remain unchanged
01184                                 selected_sources.erase( ips );
01185                                 _profile(( "AM: TRACK: %p: UNCHANGED, SOURCE: %p", _Tracks[i], _Tracks[i]->getSource() ));
01186                         }
01187                 }
01188                 else
01189                 {
01190                         _profile(( "AM: TRACK: %p: FREE", _Tracks[i] ));
01191                 }
01192         }
01193 
01194         if (!selected_sources.empty())
01195         {
01196                 // Now, only the sources to add into the tracks remain in selected_sources
01197                 CTrack *track [MAX_TRACKS]; // a little bit more than needed (avoiding a "new")
01198                 getFreeTracks( selected_sources.size(), track );
01199 
01200                 _profile(( "AM: Remaining sources: %u", selected_sources.size() ));
01201 
01202                 for (i=0, ips=selected_sources.begin(); ips!=selected_sources.end(); ++ips )
01203                 {
01204                         // FIXME: SWAPTEST
01205                         (*ips)->enterTrack( track[i] );
01206 //                      nlassert(_PlayingSources.find(*ips) == _PlayingSources.end());
01207 //                      _PlayingSources.insert(*ips);
01208         //              _profile(( "AM: TRACK: %p: ASSIGNED, SOURCE: %p", track[i], track[i]->getSource() ));
01209                         _profile(( "AM: TRACK: %p: ASSIGNED, SOURCE: %p", track[i], *ips ));
01210                         i++;
01211                 }
01212         }
01213 */
01214 //}
01215 
01216 void CAudioMixerUser::setListenerPos (const NLMISC::CVector &pos)
01217 {
01218         _Listener.setPos(pos);
01219         _BackgroundSoundManager->setListenerPosition(pos);
01220 }
01221 
01222 NLMISC_COMMAND (displaySoundInfo, "Display information about the audio mixer", "")
01223 {
01224         if(args.size() != 0) return false;
01225 
01226         if (CAudioMixerUser::instance() == NULL)
01227         {
01228                 log.displayNL ("No audio mixer available");
01229                 return true;
01230         }
01231 
01232         log.displayNL ("%d tracks, MAX_TRACKS = %d, contains:", CAudioMixerUser::instance()->_NbTracks, MAX_TRACKS);
01233 
01234         for (uint i = 0; i < CAudioMixerUser::instance()->_NbTracks; i++)
01235         {
01236                 if (CAudioMixerUser::instance()->_Tracks[i] == NULL)
01237                 {
01238                         log.displayNL ("Track %d is NULL", i);
01239                 }
01240                 else
01241                 {
01242                         log.displayNL ("Track %d %s available and %s playing.", i, (CAudioMixerUser::instance()->_Tracks[i]->isAvailable()?"is":"is not"), (CAudioMixerUser::instance()->_Tracks[i]->isPlaying()?"is":"is not"));
01243                         if (CAudioMixerUser::instance()->_Tracks[i]->getSource() == NULL)
01244                         {
01245                                 log.displayNL ("    CUserSource is NULL");
01246                         }
01247                         else
01248                         {
01249                                 const CVector &pos = CAudioMixerUser::instance()->_Tracks[i]->getSource()->getPos();
01250                                 string bufname;
01251                                 if (CAudioMixerUser::instance()->_Tracks[i]->getSource()->getBuffer())
01252                                         bufname = CAudioMixerUser::instance()->_Tracks[i]->getSource()->getBuffer()->getName();
01253                                 log.displayNL ("    CUserSource is id %d buffer name '%s' pos %f %f %f", CAudioMixerUser::instance()->_Tracks[i]->getSource()->getSound(), bufname.c_str(), pos.x, pos.y, pos.z);
01254                         }
01255                 }
01256         }
01257 
01258         return true;
01259 }
01260 
01261 void CAudioMixerUser::registerBufferAssoc(CSound *sound, IBuffer *buffer)
01262 {
01263         _BufferToSources[buffer].push_back(sound);
01264 }
01265 
01266 void CAudioMixerUser::unregisterBufferAssoc(CSound *sound, IBuffer *buffer)
01267 {
01268         TBufferToSourceContainer::iterator it(_BufferToSources.find(buffer));
01269         if (it != _BufferToSources.end())
01270         {
01271                 std::vector<CSound*>::iterator first(it->second.begin()), last(it->second.end());
01272 
01273                 for (; first != last; ++first)
01274                 {
01275                         if (*first == sound)
01276                         {
01277                                 it->second.erase(first);
01278                                 break;
01279                         }
01280                 }
01281         }
01282 }
01283 
01284 
01286 void CAudioMixerUser::registerUpdate(CAudioMixerUser::IMixerUpdate *pmixerUpdate)
01287 {
01288         _UpdateEventList.push_back(make_pair(pmixerUpdate, true));
01289 }
01291 void CAudioMixerUser::unregisterUpdate(CAudioMixerUser::IMixerUpdate *pmixerUpdate)
01292 {
01293         _UpdateEventList.push_back(make_pair(pmixerUpdate, false));
01294 }
01295 
01297 void CAudioMixerUser::addEvent( CAudioMixerUser::IMixerEvent *pmixerEvent, const NLMISC::TTime &date)
01298 {
01299 //      nldebug("Adding event %p", pmixerEvent);
01300         _EventListUpdate.push_back(make_pair(date, pmixerEvent));
01301 }
01303 void CAudioMixerUser::removeEvents( CAudioMixerUser::IMixerEvent *pmixerEvent)
01304 {
01305 //      nldebug("Removing event %p", pmixerEvent);
01306         // store the pointer fot future removal.
01307         _EventListUpdate.push_back(make_pair(0, pmixerEvent));
01308 }
01309 
01310 void CAudioMixerUser::setBackgroundFlags(const TBackgroundFlags &backgroundFlags)
01311 {
01312         _BackgroundSoundManager->setBackgroundFlags(backgroundFlags);
01313 }
01314 
01315 void CAudioMixerUser::loadBackgroundSoundFromRegion (const NLLIGO::CPrimRegion &region)
01316 {
01317         _BackgroundSoundManager->loadSoundsFromRegion(region);
01318 }
01319 
01320 void CAudioMixerUser::loadBackgroundEffectsFromRegion (const NLLIGO::CPrimRegion &region)
01321 {
01322         _BackgroundSoundManager->loadEffecsFromRegion(region);
01323 }
01324 void CAudioMixerUser::loadBackgroundSamplesFromRegion (const NLLIGO::CPrimRegion &region)
01325 {
01326         _BackgroundSoundManager->loadSamplesFromRegion(region);
01327 }
01328 
01329 
01330 void CAudioMixerUser::playBackgroundSound () 
01331 { 
01332         _BackgroundSoundManager->play (); 
01333 }
01334 
01335 void CAudioMixerUser::stopBackgroundSound () 
01336 { 
01337         _BackgroundSoundManager->stop (); 
01338 }
01339 
01340 void CAudioMixerUser::loadBackgroundSound (const std::string &continent) 
01341 { 
01342         _BackgroundSoundManager->load (continent); 
01343 }
01344 
01345 } // NLSOUND