Added: #795 Streamed audio file sound source

This commit is contained in:
kaetemi 2012-04-11 17:39:46 +02:00
parent 30240f6e52
commit fe70efdc6c
16 changed files with 621 additions and 12 deletions

View file

@ -403,6 +403,7 @@ public:
/// Add a source for play as possible (for non discadable sound) /// Add a source for play as possible (for non discadable sound)
void addSourceWaitingForPlay(CSourceCommon *source); void addSourceWaitingForPlay(CSourceCommon *source);
void removeSourceWaitingForPlay(CSourceCommon *source);
/// Read all user controled var sheets /// Read all user controled var sheets
void initUserVar(); void initUserVar();

View file

@ -142,6 +142,8 @@ private:
/// True when the sound is played muted and until the mixer event notifying the end. /// True when the sound is played muted and until the mixer event notifying the end.
bool _PlayMuted; bool _PlayMuted;
bool _WaitingForPlay;
}; };

View file

@ -61,8 +61,9 @@ public:
SOUND_COMPLEX, SOUND_COMPLEX,
SOUND_BACKGROUND, SOUND_BACKGROUND,
SOUND_CONTEXT, SOUND_CONTEXT,
SOUND_MUSIC, SOUND_MUSIC, // soon to be deprecated hopefully
SOUND_STREAM SOUND_STREAM,
SOUND_STREAM_FILE
}; };

View file

@ -37,7 +37,8 @@ public:
SOURCE_COMPLEX, SOURCE_COMPLEX,
SOURCE_BACKGROUND, SOURCE_BACKGROUND,
SOURCE_MUSIC, // DEPRECATED SOURCE_MUSIC, // DEPRECATED
SOURCE_STREAM SOURCE_STREAM,
SOURCE_STREAM_FILE
}; };
/// When groupController is NULL it will use the groupcontroller specified in the TSoundId. You should manually specify the groupController if this source is a child of another source, so that the parent source controller of the user-specified .sound file is the one that will be used. /// When groupController is NULL it will use the groupcontroller specified in the TSoundId. You should manually specify the groupController if this source is a child of another source, so that the parent source controller of the user-specified .sound file is the one that will be used.

View file

@ -0,0 +1,94 @@
/**
* \file stream_file_sound.h
* \brief CStreamFileSound
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSound
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE.
* RYZOM CORE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RYZOM CORE is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef NLSOUND_STREAM_FILE_SOUND_H
#define NLSOUND_STREAM_FILE_SOUND_H
#include <nel/misc/types_nl.h>
// STL includes
// NeL includes
// Project includes
#include <nel/sound/stream_sound.h>
namespace NLSOUND {
class CSourceMusicChannel;
/**
* \brief CStreamFileSound
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSound
*/
class CStreamFileSound : public CStreamSound
{
public:
friend CSourceMusicChannel;
public:
CStreamFileSound();
virtual ~CStreamFileSound();
/// Get the type of the sound.
virtual TSOUND_TYPE getSoundType() { return SOUND_STREAM_FILE; }
/// Load the sound parameters from georges' form
virtual void importForm(const std::string& filename, NLGEORGES::UFormElm& formRoot);
/// Used by the george sound plugin to check sound recursion (ie sound 'toto' use sound 'titi' witch also use sound 'toto' ...).
virtual void getSubSoundList(std::vector<std::pair<std::string, CSound*> > &/* subsounds */) const { }
/// Serialize the sound data.
virtual void serial(NLMISC::IStream &s);
/// Return the length of the sound in ms
virtual uint32 getDuration() { return 0; }
inline bool getAsync() { return m_Async; }
inline const std::string &getFilePath() { return m_FilePath; }
private:
/// Used by CSourceMusicChannel to set the filePath and default settings on other parameters.
void setMusicFilePath(const std::string &filePath, bool async = true, bool loop = false);
private:
CStreamFileSound(const CStreamFileSound &);
CStreamFileSound &operator=(const CStreamFileSound &);
private:
bool m_Async;
std::string m_FilePath;
}; /* class CStreamFileSound */
} /* namespace NLSOUND */
#endif /* #ifndef NLSOUND_STREAM_FILE_SOUND_H */
/* end of file */

