package kha.audio2.ogg.vorbis.data; import haxe.ds.Vector; import haxe.io.Bytes; import haxe.io.Input; import kha.audio2.ogg.tools.MathTools; import kha.audio2.ogg.vorbis.data.ReaderError.ReaderErrorType; import kha.audio2.ogg.vorbis.VorbisDecodeState; /** * ... * @author shohei909 */ class Codebook { static public inline var NO_CODE = 255; public var dimensions:Int; public var entries:Int; public var codewordLengths:Vector; //uint8* public var minimumValue:Float; public var deltaValue:Float; public var valueBits:Int; //uint8 public var lookupType:Int; //uint8 public var sequenceP:Bool; //uint8 public var sparse:Bool; //uint8 public var lookupValues:UInt; //uint32 public var multiplicands:Vector; // codetype * public var codewords:Vector; //uint32* public var fastHuffman:Vector; //[FAST_HUFFMAN_TABLE_SIZE]; public var sortedCodewords:Array; //uint32* public var sortedValues:Vector; public var sortedEntries:Int; public function new () { } static public function read(decodeState:VorbisDecodeState):Codebook { var c = new Codebook(); if (decodeState.readBits(8) != 0x42 || decodeState.readBits(8) != 0x43 || decodeState.readBits(8) != 0x56) { throw new ReaderError(ReaderErrorType.INVALID_SETUP); } var x = decodeState.readBits(8); c.dimensions = (decodeState.readBits(8) << 8) + x; var x = decodeState.readBits(8); var y = decodeState.readBits(8); c.entries = (decodeState.readBits(8) << 16) + (y << 8) + x; var ordered = decodeState.readBits(1); c.sparse = (ordered != 0) ? false : (decodeState.readBits(1) != 0); var lengths = new Vector(c.entries); if (!c.sparse) { c.codewordLengths = lengths; } var total = 0; if (ordered != 0) { var currentEntry = 0; var currentLength = decodeState.readBits(5) + 1; while (currentEntry < c.entries) { var limit = c.entries - currentEntry; var n = decodeState.readBits(MathTools.ilog(limit)); if (currentEntry + n > c.entries) { throw new ReaderError(ReaderErrorType.INVALID_SETUP, "codebook entrys"); } for (i in 0...n) { lengths.set(currentEntry + i, currentLength); } currentEntry += n; currentLength++; } } else { for (j in 0...c.entries) { var present = (c.sparse) ? decodeState.readBits(1) : 1; if (present != 0) { lengths.set(j, decodeState.readBits(5) + 1); total++; } else { lengths.set(j, NO_CODE); } } } if (c.sparse && total >= (c.entries >> 2)) { c.codewordLengths = lengths; c.sparse = false; } c.sortedEntries = if (c.sparse) { total; } else { var sortedCount = 0; for (j in 0...c.entries) { var l = lengths.get(j); if (l > Setting.FAST_HUFFMAN_LENGTH && l != NO_CODE) { ++sortedCount; } } sortedCount; } var values:Vector = null; if (!c.sparse) { c.codewords = new Vector(c.entries); } else { if (c.sortedEntries != 0) { c.codewordLengths = new Vector(c.sortedEntries); c.codewords = new Vector(c.entries); values = new Vector(c.entries); } var size:Int = c.entries + (32 + 32) * c.sortedEntries; } if (!c.computeCodewords(lengths, c.entries, values)) { throw new ReaderError(ReaderErrorType.INVALID_SETUP, "compute codewords"); } if (c.sortedEntries != 0) { // allocate an extra slot for sentinels c.sortedCodewords = []; // allocate an extra slot at the front so that sortedValues[-1] is defined // so that we can catch that case without an extra if c.sortedValues = new Vector(c.sortedEntries); c.computeSortedHuffman(lengths, values); } if (c.sparse) { values = null; c.codewords = null; lengths = null; } c.computeAcceleratedHuffman(); c.lookupType = decodeState.readBits(4); if (c.lookupType > 2) { throw new ReaderError(ReaderErrorType.INVALID_SETUP, "codebook lookup type"); } if (c.lookupType > 0) { c.minimumValue = VorbisTools.floatUnpack(decodeState.readBits(32)); c.deltaValue = VorbisTools.floatUnpack(decodeState.readBits(32)); c.valueBits = decodeState.readBits(4) + 1; c.sequenceP = (decodeState.readBits(1) != 0); if (c.lookupType == 1) { c.lookupValues = VorbisTools.lookup1Values(c.entries, c.dimensions); } else { c.lookupValues = c.entries * c.dimensions; } var mults = new Vector(c.lookupValues); for (j in 0...c.lookupValues) { var q = decodeState.readBits(c.valueBits); if (q == VorbisTools.EOP) { throw new ReaderError(ReaderErrorType.INVALID_SETUP, "fail lookup"); } mults[j] = q; } { c.multiplicands = new Vector(c.lookupValues); //STB_VORBIS_CODEBOOK_FLOATS = true for (j in 0...c.lookupValues) { c.multiplicands[j] = mults[j] * c.deltaValue + c.minimumValue; } } //STB_VORBIS_CODEBOOK_FLOATS = true if (c.lookupType == 2 && c.sequenceP) { for (j in 1...c.lookupValues) { c.multiplicands[j] = c.multiplicands[j - 1]; } c.sequenceP = false; } } return c; } inline function addEntry(huffCode:UInt, symbol:Int, count:Int, len:Int, values:Vector) { if (!sparse) { codewords[symbol] = huffCode; } else { codewords[count] = huffCode; codewordLengths.set(count, len); values[count] = symbol; } } inline function includeInSort(len:Int) { return if (sparse) { VorbisTools.assert(len != NO_CODE); true; } else if (len == NO_CODE) { false; } else if (len > Setting.FAST_HUFFMAN_LENGTH) { true; } else { false; } } function computeCodewords(len:Vector, n:Int, values:Vector) { var available = new Vector(32); for (x in 0...32) available[x] = 0; // find the first entry var k = 0; while (k < n) { if (len.get(k) < NO_CODE) { break; } k++; } if (k == n) { VorbisTools.assert(sortedEntries == 0); return true; } var m = 0; // add to the list addEntry(0, k, m++, len.get(k), values); // add all available leaves var i = 0; while (++i <= len.get(k)) { available[i] = (1:UInt) << ((32 - i):UInt); } // note that the above code treats the first case specially, // but it's really the same as the following code, so they // could probably be combined (except the initial code is 0, // and I use 0 in available[] to mean 'empty') i = k; while (++i < n) { var z = len.get(i); if (z == NO_CODE) continue; // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have // more than one free leaf at a given level, isn't totally // trivial to prove, but it seems true and the assert never // fires, so! while (z > 0 && available[z] == 0) --z; if (z == 0) { return false; } var res:UInt = available[z]; available[z] = 0; addEntry(VorbisTools.bitReverse(res), i, m++, len.get(i), values); // propogate availability up the tree if (z != len.get(i)) { var y = len.get(i); while (y > z) { VorbisTools.assert(available[y] == 0); available[y] = res + (1 << (32 - y)); y--; } } } return true; } function computeSortedHuffman(lengths:Vector, values:Vector) { // build a list of all the entries // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. // this is kind of a frivolous optimization--I don't see any performance improvement, // but it's like 4 extra lines of code, so. if (!sparse) { var k = 0; for (i in 0...entries) { if (includeInSort(lengths.get(i))) { sortedCodewords[k++] = VorbisTools.bitReverse(codewords[i]); } } VorbisTools.assert(k == sortedEntries); } else { for (i in 0...sortedEntries) { sortedCodewords[i] = VorbisTools.bitReverse(codewords[i]); } } sortedCodewords[sortedEntries] = 0xffffffff; sortedCodewords.sort(VorbisTools.uintAsc); var len = sparse ? sortedEntries : entries; // now we need to indicate how they correspond; we could either // #1: sort a different data structure that says who they correspond to // #2: for each sorted entry, search the original list to find who corresponds // #3: for each original entry, find the sorted entry // #1 requires extra storage, #2 is slow, #3 can use binary search! for (i in 0...len) { var huffLen = sparse ? lengths.get(values[i]) : lengths.get(i); if (includeInSort(huffLen)) { var code = VorbisTools.bitReverse(codewords[i]); var x = 0; var n = 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; } } //VorbisTools.assert(sortedCodewords[x] == code); if (sparse) { sortedValues[x] = values[i]; codewordLengths.set(x, huffLen); } else { sortedValues[x] = i; } } } } function computeAcceleratedHuffman() { fastHuffman = new Vector(Setting.FAST_HUFFMAN_TABLE_SIZE); fastHuffman[0] = -1; for (i in 0...(Setting.FAST_HUFFMAN_TABLE_SIZE)) { fastHuffman[i] = -1; } var len = (sparse) ? sortedEntries : entries; //STB_VORBIS_FAST_HUFFMAN_SHORT //if (len > 32767) len = 32767; // largest possible value we can encode! for (i in 0...len) { if (codewordLengths[i] <= Setting.FAST_HUFFMAN_LENGTH) { var z:Int = (sparse) ? VorbisTools.bitReverse(sortedCodewords[i]) : codewords[i]; // set table entries for all bit combinations in the higher bits while (z < Setting.FAST_HUFFMAN_TABLE_SIZE) { fastHuffman[z] = i; z += 1 << codewordLengths[i]; } } } } function codebookDecode(decodeState:VorbisDecodeState, output:Vector, offset:Int, len:Int) { var z = decodeStart(decodeState); var lookupValues = this.lookupValues; var sequenceP = this.sequenceP; var multiplicands = this.multiplicands; var minimumValue = this.minimumValue; if (z < 0) { return false; } if (len > dimensions) { len = dimensions; } // STB_VORBIS_DIVIDES_IN_CODEBOOK = true if (lookupType == 1) { var div = 1; var last = 0.0; for (i in 0...len) { var off = Std.int(z / div) % lookupValues; var val = multiplicands[off] + last; output[offset + i] += val; if (sequenceP) { last = val + minimumValue; } div *= lookupValues; } return true; } z *= dimensions; if (sequenceP) { var last = 0.0; for (i in 0...len) { var val = multiplicands[z + i] + last; output[offset + i] += val; last = val + minimumValue; } } else { var last = 0.0; for (i in 0...len) { output[offset + i] += multiplicands[z + i] + last; } } return true; } function codebookDecodeStep(decodeState:VorbisDecodeState, output:Vector, offset:Int, len:Int, step:Int) { var z = decodeStart(decodeState); var last = 0.0; if (z < 0) { return false; } if (len > dimensions) { len = dimensions; } var lookupValues = this.lookupValues; var sequenceP = this.sequenceP; var multiplicands = this.multiplicands; // STB_VORBIS_DIVIDES_IN_CODEBOOK = true if (lookupType == 1) { var div = 1; for (i in 0...len) { var off = Std.int(z / div) % lookupValues; var val = multiplicands[off] + last; output[offset + i * step] += val; if (sequenceP) { last = val; } div *= lookupValues; } return true; } z *= dimensions; for (i in 0...len) { var val = multiplicands[z + i] + last; output[offset + i * step] += val; if (sequenceP) { last = val; } } return true; } inline function decodeStart(decodeState:VorbisDecodeState) { return decodeState.decode(this); //var z = -1; //// type 0 is only legal in a scalar context //if (lookupType == 0) { // throw new ReaderError(INVALID_STREAM); //} else { // z = decodeState.decode(this); // //if (sparse) VorbisTools.assert(z < sortedEntries); // if (z < 0) { // check for VorbisTools.EOP // if (decodeState.isLastByte()) { // return z; // } else { // throw new ReaderError(INVALID_STREAM); // } // } else { // return z; // } //} } static var delay = 0; public function decodeDeinterleaveRepeat(decodeState:VorbisDecodeState, residueBuffers:Vector>, ch:Int, cInter:Int, pInter:Int, len:Int, totalDecode:Int) { var effective = dimensions; // type 0 is only legal in a scalar context if (lookupType == 0) { throw new ReaderError(INVALID_STREAM); } var multiplicands = this.multiplicands; var sequenceP = this.sequenceP; var lookupValues = this.lookupValues; while (totalDecode > 0) { var last = 0.0; var z = decodeState.decode(this); if (z < 0) { if (decodeState.isLastByte()) { return null; } throw new ReaderError(INVALID_STREAM); } // if this will take us off the end of the buffers, stop short! // we check by computing the length of the virtual interleaved // buffer (len*ch), our current offset within it (pInter*ch)+(cInter), // and the length we'll be using (effective) if (cInter + pInter * ch + effective > len * ch) { effective = len * ch - (pInter * ch - cInter); } if (lookupType == 1) { var div = 1; if (sequenceP) { for (i in 0...effective) { var off = Std.int(z / div) % lookupValues; var val = multiplicands[off] + last; residueBuffers[cInter][pInter] += val; if (++cInter == ch) { cInter = 0; ++pInter; } last = val; div *= lookupValues; } } else { for (i in 0...effective) { var off = Std.int(z / div) % lookupValues; var val = multiplicands[off] + last; residueBuffers[cInter][pInter] += val; if (++cInter == ch) { cInter = 0; ++pInter; } div *= lookupValues; } } } else { z *= dimensions; if (sequenceP) { for (i in 0...effective) { var val = multiplicands[z + i] + last; residueBuffers[cInter][pInter] += val; if (++cInter == ch) { cInter = 0; ++pInter; } last = val; } } else { for (i in 0...effective) { var val = multiplicands[z + i] + last; residueBuffers[cInter][pInter] += val; if (++cInter == ch) { cInter = 0; ++pInter; } } } } totalDecode -= effective; } return { cInter : cInter, pInter : pInter } } public function residueDecode(decodeState:VorbisDecodeState, target:Vector, offset:Int, n:Int, rtype:Int) { if (rtype == 0) { var step = Std.int(n / dimensions); for (k in 0...step) { if (!codebookDecodeStep(decodeState, target, offset + k, n-offset-k, step)) { return false; } } } else { var k = 0; while(k < n) { if (!codebookDecode(decodeState, target, offset, n-k)) { return false; } k += dimensions; offset += dimensions; } } return true; } }