/* * 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]; } } }