forked from LeenkxTeam/LNXSDK
161 lines
4.5 KiB
Haxe
161 lines
4.5 KiB
Haxe
package kha.audio2.ogg.vorbis;
|
|
|
|
import haxe.io.BytesOutput;
|
|
import haxe.io.Output;
|
|
import haxe.io.StringInput;
|
|
import kha.audio2.ogg.tools.Mdct;
|
|
import kha.audio2.ogg.vorbis.data.Floor;
|
|
import kha.audio2.ogg.vorbis.data.Mapping;
|
|
import kha.audio2.ogg.vorbis.data.Mode;
|
|
import kha.audio2.ogg.vorbis.data.Header;
|
|
import kha.audio2.ogg.vorbis.VorbisDecodeState;
|
|
import haxe.ds.Vector;
|
|
import haxe.io.Bytes;
|
|
import haxe.io.BytesInput;
|
|
import haxe.io.Eof;
|
|
import haxe.io.Input;
|
|
import haxe.PosInfos;
|
|
|
|
#if sys
|
|
import sys.FileSystem;
|
|
import sys.io.File;
|
|
import sys.io.FileInput;
|
|
#end
|
|
|
|
/**
|
|
* public domain ogg reader.
|
|
* @author shohei909
|
|
*/
|
|
class Reader {
|
|
public var decoder(default, null):VorbisDecoder;
|
|
|
|
public var header(get, never):Header;
|
|
function get_header():Header {
|
|
return decoder.header;
|
|
}
|
|
|
|
public var totalSample(get, never):Int;
|
|
function get_totalSample():Int {
|
|
return decoder.totalSample;
|
|
}
|
|
|
|
public var totalMillisecond(get, never):Float;
|
|
|
|
function get_totalMillisecond():Float {
|
|
return sampleToMillisecond(decoder.totalSample);
|
|
}
|
|
|
|
public var currentSample(get, set):Int;
|
|
function get_currentSample():Int {
|
|
return decoder.currentSample;
|
|
}
|
|
|
|
function set_currentSample(value:Int):Int {
|
|
decoder.seek(seekFunc, inputLength, value);
|
|
return decoder.currentSample;
|
|
}
|
|
|
|
public var currentMillisecond(get, set):Float;
|
|
|
|
function get_currentMillisecond():Float
|
|
{
|
|
return sampleToMillisecond(currentSample);
|
|
}
|
|
|
|
function set_currentMillisecond(value:Float):Float {
|
|
currentSample = millisecondToSample(value);
|
|
return currentMillisecond;
|
|
}
|
|
|
|
public var loopStart:Null<Int>;
|
|
public var loopLength:Null<Int>;
|
|
|
|
var seekFunc:Int->Void;
|
|
var inputLength:Int;
|
|
|
|
function new (input:Input, seekFunc:Int->Void, inputLength:Int) {
|
|
this.seekFunc = seekFunc;
|
|
this.inputLength = inputLength;
|
|
decoder = VorbisDecoder.start(input);
|
|
decoder.setupSampleNumber(seekFunc, inputLength);
|
|
loopStart = header.comment.loopStart;
|
|
loopLength = header.comment.loopLength;
|
|
}
|
|
|
|
public static function openFromBytes(bytes:Bytes) {
|
|
var input = new BytesInput(bytes);
|
|
return new Reader(input, seekBytes.bind(input), bytes.length);
|
|
}
|
|
|
|
static function seekBytes(bytes:BytesInput, pos:Int) {
|
|
bytes.position = pos;
|
|
}
|
|
|
|
#if sys
|
|
public static function openFromFile(fileName:String):Reader {
|
|
var file = File.read(fileName, true);
|
|
var stat = FileSystem.stat(fileName);
|
|
return new Reader(file, file.seek.bind(_, SeekBegin), stat.size);
|
|
}
|
|
#end
|
|
|
|
public static function readAll(bytes:Bytes, output:Output, useFloat:Bool = false):Header {
|
|
var input = new BytesInput(bytes);
|
|
var decoder = VorbisDecoder.start(input);
|
|
decoder.setupSampleNumber(seekBytes.bind(input), bytes.length);
|
|
var header = decoder.header;
|
|
var count = 0;
|
|
var bufferSize = 4096;
|
|
var buffer = new kha.arrays.Float32Array(bufferSize * header.channel);
|
|
while (true) {
|
|
var n = decoder.read(buffer, bufferSize, header.channel, header.sampleRate, useFloat);
|
|
for (i in 0...n * header.channel) {
|
|
output.writeFloat(buffer[i]);
|
|
}
|
|
if (n == 0) { break; }
|
|
count += n;
|
|
}
|
|
return decoder.header;
|
|
}
|
|
|
|
public function read(output:kha.arrays.Float32Array, ?samples:Int, ?channels:Int, ?sampleRate:Int, useFloat:Bool = false) {
|
|
decoder.ensurePosition(seekFunc);
|
|
|
|
if (samples == null) {
|
|
samples = decoder.totalSample;
|
|
}
|
|
if (channels == null) {
|
|
channels = header.channel;
|
|
}
|
|
if (sampleRate == null) {
|
|
sampleRate = header.sampleRate;
|
|
}
|
|
return decoder.read(output, samples, channels, sampleRate, useFloat);
|
|
}
|
|
|
|
public function clone():Reader {
|
|
var reader = Type.createEmptyInstance(Reader);
|
|
reader.seekFunc = seekFunc;
|
|
reader.inputLength = inputLength;
|
|
reader.decoder = decoder.clone(seekFunc);
|
|
reader.loopStart = loopStart;
|
|
reader.loopLength = loopLength;
|
|
return reader;
|
|
}
|
|
|
|
|
|
public inline function sampleToMillisecond(samples:Int) {
|
|
return samples / header.sampleRate * 1000;
|
|
}
|
|
|
|
public inline function millisecondToSample(millseconds:Float) {
|
|
return Math.floor(millseconds / 1000 * header.sampleRate);
|
|
}
|
|
}
|
|
|
|
private typedef InitData = {
|
|
input:Input,
|
|
seekFunc:Int->Void,
|
|
inputLength:Int,
|
|
}
|