Fix "no remove aqua speed" bug when player leaves the water
[ryzomcore.git] / nel / src / sound / audio_mixer_user.cpp
blobd6098e6a9c6ea0dc0e1d49dca787e71b849fc75b
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2012-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "stdsound.h"
23 #include "nel/misc/hierarchical_timer.h"
24 #include "nel/misc/progress_callback.h"
25 #include "nel/misc/big_file.h"
26 #include "nel/misc/time_nl.h"
27 #include "nel/misc/command.h"
28 #include "nel/misc/file.h"
29 #include "nel/misc/path.h"
31 #include "nel/georges/u_form_loader.h"
32 #include "nel/georges/u_form_elm.h"
33 #include "nel/georges/load_form.h"
34 #include "nel/georges/u_form.h"
36 #include "nel/3d/scene_user.h"
37 #include "nel/sound/driver/sound_driver.h"
38 #include "nel/sound/driver/buffer.h"
39 #include "nel/sound/driver/effect.h"
41 #include "nel/sound/background_sound_manager.h"
42 #include "nel/sound/music_sound_manager.h"
43 #include "nel/sound/background_source.h"
44 #include "nel/sound/clustered_sound.h"
45 #include "nel/sound/complex_source.h"
46 #include "nel/sound/simple_source.h"
47 #include "nel/sound/complex_sound.h"
48 #include "nel/sound/context_sound.h"
49 #include "nel/sound/music_source.h"
50 #include "nel/sound/stream_source.h"
51 #include "nel/sound/stream_file_source.h"
52 #include "nel/sound/simple_sound.h"
53 #include "nel/sound/music_sound.h"
54 #include "nel/sound/stream_sound.h"
55 #include "nel/sound/sample_bank_manager.h"
56 #include "nel/sound/sample_bank.h"
57 #include "nel/sound/sound_bank.h"
58 #include "nel/sound/group_controller.h"
59 #include "nel/sound/containers.h"
60 #include "nel/sound/audio_decoder.h"
62 #ifdef DEBUG_NEW
63 #define new DEBUG_NEW
64 #endif
66 using namespace std;
67 using namespace NLMISC;
69 namespace NLSOUND {
71 #define NL_TRACE_MIXER 0
73 #if NL_TRACE_MIXER
74 #define _profile(_a) nldebug ## _a
75 #else
76 #define _profile(_a)
77 #endif
79 // Return the priority cstring (debug info)
80 const char *PriToCStr [NbSoundPriorities] = { "XH", "HI", "MD", "LO" };
83 // ******************************************************************
85 const char *getPriorityStr( TSoundPriority p )
87 nlassert( ((uint)p) < NbSoundPriorities );
88 return PriToCStr[p];
92 // ******************************************************************
94 UAudioMixer *UAudioMixer::createAudioMixer()
96 return new CAudioMixerUser();
100 // ******************************************************************
102 CAudioMixerUser::CAudioMixerUser() : _AutoLoadSample(false),
103 _UseADPCM(true),
104 _SoundDriver(NULL),
105 _SoundBank(NULL),
106 _SampleBankManager(NULL),
107 _BackgroundSoundManager(NULL),
108 _ClusteredSound(0),
109 _ReverbEffect(NULL),
110 _ListenPosition(CVector::Null),
111 _BackgroundMusicManager(NULL),
112 _PlayingSources(0),
113 _PlayingSourcesMuted(0),
114 _Leaving(false)
116 #if NL_PROFILE_MIXER
117 _UpdateTime = 0.0;
118 _CreateTime = 0.0;
119 _UpdateCount = 0;
120 _CreateCount = 0;
121 #endif
123 // init the filter names and short names
124 for (uint i=0; i<TBackgroundFlags::NB_BACKGROUND_FLAGS; ++i)
126 char tmp[1024];
127 sprintf(tmp, "Filter%2u", i);
128 _BackgroundFilterNames[i] = tmp;
129 sprintf(tmp, "%u", i);
130 _BackgroundFilterShortNames[i] = tmp;
135 // ******************************************************************
137 CAudioMixerUser::~CAudioMixerUser()
139 //nldebug( "AM: Releasing..." );
141 delete _ClusteredSound; _ClusteredSound = NULL;
142 delete _BackgroundSoundManager; _BackgroundSoundManager = NULL;
143 delete _BackgroundMusicManager; _BackgroundMusicManager = NULL;
145 reset();
147 _Leaving = true;
149 // Release all the SampleBanks
150 delete _SampleBankManager; _SampleBankManager = NULL;
151 // Release the sound bank
152 delete _SoundBank; _SoundBank = NULL;
154 // Release music channels
155 for (uint i = 0; i < _NbMusicChannelFaders; ++i)
156 _MusicChannelFaders[i].release();
158 // Detete tracks
159 for (uint i = 0; i < _Tracks.size(); ++i)
161 delete _Tracks[i];
162 _Tracks[i] = NULL;
165 // Reverb effect
166 delete _ReverbEffect; _ReverbEffect = NULL;
168 // Sound driver
169 delete _SoundDriver; _SoundDriver = NULL;
171 //nldebug( "AM: Released" );
175 void CAudioMixerUser::initClusteredSound(NL3D::UScene *uscene, float minGain, float maxDistance, float portalInterpolate)
177 NL3D::CScene *scene = 0;
178 if (uscene) scene = &(static_cast<NL3D::CSceneUser*>(uscene)->getScene());
180 initClusteredSound(scene, minGain, maxDistance, portalInterpolate);
183 void CAudioMixerUser::initClusteredSound(NL3D::CScene *scene, float minGain, float maxDistance, float portalInterpolate = 20.0f)
185 if (!_ClusteredSound) _ClusteredSound = new CClusteredSound();
187 _ClusteredSound->init(scene, portalInterpolate, maxDistance, minGain);
191 void CAudioMixerUser::setPriorityReserve(TSoundPriority priorityChannel, uint reserve)
193 _PriorityReserve[priorityChannel] = (uint32)min((uint)_Tracks.size(), reserve);
196 void CAudioMixerUser::setLowWaterMark(uint value)
198 _LowWaterMark = (uint32)min((uint)_Tracks.size(), value);
202 // ******************************************************************
205 void CAudioMixerUser::writeProfile(std::string& out)
207 // compute number of muted source
208 /* uint nb = 0;
210 TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
211 for (; first != last; ++first)
213 CSimpleSource *psu = *first;
214 if (psu->getTrack() == NULL)
216 ++nb;
220 hash_set<CSimpleSource*>::const_iterator ips;
221 for ( ips=_Sources.begin(); ips!=_Sources.end(); ++ips )
223 CSimpleSource *psu = *ips;
224 if (psu->getTrack() == NULL)
226 ++nb;
230 out += "Sound mixer: \n";
231 out += "\tPlaying sources: " + toString (getPlayingSourcesCount()) + " \n";
232 out += "\tPlaying simple sources: " + toString(countPlayingSimpleSources()) + " / " + toString(countSimpleSources()) + " \n";
233 out += "\tAvailable tracks: " + toString (getAvailableTracksCount()) + " \n";
234 out += "\tUsed tracks: " + toString (getUsedTracksCount()) + " \n";
235 // out << "\tMuted sources: " << nb << " \n";
236 // out << "\tMuted sources: " << max(0, sint(_PlayingSources.size())-sint(_NbTracks)) << " \n";
237 // out << "\tMuted sources: " << max(0, sint(_PlayingSources)-sint(_NbTracks)) << " \n";
238 out += "\tMuted sources: " + toString ((int)_PlayingSourcesMuted) + "\n";
239 out += "\tSources waiting for play: " + toString (_SourceWaitingForPlay.size()) + " \n";
240 out += "\tHighestPri: " + toString ((int)_ReserveUsage[HighestPri]) + " / " + toString ((int)_PriorityReserve[HighestPri]) + " \n";
241 out += "\tHighPri: " + toString ((int)_ReserveUsage[HighPri]) + " / " + toString ((int)_PriorityReserve[HighPri]) + "\n";
242 out += "\tMidPri: " + toString ((int)_ReserveUsage[MidPri]) + " / " + toString ((int)_PriorityReserve[MidPri]) + " \n";
243 out += "\tLowPri: " + toString ((int)_ReserveUsage[LowPri]) + " / " + toString ((int)_PriorityReserve[LowPri]) + " \n";
244 out += "\tFreeTracks: " + toString (_FreeTracks.size()) + " / " + toString (_Tracks.size()) + " \n";
245 out += "\tAverage update time: " + toString (1000.0 * _UpdateTime / iavoid0(_UpdateCount)) + " msec\n";
246 out += "\tAverage create time: " + toString (1000.0 * _CreateTime / iavoid0(_CreateCount)) + " msec\n";
247 out += "\tEstimated CPU: " + toString ((100.0 * 1000.0 * (_UpdateTime + _CreateTime) / curTime())) + "%%\n";
249 if (_SoundDriver)
251 out += toString ("Sound driver: \n");
252 _SoundDriver->writeProfile(out);
256 // ******************************************************************
258 void CAudioMixerUser::addSourceWaitingForPlay(CSourceCommon *source)
260 _SourceWaitingForPlay.push_back(source);
263 // ******************************************************************
265 void CAudioMixerUser::removeSourceWaitingForPlay(CSourceCommon *source)
267 std::list<CSourceCommon *>::iterator it = find(_SourceWaitingForPlay.begin(), _SourceWaitingForPlay.end(), source);
268 if (it != _SourceWaitingForPlay.end())
270 _SourceWaitingForPlay.erase(it);
274 // ******************************************************************
276 void CAudioMixerUser::reset()
278 _Leaving = true;
280 _SourceWaitingForPlay.clear();
282 for (uint i = 0; i < _NbMusicChannelFaders; ++i)
283 _MusicChannelFaders[i].reset();
285 // Stop tracks
286 uint i;
287 for (i = 0; i < _Tracks.size(); ++i)
289 if (_Tracks[i])
291 CSourceCommon* src = _Tracks[i]->getLogicalSource();
293 if (src && src->isPlaying())
295 src->stop();
300 // Do a first multipass travesal to stop all playing source
301 // We can't do the work in 1 pass because stoping a source can lead to
302 // destruction of sub source, invalidating the iterators !
303 bool again;
306 again = false;
307 TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
308 for (; first != last; ++first)
310 if ((*first)->isPlaying())
312 (*first)->stop();
313 again = true;
314 break;
318 } while (again);
320 // Sources
321 while (!_Sources.empty())
323 //removeSource( _Sources.begin(), true ); // 3D sources, the envsounds were removed above
324 CSourceCommon *source = *(_Sources.begin());
325 if (source->isPlaying())
326 source->stop();
327 else
328 delete source;
331 _Leaving = false;
334 void CAudioMixerUser::setPackedSheetOption(const std::string &path, bool update)
336 _PackedSheetPath = CPath::standardizePath(path, true);
337 _UpdatePackedSheet = update;
341 void CAudioMixerUser::setSamplePath(const std::string& path)
343 _SampleWavPath = CPath::standardizePath(path, true);
344 _SampleBankPath = _SampleBankPath;
347 void CAudioMixerUser::setSamplePaths(const std::string &wavAssetPath, const std::string &bankBuildPath)
349 _SampleWavPath = wavAssetPath;
350 _SampleBankPath = bankBuildPath;
354 // ******************************************************************
356 void CAudioMixerUser::init(uint maxTrack, bool useEax, bool useADPCM, IProgressCallback *progressCallBack, bool autoLoadSample, TDriver driverType, bool forceSoftwareBuffer, bool manualRolloff)
358 nlctassert(NumDrivers == UAudioMixer::TDriver(ISoundDriver::NumDrivers));
359 initDriver(NLSOUND::ISoundDriver::getDriverName((ISoundDriver::TDriver)driverType));
360 CInitInfo initInfo;
361 initInfo.MaxTrack = maxTrack;
362 initInfo.EnableReverb = useEax; // :)
363 initInfo.EnableOccludeObstruct = useEax; // :)
364 initInfo.UseADPCM = useADPCM;
365 initInfo.ForceSoftware = forceSoftwareBuffer;
366 initInfo.ManualRolloff = manualRolloff;
367 initInfo.AutoLoadSample = autoLoadSample;
368 initDevice("", initInfo, progressCallBack);
371 /// Initialize the NeL Sound Driver with given driverName.
372 void CAudioMixerUser::initDriver(const std::string &driverName)
374 std::string dn = NLMISC::toLowerAscii(driverName);
375 nldebug("AM: Init Driver '%s' ('%s')...", driverName.c_str(), dn.c_str());
377 ISoundDriver::TDriver driverType;
378 if (dn == "auto") driverType = ISoundDriver::DriverAuto;
379 else if (dn == "fmod") driverType = ISoundDriver::DriverFMod;
380 else if (dn == "dsound") driverType = ISoundDriver::DriverDSound;
381 else if (dn == "openal") driverType = ISoundDriver::DriverOpenAl;
382 else if (dn == "xaudio2") driverType = ISoundDriver::DriverXAudio2;
383 else
385 driverType = ISoundDriver::DriverAuto;
386 nlwarning("AM: driverName value '%s' ('%s') is invalid.", driverName.c_str(), dn.c_str());
391 // create the wanted driver
392 nlctassert(NumDrivers == UAudioMixer::TDriver(ISoundDriver::NumDrivers));
393 _SoundDriver = ISoundDriver::createDriver(this, driverType);
394 nlassert(_SoundDriver);
396 catch (const ESoundDriver &e)
398 nlwarning(e.what());
399 delete _SoundDriver; _SoundDriver = NULL;
400 throw;
402 catch (...)
404 delete _SoundDriver; _SoundDriver = NULL;
405 throw;
409 /// Get the available devices on the loaded driver.
410 void CAudioMixerUser::getDevices(std::vector<std::string> &devices)
412 if (!_SoundDriver)
414 nlwarning("AM: You must call 'initDriver' before calling 'getDevices'");
415 return;
418 _SoundDriver->getDevices(devices);
421 /// Initialize the selected device on the currently initialized driver. Leave deviceName empty to select the default device.
422 void CAudioMixerUser::initDevice(const std::string &deviceName, const CInitInfo &initInfo, NLMISC::IProgressCallback *progressCallback)
424 if (!_SoundDriver)
426 nlwarning("AM: You must call 'initDriver' before calling 'initDevice'");
427 return;
430 nldebug( "AM: Init Device..." );
432 _profile(( "AM: ---------------------------------------------------------------" ));
434 _UseEax = initInfo.EnableReverb || initInfo.EnableOccludeObstruct; // [TODO KAETEMI: Fixme.]
435 _UseADPCM = initInfo.UseADPCM;
436 _AutoLoadSample = initInfo.AutoLoadSample;
437 bool manualRolloff = initInfo.ManualRolloff;
438 bool forceSoftware = initInfo.ForceSoftware;
439 uint maxTrack = initInfo.MaxTrack;
441 // Init sound driver
444 _profile(( "AM: DRIVER: %s", _SoundDriver->getDllName().c_str() ));
446 // the options to init the driver
447 sint driverOptions = ISoundDriver::OptionHasBufferStreaming;
448 if (_UseEax) driverOptions |= ISoundDriver::OptionEnvironmentEffects;
449 if (_UseADPCM) driverOptions |= ISoundDriver::OptionAllowADPCM;
450 if (forceSoftware) driverOptions |= ISoundDriver::OptionSoftwareBuffer;
451 if (manualRolloff) driverOptions |= ISoundDriver::OptionManualRolloff;
452 if (_AutoLoadSample) driverOptions |= ISoundDriver::OptionLocalBufferCopy;
454 // init the driver with selected device and needed options
455 _SoundDriver->initDevice(deviceName, (ISoundDriver::TSoundOptions)driverOptions);
457 // verify the options, OptionHasBufferStreaming not checked
458 if (_UseEax && !_SoundDriver->getOption(ISoundDriver::OptionEnvironmentEffects))
460 nlwarning("AM: OptionEnvironmentEffects not available, _UseEax = false");
461 _UseEax = false;
463 if (_UseADPCM && !_SoundDriver->getOption(ISoundDriver::OptionAllowADPCM))
465 nlwarning("AM: OptionAllowADPCM not available, _UseADPCM = false");
466 _UseADPCM = false;
468 if (_AutoLoadSample && !_SoundDriver->getOption(ISoundDriver::OptionLocalBufferCopy))
470 nlwarning("AM: OptionLocalBufferCopy not available, _AutoLoadSample = false");
471 _AutoLoadSample = false;
473 if (forceSoftware && !_SoundDriver->getOption(ISoundDriver::OptionSoftwareBuffer))
475 nlwarning("AM: OptionSoftwareBuffer not available, forceSoftwareBuffer = false");
476 forceSoftware = false; // not really needed, but set anyway in case this is still used later in this function
478 if (manualRolloff && !_SoundDriver->getOption(ISoundDriver::OptionManualRolloff))
480 nlwarning("AM: OptionManualRolloff not available, manualRolloff = false");
481 manualRolloff = false; // not really needed, but set anyway in case this is still used later in this function
484 catch (const ESoundDriver &e)
486 nlwarning(e.what());
487 delete _SoundDriver; _SoundDriver = NULL;
488 throw;
490 catch (...)
492 delete _SoundDriver; _SoundDriver = NULL;
493 throw;
496 uint i;
498 // Init registrable classes
499 static bool initialized = false;
500 if (!initialized)
502 initialized = true;
505 // Init listener
506 _Listener.init(_SoundDriver);
508 // Init environment reverb effects
509 if (_UseEax)
511 _ReverbEffect = _SoundDriver->createReverbEffect();
513 if (!_ReverbEffect)
514 { _UseEax = false; }
515 else // createEffect succeeded, add environments
517 nldebug("AM: Reverb OK");
518 // todo: loading this data from a file or something would be neat
519 // also: check if this should go into clustered_sound (background_sound_manager also uses this stuff at one point, though)
520 // effect presets (based on I3DL2 specification/guidelines, see 3dl2help.h)
521 addEnvironment("GENERIC", IReverbEffect::CEnvironment( -10.00f, -1.00f, 1.49f,0.83f, -26.02f,0.007f, 2.00f,0.011f,100.0f,100.0f));
522 addEnvironment("PADDEDCELL", IReverbEffect::CEnvironment( -10.00f,-60.00f, 0.17f,0.10f, -12.04f,0.001f, 2.07f,0.002f,100.0f,100.0f));
523 addEnvironment("ROOM", IReverbEffect::CEnvironment( -10.00f, -4.54f, 0.40f,0.83f, -16.46f,0.002f, 0.53f,0.003f,100.0f,100.0f));
524 addEnvironment("BATHROOM", IReverbEffect::CEnvironment( -10.00f,-12.00f, 1.49f,0.54f, -3.70f,0.007f, 10.30f,0.011f,100.0f, 60.0f));
525 addEnvironment("LIVINGROOM", IReverbEffect::CEnvironment( -10.00f,-60.00f, 0.50f,0.10f, -13.76f,0.003f, -11.04f,0.004f,100.0f,100.0f));
526 addEnvironment("STONEROOM", IReverbEffect::CEnvironment( -10.00f, -3.00f, 2.31f,0.64f, -7.11f,0.012f, 0.83f,0.017f,100.0f,100.0f));
527 addEnvironment("AUDITORIUM", IReverbEffect::CEnvironment( -10.00f, -4.76f, 4.32f,0.59f, -7.89f,0.020f, -2.89f,0.030f,100.0f,100.0f));
528 addEnvironment("CONCERTHALL", IReverbEffect::CEnvironment( -10.00f, -5.00f, 3.92f,0.70f, -12.30f,0.020f, -0.02f,0.029f,100.0f,100.0f));
529 addEnvironment("CAVE", IReverbEffect::CEnvironment( -10.00f, 0.00f, 2.91f,1.30f, -6.02f,0.015f, -3.02f,0.022f,100.0f,100.0f));
530 addEnvironment("ARENA", IReverbEffect::CEnvironment( -10.00f, -6.98f, 7.24f,0.33f, -11.66f,0.020f, 0.16f,0.030f,100.0f,100.0f));
531 addEnvironment("HANGAR", IReverbEffect::CEnvironment( -10.00f,-10.00f,10.05f,0.23f, -6.02f,0.020f, 1.98f,0.030f,100.0f,100.0f));
532 addEnvironment("CARPETEDHALLWAY", IReverbEffect::CEnvironment( -10.00f,-40.00f, 0.30f,0.10f, -18.31f,0.002f, -16.30f,0.030f,100.0f,100.0f));
533 addEnvironment("HALLWAY", IReverbEffect::CEnvironment( -10.00f, -3.00f, 1.49f,0.59f, -12.19f,0.007f, 4.41f,0.011f,100.0f,100.0f));
534 addEnvironment("STONECORRIDOR", IReverbEffect::CEnvironment( -10.00f, -2.37f, 2.70f,0.79f, -12.14f,0.013f, 3.95f,0.020f,100.0f,100.0f));
535 addEnvironment("ALLEY", IReverbEffect::CEnvironment( -10.00f, -2.70f, 1.49f,0.86f, -12.04f,0.007f, -0.04f,0.011f,100.0f,100.0f));
536 addEnvironment("FOREST", IReverbEffect::CEnvironment( -10.00f,-33.00f, 1.49f,0.54f, -25.60f,0.162f, -6.13f,0.088f, 79.0f,100.0f));
537 addEnvironment("CITY", IReverbEffect::CEnvironment( -10.00f, -8.00f, 1.49f,0.67f, -22.73f,0.007f, -22.17f,0.011f, 50.0f,100.0f));
538 addEnvironment("MOUNTAINS", IReverbEffect::CEnvironment( -10.00f,-25.00f, 1.49f,0.21f, -27.80f,0.300f, -20.14f,0.100f, 27.0f,100.0f));
539 addEnvironment("QUARRY", IReverbEffect::CEnvironment( -10.00f,-10.00f, 1.49f,0.83f,-100.00f,0.061f, 5.00f,0.025f,100.0f,100.0f));
540 addEnvironment("PLAIN", IReverbEffect::CEnvironment( -10.00f,-20.00f, 1.49f,0.50f, -24.66f,0.179f, -25.14f,0.100f, 21.0f,100.0f));
541 addEnvironment("PARKINGLOT", IReverbEffect::CEnvironment( -10.00f, 0.00f, 1.65f,1.50f, -13.63f,0.008f, -11.53f,0.012f,100.0f,100.0f));
542 addEnvironment("SEWERPIPE", IReverbEffect::CEnvironment( -10.00f,-10.00f, 2.81f,0.14f, 4.29f,0.014f, 6.48f,0.021f, 80.0f, 60.0f));
543 addEnvironment("UNDERWATER", IReverbEffect::CEnvironment( -10.00f,-40.00f, 1.49f,0.10f, -4.49f,0.007f, 17.00f,0.011f,100.0f,100.0f));
544 addEnvironment("SMALLROOM", IReverbEffect::CEnvironment( -10.00f, -6.00f, 1.10f,0.83f, -4.00f,0.005f, 5.00f,0.010f,100.0f,100.0f));
545 addEnvironment("MEDIUMROOM", IReverbEffect::CEnvironment( -10.00f, -6.00f, 1.30f,0.83f, -10.00f,0.010f, -2.00f,0.020f,100.0f,100.0f));
546 addEnvironment("LARGEROOM", IReverbEffect::CEnvironment( -10.00f, -6.00f, 1.50f,0.83f, -16.00f,0.020f, -10.00f,0.040f,100.0f,100.0f));
547 addEnvironment("MEDIUMHALL", IReverbEffect::CEnvironment( -10.00f, -6.00f, 1.80f,0.70f, -13.00f,0.015f, -8.00f,0.030f,100.0f,100.0f));
548 addEnvironment("LARGEHALL", IReverbEffect::CEnvironment( -10.00f, -6.00f, 1.80f,0.70f, -20.00f,0.030f, -14.00f,0.060f,100.0f,100.0f));
549 addEnvironment("PLATE", IReverbEffect::CEnvironment( -10.00f, -2.00f, 1.30f,0.90f, 0.00f,0.002f, 0.00f,0.010f,100.0f, 75.0f));
550 // these are the default environment settings in case no environment data is available (you'll hear this one)
551 _DefaultEnvironment = getEnvironment("PLAIN");
552 _DefaultRoomSize = 7.5f;
553 // note: 'no fx' generally does not use the default room size
554 _Environments[CStringMapper::map("no fx")] = _DefaultEnvironment;
555 // set the default environment now
556 _ReverbEffect->setEnvironment(_DefaultEnvironment, _DefaultRoomSize);
560 // Init tracks (physical sources)
561 changeMaxTrack(maxTrack);
563 // Init the reserve stuff.
564 _LowWaterMark = 0;
565 for (i=0; i<NbSoundPriorities; ++i)
567 _PriorityReserve[i] = (uint32)_Tracks.size();
568 _ReserveUsage[i] = 0;
571 _StartTime = CTime::getLocalTime();
573 // if needed (update == true), build the sample bank list
574 if (_UpdatePackedSheet)
576 buildSampleBankList();
579 // Init music channels
580 for (i = 0; i < _NbMusicChannelFaders; ++i)
581 _MusicChannelFaders[i].init(_SoundDriver);
583 // Create the background sound manager.
584 _BackgroundSoundManager = new CBackgroundSoundManager();
586 // Create the background music manager
587 _BackgroundMusicManager = new CMusicSoundManager();
589 // Load the sound bank
590 CSoundBank *soundBank = new CSoundBank();
591 _SoundBank = soundBank;
592 soundBank->load(getPackedSheetPath(), getPackedSheetUpdate());
593 nlinfo("AM: Initialized audio mixer with %u voices, %s and %s.",
594 (uint32)_Tracks.size(),
595 _UseEax ? "with EAX support" : "WITHOUT EAX",
596 _UseADPCM ? "with ADPCM sample source" : "with 16 bits PCM sample source");
598 // Init the sample bank manager
599 CSampleBankManager *sampleBankManager = new CSampleBankManager(this);
600 _SampleBankManager = sampleBankManager;
602 // try to load default configuration from george sheet
604 NLGEORGES::UFormLoader *formLoader = NULL;
608 std::string mixerConfigFile = NLMISC::CPath::lookup("default.mixer_config", false);
609 if (!mixerConfigFile.empty())
611 formLoader = NLGEORGES::UFormLoader::createLoader();
613 NLMISC::CSmartPtr<NLGEORGES::UForm> form;
614 form = formLoader->loadForm(mixerConfigFile.c_str());
616 NLGEORGES::UFormElm &root = form->getRootNode();
618 // read track reserve
619 uint32 highestRes, highRes, midRes, lowRes;
620 root.getValueByName(highestRes, ".HighestPriorityReserve");
621 root.getValueByName(highRes, ".HighPriorityReserve");
622 root.getValueByName(midRes, ".MidPriorityReserve");
623 root.getValueByName(lowRes, ".LowPriorityReserve");
625 setPriorityReserve(HighestPri, highestRes);
626 setPriorityReserve(HighPri, highRes);
627 setPriorityReserve(MidPri, midRes);
628 setPriorityReserve(LowPri, lowRes);
630 uint32 lowWater;
631 root.getValueByName(lowWater, ".LowWaterMark");
632 setLowWaterMark(lowWater);
634 // preload sample bank
635 NLGEORGES::UFormElm *sampleBanks;
636 root.getNodeByName(&sampleBanks, ".SampleBanks");
638 if (sampleBanks != NULL)
640 uint size;
641 sampleBanks->getArraySize(size);
642 for (uint i=0; i<size; ++i)
644 std::string name;
645 sampleBanks->getArrayValue(name, i);
647 if (!name.empty())
648 loadSampleBank(false, name);
650 if (progressCallback != 0)
651 progressCallback->progress(float(i) / size);
655 // configure background flags names, fades and state
656 NLGEORGES::UFormElm *bgFlags;
657 root.getNodeByName(&bgFlags, ".BackgroundFlags");
658 if (bgFlags != NULL)
660 TBackgroundFlags flags;
661 TBackgroundFilterFades fades;
663 uint size;
664 bgFlags->getArraySize(size);
665 uint i;
666 for (i=0; i<min(size, (uint)(TBackgroundFlags::NB_BACKGROUND_FLAGS)); ++i)
668 NLGEORGES::UFormElm *flag;
669 bgFlags->getArrayNode(&flag, i);
671 flag->getValueByName(flags.Flags[i], ".InitialState");
673 uint32 fadeIn, fadeOut;
674 flag->getValueByName(fadeIn, ".FadeIn");
675 flag->getValueByName(fadeOut, ".FadeOut");
677 fades.FadeIns[i] = fadeIn;
678 fades.FadeOuts[i] = fadeOut;
680 flag->getValueByName(_BackgroundFilterNames[i], ".Name");
681 flag->getValueByName(_BackgroundFilterShortNames[i], ".ShortName");
683 for (; i< TBackgroundFlags::NB_BACKGROUND_FLAGS; ++i)
685 uint32 fadeIn, fadeOut;
686 NLGEORGES::UFormElm::TWhereIsValue where = NLGEORGES::UFormElm::ValueDefaultDfn;
687 root.getValueByName(fadeIn, ".BackgroundFlags[0].FadeIn", NLGEORGES::UFormElm::Eval, &where);
688 root.getValueByName(fadeOut, ".BackgroundFlags[0].FadeOut", NLGEORGES::UFormElm::Eval, &where);
689 root.getValueByName(flags.Flags[i], ".BackgroundFlags[0].InitialState", NLGEORGES::UFormElm::Eval, &where);
691 fades.FadeIns[i] = fadeIn;
692 fades.FadeOuts[i] = fadeOut;
694 setBackgroundFilterFades(fades);
695 setBackgroundFlags(flags);
698 form = NULL;
699 NLGEORGES::UFormLoader::releaseLoader(formLoader);
702 catch(...)
704 NLGEORGES::UFormLoader::releaseLoader(formLoader);
707 // init the user var bindings
708 initUserVar();
711 /// Build a sample bank from a directory containing .wav files, and return the path to the written file.
712 std::string UAudioMixer::buildSampleBank(const std::string &wavDir, const std::string &bankDir, const std::string &bankName)
714 vector<string> sampleList;
715 CPath::getPathContent(wavDir, false, false, true, sampleList);
716 // remove any non wav file
717 for (uint j = 0; j < sampleList.size(); ++j)
719 if (sampleList[j].find(".wav") != sampleList[j].size() - 4)
721 sampleList.erase(sampleList.begin() + j);
722 --j;
725 sort(sampleList.begin(), sampleList.end());
727 return buildSampleBank(sampleList, bankDir, bankName);
730 /// Build a sample bank from a list of .wav files, and return the path to the written file.
731 std::string UAudioMixer::buildSampleBank(const std::vector<std::string> &sampleList, const std::string &bankDir, const std::string &bankName)
733 // need to create a new bank file !
734 CAudioMixerUser::TSampleBankHeader hdr;
736 vector<vector<uint8> > adpcmBuffers(sampleList.size());
737 vector<vector<sint16> > mono16Buffers(sampleList.size());
739 for (uint j = 0; j < sampleList.size(); ++j)
741 nldebug(" Adding sample [%s] into bank", CFile::getFilename(sampleList[j]).c_str());
743 CIFile sample(sampleList[j]);
744 uint size = sample.getFileSize();
745 std::vector<uint8> buffer;
746 buffer.resize(size);
747 sample.serialBuffer(&buffer[0], sample.getFileSize());
749 std::vector<uint8> result;
750 IBuffer::TBufferFormat bufferFormat;
751 uint8 channels;
752 uint8 bitsPerSample;
753 uint32 frequency;
755 if (!IBuffer::readWav(&buffer[0], size, result, bufferFormat, channels, bitsPerSample, frequency))
757 nlwarning(" IBuffer::readWav returned false");
758 continue;
761 vector<sint16> mono16Data;
762 if (!IBuffer::convertToMono16PCM(&result[0], (uint)result.size(), mono16Data, bufferFormat, channels, bitsPerSample))
764 nlwarning(" IBuffer::convertToMono16PCM returned false");
765 continue;
768 vector<uint8> adpcmData;
769 if (!IBuffer::convertMono16PCMToMonoADPCM(&mono16Data[0], (uint)mono16Data.size(), adpcmData))
771 nlwarning(" IBuffer::convertMono16PCMToMonoADPCM returned false");
772 continue;
775 // Sample number MUST be even
776 // nlassert(mono16Data.size() == (mono16Data.size() & 0xfffffffe));
777 if (mono16Data.size() & 1)
778 nlwarning("Uneven sample numbers, ADPCM will miss a sample. File: %s, Samples: %i, ADPCM Size: %i",
779 sampleList[j].c_str(), (int)mono16Data.size(), (int)adpcmData.size());
780 nlassert(adpcmData.size() == (mono16Data.size() >> 1));
782 adpcmBuffers[j].swap(adpcmData);
783 mono16Buffers[j].swap(mono16Data);
785 hdr.addSample(CFile::getFilename(sampleList[j]), frequency, (uint32)mono16Data.size(), (uint32)mono16Buffers[j].size() * 2, (uint32)adpcmBuffers[j].size());
788 // write the sample bank (if any sample available)
789 if (!hdr.Name.empty())
791 string filename = CPath::standardizePath(bankDir, true) + bankName + ".sample_bank";
792 COFile sbf(filename);
793 sbf.serial(hdr);
794 // nldebug("Header seeking = %i", sbf.getPos());
795 nlassert(mono16Buffers.size() == adpcmBuffers.size());
796 for (uint j = 0; j < mono16Buffers.size(); ++j)
798 sbf.serialBuffer((uint8*)(&mono16Buffers[j][0]), (uint)mono16Buffers[j].size()*2);
799 sbf.serialBuffer((uint8*)(&adpcmBuffers[j][0]), (uint)adpcmBuffers[j].size());
802 return filename;
804 else
806 return "";
810 void CAudioMixerUser::buildSampleBankList()
812 uint i;
813 // regenerate the sample banks list
814 const std::string &sbp = _SampleBankPath;
815 const std::string &swp = _SampleWavPath;
817 // build the list of available sample bank directory
818 vector<string> bankDir;
819 CPath::getPathContent(swp, false, true, false, bankDir);
820 sort(bankDir.begin(), bankDir.end());
821 for (i = 0; i < bankDir.size(); ++i)
823 if (bankDir[i].empty())
825 bankDir.erase(bankDir.begin()+i);
826 --i;
829 for (i = 0; i < bankDir.size(); ++i)
831 nldebug("Found sample bank dir [%s]", bankDir[i].c_str());
834 // build the list of available sample bank file
835 vector<string> bankFile;
836 CPath::getPathContent(sbp, false, false, true, bankFile);
837 // filter out any non sample bank file
838 for (i = 0; i < bankFile.size(); ++i)
840 if (bankFile[i].find(".sample_bank") != bankFile[i].size() - 12)
842 bankFile.erase(bankFile.begin()+i);
843 --i;
846 sort(bankFile.begin(), bankFile.end());
847 for (i = 0; i < bankFile.size(); ++i)
849 nldebug("Found sample bank file [%s]", bankFile[i].c_str());
852 // now, do a one to one comparison on bank file and sample file date
853 for (i = 0; i < bankDir.size(); ++i)
855 string bankname = bankDir[i];
856 if (bankname[bankname.size()-1] == '/')
857 bankname = bankname.substr(0, bankname.size()-1);
859 bankname = bankname.substr(bankname.rfind('/')+1);
861 if (i >= bankFile.size() || CFile::getFilenameWithoutExtension(bankFile[i]) > bankname)
863 nlinfo("Compiling sample bank [%s]", bankname.c_str());
864 std::string filename = buildSampleBank(bankDir[i], sbp, bankname);
865 if (bankFile.size() < i + 1) bankFile.resize(i + 1);
866 else bankFile.insert(bankFile.begin() + i, std::string());
867 bankFile[i] = filename;
869 else if (bankname < CFile::getFilenameWithoutExtension(bankDir[i]))
871 nlinfo("Removing sample bank file [%s]", bankname.c_str());
872 // remove an out of date bank file
873 CFile::deleteFile(bankFile[i]);
874 bankFile.erase(bankFile.begin()+i);
875 // recheck on this index
876 --i;
878 else
880 bool upToDate = true;
881 // check file list and date
882 nlassert(bankname == CFile::getFilenameWithoutExtension(bankFile[i]));
884 // read the sample bank file header.
887 CIFile sbf(bankFile[i]);
888 TSampleBankHeader hdr;
889 sbf.serial(hdr);
891 vector<string> sampleList;
892 CPath::getPathContent(bankDir[i], false, false, true, sampleList);
893 sort(sampleList.begin(), sampleList.end());
894 if (sampleList.size() == hdr.Name.size())
896 for (uint j=0; j<sampleList.size(); ++j)
898 // check same filename
899 if (CFile::getFilename(sampleList[j]) != hdr.Name[j])
901 upToDate = false;
902 break;
904 // check modification date
905 if (CFile::getFileModificationDate(sampleList[j]) >= CFile::getFileModificationDate(bankFile[i]))
907 upToDate = false;
908 break;
913 catch(const Exception &)
915 upToDate = false;
918 if (!upToDate)
920 nlinfo("Need to update bank file [%s]", bankname.c_str());
921 CFile::deleteFile(bankFile[i]);
922 bankFile.erase(bankFile.begin()+i);
923 // recheck on this index
924 --i;
928 // clear any out of date bank file
929 for (; i<bankFile.size(); ++i)
931 CFile::deleteFile(bankFile[i]);
936 // clear the exisiting list file
938 vector<string> fileList;
939 CPath::getPathContent(sp, false, false, true, fileList);
941 for (uint i=0; i<fileList.size(); ++i)
943 if (fileList[i].find(".sample_bank_list") == fileList[i].size() - 17)
945 CFile::deleteFile(fileList[i]);
950 std::vector <std::string> dirList;
951 if(!sp.empty())
952 CPath::getPathContent(sp, false, true, false, dirList);
954 while (!dirList.empty())
956 nldebug("Generating sample bank list for %s", dirList.back().c_str());
957 std::vector<std::string> sampleList;
958 CPath::getPathContent(dirList.back(), true, false, true, sampleList);
960 for (uint i=0; i< sampleList.size(); ++i)
962 sampleList[i] = CFile::getFilename(sampleList[i]);
963 nldebug("+- Adding sample %s to bank", sampleList[i].c_str());
966 std::vector<std::string> temp;
967 NLMISC::explode(dirList.back(), "/", temp, true);
968 nlassert(!temp.empty());
969 std::string listName(temp.back());
971 COFile file(_SamplePath+listName+SampleBankListExt);
972 file.serialCont(sampleList);
973 dirList.pop_back();
976 // update the searh path content
977 bool compressed = CPath::isMemoryCompressed();
978 if (!compressed)
979 CPath::addSearchPath(sbp);
982 void CAudioMixerUser::setBackgroundFlagName(uint flagIndex, const std::string &flagName)
984 if (flagIndex < TBackgroundFlags::NB_BACKGROUND_FLAGS)
985 _BackgroundFilterNames[flagIndex] = flagName;
987 void CAudioMixerUser::setBackgroundFlagShortName(uint flagIndex, const std::string &flagShortName)
989 if (flagIndex < TBackgroundFlags::NB_BACKGROUND_FLAGS)
990 _BackgroundFilterShortNames[flagIndex] = flagShortName;
992 const std::string &CAudioMixerUser::getBackgroundFlagName(uint flagIndex)
994 static std::string bad("");
995 if (flagIndex < TBackgroundFlags::NB_BACKGROUND_FLAGS)
996 return _BackgroundFilterNames[flagIndex];
997 else
998 return bad;
1000 const std::string &CAudioMixerUser::getBackgroundFlagShortName(uint flagIndex)
1002 static std::string bad("");
1003 if (flagIndex < TBackgroundFlags::NB_BACKGROUND_FLAGS)
1004 return _BackgroundFilterShortNames[flagIndex];
1005 else
1006 return bad;
1009 const UAudioMixer::TBackgroundFlags &CAudioMixerUser::getBackgroundFlags()
1011 return _BackgroundSoundManager->getBackgroundFlags();
1013 const UAudioMixer::TBackgroundFilterFades &CAudioMixerUser::getBackgroundFilterFades()
1015 return _BackgroundSoundManager->getBackgroundFilterFades();
1019 class CUserVarSerializer
1021 public:
1022 std::vector<CAudioMixerUser::CControledSources> Controlers;
1023 void readGeorges (const NLMISC::CSmartPtr<NLGEORGES::UForm> &form, const std::string &/* name */)
1027 std::string varname, soundName, paramId;
1028 NLGEORGES::UFormElm &root = form->getRootNode();
1029 NLGEORGES::UFormElm *items;
1030 uint size;
1032 CAudioMixerUser::CControledSources cs;
1034 // preset the default value
1035 cs.Value = 0.0f;
1037 root.getValueByName(varname, ".Name");
1038 root.getValueByName(paramId, ".ParamId");
1040 cs.Name = CStringMapper::map(varname);
1041 if (paramId == "Gain")
1042 cs.ParamId = CAudioMixerUser::gain_control;
1043 else if (paramId == "Pitch")
1044 cs.ParamId = CAudioMixerUser::pitch_control;
1045 else
1046 return;
1048 root.getNodeByName(&items, ".Sounds");
1049 items->getArraySize(size);
1051 for (uint i=0; i<size; ++i)
1053 items->getArrayValue(soundName, i);
1054 soundName = soundName.substr(0, soundName.find(".sound"));
1056 cs.SoundNames.push_back(CStringMapper::map(soundName));
1059 if (!cs.SoundNames.empty())
1060 Controlers.push_back(cs);
1062 catch(...)
1066 void serial (NLMISC::IStream &s)
1068 s.serialCont(Controlers);
1071 void removed()
1074 static uint getVersion () { return 2; }
1079 // ******************************************************************
1081 void CAudioMixerUser::initUserVar()
1083 _UserVarControls.clear();
1084 /// Temporary container.
1085 std::map<std::string, CUserVarSerializer> Container;
1087 // read all *.user_var_binding sheet in data/sound/user_var folder
1089 // load the sound_group sheets
1090 ::loadForm("user_var_binding", _PackedSheetPath+"user_var_binding.packed_sheets", Container, _UpdatePackedSheet, false);
1091 // fill the real container.
1092 std::map<std::string, CUserVarSerializer>::iterator first(Container.begin()), last(Container.end());
1093 for (; first != last; ++first)
1095 for (uint i=0; i<first->second.Controlers.size(); ++i)
1097 _UserVarControls.insert(make_pair(first->second.Controlers[i].Name, first->second.Controlers[i]));
1101 // update all the sounds to refer to the controler.
1103 TUserVarControlsContainer::iterator first(_UserVarControls.begin()), last(_UserVarControls.end());
1104 for(; first != last; ++first)
1106 std::vector<NLMISC::TStringId>::iterator first2(first->second.SoundNames.begin()), last2(first->second.SoundNames.end());
1107 for (; first2 != last2; ++first2)
1109 CSound *sound = getSoundId(*first2);
1110 if (sound != 0)
1112 // ok, the sound exist !
1113 sound->_UserVarControler = first->second.Name;
1121 /// Build the sound bank packed sheets file from georges sound sheet files with .sound extension in the search path, and return the path to the written file.
1122 std::string UAudioMixer::buildSoundBank(const std::string &packedSheetDir)
1124 CGroupControllerRoot *tempRoot = NULL;
1125 if (!CGroupControllerRoot::isInitialized())
1126 tempRoot = new CGroupControllerRoot();
1127 std::string dir = CPath::standardizePath(packedSheetDir, true);
1128 CSoundBank *soundBank = new CSoundBank();
1129 soundBank->load(dir, true);
1130 delete soundBank;
1131 delete tempRoot;
1132 return dir + "sounds.packed_sheets";
1135 /// Build the cluster sound_group sheets.
1136 std::string UAudioMixer::buildClusteredSoundGroupSheets(const std::string &packedSheetDir)
1138 std::string dir = CPath::standardizePath(packedSheetDir, true);
1139 CClusteredSound::buildSheets(dir);
1140 return dir + "sound_groups.packed_sheets";
1143 /// Build the user var binding sheets.
1144 std::string UAudioMixer::buildUserVarBindingSheets(const std::string &packedSheetDir)
1146 std::string dir = CPath::standardizePath(packedSheetDir, true);
1147 std::map<std::string, CUserVarSerializer> container;
1148 ::loadForm("user_var_binding", dir + "user_var_binding.packed_sheets", container, true, false);
1149 return dir + "user_var_binding.packed_sheets";
1152 // ******************************************************************
1154 void CAudioMixerUser::CControledSources::serial(NLMISC::IStream &s)
1156 std::string name, soundName;
1157 if (s.isReading())
1159 s.serial(name);
1160 Name = CStringMapper::map(name);
1161 s.serialEnum(ParamId);
1163 uint32 size;
1164 s.serial(size);
1165 for (uint i=0; i<size; ++i)
1167 s.serial(soundName);
1168 SoundNames.push_back(CStringMapper::map(soundName));
1171 else
1173 name = CStringMapper::unmap(Name);
1174 s.serial(name);
1175 s.serialEnum(ParamId);
1177 uint32 size = (uint32)SoundNames.size();
1178 s.serial(size);
1180 for (uint i=0; i<size; ++i)
1182 soundName = CStringMapper::unmap(SoundNames[i]);
1183 s.serial(soundName);
1187 // Default value to 0.
1188 Value = 0.0f;
1191 // ******************************************************************
1193 void CAudioMixerUser::setUserVar(NLMISC::TStringId varName, float value)
1195 TUserVarControlsContainer::iterator it(_UserVarControls.find(varName));
1196 if (it != _UserVarControls.end())
1198 // ok we found the var !
1199 // do some work only if the value is different (we don't trust client for
1200 // being smart ;) )
1201 // if (it->second.Value != value)
1203 it->second.Value = value;
1204 // update all sources
1205 std::set<CSourceCommon*>::iterator first(it->second.Sources.begin()), last(it->second.Sources.end());
1206 for (; first != last; ++first)
1208 if (it->second.ParamId == gain_control)
1210 float relGain = (*first)->getRelativeGain();
1211 float gain = (*first)->getSound()->getGain();
1212 (*first)->setGain(gain * value);
1213 (*first)->setRelativeGain(relGain);
1215 else
1217 (*first)->setPitch(value);
1224 // ******************************************************************
1226 float CAudioMixerUser::getUserVar(NLMISC::TStringId varName)
1228 TUserVarControlsContainer::iterator it(_UserVarControls.find(varName));
1229 if (it != _UserVarControls.end())
1231 return it->second.Value;
1233 // return a default value.
1234 return 1.0f;
1237 // ******************************************************************
1239 void CAudioMixerUser::addUserControledSource(CSourceCommon *source, NLMISC::TStringId varName)
1241 TUserVarControlsContainer::iterator it(_UserVarControls.find(varName));
1242 if (it != _UserVarControls.end())
1244 // ok, the var exist, insert this source
1245 it->second.Sources.insert(source);
1246 // update the controled parameter
1247 if (it->second.ParamId == gain_control)
1249 float relGain = source->getRelativeGain();
1250 float gain = source->getSound()->getGain();
1251 source->setGain(gain * it->second.Value);
1252 source->setRelativeGain(relGain);
1254 else
1256 source->setPitch(it->second.Value);
1261 // ******************************************************************
1263 void CAudioMixerUser::removeUserControledSource(CSourceCommon *source, NLMISC::TStringId varName)
1265 TUserVarControlsContainer::iterator it(_UserVarControls.find(varName));
1266 if (it != _UserVarControls.end())
1268 // ok, the var exist, remove this source
1269 it->second.Sources.erase(source);
1273 // ******************************************************************
1275 void CAudioMixerUser::bufferUnloaded(IBuffer *buffer)
1277 // check all track to find a track playing this buffer.
1278 for (uint i = 0; i < _Tracks.size(); ++i)
1280 CTrack *track = _Tracks[i];
1281 if (track && track->getLogicalSource())
1283 CSourceCommon *src = track->getLogicalSource();
1284 if (src->getType() == CSourceCommon::SOURCE_SIMPLE)
1286 CSimpleSource *simpleSrc = static_cast<CSimpleSource *>(src);
1287 if (simpleSrc->getBuffer() == buffer)
1289 simpleSrc->stop();
1296 // ******************************************************************
1298 void CAudioMixerUser::enable( bool /* b */ )
1300 // TODO : rewrite this method
1302 nlassert(false);
1303 /* if ( b )
1305 // Reenable
1306 _NbTracks = _MaxNbTracks;
1308 else
1310 // Disable
1311 uint i;
1312 for ( i=0; i!=_NbTracks; i++ )
1314 if ( _Tracks[i] && ! _Tracks[i]->isAvailable() )
1316 _Tracks[i]->getSource()->leaveTrack();
1317 // nlassert(_PlayingSources.find(_Tracks[i]->getSource()) != _PlayingSources.end());
1318 // _PlayingSources.erase(_Tracks[i]->getSource());
1321 _NbTracks = 0;
1326 // ******************************************************************
1328 ISoundDriver* CAudioMixerUser::getSoundDriver()
1330 return _SoundDriver;
1333 // ******************************************************************
1335 void CAudioMixerUser::getFreeTracks( uint nb, CTrack **tracks )
1337 std::vector<CTrack*>::iterator first(_FreeTracks.begin()), last(_FreeTracks.end());
1338 for (nb =0; first != last; ++first, ++nb)
1340 tracks[nb] = *first;
1345 // ******************************************************************
1347 void CAudioMixerUser::applyListenerMove( const NLMISC::CVector& listenerpos )
1349 // Store position
1350 _ListenPosition = listenerpos;
1352 _BackgroundSoundManager->updateBackgroundStatus();
1354 // Environmental effect
1355 // computeEnvEffect( listenerpos );
1357 /* // Environment sounds
1358 if ( _EnvSounds != NULL )
1360 _EnvSounds->recompute();
1365 // ******************************************************************
1367 void CAudioMixerUser::reloadSampleBanks(bool async)
1369 CPath::addSearchPath(_SampleBankPath, true, false);
1370 if (_UpdatePackedSheet)
1371 buildSampleBankList();
1372 _SampleBankManager->reload(async);
1375 // ******************************************************************
1377 //CTrack *CAudioMixerUser::getFreeTrackWithoutSource(bool steal)
1379 // if (!_FreeTracks.empty())
1380 // {
1381 // CTrack *free_track = _FreeTracks.back();
1382 // _FreeTracks.pop_back();
1383 // nlassert(!free_track->getLogicalSource());
1384 // ++_ReserveUsage[HighestPri];
1385 // if (_UseEax) free_track->getPhysicalSource()->setEffect(NULL); // no reverb!
1386 // return free_track;
1387 // }
1388 // else if (steal) for (uint i = 0; i < _Tracks.size(); ++i)
1389 // {
1390 // CSourceCommon *src2 = _Tracks[i]->getLogicalSource();
1391 // if (src2)
1392 // {
1393 // src2->stop();
1394 // if (_FreeTracks.empty())
1395 // {
1396 // nlwarning("No free track after cutting a playing sound source !");
1397 // }
1398 // else
1399 // {
1400 // CTrack *free_track = _FreeTracks.back();
1401 // _FreeTracks.pop_back();
1402 // nlassert(!free_track->getLogicalSource());
1403 // ++_ReserveUsage[HighestPri];
1404 // if (_UseEax) free_track->getPhysicalSource()->setEffect(NULL); // no reverb!
1405 // return free_track;
1406 // }
1407 // }
1408 // }
1409 // return NULL;
1412 CTrack *CAudioMixerUser::getFreeTrack(CSourceCommon *source)
1414 // nldebug("There are %d free tracks", _FreeTracks.size() );
1415 // at least some track free ?
1416 if (!_FreeTracks.empty())
1418 // under the low water mark or under the reserve
1419 if (_FreeTracks.size() > _LowWaterMark
1420 || _ReserveUsage[source->getPriority()] < _PriorityReserve[source->getPriority()] )
1422 // non discardable track or not too many waiting source
1423 if (source->getPriority() == HighestPri
1424 || _FreeTracks.size() > _SourceWaitingForPlay.size())
1426 CTrack *ret = _FreeTracks.back();
1427 _FreeTracks.pop_back();
1428 ret->setLogicalSource(source);
1429 _ReserveUsage[source->getPriority()]++;
1430 // nldebug("Track %p assign to source %p", ret, ret->getSource());
1431 return ret;
1435 // try to find a track with a source cuttable
1437 float srcMinDist = source->getSound()->getMinDistance();
1438 float srcMaxDist = source->getSound()->getMaxDistance();
1440 float d1, d2, t1, t2;
1441 d1 = source->getSourceRelativeMode() ? source->getPos().norm() : (source->getPos() - _ListenPosition).norm();
1442 t1 = max(0.0f, 1.0f - ((d1 - srcMinDist) / (srcMaxDist - srcMinDist)));
1444 for (uint i = 0; i < _Tracks.size(); ++i)
1446 CSourceCommon *src2 = _Tracks[i]->getLogicalSource();
1447 if (src2)
1449 float src2MinDist = src2->getSound()->getMinDistance();
1450 float src2MaxDist = src2->getSound()->getMaxDistance();
1452 d2 = src2->getSourceRelativeMode() ? src2->getPos().norm() : (src2->getPos() - _ListenPosition).norm();
1453 t2 = max(0.0f, 1.0f - ((d2 - src2MinDist) / (src2MaxDist - src2MinDist)));
1455 const float tfactor = 1.3f;
1456 if (t1 > t2 * tfactor)
1457 // if (d1 < d2)
1459 // nldebug("Cutting source %p with source %p (%f > %f*%f)", src2, source, t1, tfactor, t2);
1460 // on peut cuter cette voie !
1461 src2->stop();
1462 if (_FreeTracks.empty())
1464 nlwarning("No free track after cutting a playing sound source !");
1466 else
1468 CTrack *ret = _FreeTracks.back();
1469 _FreeTracks.pop_back();
1470 ret->setLogicalSource(source);
1471 _ReserveUsage[source->getPriority()]++;
1472 // nldebug("Track %p assign to source %p", ret, ret->getSource());
1473 return ret;
1480 return 0;
1483 // ******************************************************************
1485 //void CAudioMixerUser::freeTrackWithoutSource(CTrack *track)
1487 // nlassert(track);
1489 // if (_UseEax) track->getPhysicalSource()->setEffect(_ReverbEffect); // return reverb!
1490 // --_ReserveUsage[HighestPri];
1491 // _FreeTracks.push_back(track);
1494 void CAudioMixerUser::freeTrack(CTrack *track)
1496 nlassert(track != 0);
1497 nlassert(track->getLogicalSource() != 0);
1499 // nldebug("Track %p free by source %p", track, track->getSource());
1501 _ReserveUsage[track->getLogicalSource()->getPriority()]--;
1502 track->setLogicalSource(0);
1503 _FreeTracks.push_back(track);
1506 // ******************************************************************
1508 void CAudioMixerUser::getPlayingSoundsPos(bool virtualPos, std::vector<std::pair<bool, NLMISC::CVector> > &pos)
1510 int nbplay = 0;
1511 int nbmute = 0;
1512 int nbsrc = 0;
1514 TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
1515 for (; first != last; ++first)
1517 CSourceCommon *ps = *first;
1518 if (ps->getType() == CSourceCommon::SOURCE_SIMPLE)
1520 CSimpleSource *source = static_cast<CSimpleSource *>(*first);
1521 nbsrc++;
1523 if (source->isPlaying())
1525 if (virtualPos)
1526 pos.push_back(make_pair(source->getTrack() == 0, source->getVirtualPos()));
1527 else
1528 pos.push_back(make_pair(source->getTrack() == 0,
1529 source->getSourceRelativeMode()
1530 ? source->getPos() + _ListenPosition
1531 : source->getPos()));
1533 if (source->getTrack() == 0)
1534 nbmute++;
1535 else
1537 // nldebug ("Source %p playing on track %p", source, source->getTrack());
1538 nbplay ++;
1542 else if (ps->getType() == CSourceCommon::SOURCE_STREAM)
1544 CStreamSource *source = static_cast<CStreamSource *>(*first);
1545 nbsrc++;
1547 if (source->isPlaying())
1549 if (virtualPos)
1550 pos.push_back(make_pair(source->getTrack() == 0, source->getVirtualPos()));
1551 else
1552 pos.push_back(make_pair(source->getTrack() == 0,
1553 source->getSourceRelativeMode()
1554 ? source->getPos() + _ListenPosition
1555 : source->getPos()));
1557 if (source->getTrack() == 0)
1558 nbmute++;
1559 else
1561 // nldebug ("Source %p playing on track %p", source, source->getTrack());
1562 nbplay ++;
1568 // nldebug("Total source : %d, playing : %d, muted : %d", nbsrc, nbplay, nbmute);
1573 void CAudioMixerUser::update()
1575 H_AUTO(NLSOUND_AudioMixerUpdate)
1576 /* static NLMISC::TTime lastUpdate = NLMISC::CTime::getLocalTime();
1577 NLMISC::TTime now = NLMISC::CTime::getLocalTime();
1579 nldebug("Mixer update : %u ms", uint(now - lastUpdate));
1580 lastUpdate = now;
1582 #if NL_PROFILE_MIXER
1583 TTicks start = CTime::getPerformanceTime();
1584 #endif
1586 // update the object.
1588 H_AUTO(NLSOUND_AudioMixerUpdateObjet)
1589 // 1st, update the event list
1591 std::vector<std::pair<IMixerUpdate*, bool> >::iterator first(_UpdateEventList.begin()), last(_UpdateEventList.end());
1592 for (; first != last; ++first)
1594 if (first->second)
1596 // nldebug("Inserting update %p", first->first);
1597 _UpdateList.insert(first->first);
1599 else
1601 // nldebug("Removing update %p", first->first);
1602 _UpdateList.erase(first->first);
1605 _UpdateEventList.clear();
1607 // 2nd, do the update
1609 TMixerUpdateContainer::iterator first(_UpdateList.begin()), last(_UpdateList.end());
1610 for (; first != last; ++first)
1612 if( *first == 0)
1614 nlwarning("NULL pointeur in update list !");
1616 else
1618 // call the update method.
1619 const IMixerUpdate *update = *first;
1620 const_cast<IMixerUpdate*>(update)->onUpdate();
1625 // send the event.
1627 H_AUTO(NLSOUND_AudioMixerUpdateSendEvent)
1629 // **** 1st, update the event list
1631 std::list<std::pair<NLMISC::TTime, IMixerEvent*> >::iterator first(_EventListUpdate.begin()), last(_EventListUpdate.end());
1632 for (; first != last; ++first)
1634 // add an event
1635 // nldebug ("Add event %p", first->second);
1636 TTimedEventContainer::iterator it(_EventList.insert(make_pair(first->first, NLMISC::CDbgPtr<IMixerEvent>(first->second))));
1637 _Events.insert(make_pair(first->second, it));
1640 _EventListUpdate.clear();
1643 // **** 2nd, call the events
1644 TTime now = NLMISC::CTime::getLocalTime();
1645 while (!_EventList.empty() && _EventList.begin()->first <= now)
1647 // get the event
1648 CAudioMixerUser::IMixerEvent *currentEvent = _EventList.begin()->second;
1650 // remove the right entry in the _Events multimap.
1651 TEventContainer::iterator it(_Events.lower_bound(_EventList.begin()->second));
1652 while (it->first == _EventList.begin()->second.ptr())
1654 if (it->second == _EventList.begin())
1656 _Events.erase(it);
1657 break;
1659 it++;
1662 // remove from the _EventList
1663 _EventList.erase(_EventList.begin());
1665 // now, run the event
1666 // nldebug("Sending Event %p", _EventList.begin()->second);
1667 nlassert(currentEvent);
1668 currentEvent->onEvent();
1670 #ifdef NL_DEBUG
1671 currentEvent = 0;
1672 #endif
1677 // update the background sound
1678 _BackgroundSoundManager->updateBackgroundStatus();
1680 // update the background music
1681 _BackgroundMusicManager->update();
1683 uint i;
1684 // update music channels
1685 for (i = 0; i < _NbMusicChannelFaders; ++i)
1686 _MusicChannelFaders[i].update();
1688 // Check all playing track and stop any terminated buffer.
1689 std::list<CSourceCommon *>::size_type nbWaitingSources = _Sources.size();
1690 for (i=0; i<_Tracks.size(); ++i)
1692 if (!_Tracks[i]->isPlaying())
1694 if (_Tracks[i]->getLogicalSource() != 0)
1696 CSourceCommon *source = _Tracks[i]->getLogicalSource();
1697 source->stop();
1700 // try to play any waiting source.
1701 if (!_SourceWaitingForPlay.empty() && nbWaitingSources)
1703 // check if the source still exist before trying to play it
1704 if (_Sources.find(_SourceWaitingForPlay.front()) != _Sources.end())
1705 _SourceWaitingForPlay.front()->play();
1706 // nldebug("Before POP Sources waiting : %u", _SourceWaitingForPlay.size());
1707 _SourceWaitingForPlay.pop_front();
1708 --nbWaitingSources;
1709 // nldebug("After POP Sources waiting : %u", _SourceWaitingForPlay.size());
1714 if (_ClusteredSound)
1716 H_AUTO(NLSOUND_UpdateClusteredSound)
1717 // update the clustered sound...
1718 CVector view, up;
1719 _Listener.getOrientation(view, up);
1720 _ClusteredSound->update(_ListenPosition, view, up);
1722 // update all playng track according to there cluster status
1723 for (i=0; i<_Tracks.size(); ++i)
1725 if (_Tracks[i]->isPlaying())
1727 if (_Tracks[i]->getLogicalSource() != 0)
1729 CSourceCommon *source = _Tracks[i]->getLogicalSource();
1730 if (source->getCluster() != 0)
1732 // need to check the cluster status
1733 const CClusteredSound::CClusterSoundStatus *css = _ClusteredSound->getClusterSoundStatus(source->getCluster());
1734 if (css != 0)
1736 // there is some data here, update the virtual position of the sound.
1737 float dist = (css->Position - source->getPos()).norm();
1738 CVector vpos(_ListenPosition + css->Direction * (css->Dist + dist));
1739 // _Tracks[i]->DrvSource->setPos(source->getPos() * (1-css->PosAlpha) + css->Position*(css->PosAlpha));
1740 _Tracks[i]->getPhysicalSource()->setPos(source->getPos() * (1-css->PosAlpha) + vpos*(css->PosAlpha));
1741 // update the relative gain
1742 _Tracks[i]->getPhysicalSource()->setGain(source->getFinalGain() * css->Gain);
1743 #if EAX_AVAILABLE == 1
1744 if (_UseEax)
1746 H_AUTO(NLSOUND_SetEaxProperties)
1747 // update the occlusion parameters
1748 _Tracks[i]->DrvSource->setEAXProperty(DSPROPERTY_EAXBUFFER_OCCLUSION, (void*)&css->Occlusion, sizeof(css->Occlusion));
1749 _Tracks[i]->DrvSource->setEAXProperty(DSPROPERTY_EAXBUFFER_OCCLUSIONLFRATIO, (void*)&css->OcclusionLFFactor, sizeof(css->OcclusionLFFactor));
1750 // if (lastRatio[i] != css->OcclusionRoomRatio)
1751 // {
1752 _Tracks[i]->DrvSource->setEAXProperty(DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO, (void*)&css->OcclusionRoomRatio, sizeof(css->OcclusionRoomRatio));
1753 // lastRatio[i] = css->OcclusionRoomRatio;
1754 // nldebug("Setting room ration.");
1755 // }
1756 _Tracks[i]->DrvSource->setEAXProperty(DSPROPERTY_EAXBUFFER_OBSTRUCTION, (void*)&css->Obstruction, sizeof(css->Obstruction));
1758 #endif
1767 // Debug info
1768 /*uint32 i;
1769 nldebug( "List of the %u tracks", _NbTracks );
1770 for ( i=0; i!=_NbTracks; i++ )
1772 CSimpleSource *su;
1773 if ( su = _Tracks[i]->getSource() )
1775 nldebug( "%u: %p %s %s %s %s, vol %u",
1776 i, &_Tracks[i]->DrvSource, _Tracks[i]->isAvailable()?"FREE":"USED",
1777 _Tracks[i]->isAvailable()?"":(su->isPlaying()?"PLAYING":"STOPPED"),
1778 _Tracks[i]->isAvailable()?"":PriToCStr[su->getPriority()],
1779 _Tracks[i]->isAvailable()?"":(su->getSound()?su->getSound()->getFilename().c_str():""),
1780 (uint)(su->getGain()*100.0f) );
1784 _SoundDriver->commit3DChanges();
1786 #if NL_PROFILE_MIXER
1787 _UpdateTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
1788 _UpdateCount++;
1789 #endif
1791 /* // display the track using...
1793 char tmp[2048] = "";
1794 string str;
1796 for (uint i=0; i<_NbTracks/2; ++i)
1798 sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
1799 str += tmp;
1801 nldebug((string("Status1: ")+str).c_str());
1802 str.clear();
1803 for (i=_NbTracks/2; i<_NbTracks; ++i)
1805 sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
1806 str += tmp;
1808 // nldebug((string("Status2: ")+str).c_str());
1814 // ******************************************************************
1816 TSoundId CAudioMixerUser::getSoundId( const NLMISC::TStringId &name )
1818 return _SoundBank->getSound(name);
1821 // ******************************************************************
1823 void CAudioMixerUser::addSource( CSourceCommon *source )
1825 nlassert(_Sources.find(source) == _Sources.end());
1826 _Sources.insert( source );
1828 // _profile(( "AM: ADDSOURCE, SOUND: %d, TRACK: %p, NAME=%s", source->getSound(), source->getTrack(),
1829 // source->getSound() && (!source->getSound()->getName().empty()) ? source->getSound()->getName().c_str() : "" ));
1834 static bool checkSound(CSound *sound, const vector<pair<string, CSound*> > &subsounds, vector<string> &missingFiles)
1836 vector<pair<string, CSound*> >::const_iterator first(subsounds.begin()), last(subsounds.end());
1838 for (; first != last; ++first)
1840 if (first->second == sound)
1841 return false;
1843 if (first->second == 0 && !first->first.empty())
1844 missingFiles.push_back(first->first);
1845 else if (first->second != 0)
1847 vector<pair<string, CSound*> > v2;
1848 first->second->getSubSoundList(v2);
1850 if (!checkSound(sound, v2, missingFiles))
1851 return false;
1854 return true;
1858 bool CAudioMixerUser::tryToLoadSampleBank(const std::string &sampleName)
1860 string path = CPath::lookup(sampleName, false, false, false);
1861 if (!path.empty())
1863 // extract samplebank name
1864 path = NLMISC::CFile::getPath(path);
1865 vector<string> rep;
1866 explode(path, string("/"), rep, true);
1868 loadSampleBank(false, rep.back());
1870 return true;
1872 else
1874 nlwarning("tryToLoadSoundBank : can't find sample bank for '%s'", sampleName.c_str());
1875 return false;
1879 UGroupController *CAudioMixerUser::getGroupController(const std::string &path)
1881 return static_cast<UGroupController *>(_GroupController.getGroupController(path));
1884 // ******************************************************************
1886 USource *CAudioMixerUser::createSource( TSoundId id, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context, UGroupController *groupController )
1888 #if NL_PROFILE_MIXER
1889 TTicks start = CTime::getPerformanceTime();
1890 #endif
1892 _profile(( "AM: [%u]---------------------------------------------------------------", curTime() ));
1893 _profile(( "AM: CREATESOURCE: SOUND=%p, NAME=%s, TIME=%d", id, id->getName().c_str(), curTime() ));
1894 _profile(( "AM: SOURCES: %d, PLAYING: %d, TRACKS: %d", getSourcesNumber(), getPlayingSourcesNumber(), getNumberAvailableTracks() ));
1896 if ( id == NULL )
1898 _profile(("AM: FAILED CREATESOURCE"));
1899 // nldebug( "AM: Sound not created: invalid sound id" );
1900 return NULL;
1903 USource *ret = NULL;
1905 if (_AutoLoadSample)
1907 if (id->getSoundType() == CSound::SOUND_SIMPLE)
1909 CSimpleSound *ss = (CSimpleSound*)id;
1910 if (ss->getBuffer() == NULL)
1912 const string sampleName = CStringMapper::unmap(ss->getBuffername()) + ".wav";
1914 tryToLoadSampleBank(sampleName);
1917 else
1919 uint32 count = 0;
1920 retrySound:
1921 ++count;
1922 vector<pair<string, CSound*> > subsounds;
1923 id->getSubSoundList(subsounds);
1924 vector<string> missingFiles;
1925 // check the sound before anythink else
1926 bool invalid = !checkSound(id, subsounds, missingFiles);
1928 if (invalid)
1930 nlwarning("The sound %s contain an infinite recursion !", CStringMapper::unmap(id->getName()).c_str());
1931 return NULL;
1934 if (!missingFiles.empty()/* && count <= missingFiles.size()*/)
1936 // try to load missing sample bank
1937 for (uint i=0; i<missingFiles.size(); ++i)
1939 if (missingFiles[i].find(" (sample)") != string::npos)
1941 // try to find the sample bank
1942 string sample = missingFiles[i].substr(0, missingFiles[i].find(" (sample)")) + ".wav";
1944 if (tryToLoadSampleBank(sample))
1945 goto retrySound;
1952 switch (id->getSoundType())
1954 case CSound::SOUND_SIMPLE:
1956 CSimpleSound *simpleSound = static_cast<CSimpleSound *>(id);
1957 // This is a simple sound
1958 if (simpleSound->getBuffer() == NULL)
1960 static std::set<std::string> warned;
1962 const std::string &name = CStringMapper::unmap(simpleSound->getBuffername());
1963 if (warned.find(name) == warned.end())
1965 nlwarning ("Can't create the sound '%s'", name.c_str());
1966 warned.insert(name);
1968 return NULL;
1971 // Create source
1972 CSimpleSource *source = new CSimpleSource( simpleSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
1974 // nldebug("Mixer : source %p created", source);
1976 //if (source->getBuffer() != 0)
1978 // // Link the position to the listener position if it'a stereo source
1979 // if ( source->getBuffer()->isStereo() )
1980 // {
1981 // source->set3DPositionVector( &_ListenPosition );
1982 // }
1983 // // no, we don't, there's setSourceRelativeMode for that -_-
1985 //else
1987 // nlassert(false); // FIXME
1988 //} // FIXED [KAETEMI]
1989 ret = source;
1991 break;
1992 case CSound::SOUND_STREAM:
1994 CStreamSound *streamSound = static_cast<CStreamSound *>(id);
1995 // This is a stream thingy.
1996 ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
1998 break;
1999 case CSound::SOUND_STREAM_FILE:
2001 CStreamFileSound *streamFileSound = static_cast<CStreamFileSound *>(id);
2002 // This is a stream file thingy.
2003 ret = new CStreamFileSource(streamFileSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
2005 break;
2006 case CSound::SOUND_COMPLEX:
2008 CComplexSound *complexSound = static_cast<CComplexSound *>(id);
2009 // This is a pattern sound.
2010 ret = new CComplexSource(complexSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
2012 break;
2013 case CSound::SOUND_BACKGROUND:
2015 // This is a background sound.
2016 CBackgroundSound *bgSound = static_cast<CBackgroundSound *>(id);
2017 ret = new CBackgroundSource(bgSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
2019 break;
2020 case CSound::SOUND_MUSIC:
2022 // This is a background music sound
2023 CMusicSound *music_sound= static_cast<CMusicSound *>(id);
2024 ret = new CMusicSource(music_sound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
2026 break;
2027 case CSound::SOUND_CONTEXT:
2029 static CSoundContext defaultContext;
2030 // This is a context sound.
2031 if (context == 0)
2032 context = &defaultContext;
2034 CContextSound *ctxSound = static_cast<CContextSound *>(id);
2035 CSound *sound = ctxSound->getContextSound(*context);
2036 if (sound != 0)
2038 ret = createSource(sound, spawn, cb, userParam, cluster, NULL, static_cast<CGroupController *>(groupController));
2039 // Set the volume of the source according to the context volume
2040 if (ret != 0)
2042 ret->setGain(ret->getGain() * ctxSound->getGain());
2043 float pitch = ret->getPitch() * ctxSound->getPitch();
2044 ret->setPitch(pitch);
2047 else
2048 ret = 0;
2050 break;
2051 default:
2053 // nlassertex(false, ("Unknown sound class !"));
2054 nlwarning("Unknow sound class : %u", id->getSoundType());
2056 break;
2059 #if NL_PROFILE_MIXER
2060 _CreateTime = CTime::ticksToSecond(CTime::getPerformanceTime() - start);
2061 _CreateCount++;
2062 #endif
2064 //nldebug( "AM: Source created" );
2065 return ret;
2069 // ******************************************************************
2071 USource *CAudioMixerUser::createSource( const NLMISC::TStringId &name, bool spawn, TSpawnEndCallback cb, void *userParam, NL3D::CCluster *cluster, CSoundContext *context, UGroupController *groupController)
2073 return createSource( getSoundId( name ), spawn, cb, userParam, cluster, context, groupController);
2077 // ******************************************************************
2079 void CAudioMixerUser::removeSource( CSourceCommon *source )
2081 nlassert( source != NULL );
2083 size_t n = _Sources.erase(source);
2084 nlassert(n == 1);
2088 // ******************************************************************
2090 void CAudioMixerUser::selectEnvEffects( const std::string & tag)
2092 // for testing purposes only
2093 _ReverbEffect->setEnvironment(_Environments[CStringMapper::map(tag)]);
2094 //nlassertex(false, ("Not implemented yet"));
2095 /* // Select Env
2096 vector<CEnvEffect*>::iterator ipe;
2097 for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
2099 (*ipe)->selectEnv( tag );
2102 // Compute
2103 CVector pos;
2104 _Listener.getPos( pos );
2105 computeEnvEffect( pos, true );
2110 // ******************************************************************
2113 void CAudioMixerUser::loadEnvEffects( const char *filename )
2115 nlassert( filename != NULL );
2116 nlinfo( "Loading environmental effects from %s...", filename );
2118 // Unload previous env effects
2119 vector<CEnvEffect*>::iterator ipe;
2120 for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
2122 delete (*ipe);
2124 _EnvEffects.clear();
2126 string str = CPath::lookup( filename, false );
2128 // Load env effects
2129 CIFile file;
2130 if ( !str.empty() && file.open(str) )
2132 uint32 n = CEnvEffect::load( _EnvEffects, file );
2133 nldebug( "AM: Loaded %u environmental effects", n );
2135 else
2137 nlwarning( "AM: Environmental effects file not found" );
2142 // ******************************************************************
2144 uint32 CAudioMixerUser::loadSampleBank(bool async, const std::string &name, std::vector<std::string> *notfoundfiles )
2146 // nlassert( filename != NULL );
2148 // string path = _SamplePath;
2149 // path.append("/").append(filename);
2151 //nldebug( "Loading samples bank %s...", name.c_str() );
2152 TStringId nameId = CStringMapper::map(name);
2153 CSampleBank* bank = _SampleBankManager->findSampleBank(nameId);
2154 if (bank == NULL)
2156 // create a new sample bank
2157 bank = new CSampleBank(nameId, _SampleBankManager);
2162 bank->load(async);
2164 catch (const Exception& e)
2166 if (notfoundfiles)
2168 notfoundfiles->push_back(name);
2170 string reason = e.what();
2171 nlwarning( "AM: Failed to load the samples: %s", reason.c_str() );
2175 return bank->countSamples();
2178 bool CAudioMixerUser::unloadSampleBank(const std::string &name)
2180 // string path = _SamplePath;
2181 // path.append("/").append(filename);
2183 //nldebug( "Unloading samples bank %s...", name.c_str() );
2184 CSampleBank *pbank = _SampleBankManager->findSampleBank(CStringMapper::map(name));
2186 if (pbank != NULL)
2188 // ok, the bank exist.
2189 return pbank->unload();
2191 else
2192 return false;
2196 // ******************************************************************
2198 void CAudioMixerUser::getSoundNames( std::vector<NLMISC::TStringId> &names ) const
2200 _SoundBank->getNames(names);
2204 // ******************************************************************
2206 uint CAudioMixerUser::getPlayingSourcesCount() const
2208 return _PlayingSources;
2212 // ******************************************************************
2214 uint CAudioMixerUser::countPlayingSimpleSources() const
2216 uint count = 0;
2217 for (TSourceContainer::const_iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it)
2219 if ((*it)->getType() == CSourceCommon::SOURCE_SIMPLE && (*it)->isPlaying())
2220 ++count;
2222 return count;
2225 uint CAudioMixerUser::countSimpleSources() const
2227 uint count = 0;
2228 for (TSourceContainer::const_iterator it(_Sources.begin()), end(_Sources.end()); it != end; ++it)
2230 if ((*it)->getType() == CSourceCommon::SOURCE_SIMPLE)
2231 ++count;
2233 return count;
2237 // ******************************************************************
2239 uint CAudioMixerUser::getAvailableTracksCount() const
2241 return (uint)_FreeTracks.size();
2244 uint CAudioMixerUser::getUsedTracksCount() const
2246 return (uint)_Tracks.size() - (uint)_FreeTracks.size();
2251 // ******************************************************************
2253 string CAudioMixerUser::getSourcesStats() const
2255 // TODO : rewrite log output
2257 string s;
2258 TSourceContainer::const_iterator ips;
2259 for ( ips=_Sources.begin(); ips!=_Sources.end(); ++ips )
2261 if ( (*ips)->isPlaying() )
2263 // char line [80];
2265 /* nlassert( (*ips)->getSound() && (*ips)->getSimpleSound()->getBuffer() );
2266 smprintf( line, 80, "%s: %u%% %s %s",
2267 (*ips)->getSound()->getName().c_str(),
2268 (uint32)((*ips)->getGain()*100.0f),
2269 (*ips)->getBuffer()->isStereo()?"ST":"MO",
2270 PriToCStr[(*ips)->getPriority()] );
2271 s += string(line) + "\n";
2272 */ }
2274 return s;
2278 // ******************************************************************
2280 void CAudioMixerUser::loadEnvSounds( const char *filename, UEnvSound **treeRoot )
2282 nlassert( filename != NULL );
2283 nlinfo( "Loading environment sounds from %s...", filename );
2285 string str = CPath::lookup( filename, false );
2287 CIFile file;
2288 if ( !str.empty() && file.open( str ) )
2290 uint32 n = 0; //CEnvSoundUser::load( _EnvSounds, file );
2291 nldebug( "AM: Loaded %u environment sounds", n );
2293 else
2295 nlwarning( "AM: Environment sounds file not found: %s", filename );
2297 if ( treeRoot != NULL )
2299 *treeRoot = _EnvSounds;
2304 // ******************************************************************
2306 struct CompareSources : public binary_function<const CSimpleSource*, const CSimpleSource*, bool>
2308 // Constructor
2309 CompareSources( const CVector &pos ) : _Pos(pos) {}
2311 // Operator()
2312 bool operator()( const CSimpleSource *s1, const CSimpleSource *s2 )
2314 if (s1->getPriority() < s2->getPriority())
2316 return true;
2318 else if (s1->getPriority() == s2->getPriority())
2320 // Equal priority, test distances to the listener
2321 const CVector &src1pos = s1->getPos();
2322 const CVector &src2pos = s2->getPos();;
2323 return ( (src1pos-_Pos).sqrnorm() < (src2pos-_Pos).sqrnorm() );
2325 else
2327 return false;
2331 // Listener pos
2332 const CVector &_Pos;
2336 // ******************************************************************
2337 uint32 CAudioMixerUser::getLoadedSampleSize()
2339 return _SampleBankManager->getTotalByteSize();
2342 void CAudioMixerUser::getLoadedSampleBankInfo(std::vector<std::pair<std::string, uint> > &result)
2344 _SampleBankManager->getLoadedSampleBankInfo(result);
2349 void CAudioMixerUser::setListenerPos (const NLMISC::CVector &pos)
2351 _Listener.setPos(pos);
2352 _BackgroundSoundManager->setListenerPosition(pos);
2355 NLMISC_CATEGORISED_COMMAND(nel, displaySoundInfo, "Display information about the audio mixer", "")
2357 nlunreferenced(rawCommandString);
2358 nlunreferenced(quiet);
2359 nlunreferenced(human);
2361 if(args.size() != 0) return false;
2363 if (CAudioMixerUser::instance() == NULL)
2365 log.displayNL ("No audio mixer available");
2366 return true;
2369 log.displayNL ("%d tracks, MAX_TRACKS = %d, contains:", CAudioMixerUser::instance()->_Tracks.size(), CAudioMixerUser::instance()->getSoundDriver()->countMaxSources());
2371 for (uint i = 0; i < CAudioMixerUser::instance()->_Tracks.size(); i++)
2373 if (CAudioMixerUser::instance()->_Tracks[i] == NULL)
2375 log.displayNL ("Track %d is NULL", i);
2377 else
2379 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"));
2380 if (CAudioMixerUser::instance()->_Tracks[i]->getLogicalSource() == NULL)
2382 log.displayNL (" CSourceCommon is NULL");
2384 else
2386 const CVector &pos = CAudioMixerUser::instance()->_Tracks[i]->getLogicalSource()->getPos();
2387 string bufname;
2388 CSourceCommon *src = CAudioMixerUser::instance()->_Tracks[i]->getLogicalSource();
2389 switch (src->getType())
2391 case CSourceCommon::SOURCE_SIMPLE:
2393 CSimpleSource *simpleSrc = static_cast<CSimpleSource *>(src);
2394 if (simpleSrc->getBuffer())
2395 bufname = CStringMapper::unmap(simpleSrc->getBuffer()->getName());
2396 log.displayNL(" CSourceCommon is CSimpleSource is id %d buffer name '%s' pos %f %f %f", simpleSrc->getSound(), bufname.c_str(), pos.x, pos.y, pos.z);
2398 break;
2399 default:
2400 log.displayNL(" CSourceCommon is id %d pos %f %f %f", src->getSound(), pos.x, pos.y, pos.z);
2401 break;
2407 return true;
2410 void CAudioMixerUser::registerBufferAssoc(CSound *sound, IBuffer *buffer)
2412 _BufferToSources[buffer].push_back(sound);
2415 void CAudioMixerUser::unregisterBufferAssoc(CSound *sound, IBuffer *buffer)
2417 TBufferToSourceContainer::iterator it(_BufferToSources.find(buffer));
2418 if (it != _BufferToSources.end())
2420 std::vector<CSound*>::iterator first(it->second.begin()), last(it->second.end());
2422 for (; first != last; ++first)
2424 if (*first == sound)
2426 it->second.erase(first);
2427 break;
2434 /// Register an object in the update list.
2435 void CAudioMixerUser::registerUpdate(CAudioMixerUser::IMixerUpdate *pmixerUpdate)
2437 // nldebug("Registering update %p", pmixerUpdate);
2438 nlassert(pmixerUpdate != 0);
2439 _UpdateEventList.push_back(make_pair(pmixerUpdate, true));
2441 /// Unregister an object from the update list.
2442 void CAudioMixerUser::unregisterUpdate(CAudioMixerUser::IMixerUpdate *pmixerUpdate)
2444 // nldebug("Unregistering update %p", pmixerUpdate);
2445 nlassert(pmixerUpdate != 0);
2446 _UpdateEventList.push_back(make_pair(pmixerUpdate, false));
2449 /// Add an event in the future.
2450 void CAudioMixerUser::addEvent( CAudioMixerUser::IMixerEvent *pmixerEvent, const NLMISC::TTime &date)
2452 nlassert(pmixerEvent != 0);
2453 // nldebug("Adding event %p", pmixerEvent);
2454 _EventListUpdate.push_back(make_pair(date, pmixerEvent));
2457 /// Remove any event programmed for this object.
2458 void CAudioMixerUser::removeEvents( CAudioMixerUser::IMixerEvent *pmixerEvent)
2460 nlassert(pmixerEvent != 0);
2461 // nldebug("Removing event %p", pmixerEvent);
2463 // we have to remove from the _EventListUpdate, in the case a IMixerEvent is
2464 // added/removed during the same frame!!!
2465 // Slow O(N) but _EventListUpdate should be small, cause cleared each frame and not so
2466 // many events are added/removed.
2467 std::list<std::pair<NLMISC::TTime, IMixerEvent*> >::iterator itUp;
2468 for(itUp=_EventListUpdate.begin(); itUp!=_EventListUpdate.end();)
2470 if(itUp->second == pmixerEvent)
2471 itUp= _EventListUpdate.erase(itUp);
2472 else
2473 itUp++;
2476 // remove from the both _EventList and _Events multimap
2477 pair<TEventContainer::iterator, TEventContainer::iterator> range = _Events.equal_range(pmixerEvent);
2478 TEventContainer::iterator first(range.first), last(range.second);
2479 for (; first != last; ++first)
2481 _EventList.erase(first->second);
2483 _Events.erase(range.first, range.second);
2486 void CAudioMixerUser::setBackgroundFlags(const TBackgroundFlags &backgroundFlags)
2488 _BackgroundSoundManager->setBackgroundFlags(backgroundFlags);
2491 void CAudioMixerUser::setBackgroundFilterFades(const TBackgroundFilterFades &backgroundFilterFades)
2493 _BackgroundSoundManager->setBackgroundFilterFades(backgroundFilterFades);
2497 //void CAudioMixerUser::loadBackgroundSoundFromRegion (const NLLIGO::CPrimRegion &region)
2499 // _BackgroundSoundManager->loadSoundsFromRegion(region);
2502 //void CAudioMixerUser::loadBackgroundEffectsFromRegion (const NLLIGO::CPrimRegion &region)
2504 // _BackgroundSoundManager->loadEffecsFromRegion(region);
2506 //void CAudioMixerUser::loadBackgroundSamplesFromRegion (const NLLIGO::CPrimRegion &region)
2508 // _BackgroundSoundManager->loadSamplesFromRegion(region);
2511 void CAudioMixerUser::loadBackgroundAudioFromPrimitives(const NLLIGO::IPrimitive &audioRoot)
2513 _BackgroundSoundManager->loadAudioFromPrimitives(audioRoot);
2516 void CAudioMixerUser::playBackgroundSound ()
2518 _BackgroundSoundManager->play ();
2521 void CAudioMixerUser::stopBackgroundSound ()
2523 _BackgroundSoundManager->stop ();
2526 void CAudioMixerUser::loadBackgroundSound (const std::string &continent, NLLIGO::CLigoConfig &config)
2528 _BackgroundSoundManager->load (continent, config);
2531 void CAudioMixerUser::startDriverBench()
2533 if (_SoundDriver)
2534 _SoundDriver->startBench();
2537 void CAudioMixerUser::endDriverBench()
2539 if (_SoundDriver)
2540 _SoundDriver->endBench();
2543 void CAudioMixerUser::displayDriverBench(CLog *log)
2545 if (_SoundDriver)
2546 _SoundDriver->displayBench(log);
2549 // ***************************************************************************
2550 void CAudioMixerUser::changeMaxTrack(uint maxTrack)
2552 uint max_track_old = maxTrack;
2553 maxTrack = min(maxTrack, _SoundDriver->countMaxSources());
2554 if (maxTrack != max_track_old) nlwarning("AM: MaxTrack limited from %u to %u", (uint32)max_track_old, (uint32)maxTrack);
2556 // if same, no op
2557 if (maxTrack == _Tracks.size())
2558 return;
2560 uint prev_track_nb = (uint)_Tracks.size();
2561 // **** if try to add new tracks, easy
2562 if (maxTrack > prev_track_nb)
2564 uint i = 0;
2565 _Tracks.resize(maxTrack, NULL);
2568 for (i = prev_track_nb; i < maxTrack; ++i)
2570 nlassert(!_Tracks[i]);
2572 _Tracks[i] = new CTrack();
2573 _Tracks[i]->init(_SoundDriver);
2574 if (_UseEax) _Tracks[i]->getPhysicalSource()->setEffect(_ReverbEffect);
2575 // insert in front because the last inserted wan be sofware buffer...
2576 _FreeTracks.insert(_FreeTracks.begin(), _Tracks[i]);
2579 catch (const ESoundDriver &)
2581 delete _Tracks[i];
2582 // If the source generation failed, keep only the generated number of sources
2583 maxTrack = i;
2584 _Tracks.resize(maxTrack);
2585 nlwarning("AM: Failed to create another track, MaxTrack is %u now", (uint32)maxTrack);
2588 // **** else must delete some tracks
2589 else
2591 vector<CTrack *> non_erasable;
2592 while (_Tracks.size() + non_erasable.size() > maxTrack && !_Tracks.empty())
2594 CTrack *track = _Tracks.back();
2595 _Tracks.pop_back();
2596 nlassert(track);
2597 if (track->isAvailable())
2599 _FreeTracks.erase(find(_FreeTracks.begin(), _FreeTracks.end(), track));
2600 delete track;
2602 else if (track->getLogicalSource())
2604 track->getLogicalSource()->stop();
2605 if (track->getLogicalSource())
2607 nlwarning("AM: cant stop a track");
2608 non_erasable.push_back(track);
2610 else
2612 _FreeTracks.erase(find(_FreeTracks.begin(), _FreeTracks.end(), track));
2613 delete track;
2616 else /* music track or something */
2618 non_erasable.push_back(track);
2621 while (!non_erasable.empty())
2623 // put non erasable back into track list
2624 _Tracks.push_back(non_erasable.back());
2625 non_erasable.pop_back();
2627 if (maxTrack != _Tracks.size())
2628 nlwarning("AM: Failed to reduce number of tracks; MaxTrack is now %u instead of the requested %u", (uint32)_Tracks.size(), (uint32)maxTrack);
2632 // ***************************************************************************
2633 // insert calls of this where you want to debug events
2634 void CAudioMixerUser::debugLogEvent(const char *reason)
2636 nlinfo("****** EVENTLOG: end of %s", reason);
2637 nlinfo("****** _EventListUpdate: %d", _EventListUpdate.size());
2638 std::list<std::pair<NLMISC::TTime, IMixerEvent*> >::const_iterator itUp;
2639 for(itUp=_EventListUpdate.begin();itUp!=_EventListUpdate.end();itUp++)
2641 nlinfo("\t: %d - %x", (uint32)itUp->first, itUp->second);
2643 nlinfo("****** _EventList: %d", _EventList.size());
2644 TTimedEventContainer::const_iterator it;
2645 for(it=_EventList.begin();it!=_EventList.end();it++)
2647 nlinfo("\t: %d - %x", (uint32)it->first, it->second.ptr());
2649 nlinfo("****** _Events: %d", _Events.size());
2650 TEventContainer::const_iterator itEv;
2651 for(itEv=_Events.begin();itEv!=_Events.end();itEv++)
2653 nlinfo("\t: %x - (%d,%x)", itEv->first, (uint32)itEv->second->first, itEv->second->second.ptr());
2658 // ***************************************************************************
2659 bool CAudioMixerUser::playMusicChannel(TMusicChannel chan, const std::string &fileName, uint xFadeTime, bool async, bool loop)
2661 if (_MusicChannelFaders[chan].isInitOk())
2662 return _MusicChannelFaders[chan].play(fileName, xFadeTime, async, loop);
2663 return false;
2666 // ***************************************************************************
2667 bool CAudioMixerUser::playMusic(const std::string &fileName, uint xFadeTime, bool async, bool loop)
2669 return playMusicChannel(GeneralMusicChannel, fileName, xFadeTime, async, loop);
2672 // ***************************************************************************
2673 void CAudioMixerUser::stopMusic(uint xFadeTime)
2675 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2676 _MusicChannelFaders[GeneralMusicChannel].stop(xFadeTime);
2679 // ***************************************************************************
2680 void CAudioMixerUser::pauseMusic()
2682 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2683 _MusicChannelFaders[GeneralMusicChannel].pause();
2686 // ***************************************************************************
2687 void CAudioMixerUser::resumeMusic()
2689 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2690 _MusicChannelFaders[GeneralMusicChannel].resume();
2693 // ***************************************************************************
2694 bool CAudioMixerUser::isMusicEnded()
2696 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2697 return _MusicChannelFaders[GeneralMusicChannel].isEnded();
2698 return true;
2701 // ***************************************************************************
2702 void CAudioMixerUser::setMusicVolume(float gain)
2704 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2705 _MusicChannelFaders[GeneralMusicChannel].setVolume(gain);
2708 // ***************************************************************************
2709 float CAudioMixerUser::getMusicLength()
2711 if (_MusicChannelFaders[GeneralMusicChannel].isInitOk())
2712 return _MusicChannelFaders[GeneralMusicChannel].getLength();
2713 return 0.0f;
2716 // ***************************************************************************
2717 bool CAudioMixerUser::getSongTitle(const std::string &filename, std::string &result, float &length)
2719 if (_SoundDriver)
2721 std::string artist;
2722 std::string title;
2724 if (!_SoundDriver->getMusicInfo(filename, artist, title, length))
2726 // use 3rd party libraries supported formats
2727 IAudioDecoder::getInfo(filename, artist, title, length);
2730 if (!title.empty())
2732 if (!artist.empty()) result = artist + " - " + title;
2733 else result = title;
2735 else if (!artist.empty())
2737 result = artist + " - " + CFile::getFilename(filename);
2739 else
2741 result = CFile::getFilename(filename);
2744 return true;
2747 result = "???";
2748 length = 0;
2749 return false;
2752 // ***************************************************************************
2753 void CAudioMixerUser::enableBackgroundMusic(bool enable)
2755 getBackgroundMusicManager()->enable(enable);
2758 // ***************************************************************************
2759 void CAudioMixerUser::enableBackgroundMusicTimeConstraint(bool enable)
2761 getBackgroundMusicManager()->enableTimeConstraint(enable);
2764 // ***************************************************************************
2765 bool CAudioMixerUser::playEventMusic(const std::string &fileName, uint xFadeTime, bool async, bool loop)
2767 return playMusicChannel(EventMusicChannel, fileName, xFadeTime, async, loop);
2770 // ***************************************************************************
2771 void CAudioMixerUser::stopEventMusic(uint /* xFadeTime */)
2773 if (_MusicChannelFaders[EventMusicChannel].isInitOk())
2774 _MusicChannelFaders[EventMusicChannel].stop();
2777 // ***************************************************************************
2778 void CAudioMixerUser::setEventMusicVolume(float gain)
2780 if (_MusicChannelFaders[EventMusicChannel].isInitOk())
2781 _MusicChannelFaders[EventMusicChannel].setVolume(gain);
2784 // ***************************************************************************
2785 bool CAudioMixerUser::isEventMusicEnded()
2787 if (_MusicChannelFaders[EventMusicChannel].isInitOk())
2788 return _MusicChannelFaders[EventMusicChannel].isEnded();
2789 return true;
2792 /// Get audio/container extensions that are currently supported by nel or the used driver implementation.
2793 void CAudioMixerUser::getMusicExtensions(std::vector<std::string> &extensions)
2795 if (_SoundDriver)
2797 // add file formats supported by driver
2798 _SoundDriver->getMusicExtensions(extensions);
2801 // add 3rd party libraries support file formats
2802 IAudioDecoder::getMusicExtensions(extensions);
2805 /// Add a reverb environment
2806 void CAudioMixerUser::addEnvironment(const std::string &environmentName, const IReverbEffect::CEnvironment &environment)
2808 if (_ReverbEffect)
2810 TStringId environment_name = CStringMapper::map(environmentName);
2812 if (_Environments.find(environment_name) != _Environments.end())
2813 nlwarning("Reverb environment %s already exists, replacing with new one", CStringMapper::unmap(environment_name).c_str());
2815 _Environments[environment_name] = environment;
2819 /// Set the current reverb environment
2820 void CAudioMixerUser::setEnvironment(NLMISC::TStringId environmentName, float roomSize)
2822 if (_ReverbEffect)
2824 _ReverbEffect->setEnvironment(getEnvironment(environmentName), roomSize);
2828 /// Get a reverb environment
2829 const IReverbEffect::CEnvironment &CAudioMixerUser::getEnvironment(NLMISC::TStringId environmentName)
2831 TEnvironments::iterator it(_Environments.find(environmentName));
2832 if (it == _Environments.end())
2834 nlwarning("Reverb environment '%s' does not exist, returning default", CStringMapper::unmap(environmentName).c_str());
2835 return _DefaultEnvironment;
2837 return it->second;
2840 #if !FINAL_VERSION
2842 NLMISC_CATEGORISED_COMMAND(nel, displaySoundProfile, "Display information on sound driver", "")
2844 nlunreferenced(rawCommandString);
2845 nlunreferenced(args);
2846 nlunreferenced(quiet);
2847 nlunreferenced(human);
2849 string performance;
2850 CAudioMixerUser::getInstance()->writeProfile(performance);
2851 vector<string> pv;
2852 explode<string>(performance, "\n", pv, true);
2853 vector<string>::iterator it(pv.begin()), end(pv.end());
2854 for (; it != end; ++it) log.displayNL((*it).c_str());
2855 return true;
2858 #endif /* !FINAL_VERSION */
2861 } // NLSOUND