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