Rewrite Internal Audio Source & Fix AudioSwitchFix

This commit is contained in:
Ralty
2025-07-11 10:59:26 +07:00
parent aa5cf639e8
commit 80bc6506b9
6 changed files with 277 additions and 369 deletions
+3 -7
View File
@@ -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) {
@@ -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<FlxSound> = [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<PlayingSound> = [];
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;
}
@@ -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<NativeAudioSource> = [];
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<Bool>;
//#end
private var buffers:Array<ALBuffer>;
private var bufferDatas:Array<ArrayBufferView>;
private var bufferTimeBlocks:Array<Float>;
private var bufferLoops:Int;
private var queuedBuffers:Int;
private var requestBuffers:Int;
private var length:Null<Float>;
private var loopTime:Null<Float>;
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<ALBuffer>;
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<ArrayBufferView>;
private var bufferTimes:Array<Float>;
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<Float>;
private var loopTime:Null<Float>;
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<ALBuffer>):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<ALBuffer>) {
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<Float> {
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;
}
}
}
+4
View File
@@ -18,6 +18,8 @@ import lime.math.Vector4;
**/
class AudioSource
{
private static var activeSources:Array<AudioSource> = [];
/**
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);
}
/**
-18
View File
@@ -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();
+2 -2
View File
@@ -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;