161 lines
4.5 KiB
Haxe
Raw Normal View History

2025-01-22 16:18:30 +01:00
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,
}