858 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			858 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								package kha.audio2.ogg.vorbis;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import haxe.ds.Vector;
							 | 
						||
| 
								 | 
							
								import haxe.Int64;
							 | 
						||
| 
								 | 
							
								import haxe.io.Bytes;
							 | 
						||
| 
								 | 
							
								import haxe.io.Eof;
							 | 
						||
| 
								 | 
							
								import haxe.io.Input;
							 | 
						||
| 
								 | 
							
								import haxe.io.Output;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.tools.Crc32;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.tools.MathTools;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Codebook;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Floor.Floor1;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Header;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Mode;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Page;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.ProbedPage;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.ReaderError;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Page;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Residue;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.data.Setting;
							 | 
						||
| 
								 | 
							
								import kha.audio2.ogg.vorbis.VorbisDecoder.DecodeInitialResult;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
								 * ...
							 | 
						||
| 
								 | 
							
								 * @author shohei909
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								class VorbisDecodeState
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								    public static inline var INVALID_BITS = -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public var page(default, null):Page;
							 | 
						||
| 
								 | 
							
								    public var eof(default, null):Bool;
							 | 
						||
| 
								 | 
							
								    public var pFirst(default, null):ProbedPage;
							 | 
						||
| 
								 | 
							
								    public var pLast(default, null):ProbedPage;
							 | 
						||
| 
								 | 
							
								    public var validBits(default, null):Int = 0;
							 | 
						||
| 
								 | 
							
								    public var inputPosition(default, null):Int;
							 | 
						||
| 
								 | 
							
								    public var input(default, null):Input;
							 | 
						||
| 
								 | 
							
								    public var discardSamplesDeferred:Int;
							 | 
						||
| 
								 | 
							
								    public var segments(default, null):Vector<Int>;
							 | 
						||
| 
								 | 
							
								    public var bytesInSeg:Int = 0; // uint8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // decode buffer
							 | 
						||
| 
								 | 
							
								    public var channelBuffers:Vector<Vector<Float>>; //var *[STB_VORBIS_MAX_CHANNELS];
							 | 
						||
| 
								 | 
							
								    public var channelBufferStart:Int;
							 | 
						||
| 
								 | 
							
								    public var channelBufferEnd:Int;
							 | 
						||
| 
								 | 
							
								    public var currentSample(default, null):Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public var previousWindow:Vector<Vector<Float>>; //var *[STB_VORBIS_MAX_CHANNELS];
							 | 
						||
| 
								 | 
							
								    public var previousLength:Int;
							 | 
						||
| 
								 | 
							
								    public var finalY:Vector<Array<Int>>; // [STB_VORBIS_MAX_CHANNELS];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var firstDecode:Bool = false;
							 | 
						||
| 
								 | 
							
								    var nextSeg:Int = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var acc:UInt;
							 | 
						||
| 
								 | 
							
								    var lastSeg:Bool;  // flag that we're on the last decodeState
							 | 
						||
| 
								 | 
							
								    var lastSegWhich:Int; // what was the decodeState number of the l1ast seg?
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var endSegWithKnownLoc:Int;
							 | 
						||
| 
								 | 
							
								    var knownLocForPacket:Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var error:ReaderError;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var currentLoc:Int; //uint32  sample location of next frame to decode
							 | 
						||
| 
								 | 
							
								    var currentLocValid:Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var firstAudioPageOffset:UInt;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function new(input:Input)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        this.input = input;
							 | 
						||
| 
								 | 
							
								        inputPosition = 0;
							 | 
						||
| 
								 | 
							
								        page = new Page();
							 | 
						||
