Fixed some strange bugs and logic in Player/Stream interaction. Use the boost::condition properly to block waiting when the output device is full.

This commit is contained in:
casey 2016-05-01 00:52:10 -07:00
parent 93b42c7e61
commit 27b14e762e
8 changed files with 43 additions and 64 deletions

View File

@ -50,7 +50,7 @@ int OggDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence) {
case SEEK_CUR: case SEEK_CUR:
{ {
long currentPosition = ((OggDecoder*)datasource)->fileStream->Position(); long currentPosition = ((OggDecoder*)datasource)->fileStream->Position();
if(((OggDecoder*)datasource)->fileStream->SetPosition(currentPosition+(long) offset)) { if(((OggDecoder*) datasource)->fileStream->SetPosition(currentPosition + (long) offset)) {
return 0; return 0;
} }
} }
@ -63,23 +63,24 @@ int OggDecoder::OggSeek(void *datasource, ogg_int64_t offset, int whence) {
} }
} }
break; break;
default: case SEEK_SET:
{ {
if(((OggDecoder*) datasource)->fileStream->SetPosition((long) offset)) { if(((OggDecoder*) datasource)->fileStream->SetPosition((long) offset)) {
return 0; return 0;
} }
} }
break;
} }
return -1; return -1;
} }
long OggDecoder::OggTell(void *datasource) { long OggDecoder::OggTell(void *datasource) {
return ((OggDecoder*)datasource)->fileStream->Position(); return ((OggDecoder*) datasource)->fileStream->Position();
} }
int OggDecoder::OggClose(void *datasource) { int OggDecoder::OggClose(void *datasource) {
if(((OggDecoder*)datasource)->fileStream->Close()) { if(((OggDecoder*) datasource)->fileStream->Close()) {
return 0; return 0;
} }
return -1; return -1;

View File

@ -115,7 +115,7 @@ bool WaveOut::Play(IBuffer *buffer, IPlayer *player) {
/* if we have a different format, return false and wait for the pending /* if we have a different format, return false and wait for the pending
buffers to be written to the output device. */ buffers to be written to the output device. */
if (bufferCount > 0) { if (bufferCount > 0) {
bool formatChanged = bool formatChanged =
this->currentChannels != buffer->Channels() || this->currentChannels != buffer->Channels() ||
this->currentSampleRate != buffer->SampleRate(); this->currentSampleRate != buffer->SampleRate();

View File

@ -68,7 +68,6 @@ void WaveOutBuffer::Destroy() {
this->header.dwFlags = WHDR_DONE; this->header.dwFlags = WHDR_DONE;
} }
this->player->Notify(); /* WaveOut should do this... whatever it is. */
this->destroyed = true; this->destroyed = true;
} }
} }

View File

@ -81,7 +81,7 @@ Player::~Player() {
void Player::Play() { void Player::Play() {
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
this->state = Player::Playing; this->state = Player::Playing;
this->waitCondition.notify_all(); this->writeToOutputCondition.notify_all();
} }
void Player::Stop() { void Player::Stop() {
@ -89,6 +89,7 @@ void Player::Stop() {
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
this->state = Player::Quit; this->state = Player::Quit;
this->bufferQueue.clear(); this->bufferQueue.clear();
this->writeToOutputCondition.notify_all();
} }
if (this->output) { if (this->output) {
@ -160,7 +161,7 @@ void Player::ThreadLoop() {
{ {
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
while (this->state == Precache) { while (this->state == Precache) {
this->waitCondition.wait(lock); this->writeToOutputCondition.wait(lock);
} }
} }
@ -168,6 +169,8 @@ void Player::ThreadLoop() {
/* we're ready to go.... */ /* we're ready to go.... */
bool finished = false; bool finished = false;
BufferPtr buffer;
while(!finished && !this->Exited()) { while(!finished && !this->Exited()) {
/* see if we've been asked to seek since the last sample was /* see if we've been asked to seek since the last sample was
played. if we have, clear our output buffer and seek the played. if we have, clear our output buffer and seek the
@ -184,21 +187,22 @@ void Player::ThreadLoop() {
} }
} }
BufferPtr buffer; /* let's see if we can find some samples to play */
if (!buffer) {
/* the buffer queue may already have some buffers available if it was
prefetched. */
if (!this->BufferQueueEmpty()) {
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
buffer = this->bufferQueue.front();
} /* the buffer queue may already have some available if it was prefetched. */
/* otherwise, we need to grab a buffer from the stream and add it to the queue */ if (!this->bufferQueue.empty()) {
else { buffer = this->bufferQueue.front();
buffer = this->stream->GetNextProcessedOutputBuffer(); this->bufferQueue.pop_front();
if (buffer) { }
boost::mutex::scoped_lock lock(this->mutex); /* otherwise, we need to grab a buffer from the stream and add it to the queue */
this->bufferQueue.push_back(buffer); else {
this->totalBufferSize += buffer->Bytes(); buffer = this->stream->GetNextProcessedOutputBuffer();
if (buffer) {
this->totalBufferSize += buffer->Bytes();
}
} }
} }
@ -213,17 +217,17 @@ void Player::ThreadLoop() {
know it's done with it. */ know it's done with it. */
this->lockedBuffers.push_back(buffer); this->lockedBuffers.push_back(buffer);
/* TODO: don't understand this yet... seems like every time we enqueue a if (this->lockedBuffers.size() == 1) {
new buffer to the output we remove the old one that was at the front of this->currentPosition = buffer->Position();
*/
if (!this->bufferQueue.empty()) {
this->bufferQueue.pop_front();
// Set currentPosition
if (this->lockedBuffers.size() == 1) {
this->currentPosition = buffer->Position();
}
} }
buffer.reset(); /* important! we're done with this one locally. */
}
else {
/* the output device queue is full. we should block and wait until
the output lets us know that it needs more data */
boost::mutex::scoped_lock lock(this->mutex);
writeToOutputCondition.wait(this->mutex);
} }
} }
/* if we're unable to obtain a buffer, it means we're out of data and the /* if we're unable to obtain a buffer, it means we're out of data and the
@ -268,11 +272,6 @@ void Player::ReleaseAllBuffers() {
this->lockedBuffers.empty(); this->lockedBuffers.empty();
} }
bool Player::BufferQueueEmpty() {
boost::mutex::scoped_lock lock(this->mutex);
return this->bufferQueue.empty();
}
bool Player::PreBuffer() { bool Player::PreBuffer() {
/* don't prebuffer if the buffer is already full */ /* don't prebuffer if the buffer is already full */
if (this->totalBufferSize > this->maxBufferSize) { if (this->totalBufferSize > this->maxBufferSize) {
@ -285,7 +284,6 @@ bool Player::PreBuffer() {
boost::mutex::scoped_lock lock(this->mutex); boost::mutex::scoped_lock lock(this->mutex);
this->bufferQueue.push_back(newBuffer); this->bufferQueue.push_back(newBuffer);
this->totalBufferSize += newBuffer->Bytes(); this->totalBufferSize += newBuffer->Bytes();
this->waitCondition.notify_all(); /* TODO: what's waiting on this? */
} }
return true; return true;
@ -317,16 +315,13 @@ void Player::OnBufferProcessedByOutput(IBuffer *buffer) {
this->currentPosition = this->lockedBuffers.front()->Position(); this->currentPosition = this->lockedBuffers.front()->Position();
} }
this->waitCondition.notify_all(); /* TODO: what's waiting on this? */ /* if the output device's internal buffers are full, it will stop
accepting new samples. now that a buffer has been processed, we can
try to enqueue another sample. the thread loop blocks on this condition */
this->writeToOutputCondition.notify_all();
return; return;
} }
} }
} }
void Player::Notify() {
this->waitCondition.notify_all();
}