View file

@ -0,0 +1,105 @@
/**
* \file stream_file_source.h
* \brief CStreamFileSource
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSource
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE.
* RYZOM CORE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RYZOM CORE is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#ifndef NLSOUND_STREAM_FILE_SOURCE_H
#define NLSOUND_STREAM_FILE_SOURCE_H
#include <nel/misc/types_nl.h>
// STL includes
// NeL includes
#include <nel/misc/thread.h>
// Project includes
#include <nel/sound/stream_source.h>
#include <nel/sound/stream_file_sound.h>
namespace NLSOUND {
class IAudioDecoder;
/**
* \brief CStreamFileSource
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSource
*/
class CStreamFileSource : public CStreamSource, private NLMISC::IRunnable
{
public:
CStreamFileSource(CStreamFileSound *streamFileSound = NULL, bool spawn = false, TSpawnEndCallback cb = 0, void *cbUserParam = 0, NL3D::CCluster *cluster = 0, CGroupController *groupController = NULL);
virtual ~CStreamFileSource();
/// Return the source type
TSOURCE_TYPE getType() const { return SOURCE_STREAM_FILE; }
/// \name Playback control
//@{
/// Play
virtual void play();
/// Stop playing
virtual void stop();
/// Get playing state. Return false even if the source has stopped on its own.
virtual bool isPlaying();
/// Pause (following legacy music channel implementation)
void pause();
/// Resume (following legacy music channel implementation)
void resume();
/// check if song ended (following legacy music channel implementation)
bool isEnded();
//@}
/// \name Decoding thread
//@{
virtual void getName (std::string &result) const { result = "CStreamFileSource"; }
virtual void run();
//@}
// TODO: getTime
private:
void bufferMore(uint bytes);
private:
CStreamFileSource(const CStreamFileSource &);
CStreamFileSource &operator=(const CStreamFileSource &);
private:
inline CStreamFileSound *getStreamFileSound() { return static_cast<CStreamFileSound *>(m_StreamSound); }
NLMISC::IThread *m_Thread;
IAudioDecoder *m_AudioDecoder;
bool m_Paused;
}; /* class CStreamFileSource */
} /* namespace NLSOUND */
#endif /* #ifndef NLSOUND_STREAM_FILE_SOURCE_H */
/* end of file */

View file

@ -114,7 +114,7 @@ private:
CStreamSource(const CStreamSource &); CStreamSource(const CStreamSource &);
CStreamSource &operator=(const CStreamSource &); CStreamSource &operator=(const CStreamSource &);
private: protected:
/// Return the source type /// Return the source type
TSOURCE_TYPE getType() const { return SOURCE_STREAM; } TSOURCE_TYPE getType() const { return SOURCE_STREAM; }
@ -162,7 +162,13 @@ private:
/// The bytes per second according to the buffer format /// The bytes per second according to the buffer format
uint m_BytesPerSecond; uint m_BytesPerSecond;
/// Waiting for play for high priority sources
bool m_WaitingForPlay;
/// Inverse pitch
float m_PitchInv;
}; /* class CStreamSource */ }; /* class CStreamSource */
} /* namespace NLSOUND */ } /* namespace NLSOUND */

View file

