Fix "no remove aqua speed" bug when player leaves the water
[ryzomcore.git] / nel / src / sound / background_sound_manager.cpp
blobd59a6f09333e0ecfb621870e4ffa1200ea5d31ac
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2010 Matt RAYKOWSKI (sfb) <matt.raykowski@gmail.com>
6 // Copyright (C) 2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "stdsound.h"
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"
42 using namespace std;
43 using namespace NLMISC;
44 using namespace NLLIGO;
47 namespace NLSOUND {
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()
68 unload();
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();
91 TSoundData sd;
93 sd.SoundName = CStringMapper::map(soundName);
94 sd.Sound = mixer->getSoundId(sd.SoundName);
95 sd.Source = 0;
97 // Copy the points
98 sd.Points.resize (points.size ());
99 for (uint i=0; i<points.size (); i++)
100 sd.Points[i] = points[i];
102 sd.Selected = false;
103 sd.IsPath = isPath;
105 if (sd.Sound != 0)
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);
119 sd.MaxBox = vmax;
120 sd.MinBox = vmin;
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();
127 sd.MaxBox.x += dist;
128 sd.MaxBox.y += dist;
129 sd.MinBox.x -= dist;
130 sd.MinBox.y -= dist;
132 sd.MaxDist = dist;
134 // store the sound.
135 // TODO : handle the three layer.
136 _Layers[layerId].push_back(sd);
138 else
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)
146 uint layerId = 0;
147 uint n = 0;
148 string name;
149 // count the number of '-' in the string.
150 n = (uint)std::count(rawSoundName.begin(), rawSoundName.end(), '-');
152 if (n == 2)
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());
159 return;
161 pos1++;
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());
167 return;
170 name = rawSoundName.substr(pos1, pos2-pos1);
172 else if (n == 3)
174 // layer spec !
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());
180 return;
182 pos1++;
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());
188 return;
191 char id = rawSoundName[pos1];
193 // check caps
194 if (id < 'a')
195 id = id + ('a' - 'A');
197 layerId = id - 'a';
199 NLMISC::clamp(layerId, 0u, BACKGROUND_LAYER-1);
200 pos2++;
202 name = rawSoundName.substr(pos2, pos3-pos2);
204 else
206 nlwarning ("zone have the malformated name '%s", rawSoundName.c_str());
207 return;
210 addSound(name, layerId, points, isPath);
212 TSoundData sd;
214 sd.SoundName = name;
215 sd.Sound = mixer->getSoundId(sd.SoundName);
216 sd.Source = 0;
218 // Copy the points
219 sd.Points.resize (points.size ());
220 for (uint i=0; i<points.size (); i++)
221 sd.Points[i] = points[i];
223 sd.Selected = false;
224 sd.IsPath = isPath;
226 if (sd.Sound != 0)
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);
240 sd.MaxBox = vmax;
241 sd.MinBox = vmin;
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();
248 sd.MaxBox.x += dist;
249 sd.MaxBox.y += dist;
250 sd.MinBox.x -= dist;
251 sd.MinBox.y -= dist;
253 sd.MaxDist = dist;
255 // store the sound.
256 // TODO : handle the three layer.
257 _Layers[layerId].push_back(sd);
259 else
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;
278 unload();
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);
303 if (oldState)
304 play();
307 else
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
322 break;
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))
346 uint layerId = 0;
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);
372 else
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);
383 else
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;
408 _Banks.clear();
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;
439 _FxZones.clear();
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")
456 std::string fxName;
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)
470 TFxZone fxZone;
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)
499 TBanksData bd;
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);
518 bd.MaxBox = vmax;
519 bd.MinBox = vmin;
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 &region)
534 // _Banks.clear();
536 // for (uint i=0; i< region.VZones.size(); ++i)
537 // {
538 // if (region.VZones[i].VPoints.size() > 2)
539 // {
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)
545 // {
546 // for (uint j=1; j<splitted.size()-1; ++j)
547 // {
548 // bankNames.push_back(splitted[j]);
549 // }
551 // addSampleBank(bankNames, region.VZones[i].VPoints);
552 // }
553 // else
554 // {
555 // nlwarning ("A sample bank patatoid name did'nt contains banks name '%s'", region.VZones[i].Name.c_str());
556 // }
557 // }
558 // else
559 // {
560 // nlwarning ("A sample bank patatoid have less than 3 points '%s'", region.VZones[i].Name.c_str());
561 // }
562 // }
565 //void CBackgroundSoundManager::loadEffecsFromRegion(const NLLIGO::CPrimRegion &region)
569 //void CBackgroundSoundManager::loadSoundsFromRegion(const CPrimRegion &region)
571 // uint i;
572 // // remember playing state
573 // bool oldState = _Playing;
574 // unload();
576 // for (i = 0; i < region.VZones.size(); i++)
577 // {
578 // if(region.VZones[i].VPoints.size()>2)
579 // {
580 // addSound(region.VZones[i].Name, region.VZones[i].VPoints, false);
581 // }
582 // else
583 // {
584 // nlwarning ("A background sound patatoid have less than 3 points '%s'", region.VZones[i].Name.c_str());
585 // }
586 // }
588 // for (i = 0; i < region.VPaths.size(); i++)
589 // {
590 // if(region.VPaths[i].VPoints.size() > 1)
591 // {
592 // addSound(region.VPaths[i].Name, region.VPaths[i].VPoints, true);
593 // }
594 // else
595 // {
596 // nlwarning ("A background sound path have less than 2 points '%s'", region.VPaths[i].Name.c_str());
597 // }
598 // }
599 // for (i = 0; i < region.VPoints.size(); i++)
600 // {
601 // std::vector<CPrimVector> points;
602 // points.push_back(region.VPoints[i].Point);
604 // addSound(region.VPoints[i].Name, points, false);
605 // }
608 // // restart playing ?
609 // if (oldState)
610 // play();
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)
618 CIFile file;
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)
630 uint32 version;
631 string filename = continent+".background_primitive";
632 string binPath = CPath::lookup(filename, false, false, false);
633 if (!binPath.empty()
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());
642 _Banks.clear();
643 binFile.serialCont(_Banks);
644 for (uint i=0; i<BACKGROUND_LAYER; ++i)
646 _Layers[i].clear();
647 binFile.serialCont(_Layers[i]);
649 _FxZones.clear();
650 binFile.serialCont(_FxZones);
652 // jobs done !
653 return;
658 nlinfo ("loading '%s'", fn.c_str());
660 CIXml xml;
662 H_AUTO(BackgroundSoundMangerLoad_xml_init);
663 xml.init (file);
667 H_AUTO(BackgroundSoundMangerLoad_primitive_read);
668 primitives.read(xml.getRootNode(), fn.c_str(), config);
670 // region.serial(xml);
671 file.close ();
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 ////////////////////////////////////////////////
694 // Jobs done !
695 return;
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
703 // load the sound.
704 // {
705 // CIFile file;
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))
714 // {
715 // CIXml xml;
716 // xml.init (file);
717 // region.serial(xml);
718 // file.close ();
720 // nlinfo ("Region '%s' contains %d zones for the background sounds", continent.c_str(), region.VZones.size());
722 // loadSoundsFromRegion(region);
723 // }
724 // }
725 // // load the effect.
726 // {
727 // CIFile file;
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))
736 // {
737 // CIXml xml;
738 // xml.init (file);
739 // region.serial(xml);
740 // file.close ();
742 // nlinfo ("Region '%s' contains %d zones for the background effetcs", continent.c_str(), region.VZones.size());
744 // loadEffecsFromRegion(region);
745 // }
746 // }
747 // // load the samples banks.
748 // {
749 // CIFile file;
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))
758 // {
759 // CIXml xml;
760 // xml.init (file);
761 // region.serial(xml);
762 // file.close ();
764 // nlinfo ("Region '%s' contains %d zones for the background samples banks", continent.c_str(), region.VZones.size());
766 // loadSamplesFromRegion(region);
767 // }
768 // }
772 void CBackgroundSoundManager::play ()
774 if (_Playing)
775 return;
777 _Playing = true;
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
788 _DoFade = true;
789 updateBackgroundStatus();
795 void CBackgroundSoundManager::stop ()
797 if(!_Playing)
798 return;
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);
813 _Playing = false;
816 void CBackgroundSoundManager::unload ()
818 stop();
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)
826 if (first->Source)
827 // mixer->removeSource(first->Source);
828 delete first->Source;
831 // and free the layer.
832 _Layers[i].clear();
835 // erase the sample banks zone
836 _Banks.clear();
838 // TODO : erase the fx zones
841 void CBackgroundSoundManager::setListenerPosition (const CVector &listenerPosition)
843 if (_LastPosition == listenerPosition)
845 return;
847 _LastPosition = listenerPosition;
849 updateBackgroundStatus();
852 void CBackgroundSoundManager::updateBackgroundStatus()
854 H_AUTO(NLSOUND_UpdateBackgroundSound)
855 if (!_Playing)
856 return;
858 CAudioMixerUser *mixer = CAudioMixerUser::instance();
861 // it s on 2d so we don't have z
862 CVector listener = _LastPosition;
863 listener.z = 0.0f;
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());
871 if (css != 0)
873 listener = css->Position;
874 listener.z = 0.0f;
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
893 // bounding box ok,
894 if (CPrimZone::contains(listener, first->Points))
896 // stop at the first zone !
897 if (rootCluster)
899 // use the cluster system
900 rootCluster->setEnvironmentFx(first->FxName);
902 else
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);
912 break;
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
932 // bounding box ok,
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());
941 /* {
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);
1028 else
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())
1048 sd.Source->stop();
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];
1062 CVector pos;
1063 float gain = 1.0f;
1064 float distance;
1065 bool inside = false;
1067 // inside the patat ?
1069 if(CPrimZone::contains(listener, sd.Points, distance, pos, sd.IsPath))
1071 inside = true;
1072 pos = _LastPosition; // use the real listener position, not the 0 z centered
1073 gain = 1.0f;
1074 // nlinfo ("inside patate %d name '%s' ", *first, sd.SoundName.c_str());
1076 else
1078 if (sd.MaxDist>0 && distance < sd.MaxDist)
1080 // compute the gain.
1081 // gain = (sd.MaxDist - distance) / sd.MaxDist;
1083 else
1085 // too far
1086 gain = 0;
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)
1111 if(musicSound)
1113 if (ss.Gain > 0)
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
1143 else
1145 if (maskFactor > 0.0f && ss.Gain > 0)
1147 float gain;
1149 if (!ss.SoundData.IsPath && ss.SoundData.Points.size() > 1)
1150 gain = maskFactor * ss.Gain;
1151 else
1152 gain = ss.Gain;
1154 // maskFactor -= ss.Gain;
1156 ss.SoundData.Selected = true;
1158 // if (ss.Gain == 1)
1159 // if (ss.Distance == 0)
1160 if (ss.Inside)
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)
1176 // set the volume
1177 ss.SoundData.Source->setRelativeGain(gain);
1178 // and the position
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
1201 } // for each layer
1203 H_AFTER(NLSOUND_UpdateSoundLayer)
1206 H_BEFORE(NLSOUND_DoFadeInOut)
1207 // update the fade in / out
1208 if (_DoFade)
1210 TTime now = NLMISC::CTime::getLocalTime();
1211 _DoFade = false;
1212 uint i;
1214 //for each filter
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])
1222 // fading out
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;
1231 else
1233 _FilterFadeValues[i] = 1 - (float(delta) / _BackgroundFilterFades.FadeOuts[i]);
1234 // continue to fade (at least for this filter.
1235 _DoFade |= true;
1238 else
1240 // fading in
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;
1249 else
1251 _FilterFadeValues[i] = float(delta) / _BackgroundFilterFades.FadeIns[i];
1252 // continue to fade (at least for this filter.
1253 _DoFade |= true;
1259 // update all playing background source that filter value has changed
1260 // for each layer
1261 for (i=0; i<BACKGROUND_LAYER; ++i)
1263 // for each patat
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);
1277 if (!_DoFade)
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]);
1297 _DoFade = true;
1299 else
1301 // the filter is cleared, do a fade in
1302 _FilterFadesStart[i] = uint64(NLMISC::CTime::getLocalTime() - (_FilterFadeValues[i]) * _BackgroundFilterFades.FadeIns[i]);
1303 _DoFade = true;
1307 _BackgroundFlags.Flags[i] = backgroundFlags.Flags[i];
1310 if (_DoFade)
1311 CAudioMixerUser::instance()->registerUpdate(this);
1315 void CBackgroundSoundManager::onUpdate()
1317 updateBackgroundStatus();
1322 void CBackgroundSoundManager::update ()
1328 uint32 CBackgroundSoundManager::getZoneNumber ()
1330 // return BackgroundSounds.size();
1331 return 0;
1335 const vector<CVector> &CBackgroundSoundManager::getZone(uint32 zone)
1337 // nlassert (zone< BackgroundSounds.size());
1338 // return BackgroundSounds[zone].Points;
1339 static vector<CVector> v;
1340 return v;
1343 CVector CBackgroundSoundManager::getZoneSourcePos(uint32 /* zone */)
1345 /* nlassert (zone< BackgroundSounds.size());
1346 CVector pos;
1347 if (BackgroundSounds[zone].SourceDay != NULL)
1348 BackgroundSounds[zone].SourceDay->getPos(pos);
1349 return pos;
1351 return CVector();
1356 void CBackgroundSoundManager::setDayNightRatio(float ratio)
1358 // 0 is day
1359 // 1 is night
1361 nlassert (ratio>=0.0f && ratio<=1.0f);
1363 if (OldRatio == ratio)
1364 return;
1365 else
1366 OldRatio = ratio;
1369 // recompute all source volume
1371 for (uint i = 0; i < BackgroundSounds.size(); i++)
1373 if(ratio == 0.0f)
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();
1405 else
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);
1431 s.serial(MinBox);
1432 s.serial(MaxBox);
1433 s.serialCont(Points);
1436 void CBackgroundSoundManager::TSoundData::serial(NLMISC::IStream &s)
1438 std::string str;
1440 if (s.isReading())
1442 CAudioMixerUser *mixer = CAudioMixerUser::instance();
1443 s.serial(str);
1444 SoundName = NLMISC::CStringMapper::map(str);
1445 Sound = mixer->getSoundId(SoundName);
1446 Source = NULL;
1447 Selected = false;
1449 else
1451 s.serial(const_cast<std::string&>(NLMISC::CStringMapper::unmap(SoundName)));
1453 s.serial(MinBox);
1454 s.serial(MaxBox);
1455 s.serial(Surface);
1456 s.serial(MaxDist);
1457 s.serial(IsPath);
1458 s.serialCont(Points);
1461 void CBackgroundSoundManager::TFxZone::serial(NLMISC::IStream &s)
1463 std::string str;
1465 if (s.isReading())
1467 s.serial(str);
1468 FxName= NLMISC::CStringMapper::map(str);
1470 else
1472 s.serial(const_cast<std::string&>(NLMISC::CStringMapper::unmap(FxName)));
1475 s.serialCont(Points);
1476 s.serial(MinBox);
1477 s.serial(MaxBox);
1481 } // NLSOUND