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) 2012-2019 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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
;
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
),
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();
70 (*it
)->setLooping(false);
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
)
99 // ***************************************************************************************************
100 CPSSound::~CPSSound()
102 NL_PS_FUNC(CPSSound_CPSSound
)
108 // ***************************************************************************************************
109 uint32
CPSSound::getType(void) const
111 NL_PS_FUNC(CPSSound_getType
)
117 // ***************************************************************************************************
118 void CPSSound::step(TPSProcessPass pass
)
120 NL_PS_FUNC(CPSSound_step
)
121 if (pass
!= PSMotion
) return;
122 const uint32 size
= _Owner
->getSize();
124 if (_SoundStopped
&& !_SoundReactivated
)
128 if (_SoundReactivated
)
130 _SoundStopped
= false;
131 _SoundReactivated
= false;
135 // delete all sounds, and rebuild them all
137 for (k
= 0; k
< (sint32
) size
; ++k
)
144 // don't need to reupdate sound
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(),
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
;
167 currVol
= _GainScheme
? (float *) _GainScheme
->make(getOwner(), size
- leftToDo
, Gains
, sizeof(float), toProcess
, true)
169 if (!_UseOriginalPitch
)
172 currPitch
= _PitchScheme
? (float *) _PitchScheme
->make(getOwner(), size
- leftToDo
, frequencies
, sizeof(float), toProcess
, true)
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
),
184 if ((*it
)->isLooping() && !(*it
)->isPlaying())
186 // looping sources do not play when they are clipped, so "reminds" the source to play when possible.
190 currVol
+= GainPtInc
;
191 currPitch
+= pitchPtInc
;
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
),
212 currVol
+= GainPtInc
;
219 leftToDo
-= toProcess
;
224 // ***************************************************************************************************
225 void CPSSound::setGain(float Gain
)
227 NL_PS_FUNC(CPSSound_setGain
)
233 // ***************************************************************************************************
234 void CPSSound::setGainScheme(CPSAttribMaker
<float> *Gain
)
236 NL_PS_FUNC(CPSSound_setGainScheme
)
241 if (_GainScheme
&& _GainScheme
->hasMemory()) _GainScheme
->resize(_Owner
->getMaxSize(), _Owner
->getSize());
245 // ***************************************************************************************************
246 void CPSSound::setPitch(float pitch
)
248 NL_PS_FUNC(CPSSound_setPitch
)
254 // ***************************************************************************************************
255 void CPSSound::setPitchScheme(CPSAttribMaker
<float> *pitch
)
257 NL_PS_FUNC(CPSSound_setPitchScheme
)
259 _PitchScheme
= pitch
;
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.
279 std::string soundName
;
281 _SoundName
= NLMISC::CStringMapper::map(soundName
);
285 std::string soundName
= NLMISC::CStringMapper::unmap(_SoundName
);
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.
297 _Sounds
.resize(_Owner
->getMaxSize());
302 nbSounds
= _Sounds
.getSize(); // number of used sound
315 hasScheme
= _GainScheme
!= NULL
;
319 f
.serialPolyPtr(_GainScheme
);
325 // serial pitch infos
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
;
338 f
.serialPolyPtr(_PitchScheme
);
348 hasScheme
= _PitchScheme
!= NULL
;
352 f
.serialPolyPtr(_PitchScheme
);
362 f
.serial(_EmissionPercent
);
363 f
.serial(_SpawnSounds
);
368 _SoundStopped
= true;
369 // insert blank sources
370 for (sint k
= 0; k
< nbSounds
; ++k
)
376 _SoundStopped
= false;
377 _SoundReactivated
= true;
382 // ***************************************************************************************************
383 void CPSSound::newElement(const CPSEmitterInfo
&info
)
385 NL_PS_FUNC(CPSSound_newElement
)
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
401 const NLMISC::CMatrix
&mat
= getLocalToWorldMatrix();
403 if (_UseOriginalPitch
)
405 pitch
= _Sounds
[index
]->getPitch();
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
],
416 _Sounds
[index
]->play();
421 _Sounds
.insert(NULL
);
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
);
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
)
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
)
475 _UseOriginalPitch
= useOriginalPitch
;