@ -58,6 +58,8 @@ FILE(GLOB STREAM
FILE(GLOB STREAM_FILE FILE(GLOB STREAM_FILE
audio_decoder.cpp ../../include/nel/sound/audio_decoder.h audio_decoder.cpp ../../include/nel/sound/audio_decoder.h
audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h audio_decoder_vorbis.cpp ../../include/nel/sound/audio_decoder_vorbis.h
stream_file_sound.cpp ../../include/nel/sound/stream_file_sound.h
stream_file_source.cpp ../../include/nel/sound/stream_file_source.h
) )
FILE(GLOB USER_CLASSES FILE(GLOB USER_CLASSES

View file

@ -45,6 +45,7 @@
#include "nel/sound/context_sound.h" #include "nel/sound/context_sound.h"
#include "nel/sound/music_source.h" #include "nel/sound/music_source.h"
#include "nel/sound/stream_source.h" #include "nel/sound/stream_source.h"
#include "nel/sound/stream_file_source.h"
#include "nel/sound/simple_sound.h" #include "nel/sound/simple_sound.h"
#include "nel/sound/music_sound.h" #include "nel/sound/music_sound.h"
#include "nel/sound/stream_sound.h" #include "nel/sound/stream_sound.h"
@ -250,6 +251,16 @@ void CAudioMixerUser::addSourceWaitingForPlay(CSourceCommon *source)
_SourceWaitingForPlay.push_back(source); _SourceWaitingForPlay.push_back(source);
} }
// ******************************************************************
void CAudioMixerUser::removeSourceWaitingForPlay(CSourceCommon *source)
{
std::list<CSourceCommon *>::iterator it = find(_SourceWaitingForPlay.begin(), _SourceWaitingForPlay.end(), source);
if (it != _SourceWaitingForPlay.end())
{
_SourceWaitingForPlay.erase(it);
}
}
// ****************************************************************** // ******************************************************************
@ -1948,6 +1959,13 @@ retrySound:
ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController)); ret = new CStreamSource(streamSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
} }
break; break;
case CSound::SOUND_STREAM_FILE:
{
CStreamFileSound *streamFileSound = static_cast<CStreamFileSound *>(id);
// This is a stream file thingy.
ret = new CStreamFileSource(streamFileSound, spawn, cb, userParam, cluster, static_cast<CGroupController *>(groupController));
}
break;
case CSound::SOUND_COMPLEX: case CSound::SOUND_COMPLEX:
{ {
CComplexSound *complexSound = static_cast<CComplexSound *>(id); CComplexSound *complexSound = static_cast<CComplexSound *>(id);

View file

@ -32,7 +32,8 @@ CSimpleSource::CSimpleSource(CSimpleSound *simpleSound, bool spawn, TSpawnEndCal
: CSourceCommon(simpleSound, spawn, cb, cbUserParam, cluster, groupController), : CSourceCommon(simpleSound, spawn, cb, cbUserParam, cluster, groupController),
_SimpleSound(simpleSound), _SimpleSound(simpleSound),
_Track(NULL), _Track(NULL),
_PlayMuted(false) _PlayMuted(false),
_WaitingForPlay(false)
{ {
nlassert(_SimpleSound != 0); nlassert(_SimpleSound != 0);
@ -183,6 +184,7 @@ void CSimpleSource::play()
{ {
// This sound is not discardable, add it in waiting playlist // This sound is not discardable, add it in waiting playlist
mixer->addSourceWaitingForPlay(this); mixer->addSourceWaitingForPlay(this);
_WaitingForPlay = true;
return; return;
} }
// there is no available track, just do a 'muted' play // there is no available track, just do a 'muted' play
@ -193,6 +195,7 @@ void CSimpleSource::play()
} }
CSourceCommon::play(); CSourceCommon::play();
_WaitingForPlay = false;
} }
/// Mixer event call when doing muted play /// Mixer event call when doing muted play
@ -219,6 +222,13 @@ void CSimpleSource::stop()
// nldebug("CSimpleSource %p : stop", (CAudioMixerUser::IMixerEvent*)this); // nldebug("CSimpleSource %p : stop", (CAudioMixerUser::IMixerEvent*)this);
// nlassert(_Playing); // nlassert(_Playing);
if (_WaitingForPlay)
{
nlassert(!_Playing); // cannot already be playing if waiting for play
CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->removeSourceWaitingForPlay(this);
}
if (!_Playing) if (!_Playing)
return; return;

View file

@ -26,6 +26,7 @@
#include "nel/sound/context_sound.h" #include "nel/sound/context_sound.h"
#include "nel/sound/music_sound.h" #include "nel/sound/music_sound.h"
#include "nel/sound/stream_sound.h" #include "nel/sound/stream_sound.h"
#include "nel/sound/stream_file_sound.h"
#include "nel/sound/group_controller.h" #include "nel/sound/group_controller.h"
#include "nel/sound/group_controller_root.h" #include "nel/sound/group_controller_root.h"
@ -84,6 +85,11 @@ CSound *CSound::createSound(const std::string &filename, NLGEORGES::UFormElm& fo
ret = new CStreamSound(); ret = new CStreamSound();
ret->importForm(filename, formRoot); ret->importForm(filename, formRoot);
} }
else if (dfnName == "stream_file_sound.dfn")
{
ret = new CStreamFileSound();
ret->importForm(filename, formRoot);
}
else else
{ {
nlassertex(false, ("SoundType unsuported : %s", dfnName.c_str())); nlassertex(false, ("SoundType unsuported : %s", dfnName.c_str()));

View file

@ -23,6 +23,7 @@
#include "nel/sound/background_sound.h" #include "nel/sound/background_sound.h"
#include "nel/sound/music_sound.h" #include "nel/sound/music_sound.h"
#include "nel/sound/stream_sound.h" #include "nel/sound/stream_sound.h"
#include "nel/sound/stream_file_sound.h"
#include "nel/georges/u_form_loader.h" #include "nel/georges/u_form_loader.h"
#include "nel/georges/u_form_elm.h" #include "nel/georges/u_form_elm.h"
@ -194,6 +195,9 @@ public:
case CSound::SOUND_STREAM: case CSound::SOUND_STREAM:
Sound = new CStreamSound(); Sound = new CStreamSound();
break; break;
case CSound::SOUND_STREAM_FILE:
Sound = new CStreamFileSound();
break;
default: default:
Sound = 0; Sound = 0;
} }

