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,
 | 
						|
}
 |