[aquaria] Ogg streaming implemented

Andrew Church achurch+aquaria at achurch.org
Sun Jul 18 02:48:44 EDT 2010


I've finally gotten myself to buckle down and implement streamed decoding
for Ogg Vorbis audio using the OpenAL backend, as was discussed a while
ago.  As expected, it cuts down memory usage by close to 200MB, and fixes
the short freezes when changing areas or starting a cutscene.

The patch against current icculus.org code is below, and is also on the
ogg-streaming branch in my repository (http://achurch.org/cgi-bin/hg/aquaria/).

  --Andrew Church
    achurch at achurch.org
    http://achurch.org/

---------------------------------------------------------------------------

diff -r f08314f4174e -r a2b8997718d6 BBGE/FmodOpenALBridge.cpp
--- a/BBGE/FmodOpenALBridge.cpp	Sat Jul 10 15:04:54 2010 +0900
+++ b/BBGE/FmodOpenALBridge.cpp	Sun Jul 18 02:09:58 2010 +0900
@@ -45,6 +45,421 @@
 //#define _DEBUG 1
 #endif
 
+///////////////////////////////////////////////////////////////////////////
+
+// Decoder implementation for streamed Ogg Vorbis audio.
+
+class OggDecoder {
+public:
+    // Create a decoder that streams from a file.
+    OggDecoder(FILE *fp);
+
+    // Create a decoder that streams from a memory buffer.
+    OggDecoder(const void *data, long data_size);
+
+    ~OggDecoder();
+
+    // Start playing on the given channel, with optional looping.
+    bool start(ALuint source, bool loop);
+
+    // Decode audio into any free buffers.  Must be called periodically
+    // on systems without threads; may be called without harm on systems
+    // with threads (the function does nothing in that case).
+    void update();
+
+    // Terminate playback.
+    void stop();
+
+    // Return the current playback position in seconds.
+    double position();
+
+    // Memory buffer I/O callback functions for libvorbisfile.
+    static size_t mem_read(void *ptr, size_t size, size_t nmemb, void *datasource);
+    static int mem_seek(void *datasource, ogg_int64_t offset, int whence);
+    static long mem_tell(void *datasource);
+
+private:
+    // Decoding loop, run in a separate thread (if threads are available).
+    static void decode_loop(OggDecoder *this_);
+
+    // Decode and queue PCM data for one buffer; does nothing if the end
+    // of the stream has already been reached or an unrecoverable error
+    // has occurred during decoding.  If looping, the audio will instead
+    // restart at the beginning of the stream after reaching the end,
+    // but will still stop on an unrecoverable error.
+    void queue(ALuint buffer);
+
+    static const int NUM_BUFFERS = 8;
+    static const int BUFFER_LENGTH = 4096;  // In samples (arbitrary)
+    char pcm_buffer[BUFFER_LENGTH * 4];     // Temporary buffer for decoding
+    ALuint buffers[NUM_BUFFERS];
+    ALuint source;
+
+    // Data source.  If fp != NULL, the source is that file; otherwise, the
+    // source is the buffer pointed to by "data" with size "data_size" bytes.
+    FILE *fp;
+    const char *data;
+    long data_size;
+    long data_pos;  // Current read position for memory buffers
+
+    OggVorbis_File vf;
+    ALenum format;
+    int freq;
+
+#ifdef BBGE_BUILD_SDL
+    SDL_Thread *thread;
+#else
+    #warning Threads not supported, music may cut out on area changes!
+    // ... because the stream runs out of decoded data while the area is
+    // still loading, so OpenAL aborts playback.
+#endif
+    volatile bool stop_thread;
+
+    bool playing;
+    bool loop;
+    bool eof;  // End of file _or_ unrecoverable error encountered
+    unsigned int samples_done;  // Number of samples played and dequeued
+};
+
+// File I/O callback set (OV_CALLBACKS_NOCLOSE from libvorbis 1.2.0).
+// It might be better to just update libogg/libvorbis to the current
+// versions so we don't have to worry about identifier collisions --
+// we can then drop all this and use OV_CALLBACKS_NOCLOSE in the
+// ov_open_callbacks() call.
+static int _ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){
+  if(f==NULL)return(-1);
+#ifdef __MINGW32__
+  return fseeko64(f,off,whence);
+#elif defined (_WIN32)
+  return _fseeki64(f,off,whence);
+#else
+  return fseek(f,off,whence);
+#endif
+}
+static int noclose(FILE *f) {return 0;}
+static const ov_callbacks local_OV_CALLBACKS_NOCLOSE = {
+  (size_t (*)(void *, size_t, size_t, void *))  fread,
+  (int (*)(void *, ogg_int64_t, int))           _ov_header_fseek_wrap,
+  (int (*)(void *))                             noclose,  // NULL doesn't work in libvorbis-1.1.2
+  (long (*)(void *))                            ftell
+};
+
+// Memory I/O callback set.
+static const ov_callbacks ogg_memory_callbacks = {
+    OggDecoder::mem_read,
+    OggDecoder::mem_seek,
+    (int (*)(void *))noclose,
+    OggDecoder::mem_tell
+};
+
+
+OggDecoder::OggDecoder(FILE *fp)
+{
+    for (int i = 0; i < NUM_BUFFERS; i++)
+    {
+        buffers[i] = 0;
+    }
+    this->source = 0;
+    this->fp = fp;
+    this->data = NULL;
+    this->data_size = 0;
+    this->data_pos = 0;
+#ifdef BBGE_BUILD_SDL
+    this->thread = NULL;
+#endif
+    this->stop_thread = true;
+    this->playing = false;
+    this->loop = false;
+    this->eof = false;
+    this->samples_done = 0;
+}
+
+OggDecoder::OggDecoder(const void *data, long data_size)
+{
+    for (int i = 0; i < NUM_BUFFERS; i++)
+    {
+        buffers[i] = 0;
+    }
+    this->source = 0;
+    this->fp = NULL;
+    this->data = (const char *)data;
+    this->data_size = data_size;
+    this->data_pos = 0;
+#ifdef BBGE_BUILD_SDL
+    this->thread = NULL;
+#endif
+    this->stop_thread = true;
+    this->playing = false;
+    this->loop = false;
+    this->eof = false;
+    this->samples_done = 0;
+}
+
+OggDecoder::~OggDecoder()
+{
+    if (playing)
+        stop();
+
+    for (int i = 0; i < NUM_BUFFERS; i++)
+    {
+        if (buffers[i])
+            alDeleteBuffers(1, &buffers[i]);
+    }
+}
+
+bool OggDecoder::start(ALuint source, bool loop)
+{
+    this->source = source;
+    this->loop = loop;
+
+    if (fp) {
+        if (ov_open_callbacks(fp, &vf, NULL, 0, local_OV_CALLBACKS_NOCLOSE) != 0)
+        {
+            debugLog("ov_open() failed for file");
+            return false;
+        }
+    }
+    else
+    {
+        data_pos = 0;
+        if (ov_open_callbacks(this, &vf, NULL, 0, ogg_memory_callbacks) != 0)
+        {
+            debugLog("ov_open() failed for memory buffer");
+            return false;
+        }
+    }
+
+    vorbis_info *info = ov_info(&vf, -1);
+    if (!info)
+    {
+        debugLog("ov_info() failed");
+        ov_clear(&vf);
+        return false;
+    }
+    if (info->channels == 1)
+        format = AL_FORMAT_MONO16;
+    else if (info->channels == 2)
+        format = AL_FORMAT_STEREO16;
+    else
+    {
+        std::ostringstream os;
+        os << "Bad channel count " << info->channels;
+        debugLog(os.str());
+        ov_clear(&vf);
+        return false;
+    }
+    freq = info->rate;
+
+    /* NOTE: The failure to use alGetError() here and elsewhere is
+     * intentional -- since alGetError() writes to a global buffer and
+     * is thus not thread-safe, we can't use it either in the decoding
+     * threads _or_ here in the main thread.  In this case, we rely on 
+     * the specification that failing OpenAL calls do not modify return
+     * parameters to detect failure; for functions that do not return
+     * values, we have no choice but to hope for the best.  (From a
+     * multithreading point of view, the insistence on using a global
+     * error buffer instead of returning success/failure or error codes
+     * from functions is a remarkably poor design decision.  Not that a
+     * mere library user has much choice except to live with it...)
+     * --achurch */
+    buffers[0] = 0;
+    alGenBuffers(NUM_BUFFERS, buffers);
+    if (!buffers[0])
+    {
+        debugLog("Failed to generate OpenAL buffers");
+        ov_clear(&vf);
+        return false;
+    }
+
+    playing = true;
+    eof = false;
+    samples_done = 0;
+    for (int i = 0; i < NUM_BUFFERS; i++)
+        queue(buffers[i]);
+
+#ifdef BBGE_BUILD_SDL
+    stop_thread = false;
+    thread = SDL_CreateThread((int (*)(void *))decode_loop, this);
+    if (!thread)
+    {
+        debugLog("Failed to create Ogg Vorbis decode thread: "
+                 + std::string(SDL_GetError()));
+    }
+#endif
+
+    return true;
+}
+
+void OggDecoder::update()
+{
+    if (!playing)
+        return;
+#ifdef BBGE_BUILD_SDL
+    if (thread)
+        return;
+#endif
+
+    int processed = 0;
+    alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
+    for (int i = 0; i < processed; i++)
+    {
+        samples_done += BUFFER_LENGTH;
+        ALuint buffer;
+        alSourceUnqueueBuffers(source, 1, &buffer);
+        queue(buffer);
+    }
+}
+
+void OggDecoder::stop()
+{
+    if (!playing)
+        return;
+
+#ifdef BBGE_BUILD_SDL
+    if (thread)
+    {
+        stop_thread = true;
+        SDL_WaitThread(thread, NULL);
+        thread = NULL;
+    }
+#endif
+
+    alSourceStop(source);
+    int queued = 0;
+    alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
+    for (int i = 0; i < queued; i++)
+    {
+        ALuint buffer;
+        alSourceUnqueueBuffers(source, 1, &buffer);
+    }
+    for (int i = 0; i < NUM_BUFFERS; i++)
+    {
+        alDeleteBuffers(1, &buffers[i]);
+        buffers[i] = 0;
+    }
+}
+
+double OggDecoder::position()
+{
+    ALint samples_played = 0;
+    alGetSourcei(source, AL_SAMPLE_OFFSET, &samples_played);
+    samples_played += samples_done;
+    return (double)samples_played / (double)freq;
+}
+
+void OggDecoder::decode_loop(OggDecoder *this_)
+{
+    while (!this_->stop_thread)
+    {
+#ifdef BBGE_BUILD_SDL
+        SDL_Delay(1);
+#endif
+
+        int processed = 0;
+        alGetSourcei(this_->source, AL_BUFFERS_PROCESSED, &processed);
+        for (int i = 0; i < processed; i++)
+        {
+            this_->samples_done += BUFFER_LENGTH;
+            ALuint buffer = 0;
+            alSourceUnqueueBuffers(this_->source, 1, &buffer);
+            if (buffer)
+                this_->queue(buffer);
+        }
+    }
+}
+
+void OggDecoder::queue(ALuint buffer)
+{
+    if (!playing || eof)
+        return;
+
+    const int channels = (format == AL_FORMAT_STEREO16 ? 2 : 1);
+    const int buffer_size = BUFFER_LENGTH * channels * 2;
+    int pcm_size = 0;
+    bool just_looped = false;  // Avoid infinite loops on empty files.
+
+    while (pcm_size < buffer_size && !eof)
+    {
+        int bitstream_unused;
+        const int nread = ov_read(
+            &vf, pcm_buffer + pcm_size, buffer_size - pcm_size,
+            /*bigendianp*/ 0, /*word*/ 2, /*sgned*/ 1, &bitstream_unused
+        );
+        if (nread == 0 || nread == OV_EOF)
+        {
+            if (loop && !just_looped)
+            {
+                just_looped = true;
+                samples_done = 0;
+                ov_pcm_seek(&vf, 0);
+            }
+            else
+            {
+                eof = true;
+            }
+        }
+        else if (nread == OV_HOLE)
+        {
+            debugLog("Warning: decompression error, data dropped");
+        }
+        else if (nread < 0)
+        {
+            std::ostringstream os;
+            os << "Decompression error: " << nread;
+            debugLog(os.str());
+            eof = true;
+        }
+        else
+        {
+            pcm_size += nread;
+            just_looped = false;
+        }
+    }
+
+    if (pcm_size > 0)
+    {
+        alBufferData(buffer, format, pcm_buffer, pcm_size, freq);
+        alSourceQueueBuffers(source, 1, &buffer);
+    }
+}
+
+size_t OggDecoder::mem_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+    OggDecoder *this_ = (OggDecoder *)datasource;
+
+    long to_read = size * nmemb;
+    if (to_read > this_->data_size - this_->data_pos)
+        to_read = this_->data_size - this_->data_pos;
+    if (to_read < 0)
+        to_read = 0;
+    memcpy(ptr, this_->data + this_->data_pos, to_read);
+    this_->data_pos += to_read;
+    return to_read / size;
+}
+
+int OggDecoder::mem_seek(void *datasource, ogg_int64_t offset, int whence)
+{
+    OggDecoder *this_ = (OggDecoder *)datasource;
+    if (whence == SEEK_CUR)
+        offset += this_->data_pos;
+    else if (whence == SEEK_END)
+        offset += this_->data_size;
+    if (offset < 0)
+        offset = 0;
+    else if (offset > this_->data_size)
+        offset = this_->data_size;
+    this_->data_pos = offset;
+    return 0;
+}
+
+long OggDecoder::mem_tell(void *datasource)
+{
+    OggDecoder *this_ = (OggDecoder *)datasource;
+    return this_->data_pos;
+}
+
+///////////////////////////////////////////////////////////////////////////
+
 /* for porting purposes... */
 #ifndef STUBBED
 #ifndef _DEBUG