View File

@ -62,7 +62,6 @@ namespace musik { namespace core { namespace audio {
~Player(void); ~Player(void);
virtual void OnBufferProcessedByOutput(IBuffer *buffer); virtual void OnBufferProcessedByOutput(IBuffer *buffer);
virtual void Notify();
void Play(); void Play();
void Stop(); void Stop();
@ -90,7 +89,6 @@ namespace musik { namespace core { namespace audio {
void ThreadLoop(); void ThreadLoop();
bool PreBuffer(); bool PreBuffer();
int State(); int State();
bool BufferQueueEmpty();
void ReleaseAllBuffers(); void ReleaseAllBuffers();
protected: protected:
@ -114,7 +112,7 @@ namespace musik { namespace core { namespace audio {
BufferList lockedBuffers; BufferList lockedBuffers;
boost::mutex mutex; boost::mutex mutex;
boost::condition waitCondition; boost::condition writeToOutputCondition;
double volume; double volume;
double currentPosition; double currentPosition;

View File

@ -134,7 +134,7 @@ BufferPtr Stream::GetNextBufferFromDecoder() {
this->decoderSamplePosition += buffer->Samples(); this->decoderSamplePosition += buffer->Samples();
/* calculate the position (seconds) in the buffer */ /* calculate the position (seconds) in the buffer */
buffer->position = ((double)this->decoderSamplePosition) / ((double)this->decoderSampleRate); buffer->position = ((double) this->decoderSamplePosition) / ((double) this->decoderSampleRate);
return buffer; return buffer;
} }

View File

@ -54,12 +54,6 @@ class IDecoder{
////////////////////////////////////////// //////////////////////////////////////////
virtual void Destroy() = 0; virtual void Destroy() = 0;
//////////////////////////////////////////
///\brief
///Get the length of the track in seconds
//////////////////////////////////////////
//virtual double Length() = 0;
////////////////////////////////////////// //////////////////////////////////////////
///\brief ///\brief
///Set the position in the source (in seconds) ///Set the position in the source (in seconds)

View File

@ -43,7 +43,7 @@ namespace musik { namespace core { namespace audio {
///\brief ///\brief
///Interface for the audio::Player to make IOuput plugins be able to make callbacks ///Interface for the audio::Player to make IOuput plugins be able to make callbacks
////////////////////////////////////////// //////////////////////////////////////////
class IPlayer{ class IPlayer {
public: public:
////////////////////////////////////////// //////////////////////////////////////////
///\brief ///\brief
@ -51,14 +51,6 @@ class IPlayer{
///processing. ///processing.
////////////////////////////////////////// //////////////////////////////////////////
virtual void OnBufferProcessedByOutput(IBuffer *buffer) = 0; virtual void OnBufferProcessedByOutput(IBuffer *buffer) = 0;
//////////////////////////////////////////
///\brief
///Notifies the Player that there may be buffer.
///ready to be released in the output plugin.
///TOOD: ugh... what?
//////////////////////////////////////////
virtual void Notify() = 0;
}; };
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////