View file

@ -0,0 +1,91 @@
/**
* \file stream_file_sound.cpp
* \brief CStreamFileSound
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSound
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE.
* RYZOM CORE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RYZOM CORE is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "stdsound.h"
#include <nel/sound/stream_file_sound.h>
// STL includes
// NeL includes
// #include <nel/misc/debug.h>
// Project includes
using namespace std;
// using namespace NLMISC;
namespace NLSOUND {
CStreamFileSound::CStreamFileSound()
{
}
CStreamFileSound::~CStreamFileSound()
{
}
void CStreamFileSound::importForm(const std::string &filename, NLGEORGES::UFormElm &root)
{
// Call the base class
CStreamSound::importForm(filename, root);
// Async
root.getValueByName(m_Async, ".SoundType.Async");
// FilePath
root.getValueByName(m_FilePath, ".SoundType.FilePath");
}
void CStreamFileSound::serial(NLMISC::IStream &s)
{
CStreamSound::serial(s);
s.serial(m_Async);
s.serial(m_FilePath);
}
void CStreamFileSound::setMusicFilePath(const std::string &filePath, bool async, bool loop)
{
_ConeInnerAngle = NLMISC::Pi * 2;
_ConeOuterAngle = NLMISC::Pi * 2;
_Looping = loop;
_Gain = 1.0f;
_ConeOuterGain = 1.0f;
_Direction = NLMISC::CVector(0.f, 0.f, 0.f);
_Pitch = 1.0f;
_Priority = HighestPri;
_MaxDist = 9000.0f;
_MinDist = 1000.0f;
m_Async = async;
m_FilePath = filePath;
}
} /* namespace NLSOUND */
/* end of file */

View file

