From 80bc6506b91b8be9e3dd393594d1765e2a795a78 Mon Sep 17 00:00:00 2001 From: Ralty <78720179+Raltyro@users.noreply.github.com> Date: Fri, 11 Jul 2025 10:59:26 +0700 Subject: [PATCH] Rewrite Internal Audio Source & Fix AudioSwitchFix --- source/flixel/sound/FlxSound.hx | 10 +- .../backend/system/modules/AudioSwitchFix.hx | 72 +-- .../backend/native/NativeAudioSource.hx | 538 ++++++++---------- source/lime/media/AudioSource.hx | 4 + source/openfl/media/Sound.hx | 18 - source/openfl/media/SoundChannel.hx | 4 +- 6 files changed, 277 insertions(+), 369 deletions(-) diff --git a/source/flixel/sound/FlxSound.hx b/source/flixel/sound/FlxSound.hx index 4884a926..5109ecfa 100644 --- a/source/flixel/sound/FlxSound.hx +++ b/source/flixel/sound/FlxSound.hx @@ -2,6 +2,7 @@ package flixel.sound; import lime.media.AudioBuffer; import lime.media.AudioSource; +import lime.media.AudioManager; import lime.media.openal.AL; #if lime_vorbis import lime.media.vorbis.VorbisFile; @@ -695,11 +696,6 @@ class FlxSound extends FlxBasic { pitch = _pitch; #end - // TODO: fix the buffer loud beep for streaming sounds... - #if lime_openal - _source.__backend.setGain(0); - #end - _channel.soundTransform = _transform; _channel.__lastPeakTime = 0; _channel.__leftPeak = 0; @@ -716,7 +712,7 @@ class FlxSound extends FlxBasic { try {s = AL.getSourcei(_source.__backend.handle, AL.SOURCE_STATE) == AL.PLAYING;} catch(e) {} if (s) updateTransform(); else { - if (++_tries > 10) return; + if (++_tries > 2) return; makeChannel(); return startSound(StartTime); } @@ -889,7 +885,7 @@ class FlxSound extends FlxBasic { return _time; } function get_time():Float { - if (_channel == null) return _time; + if (_channel == null || AudioManager.context == null) return _time; final pos = _channel.position - _offset; if (!playing || _realPitch <= 0) { diff --git a/source/funkin/backend/system/modules/AudioSwitchFix.hx b/source/funkin/backend/system/modules/AudioSwitchFix.hx index 941846e0..382fcc0c 100644 --- a/source/funkin/backend/system/modules/AudioSwitchFix.hx +++ b/source/funkin/backend/system/modules/AudioSwitchFix.hx @@ -4,6 +4,8 @@ import flixel.FlxState; import flixel.sound.FlxSound; import funkin.backend.utils.NativeAPI; import lime.media.AudioManager; +import lime.media.AudioSource; +import haxe.Timer; /** * if you are stealing this keep this comment at least please lol @@ -12,58 +14,40 @@ import lime.media.AudioManager; */ @:dox(hide) class AudioSwitchFix { - @:noCompletion - private static function onStateSwitch(state:FlxState):Void { - #if windows - if (Main.audioDisconnected) { + public static function onAudioDisconnected() @:privateAccess { + var soundList:Array = [FlxG.sound.music]; + for (sound in FlxG.sound.list) if (sound.playing) soundList.push(sound); + for (source in AudioSource.activeSources) source.dispose(); - var playingList:Array = []; - for(e in FlxG.sound.list) { - if (e.playing) { - playingList.push({ - sound: e, - time: e.time - }); - e.stop(); - } - } - if (FlxG.sound.music != null) - FlxG.sound.music.stop(); + AudioManager.shutdown(); - AudioManager.shutdown(); - AudioManager.init(); - // #if !lime_doc_gen - // if (AudioManager.context.type == OPENAL) - // { - // var alc = AudioManager.context.openal; + AudioManager.init(); + Main.changeID++; + // #if !lime_doc_gen + // if (AudioManager.context.type == OPENAL) + // { + // var alc = AudioManager.context.openal; - // var device = alc.openDevice(); - // var ctx = alc.createContext(device); - // alc.makeContextCurrent(ctx); - // alc.processContext(ctx); - // } - // #end - Main.changeID++; + // var device = alc.openDevice(); + // var ctx = alc.createContext(device); + // alc.makeContextCurrent(ctx); + // alc.processContext(ctx); + // } + // #end - for(e in playingList) { - e.sound.play(e.time); - } + for (sound in soundList) { + sound._paused = true; + sound.resume(); + } - Main.audioDisconnected = false; - } - #end + Main.audioDisconnected = false; } + private static var timer:Timer; + + private static function onRun() if (Main.audioDisconnected) onAudioDisconnected(); public static function init() { - #if windows NativeAPI.registerAudio(); - FlxG.signals.preStateCreate.add(onStateSwitch); - #end + if (timer == null) (timer = new Timer(1000)).run = onRun; } -} - -@:dox(hide) -typedef PlayingSound = { - var sound:FlxSound; - var time:Float; } \ No newline at end of file diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx index dcd84632..00a96cee 100644 --- a/source/lime/_internal/backend/native/NativeAudioSource.hx +++ b/source/lime/_internal/backend/native/NativeAudioSource.hx @@ -1,3 +1,7 @@ +// ALL REWRITTEN FROM SCRATCH!!!! -raltyro +// YES ALL OF IT!!! +// FUCK + package lime._internal.backend.native; import haxe.Timer; @@ -6,7 +10,11 @@ import haxe.Int64; import lime.media.openal.AL; import lime.media.openal.ALBuffer; import lime.media.openal.ALSource; + +#if lime_vorbis +import lime.media.vorbis.Vorbis; import lime.media.vorbis.VorbisFile; +#end import lime.math.Vector4; import lime.media.AudioBuffer; @@ -19,99 +27,74 @@ import lime.utils.ArrayBufferView; #end @:access(lime.media.AudioBuffer) @:access(lime.utils.ArrayBufferView) -@:access(lime.media.vorbis.VorbisFile) class NativeAudioSource { - public static var initBuffers:Array = []; - - private static final STREAM_BUFFER_SIZE:Int = 0x8000; - private static final STREAM_NUM_BUFFERS:Int = 5; + private static final STREAM_BUFFER_SIZE:Int = 0x4000; + private static final STREAM_MAX_BUFFERS:Int = 16; private static final STREAM_TIMER_FREQUENCY:Int = 100; - //#if (lime >= "8.3.0") - //private static var hasALSoftLatencyExt:Null; - //#end - - private var buffers:Array; - private var bufferDatas:Array; - private var bufferTimeBlocks:Array; - private var bufferLoops:Int; - private var queuedBuffers:Int; - private var requestBuffers:Int; - - private var length:Null; - private var loopTime:Null; + private var parent:AudioSource; + private var disposed:Bool; + private var streamed:Bool; private var playing:Bool; - private var loops:Int; - private var position:Vector4; + private var completed:Bool; - private var bitsPerSample:Int; + private var handle:ALSource; + private var buffers:Array; + private var timer:Timer; private var format:Int; private var dataLength:Int64; private var samples:Int64; - private var completed:Bool; - private var stream:Bool; - private var handle:ALSource; - private var parent:AudioSource; - private var timer:Timer; private var streamTimer:Timer; - private var disposed:Bool; - private var safeEnd:Bool; + private var bufferDatas:Array; + private var bufferTimes:Array; + private var requestBuffers:Int; + private var queuedBuffers:Int; + private var toLoop:Int; + private var streamEnded:Bool; - public function new(parent:AudioSource) { - this.parent = parent; - position = new Vector4(); - } + private var position:Vector4 = new Vector4(); + private var length:Null; + private var loopTime:Null; + private var loops:Int; - public function dispose():Void { + public function new(parent:AudioSource) this.parent = parent; + + public function dispose() { disposed = true; stop(); if (handle != null) { - AL.sourcei(handle, AL.BUFFER, null); + AL.sourcei(handle, AL.BUFFER, AL.NONE); AL.deleteSource(handle); } handle = null; if (buffers != null) AL.deleteBuffers(buffers); buffers = null; - - initBuffers.remove(this); } - public function init():Void { - //#if (lime >= "8.3.0") - //if (hasALSoftLatencyExt == null) hasALSoftLatencyExt = AL.isExtensionPresent("AL_SOFT_source_latency"); - //#end - + public function init() { if (handle != null) return; var buffer = parent.buffer; + if (buffer.channels == 1) format = buffer.bitsPerSample == 8 ? AL.FORMAT_MONO8 : AL.FORMAT_MONO16; + else format = buffer.bitsPerSample == 8 ? AL.FORMAT_STEREO8 : AL.FORMAT_STEREO16; + if (disposed = (handle = AL.createSource()) == null) return; - AL.sourcef(handle, AL.MAX_GAIN, 10); - bufferLoops = 0; - - bitsPerSample = buffer.bitsPerSample; - if (buffer.channels > 1) format = bitsPerSample == 8 ? AL.FORMAT_STEREO8 : AL.FORMAT_STEREO16; - else format = bitsPerSample == 8 ? AL.FORMAT_MONO8 : AL.FORMAT_MONO16; var vorbisFile = buffer.__srcVorbisFile; - if (stream = vorbisFile != null) { - dataLength = (samples = vorbisFile.pcmTotal()) * buffer.channels * (Int64.ofInt(bitsPerSample) / 8); - buffers = []; - bufferDatas = []; - bufferTimeBlocks = []; + if (streamed = vorbisFile != null) { + dataLength = (samples = vorbisFile.pcmTotal()) * buffer.channels * (Int64.ofInt(buffer.bitsPerSample) / 8); - var constructor = bitsPerSample == 8 ? Int8 : Int16; - for (i in 0...STREAM_NUM_BUFFERS) { - buffers.push(AL.createBuffer()); - bufferDatas.push(new ArrayBufferView(STREAM_BUFFER_SIZE, constructor)); - bufferTimeBlocks.push(0); - } + var constructor = buffer.bitsPerSample == 8 ? Int8 : Int16; + buffers = AL.genBuffers(STREAM_MAX_BUFFERS); + bufferDatas = [for (i in 0...STREAM_MAX_BUFFERS) new ArrayBufferView(STREAM_BUFFER_SIZE, constructor)]; + bufferTimes = [for (i in 0...STREAM_MAX_BUFFERS) 0]; } else { - samples = ((dataLength = buffer.data.length) * 8) / (buffer.channels * bitsPerSample); + samples = ((dataLength = buffer.data.length) * 8) / (buffer.channels * buffer.bitsPerSample); if (buffer.__srcBuffer == null && (buffer.__srcBuffer = AL.createBuffer()) != null) AL.bufferData(buffer.__srcBuffer, format, buffer.data, buffer.data.length, buffer.sampleRate); @@ -119,15 +102,13 @@ class NativeAudioSource { AL.sourcei(handle, AL.BUFFER, buffer.__srcBuffer); } - initBuffers.push(this); - if (dataLength == 0) { trace('NativeAudioSource Bug! dataLength is 0'); dispose(); } } - public function play():Void { + public function play() { if (playing || disposed) return; playing = true; @@ -135,256 +116,35 @@ class NativeAudioSource { else setCurrentTime(getCurrentTime()); } - public function pause():Void { - if (!(disposed = handle == null)) AL.sourcePause(handle); + public function pause() { + if (!disposed) AL.sourcePause(handle); playing = false; stopStreamTimer(); stopTimer(); } - public function stop():Void { - if (!(disposed = handle == null)) { + public function stop() { + if (!disposed) { if (AL.getSourcei(handle, AL.SOURCE_STATE) != AL.STOPPED) AL.sourceStop(handle); - if (stream) AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED) + AL.getSourcei(handle, AL.BUFFERS_PROCESSED)); + if (streamed) AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); + requestBuffers = queuedBuffers = toLoop = 0; } - requestBuffers = queuedBuffers = bufferLoops = 0; playing = false; stopStreamTimer(); stopTimer(); } - private function complete():Void { - stop(); - + private function complete() { completed = true; + stop(); parent.onComplete.dispatch(); } - private function readVorbisFileBuffer(vorbisFile:VorbisFile, max:Int):ArrayBufferView { - #if lime_vorbis - var id = STREAM_NUM_BUFFERS - requestBuffers, read = STREAM_NUM_BUFFERS - 1, total = 0, readMax; - var buffer = bufferDatas[id]; - - for (i in id...read) { - bufferTimeBlocks[i] = bufferTimeBlocks[i + 1]; - bufferDatas[i] = bufferDatas[i + 1]; - } - bufferTimeBlocks[read] = vorbisFile.timeTell(); - bufferDatas[read] = buffer; - queuedBuffers = requestBuffers; - - while (total < STREAM_BUFFER_SIZE) { - if ((readMax = 4096) > (read = max - total)) readMax = read; - if (vorbisFile.handle == null) break; - if (readMax > 0 && (read = vorbisFile.read(buffer.buffer, total, readMax)) > 0) total += read; - else if (safeEnd = (loops > bufferLoops)) { - if (readMax == 4096) continue; - bufferLoops++; vorbisFile.timeSeek((loopTime != null ? Math.max(0, loopTime / 1000) : 0) + parent.offset / 1000); - if ((max = (dataLength - (vorbisFile.pcmTell() * (Int64.ofInt(bitsPerSample) / 8) * parent.buffer.channels)).low) > STREAM_BUFFER_SIZE) - max = STREAM_BUFFER_SIZE; - } - else { - buffer.buffer.fill(total, STREAM_BUFFER_SIZE - total - 1, 0); - resetTimer((getLength() - getCurrentTime()) / getPitch()); - break; - } - } - - return buffer; - #else - return null; - #end - } - - private function fillBuffers(buffers:Array):Void { - #if lime_vorbis - if (parent == null || parent.buffer == null) return dispose(); - if (handle == null || buffers.length < 1) return; - - final buffer = parent.buffer, vorbisFile = buffer.__srcVorbisFile; - final actualDataRate = (Int64.ofInt(bitsPerSample) / 8) * buffer.channels; - var position = vorbisFile.pcmTell() * actualDataRate, length = getLengthSamples() * actualDataRate, trackLoops = bufferLoops; - if (position >= length && safeEnd) return; - - var sampleRate = buffer.sampleRate, numBuffers = 0, data, size:Int64; - for (buffer in buffers) { - if ((size = length - position) > STREAM_BUFFER_SIZE) size = Int64.ofInt(STREAM_BUFFER_SIZE); - data = readVorbisFileBuffer(vorbisFile, Int64.toInt(size)); - - if (disposed) return; - AL.bufferData(buffer, format, data, STREAM_BUFFER_SIZE, sampleRate); - numBuffers++; - - if (safeEnd) break; - else if (bufferLoops != trackLoops) { - position = vorbisFile.pcmTell() * actualDataRate; - trackLoops = bufferLoops; - } - } - AL.sourceQueueBuffers(handle, numBuffers, buffers); - - if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) { - if (bufferLoops > 0) { // TODO: remove this once buffer loop stutters fixed - setCurrentTime(getCurrentTime()); - return; - } - AL.sourcePlay(handle); - resetTimer(Std.int((getLength() - getCurrentTime()) / getPitch())); - } - #end - } - - private function streamRun():Void { - #if lime_vorbis - if (disposed = (handle == null || buffers == null)) return dispose(); - - var vorbisFile = parent.buffer.__srcVorbisFile; - if (vorbisFile == null) return dispose(); - - try { - var processed = AL.getSourcei(handle, AL.BUFFERS_PROCESSED); - if (processed > 0 && playing) { - fillBuffers(AL.sourceUnqueueBuffers(handle, processed)); - if ((!safeEnd || loops > 0) && queuedBuffers < STREAM_NUM_BUFFERS && playing) { - /*if (requestBuffers >= STREAM_NUM_BUFFERS) { - AL.sourceUnqueueBuffers(handle, queuedBuffers); - setCurrentTime((bufferTimeBlocks[STREAM_NUM_BUFFERS - queuedBuffers] + - (STREAM_BUFFER_SIZE / ((bitsPerSample / 8) * parent.buffer.channels) / parent.buffer.sampleRate) - ) * 1000 - parent.offset); - } - else*/fillBuffers([buffers[++requestBuffers - 1]]); - } - } - } - catch(e) trace(e); - #end - } - - // Timers - inline function stopStreamTimer():Void if (streamTimer != null) streamTimer.stop(); - - private function resetStreamTimer():Void { - stopStreamTimer(); - - #if lime_vorbis - streamTimer = new Timer(STREAM_TIMER_FREQUENCY); - streamTimer.run = streamRun; - #end - } - - inline function stopTimer():Void if (timer != null) timer.stop(); - - private function resetTimer(timeRemaining:Float):Void { - stopTimer(); - - /*if (timeRemaining <= 26) { - timer_onRun(); - return; - }*/ - timer = new Timer(timeRemaining); - timer.run = timer_onRun; - } - - private function timer_onRun():Void { - if (!safeEnd && bufferLoops <= 0) { - #if lime_vorbis - var ranOut = false; - if (stream) { - var vorbisFile = parent.buffer.__srcVorbisFile; - if (vorbisFile == null) return dispose(); - ranOut = vorbisFile.pcmTell() >= getLengthSamples() || queuedBuffers < 3; - } - - if (!ranOut) - #end - { - var timeRemaining = (getLength() - getCurrentTime()) / getPitch(); - if (timeRemaining > 100 && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.PLAYING) { - resetTimer(timeRemaining); - return; - } - } - } - safeEnd = false; - - if (loops <= 0) { - complete(); - return; - } - - if (bufferLoops <= 0) { - loops--; - var loopTime = loopTime ?? 0; - if (stream || loopTime > 0) setCurrentTime(loopTime); - else if (!stream) { - setLoops(loops); - if (AL.getSourcei(handle, AL.SOURCE_STATE) != AL.PLAYING) - setCurrentTime(0); - } - } - else { - loops -= bufferLoops; - bufferLoops = 0; - } - parent.onLoop.dispatch(); - } - - // Get & Set Methods - public function getCurrentTime():Float { - if (completed) return getLength(); - else if (!disposed) { - var time = AL.getSourcef(handle, AL.SEC_OFFSET); - if (stream) time += bufferTimeBlocks[STREAM_NUM_BUFFERS - queuedBuffers]; - time = time * 1000 - parent.offset; - if (loops > 0 && time > getLength()) { - var start = loopTime != null ? Math.max(0, loopTime + parent.offset) : parent.offset; - return ((time - start) % (getLength() - start)) + start; - } - if (time > 0) return time; - } - return 0; - } - - public function setCurrentTime(value:Float):Float { - if (disposed = (handle == null)) return value; - - var total = getRealLength(); - var time = Math.max(0, Math.min(total, value + parent.offset)), ratio = time / total; - - if (stream) { - // TODO: smooth setCurrentTime for stream (dont refill buffers again) - - AL.sourceStop(handle); - AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); - - #if lime_vorbis - var vorbisFile = parent.buffer.__srcVorbisFile; - if (vorbisFile != null) { - // var chunk = Std.int(Math.floor(getFloat(samples) * ratio / STREAM_BUFFER_SIZE) * STREAM_BUFFER_SIZE); - vorbisFile.pcmSeek(Int64.fromFloat(getFloat(samples) * ratio)); - fillBuffers(buffers.slice(0, requestBuffers = queuedBuffers = 3)); - // AL.sourcei(handle, AL.SAMPLE_OFFSET, Std.int((samples * ratio) - chunk)); - if (playing) resetStreamTimer(); - } - #end - } - else { - AL.sourceRewind(handle); - AL.sourcei(handle, AL.BYTE_OFFSET, Int64.fromFloat(getFloat(dataLength) * ratio)); - } - - if (playing) { - var timeRemaining = (getLength() - time) / getPitch(); - if (completed = timeRemaining <= 0) complete(); - else { - AL.sourcePlay(handle); - resetTimer(timeRemaining); - } - } - - return value; + inline private function getSamples(ms:Float):Int64 { + var v = Int64.fromFloat(ms / 1000 * parent.buffer.sampleRate); + return v > samples ? samples : (v < 0 ? 0 : v); } inline private function getLengthSamples():Int64 { @@ -396,6 +156,186 @@ class NativeAudioSource { inline private function getRealLength():Float return getFloat(samples) / parent.buffer.sampleRate * 1000; + // just incase if we support more than 1 in the future?? which i doubt + /* + private function streamTell():Float { + #if lime_vorbis + if (parent.buffer.__srcVorbisFile != null) return parent.buffer.__srcVorbisFile.timeTell(); + #end + return 0; + } + */ + private function streamTell():Float return #if lime_vorbis parent.buffer.__srcVorbisFile.timeTell() #else 0 #end; + + private function streamSeek(samples:Int64) { + #if lime_vorbis + //if (parent.buffer.__srcVorbisFile != null) { + // There's apparently a bug in libvorbis <= 1.3.4, hhttps://github.com/xiph/vorbis/blob/master/CHANGES#L39 + if (samples <= 2) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples); + // break; + //} + #end + } + + // Streaming, atleast for vorbis for now. + private function readToBufferData(data:ArrayBufferView):Int { + var total = 0, result = 0, wordSize:Int = parent.buffer.bitsPerSample == 8 ? 1 : 2; + + var vorbisFile = parent.buffer.__srcVorbisFile; + while (total < STREAM_BUFFER_SIZE) { + result = vorbisFile.read(data.buffer, total, STREAM_BUFFER_SIZE - total, false, wordSize, true); + + if (result == Vorbis.HOLE) continue; + else if (result == Vorbis.EREAD) break; + else if (result == 0) { + if (!(streamEnded = (loops <= toLoop))) { + toLoop++; + streamSeek(getSamples(loopTime != null ? loopTime : 0)); + } + break; + } + else total += result; + } + if (total < STREAM_BUFFER_SIZE) data.buffer.fill(total, STREAM_BUFFER_SIZE - total - 1, 0); + + if (result < 0) return result; + return total; + } + + private function fillBuffer(buffer:ALBuffer):Int { + var i = STREAM_MAX_BUFFERS - requestBuffers; + var bufferData = bufferDatas[i], bufferTime = streamTell(); + var decoded = readToBufferData(bufferData); + + if (decoded <= 0) return 0; + AL.bufferData(buffer, format, bufferData, decoded, parent.buffer.sampleRate); + + var n = STREAM_MAX_BUFFERS - 1; + for (x in i...n) { + bufferDatas[x] = bufferDatas[x + 1]; + bufferTimes[x] = bufferTimes[x + 1]; + } + queuedBuffers = requestBuffers; + bufferDatas[n] = bufferData; + bufferTimes[n] = bufferTime; + + return decoded; + } + + private function fillBuffers(buffers:Array) { + for (buffer in buffers) { + if (fillBuffer(buffer) > 0) AL.sourceQueueBuffer(handle, buffer); + if (streamEnded) break; + } + + if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) { + AL.sourcePlay(handle); + resetTimer((getLength() - getCurrentTime()) / getPitch()); + } + } + + private function streamRun() { + #if lime_vorbis + if (parent == null || parent.buffer == null || handle == null || buffers == null || parent.buffer.__srcVorbisFile == null) return dispose(); + if (!playing || streamEnded) stopStreamTimer(); + + try { + var processed = AL.getSourcei(handle, AL.BUFFERS_PROCESSED); + if (processed > 0) { + fillBuffers(AL.sourceUnqueueBuffers(handle, processed)); + if (queuedBuffers < STREAM_MAX_BUFFERS) fillBuffers([buffers[++requestBuffers - 1]]); + } + } + catch(e) trace(e); + #end + } + + // Timers + inline function stopStreamTimer() if (streamTimer != null) streamTimer.stop(); + + private function resetStreamTimer() { + stopStreamTimer(); + + if (streamed) { + streamTimer = new Timer(STREAM_TIMER_FREQUENCY); + streamTimer.run = streamRun; + } + } + + inline function stopTimer() if (timer != null) timer.stop(); + + private function resetTimer(timeRemaining:Float) { + stopTimer(); + timer = new Timer(timeRemaining); + timer.run = timer_onRun; + } + + private function timer_onRun() { + if (!streamed || !streamEnded) { + var timeRemaining = (getLength() - getCurrentTime()) / getPitch(); + if (timeRemaining > 30 && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.PLAYING) { + resetTimer(timeRemaining); + return; + } + } + + if (loops == 0) { + complete(); + return; + } + + 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 if ((loops -= toLoop) < 0) loops = 0; + toLoop = 0; + + parent.onLoop.dispatch(); + } + + // Get & Set Methods + public function getCurrentTime():Float { + if (completed) return getLength(); + else if (!disposed) { + var time = ((streamed ? bufferTimes[STREAM_MAX_BUFFERS - queuedBuffers] : 0) + AL.getSourcef(handle, AL.SEC_OFFSET)) * 1000 - parent.offset; + if (loops > 0 && time > getLength()) { + var start = (loopTime != null ? Math.max(0, loopTime) : 0) + parent.offset; + return ((time - start) % (getLength() - start)) + start; + } + else if (time > 0) return time; + } + return 0; + } + + public function setCurrentTime(value:Float):Float { + if (disposed) return value; + + if (streamed) { + AL.sourceStop(handle); + AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); + + streamSeek(getSamples(value)); + fillBuffers(buffers.slice(0, requestBuffers = queuedBuffers = 3)); + } + else + AL.sourcei(handle, AL.SAMPLE_OFFSET, getSamples(value)); + + if (playing) { + var timeRemaining = (getLength() - value) / AL.getSourcef(handle, AL.PITCH); + if (timeRemaining > 0) { + if (AL.getSourcei(handle, AL.SOURCE_STATE) != AL.PLAYING) AL.sourcePlay(handle); + if (streamed) resetStreamTimer(); + resetTimer(timeRemaining); + } + else + complete(); + } + + return value; + } + public function getLength():Null { return if (length == null) getRealLength() - parent.offset; else length - parent.offset; @@ -440,20 +380,22 @@ class NativeAudioSource { inline public function getLoops():Int return loops; inline public function setLoops(value:Int):Int { - if (!stream && !disposed) AL.sourcei(handle, AL.LOOPING, (loopTime <= 0 && value > 0) ? AL.TRUE : AL.FALSE); + if (!streamed && !disposed) AL.sourcei(handle, AL.LOOPING, (loopTime <= 0 && value > 0) ? AL.TRUE : AL.FALSE); + else if (loops == 0 && value > 0) setCurrentTime(getCurrentTime()); + if (value < 0) return loops = 0; return loops = value; } inline public function getLoopTime():Float return loopTime; inline public function setLoopTime(value:Float):Float { - if (!stream && !disposed) AL.sourcei(handle, AL.LOOPING, (value <= 0 && loops > 0) ? AL.TRUE : AL.FALSE); + if (!streamed && !disposed) AL.sourcei(handle, AL.LOOPING, (value <= 0 && loops > 0) ? AL.TRUE : AL.FALSE); return loopTime = value; } public function getLatency():Float { /*#if (lime >= "8.3.0") - if (hasALSoftLatencyExt) { + if (AL.isExtensionPresent("AL_SOFT_source_latency")) { final offsets = AL.getSourcedvSOFT(handle, AL.SEC_OFFSET_LATENCY_SOFT, 2); if (offsets != null) return offsets[1] * 1000; } @@ -475,4 +417,4 @@ class NativeAudioSource { } return position; } -} \ No newline at end of file +} diff --git a/source/lime/media/AudioSource.hx b/source/lime/media/AudioSource.hx index 5ee8251a..87f9e327 100644 --- a/source/lime/media/AudioSource.hx +++ b/source/lime/media/AudioSource.hx @@ -18,6 +18,8 @@ import lime.math.Vector4; **/ class AudioSource { + private static var activeSources:Array = []; + /** An event that is dispatched when the audio playback is complete. **/ @@ -118,11 +120,13 @@ class AudioSource inline public function dispose():Void { __backend.dispose(); + activeSources.remove(this); } @:noCompletion inline private function init():Void { __backend.init(); + activeSources.push(this); } /** diff --git a/source/openfl/media/Sound.hx b/source/openfl/media/Sound.hx index cbccc35b..1b4a8d5e 100644 --- a/source/openfl/media/Sound.hx +++ b/source/openfl/media/Sound.hx @@ -599,17 +599,6 @@ class Sound extends EventDispatcher #end } - var changeID:Int = 0; - public function regen() { - var audioBuffer = new AudioBuffer(); - audioBuffer.bitsPerSample = __buffer.bitsPerSample; - audioBuffer.channels = __buffer.channels; - audioBuffer.data = __buffer.data; - audioBuffer.sampleRate = __buffer.sampleRate; - - __buffer = audioBuffer; - } - /** Generates a new SoundChannel object to play back the sound. This method returns a SoundChannel object, which you access to stop the sound and to @@ -636,13 +625,6 @@ class Sound extends EventDispatcher return null; } - #if !macro - if (changeID < funkin.backend.system.Main.changeID) { - changeID = funkin.backend.system.Main.changeID; - regen(); - } - #end - if (sndTransform == null) { sndTransform = new SoundTransform(); diff --git a/source/openfl/media/SoundChannel.hx b/source/openfl/media/SoundChannel.hx index 469759e9..7ba37349 100644 --- a/source/openfl/media/SoundChannel.hx +++ b/source/openfl/media/SoundChannel.hx @@ -169,7 +169,7 @@ import lime.media.AudioSource; if (__source.buffer == null || !__checkUpdatePeaks(position)) return; var index = AL.getSourcei(__source.__backend.handle, AL.SAMPLE_OFFSET), bufferDatas = __source.__backend.bufferDatas; - var bufferi = NativeAudioSource.STREAM_NUM_BUFFERS - __source.__backend.queuedBuffers, bytes = bufferDatas[bufferi].buffer; + var bufferi = NativeAudioSource.STREAM_MAX_BUFFERS - __source.__backend.queuedBuffers, bytes = bufferDatas[bufferi].buffer; var channels = __source.buffer.channels, todo = scanSamples, sample; var lfilled = false, rfilled = channels < 2; @@ -204,7 +204,7 @@ import lime.media.AudioSource; #if (lime_cffi && !macro) if (!__isValid) return; - #if lime_vorbis if (__source.__backend.stream) return __updateVorbisPeaks(); #end + #if lime_vorbis if (__source.__backend.streamed) return __updateVorbisPeaks(); #end var buffer = __source.buffer; if (buffer == null || buffer.data == null || !__checkUpdatePeaks(position)) return;