]> Dogcows Code - chaz/yoink/blobdiff - src/Moof/Sound.cc
port to NetBSD
[chaz/yoink] / src / Moof / Sound.cc
index 27905ba4a1f4893e6f69f981ab0b7e7a2ed5cbf3..4c60e328085f60d7d282c2e70144079c49c5dbc2 100644 (file)
 *******************************************************************************/
 
 #include <cstdio>
+#include <deque>
 #include <string>
-#include <queue>
 #include <vector>
 
 #include <AL/al.h>
 #include <vorbis/codec.h>
 #include <vorbis/vorbisfile.h>
 
+#include "Exception.hh"
+#include "Library.hh"
 #include "Log.hh"
-#include "Mippleton.hh"
 #include "Sound.hh"
 #include "Timer.hh"
 
@@ -60,17 +61,13 @@ public:
        class Buffer;
        typedef boost::shared_ptr<Buffer> BufferP;
        
-       class Buffer : public Mippleton<Buffer>
+       class Buffer : public Library<Buffer>
        {
-               OggVorbis_File                  mOggStream;
-               ALenum                                  mFormat;
-               ALsizei                                 mFreq;
-               std::vector<ALuint>             mObjects;
-
        public:
 
                Buffer(const std::string& name) :
-                       Mippleton<Buffer>(name)
+                       Library<Buffer>(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<ALuint>::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<BufferP>::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<BufferP>             mQueue;
-       std::vector<BufferP>    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<ALuint>             mBufferObjects;
+
+       bool                                    mIsPlaying;
+       bool                                    mIsLooping;
+
+       std::deque<BufferP>             mQueue;
+
+       Timer                                   mStreamTimer;
 };
 
+
+Sound::Sound() :
+       // pass through
+       mImpl(new Sound::Impl) {}
+
 Sound::Sound(const std::string& name) :
        // pass through
        mImpl(new Sound::Impl(name)) {}
This page took 0.028351 seconds and 4 git commands to generate.