forked from LeenkxTeam/LNXSDK
		
	
		
			
	
	
		
			785 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			785 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
|  | package kha.audio2.ogg.vorbis; | ||
|  | import haxe.ds.Vector; | ||
|  | import haxe.io.Bytes; | ||
|  | import haxe.io.BytesOutput; | ||
|  | import haxe.io.Input; | ||
|  | import haxe.io.Output; | ||
|  | import kha.audio2.ogg.tools.MathTools; | ||
|  | import kha.audio2.ogg.tools.Mdct; | ||
|  | 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.ProbedPage; | ||
|  | import kha.audio2.ogg.vorbis.data.ReaderError; | ||
|  | import kha.audio2.ogg.vorbis.VorbisDecodeState; | ||
|  | 
 | ||
|  | /** | ||
|  |  * ... | ||
|  |  * @author shohei909 | ||
|  |  */ | ||
|  | class VorbisDecoder | ||
|  | { | ||
|  |     var previousWindow:Vector<Vector<Float>>; //var *[STB_VORBIS_MAX_CHANNELS]; | ||
|  |     var previousLength:Int; | ||
|  |     var finalY:Vector<Array<Int>>; // [STB_VORBIS_MAX_CHANNELS]; | ||
|  | 
 | ||
|  |     // twiddle factors | ||
|  |     var a:Vector<Vector<Float>>; // var *  [2] | ||
|  |     var b:Vector<Vector<Float>>; // var *  [2] | ||
|  |     var c:Vector<Vector<Float>>; // var *  [2] | ||
|  |     var window:Vector<Vector<Float>>; //var * [2]; | ||
|  |     var bitReverseData:Vector<Vector<Int>>; //uint16 * [2] | ||
|  | 
 | ||
|  |     // decode buffer | ||
|  |     var channelBuffers:Vector<Vector<Float>>; //var *[STB_VORBIS_MAX_CHANNELS]; | ||
|  |     var channelBufferStart:Int; | ||
|  |     var channelBufferEnd:Int; | ||
|  | 
 | ||
|  |     public var header(default, null):Header; | ||
|  |     public var currentSample(default, null):Int; | ||
|  |     public var totalSample(default, null):Null<Int>; | ||
|  |     var decodeState:VorbisDecodeState; | ||
|  | 
 | ||
|  |     function new(header:Header, decodeState:VorbisDecodeState) { | ||
|  |         this.header = header; | ||
|  |         this.decodeState = decodeState; | ||
|  |         totalSample = null; | ||
|  |         currentSample = 0; | ||
|  | 
 | ||
|  |         //Channel | ||
|  |         previousLength = 0; | ||
|  | 
 | ||
|  |         channelBuffers = new Vector(header.channel); | ||
|  |         previousWindow = new Vector(header.channel); | ||
|  |         finalY = new Vector(header.channel); | ||
|  | 
 | ||
|  |         for (i in 0...header.channel) { | ||
|  |             channelBuffers[i] = VorbisTools.emptyFloatVector(header.blocksize1); | ||
|  |             previousWindow[i] = VorbisTools.emptyFloatVector(Std.int(header.blocksize1 / 2)); | ||
|  |             finalY[i] = new Array(); | ||
|  |         } | ||
|  | 
 | ||
|  |         a = new Vector(2); | ||
|  |         b = new Vector(2); | ||
|  |         c = new Vector(2); | ||
|  |         window = new Vector(2); | ||
|  |         bitReverseData = new Vector(2); | ||
|  |         initBlocksize(0, header.blocksize0); | ||
|  |         initBlocksize(1, header.blocksize1); | ||
|  |     } | ||
|  | 
 | ||
|  |     public static function start(input:Input) { | ||
|  |         var decodeState = new VorbisDecodeState(input); | ||
|  |         var header = Header.read(decodeState); | ||
|  |         var decoder = new VorbisDecoder(header, decodeState); | ||
|  |         decodeState.startFirstDecode(); | ||
|  |         decoder.pumpFirstFrame(); | ||
|  | 
 | ||
|  |         return decoder; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function read(output:kha.arrays.Float32Array, samples:Int, channels:Int, sampleRate:Int, useFloat:Bool) { | ||
|  |         if (sampleRate % header.sampleRate != 0) { | ||
|  |             throw 'Unsupported sampleRate : can\'t convert ${header.sampleRate} to $sampleRate'; | ||
|  |         } | ||
|  |         if (channels % header.channel != 0) { | ||
|  |             throw 'Unsupported channels : can\'t convert ${header.channel} to $channels'; | ||
|  |         } | ||
|  | 
 | ||
|  |         var sampleRepeat = Std.int(sampleRate / header.sampleRate); | ||
|  |         var channelRepeat = Std.int(channels / header.channel); | ||
|  | 
 | ||
|  |         var n = 0; | ||
|  |         var len = Math.floor(samples / sampleRepeat); | ||
|  |         if (totalSample != null && len > totalSample - currentSample) { | ||
|  |             len = totalSample - currentSample; | ||
|  |         } | ||
|  | 
 | ||
|  | 		var index = 0; | ||
|  |         while (n < len) { | ||
|  |             var k = channelBufferEnd - channelBufferStart; | ||
|  |             if (k >= len - n) k = len - n; | ||
|  |             for (j in channelBufferStart...(channelBufferStart + k)) { | ||
|  |                 for (sr in 0...sampleRepeat) { | ||
|  |                     for (i in 0...header.channel) { | ||
|  |                         for (cr in 0...channelRepeat) { | ||
|  |                             var value = channelBuffers[i][j]; | ||
|  |                             if (value > 1) { | ||
|  |                                 value = 1; | ||
|  |                             } else if (value < -1) { | ||
|  |                                 value = -1; | ||
|  |                             } | ||
|  | 
 | ||
|  |                             if (useFloat) { | ||
|  |                                 //output.writeFloat(value); | ||
|  | 								output[index] = value; | ||
|  | 								++index; | ||
|  |                             } else { | ||
|  |                                 //output.writeInt16(Math.floor(value * 0x7FFF)); | ||
|  |                             } | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  |             n += k; | ||
|  |             channelBufferStart += k; | ||
|  |             if (n == len || getFrameFloat() == 0) { | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         for (j in n...len) { | ||
|  |             for (sr in 0...sampleRepeat) { | ||
|  |                 for (i in 0...header.channel) { | ||
|  |                     for (cr in 0...channelRepeat) { | ||
|  |                         if (useFloat) { | ||
|  |                             //output.writeFloat(0); | ||
|  | 							output[index] = 0; | ||
|  | 							++index; | ||
|  |                         } else { | ||
|  |                             //output.writeInt16(0); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         currentSample += len; | ||
|  |         return len * sampleRepeat; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function skipSamples(len:Int) { | ||
|  |         var n = 0; | ||
|  |         if (totalSample != null && len > totalSample - currentSample) { | ||
|  |             len = totalSample - currentSample; | ||
|  |         } | ||
|  |         while (n < len) { | ||
|  |             var k = channelBufferEnd - channelBufferStart; | ||
|  |             if (k >= len - n) k = len - n; | ||
|  |             n += k; | ||
|  |             channelBufferStart += k; | ||
|  |             if (n == len || getFrameFloat() == 0) { | ||
|  |                 break; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         currentSample += len; | ||
|  |         return len; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function setupSampleNumber(seekFunc:Int->Void, inputLength:Int) { | ||
|  |         if (totalSample == null) { | ||
|  |             totalSample = decodeState.getSampleNumber(seekFunc, inputLength); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     public function seek(seekFunc:Int->Void, inputLength:UInt, sampleNumber:Int) { | ||
|  |         if (currentSample == sampleNumber) { | ||
|  |             return; | ||
|  |         } | ||
|  | 
 | ||
|  |         // do we know the location of the last page? | ||
|  |         if (totalSample == null) { | ||
|  |             setupSampleNumber(seekFunc, inputLength); | ||
|  |             if (totalSample == 0) { | ||
|  |                 throw new ReaderError(ReaderErrorType.CANT_FIND_LAST_PAGE); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         if (sampleNumber < 0) { | ||
|  |             sampleNumber = 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         var p0 = decodeState.pFirst; | ||
|  |         var p1 = decodeState.pLast; | ||
|  | 
 | ||
|  |         if (sampleNumber >= p1.lastDecodedSample) { | ||
|  |             sampleNumber = p1.lastDecodedSample - 1; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (sampleNumber < p0.lastDecodedSample) { | ||
|  |             seekFrameFromPage(seekFunc, p0.pageStart, 0, sampleNumber); | ||
|  |         } else { | ||
|  |             var attempts = 0; | ||
|  | 
 | ||
|  |             while (p0.pageEnd < p1.pageStart) { | ||
|  | 
 | ||
|  | 
 | ||
|  |                 // copy these into local variables so we can tweak them | ||
|  |                 // if any are unknown | ||
|  |                 var startOffset:UInt = p0.pageEnd; | ||
|  |                 var endOffset:UInt = p1.afterPreviousPageStart; // an address known to seek to page p1 | ||
|  |                 var startSample = p0.lastDecodedSample; | ||
|  |                 var endSample = p1.lastDecodedSample; | ||
|  | 
 | ||
|  |                 // currently there is no such tweaking logic needed/possible? | ||
|  |                 if (startSample == null || endSample == null) { | ||
|  |                     throw new ReaderError(SEEK_FAILED); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // now we want to lerp between these for the target samples... | ||
|  | 
 | ||
|  |                 // step 1: we need to bias towards the page start... | ||
|  |                 if (startOffset + 4000 < endOffset) { | ||
|  |                     endOffset -= 4000; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 // now compute an interpolated search loc | ||
|  |                 var probe:UInt = startOffset + Math.floor((endOffset - startOffset) / (endSample - startSample) * (sampleNumber - startSample)); | ||
|  | 
 | ||
|  |                 // next we need to bias towards binary search... | ||
|  |                 // code is a little wonky to allow for full 32-bit unsigned values | ||
|  |                 if (attempts >= 4) { | ||
|  |                     var probe2:UInt = startOffset + ((endOffset - startOffset) >> 1); | ||
|  |                     probe = if (attempts >= 8) { | ||
|  |                         probe2; | ||
|  |                     } else if (probe < probe2) { | ||
|  |                         probe + ((probe2 - probe) >>> 1); | ||
|  |                     } else { | ||
|  |                         probe2 + ((probe - probe2) >>> 1); | ||
|  |                     } | ||
|  |                 } | ||
|  |                 ++attempts; | ||
|  |                 decodeState.setInputOffset(seekFunc, probe); | ||
|  | 
 | ||
|  |                 switch (decodeState.findPage(seekFunc, inputLength)) { | ||
|  |                     case NotFound: | ||
|  |                         throw new ReaderError(SEEK_FAILED); | ||
|  |                     case Found(_): | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var q:ProbedPage = decodeState.analyzePage(seekFunc, header); | ||
|  |                 if (q == null) { | ||
|  |                     throw new ReaderError(SEEK_FAILED); | ||
|  |                 } | ||
|  |                 q.afterPreviousPageStart = probe; | ||
|  | 
 | ||
|  |                 // it's possible we've just found the last page again | ||
|  |                 if (q.pageStart == p1.pageStart) { | ||
|  |                     p1 = q; | ||
|  |                     continue; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 if (sampleNumber < q.lastDecodedSample) { | ||
|  |                     p1 = q; | ||
|  |                 } else { | ||
|  |                     p0 = q; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             if (p0.lastDecodedSample <= sampleNumber && sampleNumber < p1.lastDecodedSample) { | ||
|  |                 seekFrameFromPage(seekFunc, p1.pageStart, p0.lastDecodedSample, sampleNumber); | ||
|  |             } else { | ||
|  |                 throw new ReaderError(SEEK_FAILED); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     public function seekFrameFromPage(seekFunc:Int->Void, pageStart:Int, firstSample:Int, targetSample:Int) { | ||
|  |         var frame = 0; | ||
|  |         var frameStart:Int = firstSample; | ||
|  | 
 | ||
|  |         // firstSample is the sample # of the first sample that doesn't | ||
|  |         // overlap the previous page... note that this requires us to | ||
|  |         // Partially_ discard the first packet! bleh. | ||
|  |         decodeState.setInputOffset(seekFunc, pageStart); | ||
|  |         decodeState.forcePageResync(); | ||
|  | 
 | ||
|  |         // frame start is where the previous packet's last decoded sample | ||
|  |         // was, which corresponds to leftEnd... EXCEPT if the previous | ||
|  |         // packet was long and this packet is short? Probably a bug here. | ||
|  | 
 | ||
|  |         // now, we can start decoding frames... we'll only FAKE decode them, | ||
|  |         // until we find the frame that contains our sample; then we'll rewind, | ||
|  |         // and try again | ||
|  |         var leftEnd = 0; | ||
|  |         var leftStart = 0; | ||
|  | 
 | ||
|  |         var prevState = null; | ||
|  |         var lastState = null; | ||
|  | 
 | ||
|  |         while (true) { | ||
|  |             prevState = lastState; | ||
|  |             lastState = decodeState.clone(seekFunc); | ||
|  | 
 | ||
|  |             var initialResult = decodeInitial(); | ||
|  |             if (initialResult == null) { | ||
|  |                 lastState = prevState; | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             leftStart = initialResult.left.start; | ||
|  |             leftEnd = initialResult.left.end; | ||
|  | 
 | ||
|  |             var start = if (frame == 0) { | ||
|  |                 leftEnd; | ||
|  |             } else{ | ||
|  |                 leftStart; | ||
|  |             } | ||
|  | 
 | ||
|  |             // the window starts at leftStart; the last valid sample we generate | ||
|  |             // before the next frame's window start is rightStart-1 | ||
|  |             if (targetSample < frameStart + initialResult.right.start - start) { | ||
|  |                 break; | ||
|  |             } | ||
|  | 
 | ||
|  |             decodeState.flushPacket(); | ||
|  |             frameStart += initialResult.right.start - start; | ||
|  |             ++frame; | ||
|  |         } | ||
|  | 
 | ||
|  |         decodeState = lastState; | ||
|  |         seekFunc(decodeState.inputPosition); | ||
|  | 
 | ||
|  |         previousLength = 0; | ||
|  |         pumpFirstFrame(); | ||
|  | 
 | ||
|  |         currentSample = frameStart; | ||
|  |         skipSamples(targetSample - frameStart); | ||
|  |     } | ||
|  | 
 | ||
|  |     public function clone(seekFunc:Int->Void) { | ||
|  |         var decoder = Type.createEmptyInstance(VorbisDecoder); | ||
|  | 
 | ||
|  |         decoder.currentSample = currentSample; | ||
|  |         decoder.totalSample = totalSample; | ||
|  |         decoder.previousLength = previousLength; | ||
|  |         decoder.channelBufferStart = channelBufferStart; | ||
|  |         decoder.channelBufferEnd = channelBufferEnd; | ||
|  | 
 | ||
|  |         // sharrow copy | ||
|  |         decoder.a = a; | ||
|  |         decoder.b = b; | ||
|  |         decoder.c = c; | ||
|  |         decoder.window = window; | ||
|  |         decoder.bitReverseData = bitReverseData; | ||
|  |         decoder.header = header; | ||
|  | 
 | ||
|  |         // deep copy | ||
|  |         decoder.decodeState = decodeState.clone(seekFunc); | ||
|  |         decoder.channelBuffers = new Vector(header.channel); | ||
|  |         decoder.previousWindow = new Vector(header.channel); | ||
|  |         decoder.finalY = new Vector(header.channel); | ||
|  | 
 | ||
|  |         for (i in 0...header.channel) { | ||
|  |             decoder.channelBuffers[i] = VorbisTools.copyVector(channelBuffers[i]); | ||
|  |             decoder.previousWindow[i] = VorbisTools.copyVector(previousWindow[i]); | ||
|  |             decoder.finalY[i] = Lambda.array(finalY[i]); | ||
|  |         } | ||
|  | 
 | ||
|  |         return decoder; | ||
|  |     } | ||
|  | 
 | ||
|  |     public function ensurePosition(seekFunc:Int->Void) { | ||
|  |         seekFunc(decodeState.inputPosition); | ||
|  |     } | ||
|  | 
 | ||
|  |     function getFrameFloat() { | ||
|  |         var result = decodePacket(); | ||
|  |         if (result == null) { | ||
|  |             channelBufferStart = channelBufferEnd = 0; | ||
|  |             return 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         var len = finishFrame(result); | ||
|  | 
 | ||
|  |         channelBufferStart = result.left; | ||
|  |         channelBufferEnd = result.left + len; | ||
|  | 
 | ||
|  |         return len; | ||
|  |     } | ||
|  | 
 | ||
|  |     function pumpFirstFrame() { | ||
|  |         finishFrame(decodePacket()); | ||
|  |     } | ||
|  | 
 | ||
|  |     function finishFrame(r:DecodePacketResult):Int { | ||
|  |         var len = r.len; | ||
|  |         var right = r.right; | ||
|  |         var left = r.left; | ||
|  | 
 | ||
|  |         // we use right&left (the start of the right- and left-window sin()-regions) | ||
|  |         // to determine how much to return, rather than inferring from the rules | ||
|  |         // (same result, clearer code); 'left' indicates where our sin() window | ||
|  |         // starts, therefore where the previous window's right edge starts, and | ||
|  |         // therefore where to start mixing from the previous buffer. 'right' | ||
|  |         // indicates where our sin() ending-window starts, therefore that's where | ||
|  |         // we start saving, and where our returned-data ends. | ||
|  | 
 | ||
|  |         // mixin from previous window | ||
|  |         if (previousLength != 0) { | ||
|  |             var n = previousLength; | ||
|  |             var w = getWindow(n); | ||
|  |             for (i in 0...header.channel) { | ||
|  |                 var cb = channelBuffers[i]; | ||
|  |                 var pw = previousWindow[i]; | ||
|  |                 for (j in 0...n) { | ||
|  |                     cb[left+j] = cb[left+j] * w[j] + pw[j] * w[n-1-j]; | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         var prev = previousLength; | ||
|  | 
 | ||
|  |         // last half of this data becomes previous window | ||
|  |         previousLength = len - right; | ||
|  | 
 | ||
|  |         // @OPTIMIZE: could avoid this copy by double-buffering the | ||
|  |         // output (flipping previousWindow with channelBuffers), but | ||
|  |         // then previousWindow would have to be 2x as large, and | ||
|  |         // channelBuffers couldn't be temp mem (although they're NOT | ||
|  |         // currently temp mem, they could be (unless we want to level | ||
|  |         // performance by spreading out the computation)) | ||
|  |         for (i in 0...header.channel) { | ||
|  |             var pw = previousWindow[i]; | ||
|  |             var cb = channelBuffers[i]; | ||
|  |             for (j in 0...(len - right)) { | ||
|  |                 pw[j] = cb[right + j]; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         if (prev == 0) { | ||
|  |             // there was no previous packet, so this data isn't valid... | ||
|  |             // this isn't entirely true, only the would-have-overlapped data | ||
|  |             // isn't valid, but this seems to be what the spec requires | ||
|  |             return 0; | ||
|  |         } | ||
|  | 
 | ||
|  |         // truncate a short frame | ||
|  |         if (len < right) { | ||
|  |             right = len; | ||
|  |         } | ||
|  | 
 | ||
|  |         return right - left; | ||
|  |     } | ||
|  | 
 | ||
|  |     function getWindow(len:Int) | ||
|  |     { | ||
|  |         len <<= 1; | ||
|  |         return if (len == header.blocksize0) { | ||
|  |             window[0]; | ||
|  |         } else if (len == header.blocksize1) { | ||
|  |             window[1]; | ||
|  |         } else { | ||
|  |             VorbisTools.assert(false); | ||
|  |             null; | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     function initBlocksize(bs:Int, n:Int) | ||
|  |     { | ||
|  |         var n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; | ||
|  |         a[bs] = new Vector(n2); | ||
|  |         b[bs] = new Vector(n2); | ||
|  |         c[bs] = new Vector(n4); | ||
|  |         window[bs] = new Vector(n2); | ||
|  |         bitReverseData[bs] = new Vector(n8); | ||
|  | 
 | ||
|  |         VorbisTools.computeTwiddleFactors(n, a[bs], b[bs], c[bs]); | ||
|  |         VorbisTools.computeWindow(n, window[bs]); | ||
|  |         VorbisTools.computeBitReverse(n, bitReverseData[bs]); | ||
|  |     } | ||
|  | 
 | ||
|  |     function inverseMdct(buffer:Vector<Float>, n:Int, blocktype:Bool) { | ||
|  |         var bt = blocktype ? 1 : 0; | ||
|  |         Mdct.inverseTransform(buffer, n, a[bt], b[bt], c[bt], bitReverseData[bt]); | ||
|  |     } | ||
|  | 
 | ||
|  |     function decodePacket():DecodePacketResult | ||
|  |     { | ||
|  |         var result = decodeInitial(); | ||
|  |         if (result == null) { | ||
|  |             return null; | ||
|  |         } | ||
|  |         var rest = decodePacketRest(result); | ||
|  |         return rest; | ||
|  |     } | ||
|  | 
 | ||
|  |     function decodeInitial():DecodeInitialResult | ||
|  |     { | ||
|  |         channelBufferStart = channelBufferEnd = 0; | ||
|  | 
 | ||
|  |         do { | ||
|  |             if (!decodeState.maybeStartPacket()) { | ||
|  |                 return null; | ||
|  |             } | ||
|  | 
 | ||
|  |             // check packet type | ||
|  |             if (decodeState.readBits(1) != 0) { | ||
|  |                 while (VorbisTools.EOP != decodeState.readPacket()) {}; | ||
|  |                 continue; | ||
|  |             } | ||
|  |             break; | ||
|  |         } while (true); | ||
|  | 
 | ||
|  |         var i = decodeState.readBits(MathTools.ilog(header.modes.length - 1)); | ||
|  |         if (i == VorbisTools.EOP || i >= header.modes.length) { | ||
|  |             throw new ReaderError(ReaderErrorType.SEEK_FAILED); | ||
|  |         } | ||
|  | 
 | ||
|  |         var m = header.modes[i]; | ||
|  |         var n, prev, next; | ||
|  | 
 | ||
|  |         if (m.blockflag) { | ||
|  |             n = header.blocksize1; | ||
|  |             prev = decodeState.readBits(1); | ||
|  |             next = decodeState.readBits(1); | ||
|  |         } else { | ||
|  |             prev = next = 0; | ||
|  |             n = header.blocksize0; | ||
|  |         } | ||
|  | 
 | ||
|  |         // WINDOWING | ||
|  |         var windowCenter = n >> 1; | ||
|  | 
 | ||
|  |         return { | ||
|  |             mode : i, | ||
|  |             left : if (m.blockflag && prev == 0) { | ||
|  |                 start : (n - header.blocksize0) >> 2, | ||
|  |                 end : (n + header.blocksize0) >> 2, | ||
|  |             } else { | ||
|  |                 start : 0, | ||
|  |                 end : windowCenter, | ||
|  |             }, | ||
|  |             right : if (m.blockflag && next == 0) { | ||
|  |                 start : (n * 3 - header.blocksize0) >> 2, | ||
|  |                 end : (n * 3 + header.blocksize0) >> 2, | ||
|  |             } else { | ||
|  |                 start : windowCenter, | ||
|  |                 end : n, | ||
|  |             }, | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  | 
 | ||
|  |     function decodePacketRest(r:DecodeInitialResult):DecodePacketResult | ||
|  |     { | ||
|  |         var len = 0; | ||
|  |         var m = header.modes[r.mode]; | ||
|  | 
 | ||
|  |         var zeroChannel = new Vector<Bool>(256); | ||
|  |         var reallyZeroChannel = new Vector<Bool>(256); | ||
|  | 
 | ||
|  |         // WINDOWING | ||
|  | 
 | ||
|  |         var n = m.blockflag ? header.blocksize1 : header.blocksize0; | ||
|  |         var map = header.mapping[m.mapping]; | ||
|  | 
 | ||
|  |         // FLOORS | ||
|  |         var n2 = n >> 1; | ||
|  |         VorbisTools.stbProf(1); | ||
|  |         var rangeList = [256, 128, 86, 64]; | ||
|  |         var codebooks = header.codebooks; | ||
|  | 
 | ||
|  |         for (i in 0...header.channel) { | ||
|  |             var s = map.chan[i].mux; | ||
|  |             zeroChannel[i] = false; | ||
|  |             var floor = header.floorConfig[map.submapFloor[s]]; | ||
|  |             if (floor.type == 0) { | ||
|  |                 throw new ReaderError(INVALID_STREAM); | ||
|  |             } else { | ||
|  |                 var g:Floor1 = floor.floor1; | ||
|  |                 if (decodeState.readBits(1) != 0) { | ||
|  |                     var fy = new Array<Int>(); | ||
|  |                     var step2Flag = new Vector<Bool>(256); | ||
|  |                     var range = rangeList[g.floor1Multiplier-1]; | ||
|  |                     var offset = 2; | ||
|  |                     fy = finalY[i]; | ||
|  |                     fy[0] = decodeState.readBits(MathTools.ilog(range)-1); | ||
|  |                     fy[1] = decodeState.readBits(MathTools.ilog(range)-1); | ||
|  |                     for (j in 0...g.partitions) { | ||
|  |                         var pclass = g.partitionClassList[j]; | ||
|  |                         var cdim = g.classDimensions[pclass]; | ||
|  |                         var cbits = g.classSubclasses[pclass]; | ||
|  |                         var csub = (1 << cbits) - 1; | ||
|  |                         var cval = 0; | ||
|  |                         if (cbits != 0) { | ||
|  |                             var c = codebooks[g.classMasterbooks[pclass]]; | ||
|  |                             cval = decodeState.decode(c); | ||
|  |                         } | ||
|  | 
 | ||
|  |                         var books = g.subclassBooks[pclass]; | ||
|  |                         for (k in 0...cdim) { | ||
|  |                             var book = books[cval & csub]; | ||
|  |                             cval >>= cbits; | ||
|  |                             fy[offset++] = if (book >= 0) { | ||
|  |                                 decodeState.decode(codebooks[book]); | ||
|  |                             } else { | ||
|  |                                 0; | ||
|  |                             } | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                     if (decodeState.validBits == VorbisDecodeState.INVALID_BITS) { | ||
|  |                         zeroChannel[i] = true; | ||
|  |                         continue; | ||
|  |                     } | ||
|  | 
 | ||
|  |                     step2Flag[0] = step2Flag[1] = true; | ||
|  |                     var naighbors = g.neighbors; | ||
|  |                     var xlist = g.xlist; | ||
|  |                     for (j in 2...g.values) { | ||
|  |                         var low = naighbors[j][0]; | ||
|  |                         var high = naighbors[j][1]; | ||
|  |                         var lowroom = VorbisTools.predictPoint(xlist[j], xlist[low], xlist[high], fy[low], fy[high]); | ||
|  |                         var val = fy[j]; | ||
|  |                         var highroom = range - lowroom; | ||
|  |                         var room = if (highroom < lowroom){ | ||
|  |                             highroom * 2; | ||
|  |                         }else{ | ||
|  |                             lowroom * 2; | ||
|  |                         } | ||
|  |                         if (val != 0) { | ||
|  |                             step2Flag[low] = step2Flag[high] = true; | ||
|  |                             step2Flag[j] = true; | ||
|  |                             if (val >= room){ | ||
|  |                                 if (highroom > lowroom){ | ||
|  |                                     fy[j] = val - lowroom + lowroom; | ||
|  |                                 }else{ | ||
|  |                                     fy[j] = lowroom - val + highroom - 1; | ||
|  |                                 } | ||
|  |                             } else { | ||
|  |                                 if (val & 1 != 0){ | ||
|  |                                     fy[j] = lowroom - ((val+1)>>1); | ||
|  |                                 } else{ | ||
|  |                                     fy[j] = lowroom + (val>>1); | ||
|  |                                 } | ||
|  |                             } | ||
|  |                         } else { | ||
|  |                             step2Flag[j] = false; | ||
|  |                             fy[j] = lowroom; | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                     // defer final floor computation until _after_ residue | ||
|  |                     for (j in 0...g.values) { | ||
|  |                         if (!step2Flag[j]){ | ||
|  |                             fy[j] = -1; | ||
|  |                         } | ||
|  |                     } | ||
|  | 
 | ||
|  |                 } else { | ||
|  |                     zeroChannel[i] = true; | ||
|  |                 } | ||
|  |                 // So we just defer everything else to later | ||
|  |                 // at this point we've decoded the floor into buffer | ||
|  |             } | ||
|  |         } | ||
|  |         VorbisTools.stbProf(0); | ||
|  |         // at this point we've decoded all floors | ||
|  | 
 | ||
|  |         //if (alloc.allocBuffer) { | ||
|  |         //    assert(alloc.allocBufferLengthInBytes == tempOffset); | ||
|  |         //} | ||
|  | 
 | ||
|  |         // re-enable coupled channels if necessary | ||
|  |         for (i in 0...header.channel) { | ||
|  |             reallyZeroChannel[i] = zeroChannel[i]; | ||
|  |         } | ||
|  |         for (i in 0...map.couplingSteps) { | ||
|  |             if (!zeroChannel[map.chan[i].magnitude] || !zeroChannel[map.chan[i].angle]) { | ||
|  |                 zeroChannel[map.chan[i].magnitude] = zeroChannel[map.chan[i].angle] = false; | ||
|  |             } | ||
|  |         } | ||
|  |         // RESIDUE DECODE | ||
|  |         for (i in 0...map.submaps) { | ||
|  |             var residueBuffers = new Vector<Vector<Float>>(header.channel); | ||
|  |             var doNotDecode = new Vector<Bool>(256); | ||
|  |             var ch = 0; | ||
|  |             for (j in 0...header.channel) { | ||
|  | 
 | ||
|  |                 if (map.chan[j].mux == i) { | ||
|  |                     if (zeroChannel[j]) { | ||
|  |                         doNotDecode[ch] = true; | ||
|  |                         residueBuffers[ch] = null; | ||
|  |                     } else { | ||
|  |                         doNotDecode[ch] = false; | ||
|  |                         residueBuffers[ch] = channelBuffers[j]; | ||
|  |                     } | ||
|  |                     ++ch; | ||
|  |                 } | ||
|  |             } | ||
|  | 
 | ||
|  |             var r = map.submapResidue[i]; | ||
|  |             var residue = header.residueConfig[r]; | ||
|  |             residue.decode(decodeState,header, residueBuffers, ch, n2, doNotDecode, channelBuffers); | ||
|  |         } | ||
|  | 
 | ||
|  |         // INVERSE COUPLING | ||
|  |         VorbisTools.stbProf(14); | ||
|  | 
 | ||
|  |         var i = map.couplingSteps; | ||
|  |         var n2 = n >> 1; | ||
|  |         while (--i >= 0) { | ||
|  |             var m = channelBuffers[map.chan[i].magnitude]; | ||
|  |             var a = channelBuffers[map.chan[i].angle]; | ||
|  |             for (j in 0...n2) { | ||
|  |                 var a2, m2; | ||
|  |                 if (m[j] > 0) { | ||
|  |                     if (a[j] > 0) { | ||
|  |                         m2 = m[j]; | ||
|  |                         a2 = m[j] - a[j]; | ||
|  |                     } else { | ||
|  |                         a2 = m[j]; | ||
|  |                         m2 = m[j] + a[j]; | ||
|  |                     } | ||
|  |                 } else { | ||
|  |                     if (a[j] > 0) { | ||
|  |                         m2 = m[j]; | ||
|  |                         a2 = m[j] + a[j]; | ||
|  |                     } else { | ||
|  |                         a2 = m[j]; | ||
|  |                         m2 = m[j] - a[j]; | ||
|  |                     } | ||
|  |                 } | ||
|  |                 m[j] = m2; | ||
|  |                 a[j] = a2; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // finish decoding the floors | ||
|  |         VorbisTools.stbProf(15); | ||
|  |         for (i in 0...header.channel) { | ||
|  |             if (reallyZeroChannel[i]) { | ||
|  |                 for(j in 0...n2) { | ||
|  |                     channelBuffers[i][j] = 0; | ||
|  |                 } | ||
|  |             } else { | ||
|  |                 map.doFloor(header.floorConfig, i, n, channelBuffers[i], finalY[i], null); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         // INVERSE MDCT | ||
|  |         VorbisTools.stbProf(16); | ||
|  |         for (i in 0...header.channel) { | ||
|  |             inverseMdct(channelBuffers[i], n, m.blockflag); | ||
|  |         } | ||
|  |         VorbisTools.stbProf(0); | ||
|  | 
 | ||
|  |         // this shouldn't be necessary, unless we exited on an error | ||
|  |         // and want to flush to get to the next packet | ||
|  |         decodeState.flushPacket(); | ||
|  | 
 | ||
|  |         return decodeState.finishDecodePacket(previousLength, n, r); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | typedef DecodePacketResult = { | ||
|  |     var len : Int; | ||
|  |     var left : Int; | ||
|  |     var right : Int; | ||
|  | } | ||
|  | 
 | ||
|  | typedef DecodeInitialResult = { | ||
|  |     var mode : Int; | ||
|  |     var left : Range; | ||
|  |     var right : Range; | ||
|  | } | ||
|  | 
 | ||
|  | private typedef Range = { | ||
|  |     var start : Int; | ||
|  |     var end : Int; | ||
|  | } |