| 
								 | 
							
								        Crc32.init();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function setup(loc0:Int, loc1:Int) {
							 | 
						||
| 
								 | 
							
								        var segmentCount = readByte();
							 | 
						||
| 
								 | 
							
								        this.segments = read(segmentCount);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // assume we Don't_ know any the sample position of any segments
							 | 
						||
| 
								 | 
							
								        this.endSegWithKnownLoc = -2;
							 | 
						||
| 
								 | 
							
								        if (loc0 != 0xFFFFFFFF || loc1 != 0xFFFFFFFF) {
							 | 
						||
| 
								 | 
							
								            var i:Int = segmentCount - 1;
							 | 
						||
| 
								 | 
							
								            while (i >= 0) {
							 | 
						||
| 
								 | 
							
								                if (segments.get(i) < 255) {
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                if (i >= 0) {
							 | 
						||
| 
								 | 
							
								                    this.endSegWithKnownLoc = i;
							 | 
						||
| 
								 | 
							
								                    this.knownLocForPacket = loc0;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                i--;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (firstDecode) {
							 | 
						||
| 
								 | 
							
								            var i:Int = 0;
							 | 
						||
| 
								 | 
							
								            var len:Int = 0;
							 | 
						||
| 
								 | 
							
								            var p = new ProbedPage();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            for (i in 0...segmentCount) {
							 | 
						||
| 
								 | 
							
								                len += segments.get(i);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            len += 27 + segmentCount;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            p.pageStart = firstAudioPageOffset;
							 | 
						||
| 
								 | 
							
								            p.pageEnd = p.pageStart + len;
							 | 
						||
| 
								 | 
							
								            p.firstDecodedSample = 0;
							 | 
						||
| 
								 | 
							
								            p.lastDecodedSample = loc0;
							 | 
						||
| 
								 | 
							
								            pFirst = p;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        nextSeg = 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function clone(seekFunc:Int->Void)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        var state = Type.createEmptyInstance(VorbisDecodeState);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        seekFunc(inputPosition);
							 | 
						||
| 
								 | 
							
								        state.input = input;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // primitive
							 | 
						||
| 
								 | 
							
								        state.eof = eof;
							 | 
						||
| 
								 | 
							
								        state.validBits = validBits;
							 | 
						||
| 
								 | 
							
								        state.discardSamplesDeferred = discardSamplesDeferred;
							 | 
						||
| 
								 | 
							
								        state.firstDecode = firstDecode;
							 | 
						||
| 
								 | 
							
								        state.nextSeg = nextSeg;
							 | 
						||
| 
								 | 
							
								        state.bytesInSeg = bytesInSeg;
							 | 
						||
| 
								 | 
							
								        state.acc = state.acc;
							 | 
						||
| 
								 | 
							
								        state.lastSeg = lastSeg;
							 | 
						||
| 
								 | 
							
								        state.lastSegWhich = lastSegWhich;
							 | 
						||
| 
								 | 
							
								        state.currentLoc = currentLoc;
							 | 
						||
| 
								 | 
							
								        state.currentLocValid = currentLocValid;
							 | 
						||
| 
								 | 
							
								        state.inputPosition = inputPosition;
							 | 
						||
| 
								 | 
							
								        state.firstAudioPageOffset = firstAudioPageOffset;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // sharrow copy
							 | 
						||
| 
								 | 
							
								        state.error = error;
							 | 
						||
| 
								 | 
							
								        state.segments = segments;
							 | 
						||
| 
								 | 
							
								        state.pFirst = pFirst;
							 | 
						||
| 
								 | 
							
								        state.pLast = pLast;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // deep copy
							 | 
						||
| 
								 | 
							
								        state.page = page.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return state;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // nextSegment
							 | 
						||
| 
								 | 
							
								    public function next():Int {
							 | 
						||
| 
								 | 
							
								        if (lastSeg) {
							 | 
						||
| 
								 | 
							
								            return 0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (nextSeg == -1) {
							 | 
						||
| 
								 | 
							
								            lastSegWhich = segments.length - 1; // in case startPage fails
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            try {
							 | 
						||
| 
								 | 
							
								                page.start(this);
							 | 
						||
| 
								 | 
							
								            } catch(e:ReaderError) {
							 | 
						||
| 
								 | 
							
								                lastSeg = true;
							 | 
						||
| 
								 | 
							
								                error = e;
							 | 
						||
| 
								 | 
							
								                return 0;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if ((page.flag & PageFlag.CONTINUED_PACKET) == 0) {
							 | 
						||
| 
								 | 
							
								                throw new ReaderError(ReaderErrorType.CONTINUED_PACKET_FLAG_INVALID);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var len = segments.get(nextSeg++);
							 | 
						||
| 
								 | 
							
								        if (len < 255) {
							 | 
						||
| 
								 | 
							
								            lastSeg = true;
							 | 
						||
| 
								 | 
							
								            lastSegWhich = nextSeg - 1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (nextSeg >= segments.length) {
							 | 
						||
| 
								 | 
							
								            nextSeg = -1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        VorbisTools.assert(bytesInSeg == 0);
							 | 
						||
| 
								 | 
							
								        bytesInSeg = len;
							 | 
						||
| 
								 | 
							
								        return len;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function startPacket() {
							 | 
						||
| 
								 | 
							
								        while (nextSeg == -1) {
							 | 
						||
| 
								 | 
							
								            page.start(this);
							 | 
						||
| 
								 | 
							
								            if ((page.flag & PageFlag.CONTINUED_PACKET) != 0) {
							 | 
						||
| 
								 | 
							
								                throw new ReaderError(ReaderErrorType.MISSING_CAPTURE_PATTERN);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        lastSeg = false;
							 | 
						||
| 
								 | 
							
								        validBits = 0;
							 | 
						||
| 
								 | 
							
								        bytesInSeg = 0;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function maybeStartPacket():Bool
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (nextSeg == -1) {
							 | 
						||
| 
								 | 
							
								            var eof = false;
							 | 
						||
| 
								 | 
							
								            var x = try {
							 | 
						||
| 
								 | 
							
								                readByte();
							 | 
						||
| 
								 | 
							
								            } catch (e:Eof) {
							 | 
						||
| 
								 | 
							
								                eof = true;
							 | 
						||
| 
								 | 
							
								                0;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (eof) {
							 | 
						||
| 
								 | 
							
								                return false; // EOF at page boundary is not an error!
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            if (x != 0x4f || readByte() != 0x67 || readByte() != 0x67 || readByte() != 0x53) {
							 | 
						||
| 
								 | 
							
								                throw new ReaderError(ReaderErrorType.MISSING_CAPTURE_PATTERN);
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            page.startWithoutCapturePattern(this);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        startPacket();
							 | 
						||
| 
								 | 
							
								        return true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    // public inline function readBits(n:Int):Int
							 | 
						||
| 
								 | 
							
								    public function readBits(n:Int):Int // Kha: reduce output size
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (validBits < 0) {
							 | 
						||
| 
								 | 
							
								            return 0;
							 | 
						||
| 
								 | 
							
								        } else if (validBits < n) {
							 | 
						||
| 
								 | 
							
								            if (n > 24) {
							 | 
						||
| 
								 | 
							
								                // the accumulator technique below would not work correctly in this case
							 | 
						||
| 
								 | 
							
								                return readBits(24) + ((readBits(n - 24) << 24));
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                if (validBits == 0) {
							 | 
						||
| 
								 | 
							
								                    acc = 0;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                do {
							 | 
						||
| 
								 | 
							
								                    if (bytesInSeg == 0 && (lastSeg || next() == 0)) {
							 | 
						||
| 
								 | 
							
								                        validBits = INVALID_BITS;
							 | 
						||
| 
								 | 
							
								                        break;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        bytesInSeg--;
							 | 
						||
| 
								 | 
							
								                        acc += (readByte() << validBits);
							 | 
						||
| 
								 | 
							
								                        validBits += 8;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                } while (validBits < n);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                if (validBits < 0) {
							 | 
						||
| 
								 | 
							
								                    return 0;
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    var z = acc & ((1 << n) - 1);
							 | 
						||
| 
								 | 
							
								                    acc >>>= n;
							 | 
						||
| 
								 | 
							
								                    validBits -= n;
							 | 
						||
| 
								 | 
							
								                    return z;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            var z = acc & ((1 << n) - 1);
							 | 
						||
| 
								 | 
							
								            acc >>>= n;
							 | 
						||
| 
								 | 
							
								            validBits -= n;
							 | 
						||
| 
								 | 
							
								            return z;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    inline function readPacketRaw():Int {
							 | 
						||
| 
								 | 
							
								        return if (bytesInSeg == 0 && (lastSeg || next() == 0)) {  // CLANG!
							 | 
						||
| 
								 | 
							
								            VorbisTools.EOP;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            //VorbisTools.assert(bytesInSeg > 0);
							 | 
						||
| 
								 | 
							
								            bytesInSeg--;
							 | 
						||
| 
								 | 
							
								            readByte();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function readPacket():Int
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        var x = readPacketRaw();
							 | 
						||
| 
								 | 
							
								        validBits = 0;
							 | 
						||
| 
								 | 
							
								        return x;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function flushPacket():Void {
							 | 
						||
| 
								 | 
							
								        while (bytesInSeg != 0 || (!lastSeg && next() != 0)) {
							 | 
						||
| 
								 | 
							
								            bytesInSeg--;
							 | 
						||
| 
								 | 
							
								            readByte();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function vorbisValidate() {
							 | 
						||
| 
								 | 
							
								        var header = Bytes.alloc(6);
							 | 
						||
| 
								 | 
							
								        for (i in 0...6) {
							 | 
						||
| 
								 | 
							
								            header.set(i, readPacket());
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (header.toString() != "vorbis") {
							 | 
						||
| 
								 | 
							
								            throw new ReaderError(ReaderErrorType.INVALID_SETUP, "vorbis header");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function firstPageValidate()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (segments.length != 1) {
							 | 
						||
| 
								 | 
							
								            throw new ReaderError(INVALID_FIRST_PAGE, "segmentCount");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (segments.get(0) != 30) {
							 | 
						||
| 
								 | 
							
								            throw new ReaderError(INVALID_FIRST_PAGE, "decodeState head");
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function startFirstDecode()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        firstAudioPageOffset = inputPosition;
							 | 
						||
| 
								 | 
							
								        firstDecode = true;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function capturePattern()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (readByte() != 0x4f || readByte() != 0x67 || readByte() != 0x67 || readByte() != 0x53) {
							 | 
						||
| 
								 | 
							
								            throw new ReaderError(ReaderErrorType.MISSING_CAPTURE_PATTERN);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    inline function skip(len:Int)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        read(len);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function prepHuffman()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (validBits <= 24) {
							 | 
						||
| 
								 | 
							
								            if (validBits == 0) {
							 | 
						||
| 
								 | 
							
								                acc = 0;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            do {
							 | 
						||
| 
								 | 
							
								                if (bytesInSeg == 0 && (lastSeg || next() == 0)) {  // CLANG!
							 | 
						||
| 
								 | 
							
								                    return;
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    bytesInSeg--;
							 | 
						||
| 
								 | 
							
								                    acc += readByte() << validBits;
							 | 
						||
| 
								 | 
							
								                    validBits += 8;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            } while (validBits <= 24);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function decode(c:Codebook):Int {
							 | 
						||
| 
								 | 
							
								        var val = decodeRaw(c);
							 | 
						||
| 
								 | 
							
								        if (c.sparse) {
							 | 
						||
| 
								 | 
							
								            val = c.sortedValues[val];
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return val;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function decodeRaw(c:Codebook)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        if (validBits < Setting.FAST_HUFFMAN_LENGTH){
							 | 
						||
| 
								 | 
							
								            prepHuffman();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // fast huffman table lookup
							 | 
						||
| 
								 | 
							
								        var i = c.fastHuffman[acc & Setting.FAST_HUFFMAN_TABLE_MASK];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return if (i >= 0) {
							 | 
						||
| 
								 | 
							
								            var l = c.codewordLengths[i];
							 | 
						||
| 
								 | 
							
								            acc >>>= l;
							 | 
						||
| 
								 | 
							
								            validBits -= l;
							 | 
						||
| 
								 | 
							
								            if (validBits < 0) {
							 | 
						||
| 
								 | 
							
								                validBits = 0;
							 | 
						||
| 
								 | 
							
								                -1;
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                i;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            decodeScalarRaw(c);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function isLastByte()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        return bytesInSeg == 0 && lastSeg;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function finishDecodePacket(previousLength:Int, n:Int, r:DecodeInitialResult)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        var left = r.left.start;
							 | 
						||
| 
								 | 
							
								        var currentLocValid = false;
							 | 
						||
| 
								 | 
							
								        var n2 = n >> 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (firstDecode) {
							 | 
						||
| 
								 | 
							
								            // assume we start so first non-discarded sample is sample 0
							 | 
						||
| 
								 | 
							
								            // this isn't to spec, but spec would require us to read ahead
							 | 
						||
| 
								 | 
							
								            // and decode the size of all current frames--could be done,
							 | 
						||
| 
								 | 
							
								            // but presumably it's not a commonly used feature
							 | 
						||
| 
								 | 
							
								            currentLoc = -n2; // start of first frame is positioned for discard
							 | 
						||
| 
								 | 
							
								            // we might have to discard samples "from" the next frame too,
							 | 
						||
| 
								 | 
							
								            // if we're lapping a large block then a small at the start?
							 | 
						||
| 
								 | 
							
								            discardSamplesDeferred = n - r.right.end;
							 | 
						||
| 
								 | 
							
								            currentLocValid = true;
							 | 
						||
| 
								 | 
							
								            firstDecode = false;
							 | 
						||
| 
								 | 
							
								        } else if (discardSamplesDeferred != 0) {
							 | 
						||
| 
								 | 
							
								            r.left.start += discardSamplesDeferred;
							 | 
						||
| 
								 | 
							
								            left = r.left.start;
							 | 
						||
| 
								 | 
							
								            discardSamplesDeferred = 0;
							 | 
						||
| 
								 | 
							
								        } else if (previousLength == 0 && currentLocValid) {
							 | 
						||
| 
								 | 
							
								            // we're recovering from a seek... that means we're going to discard
							 | 
						||
| 
								 | 
							
								            // the samples from this packet even though we know our position from
							 | 
						||
| 
								 | 
							
								            // the last page header, so we need to update the position based on
							 | 
						||
| 
								 | 
							
								            // the discarded samples here
							 | 
						||
| 
								 | 
							
								            // but wait, the code below is going to add this in itself even
							 | 
						||
| 
								 | 
							
								            // on a discard, so we don't need to do it here...
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // check if we have ogg information about the sample # for this packet
							 | 
						||
| 
								 | 
							
								        if (lastSegWhich == endSegWithKnownLoc) {
							 | 
						||
| 
								 | 
							
								            // if we have a valid current loc, and this is final:
							 | 
						||
| 
								 | 
							
								            if (currentLocValid && (page.flag & PageFlag.LAST_PAGE) != 0) {
							 | 
						||
| 
								 | 
							
								                var currentEnd = knownLocForPacket - (n - r.right.end);
							 | 
						||
| 
								 | 
							
								                // then let's infer the size of the (probably) short final frame
							 | 
						||
| 
								 | 
							
								                if (currentEnd < currentLoc + r.right.end) {
							 | 
						||
| 
								 | 
							
								                    var len = if (currentEnd < currentLoc) {
							 | 
						||
| 
								 | 
							
								                        // negative truncation, that's impossible!
							 | 
						||
| 
								 | 
							
								                        0;
							 | 
						||
| 
								 | 
							
								                    } else {
							 | 
						||
| 
								 | 
							
								                        currentEnd - currentLoc;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                    len += r.left.start;
							 | 
						||
| 
								 | 
							
								                    currentLoc += len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    return {
							 | 
						||
| 
								 | 
							
								                        len : len,
							 | 
						||
| 
								 | 
							
								                        left : left,
							 | 
						||
| 
								 | 
							
								                        right : r.right.start,
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            // otherwise, just set our sample loc
							 | 
						||
| 
								 | 
							
								            // guess that the ogg granule pos refers to the Middle_ of the
							 | 
						||
| 
								 | 
							
								            // last frame?
							 | 
						||
| 
								 | 
							
								            // set currentLoc to the position of leftStart
							 | 
						||
| 
								 | 
							
								            currentLoc = knownLocForPacket - (n2-r.left.start);
							 | 
						||
| 
								 | 
							
								            currentLocValid = true;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (currentLocValid) {
							 | 
						||
| 
								 | 
							
								            currentLoc += (r.right.start - r.left.start);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // if (alloc.allocBuffer)
							 | 
						||
| 
								 | 
							
								        //assert(alloc.allocBufferLengthInBytes == tempOffset);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return {
							 | 
						||
| 
								 | 
							
								            len : r.right.end,
							 | 
						||
| 
								 | 
							
								            left : left,
							 | 
						||
| 
								 | 
							
								            right : r.right.start,
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function readInt32():Int
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        inputPosition += 4;
							 | 
						||
| 
								 | 
							
								        return input.readInt32();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function readByte():Int
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        inputPosition += 1;
							 | 
						||
| 
								 | 
							
								        return input.readByte();
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function read(n:Int):Vector<Int> {
							 | 
						||
| 
								 | 
							
								        inputPosition += n;
							 | 
						||
| 
								 | 
							
								        var vec = new Vector(n);
							 | 
						||
| 
								 | 
							
								        for (i in 0...n) {
							 | 
						||
| 
								 | 
							
								            vec[i] = input.readByte();
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        return vec;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function readBytes(n:Int):Bytes {
							 | 
						||
| 
								 | 
							
								        inputPosition += n;
							 | 
						||
| 
								 | 
							
								        return input.read(n);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function readString(n:Int):String
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        inputPosition += n;
							 | 
						||
| 
								 | 
							
								        return input.readString(n);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function getSampleNumber(seekFunc:Int->Void, inputLength:UInt):Int {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // first, store the current decode position so we can restore it
							 | 
						||
| 
								 | 
							
								        var restoreOffset = inputPosition;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // now we want to seek back 64K from the end (the last page must
							 | 
						||
| 
								 | 
							
								        // be at most a little less than 64K, but let's allow a little slop)
							 | 
						||
| 
								 | 
							
								        var previousSafe = if (inputLength >= 65536 && inputLength - 65536 >= firstAudioPageOffset) {
							 | 
						||
| 
								 | 
							
								            inputLength - 65536;
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								            firstAudioPageOffset;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        setInputOffset(seekFunc, previousSafe);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // previousSafe is now our candidate 'earliest known place that seeking
							 | 
						||
| 
								 | 
							
								        // to will lead to the final page'
							 | 
						||
| 
								 | 
							
								        var end = 0;
							 | 
						||
| 
								 | 
							
								        var last = false;
							 | 
						||
| 
								 | 
							
								        switch (findPage(seekFunc, inputLength)) {
							 | 
						||
| 
								 | 
							
								            case Found(e, l):
							 | 
						||
| 
								 | 
							
								                end = e;
							 | 
						||
| 
								 | 
							
								                last = l;
							 | 
						||
| 
								 | 
							
								            case NotFound:
							 | 
						||
| 
								 | 
							
								                throw new ReaderError(ReaderErrorType.CANT_FIND_LAST_PAGE);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // check if there are more pages
							 | 
						||
| 
								 | 
							
								        var lastPageLoc = inputPosition;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // stop when the lastPage flag is set, not when we reach eof;
							 | 
						||
| 
								 | 
							
								        // this allows us to stop short of a 'fileSection' end without
							 | 
						||
| 
								 | 
							
								        // explicitly checking the length of the section
							 | 
						||
| 
								 | 
							
								        while (!last) {
							 | 
						||
| 
								 | 
							
								            setInputOffset(seekFunc, end);
							 | 
						||
| 
								 | 
							
								            switch (findPage(seekFunc, inputLength)) {
							 | 
						||
| 
								 | 
							
								                case Found(e, l):
							 | 
						||
| 
								 | 
							
								                    end = e;
							 | 
						||
| 
								 | 
							
								                    last = l;
							 | 
						||
| 
								 | 
							
								                case NotFound:
							 | 
						||
| 
								 | 
							
								                    // the last page we found didn't have the 'last page' flag
							 | 
						||
| 
								 | 
							
								                    // set. whoops!
							 | 
						||
| 
								 | 
							
								                    break;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            previousSafe = lastPageLoc + 1;
							 | 
						||
| 
								 | 
							
								            lastPageLoc = inputPosition;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        setInputOffset(seekFunc, lastPageLoc);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // parse the header
							 | 
						||
| 
								 | 
							
								        var vorbisHeader = read(6);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // extract the absolute granule position
							 | 
						||
| 
								 | 
							
								        var lo = readInt32();
							 | 
						||
| 
								 | 
							
								        var hi = readInt32();
							 | 
						||
| 
								 | 
							
								        if (lo == 0xffffffff && hi == 0xffffffff || hi > 0) {
							 | 
						||
| 
								 | 
							
								            throw new ReaderError(ReaderErrorType.CANT_FIND_LAST_PAGE);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        pLast = new ProbedPage();
							 | 
						||
| 
								 | 
							
								        pLast.pageStart = lastPageLoc;
							 | 
						||
| 
								 | 
							
								        pLast.pageEnd    = end;
							 | 
						||
| 
								 | 
							
								        pLast.lastDecodedSample = lo;
							 | 
						||
| 
								 | 
							
								        pLast.firstDecodedSample = null;
							 | 
						||
| 
								 | 
							
								        pLast.afterPreviousPageStart = previousSafe;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        setInputOffset(seekFunc, restoreOffset);
							 | 
						||
| 
								 | 
							
								        return lo;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function forcePageResync()
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        nextSeg = -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public inline function setInputOffset(seekFunc:Int->Void, n:Int)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        seekFunc(inputPosition = n);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function findPage(seekFunc:Int->Void, inputLength:Int):FindPageResult {
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								            while (true) {
							 | 
						||
| 
								 | 
							
								                var n = readByte();
							 | 
						||
| 
								 | 
							
								                if (n == 0x4f) { // page header
							 | 
						||
| 
								 | 
							
								                    var retryLoc = inputPosition;
							 | 
						||
| 
								 | 
							
								                    // check if we're off the end of a fileSection stream
							 | 
						||
| 
								 | 
							
								                    if (retryLoc - 25 > inputLength) {
							 | 
						||
| 
								 | 
							
								                        return FindPageResult.NotFound;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (readByte() != 0x67 || readByte() != 0x67 || readByte() != 0x53) {
							 | 
						||
| 
								 | 
							
								                        continue;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    var header = new Vector<UInt>(27);
							 | 
						||
| 
								 | 
							
								                    header[0] = 0x4f;
							 | 
						||
| 
								 | 
							
								                    header[1] = 0x67;
							 | 
						||
| 
								 | 
							
								                    header[2] = 0x67;
							 | 
						||
| 
								 | 
							
								                    header[3] = 0x53;
							 | 
						||
| 
								 | 
							
								                    for (i in 4...27) {
							 | 
						||
| 
								 | 
							
								                        header[i] = readByte();
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    if (header[4] != 0) {
							 | 
						||
| 
								 | 
							
								                        setInputOffset(seekFunc, retryLoc);
							 | 
						||
| 
								 | 
							
								                        continue;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    var goal:UInt = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24);
							 | 
						||
| 
								 | 
							
								                    for (i in 22...26) {
							 | 
						||
| 
								 | 
							
								                        header[i] = 0;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    var crc:UInt = 0;
							 | 
						||
| 
								 | 
							
								                    for (i in 0...27){
							 | 
						||
| 
								 | 
							
								                        crc = Crc32.update(crc, header[i]);
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    var len = 0;
							 | 
						||
| 
								 | 
							
								                    try {
							 | 
						||
| 
								 | 
							
								                        for (i in 0...header[26]) {
							 | 
						||
| 
								 | 
							
								                            var s = readByte();
							 | 
						||
| 
								 | 
							
								                            crc = Crc32.update(crc, s);
							 | 
						||
| 
								 | 
							
								                            len += s;
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                        for (i in 0...len) {
							 | 
						||
| 
								 | 
							
								                            crc = Crc32.update(crc, readByte());
							 | 
						||
| 
								 | 
							
								                        }
							 | 
						||
| 
								 | 
							
								                    } catch (e:Eof) {
							 | 
						||
| 
								 | 
							
								                        return FindPageResult.NotFound;
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    // finished parsing probable page
							 | 
						||
| 
								 | 
							
								                    if (crc == goal) {
							 | 
						||
| 
								 | 
							
								                        // we could now check that it's either got the last
							 | 
						||
| 
								 | 
							
								                        // page flag set, OR it's followed by the capture
							 | 
						||
| 
								 | 
							
								                        // pattern, but I guess TECHNICALLY you could have
							 | 
						||
| 
								 | 
							
								                        // a file with garbage between each ogg page and recover
							 | 
						||
| 
								 | 
							
								                        // from it automatically? So even though that paranoia
							 | 
						||
| 
								 | 
							
								                        // might decrease the chance of an invalid decode by
							 | 
						||
| 
								 | 
							
								                        // another 2^32, not worth it since it would hose those
							 | 
						||
| 
								 | 
							
								                        // invalid-but-useful files?
							 | 
						||
| 
								 | 
							
								                        var end = inputPosition;
							 | 
						||
| 
								 | 
							
								                        setInputOffset(seekFunc, retryLoc - 1);
							 | 
						||
| 
								 | 
							
								                        return FindPageResult.Found(end, (header[5] & 0x04 != 0));
							 | 
						||
| 
								 | 
							
								                    }
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        } catch (e:Eof) {
							 | 
						||
| 
								 | 
							
								            return FindPageResult.NotFound;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    public function analyzePage(seekFunc:Int->Void, h:Header)
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        var z:ProbedPage = new ProbedPage();
							 | 
						||
| 
								 | 
							
								        var packetType = new Vector<Bool>(255);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // record where the page starts
							 | 
						||
| 
								 | 
							
								        z.pageStart = inputPosition;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // parse the header
							 | 
						||
| 
								 | 
							
								        var pageHeader = read(27);
							 | 
						||
| 
								 | 
							
								        VorbisTools.assert(pageHeader.get(0) == 0x4f && pageHeader.get(1) == 0x67 && pageHeader.get(2) == 0x67 && pageHeader.get(3) == 0x53);
							 | 
						||
| 
								 | 
							
								        var lacing = read(pageHeader.get(26));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // determine the length of the payload
							 | 
						||
| 
								 | 
							
								        var len = 0;
							 | 
						||
| 
								 | 
							
								        for (i in 0...pageHeader.get(26)){
							 | 
						||
| 
								 | 
							
								            len += lacing.get(i);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // this implies where the page ends
							 | 
						||
| 
								 | 
							
								        z.pageEnd = z.pageStart + 27 + pageHeader.get(26) + len;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // read the last-decoded sample out of the data
							 | 
						||
| 
								 | 
							
								        z.lastDecodedSample = pageHeader.get(6) + (pageHeader.get(7) << 8) + (pageHeader.get(8) << 16) + (pageHeader.get(9) << 16);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if ((pageHeader.get(5) & 4) != 0) {
							 | 
						||
| 
								 | 
							
								            // if this is the last page, it's not possible to work
							 | 
						||
| 
								 | 
							
								            // backwards to figure out the first sample! whoops! fuck.
							 | 
						||
| 
								 | 
							
								            z.firstDecodedSample = null;
							 | 
						||
| 
								 | 
							
								            setInputOffset(seekFunc, z.pageStart);
							 | 
						||
| 
								 | 
							
								            return z;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // scan through the frames to determine the sample-count of each one...
							 | 
						||
| 
								 | 
							
								        // our goal is the sample # of the first fully-decoded sample on the
							 | 
						||
| 
								 | 
							
								        // page, which is the first decoded sample of the 2nd packet
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var numPacket = 0;
							 | 
						||
| 
								 | 
							
								        var packetStart = ((pageHeader.get(5) & 1) == 0);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var modeCount = h.modes.length;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for (i in 0...pageHeader.get(26)) {
							 | 
						||
| 
								 | 
							
								            if (packetStart) {
							 | 
						||
| 
								 | 
							
								                if (lacing.get(i) == 0) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    setInputOffset(seekFunc, z.pageStart);
							 | 
						||
| 
								 | 
							
								                    return null; // trying to read from zero-length packet
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                var n = readByte();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                // if bottom bit is non-zero, we've got corruption
							 | 
						||
| 
								 | 
							
								                if (n & 1 != 0) {
							 | 
						||
| 
								 | 
							
								                    setInputOffset(seekFunc, z.pageStart);
							 | 
						||
| 
								 | 
							
								                    return null;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                n >>= 1;
							 | 
						||
| 
								 | 
							
								                var b = MathTools.ilog(modeCount - 1);
							 | 
						||
| 
								 | 
							
								                n &= (1 << b) - 1;
							 | 
						||
| 
								 | 
							
								                if (n >= modeCount) {
							 | 
						||
| 
								 | 
							
								                    setInputOffset(seekFunc, z.pageStart);
							 | 
						||
| 
								 | 
							
								                    return null;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                packetType[numPacket++] = h.modes[n].blockflag;
							 | 
						||
| 
								 | 
							
								                skip(lacing.get(i)-1);
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                skip(lacing.get(i));
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            packetStart = (lacing.get(i) < 255);
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // now that we know the sizes of all the pages, we can start determining
							 | 
						||
| 
								 | 
							
								        // how much sample data there is.
							 | 
						||
| 
								 | 
							
								        var samples = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // for the last packet, we step by its whole length, because the definition
							 | 
						||
| 
								 | 
							
								        // is that we encoded the end sample loc of the 'last packet completed',
							 | 
						||
| 
								 | 
							
								        // where 'completed' refers to packets being split, and we are left to guess
							 | 
						||
| 
								 | 
							
								        // what 'end sample loc' means. we assume it means ignoring the fact that
							 | 
						||
| 
								 | 
							
								        // the last half of the data is useless without windowing against the next
							 | 
						||
| 
								 | 
							
								        // packet... (so it's not REALLY complete in that sense)
							 | 
						||
| 
								 | 
							
								        if (numPacket > 1) {
							 | 
						||
| 
								 | 
							
								            samples += packetType[numPacket-1] ? h.blocksize1 : h.blocksize0;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var i = numPacket - 2;
							 | 
						||
| 
								 | 
							
								        while (i >= 1) {
							 | 
						||
| 
								 | 
							
								            i--;
							 | 
						||
| 
								 | 
							
								            // now, for this packet, how many samples do we have that
							 | 
						||
| 
								 | 
							
								            // do not overlap the following packet?
							 | 
						||
| 
								 | 
							
								            if (packetType[i]) {
							 | 
						||
| 
								 | 
							
								                if (packetType[i + 1]) {
							 | 
						||
| 
								 | 
							
								                    samples += h.blocksize1 >> 1;
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    samples += ((h.blocksize1 - h.blocksize0) >> 2) + (h.blocksize0 >> 1);
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            } else {
							 | 
						||
| 
								 | 
							
								                samples += h.blocksize0 >> 1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            i--;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        // now, at this point, we've rewound to the very beginning of the
							 | 
						||
| 
								 | 
							
								        // Second_ packet. if we entirely discard the first packet after
							 | 
						||
| 
								 | 
							
								        // a seek, this will be exactly the right sample number. HOWEVER!
							 | 
						||
| 
								 | 
							
								        // we can't as easily compute this number for the LAST page. The
							 | 
						||
| 
								 | 
							
								        // only way to get the sample offset of the LAST page is to use
							 | 
						||
| 
								 | 
							
								        // the end loc from the previous page. But what that returns us
							 | 
						||
| 
								 | 
							
								        // is _exactly_ the place where we get our first non-overlapped
							 | 
						||
| 
								 | 
							
								        // sample. (I think. Stupid spec for being ambiguous.) So for
							 | 
						||
| 
								 | 
							
								        // consistency it's better to do that here, too. However, that
							 | 
						||
| 
								 | 
							
								        // will then require us to NOT discard all of the first frame we
							 | 
						||
| 
								 | 
							
								        // decode, in some cases, which means an even weirder frame size
							 | 
						||
| 
								 | 
							
								        // and extra code. what a fucking pain.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // we're going to discard the first packet if we
							 | 
						||
| 
								 | 
							
								        // start the seek here, so we don't care about it. (we could actually
							 | 
						||
| 
								 | 
							
								        // do better; if the first packet is long, and the previous packet
							 | 
						||
| 
								 | 
							
								        // is short, there's actually data in the first half of the first
							 | 
						||
| 
								 | 
							
								        // packet that doesn't need discarding... but not worth paying the
							 | 
						||
| 
								 | 
							
								        // effort of tracking that of that here and in the seeking logic)
							 | 
						||
| 
								 | 
							
								        // except crap, if we infer it from the Previous_ packet's end
							 | 
						||
| 
								 | 
							
								        // location, we DO need to use that definition... and we HAVE to
							 | 
						||
| 
								 | 
							
								        // infer the start loc of the LAST packet from the previous packet's
							 | 
						||
| 
								 | 
							
								        // end location. fuck you, ogg vorbis.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        z.firstDecodedSample = z.lastDecodedSample - samples;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // restore file state to where we were
							 | 
						||
| 
								 | 
							
								        setInputOffset(seekFunc, z.pageStart);
							 | 
						||
| 
								 | 
							
								        return z;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function decodeScalarRaw(c:Codebook):Int
							 | 
						||
| 
								 | 
							
								    {
							 | 
						||
| 
								 | 
							
								        prepHuffman();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        VorbisTools.assert(c.sortedCodewords != null || c.codewords != null);
							 | 
						||
| 
								 | 
							
								        // cases to use binary search: sortedCodewords && !codewords
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var codewordLengths = c.codewordLengths;
							 | 
						||
| 
								 | 
							
								        var codewords = c.codewords;
							 | 
						||
| 
								 | 
							
								        var sortedCodewords = c.sortedCodewords;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (c.entries > 8 ? (sortedCodewords != null) : codewords != null) {
							 | 
						||
| 
								 | 
							
								            // binary search
							 | 
						||
| 
								 | 
							
								            var code = VorbisTools.bitReverse(acc);
							 | 
						||
| 
								 | 
							
								            var x = 0;
							 | 
						||
| 
								 | 
							
								            var n = c.sortedEntries;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            while (n > 1) {
							 | 
						||
| 
								 | 
							
								                // invariant: sc[x] <= code < sc[x+n]
							 | 
						||
| 
								 | 
							
								                var m = x + (n >> 1);
							 | 
						||
| 
								 | 
							
								                if (sortedCodewords[m] <= code) {
							 | 
						||
| 
								 | 
							
								                    x = m;
							 | 
						||
| 
								 | 
							
								                    n -= (n>>1);
							 | 
						||
| 
								 | 
							
								                } else {
							 | 
						||
| 
								 | 
							
								                    n >>= 1;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // x is now the sorted index
							 | 
						||
| 
								 | 
							
								            if (!c.sparse) {
							 | 
						||
| 
								 | 
							
								                x = c.sortedValues[x];
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            // x is now sorted index if sparse, or symbol otherwise
							 | 
						||
| 
								 | 
							
								            var len = codewordLengths[x];
							 | 
						||
| 
								 | 
							
								            if (validBits >= len) {
							 | 
						||
| 
								 | 
							
								                acc >>>= len;
							 | 
						||
| 
								 | 
							
								                validBits -= len;
							 | 
						||
| 
								 | 
							
								                return x;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            validBits = 0;
							 | 
						||
| 
								 | 
							
								            return -1;
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        // if small, linear search
							 | 
						||
| 
								 | 
							
								        VorbisTools.assert(!c.sparse);
							 | 
						||
| 
								 | 
							
								        for (i in 0...c.entries) {
							 | 
						||
| 
								 | 
							
								            var cl = codewordLengths[i];
							 | 
						||
| 
								 | 
							
								            if (cl == Codebook.NO_CODE) {
							 | 
						||
| 
								 | 
							
								                continue;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            if (codewords[i] == (acc & ((1 << cl)-1))) {
							 | 
						||
| 
								 | 
							
								                if (validBits >= cl) {
							 | 
						||
| 
								 | 
							
								                    acc >>>= cl;
							 | 
						||
| 
								 | 
							
								                    validBits -= cl;
							 | 
						||
| 
								 | 
							
								                    return i;
							 | 
						||
| 
								 | 
							
								                }
							 | 
						||
| 
								 | 
							
								                validBits = 0;
							 | 
						||
| 
								 | 
							
								                return -1;
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        error = new ReaderError(INVALID_STREAM);
							 | 
						||
| 
								 | 
							
								        validBits = 0;
							 | 
						||
| 
								 | 
							
								        return -1;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								private enum FindPageResult {
							 | 
						||
| 
								 | 
							
								    Found(end:Int, last:Bool);
							 | 
						||
| 
								 | 
							
								    NotFound;
							 | 
						||
| 
								 | 
							
								}
							 |