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/>.
22 #include "nel/sound/complex_sound.h"
23 #include "nel/misc/path.h"
24 #include "nel/misc/common.h"
25 #include "nel/sound/audio_mixer_user.h"
28 using namespace NLMISC
;
33 bool CComplexSound::isDetailed() const
38 void CComplexSound::parseSequence(const std::string
&str
, std::vector
<uint32
> &seq
, uint scale
)
44 string::const_iterator
first(str
.begin()), last(str
.end());
46 for (; first
!= last
; ++first
)
53 seq
.push_back(tmp
* scale
);
58 // parse the last value
62 seq
.push_back(tmp
* scale
);
67 void CComplexSound::getSubSoundList(std::vector
<std::pair
<std::string
, CSound
*> > &subsounds
) const
69 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
70 std::vector
<NLMISC::TStringId
>::const_iterator
first(_Sounds
.begin()), last(_Sounds
.end());
71 for (; first
!= last
; ++first
)
73 CSound
*sound
= mixer
->getSoundId(*first
);
74 subsounds
.push_back(make_pair(CStringMapper::unmap(*first
), sound
));
79 uint32
CComplexSound::getDuration()
81 // evaluate the duration of the sound...
86 // catch the duration of all sub sound.
87 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
89 vector
<sint32
> durations
;
90 std::vector
<NLMISC::TStringId
>::iterator
first(_Sounds
.begin()), last(_Sounds
.end());
91 for (; first
!= last
; ++first
)
93 CSound
*sound
= mixer
->getSoundId(*first
);
96 durations
.push_back(sint32(sound
->getDuration()));
99 durations
.push_back(0);
104 switch (_PatternMode
)
108 // sum the duration minus the xfade time (this is an aproximation if sample are shorter than 2 xfade time)
109 vector
<uint32
>::iterator
first(_SoundSeq
.begin()), last(_SoundSeq
.end()), prev
;
110 for (; first
!= last
; ++first
)
112 if (first
!= _SoundSeq
.begin() && !durations
.empty())
114 // remove a xfade value
115 _Duration
-= minof
<uint32
>(uint32(_XFadeLength
/ _TicksPerSeconds
), durations
[*first
% durations
.size()] / 2, durations
[*prev
% durations
.size()] /2);
117 if (!durations
.empty())
118 _Duration
+= durations
[*first
% durations
.size()];
121 // _Duration -= max(sint(0), sint(_XFadeLength * (_SoundSeq.size()-2) ));
126 if (_SoundSeq
.empty())
128 else if (_DelaySeq
.empty())
130 _Duration
= durations
[0];
132 else if (_DelaySeq
.size() == 1)
134 _Duration
= durations
[0] + _DelaySeq
[0];
139 _Duration
= 0; //durations[soundIndex++];
141 std::vector
<uint32
>::iterator
first(_DelaySeq
.begin()), last(_DelaySeq
.end());
145 for (; first
!= last
; ++first
)
147 // add the sound length
148 _Duration
+= durations
[soundIndex
++ % durations
.size()];
150 _Duration
+= uint32(*first
/ _TicksPerSeconds
);
155 case MODE_ALL_IN_ONE
:
156 // only find the longueur sound.
157 if (!durations
.empty())
158 _Duration
= *(std::max_element(durations
.begin(), durations
.end()));
166 _DurationValid
= true;
172 // ********************************************************
174 CComplexSound::CComplexSound() :
175 _PatternMode(CComplexSound::MODE_UNDEFINED
),
176 _TicksPerSeconds(1.0f
),
177 _XFadeLength(3000), // default to 3000 sec.
180 _MaxDistValid(false),
182 _DurationValid(false)
186 // ********************************************************
188 CComplexSound::~CComplexSound()
190 /* if (_VolumeEnvelope != 0)
192 delete _VolumeEnvelope;
195 if (_FreqModulation != 0)
197 delete _FreqModulation;
202 float CComplexSound::getMaxDistance() const
206 // compute the max distance by checking the max distance of all sounds.
207 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
209 // Hum, getMaxDistance is const, but we must compute the real max dist and update it !
210 CComplexSound
*This
= const_cast<CComplexSound
*>(this);
212 This
->_MaxDist
= 0.0f
;
213 std::vector
<NLMISC::TStringId
>::const_iterator
first(_Sounds
.begin()), last(_Sounds
.end());
215 for (; first
!= last
; ++first
)
217 CSound
*sound
= mixer
->getSoundId(*first
);
220 This
->_MaxDist
= max(_MaxDist
, sound
->getMaxDistance());
224 if (_MaxDist
== 0.0f
)
225 This
->_MaxDist
= 1000000.0f
;
228 _MaxDistValid
= true;
232 void CComplexSound::serial(NLMISC::IStream
&s
)
235 s
.serialEnum(_PatternMode
);
241 for (uint i
=0; i
<nb
; ++i
)
245 _Sounds
.push_back(CStringMapper::map(name
));
250 uint32 nb
= (uint32
)_Sounds
.size();
252 for (uint i
=0; i
<nb
; ++i
)
254 std::string name
= CStringMapper::unmap(_Sounds
[i
]);
258 s
.serial(_TicksPerSeconds
);
259 s
.serialCont(_SoundSeq
);
260 s
.serialCont(_DelaySeq
);
261 s
.serial(_XFadeLength
);
263 s
.serial(_DoFadeOut
);
266 _DurationValid
= false;
270 /// Load the sound parameters from georges' form
271 void CComplexSound::importForm(const std::string
& filename
, NLGEORGES::UFormElm
& formRoot
)
273 NLGEORGES::UFormElm
*psoundType
;
276 _DurationValid
= false;
278 // some basic checking.
279 formRoot
.getNodeByName(&psoundType
, ".SoundType");
280 nlassert(psoundType
!= NULL
);
281 psoundType
->getDfnName(dfnName
);
282 nlassert(dfnName
== "complex_sound.dfn");
284 // Call the base class
285 CSound::importForm(filename
, formRoot
);
288 formRoot
.getValueByName(_TicksPerSeconds
, ".SoundType.Beat");
289 //beat can't be null or negative!
290 if (_TicksPerSeconds
<= 0.0f
)
291 _TicksPerSeconds
= 1.0f
;
295 // List of sound int this pattern
296 NLGEORGES::UFormElm
*psoundsArray
;
298 formRoot
.getNodeByName(&psoundsArray
, ".SoundType.SoundList");
300 if (psoundsArray
!= NULL
)
303 psoundsArray
->getArraySize(size
);
304 for (uint i
=0; i
<size
; ++i
)
307 if (psoundsArray
->getArrayValue(soundname
, i
))
309 soundname
= CFile::getFilenameWithoutExtension(soundname
);
310 _Sounds
.push_back(CStringMapper::map(soundname
));
316 // Mode of the complex sound.
318 formRoot
.getValueByName(mode
, ".SoundType.Mode");
320 if (mode
== "Chained" || mode
== "Sparse")
323 if (!formRoot
.getValueByName(_XFadeLength
, ".SoundType.XFadeLength"))
324 formRoot
.getValueByName(_XFadeLength
, ".SoundType.XFadeLenght"); // WORKAROUND: Typo in sound assets
326 formRoot
.getValueByName(_DoFadeIn
, ".SoundType.DoFadeIn");
327 formRoot
.getValueByName(_DoFadeOut
, ".SoundType.DoFadeOut");
329 // convert xfade to millisec.
330 _XFadeLength
*= 1000;
331 _PatternMode
= MODE_CHAINED
;
332 // just read the sequence
336 formRoot
.getValueByName(str
, ".SoundType.SoundSeq");
337 parseSequence(str
, _SoundSeq
);
339 if (mode
== "Sparse")
341 _PatternMode
= MODE_SPARSE
;
342 // also read the delay sequence
346 formRoot
.getValueByName(str
, ".SoundType.DelaySeq");
347 // parse the delay and premult by 1000 (for millisec).
348 parseSequence(str
, _DelaySeq
, 1000);
351 else if (mode
== "AllInOne")
353 _PatternMode
= MODE_ALL_IN_ONE
;
354 // nothing special to read.
357 nlassertex(false, ("Unsupported mode : %s", mode
.c_str()));