Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / 3d / ps_sound.cpp
blobc482b5aae2498b0683952d9612ac3e96e1c100a8
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) 2012-2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "std3d.h"
21 #include "nel/misc/string_mapper.h"
22 #include "nel/3d/ps_sound.h"
23 #include "nel/3d/particle_system.h"
24 #include "nel/3d/u_ps_sound_interface.h"
25 #include "nel/3d/ps_attrib_maker.h"
27 using namespace NLMISC;
29 #ifdef DEBUG_NEW
30 #define new DEBUG_NEW
31 #endif
33 namespace NL3D
37 // we batch computation of Gains and frequencies. Here is the buffer size:
38 static const uint SoundBufSize = 1024;
41 // ***************************************************************************************************
42 CPSSound::CPSSound() : _Gain(1.f),
43 _GainScheme(NULL),
44 _Pitch(1.f),
45 _PitchScheme(NULL),
46 _EmissionPercent(1),
47 _SpawnSounds(false),
48 _Mute(false),
49 _SoundStopped(false),
50 _SoundReactivated(false),
51 _UseOriginalPitch(false)
53 NL_PS_FUNC(CPSSound_CPSSound)
54 if (CParticleSystem::getSerializeIdentifierFlag()) _Name = std::string("sound");
55 _SoundName = NLMISC::CStringMapper::emptyId();
58 // ***************************************************************************************************
59 void CPSSound::stopSound()
61 NL_PS_FUNC(CPSSound_stopSound)
62 _SoundReactivated = false;
63 if (_SoundStopped) return;
64 CPSAttrib<UPSSoundInstance *>::iterator it = _Sounds.begin()
65 , endIt = _Sounds.end();
66 while (it != endIt)
68 if (*it)
70 (*it)->setLooping(false);
71 (*it)->release();
72 (*it) = NULL;
74 ++it;
76 _SoundStopped = true;
79 // ***************************************************************************************************
80 void CPSSound::reactivateSound()
82 NL_PS_FUNC(CPSSound_reactivateSound)
83 //if (!_SoundStopped) return;
84 _SoundReactivated = true;
87 // ***************************************************************************************************
88 void CPSSound::removeAllSources(void)
90 NL_PS_FUNC(CPSSound_removeAllSources)
91 const sint32 size = _Sounds.getSize();
92 // delete all sounds, and rebuild them all
93 for (sint32 k = size - 1; k >= 0; --k)
95 deleteElement(k);
99 // ***************************************************************************************************
100 CPSSound::~CPSSound()
102 NL_PS_FUNC(CPSSound_CPSSound)
103 removeAllSources();
104 delete _GainScheme;
105 delete _PitchScheme;
108 // ***************************************************************************************************
109 uint32 CPSSound::getType(void) const
111 NL_PS_FUNC(CPSSound_getType)
112 return PSSound;
117 // ***************************************************************************************************
118 void CPSSound::step(TPSProcessPass pass)
120 NL_PS_FUNC(CPSSound_step)
121 if (pass != PSMotion) return;
122 const uint32 size = _Owner->getSize();
123 if (!size) return;
124 if (_SoundStopped && !_SoundReactivated)
126 return;
128 if (_SoundReactivated)
130 _SoundStopped = false;
131 _SoundReactivated = false;
132 if (!_Mute)
134 sint32 k;
135 // delete all sounds, and rebuild them all
136 removeAllSources();
137 for (k = 0; k < (sint32) size; ++k)
139 CPSEmitterInfo ei;
140 ei.setDefaults();
141 newElement(ei);
144 // don't need to reupdate sound
145 return;
147 nlassert(_Owner);
148 uint32 toProcess, leftToDo = size;
150 float Gains[SoundBufSize];
151 float frequencies[SoundBufSize];
153 uint GainPtInc = _GainScheme ? 1 : 0;
154 uint pitchPtInc = _PitchScheme ? 1 : 0;
155 float *currVol, *currPitch;
158 CPSAttrib<UPSSoundInstance *>::iterator it = _Sounds.begin(),
159 endIt;
160 CPSAttrib<NLMISC::CVector>::const_iterator posIt = _Owner->getPos().begin();
161 CPSAttrib<NLMISC::CVector>::const_iterator speedIt = _Owner->getSpeed().begin();
165 toProcess = leftToDo > SoundBufSize ? SoundBufSize : leftToDo;
166 // compute Gain
167 currVol = _GainScheme ? (float *) _GainScheme->make(getOwner(), size - leftToDo, Gains, sizeof(float), toProcess, true)
168 : &_Gain;
169 if (!_UseOriginalPitch)
171 // compute pitch
172 currPitch = _PitchScheme ? (float *) _PitchScheme->make(getOwner(), size - leftToDo, frequencies, sizeof(float), toProcess, true)
173 : &_Pitch;
174 endIt = it + toProcess;
175 const CMatrix &localToWorld = _Owner->getLocalToWorldMatrix();
178 if (*it) // was this sound instanciated?
180 (*it)->setSoundParams(*currVol,
181 localToWorld * *posIt,
182 localToWorld.mulVector(*speedIt),
183 *currPitch);
184 if ((*it)->isLooping() && !(*it)->isPlaying())
186 // looping sources do not play when they are clipped, so "reminds" the source to play when possible.
187 (*it)->play();
190 currVol += GainPtInc;
191 currPitch += pitchPtInc;
192 ++posIt;
193 ++speedIt;
194 ++it;
196 while (it != endIt);
198 else
200 // keep original pitch
201 endIt = it + toProcess;
202 const CMatrix &localToWorld = _Owner->getLocalToWorldMatrix();
205 if (*it) // was this sound instanciated?
207 (*it)->setSoundParams(*currVol,
208 localToWorld * *posIt,
209 localToWorld.mulVector(*speedIt),
210 (*it)->getPitch());
212 currVol += GainPtInc;
213 ++posIt;
214 ++speedIt;
215 ++it;
217 while (it != endIt);
219 leftToDo -= toProcess;
221 while (leftToDo);
224 // ***************************************************************************************************
225 void CPSSound::setGain(float Gain)
227 NL_PS_FUNC(CPSSound_setGain)
228 delete _GainScheme;
229 _GainScheme = NULL;
230 _Gain = Gain;
233 // ***************************************************************************************************
234 void CPSSound::setGainScheme(CPSAttribMaker<float> *Gain)
236 NL_PS_FUNC(CPSSound_setGainScheme)
237 delete _GainScheme;
238 _GainScheme = Gain;
239 if (_Owner)
241 if (_GainScheme && _GainScheme->hasMemory()) _GainScheme->resize(_Owner->getMaxSize(), _Owner->getSize());
245 // ***************************************************************************************************
246 void CPSSound::setPitch(float pitch)
248 NL_PS_FUNC(CPSSound_setPitch)
249 delete _PitchScheme;
250 _PitchScheme = NULL;
251 _Pitch = pitch;
254 // ***************************************************************************************************
255 void CPSSound::setPitchScheme(CPSAttribMaker<float> *pitch)
257 NL_PS_FUNC(CPSSound_setPitchScheme)
258 delete _PitchScheme;
259 _PitchScheme = pitch;
260 if (_Owner)
262 if (_PitchScheme && _PitchScheme->hasMemory()) _PitchScheme->resize(_Owner->getMaxSize(), _Owner->getSize());
266 // ***************************************************************************************************
267 void CPSSound::serial(NLMISC::IStream &f)
269 NL_PS_FUNC(CPSSound_serial)
270 CPSLocatedBindable::serial(f);
271 // version 3 : added option to keep original pitch from the .sound
272 sint ver = f.serialVersion(3);
274 // FIXME: CPSSound is reserialized from the _ParticleSystemProto
275 // cache when a non-_Shared particle system is instanced, this
276 // causes unnecessary id lookups from string.
277 if (f.isReading())
279 std::string soundName;
280 f.serial(soundName);
281 _SoundName = NLMISC::CStringMapper::map(soundName);
283 else
285 std::string soundName = NLMISC::CStringMapper::unmap(_SoundName);
286 f.serial(soundName);
289 sint32 nbSounds;
290 bool hasScheme;
291 if (f.isReading())
293 f.serial(nbSounds); // we are very unlikely to save a system with sounds being played in it,
294 // but we need to keep datas coherency.
295 if (_Owner)
297 _Sounds.resize(_Owner->getMaxSize());
300 else
302 nbSounds = _Sounds.getSize(); // number of used sound
303 f.serial(nbSounds);
307 if (f.isReading())
309 delete _GainScheme;
310 _GainScheme = NULL;
311 delete _PitchScheme;
312 _PitchScheme = NULL;
314 // save Gain infos
315 hasScheme = _GainScheme != NULL;
316 f.serial(hasScheme);
317 if (hasScheme)
319 f.serialPolyPtr(_GainScheme);
321 else
323 f.serial(_Gain);
325 // serial pitch infos
326 if (ver >= 3)
328 bool useOriginalPitch = _UseOriginalPitch;
329 f.serial(useOriginalPitch);
330 _UseOriginalPitch = useOriginalPitch;
331 if (!_UseOriginalPitch)
333 // serialize pitch infos (no needed otherwise)
334 hasScheme = _PitchScheme != NULL;
335 f.serial(hasScheme);
336 if (hasScheme)
338 f.serialPolyPtr(_PitchScheme);
340 else
342 f.serial(_Pitch);
346 else
348 hasScheme = _PitchScheme != NULL;
349 f.serial(hasScheme);
350 if (hasScheme)
352 f.serialPolyPtr(_PitchScheme);
354 else
356 f.serial(_Pitch);
360 if (ver > 1)
362 f.serial(_EmissionPercent);
363 f.serial(_SpawnSounds);
366 if (f.isReading())
368 _SoundStopped = true;
369 // insert blank sources
370 for (sint k = 0; k < nbSounds; ++k)
372 CPSEmitterInfo ei;
373 ei.setDefaults();
374 newElement(ei);
376 _SoundStopped = false;
377 _SoundReactivated = true;
382 // ***************************************************************************************************
383 void CPSSound::newElement(const CPSEmitterInfo &info)
385 NL_PS_FUNC(CPSSound_newElement)
386 nlassert(_Owner);
387 if (_GainScheme && _GainScheme->hasMemory()) _GainScheme->newElement(info);
388 if (_PitchScheme && _PitchScheme->hasMemory()) _PitchScheme->newElement(info);
389 // if there's a sound server, we generate a new sound instance
390 if (!_Mute && !_SoundStopped && CParticleSystem::getSoundServer())
392 if ((rand() % 99) * 0.01f < _EmissionPercent)
394 uint32 index = _Sounds.insert(CParticleSystem::getSoundServer()->createSound(_SoundName, _SpawnSounds));
398 /// set position and activate the sound
399 if (_Sounds[index])
401 const NLMISC::CMatrix &mat = getLocalToWorldMatrix();
402 float pitch;
403 if (_UseOriginalPitch)
405 pitch = _Sounds[index]->getPitch();
407 else
409 pitch = _PitchScheme ? _PitchScheme->get(getOwner(), 0) : _Pitch;
411 _Sounds[index]->setSoundParams(_GainScheme ? _GainScheme->get(getOwner(), 0) : _Gain,
412 mat * _Owner->getPos()[index],
413 _Owner->getSpeed()[index],
414 pitch
416 _Sounds[index]->play();
419 else
421 _Sounds.insert(NULL);
424 else
426 _Sounds.insert(NULL);
430 // ***************************************************************************************************
431 void CPSSound::deleteElement(uint32 index)
433 NL_PS_FUNC(CPSSound_deleteElement)
434 if (_GainScheme && _GainScheme->hasMemory()) _GainScheme->deleteElement(index);
435 if (_PitchScheme && _PitchScheme->hasMemory()) _PitchScheme->deleteElement(index);
436 if (_Sounds[index])
438 _Sounds[index]->setLooping(false);
439 _Sounds[index]->release();
441 _Sounds.remove(index);
444 // ***************************************************************************************************
445 void CPSSound::resize(uint32 size)
447 NL_PS_FUNC(CPSSound_resize)
448 nlassert(size < (1 << 16));
449 if (_GainScheme && _GainScheme->hasMemory()) _GainScheme->resize(size, getOwner()->getSize());
450 if (_PitchScheme && _PitchScheme->hasMemory()) _PitchScheme->resize(size, getOwner()->getSize());
451 if (size < _Sounds.getSize())
453 // if vector size has been shrunk, must delete sounds instances
454 for(uint k = size; k < _Sounds.getSize(); ++k)
456 if (_Sounds[k])
458 _Sounds[k]->setLooping(false);
459 _Sounds[k]->release();
463 _Sounds.resize(size);
466 // ***************************************************************************************************
467 void CPSSound::setUseOriginalPitchFlag(bool useOriginalPitch)
469 NL_PS_FUNC(CPSSound_setUseOriginalPitchFlag)
470 if (_PitchScheme)
472 delete _PitchScheme;
473 _PitchScheme = NULL;
475 _UseOriginalPitch = useOriginalPitch;
479 } // NL3D