[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