1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 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) 2019 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/>.
24 #include "nel/misc/file.h"
25 #include "nel/misc/i_xml.h"
26 #include "nel/misc/path.h"
27 #include "nel/misc/hierarchical_timer.h"
29 #include "nel/ligo/primitive.h"
30 #include "nel/3d/cluster.h"
32 #include "nel/sound/u_source.h"
33 #include "nel/sound/clustered_sound.h"
34 #include "nel/sound/sample_bank_manager.h"
35 #include "nel/sound/sample_bank.h"
37 #include "nel/sound/background_sound_manager.h"
38 #include "nel/sound/source_common.h"
39 #include "nel/sound/clustered_sound.h"
40 #include "nel/sound/background_source.h"
43 using namespace NLMISC
;
44 using namespace NLLIGO
;
49 // external sound are cliping after 10 meter inside the inner patate
50 const float INSIDE_FALLOF
= 10.0f
;
51 const float BACKGROUND_SOUND_ALTITUDE
= 5.0f
;
55 CBackgroundSoundManager::CBackgroundSoundManager()
56 : _Playing(false), _DoFade(false), _LastPosition(0,0,0)
58 for (uint i
=0; i
<UAudioMixer::TBackgroundFlags::NB_BACKGROUND_FLAGS
; ++i
)
60 _BackgroundFlags
.Flags
[i
] = false;
61 _FilterFadesStart
[i
] = 0;
62 _FilterFadeValues
[i
] = 1.0f
;
66 CBackgroundSoundManager::~CBackgroundSoundManager()
71 const UAudioMixer::TBackgroundFlags
&CBackgroundSoundManager::getBackgroundFlags()
73 return _BackgroundFlags
;
77 void CBackgroundSoundManager::setBackgroundFilterFades(const UAudioMixer::TBackgroundFilterFades
&backgroundFilterFades
)
79 _BackgroundFilterFades
= backgroundFilterFades
;
82 const UAudioMixer::TBackgroundFilterFades
&CBackgroundSoundManager::getBackgroundFilterFades()
84 return _BackgroundFilterFades
;
88 void CBackgroundSoundManager::addSound(const std::string
&soundName
, uint layerId
, const std::vector
<NLLIGO::CPrimVector
> &points
, bool isPath
)
90 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
93 sd
.SoundName
= CStringMapper::map(soundName
);
94 sd
.Sound
= mixer
->getSoundId(sd
.SoundName
);
98 sd
.Points
.resize (points
.size ());
99 for (uint i
=0; i
<points
.size (); i
++)
100 sd
.Points
[i
] = points
[i
];
107 // the sound is available !
108 // compute bouding box/
109 CVector
vmin(FLT_MAX
, FLT_MAX
, 0), vmax(-FLT_MAX
, -FLT_MAX
, 0);
111 vector
<CVector
>::iterator
first(sd
.Points
.begin()), last(sd
.Points
.end());
112 for (; first
!= last
; ++first
)
114 vmin
.x
= min(first
->x
, vmin
.x
);
115 vmin
.y
= min(first
->y
, vmin
.y
);
116 vmax
.x
= max(first
->x
, vmax
.x
);
117 vmax
.y
= max(first
->y
, vmax
.y
);
122 // compute the surface without the sound distance
123 sd
.Surface
= (vmax
.x
- vmin
.x
) * (vmax
.y
- vmin
.y
);
125 // add the eard distance of the sound.
126 float dist
= sd
.Sound
->getMaxDistance();
135 // TODO : handle the three layer.
136 _Layers
[layerId
].push_back(sd
);
140 nlwarning ("The sound '%s' can't be loaded", CStringMapper::unmap(sd
.SoundName
).c_str());
144 void CBackgroundSoundManager::addSound(const std::string
&rawSoundName
, const std::vector
<NLLIGO::CPrimVector
> &points
, bool isPath
)
149 // count the number of '-' in the string.
150 n
= (uint
)std::count(rawSoundName
.begin(), rawSoundName
.end(), '-');
154 // no layer spec, default to layer A
155 string::size_type pos1
= rawSoundName
.find ("-");
156 if(pos1
== string::npos
)
158 nlwarning ("zone have the malformated name '%s' missing -name-", rawSoundName
.c_str());
163 string::size_type pos2
= rawSoundName
.find ("-", pos1
);
164 if(pos2
== string::npos
)
166 nlwarning ("zone have the malformated name '%s' missing -name-", rawSoundName
.c_str());
170 name
= rawSoundName
.substr(pos1
, pos2
-pos1
);
175 string::size_type pos1
= rawSoundName
.find ("-");
176 string::size_type pos2
= rawSoundName
.find ("-", pos1
+1);
177 if(pos1
== string::npos
|| pos2
== string::npos
)
179 nlwarning ("zone have the malformated name '%s' missing -layerId- or -name-", rawSoundName
.c_str());
184 string::size_type pos3
= rawSoundName
.find ("-", pos2
+1);
185 if(pos3
== string::npos
)
187 nlwarning ("zone have the malformated name '%s' missing -name-", rawSoundName
.c_str());
191 char id
= rawSoundName
[pos1
];
195 id
= id
+ ('a' - 'A');
199 NLMISC::clamp(layerId
, 0u, BACKGROUND_LAYER
-1);
202 name
= rawSoundName
.substr(pos2
, pos3
-pos2
);
206 nlwarning ("zone have the malformated name '%s", rawSoundName
.c_str());
210 addSound(name
, layerId
, points
, isPath
);
215 sd.Sound = mixer->getSoundId(sd.SoundName);
219 sd.Points.resize (points.size ());
220 for (uint i=0; i<points.size (); i++)
221 sd.Points[i] = points[i];
228 // the sound is available !
229 // compute bouding box/
230 CVector vmin(FLT_MAX, FLT_MAX, 0), vmax(-FLT_MAX, -FLT_MAX, 0);
232 vector<CVector>::iterator first(sd.Points.begin()), last(sd.Points.end());
233 for (; first != last; ++first)
235 vmin.x = min(first->x, vmin.x);
236 vmin.y = min(first->y, vmin.y);
237 vmax.x = max(first->x, vmax.x);
238 vmax.y = max(first->y, vmax.y);
243 // compute the surface without the sound distance
244 sd.Surface = (vmax.x - vmin.x) * (vmax.y - vmin.y);
246 // add the eard distance of the sound.
247 float dist = sd.Sound->getMaxDistance();
256 // TODO : handle the three layer.
257 _Layers[layerId].push_back(sd);
261 nlwarning ("The sound '%s' can't be loaded", sd.SoundName.c_str());
267 void CBackgroundSoundManager::loadAudioFromPrimitives(const NLLIGO::IPrimitive
&audioRoot
)
269 std::string className
;
270 if(audioRoot
.getPropertyByName("class", className
))
272 if (className
== "audio")
274 // ok, it a root of the audio primitives
276 // remember playing state
277 bool oldState
= _Playing
;
280 for (uint i
=0; i
<audioRoot
.getNumChildren(); ++i
)
282 const NLLIGO::IPrimitive
*child
;
284 audioRoot
.getChild(child
, i
);
286 if (child
->getPropertyByName("class", className
))
288 if (className
== "sounds")
290 loadSoundsFromPrimitives(*child
);
292 else if (className
== "sample_banks")
294 loadSamplesFromPrimitives(*child
);
296 else if (className
== "env_fx")
298 loadEffectsFromPrimitives(*child
);
309 // try to look in the first child level
310 for (uint i
=0; i
<audioRoot
.getNumChildren(); ++i
)
312 const NLLIGO::IPrimitive
*child
;
313 audioRoot
.getChild(child
, i
);
315 if (child
->getPropertyByName("class", className
))
317 if (className
== "audio")
319 // recurse in this node
320 loadAudioFromPrimitives(*child
);
321 // don't look any other primitives
329 void CBackgroundSoundManager::loadSoundsFromPrimitives(const NLLIGO::IPrimitive
&soundRoot
)
331 std::string className
;
332 if (soundRoot
.getPropertyByName("class", className
))
334 if (className
== "sounds" || className
== "sound_folder")
336 // ok, it sounds or a sounds foilder
337 for (uint i
=0; i
<soundRoot
.getNumChildren(); ++i
)
339 const NLLIGO::IPrimitive
*child
;
340 std::string primName
;
341 soundRoot
.getChild(child
, i
);
344 if (child
->getPropertyByName("class", className
))
347 std::string layerString
;
348 std::string soundName
;
349 if (child
->getPropertyByName("layer", layerString
))
351 // extract layer number.
352 if (!layerString
.empty())
354 // TODO : handle special case for weather layer
355 layerId
= layerString
[layerString
.size()-1] - '0';
357 clamp(layerId
, 0u, BACKGROUND_LAYER
-1);
360 child
->getPropertyByName("name", primName
);
361 child
->getPropertyByName("sound", soundName
);
362 // compatibility with older primitive
363 if (soundName
.empty())
364 soundName
= primName
;
366 if (className
== "sound_zone")
368 if(child
->getNumVector()>2)
370 addSound(soundName
, layerId
, static_cast<const CPrimZone
*>(child
)->VPoints
, false);
374 nlwarning ("A background sound patatoid have less than 3 points '%s'", primName
.c_str());
377 else if (className
== "sound_path")
379 if(child
->getNumVector() > 1)
381 addSound(soundName
, layerId
, static_cast<const CPrimPath
*>(child
)->VPoints
, true);
385 nlwarning ("A background sound path have less than 2 points '%s'", primName
.c_str());
388 else if (className
== "sound_point")
390 std::vector
<NLLIGO::CPrimVector
> points
;
391 points
.push_back(static_cast<const CPrimPoint
*>(child
)->Point
);
393 addSound(soundName
, layerId
, points
, false);
395 else if (className
== "sound_folder")
397 loadSoundsFromPrimitives(*child
);
405 void CBackgroundSoundManager::loadSamplesFromPrimitives(const NLLIGO::IPrimitive
&sampleRoot
)
407 std::string className
;
409 if (sampleRoot
.getPropertyByName("class", className
))
411 if (className
== "sample_banks")
413 for (uint i
=0; i
<sampleRoot
.getNumChildren(); ++i
)
415 const NLLIGO::IPrimitive
*child
;
416 std::string primName
;
417 sampleRoot
.getChild(child
, i
);
419 if (child
->getPropertyByName("class", className
))
421 child
->getPropertyByName("name", primName
);
422 if (className
== "sample_bank_zone")
424 const std::vector
<std::string
> *names
;
425 if (child
->getPropertyByName("bank_names", names
))
427 addSampleBank(*names
, static_cast<const CPrimZone
*>(child
)->VPoints
);
436 void CBackgroundSoundManager::loadEffectsFromPrimitives(const NLLIGO::IPrimitive
&fxRoot
)
438 std::string className
;
441 if (fxRoot
.getPropertyByName("class", className
))
443 if (className
== "env_fx")
445 for (uint i
=0; i
<fxRoot
.getNumChildren(); ++i
)
447 const NLLIGO::IPrimitive
*child
;
448 std::string primName
;
449 fxRoot
.getChild(child
, i
);
451 if (child
->getPropertyByName("class", className
))
453 child
->getPropertyByName("name", primName
);
454 if (className
== "env_fx_zone")
457 if (child
->getPropertyByName("fx_name", fxName
))
459 addFxZone(fxName
, static_cast<const CPrimZone
*>(child
)->VPoints
);
468 void CBackgroundSoundManager::addFxZone(const std::string
&fxName
, const std::vector
<NLLIGO::CPrimVector
> &points
)
472 fxZone
.FxName
= CStringMapper::map(fxName
);
473 fxZone
.Points
.resize (points
.size());
474 for (uint j
=0; j
<points
.size(); j
++)
476 fxZone
.Points
[j
] = points
[j
];
479 // compute bouding box.
480 CVector
vmin(FLT_MAX
, FLT_MAX
, 0), vmax(-FLT_MAX
, -FLT_MAX
, 0);
482 vector
<CVector
>::iterator
first(fxZone
.Points
.begin()), last(fxZone
.Points
.end());
483 for (; first
!= last
; ++first
)
485 vmin
.x
= min(first
->x
, vmin
.x
);
486 vmin
.y
= min(first
->y
, vmin
.y
);
487 vmax
.x
= max(first
->x
, vmax
.x
);
488 vmax
.y
= max(first
->y
, vmax
.y
);
490 fxZone
.MaxBox
= vmax
;
491 fxZone
.MinBox
= vmin
;
493 _FxZones
.push_back(fxZone
);
497 void CBackgroundSoundManager::addSampleBank(const std::vector
<std::string
> &bankNames
, const std::vector
<CPrimVector
> &points
)
500 // uint pointCount = points.size ();
501 bd
.Points
.resize (points
.size());
502 for (uint j
=0; j
<points
.size(); j
++)
504 bd
.Points
[j
] = points
[j
];
507 // compute bouding box.
508 CVector
vmin(FLT_MAX
, FLT_MAX
, 0), vmax(-FLT_MAX
, -FLT_MAX
, 0);
510 vector
<CVector
>::iterator
first(bd
.Points
.begin()), last(bd
.Points
.end());
511 for (; first
!= last
; ++first
)
513 vmin
.x
= min(first
->x
, vmin
.x
);
514 vmin
.y
= min(first
->y
, vmin
.y
);
515 vmax
.x
= max(first
->x
, vmax
.x
);
516 vmax
.y
= max(first
->y
, vmax
.y
);
521 for(uint i
=0; i
<bankNames
.size(); ++i
)
523 if (!bankNames
[i
].empty())
524 bd
.Banks
.push_back(bankNames
[i
]);
527 // ok, store it in the container.
528 _Banks
.push_back(bd
);
532 //void CBackgroundSoundManager::loadSamplesFromRegion(const NLLIGO::CPrimRegion ®ion)
536 // for (uint i=0; i< region.VZones.size(); ++i)
538 // if (region.VZones[i].VPoints.size() > 2)
540 // // parse the zone name to find the samples name.
541 // std::vector<std::string> splitted = split(region.VZones[i].Name, '-');
542 // std::vector<std::string> bankNames;
544 // if (splitted.size() > 2)
546 // for (uint j=1; j<splitted.size()-1; ++j)
548 // bankNames.push_back(splitted[j]);
551 // addSampleBank(bankNames, region.VZones[i].VPoints);
555 // nlwarning ("A sample bank patatoid name did'nt contains banks name '%s'", region.VZones[i].Name.c_str());
560 // nlwarning ("A sample bank patatoid have less than 3 points '%s'", region.VZones[i].Name.c_str());
565 //void CBackgroundSoundManager::loadEffecsFromRegion(const NLLIGO::CPrimRegion ®ion)
569 //void CBackgroundSoundManager::loadSoundsFromRegion(const CPrimRegion ®ion)
572 // // remember playing state
573 // bool oldState = _Playing;
576 // for (i = 0; i < region.VZones.size(); i++)
578 // if(region.VZones[i].VPoints.size()>2)
580 // addSound(region.VZones[i].Name, region.VZones[i].VPoints, false);
584 // nlwarning ("A background sound patatoid have less than 3 points '%s'", region.VZones[i].Name.c_str());
588 // for (i = 0; i < region.VPaths.size(); i++)
590 // if(region.VPaths[i].VPoints.size() > 1)
592 // addSound(region.VPaths[i].Name, region.VPaths[i].VPoints, true);
596 // nlwarning ("A background sound path have less than 2 points '%s'", region.VPaths[i].Name.c_str());
599 // for (i = 0; i < region.VPoints.size(); i++)
601 // std::vector<CPrimVector> points;
602 // points.push_back(region.VPoints[i].Point);
604 // addSound(region.VPoints[i].Name, points, false);
608 // // restart playing ?
613 void CBackgroundSoundManager::load (const string
&continent
, NLLIGO::CLigoConfig
&config
)
615 uint32 PACKED_VERSION
= 1;
616 // First, try to load from a .primitive file (contain everythink)
619 // CPrimRegion region;
620 CPrimitives primitives
;
621 primitives
.RootNode
= new CPrimNode
;
622 string fn
= continent
+"_audio.primitive";
624 string path
= CPath::lookup(fn
, false);
626 if(!path
.empty() && file
.open (path
))
628 // first, try to load the binary version (if up to date)
631 string filename
= continent
+".background_primitive";
632 string binPath
= CPath::lookup(filename
, false, false, false);
634 && (CFile::getFileModificationDate(binPath
) > CFile::getFileModificationDate(path
)))
636 CIFile
binFile(binPath
);
637 binFile
.serial(version
);
639 if (version
== PACKED_VERSION
)
641 nlinfo ("loading '%s'", filename
.c_str());
643 binFile
.serialCont(_Banks
);
644 for (uint i
=0; i
<BACKGROUND_LAYER
; ++i
)
647 binFile
.serialCont(_Layers
[i
]);
650 binFile
.serialCont(_FxZones
);
658 nlinfo ("loading '%s'", fn
.c_str());
662 H_AUTO(BackgroundSoundMangerLoad_xml_init
);
667 H_AUTO(BackgroundSoundMangerLoad_primitive_read
);
668 primitives
.read(xml
.getRootNode(), fn
.c_str(), config
);
670 // region.serial(xml);
674 H_AUTO(BackgroundSoundMangerLoad_loadAudioFromPrimitive
);
675 loadAudioFromPrimitives(*primitives
.RootNode
);
678 // store the binary version of the audio primitive for later use
679 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
680 if (mixer
->getPackedSheetUpdate())
682 // need to update packed sheet, so write the binary primitive version
683 string filename
= mixer
->getPackedSheetPath()+"/"+continent
+".background_primitive";
684 COFile
file(filename
);
686 file
.serial(PACKED_VERSION
);
687 file
.serialCont(_Banks
);
688 for (uint i
=0; i
<BACKGROUND_LAYER
; ++i
)
689 file
.serialCont(_Layers
[i
]);
690 file
.serialCont(_FxZones
);
693 ////////////////////////////////////////////////
700 // We reach this only if the new .primitive file format is not found
701 // then, we try to load separate .prim file for sound, samples and fx
706 // CPrimRegion region;
707 // string fn = continent+"_audio.prim";
709 // nlinfo ("loading '%s'", fn.c_str());
711 // string path = CPath::lookup(fn, false);
713 // if(!path.empty() && file.open (path))
717 // region.serial(xml);
720 // nlinfo ("Region '%s' contains %d zones for the background sounds", continent.c_str(), region.VZones.size());
722 // loadSoundsFromRegion(region);
725 // // load the effect.
728 // CPrimRegion region;
729 // string fn = continent+"_effects.prim";
731 // nlinfo ("loading '%s'", fn.c_str());
733 // string path = CPath::lookup(fn, false);
735 // if(!path.empty() && file.open (path))
739 // region.serial(xml);
742 // nlinfo ("Region '%s' contains %d zones for the background effetcs", continent.c_str(), region.VZones.size());
744 // loadEffecsFromRegion(region);
747 // // load the samples banks.
750 // CPrimRegion region;
751 // string fn = continent+"_samples.prim";
753 // nlinfo ("loading '%s'", fn.c_str());
755 // string path = CPath::lookup(fn, false);
757 // if(!path.empty() && file.open (path))
761 // region.serial(xml);
764 // nlinfo ("Region '%s' contains %d zones for the background samples banks", continent.c_str(), region.VZones.size());
766 // loadSamplesFromRegion(region);
772 void CBackgroundSoundManager::play ()
779 CAudioMixerUser::instance()->registerUpdate(this);
781 // init the filter value and filter start time
782 for (uint i
=0; i
<UAudioMixer::TBackgroundFlags::NB_BACKGROUND_FLAGS
; ++i
)
784 _FilterFadesStart
[i
] = 0;
785 _FilterFadeValues
[i
] = 1.0f
* !_BackgroundFlags
.Flags
[i
];
787 // force an initial filtering
789 updateBackgroundStatus();
795 void CBackgroundSoundManager::stop ()
800 for (uint i
=0; i
<BACKGROUND_LAYER
; ++i
)
802 // stop all playing source
803 std::vector
<TSoundData
>::iterator
first(_Layers
[i
].begin()), last(_Layers
[i
].end());
804 for (; first
!= last
; ++first
)
806 if (first
->Source
!= 0 && first
->Source
->isPlaying())
807 first
->Source
->stop();
811 CAudioMixerUser::instance()->unregisterUpdate(this);
816 void CBackgroundSoundManager::unload ()
820 for (uint i
=0; i
<BACKGROUND_LAYER
; ++i
)
822 // delete all created source
823 std::vector
<TSoundData
>::iterator
first(_Layers
[i
].begin()), last(_Layers
[i
].end());
824 for (; first
!= last
; ++first
)
827 // mixer->removeSource(first->Source);
828 delete first
->Source
;
831 // and free the layer.
835 // erase the sample banks zone
838 // TODO : erase the fx zones
841 void CBackgroundSoundManager::setListenerPosition (const CVector
&listenerPosition
)
843 if (_LastPosition
== listenerPosition
)
847 _LastPosition
= listenerPosition
;
849 updateBackgroundStatus();
852 void CBackgroundSoundManager::updateBackgroundStatus()
854 H_AUTO(NLSOUND_UpdateBackgroundSound
)
858 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
861 // it s on 2d so we don't have z
862 CVector listener
= _LastPosition
;
865 // special case for clustered sound management. If the listener is not
866 // in the global cluster, it's background listening place could be different
867 CClusteredSound
*clusteredSound
= mixer
->getClusteredSound();
868 if (clusteredSound
!= 0)
870 const CClusteredSound::CClusterSoundStatus
*css
= clusteredSound
->getClusterSoundStatus(clusteredSound
->getRootCluster());
873 listener
= css
->Position
;
878 // evalutate the current env fx
879 if (mixer
->useEnvironmentEffects())
881 H_AUTO(NLSOUND_EvaluateEnvFx
)
882 NL3D::CCluster
*rootCluster
= 0;
883 if (mixer
->getClusteredSound())
884 rootCluster
= mixer
->getClusteredSound()->getRootCluster();
886 std::vector
<TFxZone
>::iterator
first(_FxZones
.begin()), last(_FxZones
.end());
887 for (; first
!= last
; ++first
)
889 if (listener
.x
>= first
->MinBox
.x
&& listener
.x
<= first
->MaxBox
.x
890 && listener
.y
>= first
->MinBox
.y
&& listener
.y
<= first
->MaxBox
.y
894 if (CPrimZone::contains(listener
, first
->Points
))
896 // stop at the first zone !
899 // use the cluster system
900 rootCluster
->setEnvironmentFx(first
->FxName
);
904 // no cluster system, set the env 'manualy'
905 if (_LastEnv
!= first
->FxName
)
907 // set an env with size 10.f
908 _LastEnv
= first
->FxName
;
909 mixer
->setEnvironment(first
->FxName
, 10.f
);
919 // compute the list of load/unload banks.
921 H_AUTO(NLSOUND_LoadUnloadSampleBank
)
922 // set of bank that must be in ram.
923 std::set
<std::string
> newBanks
;
925 std::vector
<TBanksData
>::iterator
first(_Banks
.begin()), last(_Banks
.end());
926 for (; first
!= last
; ++first
)
928 if (listener
.x
>= first
->MinBox
.x
&& listener
.x
<= first
->MaxBox
.x
929 && listener
.y
>= first
->MinBox
.y
&& listener
.y
<= first
->MaxBox
.y
933 if (CPrimZone::contains(listener
, first
->Points
))
935 // add the banks of this zone in the n
936 newBanks
.insert(first
->Banks
.begin(), first
->Banks
.end());
942 nldebug("-----------------------------");
943 nldebug("Loaded sample banks (%u elements):", _LoadedBanks.size());
944 set<string>::iterator first(_LoadedBanks.begin()), last(_LoadedBanks.end());
945 for (; first != last; ++first)
947 const string &str = *first;
948 nldebug(" %s", first->c_str());
952 nldebug("New Sample bank list (%u elements):", newBanks.size());
953 set<string>::iterator first(newBanks.begin()), last(newBanks.end());
954 for (; first != last; ++first)
956 const string &str = *first;
957 nldebug(" %s", first->c_str());
961 // ok, now compute to set : the set of bank to load, and the set of banks to unload.
962 std::set
<std::string
> noChange
;
963 std::set_intersection(_LoadedBanks
.begin(), _LoadedBanks
.end(), newBanks
.begin(), newBanks
.end(), std::inserter(noChange
, noChange
.end()));
965 std::set
<std::string
> loadList
;
966 std::set_difference(newBanks
.begin(), newBanks
.end(), noChange
.begin(), noChange
.end(), std::inserter(loadList
, loadList
.end()));
968 std::set
<std::string
> unloadList
;
969 std::set_difference(_LoadedBanks
.begin(), _LoadedBanks
.end(), newBanks
.begin(), newBanks
.end(), std::inserter(unloadList
, unloadList
.end()));
971 // and now, load and unload....
973 std::set
<std::string
>::iterator
first(loadList
.begin()), last(loadList
.end());
974 for (; first
!= last
; ++first
)
976 // nldebug("Trying to load sample bank %s", first->c_str());
977 mixer
->loadSampleBank(true, *first
);
979 _LoadedBanks
.insert(loadList
.begin(), loadList
.end());
982 std::set
<std::string
>::iterator
first(unloadList
.begin()), last(unloadList
.end());
983 for (; first
!= last
; ++first
)
985 // nldebug("Trying to unload sample bank %s", first->c_str());
986 if (mixer
->unloadSampleBank(*first
))
988 // ok, the bank is unloaded
989 _LoadedBanks
.erase(*first
);
991 else if (mixer
->getSampleBankManager()->findSampleBank(CStringMapper::map(*first
)) == 0)
993 // ok, the bank is unavailable !
994 _LoadedBanks
.erase(*first
);
1000 H_BEFORE(NLSOUND_UpdateSoundLayer
)
1001 // retreive the root cluster...
1002 NL3D::CCluster
*rootCluster
= 0;
1003 if (mixer
->getClusteredSound() != 0)
1004 rootCluster
= mixer
->getClusteredSound()->getRootCluster();
1006 // Apply the same algo for each sound layer.
1007 for (uint i
=0; i
<BACKGROUND_LAYER
; ++i
)
1009 vector
<TSoundData
> &layer
= _Layers
[i
];
1010 vector
<uint
> selectedIndex
;
1011 vector
<uint
> leaveIndex
;
1013 selectedIndex
.reserve(layer
.size());
1014 leaveIndex
.reserve(layer
.size());
1016 // extract the list of selected/unselected box
1017 vector
<TSoundData
>::iterator
first(layer
.begin()), last(layer
.end());
1018 for (uint count
= 0; first
!= last
; ++first
, ++count
)
1020 if (listener
.x
>= first
->MinBox
.x
&& listener
.x
<= first
->MaxBox
.x
1021 && listener
.y
>= first
->MinBox
.y
&& listener
.y
<= first
->MaxBox
.y
1022 // && listener.z >= first->MinBox.z && listener.z <= first->MaxBox.z
1025 // nldebug("patat %u is selected by box (%s)", count, first->SoundName.c_str());
1026 selectedIndex
.push_back(count
);
1030 // nldebug("patat %u is rejected by box (%s)", count, first->SoundName.c_str());
1031 // listener out of this box.
1032 if (first
->Selected
&& first
->Source
!= 0)
1034 // we leave this box.
1035 leaveIndex
.push_back(count
);
1040 // stop all the sound that are leaved.
1042 vector
<uint
>::iterator
first(leaveIndex
.begin()), last(leaveIndex
.end());
1043 for (; first
!= last
; ++first
)
1045 TSoundData
&sd
= layer
[*first
];
1046 sd
.Selected
= false;
1047 if (sd
.Source
->isPlaying())
1051 // Compute new source mixing in this layer
1053 /// Status of all selected sound ordered by surface.
1054 list
<pair
<float, TSoundStatus
> > status
;
1056 // first loop to compute selected sound gain and position and order the result by surface..
1058 vector
<uint
>::iterator
first(selectedIndex
.begin()), last(selectedIndex
.end());
1059 for (; first
!= last
; ++first
)
1061 TSoundData
&sd
= layer
[*first
];
1065 bool inside
= false;
1067 // inside the patat ?
1069 if(CPrimZone::contains(listener
, sd
.Points
, distance
, pos
, sd
.IsPath
))
1072 pos
= _LastPosition
; // use the real listener position, not the 0 z centered
1074 // nlinfo ("inside patate %d name '%s' ", *first, sd.SoundName.c_str());
1078 if (sd
.MaxDist
>0 && distance
< sd
.MaxDist
)
1080 // compute the gain.
1081 // gain = (sd.MaxDist - distance) / sd.MaxDist;
1088 //nlinfo ("near patate %d name '%s' from %f ", *first, sd.SoundName.c_str(), distance);
1091 // store the status.
1092 status
.push_back(make_pair(sd
.Surface
, TSoundStatus(sd
, pos
, gain
, distance
, inside
)));
1095 // second loop thrue the surface ordered selected sound.
1097 // Sound mixing strategie :
1098 // The smallest zone sound mask bigger one
1100 float maskFactor
= 1.0f
;
1102 list
<pair
<float, TSoundStatus
> >::iterator
first(status
.begin()), last(status
.end());
1103 for (; first
!= last
; ++first
)
1105 TSoundStatus
&ss
= first
->second
;
1107 // special algo for music sound (don't influence / use maskFactor strategy)
1108 bool musicSound
= ss
.SoundData
.Sound
&& ss
.SoundData
.Sound
->getSoundType()==CSound::SOUND_MUSIC
;
1110 // ---- music sound special case (music competition is managed specially in the CMusicSoundManager)
1115 ss
.SoundData
.Selected
= true;
1117 // start the sound (if needed) and update the volume.
1118 if (ss
.SoundData
.Source
== 0)
1120 // try to create the source.
1121 ss
.SoundData
.Source
= static_cast<CSourceCommon
*>(mixer
->createSource(ss
.SoundData
.Sound
, false, 0, 0, rootCluster
));
1123 if (ss
.SoundData
.Source
!= 0)
1125 // update the position (not used I think, but maybe important...)
1126 ss
.Position
.z
= _LastPosition
.z
+ BACKGROUND_SOUND_ALTITUDE
;
1127 ss
.SoundData
.Source
->setPos(ss
.Position
);
1129 if (!ss
.SoundData
.Source
->isPlaying())
1131 // start the sound is needed.
1132 ss
.SoundData
.Source
->play();
1136 else if (ss
.SoundData
.Source
!= 0 && ss
.SoundData
.Source
->isPlaying())
1138 // stop this too far source.
1139 ss
.SoundData
.Source
->stop();
1142 // ---- standard sound case
1145 if (maskFactor
> 0.0f
&& ss
.Gain
> 0)
1149 if (!ss
.SoundData
.IsPath
&& ss
.SoundData
.Points
.size() > 1)
1150 gain
= maskFactor
* ss
.Gain
;
1154 // maskFactor -= ss.Gain;
1156 ss
.SoundData
.Selected
= true;
1158 // if (ss.Gain == 1)
1159 // if (ss.Distance == 0)
1162 // inside a pattate, then decrease the mask factor will we are more inside the patate
1163 maskFactor
-= first
->second
.Distance
/ INSIDE_FALLOF
;
1164 clamp(maskFactor
, 0.0f
, 1.0f
);
1167 // start the sound (if needed) and update the volume.
1169 if (ss
.SoundData
.Source
== 0)
1171 // try to create the source.
1172 ss
.SoundData
.Source
= static_cast<CSourceCommon
*>(mixer
->createSource(ss
.SoundData
.Sound
, false, 0, 0, rootCluster
));
1174 if (ss
.SoundData
.Source
!= 0)
1177 ss
.SoundData
.Source
->setRelativeGain(gain
);
1179 ss
.Position
.z
= _LastPosition
.z
+ BACKGROUND_SOUND_ALTITUDE
;
1180 ss
.SoundData
.Source
->setPos(ss
.Position
);
1182 // nldebug("Setting source %s at %f", ss.SoundData.SoundName.c_str(), gain);
1183 if (!ss
.SoundData
.Source
->isPlaying())
1185 // start the sound is needed.
1186 ss
.SoundData
.Source
->play();
1188 else if (ss
.SoundData
.Source
->getType() != CSourceCommon::SOURCE_SIMPLE
)
1189 ss
.SoundData
.Source
->checkup();
1192 else if (ss
.SoundData
.Source
!= 0 && ss
.SoundData
.Source
->isPlaying())
1194 // stop this too far source.
1195 ss
.SoundData
.Source
->stop();
1200 } // compute source mixing
1203 H_AFTER(NLSOUND_UpdateSoundLayer
)
1206 H_BEFORE(NLSOUND_DoFadeInOut
)
1207 // update the fade in / out
1210 TTime now
= NLMISC::CTime::getLocalTime();
1215 for (i
=0; i
< UAudioMixer::TBackgroundFlags::NB_BACKGROUND_FLAGS
; ++i
)
1217 if (_FilterFadesStart
[i
] != 0)
1219 // this filter is fading
1220 if (_BackgroundFlags
.Flags
[i
])
1223 TTime delta
= now
- _FilterFadesStart
[i
];
1224 if (delta
> _BackgroundFilterFades
.FadeOuts
[i
])
1226 // the fade is terminated
1227 _FilterFadeValues
[i
] = 0;
1228 // stop the fade for this filter
1229 _FilterFadesStart
[i
] = 0;
1233 _FilterFadeValues
[i
] = 1 - (float(delta
) / _BackgroundFilterFades
.FadeOuts
[i
]);
1234 // continue to fade (at least for this filter.
1241 TTime delta
= now
- _FilterFadesStart
[i
];
1242 if (delta
> _BackgroundFilterFades
.FadeIns
[i
])
1244 // the fade is terminated
1245 _FilterFadeValues
[i
] = 1;
1246 // stop the fade for this filter
1247 _FilterFadesStart
[i
] = 0;
1251 _FilterFadeValues
[i
] = float(delta
) / _BackgroundFilterFades
.FadeIns
[i
];
1252 // continue to fade (at least for this filter.
1259 // update all playing background source that filter value has changed
1261 for (i
=0; i
<BACKGROUND_LAYER
; ++i
)
1264 std::vector
<TSoundData
>::iterator
first(_Layers
[i
].begin()), last(_Layers
[i
].end());
1265 for (; first
!= last
; ++first
)
1267 if (first
->Selected
)
1269 // update this playing sound
1270 if (first
->Source
!= 0 && first
->Source
->getType() == CSourceCommon::SOURCE_BACKGROUND
)
1271 static_cast<CBackgroundSource
*>(first
->Source
)->updateFilterValues(_FilterFadeValues
);
1279 // we can remove the update.
1280 mixer
->unregisterUpdate(this);
1283 H_AFTER(NLSOUND_DoFadeInOut
)
1286 void CBackgroundSoundManager::setBackgroundFlags(const UAudioMixer::TBackgroundFlags
&backgroundFlags
)
1288 for (uint i
=0; i
<UAudioMixer::TBackgroundFlags::NB_BACKGROUND_FLAGS
; ++i
)
1290 if (_BackgroundFlags
.Flags
[i
] != backgroundFlags
.Flags
[i
])
1292 // the filter flags has changed !
1293 if (backgroundFlags
.Flags
[i
])
1295 // the filter is activated, to a fade out
1296 _FilterFadesStart
[i
] = uint64(NLMISC::CTime::getLocalTime() - (1-_FilterFadeValues
[i
]) * _BackgroundFilterFades
.FadeOuts
[i
]);
1301 // the filter is cleared, do a fade in
1302 _FilterFadesStart
[i
] = uint64(NLMISC::CTime::getLocalTime() - (_FilterFadeValues
[i
]) * _BackgroundFilterFades
.FadeIns
[i
]);
1307 _BackgroundFlags
.Flags
[i
] = backgroundFlags
.Flags
[i
];
1311 CAudioMixerUser::instance()->registerUpdate(this);
1315 void CBackgroundSoundManager::onUpdate()
1317 updateBackgroundStatus();
1322 void CBackgroundSoundManager::update ()
1328 uint32 CBackgroundSoundManager::getZoneNumber ()
1330 // return BackgroundSounds.size();
1335 const vector<CVector> &CBackgroundSoundManager::getZone(uint32 zone)
1337 // nlassert (zone< BackgroundSounds.size());
1338 // return BackgroundSounds[zone].Points;
1339 static vector<CVector> v;
1343 CVector
CBackgroundSoundManager::getZoneSourcePos(uint32
/* zone */)
1345 /* nlassert (zone< BackgroundSounds.size());
1347 if (BackgroundSounds[zone].SourceDay != NULL)
1348 BackgroundSounds[zone].SourceDay->getPos(pos);
1356 void CBackgroundSoundManager::setDayNightRatio(float ratio)
1361 nlassert (ratio>=0.0f && ratio<=1.0f);
1363 if (OldRatio == ratio)
1369 // recompute all source volume
1371 for (uint i = 0; i < BackgroundSounds.size(); i++)
1375 if(BackgroundSounds[i].SourceDay != NULL)
1377 BackgroundSounds[i].SourceDay->setRelativeGain(1.0f);
1379 if (!BackgroundSounds[i].SourceDay->isPlaying())
1380 BackgroundSounds[i].SourceDay->play();
1383 if(BackgroundSounds[i].SourceNight != NULL)
1385 if (BackgroundSounds[i].SourceNight->isPlaying())
1386 BackgroundSounds[i].SourceNight->stop();
1389 else if (ratio == 1.0f)
1391 if(BackgroundSounds[i].SourceDay != NULL)
1393 if (BackgroundSounds[i].SourceDay->isPlaying())
1394 BackgroundSounds[i].SourceDay->stop();
1397 if(BackgroundSounds[i].SourceNight != NULL)
1399 BackgroundSounds[i].SourceNight->setRelativeGain(1.0f);
1401 if (!BackgroundSounds[i].SourceNight->isPlaying())
1402 BackgroundSounds[i].SourceNight->play();
1407 if(BackgroundSounds[i].SourceDay != NULL)
1409 BackgroundSounds[i].SourceDay->setRelativeGain((1.0f-ratio));
1411 if (!BackgroundSounds[i].SourceDay->isPlaying())
1412 BackgroundSounds[i].SourceDay->play();
1415 if(BackgroundSounds[i].SourceNight != NULL)
1417 BackgroundSounds[i].SourceNight->setRelativeGain(ratio);
1419 if (!BackgroundSounds[i].SourceNight->isPlaying())
1420 BackgroundSounds[i].SourceNight->play();
1428 void CBackgroundSoundManager::TBanksData::serial(NLMISC::IStream
&s
)
1430 s
.serialCont(Banks
);
1433 s
.serialCont(Points
);
1436 void CBackgroundSoundManager::TSoundData::serial(NLMISC::IStream
&s
)
1442 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
1444 SoundName
= NLMISC::CStringMapper::map(str
);
1445 Sound
= mixer
->getSoundId(SoundName
);
1451 s
.serial(const_cast<std::string
&>(NLMISC::CStringMapper::unmap(SoundName
)));
1458 s
.serialCont(Points
);
1461 void CBackgroundSoundManager::TFxZone::serial(NLMISC::IStream
&s
)
1468 FxName
= NLMISC::CStringMapper::map(str
);
1472 s
.serial(const_cast<std::string
&>(NLMISC::CStringMapper::unmap(FxName
)));
1475 s
.serialCont(Points
);