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; }