@ -0,0 +1,244 @@
/**
* \file stream_file_source.cpp
* \brief CStreamFileSource
* \date 2012-04-11 09:57GMT
* \author Jan Boon (Kaetemi)
* CStreamFileSource
*/
/*
* Copyright (C) 2012 by authors
*
* This file is part of RYZOM CORE.
* RYZOM CORE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* RYZOM CORE is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with RYZOM CORE. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include "stdsound.h"
#include <nel/sound/stream_file_source.h>
// STL includes
// NeL includes
// #include <nel/misc/debug.h>
// Project includes
#include <nel/sound/audio_mixer_user.h>
#include <nel/sound/audio_decoder.h>
using namespace std;
// using namespace NLMISC;
namespace NLSOUND {
CStreamFileSource::CStreamFileSource(CStreamFileSound *streamFileSound, bool spawn, TSpawnEndCallback cb, void *cbUserParam, NL3D::CCluster *cluster, CGroupController *groupController)
: CStreamSource(streamFileSound, spawn, cb, cbUserParam, cluster, groupController), m_AudioDecoder(NULL), m_Paused(false)
{
m_Thread = NLMISC::IThread::create(this);
}
CStreamFileSource::~CStreamFileSource()
{
stop();
m_Thread->wait(); // thread must have stopped for delete!
delete m_Thread;
m_Thread = NULL;
delete m_AudioDecoder;
m_AudioDecoder = NULL;
}
void CStreamFileSource::play()
{
// note: CStreamSource will assert crash if already physically playing!
nldebug("play");
if (m_Thread->isRunning() && m_WaitingForPlay)
{
if (m_NextBuffer || !m_FreeBuffers)
{
CStreamSource::play();
}
else
{
m_WaitingForPlay = true;
CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->addSourceWaitingForPlay(this);
}
}
else if (!_Playing)
{
if (!m_WaitingForPlay)
{
// thread may be stopping from stop call
m_Thread->wait();
}
nlassert(!_Playing);
m_WaitingForPlay = true;
m_Thread->start();
m_Thread->setPriority(NLMISC::ThreadPriorityHighest);
CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->addSourceWaitingForPlay(this);
}
/*if (!m_WaitingForPlay)
{
m_WaitingForPlay = true;
m_Thread->wait(); // thread must have stopped to restart it!
m_Thread->start();
m_Thread->setPriority(NLMISC::ThreadPriorityHighest);
}
CStreamSource::play();*/
}
void CStreamFileSource::stop()
{
nldebug("stop");
CStreamSource::stop();
// thread will check _Playing to stop
}
bool CStreamFileSource::isPlaying()
{
nldebug("isPlaying");
return m_Thread->isRunning();
}
void CStreamFileSource::pause()
{
nldebug("pause");
if (!m_Paused)
{
// thread checks for this to not delete the audio decoder
m_Paused = true;
// stop the underlying system
CStreamSource::stop();
// thread will check _Playing to stop
}
else
{
nlwarning("Already paused");
}
}
void CStreamFileSource::resume()
{
nldebug("resume");
if (m_Paused)
{
m_Thread->wait(); // thread must have stopped to restart it!
play();
}
else
{
nlwarning("Not paused");
}
}
bool CStreamFileSource::isEnded()
{
return (!m_Thread->isRunning() && !_Playing && !m_WaitingForPlay && !m_Paused);
}
void CStreamFileSource::bufferMore(uint bytes) // buffer from bytes (minimum) to bytes * 2 (maximum)
{
uint8 *buffer = this->lock(bytes * 2);
if (buffer)
{
uint32 result = m_AudioDecoder->getNextBytes(buffer, bytes, bytes * 2);
this->unlock(result);
}
}
void CStreamFileSource::run()
{
nldebug("run");
bool looping = _Looping;
if (m_Paused)
{
// handle paused!
m_Paused = false;
}
else if (m_AudioDecoder) // audio decoder should normally not exist when not paused and starting the thread
{
nlwarning("CAudioDecoder already exists, possible thread race bug with pause");
delete m_AudioDecoder;
m_AudioDecoder = NULL;
}
if (!m_AudioDecoder)
{
// load the file
m_AudioDecoder = IAudioDecoder::createAudioDecoder(getStreamFileSound()->getFilePath(), getStreamFileSound()->getAsync(), getStreamFileSound()->getLooping());
if (!m_AudioDecoder)
{
nlwarning("Failed to create IAudioDecoder, likely invalid format");
return;
}
this->setFormat(m_AudioDecoder->getChannels(), m_AudioDecoder->getBitsPerSample(), (uint32)m_AudioDecoder->getSamplesPerSec());
}
uint samples, bytes;
this->getRecommendedBufferSize(samples, bytes);
bufferMore(bytes);
while (_Playing || m_WaitingForPlay)
{
if (!m_AudioDecoder->isMusicEnded())
{
bool newLooping = _Looping;
if (looping != newLooping)
{
m_AudioDecoder->setLooping(looping);
looping = newLooping;
}
bufferMore(bytes);
NLMISC::nlSleep(this->getRecommendedSleepTime());
}
else
{
// wait until done playing buffers
while (this->hasFilledBuffersAvailable())
NLMISC::nlSleep(40);
// stop the physical source
// if (hasPhysicalSource())
// getPhysicalSource()->stop();
// the audio mixer will call stop on the logical source
break;
}
}
if (m_Paused)
{
// don't delete anything
}
else
{
delete m_AudioDecoder;
m_AudioDecoder = NULL;
}
}
} /* namespace NLSOUND */
/* end of file */

