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