package org.jcodec.codecs.aac.blocks;
import static java.lang.Math.min;
import static java.lang.Math.pow;
import static java.lang.String.format;
import static org.jcodec.codecs.aac.Profile.LC;
import static org.jcodec.codecs.aac.Profile.MAIN;
import static org.jcodec.codecs.aac.blocks.BlockICS.BandType.INTENSITY_BT;
import static org.jcodec.codecs.aac.blocks.BlockICS.BandType.INTENSITY_BT2;
import static org.jcodec.codecs.aac.blocks.BlockICS.BandType.NOISE_BT;
import static org.jcodec.codecs.aac.blocks.BlockICS.BandType.ZERO_BT;
import static org.jcodec.common.io.VLCBuilder.createVLCBuilder;
import static org.jcodec.common.tools.MathUtil.clip;
import org.jcodec.codecs.aac.Profile;
import org.jcodec.codecs.prores.ProresDecoder;
import org.jcodec.common.io.BitReader;
import org.jcodec.common.io.VLC;
import org.jcodec.common.tools.MathUtil;
import java.lang.System;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Individual Channel Stream info, AAC block
*
* @author The JCodec project
*
*/
public class BlockICS extends Block {
private boolean commonWindow;
private boolean scaleFlag;
private Profile profile;
private int samplingIndex;
private static VLC[] spectral;
private static VLC vlc;
static {
vlc = new VLC(AACTab.ff_aac_scalefactor_code, AACTab.ff_aac_scalefactor_bits);
spectral = new VLC[] { createVLCBuilder(AACTab.codes1, AACTab.bits1, AACTab.codebook_vector02_idx).getVLC(),
createVLCBuilder(AACTab.codes2, AACTab.bits2, AACTab.codebook_vector02_idx).getVLC(),
createVLCBuilder(AACTab.codes3, AACTab.bits3, AACTab.codebook_vector02_idx).getVLC(),
createVLCBuilder(AACTab.codes4, AACTab.bits4, AACTab.codebook_vector02_idx).getVLC(),
createVLCBuilder(AACTab.codes5, AACTab.bits5, AACTab.codebook_vector4_idx).getVLC(),
createVLCBuilder(AACTab.codes6, AACTab.bits6, AACTab.codebook_vector4_idx).getVLC(),
createVLCBuilder(AACTab.codes7, AACTab.bits7, AACTab.codebook_vector6_idx).getVLC(),
createVLCBuilder(AACTab.codes8, AACTab.bits8, AACTab.codebook_vector6_idx).getVLC(),
createVLCBuilder(AACTab.codes9, AACTab.bits9, AACTab.codebook_vector8_idx).getVLC(),
createVLCBuilder(AACTab.codes10, AACTab.bits10, AACTab.codebook_vector8_idx).getVLC(),
createVLCBuilder(AACTab.codes11, AACTab.bits11, AACTab.codebook_vector10_idx).getVLC() };
}
float[][] ff_aac_codebook_vector_vals;
private static final int MAX_LTP_LONG_SFB = 40;
private int windowSequence;
int num_window_groups;
private int[] group_len;
int maxSfb;
private int[] band_type;
private int[] band_type_run_end;
private int globalGain;
public BlockICS() {
this.ff_aac_codebook_vector_vals = new float[][]{ AACTab.codebook_vector0_vals, AACTab.codebook_vector0_vals,
AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector4_vals,
AACTab.codebook_vector4_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals,
AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, AACTab.codebook_vector10_vals, };
this.group_len = new int[8];
this.band_type = new int[120];
this.band_type_run_end = new int[120];
}
private static enum WindowSequence {
ONLY_LONG_SEQUENCE, LONG_START_SEQUENCE, EIGHT_SHORT_SEQUENCE, LONG_STOP_SEQUENCE;
}
protected int parseICSInfo(BitReader _in) {
_in.read1Bit();
windowSequence = (int) _in.readNBit(2);
int useKbWindow = _in.read1Bit();
num_window_groups = 1;
group_len[0] = 1;
if (windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) {
int max_sfb = (int) _in.readNBit(4);
for (int i = 0; i < 7; i++) {
if (_in.read1Bit() != 0) {
group_len[num_window_groups - 1]++;
} else {
num_window_groups++;
group_len[num_window_groups - 1] = 1;
}
}
numSwb = AACTab.ff_aac_num_swb_128[samplingIndex];
swbOffset = AACTab.ff_swb_offset_128[samplingIndex];
numWindows = 8;
} else {
maxSfb = (int) _in.readNBit(6);
numSwb = AACTab.ff_aac_num_swb_1024[samplingIndex];
swbOffset = AACTab.ff_swb_offset_1024[samplingIndex];
numWindows = 1;
int predictor_present = _in.read1Bit();
if (predictor_present != 0) {
if (profile == MAIN) {
decodePrediction(_in, maxSfb);
} else if (profile == LC) {
throw new RuntimeException("Prediction is not allowed _in AAC-LC.\n");
} else {
int ltpPresent = _in.read1Bit();
if (ltpPresent != 0)
decodeLtp(_in, maxSfb);
}
}
}
return 0;
}
private void decodePrediction(BitReader _in, int maxSfb) {
if (_in.read1Bit() != 0) {
int predictorResetGroup = (int) _in.readNBit(5);
}
for (int sfb = 0; sfb < min(maxSfb, AACTab.maxSfbTab[samplingIndex]); sfb++) {
_in.read1Bit();
}
}
private void decodeLtp(BitReader _in, int maxSfb) {
int lag = (int) _in.readNBit(11);
float coef = AACTab.ltpCoefTab[(int) _in.readNBit(3)];
for (int sfb = 0; sfb < min(maxSfb, MAX_LTP_LONG_SFB); sfb++)
_in.read1Bit();
}
private void decodeBandTypes(BitReader _in) {
int g, idx = 0;
int bits = (windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) ? 3 : 5;
for (g = 0; g < num_window_groups; g++) {
int k = 0;
while (k < maxSfb) {
int sect_end = k;
int sect_len_incr;
int sect_band_type = (int) _in.readNBit(4);
if (sect_band_type == 12) {
throw new RuntimeException("invalid band type");
}
while ((sect_len_incr = (int) _in.readNBit(bits)) == (1 << bits) - 1)
sect_end += sect_len_incr;
sect_end += sect_len_incr;
if (!_in.moreData() || sect_len_incr == (1 << bits) - 1) {
throw new RuntimeException("Overread");
}
if (sect_end > maxSfb) {
throw new RuntimeException(format("Number of bands (%d) exceeds limit (%d).\n", sect_end, maxSfb));
}
for (; k < sect_end; k++) {
band_type[idx] = sect_band_type;
band_type_run_end[idx++] = sect_end;
}
}
}
}
enum BandType {
ZERO_BT, BT_1, BT_2, BT_3, BT_4, FIRST_PAIR_BT, BT_6, BT_7, BT_8, BT_9, BT_10, ESC_BT, BT_12, NOISE_BT, INTENSITY_BT2, INTENSITY_BT
};
static float[] ff_aac_pow2sf_tab = new float[428];
private final static int POW_SF2_ZERO = 200;
private double[] sfs;
private int numSwb;
private int[] swbOffset;
private int numWindows;
static {
int i;
for (i = 0; i < 428; i++)
ff_aac_pow2sf_tab[i] = (float) pow(2, (i - POW_SF2_ZERO) / 4.);
}
private void decodeScalefactors(BitReader _in) {
int[] offset = new int[] { globalGain, globalGain - 90, 0 };
int clipped_offset;
int noise_flag = 1;
String[] sf_str = new String[] { "Global gain", "Noise gain", "Intensity stereo position" };
int idx = 0;
for (int g = 0; g < num_window_groups; g++) {
for (int i = 0; i < maxSfb;) {
int run_end = band_type_run_end[idx];
if (band_type[idx] == ZERO_BT.ordinal()) {
for (; i < run_end; i++, idx++)
sfs[idx] = 0.;
} else if ((band_type[idx] == INTENSITY_BT.ordinal()) || (band_type[idx] == INTENSITY_BT2.ordinal())) {
for (; i < run_end; i++, idx++) {
offset[2] += vlc.readVLC(_in) - 60;
clipped_offset = clip(offset[2], -155, 100);
if (offset[2] != clipped_offset) {
System.out.println(String.format("Intensity stereo "
+ "position clipped (%d -> %d).\nIf you heard an "
+ "audible artifact, there may be a bug _in the " + "decoder. ", offset[2],
clipped_offset));
}
sfs[idx] = ff_aac_pow2sf_tab[-clipped_offset + POW_SF2_ZERO];
}
} else if (band_type[idx] == NOISE_BT.ordinal()) {
for (; i < run_end; i++, idx++) {
if (noise_flag-- > 0)
offset[1] += _in.readNBit(9) - 256;
else
offset[1] += vlc.readVLC(_in) - 60;
clipped_offset = clip(offset[1], -100, 155);
if (offset[1] != clipped_offset) {
System.out.println(String.format("Noise gain clipped "
+ "(%d -> %d).\nIf you heard an audible "
+ "artifact, there may be a bug _in the decoder. ", offset[1], clipped_offset));
}
sfs[idx] = -ff_aac_pow2sf_tab[clipped_offset + POW_SF2_ZERO];
}
} else {
for (; i < run_end; i++, idx++) {
offset[0] += vlc.readVLC(_in) - 60;
if (offset[0] > 255) {
throw new RuntimeException(String.format("%s (%d) out of range.\n", sf_str[0], offset[0]));
}
sfs[idx] = -ff_aac_pow2sf_tab[offset[0] - 100 + POW_SF2_ZERO];
}
}
}
}
}
public static class Pulse {
private int numPulse;
private int[] pos;
private int[] amp;
public Pulse(int numPulse, int[] pos, int[] amp) {
this.numPulse = numPulse;
this.pos = pos;
this.amp = amp;
}
public int getNumPulse() {
return numPulse;
}
public int[] getPos() {
return pos;
}
public int[] getAmp() {
return amp;
}
}
private Pulse decodePulses(BitReader _in) {
int[] pos = new int[4];
int[] amp = new int[4];
int numPulse = (int) _in.readNBit(2) + 1;
int pulseSwb = (int) _in.readNBit(6);
if (pulseSwb >= numSwb)
throw new RuntimeException("pulseSwb >= numSwb");
pos[0] = swbOffset[pulseSwb];
pos[0] += (int) _in.readNBit(5);
if (pos[0] > 1023)
throw new RuntimeException("pos[0] > 1023");
amp[0] = (int) _in.readNBit(4);
for (int i = 1; i < numPulse; i++) {
pos[i] = (int) _in.readNBit(5) + pos[i - 1];
if (pos[i] > 1023)
throw new RuntimeException("pos[" + i + "] > 1023");
amp[i] = (int) _in.readNBit(5);
}
return new Pulse(numPulse, pos, amp);
}
public static class Tns {
private int[] nFilt;
private int[][] length;
private int[][] order;
private int[][] direction;
private float[][][] coeff;
public Tns(int[] nFilt, int[][] length, int[][] order, int[][] direction, float[][][] coeff) {
this.nFilt = nFilt;
this.length = length;
this.order = order;
this.direction = direction;
this.coeff = coeff;
}
}
private Tns decodeTns(BitReader _in) {
int is8 = windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal() ? 1 : 0;
int tns_max_order = is8 != 0 ? 7 : profile == Profile.MAIN ? 20 : 12;
int[] nFilt = new int[numWindows];
int[][] length = new int[numWindows][2];
int[][] order = new int[numWindows][2];
int[][] direction = new int[numWindows][2];
float[][][] coeff = new float[numWindows][2][1 << (5 - 2 * is8)];
for (int w = 0; w < numWindows; w++) {
if ((nFilt[w] = (int) _in.readNBit(2 - is8)) != 0) {
int coefRes = _in.read1Bit();
for (int filt = 0; filt < nFilt[w]; filt++) {
int tmp2_idx;
length[w][filt] = (int) _in.readNBit(6 - 2 * is8);
if ((order[w][filt] = (int) _in.readNBit(5 - 2 * is8)) > tns_max_order) {
throw new RuntimeException(String.format("TNS filter order %d is greater than maximum %d.\n",
order[w][filt], tns_max_order));
}
if (order[w][filt] != 0) {
direction[w][filt] = _in.read1Bit();
int coefCompress = _in.read1Bit();
int coefLen = coefRes + 3 - coefCompress;
tmp2_idx = 2 * coefCompress + coefRes;
for (int i = 0; i < order[w][filt]; i++)
coeff[w][filt][i] = AACTab.tns_tmp2_map[tmp2_idx][(int) _in.readNBit(coefLen)];
}
}
}
}
return new Tns(nFilt, length, order, direction, coeff);
}
void VMUL4(float[] result, int idx, float[] v, int code, float scale) {
result[idx] = v[code & 3] * scale;
result[idx + 1] = v[code >> 2 & 3] * scale;
result[idx + 2] = v[code >> 4 & 3] * scale;
result[idx + 3] = v[code >> 6 & 3] * scale;
}
void VMUL4S(float[] result, int idx, float[] v, int code, int sign, float scale) {
int nz = code >> 12;
// t.i = s.i ^ (sign & 1U<<31);
result[idx + 0] = v[idx & 3] * scale;
sign <<= nz & 1;
nz >>= 1;
// t.i = s.i ^ (sign & 1U<<31);
result[idx + 1] = v[idx >> 2 & 3] * scale;
sign <<= nz & 1;
nz >>= 1;
// t.i = s.i ^ (sign & 1U<<31);
result[idx + 2] = v[idx >> 4 & 3] * scale;
sign <<= nz & 1;
nz >>= 1;
// t.i = s.i ^ (sign & 1U<<31);
result[idx + 3] = v[idx >> 6 & 3] * scale;
}
void VMUL2(float[] result, int idx, float[] v, int code, float scale) {
result[idx] = v[code & 15] * scale;
result[idx + 1] = v[code >> 4 & 15] * scale;
}
void VMUL2S(float[] result, int idx, float[] v, int code, int sign, float scale) {
result[idx] = v[code & 15] * scale;
result[idx + 1] = v[code >> 4 & 15] * scale;
}
private void decodeSpectrum(BitReader _in) {
float[] coef = new float[1024];
int idx = 0;
for (int g = 0; g < num_window_groups; g++) {
for (int i = 0; i < maxSfb; i++, idx++) {
int cbt_m1 = band_type[idx] - 1;
if (cbt_m1 < INTENSITY_BT2.ordinal() - 1 && cbt_m1 != NOISE_BT.ordinal() - 1) {
float[] vq = ff_aac_codebook_vector_vals[cbt_m1];
VLC vlc = spectral[cbt_m1];
switch (cbt_m1 >> 1) {
case 0:
readBandType1And2(_in, coef, idx, g, i, vq, vlc);
break;
case 1:
readBandType3And4(_in, coef, idx, g, i, vq, vlc);
break;
case 2:
readBandType5And6(_in, coef, idx, g, i, vq, vlc);
break;
case 3:
case 4:
readBandType7Through10(_in, coef, idx, g, i, vq, vlc);
break;
default:
readOther(_in, coef, idx, g, i, vq, vlc);
}
}
}
}
}
private void readBandType3And4(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = group_len[g];
int cfo = swbOffset[sfb];
int off_len = swbOffset[sfb + 1] - swbOffset[sfb];
for (int group = 0; group < g_len; group++, cfo += 128) {
int cf = cfo;
int len = off_len;
do {
int cb_idx = vlc.readVLC(_in);
int nnz = cb_idx >> 8 & 15;
int bits = nnz == 0 ? 0 : _in.readNBit(nnz);
VMUL4S(coef, cf, vq, cb_idx, bits, (float) sfs[idx]);
cf += 4;
len -= 4;
} while (len > 0);
}
}
private void readBandType7Through10(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = group_len[g];
int cfo = swbOffset[sfb];
int off_len = swbOffset[sfb + 1] - swbOffset[sfb];
for (int group = 0; group < g_len; group++, cfo += 128) {
int cf = cfo;
int len = off_len;
do {
int cb_idx = vlc.readVLC(_in);
int nnz = cb_idx >> 8 & 15;
int bits = nnz == 0 ? 0 : (_in.readNBit(nnz) << (cb_idx >> 12));
VMUL2S(coef, cf, vq, cb_idx, bits, (float) sfs[idx]);
cf += 2;
len -= 2;
} while (len > 0);
}
}
private void readOther(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = group_len[g];
int cfo = swbOffset[sfb];
int off_len = swbOffset[sfb + 1] - swbOffset[sfb];
for (int group = 0; group < g_len; group++, cfo += 128) {
int cf = cfo;
int len = off_len;
do {
int cb_idx = vlc.readVLC(_in);
if (cb_idx != 0) {
int nnz = cb_idx >> 12;
int nzt = cb_idx >> 8;
int bits = _in.readNBit(nnz) << (32 - nnz);
for (int j = 0; j < 2; j++) {
if ((nzt & 1 << j) != 0) {
int b;
int n;
/*
* The total length of escape_sequence must be < 22
* bits according to the specification (i.e. max is
* 111111110xxxxxxxxxxxx).
*/
b = ProresDecoder.nZeros(~_in.checkNBit(14));
if (b > 8) {
throw new RuntimeException("error _in spectral data, ESC overflow\n");
}
_in.skip(b + 1);
b += 4;
n = (1 << b) + _in.readNBit(b);
coef[cf++] = MathUtil.cubeRoot(n) | (bits & 1 << 31);
bits <<= 1;
} else {
int v = (int) vq[cb_idx & 15];
coef[cf++] = (bits & 1 << 31) | v;
// bits <<= !!v;
}
cb_idx >>= 4;
}
cf += 2;
len += 2;
}
} while (len > 0);
}
}
private void readBandType1And2(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = group_len[g];
int cfo = swbOffset[sfb];
int off_len = swbOffset[sfb + 1] - swbOffset[sfb];
for (int group = 0; group < g_len; group++, cfo += 128) {
int cf = cfo;
int len = off_len;
do {
int cb_idx = vlc.readVLC(_in);
VMUL4(coef, cf, vq, cb_idx, (float) sfs[idx]);
cf += 4;
len -= 4;
} while (len > 0);
}
}
private void readBandType5And6(BitReader _in, float[] coef, int idx, int g, int sfb, float[] vq, VLC vlc) {
int g_len = group_len[g];
int cfo = swbOffset[sfb];
int off_len = swbOffset[sfb + 1] - swbOffset[sfb];
for (int group = 0; group < g_len; group++, cfo += 128) {
int cf = cfo;
int len = off_len;
do {
int cb_idx = vlc.readVLC(_in);
VMUL2(coef, cf, vq, cb_idx, (float) sfs[idx]);
cf += 2;
len -= 2;
} while (len > 0);
}
}
public void parse(BitReader _in) {
globalGain = (int) _in.readNBit(8);
if (!commonWindow && !scaleFlag) {
parseICSInfo(_in);
}
decodeBandTypes(_in);
decodeScalefactors(_in);
int pulse_present = 0;
int tns_present;
if (!scaleFlag) {
if ((pulse_present = _in.read1Bit()) != 0) {
if (windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE.ordinal()) {
throw new RuntimeException("Pulse tool not allowed _in eight short sequence.");
}
decodePulses(_in);
}
if ((tns_present = _in.read1Bit()) != 0) {
decodeTns(_in);
}
if (_in.read1Bit() != 0) {
throw new RuntimeException("SSR is not supported");
}
}
decodeSpectrum(_in);
}
}