From 10710d3428da29ffb4acf92bfdd49db708b8e4c2 Mon Sep 17 00:00:00 2001 From: raidho36 Date: Wed, 12 Oct 2016 18:53:49 +0300 Subject: [PATCH] queueData now returns success state, couple bug fixes success state return added to queueData functions queueable source is now seekable while stopped fixed bug with creating queueable source with invalid format silently crashing LOVE fixed bug with stopped queueable source reported as not queueable --HG-- branch : minor --- src/modules/audio/Source.h | 2 +- src/modules/audio/null/Source.cpp | 4 +- src/modules/audio/null/Source.h | 2 +- src/modules/audio/openal/Pool.cpp | 4 +- src/modules/audio/openal/Pool.h | 2 +- src/modules/audio/openal/Source.cpp | 100 +++++++++++++++++++--------- src/modules/audio/openal/Source.h | 5 +- src/modules/audio/wrap_Audio.cpp | 10 +-- src/modules/audio/wrap_Source.cpp | 7 +- 9 files changed, 91 insertions(+), 45 deletions(-) diff --git a/src/modules/audio/Source.h b/src/modules/audio/Source.h index 03a1dff75..3eecbb592 100644 --- a/src/modules/audio/Source.h +++ b/src/modules/audio/Source.h @@ -103,7 +103,7 @@ public: virtual int getChannels() const = 0; virtual bool isQueueable() const = 0; - virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0; + virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) = 0; virtual Type getType() const; diff --git a/src/modules/audio/null/Source.cpp b/src/modules/audio/null/Source.cpp index f58bf8309..e6a6fe7ee 100644 --- a/src/modules/audio/null/Source.cpp +++ b/src/modules/audio/null/Source.cpp @@ -222,9 +222,9 @@ bool Source::isQueueable() const return false; } -void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) +bool Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) { - return; + return false; } diff --git a/src/modules/audio/null/Source.h b/src/modules/audio/null/Source.h index 5e9e25113..bd57804e4 100644 --- a/src/modules/audio/null/Source.h +++ b/src/modules/audio/null/Source.h @@ -77,7 +77,7 @@ public: virtual int getChannels() const; virtual bool isQueueable() const; - virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels); + virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels); private: diff --git a/src/modules/audio/openal/Pool.cpp b/src/modules/audio/openal/Pool.cpp index 9af27f5de..7427bd23e 100644 --- a/src/modules/audio/openal/Pool.cpp +++ b/src/modules/audio/openal/Pool.cpp @@ -283,10 +283,10 @@ double Pool::getDuration(Source *source, void *unit) return source->getDurationAtomic(unit); } -void Pool::queueData(Source *source, void *data, ALsizei length) +bool Pool::queueData(Source *source, void *data, ALsizei length) { thread::Lock lock(mutex); - source->queueDataAtomic(data, length); + return source->queueDataAtomic(data, length); } ALuint Pool::findi(const Source *source) const diff --git a/src/modules/audio/openal/Pool.h b/src/modules/audio/openal/Pool.h index facf75c04..3a32566aa 100644 --- a/src/modules/audio/openal/Pool.h +++ b/src/modules/audio/openal/Pool.h @@ -91,7 +91,7 @@ public: void seek(Source *source, float offset, void *unit); float tell(Source *source, void *unit); double getDuration(Source *source, void *unit); - void queueData(Source *source, void *data, ALsizei length); + bool queueData(Source *source, void *data, ALsizei length); bool play(const std::vector &sources); void stop(const std::vector &sources); diff --git a/src/modules/audio/openal/Source.cpp b/src/modules/audio/openal/Source.cpp index 2424f51e3..d0b8c6065 100644 --- a/src/modules/audio/openal/Source.cpp +++ b/src/modules/audio/openal/Source.cpp @@ -390,7 +390,6 @@ bool Source::update() bufferedBytes -= size; unusedBufferPush(buffers[i]); } - return !isFinished(); } } @@ -442,12 +441,6 @@ float Source::getVolume() const void Source::seekAtomic(float offset, void *unit) { - bool wasPlaying = isPlaying(); - - // To drain all buffers - if (valid && type == TYPE_STREAM) - stopAtomic(); - switch (*((Source::Unit *) unit)) { case Source::UNIT_SAMPLES: @@ -460,17 +453,58 @@ void Source::seekAtomic(float offset, void *unit) offsetSamples = offset * sampleRate; break; } - - if (type == TYPE_STREAM) - decoder->seek(offsetSeconds); - else if (valid) // Playing static or queue + + switch (type) { - alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); - offsetSamples = offsetSeconds = 0; - } + case TYPE_STATIC: + alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); + offsetSamples = offsetSeconds = 0; + break; + case TYPE_STREAM: + { + bool wasPlaying = isPlaying(); - if (wasPlaying && type == TYPE_STREAM) - playAtomic(source); + // To drain all buffers + if (valid) + stopAtomic(); + + decoder->seek(offsetSeconds); + + if (wasPlaying) + playAtomic(source); + + break; + } + case TYPE_QUEUE: + if (valid) + { + alSourcef(source, AL_SAMPLE_OFFSET, offsetSamples); + offsetSamples = offsetSeconds = 0; + } + else + { + ALint size; + ALuint buffer = unusedBufferPeek(); + + //emulate AL behavior, discarding buffer once playback head is past one + while (buffer != AL_NONE) + { + alGetBufferi(buffer, AL_SIZE, &size); + + if (offsetSamples < size / (bitDepth / 8 * channels)) + break; + + unusedBufferPop(); + buffer = unusedBufferPeek(); + bufferedBytes -= size; + offsetSamples -= size / (bitDepth / 8 * channels); + } + if (buffer == AL_NONE) + offsetSamples = 0; + offsetSeconds = offsetSamples / sampleRate; + } + break; + } } void Source::seek(float offset, Source::Unit unit) @@ -673,7 +707,7 @@ bool Source::isLooping() const return looping; } -void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) +bool Source::queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels) { if (type != TYPE_QUEUE) throw QueueTypeMismatchException(); @@ -688,41 +722,39 @@ void Source::queueData(void *data, int length, int dataSampleRate, int dataBitDe throw QueueMalformedLengthException(bitDepth / 8 * channels); if (length > 0) - pool->queueData(this, data, (ALsizei)length); + return pool->queueData(this, data, (ALsizei)length); } -void Source::queueDataAtomic(void *data, ALsizei length) +bool Source::queueDataAtomic(void *data, ALsizei length) { if (valid) { ALuint buffer = unusedBufferPeek(); if (buffer == AL_NONE) - return; //FIXME: out of buffer space handling? + return false; alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate); alSourceQueueBuffers(source, 1, &buffer); unusedBufferPop(); } - else //in not valid state, queue stack is "reversed" + else { - //remaining buffers are stored beyond the tip of unused stack - if (unusedBufferTop == MAX_BUFFERS - 1) - return; + //remaining buffers are stored beyond the tail of unused stack + if (unusedBufferTop >= (int)MAX_BUFFERS - 1) + return false; - ALuint buffer = unusedBuffers[++unusedBufferTop]; + ALuint buffer = unusedBuffers[unusedBufferTop + 1]; alBufferData(buffer, getFormat(channels, bitDepth), data, length, sampleRate); //new buffer must go last, so it goes to the base of the stack - for (unsigned int i = unusedBufferTop; i > 0; i--) - unusedBuffers[i] = unusedBuffers[i - 1]; - unusedBuffers[0] = buffer; + unusedBufferQueue(buffer); } bufferedBytes += length; + return true; } bool Source::isQueueable() const { - //in not valid state, queue stack is "reversed" - if (type != TYPE_QUEUE || (valid && unusedBufferTop < 0) || (!valid && unusedBufferTop == MAX_BUFFERS - 1)) + if (type != TYPE_QUEUE || (valid && unusedBufferTop < 0) || (!valid && unusedBufferTop >= (int)MAX_BUFFERS - 1)) return false; return true; } @@ -811,6 +843,7 @@ void Source::teardownAtomic() alSourceUnqueueBuffers(source, 1, &buffer); unusedBufferPush(buffer); } + // put unused buffers at the end of stack for (unsigned int i = 0; i < unused; i++) unusedBuffers[unusedBufferTop + 1 + i] = buffers[i]; @@ -1013,6 +1046,13 @@ void Source::unusedBufferPush(ALuint buffer) unusedBuffers[++unusedBufferTop] = buffer; } +void Source::unusedBufferQueue(ALuint buffer) +{ + for (unsigned int i = ++unusedBufferTop; i > 0; i--) + unusedBuffers[i] = unusedBuffers[i - 1]; + unusedBuffers[0] = buffer; +} + int Source::streamAtomic(ALuint buffer, love::sound::Decoder *d) { // Get more sound data. diff --git a/src/modules/audio/openal/Source.h b/src/modules/audio/openal/Source.h index 9026a238c..32158c0be 100644 --- a/src/modules/audio/openal/Source.h +++ b/src/modules/audio/openal/Source.h @@ -132,8 +132,8 @@ public: virtual int getChannels() const; virtual bool isQueueable() const; - virtual void queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels); - virtual void queueDataAtomic(void *data, ALsizei length); + virtual bool queueData(void *data, int length, int dataSampleRate, int dataBitDepth, int dataChannels); + virtual bool queueDataAtomic(void *data, ALsizei length); void prepareAtomic(); void teardownAtomic(); @@ -167,6 +167,7 @@ private: ALuint unusedBufferPeek(); ALuint *unusedBufferPop(); void unusedBufferPush(ALuint buffer); + void unusedBufferQueue(ALuint buffer); Pool *pool; ALuint source; diff --git a/src/modules/audio/wrap_Audio.cpp b/src/modules/audio/wrap_Audio.cpp index 6b0fde48d..68e6dcaef 100644 --- a/src/modules/audio/wrap_Audio.cpp +++ b/src/modules/audio/wrap_Audio.cpp @@ -47,18 +47,20 @@ int w_newSource(lua_State *L) Source *t = 0; if (lua_isnumber(L, 1) && lua_isnumber(L, 2) && lua_isnumber(L, 3)) - t = instance()->newSource((int)lua_tonumber(L, 1), (int)lua_tonumber(L, 2), (int)lua_tonumber(L, 3)); + luax_catchexcept(L, [&]() { + t = instance()->newSource((int)lua_tonumber(L, 1), (int)lua_tonumber(L, 2), (int)lua_tonumber(L, 3)); + }); else { - if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID)) - luax_convobj(L, 1, "sound", "newDecoder"); - Source::Type stype = Source::TYPE_STREAM; const char *stypestr = lua_isnoneornil(L, 2) ? 0 : lua_tostring(L, 2); if (stypestr && !Source::getConstant(stypestr, stype)) return luaL_error(L, "Invalid source type: %s", stypestr); + if (lua_isstring(L, 1) || luax_istype(L, 1, FILESYSTEM_FILE_ID) || luax_istype(L, 1, FILESYSTEM_FILE_DATA_ID)) + luax_convobj(L, 1, "sound", "newDecoder"); + if (stype == Source::TYPE_STATIC && luax_istype(L, 1, SOUND_DECODER_ID)) luax_convobj(L, 1, "sound", "newSoundData"); diff --git a/src/modules/audio/wrap_Source.cpp b/src/modules/audio/wrap_Source.cpp index 37a4ec898..bf4b4f705 100644 --- a/src/modules/audio/wrap_Source.cpp +++ b/src/modules/audio/wrap_Source.cpp @@ -340,22 +340,25 @@ int w_Source_isQueueable(lua_State *L) int w_Source_queueData(lua_State *L) { Source *t = luax_checksource(L, 1); + bool success; + luax_catchexcept(L, [&]() { if (luax_istype(L, 2, SOUND_SOUND_DATA_ID)) { love::sound::SoundData *s = luax_totype(L, 2, SOUND_SOUND_DATA_ID); - t->queueData(s->getData(), lua_isnumber(L, 3) ? (int)lua_tonumber(L, 3) : s->getSize(), s->getSampleRate(), s->getBitDepth(), s->getChannels()); + success = t->queueData(s->getData(), lua_isnumber(L, 3) ? (int)lua_tonumber(L, 3) : s->getSize(), s->getSampleRate(), s->getBitDepth(), s->getChannels()); } else if (lua_islightuserdata(L, 2)) { if (lua_isnumber(L, 4) && lua_isnumber(L, 5) && lua_isnumber(L, 6)) - t->queueData(lua_touserdata(L, 2), (int)lua_tonumber(L, 3), (int)lua_tonumber(L, 4), (int)lua_tonumber(L, 5), (int)lua_tonumber(L, 6)); + success = t->queueData(lua_touserdata(L, 2), (int)lua_tonumber(L, 3), (int)lua_tonumber(L, 4), (int)lua_tonumber(L, 5), (int)lua_tonumber(L, 6)); else return luaL_error(L, "No format specified."); } else return luaL_error(L, "Invalid data type."); }); + luax_pushboolean(L, success); return 1; }