Stable Reworked Audio
- Reworked Internal Audio to work with Streaming Vorbis Files (for now?) - FlxSound.amplitude, FlxSound.amplitudeLeft, FlxSound.amplitudeRight, SoundChannel.leftPeak, SoundChannel.rightPeak now works. (Streamed Audio on this seems to be half-broken) - Reworked AudioAnalyzer to work with all type of audios (hopefully) and work with Streamed Audio. - Redo AudioSwitchFix to work in time instead of when switching states. gosh, and all that i hope it doesnt break anything.
This commit is contained in:
@@ -248,7 +248,6 @@ class FlxSound extends FlxBasic {
|
||||
var _amplitudeLeft:Float;
|
||||
var _amplitudeRight:Float;
|
||||
var _amplitudeUpdate:Bool;
|
||||
var _amplitudeTime:Float;
|
||||
var _alreadyPaused:Null<Bool>;
|
||||
|
||||
public function new() {
|
||||
@@ -468,7 +467,7 @@ class FlxSound extends FlxBasic {
|
||||
function init(?looped:Null<Bool>, ?autoDestroy:Null<Bool>, ?onComplete:Null<Void->Void>):FlxSound {
|
||||
if (looped != null) this.looped = looped;
|
||||
if (autoDestroy != null) this.autoDestroy = autoDestroy;
|
||||
if (onComplete != null) this.onComplete = onComplete; // DEPRECATED
|
||||
if (onComplete != null) this.onComplete = onComplete;
|
||||
|
||||
if (_sound != null) makeChannel();
|
||||
else _length = 0;
|
||||
@@ -483,6 +482,7 @@ class FlxSound extends FlxBasic {
|
||||
* @since raltyMod
|
||||
*/
|
||||
function makeChannel() @:privateAccess {
|
||||
if (_sound.__buffer == null) return;
|
||||
if (_source != null) _source.dispose();
|
||||
|
||||
if (_channel == null) (_source = (_channel = new SoundChannel(null)).__source = new AudioSource(_sound.__buffer)).gain = 0;
|
||||
@@ -684,20 +684,19 @@ class FlxSound extends FlxBasic {
|
||||
#end
|
||||
|
||||
_channel.soundTransform = _transform;
|
||||
_channel.__lastPeakTime = 0;
|
||||
_channel.__lastPeakSamples = 0;
|
||||
_channel.__leftPeak = 0;
|
||||
_channel.__rightPeak = 0;
|
||||
_channel.addEventListener(Event.SOUND_COMPLETE, stopped);
|
||||
|
||||
#if lime_openal _source.__backend.playing = true; #end
|
||||
_source.offset = 0;
|
||||
_source.currentTime = startTime - _offset;
|
||||
_source.currentTime = startTime + _offset;
|
||||
#if !lime_openal _source.play(); #end
|
||||
|
||||
looped = looped;
|
||||
loopTime = loopTime;
|
||||
endTime = endTime;
|
||||
_amplitudeTime = -1;
|
||||
|
||||
active = true;
|
||||
}
|
||||
@@ -773,13 +772,11 @@ class FlxSound extends FlxBasic {
|
||||
@:privateAccess return _sound != null ? _sound.__buffer : null;
|
||||
|
||||
function update_amplitude():Void @:privateAccess {
|
||||
if (_channel == null || _time == _amplitudeTime || !_amplitudeUpdate) return;
|
||||
_channel.__updatePeaks();
|
||||
if (_channel == null || !_channel.__updatePeaks() || !_amplitudeUpdate) return;
|
||||
|
||||
_amplitudeUpdate = false;
|
||||
_amplitudeLeft = _channel.__leftPeak;
|
||||
_amplitudeRight = _channel.__rightPeak;
|
||||
_amplitudeTime = _time;
|
||||
}
|
||||
|
||||
inline function get_amplitudeLeft():Float {
|
||||
@@ -794,7 +791,7 @@ class FlxSound extends FlxBasic {
|
||||
|
||||
inline function get_amplitude():Float {
|
||||
update_amplitude();
|
||||
return if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
|
||||
return Math.max(_amplitudeLeft, _amplitudeRight);//if (stereo) (_amplitudeLeft + _amplitudeRight) * 0.5; else _amplitudeLeft;
|
||||
}
|
||||
|
||||
inline function get_channels():Int
|
||||
@@ -845,9 +842,9 @@ class FlxSound extends FlxBasic {
|
||||
return _time;
|
||||
}
|
||||
function get_time():Float {
|
||||
if (_channel == null || AudioManager.context == null) return _time;
|
||||
if (_channel == null || /*AudioManager.context == null*/funkin.backend.system.Main.audioDisconnected /*CNE IMPLEMENTATION*/) return _time;
|
||||
|
||||
final pos = _channel.position + _offset;
|
||||
final pos = _channel.position - _offset;
|
||||
if (!playing || _realPitch <= 0) {
|
||||
_lastTime = null;
|
||||
return _time = pos;
|
||||
@@ -876,7 +873,7 @@ class FlxSound extends FlxBasic {
|
||||
}
|
||||
else if (playing) {
|
||||
_source.offset = 0;
|
||||
_source.currentTime = time - _offset;
|
||||
_source.currentTime = time + _offset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -887,7 +884,7 @@ class FlxSound extends FlxBasic {
|
||||
inline function get_offset():Float return _offset;
|
||||
inline function set_offset(offset:Float):Float {
|
||||
if (_offset == (_offset = offset)) return offset;
|
||||
//time = time - _offset;
|
||||
//time = time + _offset;
|
||||
return offset;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,13 @@ import haxe.Timer;
|
||||
class AudioSwitchFix {
|
||||
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();
|
||||
for (sound in FlxG.sound.list) if (sound.playing) {
|
||||
sound.pause();
|
||||
soundList.push(sound);
|
||||
}
|
||||
|
||||
var i = AudioSource.activeSources.length;
|
||||
while (i-- > 0) AudioSource.activeSources[i].dispose();
|
||||
|
||||
AudioManager.shutdown();
|
||||
|
||||
@@ -36,7 +41,7 @@ class AudioSwitchFix {
|
||||
// #end
|
||||
|
||||
for (sound in soundList) {
|
||||
sound._paused = true;
|
||||
sound.makeChannel();
|
||||
sound.resume();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,44 +2,121 @@ package funkin.backend.utils;
|
||||
|
||||
import flixel.sound.FlxSound;
|
||||
import lime.media.AudioBuffer;
|
||||
import lime.utils.ArrayBufferView;
|
||||
|
||||
// Thank you yosh :DDD -lunar
|
||||
// (https://github.com/CodenameCrew/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx)
|
||||
#if lime_vorbis
|
||||
import lime.media.vorbis.Vorbis;
|
||||
import lime.media.vorbis.VorbisFile;
|
||||
#end
|
||||
|
||||
// ORIGINAL CODES FROM YOSH & LUNAR https://github.com/CodenameCrew/YoshiCrafterEngine/blob/main/source/WaveformSprite.hx
|
||||
// REWRITTEN BY RALTYRO
|
||||
class AudioAnalyzer {
|
||||
public var buffer:AudioBuffer;
|
||||
var __peakByte:Float = 0;
|
||||
var __timeMulti:Float = 0;
|
||||
var __toBits:Float;
|
||||
var __wordSize:Int;
|
||||
var __bitUnsignedSize:Int;
|
||||
var __bitSize:Int;
|
||||
|
||||
#if lime_vorbis
|
||||
var __vorbis:VorbisFile;
|
||||
#end
|
||||
|
||||
public function new(sound:FlxSound) {
|
||||
@:privateAccess buffer = sound._sound.__buffer;
|
||||
buffer = sound.buffer;
|
||||
|
||||
__peakByte = Math.pow(2, buffer.bitsPerSample-1)-1;
|
||||
__bitUnsignedSize = 1 << buffer.bitsPerSample;
|
||||
__bitSize = 1 << (buffer.bitsPerSample - 1);
|
||||
__toBits = buffer.sampleRate / 1000 * buffer.channels * (__wordSize = buffer.bitsPerSample >> 3);
|
||||
|
||||
__timeMulti = 1 / (1 / buffer.sampleRate);
|
||||
__timeMulti *= buffer.bitsPerSample;
|
||||
__timeMulti -= __timeMulti % buffer.bitsPerSample;
|
||||
#if lime_vorbis
|
||||
__prepareVorbis();
|
||||
#end
|
||||
}
|
||||
|
||||
public function analyze(startPos:Float, endPos:Float):Float {
|
||||
var bytesStartPos:Int = Math.floor(startPos * __timeMulti / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample;
|
||||
var bytesEndPos:Int = Math.floor(endPos * __timeMulti / 4000 / buffer.bitsPerSample) * buffer.bitsPerSample;
|
||||
if (buffer.data != null) return __analyze(startPos, endPos);
|
||||
#if lime_vorbis
|
||||
else if (__prepareVorbis()) return __analyzeVorbis(startPos, endPos);
|
||||
#end
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytesStartPos -= bytesStartPos % buffer.bitsPerSample;
|
||||
bytesEndPos -= bytesEndPos % buffer.bitsPerSample;
|
||||
inline function __analyze(startPos:Float, endPos:Float):Float {
|
||||
var pos = Math.floor(startPos * __toBits), end = Math.floor(endPos * __toBits), buf = #if js buffer.data #else buffer.data.buffer #end;
|
||||
var max = 0, b = 0;
|
||||
if (__wordSize == 1) { // 8-bit audio data is unsigned (0 is 128, 128 is 256, -128 is 0)
|
||||
while (pos < end) {
|
||||
if (max < (b = #if js buf[pos] #else buf.get(pos) #end - __bitSize)) max = b;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
pos -= pos % __wordSize;
|
||||
end -= end % __wordSize;
|
||||
|
||||
var maxByte:Int = 0;
|
||||
for(i in 0...Math.floor((bytesEndPos - bytesStartPos) / buffer.bitsPerSample)) {
|
||||
#if js
|
||||
var byte:Int = buffer.data[bytesStartPos + (i * buffer.bitsPerSample)]
|
||||
| (buffer.data[bytesStartPos + (i * buffer.bitsPerSample) + 1] << 8);
|
||||
#else
|
||||
var byte:Int = buffer.data.buffer.get(bytesStartPos + (i * buffer.bitsPerSample))
|
||||
| (buffer.data.buffer.get(bytesStartPos + (i * buffer.bitsPerSample) + 1) << 8);
|
||||
#end
|
||||
if (byte > 256 * 128) byte -= 256 * 256;
|
||||
if (maxByte < byte) maxByte = byte;
|
||||
var w = 0;
|
||||
while (pos < end) {
|
||||
while (w < buffer.bitsPerSample) {
|
||||
b |= #if js buf[pos] #else buf.get(pos) #end << w;
|
||||
w += 8;
|
||||
pos++;
|
||||
}
|
||||
if (b > __bitSize) b -= __bitUnsignedSize;
|
||||
if (max < b) max = b;
|
||||
w = b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return maxByte/__peakByte;
|
||||
return max / __bitSize;
|
||||
}
|
||||
|
||||
#if lime_vorbis // As far i know, only native supports vorbis
|
||||
var __buffer:ArrayBufferView;
|
||||
function __prepareVorbis():Bool @:privateAccess {
|
||||
if (buffer.__srcVorbisFile == null) return __vorbis != null;
|
||||
if (__vorbis != null) return true;
|
||||
if ((__vorbis = buffer.__srcVorbisFile.clone()) != null) { // IM HOPING IT HAVE A GC CLOSURE.
|
||||
__buffer = new ArrayBufferView(0x1000, buffer.bitsPerSample == 32 ? Int32 : buffer.bitsPerSample == 16 ? Int16 : Int8);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var prevPos:Int = 0;
|
||||
inline function __analyzeVorbis(startPos:Float, endPos:Float):Float {
|
||||
var n = Math.floor((endPos - startPos) * __toBits);
|
||||
if (n < __wordSize) return 0;
|
||||
|
||||
n -= n % __wordSize;
|
||||
|
||||
if (prevPos != Math.floor(startPos)) {
|
||||
__vorbis.timeSeek(startPos / 1000);
|
||||
prevPos = Math.floor(endPos);
|
||||
}
|
||||
|
||||
var isBigEndian = lime.system.System.endianness == lime.system.Endian.BIG_ENDIAN, bufferSize = __buffer.length, buf = __buffer.buffer;
|
||||
var max = 0, b = 0, pos = 0, w = 0, result;
|
||||
while (n > 0) {
|
||||
n -= (result = __vorbis.read(buf, 0, n < bufferSize ? n : bufferSize, isBigEndian, __wordSize, true));
|
||||
|
||||
if (result == Vorbis.HOLE) continue;
|
||||
else if (result <= 0) break;
|
||||
|
||||
while (pos < result) {
|
||||
while (w < buffer.bitsPerSample) {
|
||||
b |= #if js buf[pos] #else buf.get(pos) #end << w;
|
||||
w += 8;
|
||||
pos++;
|
||||
}
|
||||
if (b > __bitSize) b -= __bitUnsignedSize;
|
||||
if (max < b) max = b;
|
||||
w = b = 0;
|
||||
}
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
return max / __bitSize;
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -823,7 +823,6 @@ class CoolUtil
|
||||
if (frontEnd.music != null) @:privateAccess frontEnd.destroySound(frontEnd.music);
|
||||
frontEnd.list.remove(music);
|
||||
frontEnd.defaultMusicGroup.add(frontEnd.music = music);
|
||||
music.persist = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1570,10 +1570,10 @@ class Charter extends UIState {
|
||||
vocals.pause();
|
||||
for (strumLine in strumLines.members) strumLine.vocals.pause();
|
||||
} else {
|
||||
FlxG.sound.music.play(Conductor.songPosition + Conductor.songOffset);
|
||||
vocals.play(FlxG.sound.music.getActualTime());
|
||||
FlxG.sound.music.play(true, Conductor.songPosition + Conductor.songOffset);
|
||||
vocals.play(true, FlxG.sound.music.getActualTime());
|
||||
for (strumLine in strumLines.members) {
|
||||
strumLine.vocals.play(FlxG.sound.music.getActualTime());
|
||||
strumLine.vocals.play(true, FlxG.sound.music.getActualTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ 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
|
||||
@@ -26,7 +25,7 @@ import lime.utils.ArrayBufferView;
|
||||
@:access(lime.media.AudioBuffer)
|
||||
@:access(lime.utils.ArrayBufferView)
|
||||
class NativeAudioSource {
|
||||
private static var STREAM_BUFFER_SIZE:Int = 0x4000;
|
||||
private static var STREAM_BUFFER_SIZE:Int = 0x4000; // what if it was odd channels (excluding mono).
|
||||
private static var STREAM_MAX_BUFFERS:Int = 32;
|
||||
private static var STREAM_TIMER_FREQUENCY:Int = 100;
|
||||
private static var STREAM_BUFFER_FREQUENCY:Int = 6;
|
||||
@@ -47,6 +46,7 @@ class NativeAudioSource {
|
||||
private var streamTimer:Timer;
|
||||
private var bufferDatas:Array<ArrayBufferView>;
|
||||
private var bufferTimes:Array<Float>;
|
||||
private var bufferSizes:Array<Int>;
|
||||
private var requestBuffers:Int;
|
||||
private var queuedBuffers:Int;
|
||||
private var toLoop:Int;
|
||||
@@ -74,30 +74,48 @@ class NativeAudioSource {
|
||||
buffers = null;
|
||||
bufferDatas = null;
|
||||
bufferTimes = null;
|
||||
bufferSizes = null;
|
||||
unusedBuffers = null;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (streamed = buffer.__srcVorbisFile != null) {
|
||||
dataLength = (samples = getFloat(buffer.__srcVorbisFile.pcmTotal())) * buffer.channels * (buffer.bitsPerSample == 8 ? 1 : 2);
|
||||
var buffer = parent.buffer;
|
||||
var channels = buffer.channels, bitsPerSample = buffer.bitsPerSample;
|
||||
|
||||
var constructor = buffer.bitsPerSample == 8 ? Int8 : Int16;
|
||||
// Default is just AL.FORMAT_MONO8 if it doesn't match any to avoid yo ears getting blasted
|
||||
var isCreativeXFi = AL.getString(AL.RENDERER) == "X-Fi";
|
||||
if (channels > 2 && (isCreativeXFi || AL.isExtensionPresent("AL_EXT_MCFORMATS"))) { // https://github.com/openalext/openalext/blob/master/AL_EXT_MCFORMATS.txt
|
||||
if (channels == 3) format = bitsPerSample == 32 ? 0x1209 : (bitsPerSample == 16 ? 0x1208 : 0x1207);
|
||||
else if (channels == 4) format = bitsPerSample == 32 ? 0x1206 : (bitsPerSample == 16 ? 0x1205 : 0x1204);
|
||||
else if (channels == 6) format = bitsPerSample == 32 ? 0x120C : (bitsPerSample == 16 ? 0x120B : 0x120A);
|
||||
else if (channels == 7) format = bitsPerSample == 32 ? 0x120F : (bitsPerSample == 16 ? 0x120E : 0x120D);
|
||||
else if (channels == 8) format = bitsPerSample == 32 ? 0x1212 : (bitsPerSample == 16 ? 0x1211 : 0x1210);
|
||||
}
|
||||
// The 32 bit for STEREO and MONO are undocumented? https://github.com/kcat/openal-soft/issues/934, though it only works in X-Fi OpenAL??
|
||||
else if (bitsPerSample == 32 && isCreativeXFi) format = channels == 2 ? 0x1203 : 0x1202;
|
||||
else if (channels == 2) format = bitsPerSample == 16 ? AL.FORMAT_STEREO16 : AL.FORMAT_STEREO8;
|
||||
else format = bitsPerSample == 16 ? AL.FORMAT_MONO16 : AL.FORMAT_MONO8;
|
||||
|
||||
#if lime_vorbis
|
||||
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;
|
||||
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];
|
||||
bufferSizes = [for (i in 0...STREAM_MAX_BUFFERS) 0];
|
||||
unusedBuffers = [];
|
||||
}
|
||||
else {
|
||||
samples = (dataLength = getFloat(Int64.make(0, buffer.data.length))) / buffer.channels / (buffer.bitsPerSample == 8 ? 1 : 2);
|
||||
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);
|
||||
@@ -129,12 +147,8 @@ 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));
|
||||
unusedBuffers.resize(0);
|
||||
}
|
||||
requestBuffers = queuedBuffers = toLoop = 0;
|
||||
AL.sourceStop(handle);
|
||||
toLoop = 0;
|
||||
}
|
||||
|
||||
playing = false;
|
||||
@@ -152,31 +166,29 @@ class NativeAudioSource {
|
||||
// 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 parent.buffer.__srcVorbisFile.timeTell();
|
||||
#else
|
||||
return 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
|
||||
// But it seems to also happen in this lime libvorbis version which is 1.3.7...? -ralty
|
||||
if (samples < 4) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
|
||||
// break;
|
||||
//}
|
||||
// There's apparently a bug in libvorbis <= 1.3.4, hhttps://github.com/xiph/vorbis/blob/master/CHANGES#L39
|
||||
// But it seems to also happen in this lime libvorbis version which is 1.3.7...? -ralty
|
||||
if (samples < 4) parent.buffer.__srcVorbisFile.rawSeek(0); else parent.buffer.__srcVorbisFile.pcmSeek(samples);
|
||||
#end
|
||||
}
|
||||
|
||||
private function readToBufferData(data:ArrayBufferView):Int {
|
||||
var total = 0, result = 0, wasEOF = false;
|
||||
var bigEndian = System.endianness == lime.system.Endian.BIG_ENDIAN, wordSize = parent.buffer.bitsPerSample == 8 ? 1 : 2;
|
||||
#if lime_vorbis
|
||||
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 < STREAM_BUFFER_SIZE ? Math.floor(size) : STREAM_BUFFER_SIZE;
|
||||
|
||||
var vorbisFile = parent.buffer.__srcVorbisFile;
|
||||
var total = 0, result = 0, wasEOF = false;
|
||||
while (total < STREAM_BUFFER_SIZE) {
|
||||
result = n > 0 ? vorbisFile.read(data.buffer, total, n, bigEndian, wordSize, true) : 0;
|
||||
result = n > 0 ? parent.buffer.__srcVorbisFile.read(data.buffer, total, n, isBigEndian, wordSize, true) : 0;
|
||||
|
||||
if (result == Vorbis.HOLE) continue;
|
||||
else if (result <= Vorbis.EREAD) break;
|
||||
@@ -205,36 +217,37 @@ class NativeAudioSource {
|
||||
return result;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private function cycleBuffer() {
|
||||
var i = STREAM_MAX_BUFFERS - (queuedBuffers = requestBuffers), n = STREAM_MAX_BUFFERS - 1;
|
||||
var data = bufferDatas[i], time = bufferTimes[i];
|
||||
while (i < n) {
|
||||
bufferDatas[i] = bufferDatas[i + 1];
|
||||
bufferTimes[i] = bufferTimes[++i];
|
||||
}
|
||||
bufferDatas[n] = data;
|
||||
bufferTimes[n] = time;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
private function fillBuffer(buffer:ALBuffer):Int {
|
||||
var i = STREAM_MAX_BUFFERS - requestBuffers, time = streamTell();
|
||||
var decoded = readToBufferData(bufferDatas[i]);
|
||||
var i = STREAM_MAX_BUFFERS - requestBuffers;
|
||||
var data = bufferDatas[i], time = streamTell();
|
||||
var decoded = readToBufferData(data);
|
||||
|
||||
if (decoded > 0) {
|
||||
AL.bufferData(buffer, format, bufferDatas[i], decoded, parent.buffer.sampleRate);
|
||||
bufferTimes[i] = time;
|
||||
cycleBuffer();
|
||||
AL.bufferData(buffer, format, data, decoded, parent.buffer.sampleRate);
|
||||
|
||||
var n = STREAM_MAX_BUFFERS - 1;
|
||||
while (i < n) {
|
||||
bufferDatas[i] = bufferDatas[i + 1];
|
||||
bufferTimes[i] = bufferTimes[i + 1];
|
||||
bufferSizes[i] = bufferSizes[++i];
|
||||
}
|
||||
queuedBuffers = requestBuffers;
|
||||
bufferDatas[n] = data;
|
||||
bufferTimes[n] = time;
|
||||
bufferSizes[n] = decoded;
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
private function streamRun() {
|
||||
#if lime_vorbis
|
||||
if (parent == null || parent.buffer == null || handle == null) return dispose();
|
||||
if (!playing || disposed) stopStreamTimer();
|
||||
if (!playing || disposed) return stopStreamTimer();
|
||||
|
||||
try {
|
||||
var processed = AL.getSourcei(handle, AL.BUFFERS_PROCESSED), n = STREAM_BUFFER_FREQUENCY, buffer;
|
||||
@@ -264,25 +277,28 @@ class NativeAudioSource {
|
||||
else requestBuffers--;
|
||||
}
|
||||
}
|
||||
|
||||
if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) {
|
||||
AL.sourcePlay(handle);
|
||||
resetTimer((getLength() - getCurrentTime()) / getPitch());
|
||||
}
|
||||
}
|
||||
catch(e) trace('NativeAudioSource Bug! in streamRun $e');
|
||||
#end
|
||||
|
||||
if (AL.getSourcei(handle, AL.SOURCE_STATE) == AL.STOPPED) AL.sourcePlay(handle);
|
||||
if (streamEnded && timer == null) resetTimer((getLength() - getCurrentTime()) / getPitch());
|
||||
}
|
||||
|
||||
// Timers
|
||||
inline function stopStreamTimer() if (streamTimer != null) streamTimer.stop();
|
||||
inline function stopStreamTimer() {
|
||||
if (streamTimer != null) streamTimer.stop();
|
||||
streamTimer = null;
|
||||
}
|
||||
|
||||
private function resetStreamTimer() {
|
||||
stopStreamTimer();
|
||||
if (streamed) (streamTimer = new Timer(STREAM_TIMER_FREQUENCY)).run = streamRun;
|
||||
}
|
||||
|
||||
inline function stopTimer() if (timer != null) timer.stop();
|
||||
inline function stopTimer() {
|
||||
if (timer != null) timer.stop();
|
||||
timer = null;
|
||||
}
|
||||
|
||||
private function resetTimer(timeRemaining:Float) {
|
||||
stopTimer();
|
||||
@@ -290,6 +306,8 @@ class NativeAudioSource {
|
||||
}
|
||||
|
||||
private function timer_onRun() {
|
||||
stopTimer();
|
||||
|
||||
var timeRemaining = (getLength() - getCurrentTime()) / getPitch();
|
||||
if (timeRemaining > 30 && AL.getSourcei(handle, AL.SOURCE_STATE) == AL.PLAYING) {
|
||||
resetTimer(timeRemaining);
|
||||
@@ -317,66 +335,69 @@ class NativeAudioSource {
|
||||
// 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;
|
||||
else if (disposed) return 0;
|
||||
|
||||
var time = AL.getSourcef(handle, AL.SAMPLE_OFFSET) / parent.buffer.sampleRate;
|
||||
if (streamed) time += bufferTimes[STREAM_MAX_BUFFERS - queuedBuffers];
|
||||
time *= 1000;
|
||||
|
||||
var length = if (length == null) getRealLength(); else length;
|
||||
if (loops > 0 && time > length) {
|
||||
var start = loopTime != null ? Math.max(0, loopTime) : 0;
|
||||
return ((time - start) % (length - start)) + start - parent.offset;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return time - parent.offset;
|
||||
}
|
||||
|
||||
public function setCurrentTime(value:Float):Float {
|
||||
if (disposed) return value;
|
||||
|
||||
if (streamed) {
|
||||
AL.sourceStop(handle);
|
||||
AL.sourceUnqueueBuffers(handle, AL.getSourcei(handle, AL.BUFFERS_QUEUED));
|
||||
|
||||
unusedBuffers.resize(0);
|
||||
streamSeek(getSamples(value + parent.offset));
|
||||
}
|
||||
else
|
||||
AL.sourcei(handle, AL.SAMPLE_OFFSET, getSamples(value + parent.offset));
|
||||
if (streamed) AL.sourceStop(handle);
|
||||
else AL.sourcei(handle, AL.SAMPLE_OFFSET, getSamples(value + parent.offset));
|
||||
|
||||
if (playing) {
|
||||
var timeRemaining = (getLength() - value) / AL.getSourcef(handle, AL.PITCH);
|
||||
if (timeRemaining > 0) {
|
||||
var timeRemaining = (getLength() - value) / getPitch();
|
||||
if (completed = timeRemaining <= 0) complete();
|
||||
else {
|
||||
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]);
|
||||
|
||||
resetStreamTimer();
|
||||
}
|
||||
resetTimer(timeRemaining);
|
||||
else
|
||||
resetTimer(timeRemaining);
|
||||
|
||||
AL.sourcePlay(handle);
|
||||
}
|
||||
else {
|
||||
stopStreamTimer();
|
||||
complete();
|
||||
}
|
||||
}
|
||||
|
||||
if (!playing && streamed) bufferTimes[STREAM_MAX_BUFFERS - (requestBuffers = queuedBuffers = 1)] = value / 1000;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
inline private function getRealLength():Float return samples / parent.buffer.sampleRate * 1000;
|
||||
public function getLength():Null<Float> {
|
||||
return if (length == null) getRealLength() - parent.offset;
|
||||
else length - parent.offset;
|
||||
}
|
||||
public function getLength():Null<Float>
|
||||
return if (disposed) 0; else if (length == null) getRealLength() - parent.offset; else length - parent.offset;
|
||||
|
||||
public function setLength(value:Null<Float>):Null<Float> {
|
||||
if (value == length || disposed) return length = value;
|
||||
|
||||
var buffer = parent.buffer, wordSize = buffer.bitsPerSample == 8 ? 1 : 2;
|
||||
var buffer = parent.buffer, wordSize = buffer.bitsPerSample >> 3;
|
||||
if ((length = value) == null) dataLength = streamed ? samples * buffer.channels * wordSize : getFloat(Int64.make(0, buffer.data.length));
|
||||
else dataLength = Math.ffloor(Math.max(0, Math.min(value, getRealLength())) / 1000 * buffer.sampleRate) * buffer.channels * wordSize;
|
||||
|
||||
if (playing) {
|
||||
if (streamed) setCurrentTime(getCurrentTime());
|
||||
if (streamed) {
|
||||
if (streamEnded || toLoop > 0) setCurrentTime(getCurrentTime());
|
||||
}
|
||||
else {
|
||||
var timeRemaining = ((getLength() - parent.offset) - getCurrentTime()) / getPitch();
|
||||
if (timeRemaining > 0) resetTimer(timeRemaining);
|
||||
@@ -392,12 +413,9 @@ class NativeAudioSource {
|
||||
|
||||
public function setPitch(value:Float):Float {
|
||||
if (disposed || value == AL.getSourcef(handle, AL.PITCH)) return value;
|
||||
if (playing) {
|
||||
if (value <= 0) stopTimer();
|
||||
else {
|
||||
var timeRemaining = (getLength() - getCurrentTime()) / value;
|
||||
if (timeRemaining > 0) resetTimer(timeRemaining);
|
||||
}
|
||||
if (playing && (streamEnded || !streamed)) {
|
||||
var timeRemaining = (getLength() - getCurrentTime()) / value;
|
||||
if (timeRemaining > 0) resetTimer(timeRemaining);
|
||||
}
|
||||
AL.sourcef(handle, AL.PITCH, value);
|
||||
return value;
|
||||
@@ -454,6 +472,6 @@ class NativeAudioSource {
|
||||
return position;
|
||||
}
|
||||
|
||||
inline private function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >>> 0);
|
||||
inline private function getFloat(x:Int64):Float return x.high * 4294967296. + (x.low >> 0);
|
||||
inline private function getSamples(ms:Float):Int64 return Int64.fromFloat(Math.max(0, Math.min(ms / 1000 * parent.buffer.sampleRate, samples)));
|
||||
}
|
||||
}
|
||||
@@ -105,10 +105,7 @@ class AudioBuffer
|
||||
__srcBuffer = null;
|
||||
#end
|
||||
#if lime_vorbis
|
||||
if (__srcVorbisFile != null) {
|
||||
__srcVorbisFile.clear();
|
||||
@:privateAccess __srcVorbisFile.handle = null;
|
||||
}
|
||||
if (__srcVorbisFile != null) __srcVorbisFile.clear();
|
||||
__srcVorbisFile = null;
|
||||
#end
|
||||
}
|
||||
@@ -131,7 +128,7 @@ class AudioBuffer
|
||||
}
|
||||
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.src = new Howl({src: [base64String], html5: true, preload: false});
|
||||
audioBuffer.src = new Howl({src: [base64String], preload: false});
|
||||
return audioBuffer;
|
||||
#elseif (lime_cffi && !macro)
|
||||
#if !cs
|
||||
@@ -177,7 +174,7 @@ class AudioBuffer
|
||||
|
||||
#if (js && html5 && lime_howlerjs)
|
||||
var audioBuffer = new AudioBuffer();
|
||||
audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], html5: true, preload: false});
|
||||
audioBuffer.src = new Howl({src: ["data:" + __getCodec(bytes) + ";base64," + Base64.encode(bytes)], preload: false});
|
||||
|
||||
return audioBuffer;
|
||||
#elseif (lime_cffi && !macro)
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
package lime.media.vorbis;
|
||||
|
||||
#if (!lime_doc_gen || lime_vorbis)
|
||||
import haxe.Int64;
|
||||
import haxe.io.Bytes;
|
||||
import lime._internal.backend.native.NativeCFFI;
|
||||
|
||||
#if hl
|
||||
@:keep
|
||||
#end
|
||||
@:access(lime._internal.backend.native.NativeCFFI)
|
||||
class VorbisFile
|
||||
{
|
||||
public var bitstream(default, null):Int;
|
||||
|
||||
@:noCompletion private var handle:Dynamic;
|
||||
|
||||
@:noCompletion var _filePath:String;
|
||||
@:noCompletion var _bytes:Bytes;
|
||||
|
||||
@:noCompletion private function new(handle:Dynamic)
|
||||
{
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public function bitrate(bitstream:Int = -1):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_bitrate(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function bitrateInstant():Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_bitrate_instant(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function clear():Void
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
NativeCFFI.lime_vorbis_file_clear(handle);
|
||||
#end
|
||||
handle = null;
|
||||
_filePath = null;
|
||||
_bytes = null;
|
||||
}
|
||||
|
||||
public function clone():VorbisFile
|
||||
{
|
||||
if (_filePath != null) return fromFile(_filePath);
|
||||
if (_bytes != null) return fromBytes(_bytes);
|
||||
return null;
|
||||
}
|
||||
|
||||
public function comment(bitstream:Int = -1):VorbisComment
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_comment(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var comment = new VorbisComment();
|
||||
comment.userComments = data.userComments;
|
||||
comment.vendor = data.vendor;
|
||||
return comment;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function crosslap(other:VorbisFile):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_crosslap(handle, other.handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public static function fromBytes(bytes:Bytes):VorbisFile
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var handle = NativeCFFI.lime_vorbis_file_from_bytes(bytes);
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
var vorbisFile = new VorbisFile(handle);
|
||||
vorbisFile._bytes = bytes;
|
||||
return vorbisFile;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function fromFile(path:String):VorbisFile
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var handle = NativeCFFI.lime_vorbis_file_from_file(path);
|
||||
|
||||
if (handle != null)
|
||||
{
|
||||
var vorbisFile = new VorbisFile(handle);
|
||||
vorbisFile._filePath = path;
|
||||
return vorbisFile;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function info(bitstream:Int = -1):VorbisInfo
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_info(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
var info = new VorbisInfo();
|
||||
info.bitrateLower = data.bitrateLower;
|
||||
info.bitrateNominal = data.bitrateNominal;
|
||||
info.bitrateUpper = data.bitrateUpper;
|
||||
info.channels = data.channels;
|
||||
info.rate = data.rate;
|
||||
info.version = data.version;
|
||||
return info;
|
||||
}
|
||||
#end
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function pcmSeek(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekPage(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_page(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmSeekPageLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_pcm_seek_page_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function pcmTell():Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_pcm_tell(handle);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function pcmTotal(bitstream:Int = -1):Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_pcm_total(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function rawSeek(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_raw_seek(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function rawSeekLap(pos:Int64):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_raw_seek_lap(handle, pos.low, pos.high);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function rawTell():Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_raw_tell(handle);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function rawTotal(bitstream:Int = -1):Int64
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_raw_total(handle, bitstream);
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
return Int64.make(data.high, data.low);
|
||||
}
|
||||
#end
|
||||
|
||||
return Int64.ofInt(0);
|
||||
}
|
||||
|
||||
public function read(buffer:Bytes, position:Int, length:Int = 4096, bigEndianPacking:Bool = false, wordSize:Int = 2, signed:Bool = true):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_read(handle, buffer, position, length, bigEndianPacking, wordSize, signed);
|
||||
if (data == null) return 0;
|
||||
bitstream = data.bitstream;
|
||||
return data.returnValue;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
// public function readFilter (buffer:Bytes, length:Int = 4096, endianness:Endian = LITTLE_ENDIAN, wordSize:Int = 2, signed:Bool = true, bitstream:Int = 0, filter, filter_param
|
||||
public function readFloat(pcmChannels:Bytes, samples:Int):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
var data = NativeCFFI.lime_vorbis_file_read_float(handle, pcmChannels, samples);
|
||||
if (data == null) return 0;
|
||||
bitstream = data.bitstream;
|
||||
return data.returnValue;
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function seekable():Bool
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_seekable(handle);
|
||||
#else
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
public function serialNumber(bitstream:Int = -1):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_serial_number(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function streams():Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_streams(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeek(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekLap(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_lap(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekPage(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_page(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeSeekPageLap(s:Float):Int
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_seek_page_lap(handle, s);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeTell():Float
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_tell(handle);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
|
||||
public function timeTotal(bitstream:Int = -1):Float
|
||||
{
|
||||
#if (lime_cffi && lime_vorbis && !macro)
|
||||
return NativeCFFI.lime_vorbis_file_time_total(handle, bitstream);
|
||||
#else
|
||||
return 0;
|
||||
#end
|
||||
}
|
||||
}
|
||||
#end
|
||||
@@ -82,7 +82,7 @@ import lime.media.openal.AL;
|
||||
@:noCompletion private var __sound:Sound;
|
||||
@:noCompletion private var __isValid:Bool;
|
||||
@:noCompletion private var __soundTransform:SoundTransform;
|
||||
@:noCompletion private var __lastPeakTime:Float;
|
||||
@:noCompletion private var __lastPeakSamples:Float;
|
||||
@:noCompletion private var __leftPeak:Float;
|
||||
@:noCompletion private var __rightPeak:Float;
|
||||
#if lime
|
||||
@@ -151,86 +151,66 @@ import lime.media.openal.AL;
|
||||
this.soundTransform = soundTransform;
|
||||
}
|
||||
|
||||
// hi i made these - raltyro
|
||||
// took me 8 months to figure this out
|
||||
@:noCompletion inline private static var scanSamples = 480;
|
||||
|
||||
@:noCompletion private function __checkUpdatePeaks(time:Float):Bool
|
||||
@:noCompletion private function __updatePeaks():Bool
|
||||
{
|
||||
if (Math.abs(__lastPeakTime - time) < Math.max(1, pitch * 8)) return false;
|
||||
__lastPeakTime = time;
|
||||
return true;
|
||||
}
|
||||
var backend = __source.__backend;
|
||||
var samples = AL.getSourcef(backend.handle, AL.SAMPLE_OFFSET);
|
||||
if (__lastPeakSamples == samples) return false;
|
||||
|
||||
/*@:noCompletion private function __updateVorbisPeaks():Void
|
||||
{
|
||||
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_MAX_BUFFERS - __source.__backend.queuedBuffers, bytes = bufferDatas[bufferi].buffer;
|
||||
|
||||
var channels = __source.buffer.channels, todo = scanSamples, sample;
|
||||
var lfilled = false, rfilled = channels < 2;
|
||||
|
||||
while(todo > 0 && (!lfilled || !rfilled)) {
|
||||
if (index >= NativeAudioSource.STREAM_BUFFER_SIZE) {
|
||||
if ((bytes = bufferDatas[++bufferi].buffer) == null) break;
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if (!lfilled) {
|
||||
if ((sample = bytes.getUInt16(index * channels * 2) / 65535) > .5) sample = -(sample - 1);
|
||||
if (sample > __leftPeak) lfilled = (__leftPeak = sample) >= 1;
|
||||
}
|
||||
|
||||
if (!rfilled) {
|
||||
if ((sample = bytes.getUInt16(index * channels * 2 + 2) / 65535) > .5) sample = -(sample - 1);
|
||||
if (sample > __rightPeak) rfilled = (__rightPeak = sample) >= 1;
|
||||
}
|
||||
|
||||
index++;
|
||||
todo--;
|
||||
}
|
||||
}
|
||||
|
||||
@:noCompletion private function __updatePeaks():Void
|
||||
{
|
||||
__lastPeakSamples = samples;
|
||||
__leftPeak = __rightPeak = 0;
|
||||
|
||||
if (!__isValid) return;
|
||||
|
||||
#if lime_vorbis if (__source.__backend.streamed) return __updateVorbisPeaks(); #end
|
||||
#if (lime_cffi && !macro)
|
||||
if (!__isValid) return false;
|
||||
|
||||
var buffer = __source.buffer;
|
||||
if (buffer == null || buffer.data == null || !__checkUpdatePeaks(position)) return;
|
||||
var wordSize = buffer.bitsPerSample >> 3, bitUnsignedSize = 1 << buffer.bitsPerSample, bitSize = 1 << (buffer.bitsPerSample - 1);
|
||||
var pos = Math.floor(samples * buffer.channels * wordSize);
|
||||
var n = Math.floor(Math.min(buffer.sampleRate / 80, 600)), b = 0, w = 0, c = 0, i = 0, size = 0, buf;
|
||||
|
||||
var index = Int64.make(0, AL.getSourcei(__source.__backend.handle, AL.SAMPLE_OFFSET));
|
||||
var bytes = buffer.data.buffer, length = __source.__backend.samples;
|
||||
if (index >= length) return;
|
||||
|
||||
var temp = index + scanSamples;
|
||||
if (temp < length) length = temp;
|
||||
|
||||
var channels = buffer.channels, sample;
|
||||
var lfilled = false, rfilled = channels < 2;
|
||||
|
||||
while(index < length && (!lfilled || !rfilled)) {
|
||||
if (!lfilled) {
|
||||
if ((sample = bytes.getUInt16(Int64.toInt(index * (channels * 2))) / 65535) > .5) sample = -(sample - 1);
|
||||
if (sample > __leftPeak) lfilled = (__leftPeak = sample) >= 1;
|
||||
}
|
||||
if (!rfilled) {
|
||||
if ((sample = bytes.getUInt16(Int64.toInt(index * (channels * 2) + 2)) / 65535) > .5) sample = -(sample - 1);
|
||||
if (sample > __rightPeak) rfilled = (__rightPeak = sample) >= 1;
|
||||
}
|
||||
index++;
|
||||
if (backend.streamed) {
|
||||
buf = backend.bufferDatas[i = NativeAudioSource.STREAM_MAX_BUFFERS - backend.queuedBuffers] #if !js .buffer #end;
|
||||
pos %= (size = NativeAudioSource.STREAM_BUFFER_SIZE * wordSize);
|
||||
}
|
||||
else {
|
||||
buf = buffer.data #if !js .buffer #end;
|
||||
size = #if js buf.byteLength #else buf.length #end;
|
||||
}
|
||||
}*/
|
||||
|
||||
@:noCompletion private function __updatePeaks():Void
|
||||
{
|
||||
__leftPeak = __rightPeak = 0;
|
||||
if (!__isValid) return;
|
||||
pos -= pos % wordSize;
|
||||
|
||||
while (n > 0) {
|
||||
if (wordSize == 1) b = #if js buf[pos] #else buf.get(pos) #end - bitSize;
|
||||
else {
|
||||
while (w < buffer.bitsPerSample) {
|
||||
b |= #if js buf[pos] #else buf.get(pos) #end << w;
|
||||
w += 8;
|
||||
pos++;
|
||||
}
|
||||
if (b > bitSize) b -= bitUnsignedSize;
|
||||
}
|
||||
((c % 2 == 0) ? (if (__leftPeak < b) __leftPeak = b) : (if (__rightPeak < b) __rightPeak = b));
|
||||
|
||||
if (pos >= size) {
|
||||
if (!backend.streamed || ++i >= backend.bufferDatas.length) break;
|
||||
buf = backend.bufferDatas[i] #if !js .buffer #end;
|
||||
pos = 0;
|
||||
}
|
||||
|
||||
if (++c > buffer.channels) {
|
||||
c = 0;
|
||||
n--;
|
||||
}
|
||||
w = b = 0;
|
||||
}
|
||||
|
||||
if (buffer.channels == 1) __rightPeak = __leftPeak / bitSize;
|
||||
else {
|
||||
__leftPeak = __leftPeak / bitSize;
|
||||
__rightPeak = __rightPeak / bitSize;
|
||||
}
|
||||
#end
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@:noCompletion private function __initAudioSource(source:#if lime AudioSource #else Dynamic #end):Void
|
||||
|
||||
@@ -248,8 +248,7 @@ class Assets
|
||||
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);
|
||||
if (vorbisFile != null) return Sound.fromAudioBuffer(AudioBuffer.fromVorbisFile(vorbisFile));
|
||||
}
|
||||
#end
|
||||
return getSound(id, useCache);
|
||||
|
||||
Reference in New Issue
Block a user