diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 36cc38f1..3c6d7c68 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -616,22 +616,17 @@ class Charter extends UIState { updateWaveforms(); } - inline function isSoundLoaded(sound:FlxSound) { - @:privateAccess - return sound != null && sound._sound != null && sound._sound.length > 0; - } - public function updateWaveforms() { var wavesToGenerate:Array<{name:String, sound:FlxSound}> = []; - if(isSoundLoaded(FlxG.sound.music)) + if (FlxG.sound.music.loaded) wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music}); - if (PlayState.SONG.meta.needsVoices != false && isSoundLoaded(vocals)) + if (PlayState.SONG.meta.needsVoices != false && vocals.loaded) wavesToGenerate.push({name: "Voices.ogg", sound: vocals}); for (strumLine in strumLines) - if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && isSoundLoaded(strumLine.vocals)) + if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && strumLine.vocals.loaded) wavesToGenerate.push({ name: 'Voices${strumLine.strumLine.vocalsSuffix}.ogg', sound: strumLine.vocals @@ -1575,12 +1570,10 @@ class Charter extends UIState { vocals.pause(); for (strumLine in strumLines.members) strumLine.vocals.pause(); } else { - FlxG.sound.music.play(); - vocals.play(); - vocals.time = FlxG.sound.music.time = Conductor.songPosition + Conductor.songOffset * 2; + FlxG.sound.music.play(Conductor.songPosition + Conductor.songOffset); + vocals.play(FlxG.sound.music.getActualTime()); for (strumLine in strumLines.members) { - strumLine.vocals.play(); - strumLine.vocals.time = vocals.time; + strumLine.vocals.play(FlxG.sound.music.getActualTime()); } } } diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 71423236..b8483189 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -40,6 +40,7 @@ class Options public static var songOffset:Float = 0; public static var framerate:Int = 120; public static var gpuOnlyBitmaps:Bool = #if (mac || web) false #else true #end; // causes issues on mac and web + public static var streamedMusic:Bool = true; public static var lastLoadedMod:String = null; diff --git a/source/funkin/options/categories/AppearanceOptions.hx b/source/funkin/options/categories/AppearanceOptions.hx index 9b97e299..272074ad 100644 --- a/source/funkin/options/categories/AppearanceOptions.hx +++ b/source/funkin/options/categories/AppearanceOptions.hx @@ -43,6 +43,10 @@ class AppearanceOptions extends OptionsScreen { "gpuOnlyBitmaps")); } #end + add(new Checkbox( + "Streamed Music", + "If checked, only musics will have streamed audio, ALSO freeing a LOT of memory with the downside of higher cpu usage if more audio are being streamed at once (EXPERIMENTAL). Turning this off will consume a lot of memory, especially on longer songs. If you aren't sure, leave this on.", + "streamedMusic")); add(new Checkbox( "Auto Pause", "If checked, switching windows will pause the game.", diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx index 25a34283..ce2d6782 100644 --- a/source/lime/_internal/backend/native/NativeAudioSource.hx +++ b/source/lime/_internal/backend/native/NativeAudioSource.hx @@ -16,6 +16,7 @@ import lime.media.vorbis.VorbisFile; import lime.math.Vector4; import lime.media.AudioBuffer; import lime.media.AudioSource; +import lime.system.System; import lime.utils.ArrayBufferView; #if !lime_debug @@ -41,7 +42,6 @@ class NativeAudioSource { private var unusedBuffers:Array; private var timer:Timer; private var format:Int; - private var dataLength:Float; private var samples:Float; private var streamTimer:Timer; @@ -51,6 +51,7 @@ class NativeAudioSource { private var queuedBuffers:Int; private var toLoop:Int; private var streamEnded:Bool; + private var dataLength:Float; private var position:Vector4 = new Vector4(); private var length:Null; @@ -130,7 +131,10 @@ class NativeAudioSource { public function stop() { if (!disposed) { if (AL.getSourcei(handle, AL.SOURCE_STATE) != AL.STOPPED) AL.sourceStop(handle); - if (streamed) AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); + if (streamed) { + AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED)); + unusedBuffers.resize(0); + } requestBuffers = queuedBuffers = toLoop = 0; } @@ -166,19 +170,21 @@ class NativeAudioSource { } private function readToBufferData(data:ArrayBufferView):Int { - var total = 0, result = 0, wordSize = parent.buffer.bitsPerSample == 8 ? 1 : 2; + var total = 0, result = 0, wordSize = parent.buffer.bitsPerSample == 8 ? 1 : 2, wasEOF = false; var size = dataLength - (streamTell() * parent.buffer.sampleRate * parent.buffer.channels * wordSize); - var n = size < STREAM_BUFFER_SIZE ? Math.floor(size) : STREAM_BUFFER_SIZE; + var n:Int = size < STREAM_BUFFER_SIZE ? Math.floor(size) : STREAM_BUFFER_SIZE; var vorbisFile = parent.buffer.__srcVorbisFile; while (total < STREAM_BUFFER_SIZE) { - result = n > 0 ? vorbisFile.read(data.buffer, total, n, false, wordSize, true) : 0; + result = n > 0 ? vorbisFile.read(data.buffer, total, n, System.endianness == lime.system.Endian.BIG_ENDIAN, wordSize, true) : 0; if (result == Vorbis.HOLE) continue; - else if (result == Vorbis.EREAD) break; + else if (result <= Vorbis.EREAD) break; else if (result == 0) { - if (streamEnded = loops <= toLoop++) break; + if (wasEOF || (streamEnded = loops <= toLoop++)) break; else { + wasEOF = true; + var samples = getSamples(loopTime != null ? loopTime : 0); streamSeek(samples); if ((size = dataLength - (getFloat(samples) * parent.buffer.channels * wordSize)) < (n = STREAM_BUFFER_SIZE - total)) @@ -192,7 +198,10 @@ class NativeAudioSource { } if (total < STREAM_BUFFER_SIZE) data.buffer.fill(total, n, 0); - if (result < 0) return result; + if (result < 0) { + trace('NativeAudioSource Streaming Bug! reading result is $result'); + return result; + } return total; } @@ -222,19 +231,22 @@ class NativeAudioSource { if (!playing || streamEnded || disposed) stopStreamTimer(); try { - var processed = AL.getSourcei(handle, AL.BUFFERS_PROCESSED), n = STREAM_BUFFER_FREQUENCY, buffer; - - while (processed-- > 0 && !streamEnded) { - buffer = AL.sourceUnqueueBuffer(handle); - if (n-- > 0 && fillBuffer(buffer) > 0) AL.sourceQueueBuffer(handle, buffer); + var processed = AL.getSourcei(handle, AL.BUFFERS_PROCESSED), n = STREAM_BUFFER_FREQUENCY * Math.min(1, AL.getSourcef(handle, AL.PITCH)), buffer; + while (processed-- > 0) { + if (streamEnded || --n < 0) unusedBuffers.push(AL.sourceUnqueueBuffer(handle)); + else if (fillBuffer(buffer = AL.sourceUnqueueBuffer(handle)) > 0) AL.sourceQueueBuffer(handle, buffer); else unusedBuffers.push(buffer); } if (!streamEnded) { if (queuedBuffers < STREAM_MAX_BUFFERS) { if (fillBuffer(buffer = buffers[requestBuffers++]) > 0) AL.sourceQueueBuffer(handle, buffer); + else requestBuffers--; + } + else if (unusedBuffers.length != 0) { + if (fillBuffer(buffer = unusedBuffers.pop()) > 0) AL.sourceQueueBuffer(handle, buffer); + else unusedBuffers.push(buffer); } - else if (unusedBuffers.length != 0 && fillBuffer(buffer = unusedBuffers.pop()) > 0) AL.sourceQueueBuffer(handle, buffer); } if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) { diff --git a/source/openfl/utils/Assets.hx b/source/openfl/utils/Assets.hx index db92e530..0909bdd4 100644 --- a/source/openfl/utils/Assets.hx +++ b/source/openfl/utils/Assets.hx @@ -243,16 +243,16 @@ class Assets public static function getMusic(id:String, useCache:Bool = true):Sound { - #if (lime_vorbis && lime > "7.9.0") - var path = getPath(id); - // TODO: What if it is a WAV or non-Vorbis file? - var vorbisFile = VorbisFile.fromFile(path); - var buffer = AudioBuffer.fromVorbisFile(vorbisFile); - return Sound.fromAudioBuffer(buffer); - #else - // TODO: Streaming sound - return getSound(id, useCache); + #if (lime_vorbis && lime > "7.9.0" && !macro) + if (Options.streamedMusic) { + var path = getPath(id); + // TODO: What if it is a WAV or non-Vorbis file? + var vorbisFile = VorbisFile.fromFile(path); + var buffer = AudioBuffer.fromVorbisFile(vorbisFile); + return Sound.fromAudioBuffer(buffer); + } #end + return getSound(id, useCache); } /**