/*
* 11/19/04 1.0 moved to LGPL.
*
* 18/06/01 Michael Scheerer, Fixed bugs which causes
* negative indexes in method huffmann_decode and in method
* dequanisize_sample.
*
* 16/07/01 Michael Scheerer, Catched a bug in method
* huffmann_decode, which causes an outOfIndexException.
* Cause : Indexnumber of 24 at SfBandIndex,
* which has only a length of 22. I have simply and dirty
* fixed the index to <= 22, because I'm not really be able
* to fix the bug. The Indexnumber is taken from the MP3
* file and the origin Ma-Player with the same code works
* well.
*
* 02/19/99 Java Conversion by E.B, javalayer@javazoom.net
*-----------------------------------------------------------------------
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*----------------------------------------------------------------------
*/
package org.geogebra.desktop.sound.mp3transform;
import java.io.IOException;
import org.geogebra.desktop.sound.mp3transform.Constants.SBI;
/**
* Class Implementing Layer 3 Decoder.
* http://www.oreilly.com/catalog/mp3/chapter/ch02.html Maximum bitreservoir is
* 511 byte.
* http://www.hydrogenaudio.org/forums/lofiversion/index.php/t42194.html gr:
* granules (sub-frames)
*/
final class Layer3Decoder {
static class GrInfo {
int part23Length;
int bigValues;
int globalGain;
int scaleFactorCompress;
boolean windowSwitching;
int blockType;
boolean mixedBlock;
int[] tableSelect = new int[3];
int[] subblockGain = new int[3];
int region0Count;
int region1Count;
int preflag;
int scaleFactorScale;
int count1TableSelect;
}
static class Channel {
int[] scfsi = new int[4];
GrInfo[] gr = new GrInfo[] { new GrInfo(), new GrInfo() };
}
static class SideInfo {
int mainDataBegin = 0;
Channel[] ch = new Channel[] { new Channel(), new Channel() };
}
static class ScaleFactor {
int[] l = new int[23]; /* [cb] */
int[][] s = new int[3][13]; /* [window][cb] */
}
private static final int SSLIMIT = 18;
private static final int SBLIMIT = 32;
// DOUBLE
private static final double D43 = (4.0 / 3.0);
private final int[] scaleFactorBuffer = new int[54];
// TODO why +4?
private final int[] is1d = new int[SBLIMIT * SSLIMIT + 4];
private final double[][] ro0 = new double[SBLIMIT][SSLIMIT];
private final double[][] ro1 = new double[SBLIMIT][SSLIMIT];
private final double[][] lr0 = new double[SBLIMIT][SSLIMIT];
private final double[][] lr1 = new double[SBLIMIT][SSLIMIT];
private final double[] out1d = new double[SBLIMIT * SSLIMIT];
private final double[][] prevBlock = new double[2][SBLIMIT * SSLIMIT];
private final double[] k0 = new double[SBLIMIT * SSLIMIT];
private final double[] k1 = new double[SBLIMIT * SSLIMIT];
private final int[] nonzero = new int[2];
private final Bitstream stream;
private final Header header;
private final SynthesisFilter filter1, filter2;
private final Decoder player;
private final BitReservoir br = new BitReservoir();
private final SideInfo si = new SideInfo();
private final ScaleFactor[] scaleFactors = new ScaleFactor[] {
new ScaleFactor(), new ScaleFactor() };
private int maxGr;
private int frameStart;
private int part2Start;
private int channels;
private int firstChannel;
private int lastChannel;
private int sfreq;
private final int[] isPos = new int[576];
private final double[] isRatio = new double[576];
private final double[] tsOutCopy = new double[18];
private final double[] rawout = new double[36];
// subband samples are buffered and passed to the
// SynthesisFilter in one go.
private double[] samples1 = new double[32];
private double[] samples2 = new double[32];
private final int[] newSlen = new int[4];
int x, y, v, w;
public Layer3Decoder(Bitstream stream, Header header,
SynthesisFilter filter1, SynthesisFilter filter2, Decoder player) {
this.stream = stream;
this.header = header;
this.filter1 = filter1;
this.filter2 = filter2;
this.player = player;
channels = (header.mode() == Header.MODE_SINGLE_CHANNEL) ? 1 : 2;
maxGr = (header.version() == Header.VERSION_MPEG1) ? 2 : 1;
sfreq = header.sampleFrequency()
+ ((header.version() == Header.VERSION_MPEG1) ? 3
: (header.version() == Header.VERSION_MPEG25_LSF) ? 6
: 0);
if (channels == 2) {
firstChannel = 0;
lastChannel = 1;
}
nonzero[0] = nonzero[1] = 576;
}
public void decodeFrame() throws IOException {
int slots = header.slots();
getSideInfo();
int flushMain = br.getBitCount() & 7;
if (flushMain != 0) {
br.getBits(8 - flushMain);
}
int mainDataEnd = br.getBitCount() >>> 3; // of previous frame
for (int i = 0; i < slots; i++) {
br.putByte(stream.getBits(8));
}
int bytesToDiscard = frameStart - mainDataEnd - si.mainDataBegin;
frameStart += slots;
if (bytesToDiscard < 0) {
return;
}
if (mainDataEnd > 4096) {
frameStart -= 4096;
br.rewindBytes(4096);
}
for (; bytesToDiscard > 0; bytesToDiscard--) {
br.getBits(8);
}
for (int gr = 0; gr < maxGr; gr++) {
for (int ch = 0; ch < channels; ch++) {
part2Start = br.getBitCount();
if (header.version() == Header.VERSION_MPEG1) {
getScaleFactors(ch, gr);
} else {
// MPEG-2 LSF, MPEG-2.5 LSF
getLsfScaleFactors(ch, gr);
}
huffmanDecode(ch, gr);
dequantizeSample(ch == 0 ? ro0 : ro1, ch, gr);
}
stereo(gr);
for (int ch = firstChannel; ch <= lastChannel; ch++) {
reorder(ch == 0 ? lr0 : lr1, ch, gr);
antialias(ch, gr);
hybrid(ch, gr);
for (int sb18 = 18; sb18 < 576; sb18 += 36) {
// Frequency inversion
for (int ss = 1; ss < SSLIMIT; ss += 2) {
out1d[sb18 + ss] = -out1d[sb18 + ss];
}
}
if (ch == 0) {
for (int ss = 0; ss < SSLIMIT; ss++) {
// Polyphase synthesis
for (int sb18 = 0, sb = 0; sb18 < 576; sb18 += 18, sb++) {
samples1[sb] = out1d[sb18 + ss];
}
filter1.calculatePcmSamples(samples1, player);
}
} else {
for (int ss = 0; ss < SSLIMIT; ss++) {
// Polyphase synthesis
for (int sb18 = 0, sb = 0; sb18 < 576; sb18 += 18, sb++) {
samples2[sb] = out1d[sb18 + ss];
}
filter2.calculatePcmSamples(samples2, player);
}
}
}
}
}
/**
* Reads the side info from the stream, assuming the entire frame has been
* read already. Mono : 136 bits (= 17 bytes) Stereo : 256 bits (= 32 bytes)
*/
private void getSideInfo() throws IOException {
if (header.version() == Header.VERSION_MPEG1) {
si.mainDataBegin = stream.getBits(9);
if (channels == 1) {
stream.getBits(5);
} else {
stream.getBits(3);
}
for (int ch = 0; ch < channels; ch++) {
Channel c = si.ch[ch];
c.scfsi[0] = stream.getBits(1);
c.scfsi[1] = stream.getBits(1);
c.scfsi[2] = stream.getBits(1);
c.scfsi[3] = stream.getBits(1);
}
for (int gr = 0; gr < 2; gr++) {
for (int ch = 0; ch < channels; ch++) {
GrInfo gi = si.ch[ch].gr[gr];
gi.part23Length = stream.getBits(12);
gi.bigValues = stream.getBits(9);
gi.globalGain = stream.getBits(8);
gi.scaleFactorCompress = stream.getBits(4);
gi.windowSwitching = stream.getBits(1) != 0;
if (gi.windowSwitching) {
gi.blockType = stream.getBits(2);
gi.mixedBlock = stream.getBits(1) != 0;
gi.tableSelect[0] = stream.getBits(5);
gi.tableSelect[1] = stream.getBits(5);
gi.subblockGain[0] = stream.getBits(3);
gi.subblockGain[1] = stream.getBits(3);
gi.subblockGain[2] = stream.getBits(3);
// Set regionCount: implicit in this case
if (gi.blockType == 0) {
throw new IOException(
"Side info bad: blockType == 0 in split block");
} else if (gi.blockType == 2 && !gi.mixedBlock) {
gi.region0Count = 8;
} else {
gi.region0Count = 7;
}
gi.region1Count = 20 - gi.region0Count;
} else {
gi.tableSelect[0] = stream.getBits(5);
gi.tableSelect[1] = stream.getBits(5);
gi.tableSelect[2] = stream.getBits(5);
gi.region0Count = stream.getBits(4);
gi.region1Count = stream.getBits(3);
gi.blockType = 0;
}
gi.preflag = stream.getBits(1);
gi.scaleFactorScale = stream.getBits(1);
gi.count1TableSelect = stream.getBits(1);
}
}
} else { // MPEG-2 LSF, MPEG-2.5 LSF
si.mainDataBegin = stream.getBits(8);
if (channels == 1) {
stream.getBits(1);
} else {
stream.getBits(2);
}
for (int ch = 0; ch < channels; ch++) {
GrInfo gi = si.ch[ch].gr[0];
gi.part23Length = stream.getBits(12);
gi.bigValues = stream.getBits(9);
gi.globalGain = stream.getBits(8);
gi.scaleFactorCompress = stream.getBits(9);
gi.windowSwitching = stream.getBits(1) != 0;
if (gi.windowSwitching) {
gi.blockType = stream.getBits(2);
gi.mixedBlock = stream.getBits(1) != 0;
gi.tableSelect[0] = stream.getBits(5);
gi.tableSelect[1] = stream.getBits(5);
gi.subblockGain[0] = stream.getBits(3);
gi.subblockGain[1] = stream.getBits(3);
gi.subblockGain[2] = stream.getBits(3);
// Set regionCount: implicit in this case
if (gi.blockType == 0) {
throw new IOException(
"Side info bad: blockType == 0 in split block");
} else if (gi.blockType == 2 && !gi.mixedBlock) {
gi.region0Count = 8;
} else {
gi.region0Count = 7;
gi.region1Count = 20 - gi.region0Count;
}
} else {
gi.tableSelect[0] = stream.getBits(5);
gi.tableSelect[1] = stream.getBits(5);
gi.tableSelect[2] = stream.getBits(5);
gi.region0Count = stream.getBits(4);
gi.region1Count = stream.getBits(3);
gi.blockType = 0;
}
gi.scaleFactorScale = stream.getBits(1);
gi.count1TableSelect = stream.getBits(1);
}
}
}
private void getScaleFactors(int ch, int gr) {
int sfb, window;
GrInfo gi = si.ch[ch].gr[gr];
int scaleComp = gi.scaleFactorCompress;
int[][] slen = Constants.SLEN;
int length0 = slen[0][scaleComp];
int length1 = slen[1][scaleComp];
ScaleFactor sfc = scaleFactors[ch];
int[] sfl = sfc.l;
int[][] sfs = sfc.s;
if (gi.windowSwitching && gi.blockType == 2) {
if (gi.mixedBlock) {
for (sfb = 0; sfb < 8; sfb++) {
sfl[sfb] = br.getBits(slen[0][gi.scaleFactorCompress]);
}
for (sfb = 3; sfb < 6; sfb++) {
for (window = 0; window < 3; window++) {
sfs[window][sfb] = br
.getBits(slen[0][gi.scaleFactorCompress]);
}
}
for (sfb = 6; sfb < 12; sfb++) {
for (window = 0; window < 3; window++) {
sfs[window][sfb] = br
.getBits(slen[1][gi.scaleFactorCompress]);
}
}
for (sfb = 12, window = 0; window < 3; window++) {
sfs[window][sfb] = 0;
}
} else { // SHORT
sfs[0][0] = br.getBits(length0);
sfs[1][0] = br.getBits(length0);
sfs[2][0] = br.getBits(length0);
sfs[0][1] = br.getBits(length0);
sfs[1][1] = br.getBits(length0);
sfs[2][1] = br.getBits(length0);
sfs[0][2] = br.getBits(length0);
sfs[1][2] = br.getBits(length0);
sfs[2][2] = br.getBits(length0);
sfs[0][3] = br.getBits(length0);
sfs[1][3] = br.getBits(length0);
sfs[2][3] = br.getBits(length0);
sfs[0][4] = br.getBits(length0);
sfs[1][4] = br.getBits(length0);
sfs[2][4] = br.getBits(length0);
sfs[0][5] = br.getBits(length0);
sfs[1][5] = br.getBits(length0);
sfs[2][5] = br.getBits(length0);
sfs[0][6] = br.getBits(length1);
sfs[1][6] = br.getBits(length1);
sfs[2][6] = br.getBits(length1);
sfs[0][7] = br.getBits(length1);
sfs[1][7] = br.getBits(length1);
sfs[2][7] = br.getBits(length1);
sfs[0][8] = br.getBits(length1);
sfs[1][8] = br.getBits(length1);
sfs[2][8] = br.getBits(length1);
sfs[0][9] = br.getBits(length1);
sfs[1][9] = br.getBits(length1);
sfs[2][9] = br.getBits(length1);
sfs[0][10] = br.getBits(length1);
sfs[1][10] = br.getBits(length1);
sfs[2][10] = br.getBits(length1);
sfs[0][11] = br.getBits(length1);
sfs[1][11] = br.getBits(length1);
sfs[2][11] = br.getBits(length1);
sfs[0][12] = 0;
sfs[1][12] = 0;
sfs[2][12] = 0;
} // SHORT
} else { // LONG types 0,1,3
if ((si.ch[ch].scfsi[0] == 0) || (gr == 0)) {
sfl[0] = br.getBits(length0);
sfl[1] = br.getBits(length0);
sfl[2] = br.getBits(length0);
sfl[3] = br.getBits(length0);
sfl[4] = br.getBits(length0);
sfl[5] = br.getBits(length0);
}
if ((si.ch[ch].scfsi[1] == 0) || (gr == 0)) {
sfl[6] = br.getBits(length0);
sfl[7] = br.getBits(length0);
sfl[8] = br.getBits(length0);
sfl[9] = br.getBits(length0);
sfl[10] = br.getBits(length0);
}
if ((si.ch[ch].scfsi[2] == 0) || (gr == 0)) {
sfl[11] = br.getBits(length1);
sfl[12] = br.getBits(length1);
sfl[13] = br.getBits(length1);
sfl[14] = br.getBits(length1);
sfl[15] = br.getBits(length1);
}
if ((si.ch[ch].scfsi[3] == 0) || (gr == 0)) {
sfl[16] = br.getBits(length1);
sfl[17] = br.getBits(length1);
sfl[18] = br.getBits(length1);
sfl[19] = br.getBits(length1);
sfl[20] = br.getBits(length1);
}
sfl[21] = 0;
sfl[22] = 0;
}
}
private void getLsfScaleData(int ch, int gr) {
int scaleFactorComp, intScalefacComp;
int modeExt = header.modeExtension();
int blockTypeNumber;
int blockNumber = 0;
GrInfo gi = si.ch[ch].gr[gr];
scaleFactorComp = gi.scaleFactorCompress;
if (gi.blockType == 2) {
if (!gi.mixedBlock) {
blockTypeNumber = 1;
} else {
blockTypeNumber = 2;
}
} else {
blockTypeNumber = 0;
}
if (!(((modeExt == 1) || (modeExt == 3)) && (ch == 1))) {
if (scaleFactorComp < 400) {
newSlen[0] = (scaleFactorComp >>> 4) / 5;
newSlen[1] = (scaleFactorComp >>> 4) % 5;
newSlen[2] = (scaleFactorComp & 0xF) >>> 2;
newSlen[3] = (scaleFactorComp & 3);
si.ch[ch].gr[gr].preflag = 0;
blockNumber = 0;
} else if (scaleFactorComp < 500) {
newSlen[0] = ((scaleFactorComp - 400) >>> 2) / 5;
newSlen[1] = ((scaleFactorComp - 400) >>> 2) % 5;
newSlen[2] = (scaleFactorComp - 400) & 3;
newSlen[3] = 0;
si.ch[ch].gr[gr].preflag = 0;
blockNumber = 1;
} else if (scaleFactorComp < 512) {
newSlen[0] = (scaleFactorComp - 500) / 3;
newSlen[1] = (scaleFactorComp - 500) % 3;
newSlen[2] = 0;
newSlen[3] = 0;
si.ch[ch].gr[gr].preflag = 1;
blockNumber = 2;
}
}
if ((((modeExt == 1) || (modeExt == 3)) && (ch == 1))) {
intScalefacComp = scaleFactorComp >>> 1;
if (intScalefacComp < 180) {
newSlen[0] = intScalefacComp / 36;
newSlen[1] = (intScalefacComp % 36) / 6;
newSlen[2] = (intScalefacComp % 36) % 6;
newSlen[3] = 0;
si.ch[ch].gr[gr].preflag = 0;
blockNumber = 3;
} else if (intScalefacComp < 244) {
newSlen[0] = ((intScalefacComp - 180) & 0x3F) >>> 4;
newSlen[1] = ((intScalefacComp - 180) & 0xF) >>> 2;
newSlen[2] = (intScalefacComp - 180) & 3;
newSlen[3] = 0;
si.ch[ch].gr[gr].preflag = 0;
blockNumber = 4;
} else if (intScalefacComp < 255) {
newSlen[0] = (intScalefacComp - 244) / 3;
newSlen[1] = (intScalefacComp - 244) % 3;
newSlen[2] = 0;
newSlen[3] = 0;
si.ch[ch].gr[gr].preflag = 0;
blockNumber = 5;
}
}
for (int x1 = 0; x1 < 45; x1++) {
// TODO: why 45, not 54?
scaleFactorBuffer[x1] = 0;
}
for (int i = 0, m = 0; i < 4; i++) {
int len = Constants.NR_OF_SFB_BLOCK[blockNumber][blockTypeNumber][i];
for (int j = 0; j < len; j++) {
scaleFactorBuffer[m] = (newSlen[i] == 0) ? 0
: br.getBits(newSlen[i]);
m++;
}
}
}
private void getLsfScaleFactors(int ch, int gr) {
int m = 0;
int sfb, window;
GrInfo gi = si.ch[ch].gr[gr];
getLsfScaleData(ch, gr);
ScaleFactor sf = scaleFactors[ch];
if (gi.windowSwitching && (gi.blockType == 2)) {
if (gi.mixedBlock) {
for (sfb = 0; sfb < 8; sfb++) {
sf.l[sfb] = scaleFactorBuffer[m];
m++;
}
for (sfb = 3; sfb < 12; sfb++) {
for (window = 0; window < 3; window++) {
sf.s[window][sfb] = scaleFactorBuffer[m];
m++;
}
}
for (window = 0; window < 3; window++) {
sf.s[window][12] = 0;
}
} else { // SHORT
for (sfb = 0; sfb < 12; sfb++) {
for (window = 0; window < 3; window++) {
sf.s[window][sfb] = scaleFactorBuffer[m];
m++;
}
}
for (window = 0; window < 3; window++) {
sf.s[window][12] = 0;
}
}
} else { // LONG types 0,1,3
for (sfb = 0; sfb < 21; sfb++) {
sf.l[sfb] = scaleFactorBuffer[m];
m++;
}
sf.l[21] = 0;
sf.l[22] = 0;
}
}
private void huffmanDecode(final int ch, final int gr) {
GrInfo gi = si.ch[ch].gr[gr];
x = y = v = w = 0;
int part23End = part2Start + gi.part23Length;
int region1Start;
int region2Start;
int buf, buf1;
Huffman huffman;
// Find region boundary for short block case
if (gi.windowSwitching && (gi.blockType == 2)) {
// Region2.
// MS: Extrahandling for 8KHZ
region1Start = (sfreq == 8) ? 72 : 36; // sfb[9/3]*3=36 or in case
// 8KHZ = 72
region2Start = 576; // No Region2 for short block case
} else { // Find region boundary for long block case
buf = gi.region0Count + 1;
buf1 = buf + gi.region1Count + 1;
if (buf1 > Constants.SF_BAND_INDEX[sfreq].l.length - 1) {
buf1 = Constants.SF_BAND_INDEX[sfreq].l.length - 1;
}
region1Start = Constants.SF_BAND_INDEX[sfreq].l[buf];
region2Start = Constants.SF_BAND_INDEX[sfreq].l[buf1]; /* MI */
}
int index = 0;
for (int i = 0; i < (gi.bigValues << 1); i += 2) {
if (i < region1Start) {
huffman = Huffman.HUFFMAN[gi.tableSelect[0]];
} else if (i < region2Start) {
huffman = Huffman.HUFFMAN[gi.tableSelect[1]];
} else {
huffman = Huffman.HUFFMAN[gi.tableSelect[2]];
}
huffman.decode(this, br);
is1d[index++] = x;
is1d[index++] = y;
}
// Read count1 area
huffman = Huffman.HUFFMAN[gi.count1TableSelect + 32];
int numBits = br.getBitCount();
while ((numBits < part23End) && (index < 576)) {
huffman.decode(this, br);
is1d[index++] = v;
is1d[index++] = w;
is1d[index++] = x;
is1d[index++] = y;
numBits = br.getBitCount();
}
if (numBits > part23End) {
br.rewindBits(numBits - part23End);
index -= 4;
}
numBits = br.getBitCount();
// Dismiss stuffing bits
if (numBits < part23End) {
br.getBits(part23End - numBits);
}
// Zero out rest
if (index < 576) {
nonzero[ch] = index;
} else {
nonzero[ch] = 576;
}
if (index < 0) {
index = 0;
}
// may not be necessary
for (; index < 576; index++) {
is1d[index] = 0;
}
}
private void iStereoKValues(int pos, int type, int i) {
if (pos == 0) {
k0[i] = 1.0f;
k1[i] = 1.0f;
} else if ((pos & 1) != 0) {
k0[i] = Constants.IO[type][(pos + 1) >>> 1];
k1[i] = 1.0f;
} else {
k0[i] = 1.0f;
k1[i] = Constants.IO[type][pos >>> 1];
}
}
private static double getT43(int abv, double globalGain) {
switch (abv) {
case 0:
return 0.0f;
case 1:
return globalGain;
case -1:
return -globalGain;
case 2:
case 3:
case 4:
case 5:
case 6:
return globalGain * Constants.T43[abv];
case -2:
case -3:
case -4:
case -5:
case -6:
return -globalGain * Constants.T43[-abv];
default:
if (abv > 0) {
if (abv < Constants.T43_SIZE) {
return globalGain * Constants.T43[abv];
}
return globalGain * Math.pow(abv, D43);
}
if (-abv < Constants.T43_SIZE) {
return -globalGain * Constants.T43[-abv];
}
return -globalGain * Math.pow(-abv, D43);
}
}
private void dequantizeSample(double[][] xr, int ch, int gr) {
GrInfo gi = si.ch[ch].gr[gr];
int nextCb; // next critical band boundary
Constants.SBI sbif = Constants.SF_BAND_INDEX[sfreq];
int[] s = sbif.s;
int[] l = sbif.l;
int cbWidth = 0;
int len = nonzero[ch];
// Compute overall (global) scaling
double globalGain = Constants.POW2[gi.globalGain];
int i = 0;
for (int sb = 0; sb < SBLIMIT; sb++) {
for (int ss = 0; ss < SSLIMIT; ss++, i++) {
if (i >= len) {
break;
}
xr[sb][ss] = getT43(is1d[i], globalGain);
}
}
// choose correct scalefactor band per block type, initalize boundary
if (gi.windowSwitching && (gi.blockType == 2)) {
if (gi.mixedBlock) {
nextCb = l[1];
// LONG blocks: 0,1,3
} else {
cbWidth = s[1];
nextCb = (cbWidth << 2) - cbWidth;
}
} else {
nextCb = l[1];
// LONG blocks: 0,1,3
}
int cb = 0;
int cbBegin = 0;
int index = 0;
// apply formula per block type
for (int j = 0; j < len; j++) {
if (index == nextCb) {
// adjust critical band boundary
if (gi.windowSwitching && gi.blockType == 2) {
if (gi.mixedBlock) {
if (index == l[8]) {
nextCb = s[4];
nextCb = (nextCb << 2) - nextCb;
cb = 3;
cbWidth = s[4] - s[3];
cbBegin = s[3];
cbBegin = (cbBegin << 2) - cbBegin;
} else if (index < l[8]) {
nextCb = l[(++cb) + 1];
} else {
nextCb = s[(++cb) + 1];
nextCb = (nextCb << 2) - nextCb;
cbBegin = s[cb];
cbWidth = s[cb + 1] - cbBegin;
cbBegin = (cbBegin << 2) - cbBegin;
}
} else {
nextCb = s[(++cb) + 1];
nextCb = (nextCb << 2) - nextCb;
cbBegin = s[cb];
cbWidth = s[cb + 1] - cbBegin;
cbBegin = (cbBegin << 2) - cbBegin;
}
} else { // long blocks
nextCb = l[(++cb) + 1];
}
}
int sb = j / SSLIMIT;
int ss = j - sb * SSLIMIT; // % SSLIMIT
// Do long/short dependent scaling operations
int idx;
if (gi.windowSwitching && gi.blockType == 2
&& (!gi.mixedBlock || j >= 36)) {
int ti = (index - cbBegin) / cbWidth;
idx = scaleFactors[ch].s[ti][cb] << gi.scaleFactorScale;
idx += (gi.subblockGain[ti] << 2);
} else {
// LONG block types 0,1,3 & 1st 2 subbands of switched blocks
idx = scaleFactors[ch].l[cb];
if (gi.preflag != 0) {
idx += Constants.PRETAB[cb];
}
idx = idx << gi.scaleFactorScale;
}
xr[sb][ss] *= Constants.TWO_TO_NEGATIVE_HALF_POW[idx];
index++;
}
for (int j = len; j < 576; j++) {
int sb = j / SSLIMIT;
int ss = j - sb * SSLIMIT; // % SSLIMIT
xr[sb][ss] = 0.0f;
}
return;
}
private void reorder(double[][] xr, int ch, int gr) {
GrInfo gi = si.ch[ch].gr[gr];
if (gi.windowSwitching && gi.blockType == 2) {
for (int index = 0; index < 576; index++) {
out1d[index] = 0.0f;
}
if (gi.mixedBlock) {
// NO REORDER FOR LOW 2 SUBBANDS
for (int index = 0; index < 36; index++) {
int sb = index / SSLIMIT;
int ss = index - sb * SSLIMIT; // % SSLIMIT
out1d[index] = xr[sb][ss];
}
// REORDERING FOR REST SWITCHED SHORT
for (int sfb = 3; sfb < 13; sfb++) {
int sfbStart = Constants.SF_BAND_INDEX[sfreq].s[sfb];
int sfbLines = Constants.SF_BAND_INDEX[sfreq].s[sfb + 1]
- sfbStart;
int sfbStart3 = (sfbStart << 2) - sfbStart;
for (int freq = 0, freq3 = 0; freq < sfbLines; freq++, freq3 += 3) {
int srcLine = sfbStart3 + freq;
int desLine = sfbStart3 + freq3;
int sb = srcLine / SSLIMIT;
int ss = srcLine - sb * SSLIMIT; // % SSLIMIT
out1d[desLine] = xr[sb][ss];
srcLine += sfbLines;
desLine++;
sb = srcLine / SSLIMIT;
ss = srcLine - sb * SSLIMIT; // % SSLIMIT
out1d[desLine] = xr[sb][ss];
srcLine += sfbLines;
desLine++;
sb = srcLine / SSLIMIT;
ss = srcLine - sb * SSLIMIT; // % SSLIMIT
out1d[desLine] = xr[sb][ss];
}
}
} else {
// pure short
int[] reorder = Constants.REORDER_TABLE[sfreq];
for (int index = 0; index < 576; index++) {
int j = reorder[index];
int sb = j / SSLIMIT;
int ss = j - sb * SSLIMIT; // % SSLIMIT
out1d[index] = xr[sb][ss];
}
}
} else {
// long blocks
for (int i = 0, sb = 0; sb < SBLIMIT; sb++) {
for (int ss = 0; ss < SSLIMIT; ss++, i++) {
out1d[i] = xr[sb][ss];
}
}
}
}
private void stereo(int gr) {
if (channels == 1) { // mono , bypass xr[0][][] to lr[0][][]
for (int sb = 0; sb < SBLIMIT; sb++) {
for (int ss = 0; ss < SSLIMIT; ss += 3) {
lr0[sb][ss] = ro0[sb][ss];
lr0[sb][ss + 1] = ro0[sb][ss + 1];
lr0[sb][ss + 2] = ro0[sb][ss + 2];
}
}
return;
}
GrInfo gi = si.ch[0].gr[gr];
int modeExt = header.modeExtension();
int sfb;
int temp, temp2;
boolean msStereo = ((header.mode() == Header.MODE_JOINT_STEREO)
&& ((modeExt & 0x2) != 0));
boolean iStereo = ((header.mode() == Header.MODE_JOINT_STEREO)
&& ((modeExt & 0x1) != 0));
boolean lsf = ((header.version() == Header.VERSION_MPEG2_LSF
|| header.version() == Header.VERSION_MPEG25_LSF));
int ioType = (gi.scaleFactorCompress & 1);
for (int i = 0; i < 576; i++) {
isPos[i] = 7;
isRatio[i] = 0.0f;
}
if (iStereo) {
SBI sbif = Constants.SF_BAND_INDEX[sfreq];
int[] s = sbif.s;
int[] l = sbif.l;
if (gi.windowSwitching && gi.blockType == 2) {
if (gi.mixedBlock) {
int maxSfb = 0;
for (int j = 0; j < 3; j++) {
int sfbcnt = 2;
for (sfb = 12; sfb >= 3; sfb--) {
int i = s[sfb];
int lines = s[sfb + 1] - i;
i = (i << 2) - i + (j + 1) * lines - 1;
while (lines > 0) {
if (ro1[i / 18][i % 18] != 0.0f) {
sfbcnt = sfb;
sfb = -10;
lines = -10;
}
lines--;
i--;
}
}
sfb = sfbcnt + 1;
if (sfb > maxSfb) {
maxSfb = sfb;
}
while (sfb < 12) {
temp = s[sfb];
int sb = s[sfb + 1] - temp;
int i = (temp << 2) - temp + j * sb;
for (; sb > 0; sb--) {
isPos[i] = scaleFactors[1].s[j][sfb];
if (isPos[i] != 7) {
if (lsf) {
iStereoKValues(isPos[i], ioType, i);
} else {
isRatio[i] = Constants.TAN12[isPos[i]];
}
}
i++;
}
sfb++;
}
sfb = s[10];
int sb = s[11] - sfb;
sfb = (sfb << 2) - sfb + j * sb;
temp = s[11];
sb = s[12] - temp;
int i = (temp << 2) - temp + j * sb;
for (; sb > 0; sb--) {
isPos[i] = isPos[sfb];
if (lsf) {
k0[i] = k0[sfb];
k1[i] = k1[sfb];
} else {
isRatio[i] = isRatio[sfb];
}
i++;
}
}
if (maxSfb <= 3) {
int i = 2;
int ss = 17;
int sb = -1;
while (i >= 0) {
if (ro1[i][ss] != 0.0f) {
sb = (i << 4) + (i << 1) + ss;
i = -1;
} else {
ss--;
if (ss < 0) {
i--;
ss = 17;
}
}
}
i = 0;
while (l[i] <= sb) {
i++;
}
sfb = i;
i = l[i];
for (; sfb < 8; sfb++) {
sb = l[sfb + 1] - l[sfb];
for (; sb > 0; sb--) {
isPos[i] = scaleFactors[1].l[sfb];
if (isPos[i] != 7) {
if (lsf) {
iStereoKValues(isPos[i], ioType, i);
} else {
isRatio[i] = Constants.TAN12[isPos[i]];
}
}
i++;
}
}
}
} else {
for (int j = 0; j < 3; j++) {
int sfbcnt;
sfbcnt = -1;
for (sfb = 12; sfb >= 0; sfb--) {
temp = s[sfb];
int lines = s[sfb + 1] - temp;
int i = (temp << 2) - temp + (j + 1) * lines - 1;
while (lines > 0) {
if (ro1[i / 18][i % 18] != 0.0f) {
sfbcnt = sfb;
sfb = -10;
lines = -10;
}
lines--;
i--;
}
}
sfb = sfbcnt + 1;
while (sfb < 12) {
temp = s[sfb];
int sb = s[sfb + 1] - temp;
int i = (temp << 2) - temp + j * sb;
for (; sb > 0; sb--) {
isPos[i] = scaleFactors[1].s[j][sfb];
if (isPos[i] != 7) {
if (lsf) {
iStereoKValues(isPos[i], ioType, i);
} else {
isRatio[i] = Constants.TAN12[isPos[i]];
}
}
i++;
} // for (; sb>0 ...
sfb++;
} // while (sfb<12)
temp = s[10];
temp2 = s[11];
int sb = temp2 - temp;
sfb = (temp << 2) - temp + j * sb;
sb = s[12] - temp2;
int i = (temp2 << 2) - temp2 + j * sb;
for (; sb > 0; sb--) {
isPos[i] = isPos[sfb];
if (lsf) {
k0[i] = k0[sfb];
k1[i] = k1[sfb];
} else {
isRatio[i] = isRatio[sfb];
}
i++;
}
}
}
} else {
int i = 31;
int ss = 17;
int sb = 0;
while (i >= 0) {
if (ro1[i][ss] != 0.0f) {
sb = (i << 4) + (i << 1) + ss;
i = -1;
} else {
ss--;
if (ss < 0) {
i--;
ss = 17;
}
}
}
i = 0;
while (l[i] <= sb) {
i++;
}
sfb = i;
i = l[i];
for (; sfb < 21; sfb++) {
sb = l[sfb + 1] - l[sfb];
for (; sb > 0; sb--) {
isPos[i] = scaleFactors[1].l[sfb];
if (isPos[i] != 7) {
if (lsf) {
iStereoKValues(isPos[i], ioType, i);
} else {
isRatio[i] = Constants.TAN12[isPos[i]];
}
}
i++;
}
}
sfb = l[20];
for (sb = 576 - l[21]; (sb > 0) && (i < 576); sb--) {
isPos[i] = isPos[sfb]; // error here : i >=576
if (lsf) {
k0[i] = k0[sfb];
k1[i] = k1[sfb];
} else {
isRatio[i] = isRatio[sfb];
}
i++;
}
}
}
for (int sb = 0, i = 0; sb < SBLIMIT; sb++) {
for (int ss = 0; ss < SSLIMIT; ss++) {
if (isPos[i] == 7) {
if (msStereo) {
lr0[sb][ss] = (ro0[sb][ss] + ro1[sb][ss])
* 0.707106781f;
lr1[sb][ss] = (ro0[sb][ss] - ro1[sb][ss])
* 0.707106781f;
} else {
lr0[sb][ss] = ro0[sb][ss];
lr1[sb][ss] = ro1[sb][ss];
}
} else if (iStereo) {
if (lsf) {
lr0[sb][ss] = ro0[sb][ss] * k0[i];
lr1[sb][ss] = ro0[sb][ss] * k1[i];
} else {
lr1[sb][ss] = ro0[sb][ss] / (1 + isRatio[i]);
lr0[sb][ss] = lr1[sb][ss] * isRatio[i];
}
}
i++;
}
}
}
private void antialias(int ch, int gr) {
int sb18, ss, sb18lim;
GrInfo gi = si.ch[ch].gr[gr];
// 31 alias-reduction operations between each pair of sub-bands
// with 8 butterflies between each pair
if (gi.windowSwitching && (gi.blockType == 2) && !gi.mixedBlock) {
return;
}
if (gi.windowSwitching && gi.mixedBlock && (gi.blockType == 2)) {
sb18lim = 18;
} else {
sb18lim = 558;
}
for (sb18 = 0; sb18 < sb18lim; sb18 += 18) {
for (ss = 0; ss < 8; ss++) {
int srcIdx1 = sb18 + 17 - ss;
int srcIdx2 = sb18 + 18 + ss;
double bu = out1d[srcIdx1];
double bd = out1d[srcIdx2];
out1d[srcIdx1] = (bu * Constants.CS[ss])
- (bd * Constants.CA[ss]);
out1d[srcIdx2] = (bd * Constants.CS[ss])
+ (bu * Constants.CA[ss]);
}
}
}
private void hybrid(int ch, int gr) {
GrInfo gi = si.ch[ch].gr[gr];
for (int sb18 = 0; sb18 < 576; sb18 += 18) {
int bt = (gi.windowSwitching && gi.mixedBlock && (sb18 < 36)) ? 0
: gi.blockType;
double[] tsOut = out1d;
double[] r = rawout;
for (int cc = 0; cc < 18; cc++) {
tsOutCopy[cc] = tsOut[cc + sb18];
}
fastInvMdct(tsOutCopy, r, bt);
for (int cc = 0; cc < 18; cc++) {
tsOut[cc + sb18] = tsOutCopy[cc];
}
// overlap addition
double[] p = prevBlock[ch];
tsOut[0 + sb18] = r[0] + p[sb18 + 0];
p[sb18 + 0] = r[18];
tsOut[1 + sb18] = r[1] + p[sb18 + 1];
p[sb18 + 1] = r[19];
tsOut[2 + sb18] = r[2] + p[sb18 + 2];
p[sb18 + 2] = r[20];
tsOut[3 + sb18] = r[3] + p[sb18 + 3];
p[sb18 + 3] = r[21];
tsOut[4 + sb18] = r[4] + p[sb18 + 4];
p[sb18 + 4] = r[22];
tsOut[5 + sb18] = r[5] + p[sb18 + 5];
p[sb18 + 5] = r[23];
tsOut[6 + sb18] = r[6] + p[sb18 + 6];
p[sb18 + 6] = r[24];
tsOut[7 + sb18] = r[7] + p[sb18 + 7];
p[sb18 + 7] = r[25];
tsOut[8 + sb18] = r[8] + p[sb18 + 8];
p[sb18 + 8] = r[26];
tsOut[9 + sb18] = r[9] + p[sb18 + 9];
p[sb18 + 9] = r[27];
tsOut[10 + sb18] = r[10] + p[sb18 + 10];
p[sb18 + 10] = r[28];
tsOut[11 + sb18] = r[11] + p[sb18 + 11];
p[sb18 + 11] = r[29];
tsOut[12 + sb18] = r[12] + p[sb18 + 12];
p[sb18 + 12] = r[30];
tsOut[13 + sb18] = r[13] + p[sb18 + 13];
p[sb18 + 13] = r[31];
tsOut[14 + sb18] = r[14] + p[sb18 + 14];
p[sb18 + 14] = r[32];
tsOut[15 + sb18] = r[15] + p[sb18 + 15];
p[sb18 + 15] = r[33];
tsOut[16 + sb18] = r[16] + p[sb18 + 16];
p[sb18 + 16] = r[34];
tsOut[17 + sb18] = r[17] + p[sb18 + 17];
p[sb18 + 17] = r[35];
}
}
private static void fastInvMdct(double[] in, double[] out, int blockType) {
double t0, t1, t2, t3, t4, t5, t6, t7, t8, t9;
double t10, t11, t12, t13, t14, t15, t16, t17;
if (blockType == 2) {
for (int p = 0; p < 36; p += 9) {
out[p] = out[p
+ 1] = out[p + 2] = out[p + 3] = out[p + 4] = 0.0f;
out[p + 5] = out[p + 6] = out[p + 7] = out[p + 8] = 0.0f;
}
int sixI = 0;
for (int i = 0; i < 3; i++) {
// 12 point IMDCT
// Begin 12 point IDCT
// Input aliasing for 12 pt IDCT
in[15 + i] += in[12 + i];
in[12 + i] += in[9 + i];
in[9 + i] += in[6 + i];
in[6 + i] += in[3 + i];
in[3 + i] += in[0 + i];
// Input aliasing on odd indices (for 6 point IDCT)
in[15 + i] += in[9 + i];
in[9 + i] += in[3 + i];
// 3 point IDCT on even indices
double pp1, pp2, sum;
pp2 = in[12 + i] * 0.500000000f;
pp1 = in[6 + i] * 0.866025403f;
sum = in[0 + i] + pp2;
t1 = in[0 + i] - in[12 + i];
t0 = sum + pp1;
t2 = sum - pp1;
// End 3 point IDCT on even indices
// 3 point IDCT on odd indices (for 6 point IDCT)
pp2 = in[15 + i] * 0.500000000f;
pp1 = in[9 + i] * 0.866025403f;
sum = in[3 + i] + pp2;
t4 = in[3 + i] - in[15 + i];
t5 = sum + pp1;
t3 = sum - pp1;
// End 3 point IDCT on odd indices
// Twiddle factors on odd indices (for 6 point IDCT)
t3 *= 1.931851653f;
t4 *= 0.707106781f;
t5 *= 0.517638090f;
// Output butterflies on 2 3 point IDCT's (for 6 point IDCT)
double save = t0;
t0 += t5;
t5 = save - t5;
save = t1;
t1 += t4;
t4 = save - t4;
save = t2;
t2 += t3;
t3 = save - t3;
// End 6 point IDCT
// Twiddle factors on indices (for 12 point IDCT)
t0 *= 0.504314480f;
t1 *= 0.541196100f;
t2 *= 0.630236207f;
t3 *= 0.821339815f;
t4 *= 1.306562965f;
t5 *= 3.830648788f;
// End 12 point IDCT
// Shift to 12 point modified IDCT, multiply by window type 2
t8 = -t0 * 0.793353340f;
t9 = -t0 * 0.608761429f;
t7 = -t1 * 0.923879532f;
t10 = -t1 * 0.382683432f;
t6 = -t2 * 0.991444861f;
t11 = -t2 * 0.130526192f;
t0 = t3;
t1 = t4 * 0.382683432f;
t2 = t5 * 0.608761429f;
t3 = -t5 * 0.793353340f;
t4 = -t4 * 0.923879532f;
t5 = -t0 * 0.991444861f;
t0 *= 0.130526192f;
out[sixI + 6] += t0;
out[sixI + 7] += t1;
out[sixI + 8] += t2;
out[sixI + 9] += t3;
out[sixI + 10] += t4;
out[sixI + 11] += t5;
out[sixI + 12] += t6;
out[sixI + 13] += t7;
out[sixI + 14] += t8;
out[sixI + 15] += t9;
out[sixI + 16] += t10;
out[sixI + 17] += t11;
sixI += 6;
}
} else {
// 36 point IDCT
// input aliasing for 36 point IDCT
in[17] += in[16];
in[16] += in[15];
in[15] += in[14];
in[14] += in[13];
in[13] += in[12];
in[12] += in[11];
in[11] += in[10];
in[10] += in[9];
in[9] += in[8];
in[8] += in[7];
in[7] += in[6];
in[6] += in[5];
in[5] += in[4];
in[4] += in[3];
in[3] += in[2];
in[2] += in[1];
in[1] += in[0];
// 18 point IDCT for odd indices
// input aliasing for 18 point IDCT
in[17] += in[15];
in[15] += in[13];
in[13] += in[11];
in[11] += in[9];
in[9] += in[7];
in[7] += in[5];
in[5] += in[3];
in[3] += in[1];
double tmp0, tmp1, tmp2, tmp3, tmp4, tmp0b, tmp1b, tmp2b, tmp3b;
double tmp0o, tmp1o, tmp2o, tmp3o, tmp4o, tmp0ob, tmp1ob, tmp2ob,
tmp3ob;
// Fast 9 Point Inverse Discrete Cosine Transform
//
// By Francois-Raymond Boyer
// mailto:boyerf@iro.umontreal.ca
// http://www.iro.umontreal.ca/~boyerf
//
// The code has been optimized for Intel processors
// (takes a lot of time to convert double to and from iternal FPU
// representation)
//
// It is a simple "factorization" of the IDCT matrix.
// 9 point IDCT on even indices
// 5 points on odd indices (not really an IDCT)
double i00 = in[0] + in[0];
double iip12 = i00 + in[12];
tmp0 = iip12 + in[4] * 1.8793852415718f + in[8] * 1.532088886238f
+ in[16] * 0.34729635533386f;
tmp1 = i00 + in[4] - in[8] - in[12] - in[12] - in[16];
tmp2 = iip12 - in[4] * 0.34729635533386f - in[8] * 1.8793852415718f
+ in[16] * 1.532088886238f;
tmp3 = iip12 - in[4] * 1.532088886238f + in[8] * 0.34729635533386f
- in[16] * 1.8793852415718f;
tmp4 = in[0] - in[4] + in[8] - in[12] + in[16];
// 4 points on even indices
double i6s = in[6] * 1.732050808f; // Sqrt[3]
tmp0b = in[2] * 1.9696155060244f + i6s + in[10] * 1.2855752193731f
+ in[14] * 0.68404028665134f;
tmp1b = (in[2] - in[10] - in[14]) * 1.732050808f;
tmp2b = in[2] * 1.2855752193731f - i6s - in[10] * 0.68404028665134f
+ in[14] * 1.9696155060244f;
tmp3b = in[2] * 0.68404028665134f - i6s + in[10] * 1.9696155060244f
- in[14] * 1.2855752193731f;
// 9 point IDCT on odd indices
// 5 points on odd indices (not really an IDCT)
double i0 = in[0 + 1] + in[0 + 1];
double i0p12 = i0 + in[12 + 1];
tmp0o = i0p12 + in[4 + 1] * 1.8793852415718f
+ in[8 + 1] * 1.532088886238f
+ in[16 + 1] * 0.34729635533386f;
tmp1o = i0 + in[4 + 1] - in[8 + 1] - in[12 + 1] - in[12 + 1]
- in[16 + 1];
tmp2o = i0p12 - in[4 + 1] * 0.34729635533386f
- in[8 + 1] * 1.8793852415718f
+ in[16 + 1] * 1.532088886238f;
tmp3o = i0p12 - in[4 + 1] * 1.532088886238f
+ in[8 + 1] * 0.34729635533386f
- in[16 + 1] * 1.8793852415718f;
tmp4o = (in[0 + 1] - in[4 + 1] + in[8 + 1] - in[12 + 1]
+ in[16 + 1]) * 0.707106781f; // Twiddled
// 4 points on even indices
double i7s = in[6 + 1] * 1.732050808f; // Sqrt[3]
tmp0ob = in[2 + 1] * 1.9696155060244f + i7s
+ in[10 + 1] * 1.2855752193731f
+ in[14 + 1] * 0.68404028665134f;
tmp1ob = (in[2 + 1] - in[10 + 1] - in[14 + 1]) * 1.732050808f;
tmp2ob = in[2 + 1] * 1.2855752193731f - i7s
- in[10 + 1] * 0.68404028665134f
+ in[14 + 1] * 1.9696155060244f;
tmp3ob = in[2 + 1] * 0.68404028665134f - i7s
+ in[10 + 1] * 1.9696155060244f
- in[14 + 1] * 1.2855752193731f;
// Twiddle factors on odd indices and
// Butterflies on 9 point IDCT's and
// twiddle factors for 36 point IDCT
double e, o;
e = tmp0 + tmp0b;
o = (tmp0o + tmp0ob) * 0.501909918f;
t0 = e + o;
t17 = e - o;
e = tmp1 + tmp1b;
o = (tmp1o + tmp1ob) * 0.517638090f;
t1 = e + o;
t16 = e - o;
e = tmp2 + tmp2b;
o = (tmp2o + tmp2ob) * 0.551688959f;
t2 = e + o;
t15 = e - o;
e = tmp3 + tmp3b;
o = (tmp3o + tmp3ob) * 0.610387294f;
t3 = e + o;
t14 = e - o;
t4 = tmp4 + tmp4o;
t13 = tmp4 - tmp4o;
e = tmp3 - tmp3b;
o = (tmp3o - tmp3ob) * 0.871723397f;
t5 = e + o;
t12 = e - o;
e = tmp2 - tmp2b;
o = (tmp2o - tmp2ob) * 1.183100792f;
t6 = e + o;
t11 = e - o;
e = tmp1 - tmp1b;
o = (tmp1o - tmp1ob) * 1.931851653f;
t7 = e + o;
t10 = e - o;
e = tmp0 - tmp0b;
o = (tmp0o - tmp0ob) * 5.736856623f;
t8 = e + o;
t9 = e - o;
// end 36 point IDCT */
// shift to modified IDCT
double[] win = Constants.WIN[blockType];
out[0] = -t9 * win[0];
out[1] = -t10 * win[1];
out[2] = -t11 * win[2];
out[3] = -t12 * win[3];
out[4] = -t13 * win[4];
out[5] = -t14 * win[5];
out[6] = -t15 * win[6];
out[7] = -t16 * win[7];
out[8] = -t17 * win[8];
out[9] = t17 * win[9];
out[10] = t16 * win[10];
out[11] = t15 * win[11];
out[12] = t14 * win[12];
out[13] = t13 * win[13];
out[14] = t12 * win[14];
out[15] = t11 * win[15];
out[16] = t10 * win[16];
out[17] = t9 * win[17];
out[18] = t8 * win[18];
out[19] = t7 * win[19];
out[20] = t6 * win[20];
out[21] = t5 * win[21];
out[22] = t4 * win[22];
out[23] = t3 * win[23];
out[24] = t2 * win[24];
out[25] = t1 * win[25];
out[26] = t0 * win[26];
out[27] = t0 * win[27];
out[28] = t1 * win[28];
out[29] = t2 * win[29];
out[30] = t3 * win[30];
out[31] = t4 * win[31];
out[32] = t5 * win[32];
out[33] = t6 * win[33];
out[34] = t7 * win[34];
out[35] = t8 * win[35];
}
}
}