Fix "no remove aqua speed" bug when player leaves the water
[ryzomcore.git] / nel / src / sound / stream_source.cpp
blobfc8db8dd4836a19bff7b63a75e1f2faedb2396d2
1 // NeL - MMORPG Framework <https://wiki.ryzom.dev/>
2 // Copyright (C) 2010-2012 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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) 2019 Winch Gate Property Limited
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"
22 #include "nel/sound/stream_source.h"
24 // Project includes
25 #include "nel/sound/driver/buffer.h"
26 #include "nel/sound/audio_mixer_user.h"
27 #include "nel/sound/stream_sound.h"
28 #include "nel/sound/clustered_sound.h"
30 // using namespace std;
31 using namespace NLMISC;
33 // #define NLSOUND_DEBUG_STREAM
35 namespace NLSOUND {
37 CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController)
38 : CSourceCommon(streamSound, spawn, cb, cbUserParam, cluster, groupController),
39 m_StreamSound(streamSound),
40 m_Alpha(0.0f),
41 m_Track(NULL),
42 m_FreeBuffers(3),
43 m_NextBuffer(0),
44 m_LastSize(0),
45 m_BytesPerSecond(0),
46 m_WaitingForPlay(false),
47 m_PitchInv(1.0f)
49 nlassert(m_StreamSound != 0);
51 // get a local copy of the stream sound parameter
52 m_Alpha = m_StreamSound->getAlpha();//m_Buffers
53 m_PitchInv = 1.0f / _Pitch;
55 // create the three buffer objects
56 CAudioMixerUser *mixer = CAudioMixerUser::instance();
57 ISoundDriver *driver = mixer->getSoundDriver();
58 m_Buffers[0] = driver->createBuffer();
59 m_Buffers[0]->setStorageMode(IBuffer::StorageSoftware);
60 m_Buffers[1] = driver->createBuffer();
61 m_Buffers[1]->setStorageMode(IBuffer::StorageSoftware);
62 m_Buffers[2] = driver->createBuffer();
63 m_Buffers[2]->setStorageMode(IBuffer::StorageSoftware);
66 CStreamSource::~CStreamSource()
68 if (_Playing)
69 stop();
71 if (m_Buffers[0] != NULL) { delete m_Buffers[0]; m_Buffers[0] = NULL; }
72 if (m_Buffers[1] != NULL) { delete m_Buffers[1]; m_Buffers[1] = NULL; }
73 if (m_Buffers[2] != NULL) { delete m_Buffers[2]; m_Buffers[2] = NULL; }
76 void CStreamSource::initPhysicalSource()
78 CAudioMixerUser *mixer = CAudioMixerUser::instance();
79 CTrack *track = mixer->getFreeTrack(this);
80 if (track != NULL)
82 nlassert(track->hasPhysicalSource());
83 m_Track = track;
84 getPhysicalSource()->setStreaming(true);
88 void CStreamSource::releasePhysicalSource()
90 if (hasPhysicalSource())
92 CAudioMixerUser *mixer = CAudioMixerUser::instance();
93 ISource *pSource = getPhysicalSource();
94 nlassert(pSource != NULL);
96 // free the track
97 pSource->stop();
98 pSource->setStreaming(false);
99 if (mixer) mixer->freeTrack(m_Track);
100 m_Track = NULL;
104 uint32 CStreamSource::getTime()
106 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
108 if (hasPhysicalSource())
109 return getPhysicalSource()->getTime();
110 else
111 return 0;
114 bool CStreamSource::isPlaying()
116 return _Playing;
119 /// Set looping on/off for future playbacks (default: off)
120 void CStreamSource::setLooping(bool l)
122 CSourceCommon::setLooping(l);
124 //CAutoMutex<CMutex> autoMutex(m_BufferMutex);
126 //CSourceCommon::setLooping(l);
127 //if (hasPhysicalSource())
128 // getPhysicalSource()->setLooping(l);
131 CVector CStreamSource::getVirtualPos() const
133 if (getCluster() != 0)
135 // need to check the cluster status
136 const CClusteredSound::CClusterSoundStatus *css = CAudioMixerUser::instance()->getClusteredSound()->getClusterSoundStatus(getCluster());
137 if (css != 0)
139 // there is some data here, update the virtual position of the sound.
140 float dist = (css->Position - getPos()).norm();
141 CVector vpos(CAudioMixerUser::instance()->getListenPosVector() + css->Direction * (css->Dist + dist));
142 vpos = _Position * (1-css->PosAlpha) + vpos*(css->PosAlpha);
143 return vpos;
147 return getPos();
150 void CStreamSource::play()
152 nlassert(!_Playing);
153 bool play = false;
154 CAudioMixerUser *mixer = CAudioMixerUser::instance();
157 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
159 //if ((mixer->getListenPosVector() - _Position).sqrnorm() > m_StreamSound->getMaxDistance() * m_StreamSound->getMaxDistance())
160 if ((_RelativeMode ? getPos().sqrnorm() : (mixer->getListenPosVector() - getPos()).sqrnorm()) > m_StreamSound->getMaxDistance() * m_StreamSound->getMaxDistance())
162 // Source is too far to play
163 m_WaitingForPlay = false;
164 if (_Spawn)
166 if (_SpawnEndCb != NULL)
167 _SpawnEndCb(this, _CbUserParam);
168 delete this;
170 #ifdef NLSOUND_DEBUG_STREAM
171 nldebug("CStreamSource %p : play FAILED, source is too far away !", (CAudioMixerUser::IMixerEvent*)this);
172 #endif
173 return;
176 CAudioMixerUser *mixer = CAudioMixerUser::instance();
178 if (!hasPhysicalSource())
179 initPhysicalSource();
181 if (hasPhysicalSource())
183 ISource *pSource = getPhysicalSource();
184 nlassert(pSource != NULL);
186 uint nbS = m_NextBuffer;
187 if (!m_NextBuffer && !m_FreeBuffers) nbS = 3;
188 for (uint i = 0; i < nbS; ++i)
189 pSource->submitStreamingBuffer(m_Buffers[i]);
191 // pSource->setPos( _Position, false);
192 pSource->setPos(getVirtualPos(), false);
193 pSource->setMinMaxDistances(m_StreamSound->getMinDistance(), m_StreamSound->getMaxDistance(), false);
194 if (!m_Buffers[0]->isStereo())
196 setDirection(_Direction); // because there is a workaround inside
197 pSource->setVelocity(_Velocity);
199 else
201 pSource->setDirection(NLMISC::CVector::I);
202 pSource->setCone(float(Pi * 2), float(Pi * 2), 1.0f);
203 pSource->setVelocity(NLMISC::CVector::Null);
205 pSource->setGain(getFinalGain());
206 pSource->setSourceRelativeMode(_RelativeMode);
207 // pSource->setLooping(_Looping);
208 pSource->setPitch(_Pitch);
209 pSource->setAlpha(m_Alpha);
211 // and play the sound
212 nlassert(nbS); // must have buffered already!
213 play = pSource->play();
214 // nldebug("CStreamSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this);
216 else
218 if (_Priority == HighestPri)
220 // This sound is not discardable, add it in waiting playlist
221 mixer->addSourceWaitingForPlay(this);
222 m_WaitingForPlay = true;
223 return;
225 else
227 // No source available, kill.
228 m_WaitingForPlay = false;
229 if (_Spawn)
231 if (_SpawnEndCb != NULL)
232 _SpawnEndCb(this, _CbUserParam);
233 delete this;
235 return;
239 if (play)
241 CSourceCommon::play();
242 m_WaitingForPlay = false;
243 #ifdef NLSOUND_DEBUG_STREAM
244 // Dump source info
245 nlwarning("--- DUMP SOURCE INFO ---");
246 nlwarning(" * getLooping: %s", getPhysicalSource()->getLooping() ? "YES" : "NO");
247 nlwarning(" * isPlaying: %s", getPhysicalSource()->isPlaying() ? "YES" : "NO");
248 nlwarning(" * isStopped: %s", getPhysicalSource()->isStopped() ? "YES" : "NO");
249 nlwarning(" * isPaused: %s", getPhysicalSource()->isPaused() ? "YES" : "NO");
250 nlwarning(" * getPos: %f, %f, %f", getPhysicalSource()->getPos().x, getPhysicalSource()->getPos().y, getPhysicalSource()->getPos().z);
251 NLMISC::CVector v;
252 getPhysicalSource()->getVelocity(v);
253 nlwarning(" * getVelocity: %f, %f, %f", v.x, v.y, v.z);
254 getPhysicalSource()->getDirection(v);
255 nlwarning(" * getDirection: %f, %f, %f", v.x, v.y, v.z);
256 nlwarning(" * getGain: %f", getPhysicalSource()->getGain());
257 nlwarning(" * getPitch: %f", getPhysicalSource()->getPitch());
258 nlwarning(" * getSourceRelativeMode: %s", getPhysicalSource()->getSourceRelativeMode() ? "YES" : "NO");
259 float a, b, c;
260 getPhysicalSource()->getMinMaxDistances(a, b);
261 nlwarning(" * getMinMaxDistances: %f, %f", a, b);
262 getPhysicalSource()->getCone(a, b, c);
263 nlwarning(" * getCone: %f, %f", a, b, c);
264 nlwarning(" * getDirect: %s", getPhysicalSource()->getDirect() ? "YES" : "NO");
265 nlwarning(" * getDirectGain: %f", getPhysicalSource()->getDirectGain());
266 nlwarning(" * isDirectFilterEnabled: %s", getPhysicalSource()->isDirectFilterEnabled() ? "YES" : "NO");
267 nlwarning(" * getEffect: %s", getPhysicalSource()->getEffect() ? "YES" : "NO");
268 nlwarning(" * getEffectGain: %f", getPhysicalSource()->getEffectGain());
269 nlwarning(" * isEffectFilterEnabled: %s", getPhysicalSource()->isEffectFilterEnabled() ? "YES" : "NO");
270 #endif
274 #ifdef NL_DEBUG
275 nlassert(play);
276 #else
277 if (!play)
278 nlwarning("Failed to play physical sound source. This is a serious error");
279 #endif
282 void CStreamSource::stopInt()
284 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
286 // nldebug("CStreamSource %p : stop", (CAudioMixerUser::IMixerEvent*)this);
287 // nlassert(_Playing);
289 if (m_WaitingForPlay)
291 nlassert(!_Playing); // cannot already be playing if waiting for play
292 CAudioMixerUser *mixer = CAudioMixerUser::instance();
293 mixer->removeSourceWaitingForPlay(this);
296 if (!_Playing)
298 m_WaitingForPlay = false;
299 return;
302 if (hasPhysicalSource())
303 releasePhysicalSource();
305 CSourceCommon::stop();
307 m_FreeBuffers = 3;
308 m_NextBuffer = 0;
310 m_WaitingForPlay = false;
313 /// Stop playing
314 void CStreamSource::stop()
316 stopInt();
318 if (_Spawn)
320 if (_SpawnEndCb != NULL)
321 _SpawnEndCb(this, _CbUserParam);
322 delete this;
326 void CStreamSource::setPos(const NLMISC::CVector& pos)
328 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
330 CSourceCommon::setPos(pos);
331 if (hasPhysicalSource())
332 getPhysicalSource()->setPos(getVirtualPos());
335 void CStreamSource::setVelocity(const NLMISC::CVector& vel)
337 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
339 CSourceCommon::setVelocity(vel);
340 if (hasPhysicalSource())
341 getPhysicalSource()->setVelocity(vel);
345 * Set the direction vector (3D mode only, ignored in stereo mode) (default: (0,0,0) as non-directional)
347 void CStreamSource::setDirection(const NLMISC::CVector& dir)
349 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
351 CSourceCommon::setDirection(dir);
353 // Set the direction
354 if (hasPhysicalSource())
356 if (!m_Buffers[0]->isStereo())
358 static bool coneset = false;
359 if (dir.isNull()) // workaround // For what?
361 getPhysicalSource()->setCone(float(Pi * 2), float(Pi * 2), 1.0f); // because the direction with 0 is not enough for a non-directional source!
362 getPhysicalSource()->setDirection(CVector::I); // Don't send a 0 vector, DSound will complain. Send (1,0,0), it's omnidirectional anyway.
363 coneset = false;
365 else
367 // if (!coneset)
369 getPhysicalSource()->setCone(m_StreamSound->getConeInnerAngle(), m_StreamSound->getConeOuterAngle(), m_StreamSound->getConeOuterGain());
370 coneset = true;
372 getPhysicalSource()->setDirection(dir);
378 void CStreamSource::updateFinalGain()
380 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
382 if (hasPhysicalSource())
383 getPhysicalSource()->setGain(getFinalGain());
386 void CStreamSource::setPitch(float pitch)
388 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
389 m_PitchInv = 1.0f / pitch;
390 CSourceCommon::setPitch(pitch);
391 if (hasPhysicalSource())
392 getPhysicalSource()->setPitch(pitch);
395 void CStreamSource::setSourceRelativeMode(bool mode)
397 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
399 CSourceCommon::setSourceRelativeMode(mode);
400 if (hasPhysicalSource())
401 getPhysicalSource()->setSourceRelativeMode(mode);
404 /// Set the sample format. (channels = 1, 2, ...; bitsPerSample = 8, 16; frequency = samples per second, 44100, ...)
405 void CStreamSource::setFormat(uint8 channels, uint8 bitsPerSample, uint32 frequency)
407 nlassert(!_Playing);
409 m_Buffers[0]->setFormat(IBuffer::FormatPcm, channels, bitsPerSample, frequency);
410 m_Buffers[1]->setFormat(IBuffer::FormatPcm, channels, bitsPerSample, frequency);
411 m_Buffers[2]->setFormat(IBuffer::FormatPcm, channels, bitsPerSample, frequency);
413 m_BytesPerSecond = ((uint)bitsPerSample * (uint)frequency * (uint)channels) / 8;
416 /// Return the sample format information.
417 void CStreamSource::getFormat(uint8 &channels, uint8 &bitsPerSample, uint32 &frequency) const
419 IBuffer::TBufferFormat bufferFormat;
421 m_Buffers[0]->getFormat(bufferFormat, channels, bitsPerSample, frequency);
424 void CStreamSource::updateAvailableBuffers()
426 if (hasPhysicalSource())
428 m_FreeBuffers = 3 - getPhysicalSource()->countStreamingBuffers();
432 /// Get a writable pointer to the buffer of specified size. Use capacity to specify the required bytes. Returns NULL when all the buffer space is already filled. Call setFormat() first.
433 uint8 *CStreamSource::lock(uint capacity)
435 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
436 updateAvailableBuffers();
437 if (m_FreeBuffers > 0)
438 return m_Buffers[m_NextBuffer]->lock(capacity);
439 return NULL;
442 /// Notify that you are done writing to the locked buffer, so it can be copied over to hardware if needed. Set size to the number of bytes actually written to the buffer. Returns true if ok.
443 bool CStreamSource::unlock(uint size)
445 nlassert(m_FreeBuffers > 0);
447 CAutoMutex<CMutex> autoMutex(m_BufferMutex);
448 IBuffer *buffer = m_Buffers[m_NextBuffer];
449 bool result = buffer->unlock(size);
451 if (size > 0)
453 ++m_NextBuffer; m_NextBuffer %= 3;
454 --m_FreeBuffers;
455 if (hasPhysicalSource())
457 getPhysicalSource()->submitStreamingBuffer(buffer);
459 m_LastSize = size;
462 return result;
465 /// Get the recommended buffer size to use with lock()/unlock()
466 void CStreamSource::getRecommendedBufferSize(uint &samples, uint &bytes) const
468 IBuffer::TBufferFormat bufferFormat;
469 uint8 channels;
470 uint8 bitsPerSample;
471 uint32 frequency;
472 m_Buffers[0]->getFormat(bufferFormat, channels, bitsPerSample, frequency);
474 samples = frequency / 25; // 25 is a good value
475 bytes = ((uint)bitsPerSample * samples * (uint)channels) / 8;
478 /// Get the recommended sleep time based on the size of the last submitted buffer and the available buffer space
479 uint32 CStreamSource::getRecommendedSleepTime() const
481 if (m_FreeBuffers > 0) return 0;
482 uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) * m_PitchInv);
483 clamp(sleepTime, (uint32)0, (uint32)80);
484 return sleepTime;
487 /// Return if there are still buffers available for playback.
488 bool CStreamSource::hasFilledBuffersAvailable() const
490 const_cast<CStreamSource *>(this)->updateAvailableBuffers();
491 return m_FreeBuffers < 3;
494 void CStreamSource::preAllocate(uint capacity)
496 uint8 *b0 = m_Buffers[0]->lock(capacity);
497 memset(b0, 0, capacity);
498 m_Buffers[0]->unlock(capacity);
499 uint8 *b1 = m_Buffers[1]->lock(capacity);
500 memset(b1, 0, capacity);
501 m_Buffers[1]->unlock(capacity);
502 uint8 *b2 = m_Buffers[2]->lock(capacity);
503 memset(b2, 0, capacity);
504 m_Buffers[2]->unlock(capacity);
507 } /* namespace NLSOUND */
509 /* end of file */