View file

@ -35,14 +35,15 @@ CStreamSound::~CStreamSound()
void CStreamSound::importForm(const std::string &filename, NLGEORGES::UFormElm &root) void CStreamSound::importForm(const std::string &filename, NLGEORGES::UFormElm &root)
{ {
NLGEORGES::UFormElm *psoundType; // cannot do this debug check because used also by CStreamFileSound
/*NLGEORGES::UFormElm *psoundType;
std::string dfnName; std::string dfnName;
// some basic checking. // some basic checking.
root.getNodeByName(&psoundType, ".SoundType"); root.getNodeByName(&psoundType, ".SoundType");
nlassert(psoundType != NULL); nlassert(psoundType != NULL);
psoundType->getDfnName(dfnName); psoundType->getDfnName(dfnName);
nlassert(dfnName == "stream_sound.dfn"); nlassert(dfnName == "stream_sound.dfn");*/
// Call the base class // Call the base class
CSound::importForm(filename, root); CSound::importForm(filename, root);

View file

@ -36,12 +36,15 @@ CStreamSource::CStreamSource(CStreamSound *streamSound, bool spawn, TSpawnEndCal
m_FreeBuffers(3), m_FreeBuffers(3),
m_NextBuffer(0), m_NextBuffer(0),
m_LastSize(0), m_LastSize(0),
m_BytesPerSecond(0) m_BytesPerSecond(0),
m_WaitingForPlay(false),
m_PitchInv(1.0f)
{ {
nlassert(m_StreamSound != 0); nlassert(m_StreamSound != 0);
// get a local copy of the stream sound parameter // get a local copy of the stream sound parameter
m_Alpha = m_StreamSound->getAlpha();//m_Buffers m_Alpha = m_StreamSound->getAlpha();//m_Buffers
m_PitchInv = 1.0f / _Pitch;
// create the three buffer objects // create the three buffer objects
CAudioMixerUser *mixer = CAudioMixerUser::instance(); CAudioMixerUser *mixer = CAudioMixerUser::instance();
@ -107,6 +110,8 @@ bool CStreamSource::isPlaying()
/// Set looping on/off for future playbacks (default: off) /// Set looping on/off for future playbacks (default: off)
void CStreamSource::setLooping(bool l) void CStreamSource::setLooping(bool l)
{ {
CSourceCommon::setLooping(l);
//CAutoMutex<CMutex> autoMutex(m_BufferMutex); //CAutoMutex<CMutex> autoMutex(m_BufferMutex);
// //
//CSourceCommon::setLooping(l); //CSourceCommon::setLooping(l);
@ -166,7 +171,9 @@ void CStreamSource::play()
ISource *pSource = getPhysicalSource(); ISource *pSource = getPhysicalSource();
nlassert(pSource != NULL); nlassert(pSource != NULL);
for (uint i = 0; i < m_NextBuffer; ++i) uint nbS = m_NextBuffer;
if (!m_NextBuffer && !m_FreeBuffers) nbS = 3;
for (uint i = 0; i < nbS; ++i)
pSource->submitStreamingBuffer(m_Buffers[i]); pSource->submitStreamingBuffer(m_Buffers[i]);
// pSource->setPos( _Position, false); // pSource->setPos( _Position, false);
@ -184,6 +191,7 @@ void CStreamSource::play()
pSource->setAlpha(m_Alpha); pSource->setAlpha(m_Alpha);
// and play the sound // and play the sound
nlassert(nbS); // must have buffered already!
play = pSource->play(); play = pSource->play();
// nldebug("CStreamSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this); // nldebug("CStreamSource %p : REAL play done", (CAudioMixerUser::IMixerEvent*)this);
} }
@ -193,6 +201,7 @@ void CStreamSource::play()
{ {
// This sound is not discardable, add it in waiting playlist // This sound is not discardable, add it in waiting playlist
mixer->addSourceWaitingForPlay(this); mixer->addSourceWaitingForPlay(this);
m_WaitingForPlay = true;
return; return;
} }
else else
@ -209,10 +218,15 @@ void CStreamSource::play()
} }
if (play) if (play)
{
CSourceCommon::play(); CSourceCommon::play();
m_WaitingForPlay = false;
}
} }
#ifdef NL_DEBUG
nlassert(play); nlassert(play);
#endif
} }
/// Stop playing /// Stop playing
@ -222,6 +236,13 @@ void CStreamSource::stop()
// nldebug("CStreamSource %p : stop", (CAudioMixerUser::IMixerEvent*)this); // nldebug("CStreamSource %p : stop", (CAudioMixerUser::IMixerEvent*)this);
// nlassert(_Playing); // nlassert(_Playing);
if (m_WaitingForPlay)
{
nlassert(!_Playing); // cannot already be playing if waiting for play
CAudioMixerUser *mixer = CAudioMixerUser::instance();
mixer->removeSourceWaitingForPlay(this);
}
if (!_Playing) if (!_Playing)
return; return;
@ -305,7 +326,7 @@ void CStreamSource::updateFinalGain()
void CStreamSource::setPitch(float pitch) void CStreamSource::setPitch(float pitch)
{ {
CAutoMutex<CMutex> autoMutex(m_BufferMutex); CAutoMutex<CMutex> autoMutex(m_BufferMutex);
m_PitchInv = 1.0f / pitch;
CSourceCommon::setPitch(pitch); CSourceCommon::setPitch(pitch);
if (hasPhysicalSource()) if (hasPhysicalSource())
getPhysicalSource()->setPitch(pitch); getPhysicalSource()->setPitch(pitch);
@ -372,7 +393,9 @@ bool CStreamSource::unlock(uint size)
++m_NextBuffer; m_NextBuffer %= 3; ++m_NextBuffer; m_NextBuffer %= 3;
--m_FreeBuffers; --m_FreeBuffers;
if (hasPhysicalSource()) if (hasPhysicalSource())
{
getPhysicalSource()->submitStreamingBuffer(buffer); getPhysicalSource()->submitStreamingBuffer(buffer);
}
m_LastSize = size; m_LastSize = size;
} }
@ -396,7 +419,7 @@ void CStreamSource::getRecommendedBufferSize(uint &samples, uint &bytes) const
uint32 CStreamSource::getRecommendedSleepTime() const uint32 CStreamSource::getRecommendedSleepTime() const
{ {
if (m_FreeBuffers > 0) return 0; if (m_FreeBuffers > 0) return 0;
uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) / _Pitch); uint32 sleepTime = (uint32)((1000.0f * ((float)m_LastSize) / (float)m_BytesPerSecond) * m_PitchInv);
clamp(sleepTime, (uint32)0, (uint32)1000); clamp(sleepTime, (uint32)0, (uint32)1000);
return sleepTime; return sleepTime;
} }