1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
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>
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/>.
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"
67 using namespace NLMISC
;
71 #define NL_TRACE_MIXER 0
74 #define _profile(_a) nldebug ## _a
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
);
92 // ******************************************************************
94 UAudioMixer
*UAudioMixer::createAudioMixer()
96 return new CAudioMixerUser();
100 // ******************************************************************
102 CAudioMixerUser::CAudioMixerUser() : _AutoLoadSample(false),
106 _SampleBankManager(NULL
),
107 _BackgroundSoundManager(NULL
),
110 _ListenPosition(CVector::Null
),
111 _BackgroundMusicManager(NULL
),
113 _PlayingSourcesMuted(0),
123 // init the filter names and short names
124 for (uint i
=0; i
<TBackgroundFlags::NB_BACKGROUND_FLAGS
; ++i
)
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
;
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();
159 for (uint i
= 0; i
< _Tracks
.size(); ++i
)
166 delete _ReverbEffect
; _ReverbEffect
= NULL
;
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
210 TSourceContainer::iterator first(_Sources.begin()), last(_Sources.end());
211 for (; first != last; ++first)
213 CSimpleSource *psu = *first;
214 if (psu->getTrack() == NULL)
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)
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";
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()
280 _SourceWaitingForPlay
.clear();
282 for (uint i
= 0; i
< _NbMusicChannelFaders
; ++i
)
283 _MusicChannelFaders
[i
].reset();
287 for (i
= 0; i
< _Tracks
.size(); ++i
)
291 CSourceCommon
* src
= _Tracks
[i
]->getLogicalSource();
293 if (src
&& src
->isPlaying())
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 !
307 TSourceContainer::iterator
first(_Sources
.begin()), last(_Sources
.end());
308 for (; first
!= last
; ++first
)
310 if ((*first
)->isPlaying())
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())
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
));
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
;
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
)
399 delete _SoundDriver
; _SoundDriver
= NULL
;
404 delete _SoundDriver
; _SoundDriver
= NULL
;
409 /// Get the available devices on the loaded driver.
410 void CAudioMixerUser::getDevices(std::vector
<std::string
> &devices
)
414 nlwarning("AM: You must call 'initDriver' before calling 'getDevices'");
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
)
426 nlwarning("AM: You must call 'initDriver' before calling 'initDevice'");
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
;
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");
463 if (_UseADPCM
&& !_SoundDriver
->getOption(ISoundDriver::OptionAllowADPCM
))
465 nlwarning("AM: OptionAllowADPCM not available, _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
)
487 delete _SoundDriver
; _SoundDriver
= NULL
;
492 delete _SoundDriver
; _SoundDriver
= NULL
;
498 // Init registrable classes
499 static bool initialized
= false;
506 _Listener
.init(_SoundDriver
);
508 // Init environment reverb effects
511 _ReverbEffect
= _SoundDriver
->createReverbEffect();
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.
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
);
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
)
641 sampleBanks
->getArraySize(size
);
642 for (uint i
=0; i
<size
; ++i
)
645 sampleBanks
->getArrayValue(name
, i
);
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");
660 TBackgroundFlags flags
;
661 TBackgroundFilterFades fades
;
664 bgFlags
->getArraySize(size
);
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
);
699 NLGEORGES::UFormLoader::releaseLoader(formLoader
);
704 NLGEORGES::UFormLoader::releaseLoader(formLoader
);
707 // init the user var bindings
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
);
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
;
747 sample
.serialBuffer(&buffer
[0], sample
.getFileSize());
749 std::vector
<uint8
> result
;
750 IBuffer::TBufferFormat bufferFormat
;
755 if (!IBuffer::readWav(&buffer
[0], size
, result
, bufferFormat
, channels
, bitsPerSample
, frequency
))
757 nlwarning(" IBuffer::readWav returned false");
761 vector
<sint16
> mono16Data
;
762 if (!IBuffer::convertToMono16PCM(&result
[0], (uint
)result
.size(), mono16Data
, bufferFormat
, channels
, bitsPerSample
))
764 nlwarning(" IBuffer::convertToMono16PCM returned false");
768 vector
<uint8
> adpcmData
;
769 if (!IBuffer::convertMono16PCMToMonoADPCM(&mono16Data
[0], (uint
)mono16Data
.size(), adpcmData
))
771 nlwarning(" IBuffer::convertMono16PCMToMonoADPCM returned false");
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
);
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());
810 void CAudioMixerUser::buildSampleBankList()
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
);
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
);
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
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
;
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
])
904 // check modification date
905 if (CFile::getFileModificationDate(sampleList
[j
]) >= CFile::getFileModificationDate(bankFile
[i
]))
913 catch(const Exception
&)
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
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;
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);
976 // update the searh path content
977 bool compressed
= CPath::isMemoryCompressed();
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
];
1000 const std::string
&CAudioMixerUser::getBackgroundFlagShortName(uint flagIndex
)
1002 static std::string
bad("");
1003 if (flagIndex
< TBackgroundFlags::NB_BACKGROUND_FLAGS
)
1004 return _BackgroundFilterShortNames
[flagIndex
];
1009 const UAudioMixer::TBackgroundFlags
&CAudioMixerUser::getBackgroundFlags()
1011 return _BackgroundSoundManager
->getBackgroundFlags();
1013 const UAudioMixer::TBackgroundFilterFades
&CAudioMixerUser::getBackgroundFilterFades()
1015 return _BackgroundSoundManager
->getBackgroundFilterFades();
1019 class CUserVarSerializer
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
;
1032 CAudioMixerUser::CControledSources cs
;
1034 // preset the default value
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
;
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
);
1066 void serial (NLMISC::IStream
&s
)
1068 s
.serialCont(Controlers
);
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
);
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);
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
;
1160 Name
= CStringMapper::map(name
);
1161 s
.serialEnum(ParamId
);
1165 for (uint i
=0; i
<size
; ++i
)
1167 s
.serial(soundName
);
1168 SoundNames
.push_back(CStringMapper::map(soundName
));
1173 name
= CStringMapper::unmap(Name
);
1175 s
.serialEnum(ParamId
);
1177 uint32 size
= (uint32
)SoundNames
.size();
1180 for (uint i
=0; i
<size
; ++i
)
1182 soundName
= CStringMapper::unmap(SoundNames
[i
]);
1183 s
.serial(soundName
);
1187 // Default value to 0.
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
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
);
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.
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
);
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
)
1296 // ******************************************************************
1298 void CAudioMixerUser::enable( bool /* b */ )
1300 // TODO : rewrite this method
1306 _NbTracks = _MaxNbTracks;
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());
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
)
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())
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;
1388 // else if (steal) for (uint i = 0; i < _Tracks.size(); ++i)
1390 // CSourceCommon *src2 = _Tracks[i]->getLogicalSource();
1394 // if (_FreeTracks.empty())
1396 // nlwarning("No free track after cutting a playing sound source !");
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;
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());
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();
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
)
1459 // nldebug("Cutting source %p with source %p (%f > %f*%f)", src2, source, t1, tfactor, t2);
1460 // on peut cuter cette voie !
1462 if (_FreeTracks
.empty())
1464 nlwarning("No free track after cutting a playing sound source !");
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());
1483 // ******************************************************************
1485 //void CAudioMixerUser::freeTrackWithoutSource(CTrack *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
)
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
);
1523 if (source
->isPlaying())
1526 pos
.push_back(make_pair(source
->getTrack() == 0, source
->getVirtualPos()));
1528 pos
.push_back(make_pair(source
->getTrack() == 0,
1529 source
->getSourceRelativeMode()
1530 ? source
->getPos() + _ListenPosition
1531 : source
->getPos()));
1533 if (source
->getTrack() == 0)
1537 // nldebug ("Source %p playing on track %p", source, source->getTrack());
1542 else if (ps
->getType() == CSourceCommon::SOURCE_STREAM
)
1544 CStreamSource
*source
= static_cast<CStreamSource
*>(*first
);
1547 if (source
->isPlaying())
1550 pos
.push_back(make_pair(source
->getTrack() == 0, source
->getVirtualPos()));
1552 pos
.push_back(make_pair(source
->getTrack() == 0,
1553 source
->getSourceRelativeMode()
1554 ? source
->getPos() + _ListenPosition
1555 : source
->getPos()));
1557 if (source
->getTrack() == 0)
1561 // nldebug ("Source %p playing on track %p", source, source->getTrack());
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));
1582 #if NL_PROFILE_MIXER
1583 TTicks start
= CTime::getPerformanceTime();
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
)
1596 // nldebug("Inserting update %p", first->first);
1597 _UpdateList
.insert(first
->first
);
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
)
1614 nlwarning("NULL pointeur in update list !");
1618 // call the update method.
1619 const IMixerUpdate
*update
= *first
;
1620 const_cast<IMixerUpdate
*>(update
)->onUpdate();
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
)
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
)
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())
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();
1677 // update the background sound
1678 _BackgroundSoundManager
->updateBackgroundStatus();
1680 // update the background music
1681 _BackgroundMusicManager
->update();
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();
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();
1709 // nldebug("After POP Sources waiting : %u", _SourceWaitingForPlay.size());
1714 if (_ClusteredSound
)
1716 H_AUTO(NLSOUND_UpdateClusteredSound
)
1717 // update the clustered sound...
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());
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
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)
1752 _Tracks
[i
]->DrvSource
->setEAXProperty(DSPROPERTY_EAXBUFFER_OCCLUSIONROOMRATIO
, (void*)&css
->OcclusionRoomRatio
, sizeof(css
->OcclusionRoomRatio
));
1753 // lastRatio[i] = css->OcclusionRoomRatio;
1754 // nldebug("Setting room ration.");
1756 _Tracks
[i
]->DrvSource
->setEAXProperty(DSPROPERTY_EAXBUFFER_OBSTRUCTION
, (void*)&css
->Obstruction
, sizeof(css
->Obstruction
));
1769 nldebug( "List of the %u tracks", _NbTracks );
1770 for ( i=0; i!=_NbTracks; i++ )
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
);
1791 /* // display the track using...
1793 char tmp[2048] = "";
1796 for (uint i=0; i<_NbTracks/2; ++i)
1798 sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
1801 nldebug((string("Status1: ")+str).c_str());
1803 for (i=_NbTracks/2; i<_NbTracks; ++i)
1805 sprintf(tmp, "[%2u]%8p ", i, _Tracks[i]->getSource());
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
)
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
))
1858 bool CAudioMixerUser::tryToLoadSampleBank(const std::string
&sampleName
)
1860 string path
= CPath::lookup(sampleName
, false, false, false);
1863 // extract samplebank name
1864 path
= NLMISC::CFile::getPath(path
);
1866 explode(path
, string("/"), rep
, true);
1868 loadSampleBank(false, rep
.back());
1874 nlwarning("tryToLoadSoundBank : can't find sample bank for '%s'", sampleName
.c_str());
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();
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() ));
1898 _profile(("AM: FAILED CREATESOURCE"));
1899 // nldebug( "AM: Sound not created: invalid sound id" );
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
);
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
);
1930 nlwarning("The sound %s contain an infinite recursion !", CStringMapper::unmap(id
->getName()).c_str());
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
))
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
);
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() )
1981 // source->set3DPositionVector( &_ListenPosition );
1983 // // no, we don't, there's setSourceRelativeMode for that -_-
1987 // nlassert(false); // FIXME
1988 //} // FIXED [KAETEMI]
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
));
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
));
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
));
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
));
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
));
2027 case CSound::SOUND_CONTEXT
:
2029 static CSoundContext defaultContext
;
2030 // This is a context sound.
2032 context
= &defaultContext
;
2034 CContextSound
*ctxSound
= static_cast<CContextSound
*>(id
);
2035 CSound
*sound
= ctxSound
->getContextSound(*context
);
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
2042 ret
->setGain(ret
->getGain() * ctxSound
->getGain());
2043 float pitch
= ret
->getPitch() * ctxSound
->getPitch();
2044 ret
->setPitch(pitch
);
2053 // nlassertex(false, ("Unknown sound class !"));
2054 nlwarning("Unknow sound class : %u", id
->getSoundType());
2059 #if NL_PROFILE_MIXER
2060 _CreateTime
= CTime::ticksToSecond(CTime::getPerformanceTime() - start
);
2064 //nldebug( "AM: Source created" );
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
);
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"));
2096 vector<CEnvEffect*>::iterator ipe;
2097 for ( ipe=_EnvEffects.begin(); ipe!=_EnvEffects.end(); ++ipe )
2099 (*ipe)->selectEnv( tag );
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 )
2124 _EnvEffects.clear();
2126 string str = CPath::lookup( filename, false );
2130 if ( !str.empty() && file.open(str) )
2132 uint32 n = CEnvEffect::load( _EnvEffects, file );
2133 nldebug( "AM: Loaded %u environmental effects", n );
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
);
2156 // create a new sample bank
2157 bank
= new CSampleBank(nameId
, _SampleBankManager
);
2164 catch (const Exception
& e
)
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
));
2188 // ok, the bank exist.
2189 return pbank
->unload();
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
2217 for (TSourceContainer::const_iterator
it(_Sources
.begin()), end(_Sources
.end()); it
!= end
; ++it
)
2219 if ((*it
)->getType() == CSourceCommon::SOURCE_SIMPLE
&& (*it
)->isPlaying())
2225 uint
CAudioMixerUser::countSimpleSources() const
2228 for (TSourceContainer::const_iterator
it(_Sources
.begin()), end(_Sources
.end()); it
!= end
; ++it
)
2230 if ((*it
)->getType() == CSourceCommon::SOURCE_SIMPLE
)
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
2258 TSourceContainer::const_iterator ips
;
2259 for ( ips
=_Sources
.begin(); ips
!=_Sources
.end(); ++ips
)
2261 if ( (*ips
)->isPlaying() )
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";
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 );
2288 if ( !str.empty() && file.open( str ) )
2290 uint32 n = 0; //CEnvSoundUser::load( _EnvSounds, file );
2291 nldebug( "AM: Loaded %u environment sounds", n );
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>
2309 CompareSources( const CVector
&pos
) : _Pos(pos
) {}
2312 bool operator()( const CSimpleSource
*s1
, const CSimpleSource
*s2
)
2314 if (s1
->getPriority() < s2
->getPriority())
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() );
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");
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
);
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");
2386 const CVector
&pos
= CAudioMixerUser::instance()->_Tracks
[i
]->getLogicalSource()->getPos();
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
);
2400 log
.displayNL(" CSourceCommon is id %d pos %f %f %f", src
->getSound(), pos
.x
, pos
.y
, pos
.z
);
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
);
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
);
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 ®ion)
2499 // _BackgroundSoundManager->loadSoundsFromRegion(region);
2502 //void CAudioMixerUser::loadBackgroundEffectsFromRegion (const NLLIGO::CPrimRegion ®ion)
2504 // _BackgroundSoundManager->loadEffecsFromRegion(region);
2506 //void CAudioMixerUser::loadBackgroundSamplesFromRegion (const NLLIGO::CPrimRegion ®ion)
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()
2534 _SoundDriver
->startBench();
2537 void CAudioMixerUser::endDriverBench()
2540 _SoundDriver
->endBench();
2543 void CAudioMixerUser::displayDriverBench(CLog
*log
)
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
);
2557 if (maxTrack
== _Tracks
.size())
2560 uint prev_track_nb
= (uint
)_Tracks
.size();
2561 // **** if try to add new tracks, easy
2562 if (maxTrack
> prev_track_nb
)
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
&)
2582 // If the source generation failed, keep only the generated number of sources
2584 _Tracks
.resize(maxTrack
);
2585 nlwarning("AM: Failed to create another track, MaxTrack is %u now", (uint32
)maxTrack
);
2588 // **** else must delete some tracks
2591 vector
<CTrack
*> non_erasable
;
2592 while (_Tracks
.size() + non_erasable
.size() > maxTrack
&& !_Tracks
.empty())
2594 CTrack
*track
= _Tracks
.back();
2597 if (track
->isAvailable())
2599 _FreeTracks
.erase(find(_FreeTracks
.begin(), _FreeTracks
.end(), 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
);
2612 _FreeTracks
.erase(find(_FreeTracks
.begin(), _FreeTracks
.end(), 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
);
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();
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();
2716 // ***************************************************************************
2717 bool CAudioMixerUser::getSongTitle(const std::string
&filename
, std::string
&result
, float &length
)
2724 if (!_SoundDriver
->getMusicInfo(filename
, artist
, title
, length
))
2726 // use 3rd party libraries supported formats
2727 IAudioDecoder::getInfo(filename
, artist
, title
, length
);
2732 if (!artist
.empty()) result
= artist
+ " - " + title
;
2733 else result
= title
;
2735 else if (!artist
.empty())
2737 result
= artist
+ " - " + CFile::getFilename(filename
);
2741 result
= CFile::getFilename(filename
);
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();
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
)
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
)
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
)
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
;
2842 NLMISC_CATEGORISED_COMMAND(nel
, displaySoundProfile
, "Display information on sound driver", "")
2844 nlunreferenced(rawCommandString
);
2845 nlunreferenced(args
);
2846 nlunreferenced(quiet
);
2847 nlunreferenced(human
);
2850 CAudioMixerUser::getInstance()->writeProfile(performance
);
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());
2858 #endif /* !FINAL_VERSION */