@@ -92,8 +507,63 @@
 
 static ALenum GVorbisFormat = AL_NONE;
 
+// FMOD::Sound implementation ...
+
+class OpenALSound
+{
+public:
+    OpenALSound(FILE *_fp, const bool _looping);
+    OpenALSound(void *_data, long _size, const bool _looping);
+    FILE *getFile() const { return fp; }
+    const void *getData() const { return data; }
+    long getSize() const { return size; }
+    bool isLooping() const { return looping; }
+    FMOD_RESULT release();
+    void reference() { refcount++; }
+
+private:
+    FILE * const fp;
+    void * const data;  // Only used if fp==NULL
+    const long size;    // Only used if fp==NULL
+    const bool looping;
+    int refcount;
+};
+
+OpenALSound::OpenALSound(FILE *_fp, const bool _looping)
+    : fp(_fp)
+    , data(NULL)
+    , size(0)
+    , looping(_looping)
+    , refcount(1)
+{
+}
+
+OpenALSound::OpenALSound(void *_data, long _size, const bool _looping)
+    : fp(NULL)
+    , data(_data)
+    , size(_size)
+    , looping(_looping)
+    , refcount(1)
+{
+}
+
+ALBRIDGE(Sound,release,(),())
+FMOD_RESULT OpenALSound::release()
+{
+    refcount--;
+    if (refcount <= 0)
+    {
+	if (fp)
+	    fclose(fp);
+	else
+	    free(data);
+        delete this;
+    }
+    return FMOD_OK;
+}
+
+
 class OpenALChannelGroup;
