package net.sourceforge.jaad.aac.sbr;
/**
* This class is part of JAAD ( jaadec.sourceforge.net ) that is distributed
* under the Public Domain license. Code changes provided by the JCodec project
* are distributed under FreeBSD license.
*
* @author in-somnia
*/
class HFGeneration {
private static final int[] goalSbTab = {21, 23, 32, 43, 46, 64, 85, 93, 128, 0, 0, 0};
private static class acorr_coef {
float[] r01;
float[] r02;
float[] r11;
float[] r12;
float[] r22;
float det;
public acorr_coef() {
this.r01 = new float[2];
this.r02 = new float[2];
this.r11 = new float[2];
this.r12 = new float[2];
this.r22 = new float[2];
}
}
public static void hf_generation(SBR sbr, float[][][] Xlow,
float[][][] Xhigh, int ch) {
int l, i, x;
float[][] alpha_0 = new float[64][2], alpha_1 = new float[64][2];
int offset = sbr.tHFAdj;
int first = sbr.t_E[ch][0];
int last = sbr.t_E[ch][sbr.L_E[ch]];
calc_chirp_factors(sbr, ch);
if((ch==0)&&(sbr.Reset))
patch_construction(sbr);
/* calculate the prediction coefficients */
/* actual HF generation */
for(i = 0; i<sbr.noPatches; i++) {
for(x = 0; x<sbr.patchNoSubbands[i]; x++) {
float a0_r, a0_i, a1_r, a1_i;
float bw, bw2;
int q, p, k, g;
/* find the low and high band for patching */
k = sbr.kx+x;
for(q = 0; q<i; q++) {
k += sbr.patchNoSubbands[q];
}
p = sbr.patchStartSubband[i]+x;
g = sbr.table_map_k_to_g[k];
bw = sbr.bwArray[ch][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;
calc_prediction_coef(sbr, Xlow, alpha_0, alpha_1, p);
a0_r = (alpha_0[p][0]*bw);
a1_r = (alpha_1[p][0]*bw2);
a0_i = (alpha_0[p][1]*bw);
a1_i = (alpha_1[p][1]*bw2);
temp2_r = (Xlow[first-2+offset][p][0]);
temp3_r = (Xlow[first-1+offset][p][0]);
temp2_i = (Xlow[first-2+offset][p][1]);
temp3_i = (Xlow[first-1+offset][p][1]);
for(l = first; l<last; l++) {
temp1_r = temp2_r;
temp2_r = temp3_r;
temp3_r = (Xlow[l+offset][p][0]);
temp1_i = temp2_i;
temp2_i = temp3_i;
temp3_i = (Xlow[l+offset][p][1]);
Xhigh[l+offset][k][0]
= temp3_r
+((a0_r*temp2_r)
-(a0_i*temp2_i)
+(a1_r*temp1_r)
-(a1_i*temp1_i));
Xhigh[l+offset][k][1]
= temp3_i
+((a0_i*temp2_r)
+(a0_r*temp2_i)
+(a1_i*temp1_r)
+(a1_r*temp1_i));
}
}
else {
for(l = first; l<last; l++) {
Xhigh[l+offset][k][0] = Xlow[l+offset][p][0];
Xhigh[l+offset][k][1] = Xlow[l+offset][p][1];
}
}
}
}
if(sbr.Reset) {
FBT.limiter_frequency_table(sbr);
}
}
private static void auto_correlation(SBR sbr, acorr_coef ac, float[][][] buffer,
int bd, int len) {
float r01r = 0, r01i = 0, r02r = 0, r02i = 0, r11r = 0;
float temp1_r, temp1_i, temp2_r, temp2_i, temp3_r, temp3_i, temp4_r, temp4_i, temp5_r, temp5_i;
float rel = 1.0f/(1+1e-6f);
int j;
int offset = sbr.tHFAdj;
temp2_r = buffer[offset-2][bd][0];
temp2_i = buffer[offset-2][bd][1];
temp3_r = buffer[offset-1][bd][0];
temp3_i = buffer[offset-1][bd][1];
// Save these because they are needed after loop
temp4_r = temp2_r;
temp4_i = temp2_i;
temp5_r = temp3_r;
temp5_i = temp3_i;
for(j = offset; j<len+offset; j++) {
temp1_r = temp2_r; // temp1_r = QMF_RE(buffer[j-2][bd];
temp1_i = temp2_i; // temp1_i = QMF_IM(buffer[j-2][bd];
temp2_r = temp3_r; // temp2_r = QMF_RE(buffer[j-1][bd];
temp2_i = temp3_i; // temp2_i = QMF_IM(buffer[j-1][bd];
temp3_r = buffer[j][bd][0];
temp3_i = buffer[j][bd][1];
r01r += temp3_r*temp2_r+temp3_i*temp2_i;
r01i += temp3_i*temp2_r-temp3_r*temp2_i;
r02r += temp3_r*temp1_r+temp3_i*temp1_i;
r02i += temp3_i*temp1_r-temp3_r*temp1_i;
r11r += temp2_r*temp2_r+temp2_i*temp2_i;
}
// These are actual values in temporary variable at this point
// temp1_r = QMF_RE(buffer[len+offset-1-2][bd];
// temp1_i = QMF_IM(buffer[len+offset-1-2][bd];
// temp2_r = QMF_RE(buffer[len+offset-1-1][bd];
// temp2_i = QMF_IM(buffer[len+offset-1-1][bd];
// temp3_r = QMF_RE(buffer[len+offset-1][bd]);
// temp3_i = QMF_IM(buffer[len+offset-1][bd]);
// temp4_r = QMF_RE(buffer[offset-2][bd]);
// temp4_i = QMF_IM(buffer[offset-2][bd]);
// temp5_r = QMF_RE(buffer[offset-1][bd]);
// temp5_i = QMF_IM(buffer[offset-1][bd]);
ac.r12[0] = r01r
-(temp3_r*temp2_r+temp3_i*temp2_i)
+(temp5_r*temp4_r+temp5_i*temp4_i);
ac.r12[1] = r01i
-(temp3_i*temp2_r-temp3_r*temp2_i)
+(temp5_i*temp4_r-temp5_r*temp4_i);
ac.r22[0] = r11r
-(temp2_r*temp2_r+temp2_i*temp2_i)
+(temp4_r*temp4_r+temp4_i*temp4_i);
ac.r01[0] = r01r;
ac.r01[1] = r01i;
ac.r02[0] = r02r;
ac.r02[1] = r02i;
ac.r11[0] = r11r;
ac.det = (ac.r11[0]*ac.r22[0])-(rel*((ac.r12[0]*ac.r12[0])+(ac.r12[1]*ac.r12[1])));
}
/* calculate linear prediction coefficients using the covariance method */
private static void calc_prediction_coef(SBR sbr, float[][][] Xlow,
float[][] alpha_0, float[][] alpha_1, int k) {
float tmp;
acorr_coef ac = new acorr_coef();
auto_correlation(sbr, ac, Xlow, k, sbr.numTimeSlotsRate+6);
if(ac.det==0) {
alpha_1[k][0] = 0;
alpha_1[k][1] = 0;
}
else {
tmp = 1.0f/ac.det;
alpha_1[k][0] = ((ac.r01[0]*ac.r12[0])-(ac.r01[1]*ac.r12[1])-(ac.r02[0]*ac.r11[0]))*tmp;
alpha_1[k][1] = ((ac.r01[1]*ac.r12[0])+(ac.r01[0]*ac.r12[1])-(ac.r02[1]*ac.r11[0]))*tmp;
}
if(ac.r11[0]==0) {
alpha_0[k][0] = 0;
alpha_0[k][1] = 0;
}
else {
tmp = 1.0f/ac.r11[0];
alpha_0[k][0] = -(ac.r01[0]+(alpha_1[k][0]*ac.r12[0])+(alpha_1[k][1]*ac.r12[1]))*tmp;
alpha_0[k][1] = -(ac.r01[1]+(alpha_1[k][1]*ac.r12[0])-(alpha_1[k][0]*ac.r12[1]))*tmp;
}
if(((alpha_0[k][0]*alpha_0[k][0])+(alpha_0[k][1]*alpha_0[k][1])>=16.0f)
||((alpha_1[k][0]*alpha_1[k][0])+(alpha_1[k][1]*alpha_1[k][1])>=16.0f)) {
alpha_0[k][0] = 0;
alpha_0[k][1] = 0;
alpha_1[k][0] = 0;
alpha_1[k][1] = 0;
}
}
/* FIXED POINT: bwArray = COEF */
private static float mapNewBw(int invf_mode, int invf_mode_prev) {
switch(invf_mode) {
case 1: /* LOW */
if(invf_mode_prev==0) /* NONE */
return 0.6f;
else
return 0.75f;
case 2: /* MID */
return 0.9f;
case 3: /* HIGH */
return 0.98f;
default: /* NONE */
if(invf_mode_prev==1) /* LOW */
return 0.6f;
else
return 0.0f;
}
}
/* FIXED POINT: bwArray = COEF */
private static void calc_chirp_factors(SBR sbr, int ch) {
int i;
for(i = 0; i<sbr.N_Q; i++) {
sbr.bwArray[ch][i] = mapNewBw(sbr.bs_invf_mode[ch][i], sbr.bs_invf_mode_prev[ch][i]);
if(sbr.bwArray[ch][i]<sbr.bwArray_prev[ch][i])
sbr.bwArray[ch][i] = (sbr.bwArray[ch][i]*0.75f)+(sbr.bwArray_prev[ch][i]*0.25f);
else
sbr.bwArray[ch][i] = (sbr.bwArray[ch][i]*0.90625f)+(sbr.bwArray_prev[ch][i]*0.09375f);
if(sbr.bwArray[ch][i]<0.015625f)
sbr.bwArray[ch][i] = 0.0f;
if(sbr.bwArray[ch][i]>=0.99609375f)
sbr.bwArray[ch][i] = 0.99609375f;
sbr.bwArray_prev[ch][i] = sbr.bwArray[ch][i];
sbr.bs_invf_mode_prev[ch][i] = sbr.bs_invf_mode[ch][i];
}
}
private static void patch_construction(SBR sbr) {
int i, k;
int odd, sb;
int msb = sbr.k0;
int usb = sbr.kx;
/* (uint8_t)(2.048e6/sbr.sample_rate + 0.5); */
int goalSb = goalSbTab[sbr.sample_rate.getIndex()];
sbr.noPatches = 0;
if(goalSb<(sbr.kx+sbr.M)) {
for(i = 0, k = 0; sbr.f_master[i]<goalSb; i++) {
k = i+1;
}
}
else {
k = sbr.N_master;
}
if(sbr.N_master==0) {
sbr.noPatches = 0;
sbr.patchNoSubbands[0] = 0;
sbr.patchStartSubband[0] = 0;
return;
}
do {
int j = k+1;
do {
j--;
sb = sbr.f_master[j];
odd = (sb-2+sbr.k0)%2;
}
while(sb>(sbr.k0-1+msb-odd));
sbr.patchNoSubbands[sbr.noPatches] = Math.max(sb-usb, 0);
sbr.patchStartSubband[sbr.noPatches] = sbr.k0-odd
-sbr.patchNoSubbands[sbr.noPatches];
if(sbr.patchNoSubbands[sbr.noPatches]>0) {
usb = sb;
msb = sb;
sbr.noPatches++;
}
else {
msb = sbr.kx;
}
if(sbr.f_master[k]-sb<3)
k = sbr.N_master;
}
while(sb!=(sbr.kx+sbr.M));
if((sbr.patchNoSubbands[sbr.noPatches-1]<3)&&(sbr.noPatches>1)) {
sbr.noPatches--;
}
sbr.noPatches = Math.min(sbr.noPatches, 5);
}
}