/* 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.atrac3plus; import static java.lang.Math.pow; public class Atrac { public static final float[] ff_atrac_sf_table = new float[64]; private static final float[] qmf_window = new float[48]; private static final float qmf_48tap_half[] = new float[] { -0.00001461907f, -0.00009205479f,-0.000056157569f,0.00030117269f, 0.0002422519f, -0.00085293897f,-0.0005205574f, 0.0020340169f, 0.00078333891f, -0.0042153862f, -0.00075614988f, 0.0078402944f, -0.000061169922f,-0.01344162f, 0.0024626821f, 0.021736089f, -0.007801671f, -0.034090221f, 0.01880949f, 0.054326009f, -0.043596379f, -0.099384367f, 0.13207909f, 0.46424159f }; private float gainTab1[] = new float[16]; ///< gain compensation level table private float gainTab2[] = new float[31]; ///< gain compensation interpolation table private int id2expOffset; ///< offset for converting level index into level exponent private int locScale; ///< scale of location code = 2^loc_scale samples private int locSize; ///< size of location code in samples public static void generateTables() { // Generate scale factors if (ff_atrac_sf_table[63] == 0f) { for (int i = 0; i < 64; i++) { ff_atrac_sf_table[i] = (float) pow(2.0f, (i - 15) / 3.0); } } // Generate the QMF window if (qmf_window[47] == 0f) { for (int i = 0; i < 24; i++) { float s = qmf_48tap_half[i] * 2.0f; qmf_window[i] = s; qmf_window[47 - i] = s; } } } public void initGainCompensation(int id2expOffset, int locScale) { this.locScale = locScale; this.locSize = 1 << locScale; this.id2expOffset = id2expOffset; // Generate gain level table for (int i = 0; i < 16; i++) { gainTab1[i] = (float) Math.pow(2, id2expOffset - i); } // Generate gain interpolation table for (int i = -15; i < 16; i++) { gainTab2[i + 15] = (float) Math.pow(2, -1.0 / locSize * i); } } public void gainCompensation(float in[], int inOffset, float prev[], int prevOffset, AtracGainInfo gcNow, AtracGainInfo gcNext, int numSamples, float out[], int outOffset) { float gcScale = (gcNext.numPoints != 0 ? gainTab1[gcNext.levCode[0]] : 1f); if (gcNow.numPoints == 0) { for (int pos = 0; pos < numSamples; pos++) { out[outOffset + pos] = in[inOffset + pos] * gcScale + prev[prevOffset + pos]; } } else { int pos = 0; for (int i = 0; i < gcNow.numPoints; i++) { int lastpos = gcNow.locCode[i] << locScale; float lev = gainTab1[gcNow.levCode[i]]; float gainInc = gainTab2[(i + 1 < gcNow.numPoints ? gcNow.levCode[i + 1] : id2expOffset) - gcNow.levCode[i] + 15]; // apply constant gain level and overlap for (; pos < lastpos; pos++) { out[outOffset + pos] = (in[inOffset + pos] * gcScale + prev[prevOffset + pos]) * lev; } // interpolate between two different gain levels for (; pos < lastpos + locSize; pos++) { out[outOffset + pos] = (in[inOffset + pos] * gcScale + prev[prevOffset + pos]) * lev; lev *= gainInc; } } for (; pos < numSamples; pos++) { out[outOffset + pos] = in[inOffset + pos] * gcScale + prev[prevOffset + pos]; } } // copy the overlapping part into the delay buffer System.arraycopy(in, inOffset + numSamples, prev, prevOffset, numSamples); } public static void iqmf(float[] inlo, int inloOffset, float[] inhi, int inhiOffset, int nIn, float[] out, int outOffset, float[] delayBuf, float[] temp) { System.arraycopy(delayBuf, 0, temp, 0, 46); // loop1 for (int i = 0; i < nIn; i += 2) { temp[46 + 2 * i + 0] = inlo[inloOffset + i ] + inhi[inhiOffset + i ]; temp[46 + 2 * i + 1] = inlo[inloOffset + i ] - inhi[inhiOffset + i ]; temp[46 + 2 * i + 2] = inlo[inloOffset + i + 1] + inhi[inhiOffset + i + 1]; temp[46 + 2 * i + 3] = inlo[inloOffset + i + 1] - inhi[inhiOffset + i + 1]; } // loop2 int p1 = 0; for (int j = nIn; j != 0; j--) { float s1 = 0f; float s2 = 0f; for (int i = 0; i < 48; i += 2) { s1 += temp[p1 + i] * qmf_window[i]; s2 += temp[p1 + i + 1] * qmf_window[i + 1]; } out[outOffset + 0] = s2; out[outOffset + 1] = s1; p1 += 2; outOffset += 2; } // Update the delay buffer. System.arraycopy(temp, nIn * 2, delayBuf, 0, 46); } }