/*
* 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.sbr2;
import net.sourceforge.jaad.aac.AACException;
//TODO: patch and chirp factor calculation are ok, next inverse filtering (p229)
class HFGenerator implements SBRConstants {
private static final float RELAX_COEF = 1.000001f;
private static final int ALPHA_MAX = 16;
private static final float[][] CHIRP_COEFS = {{0.75f, 0.25f}, {0.90625f, 0.09375f}};
//values for bw [invfModePrev][invfMode]
private static final float[][] BW_COEFS = {
{0.0f, 0.6f, 0.9f, 0.98f},
{0.6f, 0.75f, 0.9f, 0.98f},
{0.0f, 0.75f, 0.9f, 0.98f},
{0.0f, 0.75f, 0.9f, 0.98f}
};
private static final float CHIRP_MIN = 0.015625f;
//in: 32x40 complex Xlow, out: 23x40 complex Xhigh
public static void process(FrequencyTables tables, ChannelData cd, float[][][] Xlow, float[][][] Xhigh) throws AACException {
//calculate chirp factors
final float[] bwArray = calculateChirpFactors(tables, cd);
//calculate inverse filter coefficients for bands 0-k0
final int k0 = tables.getK0();
final float[][] alpha0 = new float[k0][2];
final float[][] alpha1 = new float[k0][2];
calculateIFCoefs(tables, alpha0, alpha1, Xlow);
//HF generation
final int patchCount = tables.getPatchCount();
final int[] patchSubbands = tables.getPatchSubbands();
final int[] patchStartSubband = tables.getPatchStartSubband();
final int kx = tables.getKx(false);
final int m = tables.getM(false);
final int Nq = tables.getNq();
final int[] fNoise = tables.getNoiseTable();
final int[] te = cd.getTe();
final int start = RATE*te[0];
final int end = RATE*te[cd.getEnvCount()];
final float[] alpha = new float[4];
float square;
int l, x; //loop indizes
int k = kx;
int g = 0;
for(int j = 0; j<patchCount; j++) {
for(x = 0; x<patchSubbands[j]; x++, k++) {
final int p = patchStartSubband[j]+x;
while(g<=Nq&&k>=fNoise[g]) {
g++;
}
g--;
if(g<0) throw new AACException("SBR: HFGenerator: no subband found for frequency "+k);
//fill Xhigh[k] (4.6.18.6.3)
square = bwArray[g]*bwArray[g];
alpha[0] = alpha1[p][0]*square;
alpha[1] = alpha1[p][1]*square;
alpha[2] = alpha0[p][0]*bwArray[g];
alpha[3] = alpha0[p][1]*bwArray[g];
for(l = start; l<end; l++) {
final int off = l+T_HF_ADJ;
Xhigh[k][off][0] = alpha[0]*Xlow[p][off-2][0]
-alpha[1]*Xlow[p][off-2][1]
+alpha[2]*Xlow[p][off-1][0]
-alpha[3]*Xlow[p][off-1][1]
+Xlow[p][off][0];
Xhigh[k][off][1] = alpha[0]*Xlow[p][off-2][1]
+alpha[1]*Xlow[p][off-2][0]
+alpha[2]*Xlow[p][off-1][1]
+alpha[3]*Xlow[p][off-1][0]
+Xlow[p][off][1];
}
}
}
//fill remaining with zero
while(k<m+kx) {
for(int j = 0; j<Xhigh[k].length; j++) {
Xhigh[k][j][0] = 0;
Xhigh[k][j][1] = 0;
}
k++;
}
}
private static float[] calculateChirpFactors(FrequencyTables tables, ChannelData cd) {
//calculates chirp factors and replaces old ones in ChannelData
final int nq = tables.getNq();
final int[] invfMode = cd.getInvfMode(false);
final int[] invfModePrevious = cd.getInvfMode(true);
final float[] bwArray = cd.getChirpFactors();
float tmp;
float[] chirpCoefs;
for(int i = 0; i<nq; i++) {
tmp = BW_COEFS[invfModePrevious[i]][invfMode[i]];
chirpCoefs = (tmp<bwArray[i]) ? CHIRP_COEFS[0] : CHIRP_COEFS[1];
bwArray[i] = (chirpCoefs[0]*tmp)+(chirpCoefs[1]*bwArray[i]);
if(bwArray[i]<CHIRP_MIN) bwArray[i] = 0;
}
return bwArray;
}
//calculates inverse filter coefficients for bands 0-k0 (4.6.18.6.2)
private static void calculateIFCoefs(FrequencyTables tables, float[][] alpha0, float[][] alpha1, float[][][] Xlow) {
final int k0 = tables.getK0();
final float[] tmp = new float[2];
float[][][] phi;
float d;
for(int k = 0; k<k0; k++) {
//get covariance matrix
phi = new float[3][2][2];
getCovarianceMatrix(Xlow[k], phi, 0);
getCovarianceMatrix(Xlow[k], phi, 1);
getCovarianceMatrix(Xlow[k], phi, 2);
//d(k)
d = phi[2][1][0]*phi[1][0][0]-(phi[1][1][0]*phi[1][1][0]+phi[1][1][1]*phi[1][1][1])/RELAX_COEF;
//alpha1
if(d==0) {
alpha1[k][0] = 0;
alpha1[k][1] = 0;
}
else {
tmp[0] = phi[0][0][0]*phi[1][1][0]-phi[0][0][1]*phi[1][1][1]-phi[0][1][0]*phi[1][0][0];
tmp[1] = phi[0][0][0]*phi[1][1][1]+phi[0][0][1]*phi[1][1][0]-phi[0][1][1]*phi[1][0][0];
alpha1[k][0] = tmp[0]/d;
alpha1[k][1] = tmp[1]/d;
}
//alpha0
if(phi[1][0][0]==0) {
alpha0[k][0] = 0;
alpha0[k][1] = 0;
}
else {
tmp[0] = phi[0][0][0]+alpha1[k][0]*phi[1][1][0]+alpha1[k][1]*phi[1][1][1];
tmp[1] = phi[0][0][1]+alpha1[k][1]*phi[1][1][0]-alpha1[k][0]*phi[1][1][1];
alpha0[k][0] = -tmp[0]/phi[1][0][0];
alpha0[k][1] = -tmp[1]/phi[1][0][0];
}
if(alpha1[k][0]*alpha1[k][0]+alpha1[k][1]*alpha1[k][1]>=ALPHA_MAX
||alpha0[k][0]*alpha0[k][0]+alpha0[k][1]*alpha0[k][1]>=ALPHA_MAX) {
alpha1[k][0] = 0;
alpha1[k][1] = 0;
alpha0[k][0] = 0;
alpha0[k][1] = 0;
}
}
}
//calculates covariance matrix (4.6.18.6.2)
private static void getCovarianceMatrix(float[][] x, float[][][] phi, int off) {
final float[] sum = new float[2];
if(off==0) {
for(int i = 1; i<38; i++) {
sum[0] += x[i][0]*x[i][0]+x[i][1]*x[i][1];
}
phi[2][1][0] = sum[0]+x[0][0]*x[0][0]+x[0][1]*x[0][1];
phi[1][0][0] = sum[0]+x[38][0]*x[38][0]+x[38][1]*x[38][1];
}
else {
for(int i = 1; i<38; i++) {
sum[0] += x[i][0]*x[i+off][0]+x[i][1]*x[i+off][1];
sum[1] += x[i][0]*x[i+off][1]-x[i][1]*x[i+off][0];
}
phi[2-off][1][0] = sum[0]+x[0][0]*x[off][0]+x[0][1]*x[off][1];
phi[2-off][1][1] = sum[1]+x[0][0]*x[off][1]-x[0][1]*x[off][0];
if(off==1) {
phi[0][0][0] = sum[0]+x[38][0]*x[39][0]+x[38][1]*x[39][1];
phi[0][0][1] = sum[1]+x[38][0]*x[39][1]-x[38][1]*x[39][0];
}
}
}
}