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) 2012 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/driver/buffer.h"
24 #include "nel/sound/driver/source.h"
26 #include "nel/sound/simple_source.h"
27 #include "nel/sound/mixing_track.h"
28 #include "nel/sound/simple_sound.h"
29 #include "nel/sound/clustered_sound.h"
31 using namespace NLMISC
;
35 CSimpleSource::CSimpleSource(CSimpleSound
*simpleSound
, bool spawn
, TSpawnEndCallback cb
, void *cbUserParam
, NL3D::CCluster
*cluster
, CGroupController
*groupController
)
36 : CSourceCommon(simpleSound
, spawn
, cb
, cbUserParam
, cluster
, groupController
),
37 _SimpleSound(simpleSound
),
40 _WaitingForPlay(false)
42 nlassert(_SimpleSound
!= 0);
44 // get a local copy of the simple sound parameter
45 _Alpha
= _SimpleSound
->getAlpha();
48 CSimpleSource::~CSimpleSource()
52 // Yoyo: security. with prec stop(), should not be needed, but a crash still raise
53 // in "currentEvent->onEvent();" in audio_mixer_user.cpp
54 // [KAETEMI TODO: Take a look at previous comment.]
55 CAudioMixerUser::instance()->removeEvents(this);
58 void CSimpleSource::initPhysicalSource()
60 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
61 CTrack
*track
= mixer
->getFreeTrack(this);
64 nlassert(track
->hasPhysicalSource());
69 void CSimpleSource::releasePhysicalSource()
71 if (hasPhysicalSource())
73 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
74 ISource
*pSource
= getPhysicalSource();
75 nlassert(pSource
!= NULL
);
79 pSource
->setStaticBuffer(NULL
);
80 mixer
->freeTrack(_Track
);
85 uint32
CSimpleSource::getTime()
87 if (hasPhysicalSource())
88 return getPhysicalSource()->getTime();
93 IBuffer
*CSimpleSource::getBuffer()
95 return _SimpleSound
->getBuffer();
98 /// Set looping on/off for future playbacks (default: off)
99 void CSimpleSource::setLooping(bool l
)
101 CSourceCommon::setLooping(l
);
102 if (hasPhysicalSource())
103 getPhysicalSource()->setLooping( l
);
106 CVector
CSimpleSource::getVirtualPos() const
108 if (getCluster() != 0)
110 // need to check the cluster status
111 const CClusteredSound::CClusterSoundStatus
*css
= CAudioMixerUser::instance()->getClusteredSound()->getClusterSoundStatus(getCluster());
114 // there is some data here, update the virtual position of the sound.
115 float dist
= (css
->Position
- getPos()).norm();
116 CVector
vpos(CAudioMixerUser::instance()->getListenPosVector() + css
->Direction
* (css
->Dist
+ dist
));
117 vpos
= _Position
* (1-css
->PosAlpha
) + vpos
*(css
->PosAlpha
);
126 void CSimpleSource::play()
128 // nldebug("CSimpleSource %p : play", this);
130 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
132 // -- Some test to check if we can play the source
134 // Check if sample buffer is available and if the sound source is not too far
135 if (_SimpleSound
->getBuffer() == 0
136 || !_SimpleSound
->getBuffer()->isBufferLoaded()
137 //|| (mixer->getListenPosVector() - _Position).sqrnorm() > _SimpleSound->getMaxDistance() * _SimpleSound->getMaxDistance())
138 || (_RelativeMode
? getPos().sqrnorm() : (mixer
->getListenPosVector() - getPos()).sqrnorm()) > _SimpleSound
->getMaxDistance() * _SimpleSound
->getMaxDistance())
140 // The sample buffer is not available, don't play (we don't know the length)
141 _WaitingForPlay
= false;
144 if (_SpawnEndCb
!= 0)
145 _SpawnEndCb(this, _CbUserParam
);
149 // nldebug("CSimpleSource %p : play FAILED !", (CAudioMixerUser::IMixerEvent*)this);
153 // -- Here we can play the source, either in a real track or as a muted source.
155 // Try to obtain a track
156 if (!hasPhysicalSource())
157 initPhysicalSource();
159 if (hasPhysicalSource())
161 ISource
*pSource
= getPhysicalSource();
162 nlassert(pSource
!= NULL
);
164 // ok, we have a track to realy play, fill the data into the track
165 pSource
->setStaticBuffer(_SimpleSound
->getBuffer());
167 // pSource->setPos( _Position, false);
168 pSource
->setPos(getVirtualPos(), false);
169 if (!_SimpleSound
->getBuffer()->isStereo())
171 pSource
->setMinMaxDistances(_SimpleSound
->getMinDistance(), _SimpleSound
->getMaxDistance(), false);
172 setDirection(_Direction
); // because there is a workaround inside
173 pSource
->setVelocity(_Velocity
);
175 pSource
->setGain(getFinalGain());
176 pSource
->setSourceRelativeMode(_RelativeMode
);
177 pSource
->setLooping(_Looping
);
178 pSource
->setPitch(_Pitch
);
179 pSource
->setAlpha(_Alpha
);
181 // and play the sound
182 bool play
= pSource
->play();
188 nlwarning("Failed to play physical sound source. This is a serious error");
191 // nldebug("CSimpleSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this);
195 if (_Priority
== HighestPri
)
197 // This sound is not discardable, add it in waiting playlist
198 mixer
->addSourceWaitingForPlay(this);
199 _WaitingForPlay
= true;
202 // there is no available track, just do a 'muted' play
203 mixer
->addEvent(this, CTime::getLocalTime() + _SimpleSound
->getDuration());
205 mixer
->incPlayingSourceMuted();
206 // nldebug("CSimpleSource %p : MUTED play done", (CAudioMixerUser::IMixerEvent*)this);
209 CSourceCommon::play();
210 _WaitingForPlay
= false;
213 /// Mixer event call when doing muted play
214 void CSimpleSource::onEvent()
216 // nldebug("CSimpleSource %p : stop EVENT", (CAudioMixerUser::IMixerEvent*)this);
218 // A muted play is terminated.
222 // nlassert(_Playing);
223 // nlassert(_Track == 0);
226 CAudioMixerUser::instance()->decPlayingSourceMuted();
232 void CSimpleSource::stop()
234 // nldebug("CSimpleSource %p : stop", (CAudioMixerUser::IMixerEvent*)this);
235 // nlassert(_Playing);
239 nlassert(!_Playing
); // cannot already be playing if waiting for play
240 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
241 mixer
->removeSourceWaitingForPlay(this);
247 if (hasPhysicalSource())
249 releasePhysicalSource();
253 CAudioMixerUser
*mixer
= CAudioMixerUser::instance();
254 // clear the registered event because of a stop before normal end of play
255 mixer
->decPlayingSourceMuted();
256 mixer
->removeEvents(this);
259 CSourceCommon::stop();
263 if (_SpawnEndCb
!= NULL
)
265 _SpawnEndCb(this, _CbUserParam
);
272 /* Set the position vector (default: (0,0,0)).
273 * 3D mode -> 3D position
274 * st mode -> x is the pan value (from left (-1) to right (1)), set y and z to 0
276 void CSimpleSource::setPos(const NLMISC::CVector
& pos
)
278 CSourceCommon::setPos(pos
);
281 if (hasPhysicalSource())
283 // getPhysicalSource()->setPos(pos);
284 getPhysicalSource()->setPos(getVirtualPos());
290 * Set the velocity vector (3D mode only, ignored in stereo mode) (default: (0,0,0))
292 void CSimpleSource::setVelocity(const NLMISC::CVector
& vel
)
294 CSourceCommon::setVelocity(vel
);
297 if (hasPhysicalSource())
299 // TODO : uncomment, test only
300 getPhysicalSource()->setVelocity(vel
);
306 * Set the direction vector (3D mode only, ignored in stereo mode) (default: (0,0,0) as non-directional)
308 void CSimpleSource::setDirection(const NLMISC::CVector
& dir
)
310 CSourceCommon::setDirection(dir
);
313 if (hasPhysicalSource())
315 if (!_SimpleSound
->getBuffer()->isStereo())
317 static bool coneset
= false;
318 if (dir
.isNull()) // workaround
320 getPhysicalSource()->setCone(float(Pi
* 2), float(Pi
* 2), 1.0f
); // because the direction with 0 is not enough for a non-directional source!
321 getPhysicalSource()->setDirection(CVector::I
); // Don't send a 0 vector, DSound will complain. Send (1,0,0), it's omnidirectional anyway.
328 getPhysicalSource()->setCone(_SimpleSound
->getConeInnerAngle(), _SimpleSound
->getConeOuterAngle(), _SimpleSound
->getConeOuterGain());
331 getPhysicalSource()->setDirection(dir
);
337 void CSimpleSource::updateFinalGain()
340 if (hasPhysicalSource())
341 getPhysicalSource()->setGain(getFinalGain());
344 /* Shift the frequency. 1.0f equals identity, each reduction of 50% equals a pitch shift
345 * of one octave. 0 is not a legal value.
347 void CSimpleSource::setPitch(float pitch
)
349 CSourceCommon::setPitch(pitch
);
352 if (hasPhysicalSource())
354 getPhysicalSource()->setPitch( pitch
);
360 * Set the source relative mode. If true, positions are interpreted relative to the listener position (default: false)
362 void CSimpleSource::setSourceRelativeMode(bool mode
)
364 CSourceCommon::setSourceRelativeMode(mode
);
366 // Set the relative mode
367 if (hasPhysicalSource())
369 getPhysicalSource()->setSourceRelativeMode( mode
);
374 * Get playing state. Return false if the source has stopped on its own.
376 bool CSimpleSource::isPlaying()