/*
* 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.sbr;
class HFGeneration implements SBRConstants {
private static class ACCoefs {
float[] r01 = new float[2];
float[] r02 = new float[2];
float r11 = 0;
float[] r12 = new float[2];
float[] r22 = new float[2];
float det = 0;
}
private static final int[] GOAL_SB_TABLE = {21, 23, 32, 43, 46, 64, 85, 93, 128, 0, 0, 0};
private static final float[][] BW_VALUES = {
{0.0f, 0.6f},
{0.75f, 0.6f},
{0.9f, 0.9f},
{0.98f, 0.98f}
};
private static final float BW_MIN = 0.015625f;
private static final float BW_MAX = 0.99609375f;
private static final float[] CHIRP_COEFS = {0.75f, 0.25f, 0.90625f, 0.09375f};
private static final float AC_REL = 0.9999990000010001f; //1/(1+1e-6);
private final SBR sbr;
private final float[][] alpha0, alpha1;
private final float[] a0, a1;
private final ACCoefs ac;
HFGeneration(SBR sbr) {
this.sbr = sbr;
alpha0 = new float[64][2];
alpha1 = new float[64][2];
a0 = new float[2];
a1 = new float[2];
ac = new ACCoefs();
}
void process(float[][][] Xlow, float[][][] Xhigh, int ch, ChannelData cd) {
final int offset = T_HFADJ;
final int first = cd.t_E[0];
final int last = cd.t_E[cd.L_E];
calculateChirpFactors(cd);
if(ch==0&&sbr.reset) constructPatches();
//actual HF generation
int j, l, off, k, g;
float bw, bw2;
for(int i = 0; i<sbr.patches; i++) {
for(j = 0; j<sbr.patchNoSubbands[i]; j++) {
//find the low and high band for patching
k = sbr.kx+j;
for(l = 0; l<i; l++) {
k += sbr.patchNoSubbands[l];
}
off = sbr.patchStartSubband[i]+j;
g = sbr.tableMapKToG[k];
bw = cd.bwArray[g];
bw2 = bw*bw;
//do the patching with or without filtering
if(bw2>0) {
float temp1_r, temp2_r, temp3_r;
float temp1_i, temp2_i, temp3_i;
calculatePredictionCoef(Xlow, off);
a0[0] = alpha0[off][0]*bw;
a1[0] = alpha1[off][0]*bw2;
a0[1] = alpha0[off][1]*bw;
a1[1] = alpha1[off][1]*bw2;
temp2_r = Xlow[first-2+offset][off][0];
temp3_r = Xlow[first-1+offset][off][0];
temp2_i = Xlow[first-2+offset][off][1];
temp3_i = Xlow[first-1+offset][off][1];
for(l = first; l<last; l++) {
temp1_r = temp2_r;
temp2_r = temp3_r;
temp3_r = Xlow[l+offset][off][0];
temp1_i = temp2_i;
temp2_i = temp3_i;
temp3_i = Xlow[l+offset][off][1];
Xhigh[l+offset][k][0] = temp3_r
+((a0[0]*temp2_r)-(a0[1]*temp2_i)
+(a1[0]*temp1_r)-(a1[1]*temp1_i));
Xhigh[l+offset][k][1] = temp3_i
+((a0[1]*temp2_r)+(a0[0]*temp2_i)
+(a1[1]*temp1_r)+(a1[0]*temp1_i));
}
}
else {
for(l = first; l<last; l++) {
Xhigh[l+offset][k][0] = Xlow[l+offset][off][0];
Xhigh[l+offset][k][1] = Xlow[l+offset][off][1];
}
}
}
}
if(sbr.reset) sbr.calculateLimiterFrequencyTable();
}
private void calculateChirpFactors(ChannelData cd) {
int off;
for(int i = 0; i<sbr.N_Q; i++) {
cd.bwArray[i] = getBW(cd.invfMode[i], cd.invfModePrev[i]);
off = (cd.bwArray[i]<cd.bwArrayPrev[i]) ? 0 : 2;
cd.bwArray[i] = (cd.bwArray[i]*CHIRP_COEFS[off])+(cd.bwArrayPrev[i]*CHIRP_COEFS[off+1]);
if(cd.bwArray[i]<BW_MIN) cd.bwArray[i] = 0.0f;
if(cd.bwArray[i]>=BW_MAX) cd.bwArray[i] = BW_MAX;
cd.bwArrayPrev[i] = cd.bwArray[i];
cd.invfModePrev[i] = cd.invfMode[i];
}
}
private float getBW(int invfMode, int invfModePrev) {
int sec;
if(invfMode==0) sec = invfModePrev==1 ? 1 : 0;
else if(invfMode==1) sec = invfModePrev==0 ? 1 : 0;
else sec = 0;
return BW_VALUES[invfMode][sec];
}
private void constructPatches() {
final int goalSb = GOAL_SB_TABLE[Calculation.getSampleRateIndex(sbr.sampleRate)]; //(2.048e6/sbr.sample_rate + 0.5);
sbr.patches = 0;
int k;
if(goalSb<(sbr.kx+sbr.M)) {
int i;
for(i = 0, k = 0; sbr.mft[i]<goalSb; i++) {
k = i+1;
}
}
else k = sbr.N_master;
if(sbr.N_master==0) {
sbr.patchNoSubbands[0] = 0;
sbr.patchStartSubband[0] = 0;
return;
}
int msb = sbr.k0;
int usb = sbr.kx;
int j, odd, sb;
do {
j = k+1;
do {
j--;
sb = sbr.mft[j];
odd = (sb-2+sbr.k0)%2;
}
while(sb>(sbr.k0-1+msb-odd));
sbr.patchNoSubbands[sbr.patches] = Math.max(sb-usb, 0);
sbr.patchStartSubband[sbr.patches] = sbr.k0-odd-sbr.patchNoSubbands[sbr.patches];
if(sbr.patchNoSubbands[sbr.patches]>0) {
usb = sb;
msb = sb;
sbr.patches++;
}
else msb = sbr.kx;
if(sbr.mft[k]-sb<3) k = sbr.N_master;
}
while(sb!=(sbr.kx+sbr.M));
if((sbr.patchNoSubbands[sbr.patches-1]<3)&&(sbr.patches>1)) sbr.patches--;
sbr.patches = Math.min(sbr.patches, 5);
}
private void calculatePredictionCoef(float[][][] Xlow, int k) {
float tmp;
calculateAutoCorrelation(Xlow, k, TIME_SLOTS_RATE+6);
if(ac.det==0) {
alpha1[k][0] = 0;
alpha1[k][1] = 0;
}
else {
tmp = 1.0f/ac.det;
alpha1[k][0] = ((ac.r01[0]*ac.r12[0])-(ac.r01[1]*ac.r12[1])-(ac.r02[0]*ac.r11))*tmp;
alpha1[k][1] = ((ac.r01[1]*ac.r12[0])+(ac.r01[0]*ac.r12[1])-(ac.r02[1]*ac.r11))*tmp;
}
if(ac.r11==0) {
alpha0[k][0] = 0;
alpha0[k][1] = 0;
}
else {
tmp = 1.0f/ac.r11;
alpha0[k][0] = -(ac.r01[0]+(alpha1[k][0]*ac.r12[0])+(alpha1[k][1]*ac.r12[1]))*tmp;
alpha0[k][1] = -(ac.r01[1]+(alpha1[k][1]*ac.r12[0])-(alpha1[k][0]*ac.r12[1]))*tmp;
}
if(((alpha0[k][0]*alpha0[k][0])+(alpha0[k][1]*alpha0[k][1])>=16)
||((alpha1[k][0]*alpha1[k][0])+(alpha1[k][1]*alpha1[k][1])>=16)) {
alpha0[k][0] = 0;
alpha0[k][1] = 0;
alpha1[k][0] = 0;
alpha1[k][1] = 0;
}
}
private ACCoefs calculateAutoCorrelation(float[][][] buffer, int bd, int len) {
final int offset = T_HFADJ;
float temp2r = buffer[offset-2][bd][0];
float temp2i = buffer[offset-2][bd][1];
float temp3r = buffer[offset-1][bd][0];
float temp3i = buffer[offset-1][bd][1];
//save these because they are needed after loop
final float[] temp4 = {temp2r, temp2i};
final float[] temp5 = {temp3r, temp3i};
final float[] r01 = new float[2];
final float[] r02 = new float[2];
float r11 = 0;
final float[] temp1 = new float[2];
for(int i = offset; i<len+offset; i++) {
temp1[0] = temp2r;
temp1[1] = temp2i;
temp2r = temp3r;
temp2i = temp3i;
temp3r = buffer[i][bd][0];
temp3i = buffer[i][bd][1];
r01[0] += temp3r*temp2r+temp3i*temp2i;
r01[1] += temp3i*temp2r-temp3r*temp2i;
r02[0] += temp3r*temp1[0]+temp3i*temp1[1];
r02[1] += temp3i*temp1[0]-temp3r*temp1[1];
r11 += temp2r*temp2r+temp2i*temp2i;
}
ac.r12[0] = r01[0]-(temp3r*temp2r+temp3i*temp2i)
+(temp5[0]*temp4[0]+temp5[1]*temp4[1]);
ac.r12[1] = r01[1]-(temp3i*temp2r-temp3r*temp2i)
+(temp5[1]*temp4[0]-temp5[0]*temp4[1]);
ac.r22[0] = r11-(temp2r*temp2r+temp2i*temp2i)
+(temp4[0]*temp4[0]+temp4[1]*temp4[1]);
ac.r01 = r01;
ac.r02 = r02;
ac.r11 = r11;
ac.det = (ac.r11*ac.r22[0])-(AC_REL*((ac.r12[0]*ac.r12[0])+(ac.r12[1]*ac.r12[1])));
return ac;
}
}