/*
* Copyright (C) 2011 in-somnia
*
* This file is part of JAAD.
*
* JAAD is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of the
* License, or (at your option) any later version.
*
* JAAD 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 Lesser General
* Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
* If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.jaad.aac.gain;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.syntax.ICSInfo.WindowSequence;
//inverse modified discrete cosine transform
class IMDCT implements GCConstants, IMDCTTables, Windows {
private static final float[][] LONG_WINDOWS = {SINE_256, KBD_256};
private static final float[][] SHORT_WINDOWS = {SINE_32, KBD_32};
private final int frameLen, shortFrameLen, lbLong, lbShort, lbMid;
IMDCT(int frameLen) {
this.frameLen = frameLen;
lbLong = frameLen/BANDS;
shortFrameLen = frameLen/8;
lbShort = shortFrameLen/BANDS;
lbMid = (lbLong-lbShort)/2;
}
void process(float[] in, float[] out, int winShape, int winShapePrev, WindowSequence winSeq) throws AACException {
final float[] buf = new float[frameLen];
int b, j, i;
if(winSeq.equals(WindowSequence.EIGHT_SHORT_SEQUENCE)) {
for(b = 0; b<BANDS; b++) {
for(j = 0; j<8; j++) {
for(i = 0; i<lbShort; i++) {
if(b%2==0) buf[lbLong*b+lbShort*j+i] = in[shortFrameLen*j+lbShort*b+i];
else buf[lbLong*b+lbShort*j+i] = in[shortFrameLen*j+lbShort*b+lbShort-1-i];
}
}
}
}
else {
for(b = 0; b<BANDS; b++) {
for(i = 0; i<lbLong; i++) {
if(b%2==0) buf[lbLong*b+i] = in[lbLong*b+i];
else buf[lbLong*b+i] = in[lbLong*b+lbLong-1-i];
}
}
}
for(b = 0; b<BANDS; b++) {
process2(buf, out, winSeq, winShape, winShapePrev, b);
}
}
private void process2(float[] in, float[] out, WindowSequence winSeq, int winShape, int winShapePrev, int band) throws AACException {
final float[] bufIn = new float[lbLong];
final float[] bufOut = new float[lbLong*2];
final float[] window = new float[lbLong*2];
final float[] window1 = new float[lbShort*2];
final float[] window2 = new float[lbShort*2];
//init windows
int i;
switch(winSeq) {
case ONLY_LONG_SEQUENCE:
for(i = 0; i<lbLong; i++) {
window[i] = LONG_WINDOWS[winShapePrev][i];
window[lbLong*2-1-i] = LONG_WINDOWS[winShape][i];
}
break;
case EIGHT_SHORT_SEQUENCE:
for(i = 0; i<lbShort; i++) {
window1[i] = SHORT_WINDOWS[winShapePrev][i];
window1[lbShort*2-1-i] = SHORT_WINDOWS[winShape][i];
window2[i] = SHORT_WINDOWS[winShape][i];
window2[lbShort*2-1-i] = SHORT_WINDOWS[winShape][i];
}
break;
case LONG_START_SEQUENCE:
for(i = 0; i<lbLong; i++) {
window[i] = LONG_WINDOWS[winShapePrev][i];
}
for(i = 0; i<lbMid; i++) {
window[i+lbLong] = 1.0f;
}
for(i = 0; i<lbShort; i++) {
window[i+lbMid+lbLong] = SHORT_WINDOWS[winShape][lbShort-1-i];
}
for(i = 0; i<lbMid; i++) {
window[i+lbMid+lbLong+lbShort] = 0.0f;
}
break;
case LONG_STOP_SEQUENCE:
for(i = 0; i<lbMid; i++) {
window[i] = 0.0f;
}
for(i = 0; i<lbShort; i++) {
window[i+lbMid] = SHORT_WINDOWS[winShapePrev][i];
}
for(i = 0; i<lbMid; i++) {
window[i+lbMid+lbShort] = 1.0f;
}
for(i = 0; i<lbLong; i++) {
window[i+lbMid+lbShort+lbMid] = LONG_WINDOWS[winShape][lbLong-1-i];
}
break;
}
int j;
if(winSeq.equals(WindowSequence.EIGHT_SHORT_SEQUENCE)) {
int k;
for(j = 0; j<8; j++) {
for(k = 0; k<lbShort; k++) {
bufIn[k] = in[band*lbLong+j*lbShort+k];
}
if(j==0) System.arraycopy(window1, 0, window, 0, lbShort*2);
else System.arraycopy(window2, 0, window, 0, lbShort*2);
imdct(bufIn, bufOut, window, lbShort);
for(k = 0; k<lbShort*2; k++) {
out[band*lbLong*2+j*lbShort*2+k] = bufOut[k]/32.0f;
}
}
}
else {
for(j = 0; j<lbLong; j++) {
bufIn[j] = in[band*lbLong+j];
}
imdct(bufIn, bufOut, window, lbLong);
for(j = 0; j<lbLong*2; j++) {
out[band*lbLong*2+j] = bufOut[j]/256.0f;
}
}
}
private void imdct(float[] in, float[] out, float[] window, int n) throws AACException {
final int n2 = n/2;
float[][] table, table2;
if(n==256) {
table = IMDCT_TABLE_256;
table2 = IMDCT_POST_TABLE_256;
}
else if(n==32) {
table = IMDCT_TABLE_32;
table2 = IMDCT_POST_TABLE_32;
}
else throw new AACException("gain control: unexpected IMDCT length");
final float[] tmp = new float[n];
int i;
for(i = 0; i<n2; ++i) {
tmp[i] = in[2*i];
}
for(i = n2; i<n; ++i) {
tmp[i] = -in[2*n-1-2*i];
}
//pre-twiddle
final float[][] buf = new float[n2][2];
for(i = 0; i<n2; i++) {
buf[i][0] = (table[i][0]*tmp[2*i])-(table[i][1]*tmp[2*i+1]);
buf[i][1] = (table[i][0]*tmp[2*i+1])+(table[i][1]*tmp[2*i]);
}
//fft
FFT.process(buf, n2);
//post-twiddle and reordering
for(i = 0; i<n2; i++) {
tmp[i] = table2[i][0]*buf[i][0]+table2[i][1]*buf[n2-1-i][0]
+table2[i][2]*buf[i][1]+table2[i][3]*buf[n2-1-i][1];
tmp[n-1-i] = table2[i][2]*buf[i][0]-table2[i][3]*buf[n2-1-i][0]
-table2[i][0]*buf[i][1]+table2[i][1]*buf[n2-1-i][1];
}
//copy to output and apply window
System.arraycopy(tmp, n2, out, 0, n2);
for(i = n2; i<n*3/2; ++i) {
out[i] = -tmp[n*3/2-1-i];
}
for(i = n*3/2; i<n*2; ++i) {
out[i] = -tmp[i-n*3/2];
}
for(i = 0; i<n; i++) {
out[i] *= window[i];
}
}
}