Update Audio Backend from Vs Camellia v2.75.1 (info at desc)

Before anyone asks why vs cam, the code is public, plus i helped most of the audio backend rewrite in the mod too. :P

- Static sounds can now also seamlessly loop with AL_SOFT_loop_points extension
- Panning stereo sounds is now possible with AL_EXT_STEREO_ANGLES extension (although this was already implemented before)
- Improved streaming sounds, should fix most of the lagspike issues
- Streaming sounds should no longer regenrate if it's in the nearest timestamp (not for reversing though)
! You still can't access FlxSound.latency because of the new procedures that's not available in lime

If there's any crashes with this pls report an issue asap.
This commit is contained in:
Ralty
2025-11-16 15:22:58 +07:00
parent bbe3464085
commit 5d8a9c71a2
8 changed files with 588 additions and 411 deletions
+9 -6
View File
@@ -1,14 +1,17 @@
[general]
[General]
sample-type=float32
stereo-mode=speakers
stereo-encoding=panpot
hrtf=false
cf_level=0
resampler=fast_bsinc24
front-stablizer=false
output-limiter=false
front-stabilizer=false
volume-adjust=0
period_size=441
sources=512
sends=64
dither=false
[decoder]
hq-mode=false
distance-comp=false
hq-mode=true
distance-comp=true
nfc=false
+8 -8
View File
@@ -718,7 +718,7 @@ class FlxSound extends FlxBasic {
_paused = false;
_time = startTime;
_lastTime = FlxG.game.getTicks();
if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed || _source.__backend.handle == null #end)
if (_channel == null || !_channel.__isValid || _source == null #if lime_cffi || _source.__backend.disposed #end)
makeChannel();
if (_channel != null) {
@@ -939,19 +939,19 @@ class FlxSound extends FlxBasic {
return _time = time;
}
inline function get_offset():Float return _offset;
inline function set_offset(offset:Float):Float {
function get_offset():Float return _offset;
function set_offset(offset:Float):Float {
if (_offset == (_offset = offset)) return offset;
//time = time + _offset;
return offset;
}
inline function get_length():Float return _length - _offset;
function get_length():Float return _length - _offset;
/*function get_latency():Float {
if (_channel != null) return _source.latency;
return 0;
}*/
//function get_latency():Float {
// if (_channel != null) return _source.latency;
// return 0;
//}
override function toString():String {
return FlxStringUtil.getDebugString([
+19 -19
View File
@@ -20,8 +20,7 @@ typedef AudioAnalyzerCallback = Int->Int->Void;
* An utility that analyze FlxSounds,
* can be used to make waveform or real-time audio visualizer.
*
* FlxSound.amplitude does work in CNE so if any case if your only checking for peak of current
* time, use that instead.
* FlxSound.amplitude works so if any case if your only checking for peak of current time, use that instead.
*/
final class AudioAnalyzer {
/**
@@ -296,7 +295,7 @@ final class AudioAnalyzer {
__check();
}
function __check() if (sound.buffer != buffer) {
function __check() if (sound != null && sound.buffer != buffer) {
byteSize = 1 << ((buffer = sound.buffer).bitsPerSample - 1);
#if (lime_cffi && lime_vorbis)
@@ -324,7 +323,7 @@ final class AudioAnalyzer {
* @param maxFreq The maximum frequency to cap (Optional, default 22000.0, Above 23000.0 is not recommended).
* @return Output of levels/bars that ranges from 0 to 1.
*/
public function getLevels(startPos:Float, ?volume:Float, barCount:Int, ?levels:Array<Float>, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array<Float>
public function getLevels(?startPos:Float, ?volume:Float, barCount:Int, ?levels:Array<Float>, ?ratio:Float, ?minDb:Float, ?maxDb:Float, ?minFreq:Float, ?maxFreq:Float):Array<Float>
return inline getLevelsFromFrequencies(__frequencies = getFrequencies(startPos, volume, __frequencies), buffer.sampleRate, barCount, levels, ratio, minDb, maxDb, minFreq, maxFreq);
/**
@@ -334,8 +333,8 @@ final class AudioAnalyzer {
* @param frequencies The output for getting the frequencies, to avoid memory leaks (Optional).
* @return Output of frequencies.
*/
public function getFrequencies(startPos:Float, ?volume:Float, ?frequencies:Array<Float>):Array<Float>
return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
public function getFrequencies(?startPos:Float, ?volume:Float, ?frequencies:Array<Float>):Array<Float>
return inline getFrequenciesFromSamples(__freqSamples = getSamples(startPos != null ? startPos : sound.time, fftN, true, -1, volume, __freqSamples), fftN, useWindowingFFT, frequencies);
/**
* Analyzes an attached FlxSound from startPos to endPos in milliseconds to get the amplitudes.
@@ -467,36 +466,37 @@ final class AudioAnalyzer {
@:privateAccess return sound._source != null && sound._source.__backend != null && sound._source.__backend.playing;
inline function __readStream(startPos:Float, endPos:Float, callback:AudioAnalyzerCallback):Float @:privateAccess {
var backend = sound._source.__backend;
var i = backend.bufferSizes.length - backend.queuedBuffers;
var time = backend.bufferTimes[i] * 1000;
final backend = sound._source.__backend;
// TODO: Wrap it with try until i figured it out an effective way to do this...
// So... sometimes it just uses the decoder even if it looks good?? please help
var n = Math.floor((endPos - startPos) * __toBits);
if (startPos >= time && startPos < backend.bufferTimes[backend.bufferSizes.length - 1] * 1000) {
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferSizes[i], c = 0;
var i = backend.bufferLengths.length - backend.queuedBuffers - 1, time:Float;
while (++i < backend.bufferLengths.length) if (startPos >= (time = backend.bufferTimes[i] * 1000)) {
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferLengths[i], c = 0;
while (pos >= size) {
if (++i >= backend.bufferSizes.length) {
n = 0;
break;
}
if (++i >= backend.bufferLengths.length) break;
pos -= size;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferSizes[i];
size = backend.bufferLengths[i];
}
pos -= pos % __sampleSize;
if (i >= backend.bufferLengths.length) break;
if ((pos -= pos % __sampleSize) < 0) pos = 0;
n -= pos % __sampleSize;
while (n > 0) {
callback(getByte(buf, pos, __wordSize), c);
if (++c > buffer.channels) c = 0;
if ((pos += __wordSize) >= size) {
if (++i >= backend.bufferSizes.length) break;
if (++i >= backend.bufferLengths.length) break;
pos = 0;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferSizes[i];
size = backend.bufferLengths[i];
}
n -= __wordSize;
}
break;
}
return endPos - (n / __toBits);
+5 -7
View File
@@ -1587,13 +1587,11 @@ class PlayState extends MusicBeatState
var camera:FlxCamera = event.params[1] == "camHUD" ? camHUD : camGame;
camera.zoom += event.params[0];
case "Camera Bop":
if (Options.camZoomOnBeat) {
if (useCamZoomMult) {
camZoomingMult += event.params[0];
} else {
FlxG.camera.zoom += event.params[0] * camZoomingStrength;
camHUD.zoom += event.params[0] * camZoomingStrength;
}
if (useCamZoomMult) {
camZoomingMult += event.params[0];
} else {
FlxG.camera.zoom += event.params[0] * camZoomingStrength;
camHUD.zoom += event.params[0] * camZoomingStrength;
}
case "Camera Zoom":
var cam = event.params[2] == "camHUD" ? camHUD : camGame;
File diff suppressed because it is too large Load Diff
+20 -16
View File
@@ -8,7 +8,10 @@ import lime.app.Future;
import lime.app.Promise;
import lime.media.openal.AL;
import lime.media.openal.ALBuffer;
#if lime_vorbis
import lime.media.vorbis.Vorbis;
import lime.media.vorbis.VorbisFile;
#end
import lime.net.HTTPRequest;
import lime.utils.Log;
import lime.utils.UInt8Array;
@@ -101,7 +104,10 @@ class AudioBuffer
__srcHowl = null;
#end
#if lime_cffi
if (__srcBuffer != null) AL.deleteBuffer(__srcBuffer);
if (__srcBuffer != null) {
AL.bufferData(__srcBuffer, 0, null, 0, 0);
AL.deleteBuffer(__srcBuffer);
}
__srcBuffer = null;
#end
#if lime_vorbis
@@ -178,11 +184,9 @@ class AudioBuffer
return audioBuffer;
#elseif (lime_cffi && !macro)
#if lime_vorbis // CNE
if (funkin.options.Options.streamedMusic) {
var vorbisFile = VorbisFile.fromBytes(bytes);
if (vorbisFile != null) return fromVorbisFile(vorbisFile);
}
#if lime_vorbis
var vorbisFile = VorbisFile.fromBytes(bytes);
if (vorbisFile != null) return fromVorbisFile(vorbisFile);
#end
#if !cs
var audioBuffer = new AudioBuffer();
@@ -312,29 +316,29 @@ class AudioBuffer
if (vorbisFile == null) return null;
var info = vorbisFile.info();
if (info == null) return null;
var audioBuffer = new AudioBuffer();
audioBuffer.channels = info.channels;
audioBuffer.sampleRate = info.rate;
audioBuffer.bitsPerSample = 16;
if (!vorbisFile.seekable() ||
vorbisFile.pcmTotal() < #if lime_cffi @:privateAccess lime._internal.backend.native.NativeAudioSource.STREAM_BUFFER_SAMPLES #else 0x4000 #end)
{
// convert it to static if its too short or unseekable.
final pcmTotal = vorbisFile.pcmTotal(-1);
if (!vorbisFile.seekable() || pcmTotal < (audioBuffer.sampleRate << 2)) {
vorbisFile.rawSeek(0);
var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
var bytes:Bytes = Bytes.alloc(Std.int(haxe.Int64.toInt(vorbisFile.pcmTotal()) * info.channels * 2));
final isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN;
final bytes = Bytes.alloc(Std.int((pcmTotal.high * 4294967296. + (pcmTotal.low >> 0)) * info.channels * (audioBuffer.bitsPerSample >> 3)));
var total = 0, result = 0;
do {
total += (result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true));
} while (result > 0);
result = vorbisFile.read(bytes, total, 0x1000, isBigEndian, 2, true);
total += result;
} while (result > 0 || result == Vorbis.HOLE);
audioBuffer.data = new UInt8Array(bytes);
vorbisFile.clear();
}
else audioBuffer.__srcVorbisFile = vorbisFile;
else
audioBuffer.__srcVorbisFile = vorbisFile;
return audioBuffer;
}
+1 -1
View File
@@ -131,7 +131,7 @@ class AudioSource
@:noCompletion inline private function init():Void
{
__backend.init();
activeSources.push(this);
if (!activeSources.contains(this)) activeSources.push(this);
}
/**
+5 -12
View File
@@ -137,7 +137,6 @@ import lime.media.openal.AL;
#if lime
__source.onComplete.remove(source_onComplete);
__source.onLoop.remove(source_onLoop);
__source.dispose();
__source = null;
#end
@@ -165,14 +164,14 @@ import lime.media.openal.AL;
#if lime_cffi
var backend = __source.__backend, i = 0;
if (backend.streamed) {
size = backend.bufferSizes[i = backend.bufferSizes.length - backend.queuedBuffers];
size = backend.bufferLengths[i = backend.bufferLengths.length - backend.queuedBuffers];
buf = backend.bufferDatas[i].buffer;
pos -= Math.floor(backend.bufferTimes[i] * buffer.sampleRate * buffer.channels * wordSize);
while (pos > size) {
if (++i >= backend.bufferSizes.length) return false;
if (++i >= backend.bufferLengths.length) return false;
pos -= size;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferSizes[i];
size = backend.bufferLengths[i];
}
}
else
@@ -189,10 +188,10 @@ import lime.media.openal.AL;
if (c % 2 == 0) ((b > leftMax) ? (leftMax = b) : (if ((b = -b) > leftMin) (leftMin = b)));
else ((b > rightMax) ? (rightMax = b) : (if ((b = -b) > rightMin) (rightMin = b)));
if ((pos += wordSize) >= size) #if lime_cffi {
if (!backend.streamed || ++i >= backend.bufferSizes.length) break;
if (!backend.streamed || ++i >= backend.bufferLengths.length) break;
pos = 0;
buf = backend.bufferDatas[i].buffer;
size = backend.bufferSizes[i];
size = backend.bufferLengths[i];
}
#else break; #end
@@ -222,7 +221,6 @@ import lime.media.openal.AL;
}
__source.onComplete.add(source_onComplete);
__source.onLoop.add(source_onLoop);
__isValid = true;
__source.play();
@@ -394,11 +392,6 @@ import lime.media.openal.AL;
dispatchEvent(new Event(Event.SOUND_COMPLETE));
}
@:noCompletion private function source_onLoop():Void
{
//dispatchEvent(new Event(Event.SOUND_LOOP));
}
@:noCompletion private function get___audioSource():AudioSource return __source;
@:noCompletion private function set___audioSource(source:AudioSource):AudioSource return __source = source;
}