1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2019 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) 2012-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/>.
23 #include "nel/sound/complex_source.h"
24 #include "nel/sound/complex_sound.h"
27 using namespace NLMISC
;
32 CComplexSource::CComplexSource (CComplexSound
*soundPattern
, bool spawn
, TSpawnEndCallback cb
, void *cbUserParam
, NL3D::CCluster
*cluster
, CGroupController
*groupController
)
33 : CSourceCommon(soundPattern
, spawn
, cb
, cbUserParam
, cluster
, groupController
),
38 nlassert(soundPattern
->getSoundType() == CSound::SOUND_COMPLEX
);
39 _PatternSound
= static_cast<CComplexSound
*>(soundPattern
);
41 // read original parameters
42 _Gain
= soundPattern
->getGain();
43 _Pitch
= soundPattern
->getPitch();
44 _Looping
= soundPattern
->getLooping();
45 _Priority
= soundPattern
->getPriority();
46 _TickPerSecond
= soundPattern
->getTicksPerSecond();
49 CComplexSource::~CComplexSource()
51 // CAudioMixerUser *mixer = CAudioMixerUser::instance();
53 CAudioMixerUser::instance()->unregisterUpdate(this);
54 CAudioMixerUser::instance()->removeEvents(this);
56 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
57 for (; first
!= last
; ++first
)
59 //mixer->removeSource(*first);
65 TSoundId
CComplexSource::getSound()
70 void CComplexSource::setPriority( TSoundPriority pr, bool redispatch)
74 void CComplexSource:: setLooping( bool l )
77 bool CComplexSource::getLooping() const
83 bool CComplexSource::isPlaying()
88 void CComplexSource::play()
98 CSourceCommon::play();
101 void CComplexSource::playStuf()
103 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
104 NLMISC::TTime now
= NLMISC::CTime::getLocalTime();
106 switch (_PatternSound
->getPatternMode())
108 case CComplexSound::MODE_CHAINED
:
112 const vector
<uint32
> &soundSeq
= _PatternSound
->getSoundSeq();
113 if (!soundSeq
.empty())
115 CSound
*sound
= mixer
->getSoundId(_PatternSound
->getSound(soundSeq
[_SoundSeqIndex
++]));
120 if (_PatternSound
->doFadeIn())
121 _FadeLength
= min(uint32(_PatternSound
->getFadeLength()/_TickPerSecond
), sound
->getDuration() /2);
125 _Source2
= mixer
->createSource(sound
, false, 0, 0, _Cluster
, NULL
, _GroupController
);
126 if (_Source2
== NULL
)
128 _Source2
->setPriority(_Priority
);
129 _Source2
->setRelativeGain(0);
130 _Source2
->setPos(_Position
);
131 _Source2
->setPitch(_Source2
->getSound()->getPitch() * _Pitch
);
135 // register for fade in.
136 mixer
->registerUpdate(this);
140 case CComplexSound::MODE_SPARSE
:
142 // use Source1, sound sequence, delay sequence and event.
146 const std::vector
<uint32
> &delaySeq
= _PatternSound
->getDelaySeq();
148 if (!delaySeq
.empty() && delaySeq
[_DelaySeqIndex
] != 0)
150 _LastSparseEvent
= false;
151 // begin with a delay
152 mixer
->addEvent(this, uint64(now
+ delaySeq
[_DelaySeqIndex
++]/_TickPerSecond
));
156 if (!delaySeq
.empty())
158 const vector
<uint32
> &soundSeq
= _PatternSound
->getSoundSeq();
159 if (!soundSeq
.empty())
161 CSound
*sound
= mixer
->getSoundId(_PatternSound
->getSound(soundSeq
[_SoundSeqIndex
++]));
163 _Source1
= mixer
->createSource(sound
, false, 0, 0, _Cluster
, NULL
, _GroupController
);
164 if (_Source1
== NULL
)
166 _Source1
->setPriority(_Priority
);
167 _Source1
->setRelativeGain(_Gain
*_Gain
*_Gain
);
168 _Source1
->setPos(_Position
);
169 _Source1
->setPitch(_Source1
->getSound()->getPitch() * _Pitch
);
173 // register event for next sound.
174 const std::vector
<uint32
> &delaySeq
= _PatternSound
->getDelaySeq();
175 if (!delaySeq
.empty() && _DelaySeqIndex
< delaySeq
.size())
177 // event for next sound.
178 mixer
->addEvent(this, uint64(now
+ sound
->getDuration() + delaySeq
[_DelaySeqIndex
++]/_TickPerSecond
));
179 if (_DelaySeqIndex
>= delaySeq
.size() && !_Looping
)
180 _LastSparseEvent
= true;
182 _LastSparseEvent
= false;
186 _LastSparseEvent
= true;
188 mixer
->addEvent(this, now
+ sound
->getDuration());
195 case CComplexSound::MODE_ALL_IN_ONE
:
197 // just spanw all the listed source.
198 const std::vector
<NLMISC::TStringId
> &sounds
= _PatternSound
->getSounds();
200 std::vector
<NLMISC::TStringId
>::const_iterator
first(sounds
.begin()), last(sounds
.end());
202 if (_AllSources
.empty())
204 // create the sources
205 for (; first
!= last
; ++first
)
207 CSound
*sound
= mixer
->getSoundId(*first
);
210 USource
*source
= mixer
->createSource(sound
, false, 0, 0, _Cluster
, NULL
, _GroupController
);
213 source
->setPriority(_Priority
);
214 source
->setRelativeGain(_Gain
*_Gain
*_Gain
);
215 source
->setPos(_Position
);
216 source
->setPitch(source
->getSound()->getPitch() * _Pitch
);
219 _AllSources
.push_back(source
);
226 // just replay the existing source.
227 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
229 for (; first
!= last
; ++first
)
231 (*first
)->setRelativeGain(_Gain
*_Gain
*_Gain
);
232 (*first
)->setPos(_Position
);
233 (*first
)->setPitch((*first
)->getSound()->getPitch() * _Pitch
);
240 // event to stop the sound
241 mixer
->addEvent(this, NLMISC::CTime::getLocalTime() + _PatternSound
->getDuration());
246 nldebug("Unknow pattern mode. Can't play.");
251 void CComplexSource::stop()
253 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
257 // mixer->removeSource(_Source1);
265 // mixer->removeSource(_Source2);
271 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
272 for (; first
!= last
; ++first
)
274 if ((*first
)->isPlaying())
280 switch (_PatternSound
->getPatternMode())
282 case CComplexSound::MODE_CHAINED
:
283 mixer
->unregisterUpdate(this);
284 mixer
->removeEvents(this);
286 case CComplexSound::MODE_SPARSE
:
287 case CComplexSound::MODE_ALL_IN_ONE
:
288 mixer
->removeEvents(this);
290 case CComplexSound::MODE_UNDEFINED
:
295 CSourceCommon::stop();
298 /*void CComplexSource::unregisterSpawnCallBack()
302 void CComplexSource::setPos( const NLMISC::CVector
& pos
)
304 CSourceCommon::setPos(pos
);
306 if (_Source1
!= NULL
)
307 _Source1
->setPos(pos
);
308 if (_Source2
!= NULL
)
309 _Source2
->setPos(pos
);
311 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
312 for (; first
!= last
; ++first
)
314 (*first
)->setPos(pos
);
318 void CComplexSource::setVelocity( const NLMISC::CVector
& vel
)
320 CSourceCommon::setVelocity(vel
);
322 if (_Source1
!= NULL
)
323 _Source1
->setVelocity(vel
);
324 if (_Source2
!= NULL
)
325 _Source2
->setVelocity(vel
);
327 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
328 for (; first
!= last
; ++first
)
330 (*first
)->setVelocity(vel
);
333 /*void CComplexSource::getVelocity( NLMISC::CVector& vel ) const
337 void CComplexSource::setDirection( const NLMISC::CVector
& dir
)
339 CSourceCommon::setDirection(dir
);
341 if (_Source1
!= NULL
)
342 _Source1
->setDirection(dir
);
343 if (_Source2
!= NULL
)
344 _Source2
->setDirection(dir
);
346 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
347 for (; first
!= last
; ++first
)
349 (*first
)->setDirection(dir
);
353 void CComplexSource::getDirection( NLMISC::CVector& dir ) const
357 void CComplexSource::setGain( float gain
)
359 CSourceCommon::setGain(gain
);
361 // update the gain of the played source.
362 if (_PatternSound
->getPatternMode() == CComplexSound::MODE_CHAINED
)
364 // set sub source volume with fade value.
365 if (_Source1
!= NULL
)
366 _Source1
->setRelativeGain((1.0f
- _FadeFactor
) * _Gain
*_Gain
*_Gain
);
367 if (_Source2
!= NULL
)
368 _Source2
->setRelativeGain(_FadeFactor
* _Gain
*_Gain
*_Gain
);
373 if (_Source1
!= NULL
)
374 _Source1
->setRelativeGain(_Gain
*_Gain
*_Gain
);
375 if (_Source2
!= NULL
)
376 _Source2
->setRelativeGain(_Gain
*_Gain
*_Gain
);
379 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
380 for (; first
!= last
; ++first
)
382 (*first
)->setGain(_Gain
);
385 if (_Muted
&& _Playing
)
389 void CComplexSource::setRelativeGain( float gain
)
391 CSourceCommon::setRelativeGain(gain
);
393 // update the gain of the played source.
394 if (_PatternSound
->getPatternMode() == CComplexSound::MODE_CHAINED
)
396 // set sub source volume with fade value.
397 if (_Source1
!= NULL
)
398 _Source1
->setRelativeGain((1.0f
- _FadeFactor
) * _Gain
*_Gain
*_Gain
);
399 if (_Source2
!= NULL
)
400 _Source2
->setRelativeGain(_FadeFactor
* _Gain
*_Gain
*_Gain
);
405 if (_Source1
!= NULL
)
406 _Source1
->setRelativeGain(_Gain
*_Gain
*_Gain
);
407 if (_Source2
!= NULL
)
408 _Source2
->setRelativeGain(_Gain
*_Gain
*_Gain
);
411 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
412 for (; first
!= last
; ++first
)
414 (*first
)->setRelativeGain(_Gain
);
417 if (_Muted
&& _Playing
)
422 void CComplexSource::setPitch( float pitch )
425 float CComplexSource::getPitch() const
431 void CComplexSource::setSourceRelativeMode( bool mode )
434 bool CComplexSource::getSourceRelativeMode() const
440 uint32
CComplexSource::getTime()
442 // evaluate the elapsed time.
443 if (!_Playing
|| _PlayStart
== 0) // not started ?
446 TTime now
= NLMISC::CTime::getLocalTime();
448 TTime delta
= now
- _PlayStart
;
450 return uint32(delta
);
454 /// Mixer update implementation.
455 void CComplexSource::onUpdate()
457 // do the cross fade :
458 // - lower sound1, louder sound2,
459 // - when max reach, stop the update, swap the sound, delete sound1 and set event for next fade.
461 // can only occur for chained mode.
462 nlassert(_PatternSound
->getPatternMode() == CComplexSound::MODE_CHAINED
);
464 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
466 // compute xfade factor.
467 TTime now
= NLMISC::CTime::getLocalTime();
470 _FadeFactor
= float((double(now
) - double(_StartTime2
)) / double(_FadeLength
)) ;
471 // _FadeFactor = (_FadeFactor*_FadeFactor);
476 // nldebug("Fade factor = %f", _FadeFactor);
477 if (_FadeFactor
>= 1.0)
484 // mixer->removeSource(_Source1);
491 _Source2
->setRelativeGain(1.0f
* _Gain
);
495 _StartTime1
= _StartTime2
;
497 // if there is a next sound available, program an event for the next xfade.
498 CSound
*sound2
= NULL
;
500 const vector
<uint32
> &soundSeq
= _PatternSound
->getSoundSeq();
501 if (_SoundSeqIndex
< soundSeq
.size())
503 sound2
= mixer
->getSoundId(_PatternSound
->getSound(soundSeq
[_SoundSeqIndex
++]));
507 // restart the sound sequence
509 sound2
= mixer
->getSoundId(_PatternSound
->getSound(soundSeq
[_SoundSeqIndex
++]));
515 //nldebug("CS : Chaining to sound %s", CStringMapper::unmap(sound2->getName()).c_str());
516 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
518 // determine the XFade length (if next sound is too short.
519 _FadeLength
= minof
<uint32
>(uint32(_PatternSound
->getFadeLength()/_TickPerSecond
), (sound2
->getDuration()) / 2, (_Source1
->getSound()->getDuration())/2);
520 _Source2
= mixer
->createSource(sound2
, false, 0, 0, _Cluster
, NULL
, _GroupController
);
523 _Source2
->setPriority(_Priority
);
524 // there is a next sound, add event for xfade.
525 //nldebug("Seting event for sound %s in %u millisec (XFade = %u).", CStringMapper::unmap(_Source1->getSound()->getName()).c_str(), _Source1->getSound()->getDuration()-_FadeLength, _FadeLength);
526 mixer
->addEvent(this, _StartTime1
+ _Source1
->getSound()->getDuration() - _FadeLength
);
531 // no sound after, just set an event at end of current sound to stop the complex sound.
532 nldebug("Setting last event for sound %s in %u millisec.", CStringMapper::unmap(_Source1
->getSound()->getName()).c_str(), _Source1
->getSound()->getDuration());
533 if (_PatternSound
->doFadeOut())
535 // set the event to begin fade out.
536 mixer
->addEvent(this, _StartTime1
+ _Source1
->getSound()->getDuration() - _PatternSound
->getFadeLength());
540 // set the event at end of sound.
541 mixer
->addEvent(this, _StartTime1
+ _Source1
->getSound()->getDuration());
547 if (_PatternSound
->doFadeOut())
549 // update is responsible for stoping the sound.
553 // remove from the update list
554 mixer
->unregisterUpdate(this);
558 // nldebug("XFade : %4.3f <=> %4.3f (Fade Len = %6.3f", (1.0f-_FadeFactor)*_Gain, _FadeFactor*_Gain, _FadeLength/1000.0f);
563 // lower the sound 1.
564 _Source1
->setRelativeGain((1.0f
- _FadeFactor
) * _Gain
);
569 // lower the sound 1.
570 // _Source2->setRelativeGain(float(sqrt(_FadeFactor)) * _Gain);
571 _Source2
->setRelativeGain(_FadeFactor
* _Gain
);
575 /// Mixer event implementation.
576 void CComplexSource::onEvent()
578 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
579 NLMISC::TTime now
= NLMISC::CTime::getLocalTime();
581 switch (_PatternSound
->getPatternMode())
583 case CComplexSound::MODE_CHAINED
:
585 // either it's time to begin a new xfade, or to end this sound.
586 if (_Source2
!= NULL
)
588 // start new cross fade.?
591 _Source2
->setRelativeGain(0);
592 _Source2
->setPitch(_Source2
->getSound()->getPitch() * _Pitch
);
593 _Source2
->setPos(_Position
);
594 // start the source 2
596 // register for update.
597 mixer
->registerUpdate(this);
601 if (_PatternSound
->doFadeOut())
603 // set in update list for fade out.
605 mixer
->registerUpdate(this);
611 // mixer->removeSource(_Source1);
619 case CComplexSound::MODE_SPARSE
:
621 if (_Source1
!= NULL
)
624 // mixer->removeSource(_Source1);
629 const std::vector
<uint32
> &delaySeq
= _PatternSound
->getDelaySeq();
630 const vector
<uint32
> &soundSeq
= _PatternSound
->getSoundSeq();
632 if (_Looping
&& _DelaySeqIndex
>= delaySeq
.size())
635 /* if (!delaySeq.empty() && delaySeq[0] == 0)
641 if (!soundSeq
.empty() && !_LastSparseEvent
)
643 // wrap around sound sequence until there are delays...
644 if (_SoundSeqIndex
>= soundSeq
.size())
647 CSound
*sound
= mixer
->getSoundId(_PatternSound
->getSound(soundSeq
[_SoundSeqIndex
++]));
649 _Source1
= mixer
->createSource(sound
, false, 0, 0, _Cluster
, NULL
, _GroupController
);
650 if (_Source1
== NULL
)
655 _Source1
->setPriority(_Priority
);
656 _Source1
->setRelativeGain(_Gain
*_Gain
*_Gain
);
657 _Source1
->setPitch(_Source1
->getSound()->getPitch() * _Pitch
);
658 _Source1
->setPos(_Position
);
662 // register event for next sound.
663 if (!delaySeq
.empty() && _DelaySeqIndex
< delaySeq
.size())
665 // event for next sound.
666 mixer
->addEvent(this, uint64(now
+ sound
->getDuration() + delaySeq
[_DelaySeqIndex
++]/_TickPerSecond
));
667 if (_DelaySeqIndex
== delaySeq
.size() && !_Looping
)
668 _LastSparseEvent
= true;
674 _LastSparseEvent
= true;
676 _LastSparseEvent
= false;
677 mixer
->addEvent(this, now
+ sound
->getDuration());
682 // this is the event for stop !
687 case CComplexSound::MODE_ALL_IN_ONE
:
688 // just call the stop method.
697 void CComplexSource::checkup()
702 if (_Source1
!= NULL
&& _Source1
->getSound()->getLooping() && !_Source1
->isPlaying())
704 if (_Source2
!= NULL
&& _Source2
->getSound()->getLooping() && !_Source2
->isPlaying())
707 std::vector
<USource
*>::iterator
first(_AllSources
.begin()), last(_AllSources
.end());
708 for (; first
!= last
; ++first
)
710 USource
*source
= *first
;
713 if (source
->getSound()->getLooping() && !source
->isPlaying())
715 if (source
->getSound()->getSoundType() != CSound::SOUND_SIMPLE
)
716 static_cast<CSourceCommon
*>(source
)->checkup();