Linux multi-monitor fullscreen support
[ryzomcore.git] / nel / src / sound / complex_source.cpp
blob6160b0bfecae5dd48999659c6b34fb99ff50be02
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2019 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) 2012-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/>.
21 #include "stdsound.h"
23 #include "nel/sound/complex_source.h"
24 #include "nel/sound/complex_sound.h"
26 using namespace std;
27 using namespace NLMISC;
29 namespace NLSOUND
32 CComplexSource::CComplexSource (CComplexSound *soundPattern, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController)
33 : CSourceCommon(soundPattern, spawn, cb, cbUserParam, cluster, groupController),
34 _Source1(NULL),
35 _Source2(NULL),
36 _Muted(false)
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();
52 // security
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);
60 delete *first;
65 TSoundId CComplexSource::getSound()
67 return _PatternSound;
70 void CComplexSource::setPriority( TSoundPriority pr, bool redispatch)
74 void CComplexSource:: setLooping( bool l )
77 bool CComplexSource::getLooping() const
79 return false;
83 bool CComplexSource::isPlaying()
85 return _Playing;
88 void CComplexSource::play()
90 if (_Gain == 0)
92 _Muted = true;
94 else
96 playStuf();
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:
110 _SoundSeqIndex = 0;
111 _FadeFactor = 1.0f;
112 const vector<uint32> &soundSeq = _PatternSound->getSoundSeq();
113 if (!soundSeq.empty())
115 CSound *sound = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++]));
117 if (sound == 0)
118 return;
120 if (_PatternSound->doFadeIn())
121 _FadeLength = min(uint32(_PatternSound->getFadeLength()/_TickPerSecond), sound->getDuration() /2);
122 else
123 _FadeLength = 0;
125 _Source2 = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController);
126 if (_Source2 == NULL)
127 return;
128 _Source2->setPriority(_Priority);
129 _Source2->setRelativeGain(0);
130 _Source2->setPos(_Position);
131 _Source2->setPitch(_Source2->getSound()->getPitch() * _Pitch);
132 _Source2->play();
133 _StartTime2 = now;
135 // register for fade in.
136 mixer->registerUpdate(this);
139 break;
140 case CComplexSound::MODE_SPARSE:
142 // use Source1, sound sequence, delay sequence and event.
143 _SoundSeqIndex = 0;
144 _DelaySeqIndex = 0;
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));
154 else
156 if (!delaySeq.empty())
157 _DelaySeqIndex = 1;
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)
165 return;
166 _Source1->setPriority(_Priority);
167 _Source1->setRelativeGain(_Gain*_Gain*_Gain);
168 _Source1->setPos(_Position);
169 _Source1->setPitch(_Source1->getSound()->getPitch() * _Pitch);
170 _Source1->play();
171 _StartTime1 = now;
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;
181 else
182 _LastSparseEvent = false;
184 else
186 _LastSparseEvent = true;
187 // event for stop
188 mixer->addEvent(this, now + sound->getDuration());
194 break;
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);
208 if (sound != NULL)
210 USource *source = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController);
211 if (source != NULL)
213 source->setPriority(_Priority);
214 source->setRelativeGain(_Gain*_Gain*_Gain);
215 source->setPos(_Position);
216 source->setPitch(source->getSound()->getPitch() * _Pitch);
217 source->play();
219 _AllSources.push_back(source);
224 else
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);
234 (*first)->play();
238 if (!_Looping)
240 // event to stop the sound
241 mixer->addEvent(this, NLMISC::CTime::getLocalTime() + _PatternSound->getDuration());
244 break;
245 default:
246 nldebug("Unknow pattern mode. Can't play.");
249 _Muted = false;
251 void CComplexSource::stop()
253 CAudioMixerUser *mixer = CAudioMixerUser::instance();
254 if (_Source1)
256 // _Source1->stop();
257 // mixer->removeSource(_Source1);
258 delete _Source1;
260 _Source1 = NULL;
262 if (_Source2)
264 // _Source2->stop();
265 // mixer->removeSource(_Source2);
266 delete _Source2;
268 _Source2 = NULL;
271 std::vector<USource *>::iterator first(_AllSources.begin()), last(_AllSources.end());
272 for (; first != last; ++first)
274 if ((*first)->isPlaying())
275 (*first)->stop();
276 delete *first;
278 _AllSources.clear();
280 switch (_PatternSound->getPatternMode())
282 case CComplexSound::MODE_CHAINED:
283 mixer->unregisterUpdate(this);
284 mixer->removeEvents(this);
285 break;
286 case CComplexSound::MODE_SPARSE:
287 case CComplexSound::MODE_ALL_IN_ONE:
288 mixer->removeEvents(this);
289 break;
290 case CComplexSound::MODE_UNDEFINED:
291 default:
292 break;
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);
371 else
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)
386 playStuf();
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);
403 else
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)
418 playStuf();
422 void CComplexSource::setPitch( float pitch )
425 float CComplexSource::getPitch() const
427 return 0;
431 void CComplexSource::setSourceRelativeMode( bool mode )
434 bool CComplexSource::getSourceRelativeMode() const
436 return false;
440 uint32 CComplexSource::getTime()
442 // evaluate the elapsed time.
443 if (!_Playing || _PlayStart == 0) // not started ?
444 return 0;
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();
468 if (_FadeLength > 0)
470 _FadeFactor = float((double(now) - double(_StartTime2)) / double(_FadeLength)) ;
471 // _FadeFactor = (_FadeFactor*_FadeFactor);
473 else
474 _FadeFactor = 1.0f;
476 // nldebug("Fade factor = %f", _FadeFactor);
477 if (_FadeFactor >= 1.0)
479 _FadeFactor = 1.0f;
480 // fade end !
481 if (_Source1)
483 // _Source1->stop();
484 // mixer->removeSource(_Source1);
485 delete _Source1;
486 _Source1 = NULL;
488 if (_Source2)
490 // set max volume
491 _Source2->setRelativeGain(1.0f * _Gain);
492 // 'swap' the source
493 _Source1 = _Source2;
494 _FadeFactor = 0.0f;
495 _StartTime1 = _StartTime2;
496 _Source2 = NULL;
497 // if there is a next sound available, program an event for the next xfade.
498 CSound *sound2 = NULL;
499 // _SoundSeqIndex++;
500 const vector<uint32> &soundSeq = _PatternSound->getSoundSeq();
501 if (_SoundSeqIndex < soundSeq.size())
503 sound2 = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++]));
505 else if (_Looping)
507 // restart the sound sequence
508 _SoundSeqIndex = 0;
509 sound2 = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++]));
513 if (sound2 != NULL)
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);
521 if (_Source2)
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);
529 else
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());
538 else
540 // set the event at end of sound.
541 mixer->addEvent(this, _StartTime1 + _Source1->getSound()->getDuration());
545 else
547 if (_PatternSound->doFadeOut())
549 // update is responsible for stoping the sound.
550 _Playing = false;
553 // remove from the update list
554 mixer->unregisterUpdate(this);
556 else
558 // nldebug("XFade : %4.3f <=> %4.3f (Fade Len = %6.3f", (1.0f-_FadeFactor)*_Gain, _FadeFactor*_Gain, _FadeLength/1000.0f);
560 // do the xfade
561 if (_Source1)
563 // lower the sound 1.
564 _Source1->setRelativeGain((1.0f - _FadeFactor) * _Gain);
567 if (_Source2)
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.?
589 _StartTime2 = now;
590 // mute the source2
591 _Source2->setRelativeGain(0);
592 _Source2->setPitch(_Source2->getSound()->getPitch() * _Pitch);
593 _Source2->setPos(_Position);
594 // start the source 2
595 _Source2->play();
596 // register for update.
597 mixer->registerUpdate(this);
599 else
601 if (_PatternSound->doFadeOut())
603 // set in update list for fade out.
604 _StartTime2 = now;
605 mixer->registerUpdate(this);
607 else
609 // end the sound.
610 // _Source1->stop();
611 // mixer->removeSource(_Source1);
612 delete _Source1;
613 _Source1 = NULL;
614 _Playing = false;
618 break;
619 case CComplexSound::MODE_SPARSE:
621 if (_Source1 != NULL)
623 // _Source1->stop();
624 // mixer->removeSource(_Source1);
625 delete _Source1;
626 _Source1 = NULL;
629 const std::vector<uint32> &delaySeq = _PatternSound->getDelaySeq();
630 const vector<uint32> &soundSeq = _PatternSound->getSoundSeq();
632 if (_Looping && _DelaySeqIndex >= delaySeq.size())
634 _DelaySeqIndex = 1;
635 /* if (!delaySeq.empty() && delaySeq[0] == 0)
636 _DelaySeqIndex = 1;
637 else
638 _DelaySeqIndex = 0;
639 */ }
641 if (!soundSeq.empty() && !_LastSparseEvent)
643 // wrap around sound sequence until there are delays...
644 if (_SoundSeqIndex >= soundSeq.size())
645 _SoundSeqIndex = 0;
647 CSound *sound = mixer->getSoundId(_PatternSound->getSound(soundSeq[_SoundSeqIndex++]));
649 _Source1 = mixer->createSource(sound, false, 0, 0, _Cluster, NULL, _GroupController);
650 if (_Source1 == NULL)
652 stop();
653 return;
655 _Source1->setPriority(_Priority);
656 _Source1->setRelativeGain(_Gain*_Gain*_Gain);
657 _Source1->setPitch(_Source1->getSound()->getPitch() * _Pitch);
658 _Source1->setPos(_Position);
659 _Source1->play();
660 _StartTime1 = now;
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;
670 else
672 // event for stop ?
673 if (!_Looping)
674 _LastSparseEvent = true;
675 else
676 _LastSparseEvent = false;
677 mixer->addEvent(this, now + sound->getDuration());
680 else
682 // this is the event for stop !
683 stop();
686 break;
687 case CComplexSound::MODE_ALL_IN_ONE:
688 // just call the stop method.
689 stop();
690 break;
691 default:
692 nlassert(false);
697 void CComplexSource::checkup()
699 if (_Muted)
700 return;
702 if (_Source1 != NULL && _Source1->getSound()->getLooping() && !_Source1->isPlaying())
703 _Source1->play();
704 if (_Source2 != NULL && _Source2->getSound()->getLooping() && !_Source2->isPlaying())
705 _Source2->play();
707 std::vector<USource *>::iterator first(_AllSources.begin()), last(_AllSources.end());
708 for (; first != last; ++first)
710 USource *source = *first;
711 if (source == NULL)
712 continue;
713 if (source->getSound()->getLooping() && !source->isPlaying())
714 source->play();
715 if (source->getSound()->getSoundType() != CSound::SOUND_SIMPLE)
716 static_cast<CSourceCommon*>(source)->checkup();
722 } // NLSOUND