/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.media.codec.util;
import static java.lang.Math.max;
import static java.lang.Math.min;
import java.util.Arrays;
import jpcsp.media.codec.atrac3plus.Atrac3plusDecoder;
import org.apache.log4j.Logger;
public class VLC {
private static Logger log = Atrac3plusDecoder.log;
public int bits;
public int[][] table;
public int tableSize;
public int tableAllocated;
private static class VLCcode implements Comparable<VLCcode> {
int bits;
int symbol;
int code;
@Override
public int compareTo(VLCcode o) {
return (this.code >>> 1) - (o.code >>> 1);
}
}
public int initVLCSparse(int[] bits, int[] codes, int[] symbols) {
return initVLCSparse(bits.length, codes.length, bits, codes, symbols);
}
public int initVLCSparse(int nbBits, int nbCodes, int[] bits, int[] codes, int[] symbols) {
VLCcode buf[] = new VLCcode[nbCodes + 1];
this.bits = nbBits;
int j = 0;
for (int i = 0; i < nbCodes; i++) {
buf[j] = new VLCcode();
buf[j].bits = bits[i];
if (!(buf[j].bits > nbBits)) {
continue;
}
if (buf[j].bits > 3 * nbBits || buf[j].bits > 32) {
log.error(String.format("Too long VLC (%d) in initVLC", buf[j].bits));
return -1;
}
buf[j].code = codes[i];
if (buf[j].code >= (1 << buf[j].bits)) {
log.error(String.format("Invalid code in initVLC"));
return -1;
}
buf[j].code <<= 32 - buf[j].bits;
if (symbols != null) {
buf[j].symbol = symbols[i];
} else {
buf[j].symbol = i;
}
j++;
}
Arrays.sort(buf, 0, j);
for (int i = 0; i < nbCodes; i++) {
buf[j] = new VLCcode();
buf[j].bits = bits[i];
if (!(buf[j].bits != 0 && buf[j].bits <= nbBits)) {
continue;
}
buf[j].code = codes[i];
buf[j].code <<= 32 - buf[j].bits;
if (symbols != null) {
buf[j].symbol = symbols[i];
} else {
buf[j].symbol = i;
}
j++;
}
nbCodes = j;
return buildTable(nbBits, nbCodes, buf, 0);
}
private int buildTable(int tableNbBits, int nbCodes, VLCcode[] codes, int codeOffset) {
int tableSize = 1 << tableNbBits;
if (tableNbBits > 30) {
return -1;
}
int tableIndex = allocTable(tableSize);
if (tableIndex < 0) {
return tableIndex;
}
// first pass: map codes and compute auxiliary table sizes
for (int i = 0; i < nbCodes; i++) {
int n = codes[codeOffset + i].bits;
int code = codes[codeOffset + i].code;
int symbol = codes[codeOffset + i].symbol;
if (n <= tableNbBits) {
// no need to add another table
int j = code >>> (32 - tableNbBits);
int nb = 1 << (tableNbBits - n);
int inc = 1;
for (int k = 0; k < nb; k++) {
int bits = table[tableIndex + j][1];
if (bits != 0 && bits != n) {
log.error(String.format("incorrect codes"));
return -1;
}
table[tableIndex + j][1] = n; //bits
table[tableIndex + j][0] = symbol;
j += inc;
}
} else {
// fill auxiliary table recursively
n -= tableNbBits;
int codePrefix = code >>> (32 - tableNbBits);
int subtableBits = n;
codes[codeOffset + i].bits = n;
codes[codeOffset + i].code = code << tableNbBits;
int k;
for (k = i + 1; k < nbCodes; k++) {
n = codes[codeOffset + k].bits - tableNbBits;
if (n <= 0) {
break;
}
code = codes[codeOffset + k].code;
if ((code >>> (32 - tableNbBits)) != codePrefix) {
break;
}
codes[codeOffset + k].bits = n;
codes[codeOffset + k].code = code << tableNbBits;
subtableBits = max(subtableBits, n);
}
subtableBits = min(subtableBits, tableNbBits);
int j = codePrefix;
table[tableIndex + j][1] = -subtableBits;
int index = buildTable(subtableBits, k - i, codes, codeOffset + i);
if (index < 0) {
return index;
}
table[tableIndex + j][0] = index; //code
i = k - 1;
}
}
for (int i = 0; i < tableSize; i++) {
if (table[tableIndex + i][1] == 0) { //bits
table[tableIndex + i][0] = -1; //codes
}
}
return tableIndex;
}
private int allocTable(int size) {
int index = tableSize;
tableSize += size;
tableAllocated = tableSize;
int[][] newTable = new int[tableAllocated][2];
if (table != null) {
for (int i = 0; i < index; i++) {
newTable[i][0] = table[i][0];
newTable[i][1] = table[i][1];
}
}
table = newTable;
return index;
}
/**
* Parse a vlc code.
* @param bits is the number of bits which will be read at once, must be
* identical to nb_bits in init_vlc()
* @param maxDepth is the number of times bits bits must be read to completely
* read the longest vlc code
* = (max_vlc_length + bits - 1) / bits
*/
public int getVLC2(IBitReader br, int maxDepth) {
int nbBits;
int index = br.peek(bits);
int code = table[index][0];
int n = table[index][1];
if (maxDepth > 1 && n < 0) {
br.skip(bits);
nbBits = -n;
index = br.peek(nbBits) + code;
code = table[index][0];
n = table[index][1];
if (maxDepth > 2 && n < 0) {
br.skip(nbBits);
nbBits = -n;
index = br.peek(nbBits) + code;
code = table[index][0];
n = table[index][1];
}
}
br.skip(n);
return code;
}
public int getVLC2(IBitReader br) {
return getVLC2(br, 1);
}
}