From 7f3d976ac6dcda28fdbcb4700c066ff477c2a319 Mon Sep 17 00:00:00 2001 From: Ralty <78720179+Raltyro@users.noreply.github.com> Date: Tue, 15 Jul 2025 01:22:53 +0700 Subject: [PATCH] Fix FlxSound not completing correctly, & callbacks --- source/flixel/sound/FlxSound.hx | 29 ++++--- .../backend/native/NativeAudioSource.hx | 80 +++++++++++-------- source/openfl/media/Sound.hx | 24 +----- 3 files changed, 70 insertions(+), 63 deletions(-) diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx index fdf59233..a1719197 100644 --- a/source/flixel/sound/FlxSound.hx +++ b/source/flixel/sound/FlxSound.hx @@ -296,8 +296,9 @@ class FlxSound extends FlxBasic { active = false; _lastTime = null; - if (_channel != null) { - _channel.removeEventListener(Event.SOUND_COMPLETE, stopped); + if (_source != null) { + _source.onComplete.remove(stopped); + _source.onLoop.remove(source_looped); } if (destroySound) { @@ -496,8 +497,8 @@ class FlxSound extends FlxBasic { _transform.pan = 0; _length = _source.length; - _source.onComplete.add(_channel.source_onComplete); - _source.onLoop.add(_channel.source_onLoop); + _source.onComplete.add(stopped); + _source.onLoop.add(source_looped); _channel.__soundTransform = _transform; _channel.__isValid = true; } @@ -674,7 +675,7 @@ class FlxSound extends FlxBasic { _paused = false; _time = startTime; _lastTime = FlxG.game.getTicks(); - if (_channel == null || !_channel.__isValid || _source.__backend == null #if lime_openal || _source.__backend.disposed || _source.__backend.handle == null #end) + if (_channel == null || !_channel.__isValid || _source.__backend == null #if lime_cffi || _source.__backend.disposed || _source.__backend.handle == null #end) makeChannel(); if (_channel != null) { @@ -687,12 +688,11 @@ class FlxSound extends FlxBasic { _channel.__lastPeakTime = -10; _channel.__leftPeak = 0; _channel.__rightPeak = 0; - _channel.addEventListener(Event.SOUND_COMPLETE, stopped); - #if lime_openal _source.__backend.playing = true; #end + #if lime_cffi _source.__backend.playing = true; #end _source.offset = 0; _source.currentTime = startTime + _offset; - #if !lime_openal _source.play(); #end + #if !lime_cffi _source.play(); #end looped = looped; loopTime = loopTime; @@ -706,7 +706,7 @@ class FlxSound extends FlxBasic { } } - function stopped(?_) { + function stopped() { onFinish.dispatch(); if (onComplete != null) onComplete(); @@ -718,6 +718,17 @@ class FlxSound extends FlxBasic { else cleanup(autoDestroy); } + function source_looped() { + if (onComplete != null) onComplete(); + + if (!looped) { + cleanup(autoDestroy); + _lastTime = FlxG.game.getTicks(); + _time = loopTime; + } + else _channel.loops = 999; + } + /** * Internal event handler for ID3 info (i.e. fetching the song name). */ diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx index 3ef6b002..75832ee4 100644 --- a/source/lime/_internal/backend/native/NativeAudioSource.hx +++ b/source/lime/_internal/backend/native/NativeAudioSource.hx @@ -25,7 +25,7 @@ import lime.utils.ArrayBufferView; @:access(lime.media.AudioBuffer) @:access(lime.utils.ArrayBufferView) class NativeAudioSource { - private static var STREAM_BUFFER_SIZE:Int = 0x1000; + private static var STREAM_BUFFER_SAMPLES:Int = 0x1000; private static var STREAM_MAX_BUFFERS:Int = 32; private static var STREAM_TIMER_FREQUENCY:Int = 100; private static var STREAM_BUFFER_FREQUENCY:Int = 6; @@ -102,13 +102,22 @@ class NativeAudioSource { else if (channels == 2) format = bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8; else format = bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8; + if (buffer.data != null) { + streamed = false; + samples = (dataLength = (buffer.data.length >> 0)) / buffer.channels / (buffer.bitsPerSample >> 3); + + if (buffer.__srcBuffer == null && (buffer.__srcBuffer = AL.createBuffer()) != null) + AL.bufferData(buffer.__srcBuffer, format, buffer.data, buffer.data.length, buffer.sampleRate); + + AL.sourcei(handle, AL.BUFFER, buffer.__srcBuffer); + } #if lime_vorbis - if (buffer.__srcVorbisFile != null) { + else if (buffer.__srcVorbisFile != null) { streamed = true; dataLength = (samples = getFloat(buffer.__srcVorbisFile.pcmTotal())) * buffer.channels * (buffer.bitsPerSample >> 3); var constructor = buffer.bitsPerSample == 32 ? Int32 : buffer.bitsPerSample == 16 ? Int16 : Int8; - bufferSize = STREAM_BUFFER_SIZE * buffer.channels * (constructor == Int8 ? buffer.bitsPerSample >> 3 : 1); + bufferSize = STREAM_BUFFER_SAMPLES * buffer.channels * (constructor == Int8 ? buffer.bitsPerSample >> 3 : 1); buffers = AL.genBuffers(STREAM_MAX_BUFFERS); bufferDatas = [for (i in 0...STREAM_MAX_BUFFERS) new ArrayBufferView(bufferSize, constructor)]; bufferTimes = [for (i in 0...STREAM_MAX_BUFFERS) 0]; @@ -116,15 +125,7 @@ class NativeAudioSource { unusedBuffers = []; bufferSize *= constructor == Int8 ? 1 : buffer.bitsPerSample >> 3; } - else #end { - streamed = false; - samples = (dataLength = getFloat(Int64.make(0, buffer.data.length))) / buffer.channels / (buffer.bitsPerSample >> 3); - - if (buffer.__srcBuffer == null && (buffer.__srcBuffer = AL.createBuffer()) != null) - AL.bufferData(buffer.__srcBuffer, format, buffer.data, buffer.data.length, buffer.sampleRate); - - AL.sourcei(handle, AL.BUFFER, buffer.__srcBuffer); - } + #end if (dataLength == 0) { trace('NativeAudioSource Bug! dataLength is 0'); @@ -188,6 +189,7 @@ class NativeAudioSource { var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN, wordSize = parent.buffer.bitsPerSample >> 3; var size = dataLength - (streamTell() * parent.buffer.sampleRate * parent.buffer.channels * wordSize); var n:Int = size < bufferSize ? Math.floor(size) : bufferSize; + n -= n % (parent.buffer.channels * wordSize); var total = 0, result = 0, wasEOF = false; while (total < bufferSize) { @@ -203,8 +205,8 @@ class NativeAudioSource { var samples = getSamples(loopTime != null ? loopTime + parent.offset : parent.offset); streamSeek(samples); - if ((size = dataLength - (getFloat(samples) * parent.buffer.channels * wordSize)) < (n = bufferSize - total)) - n = Math.floor(size); + if ((size = dataLength - (getFloat(samples) * parent.buffer.channels * wordSize)) < (n = bufferSize - total)) n = Math.floor(size); + n -= n % (parent.buffer.channels * wordSize); } } else { @@ -216,7 +218,7 @@ class NativeAudioSource { if (total < bufferSize) data.buffer.fill(total, n, 0); if (result < 0) { - trace('NativeAudioSource Streaming Bug! reading result is $result'); + trace('NativeAudioSource Streaming Bug! reading result is $result, streamEnded: $streamEnded, total: $total, n: $n'); return result; } return total; @@ -283,8 +285,10 @@ class NativeAudioSource { } catch(e) trace('NativeAudioSource Bug! in streamRun $e'); - if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) AL.sourcePlay(handle); - if (streamEnded && timer == null) resetTimer((getLength() - getCurrentTime()) / getPitch()); + if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) { + AL.sourcePlay(handle); + resetTimer((getLength() - getCurrentTime()) / getPitch()); + } } // Timers @@ -309,10 +313,8 @@ class NativeAudioSource { } private function timer_onRun() { - stopTimer(); - var timeRemaining = (getLength() - getCurrentTime()) / getPitch(); - if (timeRemaining > 30 && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.PLAYING) { + if (timeRemaining > 100 && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.PLAYING && (!streamed || !streamEnded)) { resetTimer(timeRemaining); return; } @@ -322,14 +324,16 @@ class NativeAudioSource { return; } + var start = loopTime != null ? loopTime : 0; if (toLoop <= 0) { - var start = loopTime != null ? loopTime : 0; setLoops(--loops); if (start > 0 || AL.getSourcei(handle, AL.SOURCE_STATE) != AL.PLAYING) setCurrentTime(start); + else resetTimer((getLength() - start) / getPitch()); } else { if ((loops -= toLoop) < 0) loops = 0; toLoop = 0; + resetTimer((getLength() - start) / getPitch()); } parent.onLoop.dispatch(); @@ -337,11 +341,18 @@ class NativeAudioSource { // Get & Set Methods public function getCurrentTime():Float { - if (completed) return getLength(); - else if (disposed) return 0; + if (disposed) return 0; + else if (completed) return getLength(); var time = AL.getSourcef(handle, AL.SAMPLE_OFFSET) / parent.buffer.sampleRate; - if (streamed) time += bufferTimes[STREAM_MAX_BUFFERS - queuedBuffers]; + if (streamed) { + if (playing && streamEnded && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) { + complete(); + return getLength(); + } + else + time += bufferTimes[STREAM_MAX_BUFFERS - queuedBuffers]; + } time *= 1000; var length = if (length == null) getRealLength(); else length; @@ -361,22 +372,25 @@ class NativeAudioSource { if (playing) { var timeRemaining = (getLength() - value) / getPitch(); - if (completed = timeRemaining <= 0) complete(); + if (timeRemaining < 8) complete(); else { + completed = streamEnded = false; if (streamed) { AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); unusedBuffers.resize(0); streamSeek(getSamples(value + parent.offset)); - for (i in 0...(requestBuffers = queuedBuffers = STREAM_BUFFER_FREQUENCY)) - if (fillBuffer(buffers[i]) > 0) AL.sourceQueueBuffer(handle, buffers[i]); + requestBuffers = queuedBuffers = STREAM_BUFFER_FREQUENCY; + for (i in 0...queuedBuffers) { + if (!streamEnded && fillBuffer(buffers[i]) > 0) AL.sourceQueueBuffer(handle, buffers[i]); + else queuedBuffers = --requestBuffers; + } - resetStreamTimer(); + if (!streamEnded) resetStreamTimer(); } - else - resetTimer(timeRemaining); + resetTimer(timeRemaining); AL.sourcePlay(handle); } } @@ -398,9 +412,7 @@ class NativeAudioSource { else dataLength = Math.ffloor(Math.max(0, Math.min(value, getRealLength())) / 1000 * buffer.sampleRate) * buffer.channels * wordSize; if (playing) { - if (streamed) { - if (streamEnded || toLoop > 0) setCurrentTime(getCurrentTime()); - } + if (streamed && (streamEnded || toLoop > 0)) setCurrentTime(getCurrentTime()); else { var timeRemaining = ((getLength() - parent.offset) - getCurrentTime()) / getPitch(); if (timeRemaining > 0) resetTimer(timeRemaining); @@ -416,7 +428,7 @@ class NativeAudioSource { public function setPitch(value:Float):Float { if (disposed || value == AL.getSourcef(handle, AL.PITCH)) return value; - if (playing && (streamEnded || !streamed)) { + if (playing) { var timeRemaining = (getLength() - getCurrentTime()) / value; if (timeRemaining > 0) resetTimer(timeRemaining); } diff --git a/source/openfl/media/Sound.hx b/source/openfl/media/Sound.hx index 1b4a8d5e..808399c3 100644 --- a/source/openfl/media/Sound.hx +++ b/source/openfl/media/Sound.hx @@ -672,26 +672,10 @@ class Sound extends EventDispatcher #if (js && html5 && howlerjs) return __buffer.src.duration() * 1000; #else - if (__buffer.data != null) - { - // var samples = (__buffer.data.length * 8) / (__buffer.channels * __buffer.bitsPerSample); - // return Std.int(samples / __buffer.sampleRate * 1000); - var samples = (Int64.make(0, __buffer.data.length) * Int64.ofInt(8)) / Int64.ofInt(__buffer.channels * __buffer.bitsPerSample); - var div = Int64.divMod(samples * Int64.ofInt(1000), Int64.ofInt(__buffer.sampleRate)); - var value = (div.quotient + (div.modulus / __buffer.sampleRate)); - return Int64.toInt(value); - } - else if (__buffer.__srcVorbisFile != null) - { - //var samples = Int64.toInt(__buffer.__srcVorbisFile.pcmTotal()); - //return Std.int(samples / __buffer.sampleRate * 1000); - var samples = __buffer.__srcVorbisFile.pcmTotal(); - var value = Int64.fromFloat(__buffer.__srcVorbisFile.timeTotal()) * 1000; - return Int64.toInt(value); - } - else - { - return 0; + if (__buffer.data != null) return (__buffer.data.length >> 0) / __buffer.channels / (__buffer.bitsPerSample >> 3) / __buffer.sampleRate * 1000; + else if (__buffer.__srcVorbisFile != null) { + var x = __buffer.__srcVorbisFile.pcmTotal(); + return (x.high * 4294967296. + (x.low >> 0)) / __buffer.sampleRate * 1000; } #end }