-class OpenALSound;
 
 class OpenALChannel
 {
@@ -111,6 +581,7 @@
     void setGroupVolume(const float _volume);
     void setSourceName(const ALuint _sid) { sid = _sid; }
     ALuint getSourceName() const { return sid; }
+    bool start(OpenALSound *sound);
     void update();
     void reacquire();
     bool isInUse() const { return inuse; }
@@ -125,6 +596,7 @@
     float frequency;
     OpenALChannelGroup *group;
     OpenALSound *sound;
+    OggDecoder *decoder;
     bool inuse;
     bool initial;
 };
@@ -188,10 +660,29 @@
     SANITY_CHECK_OPENAL_CALL();
 }
 
+bool OpenALChannel::start(OpenALSound *sound)
+{
+    if (decoder)
+	delete decoder;
+    if (sound->getFile())
+	decoder = new OggDecoder(sound->getFile());
+    else
+	decoder = new OggDecoder(sound->getData(), sound->getSize());
+    if (!decoder->start(sid, sound->isLooping()))
+    {
+	delete decoder;
+	decoder = NULL;
+	return false;
+    }
+    return true;
+}
+
 void OpenALChannel::update()
 {
     if (inuse)
     {
+	if (decoder)
+	    decoder->update();
         ALint state = 0;
         alGetSourceiv(sid, AL_SOURCE_STATE, &state);
         SANITY_CHECK_OPENAL_CALL();
@@ -214,10 +705,17 @@
 FMOD_RESULT OpenALChannel::getPosition(unsigned int *position, FMOD_TIMEUNIT postype)
 {
     assert(postype == FMOD_TIMEUNIT_MS);
-    ALfloat secs = 0.0f;
-    alGetSourcefv(sid, AL_SEC_OFFSET, &secs);
-    SANITY_CHECK_OPENAL_CALL();
-    *position = (unsigned int) (secs * 1000.0f);
+    if (decoder)
+    {
+        *position = (unsigned int) (decoder->position() * 1000.0);
+    }
+    else
+    {
+        ALfloat secs = 0.0f;
+        alGetSourcefv(sid, AL_SEC_OFFSET, &secs);
+        SANITY_CHECK_OPENAL_CALL();
+        *position = (unsigned int) (secs * 1000.0f);
+    }
     return FMOD_OK;
 }
 
@@ -432,44 +930,6 @@
 }
 
 
-// FMOD::Sound implementation ...
-
-class OpenALSound
-{
-public:
-    OpenALSound(const ALuint _bid, const bool _looping);
-    bool isLooping() const { return looping; }
-    ALuint getBufferName() const { return bid; }
-    FMOD_RESULT release();
-    void reference() { refcount++; }
-
-private:
-    const ALuint bid;  // buffer id
-    const bool looping;
-    int refcount;
-};
-
-OpenALSound::OpenALSound(const ALuint _bid, const bool _looping)
-    : bid(_bid)
-    , looping(_looping)
-    , refcount(1)
-{
-}
-
-ALBRIDGE(Sound,release,(),())
-FMOD_RESULT OpenALSound::release()
-{
-    refcount--;
-    if (refcount <= 0)
-    {
-        alDeleteBuffers(1, &bid);
-        SANITY_CHECK_OPENAL_CALL();
-        delete this;
-    }
-    return FMOD_OK;
-}
-
-
 void OpenALChannel::setSound(OpenALSound *_sound)
 {
     if (sound)
@@ -485,6 +945,11 @@
 ALBRIDGE(Channel,stop,(),())
 FMOD_RESULT OpenALChannel::stop()
 {
+    if (decoder)
+    {
+	delete decoder;
+	decoder = NULL;
+    }
     alSourceStop(sid);
     SANITY_CHECK_OPENAL_CALL();
     alSourcei(sid, AL_BUFFER, 0);
@@ -566,17 +1031,16 @@
     return FMOD_ERR_INTERNAL;
 }
 
-static void *decode_to_pcm(const char *_fname, ALenum &format, ALsizei &size, ALuint &freq, const bool streaming)
+ALBRIDGE(System,createSound,(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound),(name_or_data,mode,exinfo,sound))
+FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE mode, const FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound)
 {
-#ifdef __POWERPC__
-    const int bigendian = 1;
-#else
-    const int bigendian = 0;
-#endif
+    assert(!exinfo);
+
+    FMOD_RESULT retval = FMOD_ERR_INTERNAL;
 
     // !!! FIXME: if it's not Ogg, we don't have a decoder. I'm lazy.  :/
-    char *fname = (char *) alloca(strlen(_fname) + 16);
-    strcpy(fname, _fname);
+    char *fname = (char *) alloca(strlen(name_or_data) + 16);
+    strcpy(fname, name_or_data);
     char *ptr = strrchr(fname, '.');
     if (ptr) *ptr = '\0';
     strcat(fname, ".ogg");
@@ -585,118 +1049,45 @@
     #undef fopen
     FILE *io = fopen(core->adjustFilenameCase(fname).c_str(), "rb");
     if (io == NULL)
-        return NULL;
+        return FMOD_ERR_INTERNAL;
 
-    ALubyte *retval = NULL;
-
-    if ((streaming) && (GVorbisFormat != AL_NONE))
+    if (mode & FMOD_CREATESTREAM)
     {
-        // Can we just feed it to the AL compressed?
-        format = GVorbisFormat;
-        freq = 44100;
+        *sound = (Sound *) new OpenALSound(io, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL)));
+        retval = FMOD_OK;
+    }
+    else
+    {
         fseek(io, 0, SEEK_END);
-        size = ftell(io);
-        fseek(io, 0, SEEK_SET);
-        retval = (ALubyte *) malloc(size);
-        size_t rc = fread(retval, size, 1, io);
-        fclose(io);
-        if (rc != 1)
+        long size = ftell(io);
+        if (fseek(io, 0, SEEK_SET) != 0)
         {
-            free(retval);
-            return NULL;
-        }
-        return retval;
-    }
-
-    // Uncompress and feed to the AL.
-    OggVorbis_File vf;
-    memset(&vf, '\0', sizeof (vf));
-    if (ov_open(io, &vf, NULL, 0) == 0)
-    {
-        int bitstream = 0;
-        vorbis_info *info = ov_info(&vf, -1);
-        size = 0;
-        format = (info->channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
-        freq = info->rate;
-
-        if ((info->channels != 1) && (info->channels != 2))
-        {
-            ov_clear(&vf);
-            return NULL;
+            debugLog("Seek error on " + std::string(fname));
+            fclose(io);
+            return FMOD_ERR_INTERNAL;
         }
 
-        char buf[1024 * 16];
-        long rc = 0;
-        size_t allocated = 64 * 1024;
-        retval = (ALubyte *) malloc(allocated);
-        while ( (rc = ov_read(&vf, buf, sizeof (buf), bigendian, 2, 1, &bitstream)) != 0 )
+        void *data = malloc(size);
+        if (data == NULL)
         {
-            if (rc > 0)
-            {
-                size += rc;
-                if (size >= allocated)
-                {
-                    allocated *= 2;
-                    ALubyte *tmp = (ALubyte *) realloc(retval, allocated);
-                    if (tmp == NULL)
-                    {
-                        free(retval);
-                        retval = NULL;
-                        break;
-                    }
-                    retval = tmp;
-                }
-                memcpy(retval + (size - rc), buf, rc);
-            }
+            debugLog("Out of memory for " + std::string(fname));
+            fclose(io);
+            return FMOD_ERR_INTERNAL;
         }
-        ov_clear(&vf);
-        return retval;
-    }
 
-    fclose(io);
-    return NULL;
-}
+        long nread = fread(data, 1, size, io);
+        fclose(io);
+        if (nread != size)
+        {
+            debugLog("Failed to read data from " + std::string(fname));
+            free(data);
+            return FMOD_ERR_INTERNAL;
+        }
 
