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); } }