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