-
-ALBRIDGE(System,createSound,(const char *name_or_data, FMOD_MODE mode, FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound),(name_or_data,mode,exinfo,sound))
-FMOD_RESULT OpenALSystem::createSound(const char *name_or_data, const FMOD_MODE mode, const FMOD_CREATESOUNDEXINFO *exinfo, Sound **sound)
-{
-    assert(!exinfo);
-
-    FMOD_RESULT retval = FMOD_ERR_INTERNAL;
-    ALenum pcmfmt = AL_NONE;
-    ALsizei pcmsize = 0;
-    ALuint pcmfreq = 0;
-    void *pcm = decode_to_pcm(name_or_data, pcmfmt, pcmsize, pcmfreq, (mode & FMOD_CREATESTREAM) != 0);
-    if (pcm == NULL)
-        return FMOD_ERR_INTERNAL;
-
-    #if 0
-    static int dump_id = 0;
-    char buf[128];
-    snprintf(buf, sizeof (buf), "dump_%d.pcm", dump_id++);
-    FILE *io = fopen(buf, "wb");
-    if (io != NULL)
-    {
-        fwrite(pcm, pcmsize, 1, io);
-        fclose(io);
-    }
-    #endif
-
-    ALuint bid = 0;
-    alGetError();
-    alGenBuffers(1, &bid);
-    SANITY_CHECK_OPENAL_CALL();
-    if (alGetError() == AL_NO_ERROR)
-    {
-        alBufferData(bid, pcmfmt, pcm, pcmsize, pcmfreq);
-        SANITY_CHECK_OPENAL_CALL();
-        *sound = (Sound *) new OpenALSound(bid, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL)));
+        *sound = (Sound *) new OpenALSound(data, size, (((mode & FMOD_LOOP_OFF) == 0) && (mode & FMOD_LOOP_NORMAL)));
         retval = FMOD_OK;
     }
 
-    free(pcm);
-
     return retval;
 }
 
@@ -815,10 +1206,11 @@
     alSourceStop(sid);  // stop any playback, set to AL_INITIAL.
     alSourceRewind(sid);  // stop any playback, set to AL_INITIAL.
     SANITY_CHECK_OPENAL_CALL();
-    alSourcei(sid, AL_BUFFER, sound->getBufferName());
+    alSourcei(sid, AL_BUFFER, NULL);  // Reset state to AL_UNDETERMINED.
     SANITY_CHECK_OPENAL_CALL();
-    alSourcei(sid, AL_LOOPING, sound->isLooping() ? AL_TRUE : AL_FALSE);
-    SANITY_CHECK_OPENAL_CALL();
+
+    if (!channels[channelid].start(sound))
+	return FMOD_ERR_INTERNAL;
 
     channels[channelid].reacquire();
     channels[channelid].setPaused(paused);


More information about the aquaria mailing list