X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2FMoof%2FSound.cc;h=4c60e328085f60d7d282c2e70144079c49c5dbc2;hp=a04d29b814943de018f678a3425448053f17ab8f;hb=c9e20ac06383b20ceb5404c9237e319c2e90d157;hpb=bffc879fc8ee8167bb123310d39fad4e2f426ffd diff --git a/src/Moof/Sound.cc b/src/Moof/Sound.cc index a04d29b..4c60e32 100644 --- a/src/Moof/Sound.cc +++ b/src/Moof/Sound.cc @@ -27,14 +27,15 @@ *******************************************************************************/ #include +#include #include -#include #include #include #include #include +#include "Exception.hh" #include "Library.hh" #include "Log.hh" #include "Sound.hh" @@ -62,15 +63,11 @@ public: class Buffer : public Library { - OggVorbis_File mOggStream; - ALenum mFormat; - ALsizei mFreq; - std::vector mObjects; - public: Buffer(const std::string& name) : - Library(name) + Library(name), + mBuffer(-1) { mOggStream.datasource = 0; openFile(); @@ -78,16 +75,11 @@ public: ~Buffer() { - while (!mObjects.empty()) - { - alDeleteBuffers(1, &mObjects.back()); - mObjects.pop_back(); - } - if (mOggStream.datasource) { ov_clear(&mOggStream); } + if (int(mBuffer) != -1) alDeleteBuffers(1, &mBuffer); } @@ -106,7 +98,7 @@ public: { logWarning("error while loading sound %s", getName().c_str()); - throw Exception(Exception::BAD_AUDIO_FORMAT); + throw Exception(ErrorCode::UNKNOWN_AUDIO_FORMAT); } vorbis_info* vorbisInfo = ov_info(&mOggStream, -1); @@ -145,57 +137,22 @@ public: if (size == 0) { logWarning("decoded no bytes from %s", getName().c_str()); - //throw Exception(Exception::FILE_NOT_FOUND); + //throw Exception("file_not_found"); return; } - ALuint obj; - alGenBuffers(1, &obj); - - alBufferData(obj, mFormat, data, size, mFreq); - - mObjects.push_back(obj); + alGenBuffers(1, &mBuffer); - alSourcei(source, AL_BUFFER, obj); + alBufferData(mBuffer, mFormat, data, size, mFreq); + alSourcei(source, AL_BUFFER, mBuffer); - // don't need this anymore + // don't need to keep this loaded ov_clear(&mOggStream); mOggStream.datasource = 0; } - - void beginStream(ALuint source, int nBuffers = 8) - { - if (!mOggStream.datasource) openFile(); - if (!mOggStream.datasource) return; - - ALuint objs[nBuffers]; - alGenBuffers(nBuffers, objs); - - for (int i = 0; i < nBuffers; ++i) - { - mObjects.push_back(objs[i]); - stream(objs[i]); - } - - alSourceQueueBuffers(source, nBuffers, objs); - } - - enum StreamStatus - { - STREAM_OK = 0, - STREAM_EOF = 1, - STREAM_WRONG = 2 - }; - - StreamStatus stream(ALuint buffer) + bool stream(ALuint buffer) { - std::vector::iterator it = - std::find(mObjects.begin(), mObjects.end(), buffer); - - // that buffer doesn't belong to us - if (it == mObjects.end()) return STREAM_WRONG; - char data[BUFFER_SIZE]; int size = 0; @@ -216,47 +173,41 @@ public: } } - if (size == 0) return STREAM_EOF; + if (size == 0) return false; alBufferData(buffer, mFormat, data, size, mFreq); - return STREAM_OK; + return true; } - inline void rewind() + void rewind() { if (!mOggStream.datasource) openFile(); else ov_raw_seek(&mOggStream, 0); } - // delete unused buffers, return true if all buffers deleted - inline bool clear() - { - // clear any openal errors - alGetError(); + private: - while (!mObjects.empty()) - { - ALuint buffer = mObjects.back(); - alDeleteBuffers(1, &buffer); - - // if an error occured, the buffer was not deleted because it's - // still in use by some source - if (alGetError() != AL_NO_ERROR) return false; + OggVorbis_File mOggStream; + ALenum mFormat; + ALsizei mFreq; + ALuint mBuffer; + }; - mObjects.pop_back(); - } - return true; - } - }; + Impl() + { + init(); + } + Impl(const std::string& name) + { + init(); + enqueue(name); + } - Impl(const std::string& name) : - mBuffer(Buffer::getInstance(name)), - mIsPlaying(false), - mIsLooping(false) + void init() { ALfloat zero[] = {0.0f, 0.0f, 0.0f}; @@ -266,22 +217,35 @@ public: alSourcef(mSource, AL_GAIN, 1.0f); alSourcefv(mSource, AL_POSITION, zero); alSourcefv(mSource, AL_VELOCITY, zero); + + mIsPlaying = false; + mIsLooping = false; } ~Impl() { + stop(); + alDeleteSources(1, &mSource); + + while (!mBufferObjects.empty()) + { + alDeleteBuffers(1, &mBufferObjects.back()); + mBufferObjects.pop_back(); + } } void play() { + if (mQueue.empty()) return; + ALenum type; alGetSourcei(mSource, AL_SOURCE_TYPE, &type); if (type != AL_STATIC) { - mBuffer->loadAll(mSource); + mQueue.front()->loadAll(mSource); } alSourcei(mSource, AL_LOOPING, mIsLooping); @@ -292,12 +256,11 @@ public: void stream() { - ALenum type; - alGetSourcei(mSource, AL_SOURCE_TYPE, &type); + stop(); alSourcei(mSource, AL_BUFFER, AL_NONE); - mBuffer->rewind(); - mBuffer->beginStream(mSource); + mQueue.front()->rewind(); + beginStream(); alSourcei(mSource, AL_LOOPING, AL_FALSE); alSourcePlay(mSource); @@ -307,7 +270,24 @@ public: Timer::REPEAT); } - inline void update() + void beginStream() + { + ALuint buffer; + for (int i = mBufferObjects.size(); i < 8; ++i) + { + alGenBuffers(1, &buffer); + mBufferObjects.push_back(buffer); + } + for (int i = 0; i < 8; ++i) + { + buffer = mBufferObjects[i]; + mQueue.front()->stream(buffer); + alSourceQueueBuffers(mSource, 1, &buffer); + } + } + + + void update() { ALint finished = 0; @@ -315,111 +295,112 @@ public: while (finished-- > 0) { - ALuint buffer; + ALuint bufferObj; + alSourceUnqueueBuffers(mSource, 1, &bufferObj); - alSourceUnqueueBuffers(mSource, 1, &buffer); + BufferP buffer = mQueue.front(); + bool streamed = buffer->stream(bufferObj); - Buffer::StreamStatus status = mBuffer->stream(buffer); - - if (status == Buffer::STREAM_OK) + if (streamed) { - alSourceQueueBuffers(mSource, 1, &buffer); + alSourceQueueBuffers(mSource, 1, &bufferObj); } - else if (status == Buffer::STREAM_EOF) + else { + // the buffer couldn't be streamed, so get rid of it + mQueue.pop_front(); + if (!mQueue.empty()) { // begin the next buffer in the queue - mExpired.push_back(mBuffer); - mBuffer = mQueue.front(); - mQueue.pop(); - mBuffer->beginStream(mSource, 1); + mQueue.front()->rewind(); + mQueue.front()->stream(bufferObj); + alSourceQueueBuffers(mSource, 1, &bufferObj); + logInfo("loading new buffer"); } else if (mIsLooping) { - // restart from the beginning - mBuffer->rewind(); - mBuffer->stream(buffer); - alSourceQueueBuffers(mSource, 1, &buffer); + // reload the same buffer + mQueue.push_back(buffer); + buffer->rewind(); + buffer->stream(bufferObj); + alSourceQueueBuffers(mSource, 1, &bufferObj); + logInfo("looping same buffer"); } } - else if (status == Buffer::STREAM_WRONG) - { - clear(); - mBuffer->beginStream(mSource, 1); - } } ALenum state; alGetSourcei(mSource, AL_SOURCE_STATE, &state); // restart playing if we're stopped but supposed to be playing... this - // means we didn't queue enough and the audio skipped + // means we didn't queue enough and the audio skipped :-( if (mIsPlaying && state != AL_PLAYING) { alSourcePlay(mSource); } } - inline void clear() - { - // try to remove expired buffers - std::vector::iterator it; - for (it = mExpired.end() - 1; it >= mExpired.begin(); --it) - { - if ((*it)->clear()) mExpired.erase(it); - } - } - void stop() { alSourceStop(mSource); mIsPlaying = false; + + mStreamTimer.invalidate(); } - inline void pause() + void pause() { alSourcePause(mSource); mIsPlaying = false; + + mStreamTimer.invalidate(); } - inline void resume() + void resume() { alSourcePlay(mSource); mIsPlaying = true; + + ALenum type; + alGetSourcei(mSource, AL_SOURCE_TYPE, &type); + + if (type == AL_STREAMING) + { + mStreamTimer.init(boost::bind(&Impl::streamUpdate, this, _1, _2), + 1.0, Timer::REPEAT); + } } - inline void setSample(const std::string& name) + void setSample(const std::string& name) { bool playing = isPlaying(); ALenum type; alGetSourcei(mSource, AL_SOURCE_TYPE, &type); stop(); + mQueue.clear(); //alSourcei(mSource, AL_BUFFER, AL_NONE); - mBuffer = Buffer::getInstance(name); + enqueue(name); - if (type == AL_STREAMING) - { - if (playing) stream(); - } - else + if (playing) { - if (playing) play(); + if (type == AL_STREAMING) stream(); + else play(); } } - inline void enqueue(const std::string& name) + void enqueue(const std::string& name) { BufferP buffer = Buffer::getInstance(name); - mQueue.push(buffer); + mQueue.push_back(buffer); } - inline bool isPlaying() const + bool isPlaying() const { if (mIsPlaying) return true; @@ -430,7 +411,7 @@ public: } - inline void setLooping(bool looping) + void setLooping(bool looping) { mIsLooping = looping; @@ -444,17 +425,6 @@ public: } - ALuint mSource; - BufferP mBuffer; - - bool mIsPlaying; - bool mIsLooping; - - std::queue mQueue; - std::vector mExpired; - - Timer mStreamTimer; - void streamUpdate(Timer& timer, Scalar t) { // don't let the music die! @@ -462,8 +432,24 @@ public: // TODO - might be nice to also allow using threads for streaming rather // than a timer, probably as a compile-time option } + + + ALuint mSource; + std::vector mBufferObjects; + + bool mIsPlaying; + bool mIsLooping; + + std::deque mQueue; + + Timer mStreamTimer; }; + +Sound::Sound() : + // pass through + mImpl(new Sound::Impl) {} + Sound::Sound(const std::string& name) : // pass through mImpl(new Sound::Impl(name)) {}