/*
* 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.ps;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.syntax.BitStream;
import java.util.Arrays;
public class PS implements PSConstants, PSTables, HuffmanTables {
//header data
private boolean header;
private boolean enableIID, enableICC, enableExt, enableIPDOPD;
private int iidMode, iccMode;
private boolean iidQuant;
private int iidParCount, iccParCount, ipdopdParCount;
private boolean use34, use34Old;
//standard data
private int frameClass;
private int envCount, envCountOld;
private final int[] borderPositions;
//pars
private final int[][] iidPars, iccPars, ipdPars, opdPars;
private final int[] iidParsPrev, iccParsPrev, ipdParsPrev, opdParsPrev;
//processing
private float[][][] in_buf;
private float[][][] delay;
private float[][][][] ap_delay;
//decorrelation
private float[] peak_decay_nrg;
private float[] power_smooth;
private float[] peak_decay_diff_smooth;
//stereo processing
private float[][][] H11, H12, H21, H22;
private int[] ipd_hist, opd_hist;
public PS() {
//ipd/opd is iid/icc sized so that the same functions can handle both
iidPars = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
iccPars = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
ipdPars = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
opdPars = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
//last envelope of previous frame
iidParsPrev = new int[MAX_IID_ICC_PARS];
iccParsPrev = new int[MAX_IID_ICC_PARS];
ipdParsPrev = new int[MAX_IID_ICC_PARS];
opdParsPrev = new int[MAX_IID_ICC_PARS];
borderPositions = new int[MAX_ENVELOPES+1];
in_buf = new float[5][44][2];
delay = new float[MAX_SSB][QMF_SLOTS+MAX_DELAY][2];
ap_delay = new float[MAX_AP_BANDS][AP_LINKS][QMF_SLOTS+MAX_AP_DELAY][2];
peak_decay_nrg = new float[34];
power_smooth = new float[34];
peak_decay_diff_smooth = new float[34];
H11 = new float[2][MAX_ENVELOPES+1][MAX_IID_ICC_PARS];
H12 = new float[2][MAX_ENVELOPES+1][MAX_IID_ICC_PARS];
H21 = new float[2][MAX_ENVELOPES+1][MAX_IID_ICC_PARS];
H22 = new float[2][MAX_ENVELOPES+1][MAX_IID_ICC_PARS];
ipd_hist = new int[MAX_IID_ICC_PARS];
opd_hist = new int[MAX_IID_ICC_PARS];
}
//============================ decoding ==============================
private int frame = 0;
public int decode(BitStream in) throws AACException {
frame++;
final int off = in.getPosition();
if(in.readBool()) {
header = true;
if(enableIID = in.readBool()) {
iidMode = in.readBits(3);
iidQuant = iidMode>2;
iidParCount = IID_ICC_PAR_TABLE[iidMode];
ipdopdParCount = IPDOPD_PAR_TABLE[iidMode];
}
if(enableICC = in.readBool()) {
iccMode = in.readBits(3);
iccParCount = IID_ICC_PAR_TABLE[iccMode];
}
enableExt = in.readBool();
}
frameClass = in.readBit();
envCountOld = envCount;
envCount = ENV_COUNT_TABLE[frameClass][in.readBits(2)];
borderPositions[0] = -1;
if(frameClass==1) {
for(int i = 1; i<=envCount; i++) {
borderPositions[i] = in.readBits(5);
}
}
else {
for(int i = 1; i<=envCount; i++) {
borderPositions[i] = (i*QMF_SLOTS>>LOG2_TABLE[envCount])-1;
}
}
if(enableIID) {
boolean time;
for(int i = 0; i<envCount; i++) {
time = in.readBool();
decodeIIDData(in, time, i);
}
}
else Arrays.fill(iidPars, new int[MAX_IID_ICC_PARS]);
if(enableICC) {
boolean time;
for(int i = 0; i<envCount; i++) {
time = in.readBool();
decodeICCData(in, time, i);
}
}
else Arrays.fill(iccPars, new int[MAX_IID_ICC_PARS]);
use34Old = use34;
if(enableIID||enableICC) use34 = (enableIID&&iidParCount==34)||(enableICC&&iccParCount==34);
if(enableExt) {
int size = in.readBits(4);
if(size==15) size += in.readBits(8);
int bitsLeft = 8*size;
int id;
while(bitsLeft>7) {
id = in.readBits(2);
bitsLeft -= 2;
bitsLeft -= decodeExtension(in, id);
}
in.skipBits(bitsLeft);
}
//fix up envelopes
if(envCount==0||borderPositions[envCount]<QMF_SLOTS-1) {
//create a fake envelope
int source = envCount!=0 ? envCount-1 : envCountOld-1;
if(source>=0&&source!=envCount) {
if(enableIID) System.arraycopy(iidPars[source], 0, iidPars[envCount], 0, iidPars[source].length);
if(enableICC) System.arraycopy(iccPars[source], 0, iccPars[envCount], 0, iccPars[source].length);
if(enableIPDOPD) {
System.arraycopy(ipdPars[source], 0, ipdPars[envCount], 0, ipdPars[source].length);
System.arraycopy(opdPars[source], 0, opdPars[envCount], 0, opdPars[source].length);
}
}
envCount++;
borderPositions[envCount] = QMF_SLOTS-1;
}
if(enableIPDOPD) {
Arrays.fill(ipdPars, new int[MAX_IID_ICC_PARS]);
Arrays.fill(opdPars, new int[MAX_IID_ICC_PARS]);
}
//update previous indices
System.arraycopy(iidPars[envCount-1], 0, iidParsPrev, 0, 34);
System.arraycopy(iccPars[envCount-1], 0, iccParsPrev, 0, 34);
System.arraycopy(ipdPars[envCount-1], 0, ipdParsPrev, 0, 17);
System.arraycopy(opdPars[envCount-1], 0, opdParsPrev, 0, 17);
final int read = in.getPosition()-off;
return read;
}
private int decodeExtension(BitStream in, int id) throws AACException {
int off = in.getPosition();
if(id==0) {
if(enableIPDOPD = in.readBool()) {
boolean time;
for(int i = 0; i<envCount; i++) {
time = in.readBool();
decodeIPDData(in, time, i);
time = in.readBool();
decodeOPDData(in, time, i);
}
}
in.skipBit(); //reserved
}
return in.getPosition()-off;
}
private void decodeIIDData(BitStream in, boolean time, int index) throws AACException {
final int[][] table;
if(iidQuant) table = time ? HUFFMAN_IID_FINE_DT : HUFFMAN_IID_FINE_DF;
else table = time ? HUFFMAN_IID_DEFAULT_DT : HUFFMAN_IID_DEFAULT_DF;
Huffman.decode(in, table, iidPars[index], iidParCount);
final int[] iidP = (index==0) ? iidParsPrev : iidPars[index-1];
final int iidSteps = iidQuant ? 15 : 7;
Utils.deltaDecode(iidPars[index], iidParCount, iidP,
time, enableIID,
(iidMode==0||iidMode==3) ? 2 : 1,
-iidSteps, iidSteps);
}
private void decodeICCData(BitStream in, boolean time, int index) throws AACException {
final int[][] table = time ? HUFFMAN_ICC_DT : HUFFMAN_ICC_DF;
Huffman.decode(in, table, iccPars[index], iccParCount);
final int[] iccP = (index==0) ? iccParsPrev : iccPars[index-1];
Utils.deltaDecode(iccPars[index], iccParCount, iccP,
time, enableICC,
(iccMode==0||iccMode==3) ? 2 : 1,
0, 7);
}
private void decodeIPDData(BitStream in, boolean time, int index) throws AACException {
final int[][] table = time ? HUFFMAN_IPD_DT : HUFFMAN_IPD_DF;
Huffman.decode(in, table, ipdPars[index], ipdopdParCount);
final int[] ipdP = (index==0) ? ipdParsPrev : ipdPars[index-1];
Utils.deltaModuloDecode(ipdPars[index], ipdopdParCount, ipdP,
time, enableIPDOPD);
}
private void decodeOPDData(BitStream in, boolean time, int index) throws AACException {
final int[][] table = time ? HUFFMAN_OPD_DT : HUFFMAN_OPD_DF;
Huffman.decode(in, table, opdPars[index], ipdopdParCount);
final int[] opdP = (index==0) ? opdParsPrev : opdPars[index-1];
Utils.deltaModuloDecode(opdPars[index], ipdopdParCount, opdP,
time, enableIPDOPD);
}
public boolean hasHeader() {
return header;
}
//============================ processing ==============================
//left,right: [38][64][2]
public void process(float[][][] left, float[][][] right, int top) {
float[][][] lBuf = new float[91][32][2];
float[][][] rBuf = new float[91][32][2];
int use34I = use34 ? 1 : 0;
top += BANDS[use34I]-64;
//memset(ps->delay+top, 0, (BANDS[use34I] - top)*sizeof(ps->delay[0]));
Arrays.fill(delay, top, BANDS[use34I], new float[QMF_SLOTS+MAX_DELAY][2]);
//memset(ps->ap_delay+top, 0, (ALLPASS_BANDS[use34I]-top)*sizeof(ps->ap_delay[0]));
if(top<ALLPASS_BANDS[use34I]) Arrays.fill(ap_delay, top, ALLPASS_BANDS[use34I], new float[AP_LINKS][QMF_SLOTS+MAX_AP_DELAY][2]);
Filterbank.performAnalysis(left, lBuf, in_buf, use34);
decorrelate(lBuf, rBuf);
processStereo(lBuf, rBuf);
Filterbank.performSynthesis(lBuf, left, use34);
Filterbank.performSynthesis(rBuf, right, use34);
}
//out: [32][2]*, in: [32][2]*
private void decorrelate(float[][][] s, float[][][] out) {
final float[][] power = new float[34][QMF_SLOTS];
float[][] transient_gain = new float[34][QMF_SLOTS];
//float *peak_decay_nrg = ps->peak_decay_nrg;
//float *power_smooth = ps->power_smooth;
//float *peak_decay_diff_smooth = ps->peak_decay_diff_smooth;
//float (*delay)[PS_QMF_TIME_SLOTS + PS_MAX_DELAY][2] = ps->delay;
//float (*ap_delay)[PS_AP_LINKS][PS_QMF_TIME_SLOTS + PS_MAX_AP_DELAY][2] = ps->ap_delay;
final int[] k_to_i = use34 ? K_TO_I_34 : K_TO_I_20;
final int use34I = use34 ? 1 : 0;
int i, k, m, n;
int n0 = 0, nL = 32;
if(use34!=use34Old) {
//reset
Arrays.fill(peak_decay_nrg, 0);
Arrays.fill(power_smooth, 0);
Arrays.fill(peak_decay_diff_smooth, 0);
delay = new float[MAX_SSB][QMF_SLOTS+MAX_DELAY][2];
ap_delay = new float[MAX_AP_BANDS][AP_LINKS][QMF_SLOTS+MAX_AP_DELAY][2];
//memset(ps->peak_decay_nrg, 0, sizeof(ps->peak_decay_nrg));
//memset(ps->power_smooth, 0, sizeof(ps->power_smooth));
//memset(ps->peak_decay_diff_smooth, 0, sizeof(ps->peak_decay_diff_smooth));
//memset(ps->delay, 0, sizeof(ps->delay));
//memset(ps->ap_delay, 0, sizeof(ps->ap_delay));
}
//calculate power
for(n = n0; n<nL; n++) {
for(k = 0; k<BANDS[use34I]; k++) {
i = k_to_i[k];
power[i][n] += (s[k][n][0]*s[k][n][0])+(s[k][n][1]*s[k][n][1]);
}
}
//transient detection
float decayed_peak, denom;
for(i = 0; i<PAR_BANDS[use34I]; i++) {
for(n = n0; n<nL; n++) {
decayed_peak = PEAK_DECAY_FACTOR*peak_decay_nrg[i];
peak_decay_nrg[i] = Math.max(decayed_peak, power[i][n]);
power_smooth[i] += A_SMOOTH*(power[i][n]-power_smooth[i]);
peak_decay_diff_smooth[i] += A_SMOOTH*(peak_decay_nrg[i]-power[i][n]-peak_decay_diff_smooth[i]);
denom = TRANSIENT_IMPACT*peak_decay_diff_smooth[i];
transient_gain[i][n] = (denom>power_smooth[i]) ? power_smooth[i]/denom : 1.0f;
}
}
/* decorrelation and transient reduction
PS_AP_LINKS - 1
-----
| | (Q_FRACT_ALLPASS[k][m]*z^-link_delay[m]) - (a[m]*g_decay_slope[k])
H[k][z] = z^-2 * PHI_FRACT[k] * | | ----------------------------------------------------------------
| | 1 - (a[m]*g_decay_slope[k]*Q_FRACT_ALLPASS[k][m]*z^-link_delay[m])
m = 0
d[k][z](out) = transient_gain_mapped[k][z] * H[k][z] * s[k][z]
*/
for(k = 0; k<ALLPASS_BANDS[use34I]; k++) {
int b = k_to_i[k];
float g_decay_slope = 1.0f-DECAY_SLOPE*(k-DECAY_CUTOFF[use34I]);
float[] ag = new float[AP_LINKS];
g_decay_slope = Math.min(Math.max(g_decay_slope, 0.0f), 1.0f);
copyArray(delay[k], nL, delay[k], 0, MAX_DELAY);
copyArray(s[k], 0, delay[k], MAX_DELAY, QMF_SLOTS);
//memcpy(delay[k], delay[k] + nL, MAX_DELAY * sizeof(delay[k][0]));
//memcpy(delay[k] + MAX_DELAY, s[k], QMF_SLOTS * sizeof(delay[k][0]));
for(m = 0; m<AP_LINKS; m++) {
copyArray(ap_delay[k][m], QMF_SLOTS, ap_delay[k][m], 0, 5);
//memcpy(ap_delay[k][m], ap_delay[k][m] + QMF_SLOTS, 5 * sizeof(ap_delay[k][m][0]));
ag[m] = FILTER_COEF_A[m]*g_decay_slope;
}
for(n = n0; n<nL; n++) {
float in_re = delay[k][n+MAX_DELAY-2][0]*PHI_FRACT[use34I][k][0]
-delay[k][n+MAX_DELAY-2][1]*PHI_FRACT[use34I][k][1];
float in_im = delay[k][n+MAX_DELAY-2][0]*PHI_FRACT[use34I][k][1]
+delay[k][n+MAX_DELAY-2][1]*PHI_FRACT[use34I][k][0];
for(m = 0; m<AP_LINKS; m++) {
float a_re = ag[m]*in_re;
float a_im = ag[m]*in_im;
float link_delay_re = ap_delay[k][m][n+5-LINK_DELAY[m]][0];
float link_delay_im = ap_delay[k][m][n+5-LINK_DELAY[m]][1];
float fractional_delay_re = Q_FRACT_ALLPASS[use34I][k][m][0];
float fractional_delay_im = Q_FRACT_ALLPASS[use34I][k][m][1];
ap_delay[k][m][n+5][0] = in_re;
ap_delay[k][m][n+5][1] = in_im;
in_re = link_delay_re*fractional_delay_re-link_delay_im*fractional_delay_im-a_re;
in_im = link_delay_re*fractional_delay_im+link_delay_im*fractional_delay_re-a_im;
ap_delay[k][m][n+5][0] += ag[m]*in_re;
ap_delay[k][m][n+5][1] += ag[m]*in_im;
}
out[k][n][0] = transient_gain[b][n]*in_re;
out[k][n][1] = transient_gain[b][n]*in_im;
}
}
for(; k<SHORT_DELAY_BAND[use34I]; k++) {
//memcpy(delay[k], delay[k]+nL, MAX_DELAY*sizeof(delay[k][0]));
//memcpy(delay[k]+MAX_DELAY, s[k], QMF_SLOTS*sizeof(delay[k][0]));
copyArray(delay[k], nL, delay[k], 0, MAX_DELAY);
copyArray(s[k], 0, delay[k], MAX_DELAY, QMF_SLOTS);
for(n = n0; n<nL; n++) {
out[k][n][0] = transient_gain[k_to_i[k]][n]*delay[k][n+MAX_DELAY-14][0];
out[k][n][1] = transient_gain[k_to_i[k]][n]*delay[k][n+MAX_DELAY-14][1];
}
}
for(; k<BANDS[use34I]; k++) {
//memcpy(delay[k], delay[k]+nL, MAX_DELAY*sizeof(delay[k][0]));
//memcpy(delay[k]+MAX_DELAY, s[k], QMF_SLOTS*sizeof(delay[k][0]));
copyArray(delay[k], nL, delay[k], 0, MAX_DELAY);
copyArray(s[k], 0, delay[k], MAX_DELAY, QMF_SLOTS);
for(n = n0; n<nL; n++) {
//H = delay 1
out[k][n][0] = transient_gain[k_to_i[k]][n]*delay[k][n+MAX_DELAY-1][0];
out[k][n][1] = transient_gain[k_to_i[k]][n]*delay[k][n+MAX_DELAY-1][1];
}
}
}
//utility for copying complex array
private void copyArray(float[][] from, int fromOff, float[][] to, int toOff, int len) {
for(int i = 0; i<len; i++) {
to[toOff+i][0] = from[fromOff+i][0];
to[toOff+i][1] = from[fromOff+i][1];
}
}
//l, r: [32][2]*
private void processStereo(float[][][] l, float[][][] r) {
int e, b, k, n;
final int[][] iidMapped = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
final int[][] iccMapped = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
final int[][] ipdMapped = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
final int[][] opdMapped = new int[MAX_ENVELOPES][MAX_IID_ICC_PARS];
final int[] k_to_i = use34 ? K_TO_I_34 : K_TO_I_20;
final float[][][] H_LUT = iidQuant ? HB : HA;
final int use34I = use34 ? 1 : 0;
//remapping
//memcpy(H11[0][0], H11[0][envCountOld], MAX_IID_ICC_PARS*sizeof(H11[0][0][0]));
System.arraycopy(H11[0][envCountOld], 0, H11[0][0], 0, MAX_IID_ICC_PARS);
//memcpy(H11[1][0], H11[1][envCountOld], MAX_IID_ICC_PARS*sizeof(H11[1][0][0]));
System.arraycopy(H11[1][envCountOld], 0, H11[1][0], 0, MAX_IID_ICC_PARS);
//memcpy(H12[0][0], H12[0][envCountOld], MAX_IID_ICC_PARS*sizeof(H12[0][0][0]));
System.arraycopy(H12[0][envCountOld], 0, H12[0][0], 0, MAX_IID_ICC_PARS);
//memcpy(H12[1][0], H12[1][envCountOld], MAX_IID_ICC_PARS*sizeof(H12[1][0][0]));
System.arraycopy(H12[1][envCountOld], 0, H12[1][0], 0, MAX_IID_ICC_PARS);
//memcpy(H21[0][0], H21[0][envCountOld], MAX_IID_ICC_PARS*sizeof(H21[0][0][0]));
System.arraycopy(H21[0][envCountOld], 0, H21[0][0], 0, MAX_IID_ICC_PARS);
//memcpy(H21[1][0], H21[1][envCountOld], MAX_IID_ICC_PARS*sizeof(H21[1][0][0]));
System.arraycopy(H21[1][envCountOld], 0, H21[1][0], 0, MAX_IID_ICC_PARS);
//memcpy(H22[0][0], H22[0][envCountOld], MAX_IID_ICC_PARS*sizeof(H22[0][0][0]));
System.arraycopy(H22[0][envCountOld], 0, H22[0][0], 0, MAX_IID_ICC_PARS);
//memcpy(H22[1][0], H22[1][envCountOld], MAX_IID_ICC_PARS*sizeof(H22[1][0][0]));
System.arraycopy(H22[1][envCountOld], 0, H22[1][0], 0, MAX_IID_ICC_PARS);
if(use34) {
Utils.remap34(iidMapped, iidPars, iidParCount, envCount, true);
Utils.remap34(iccMapped, iccPars, iccParCount, envCount, true);
if(enableIPDOPD) {
Utils.remap34(ipdMapped, ipdPars, ipdopdParCount, envCount, false);
Utils.remap34(opdMapped, opdPars, ipdopdParCount, envCount, false);
}
if(use34Old) {
Utils.map20To34Float(H11[0][0]);
Utils.map20To34Float(H11[1][0]);
Utils.map20To34Float(H12[0][0]);
Utils.map20To34Float(H12[1][0]);
Utils.map20To34Float(H21[0][0]);
Utils.map20To34Float(H21[1][0]);
Utils.map20To34Float(H22[0][0]);
Utils.map20To34Float(H22[1][0]);
resetIPDOPD();
}
}
else {
Utils.remap20(iidMapped, iidPars, iidParCount, envCount, true);
Utils.remap20(iccMapped, iccPars, iccParCount, envCount, true);
if(enableIPDOPD) {
Utils.remap20(ipdMapped, ipdPars, ipdopdParCount, envCount, false);
Utils.remap20(opdMapped, opdPars, ipdopdParCount, envCount, false);
}
if(use34Old) {
Utils.map34To20Float(H11[0][0]);
Utils.map34To20Float(H11[1][0]);
Utils.map34To20Float(H12[0][0]);
Utils.map34To20Float(H12[1][0]);
Utils.map34To20Float(H21[0][0]);
Utils.map34To20Float(H21[1][0]);
Utils.map34To20Float(H22[0][0]);
Utils.map34To20Float(H22[1][0]);
resetIPDOPD();
}
}
//mixing
final int iidQuantI = iidQuant ? 1 : 0;
for(e = 0; e<envCount; e++) {
for(b = 0; b<PAR_BANDS[use34I]; b++) {
float h11, h12, h21, h22;
h11 = H_LUT[iidMapped[e][b]+7+23*iidQuantI][iccMapped[e][b]][0];
h12 = H_LUT[iidMapped[e][b]+7+23*iidQuantI][iccMapped[e][b]][1];
h21 = H_LUT[iidMapped[e][b]+7+23*iidQuantI][iccMapped[e][b]][2];
h22 = H_LUT[iidMapped[e][b]+7+23*iidQuantI][iccMapped[e][b]][3];
if(enableIPDOPD&&b<ipdopdParCount) {
float h11i, h12i, h21i, h22i;
float ipd_adj_re, ipd_adj_im;
int opd_idx = opd_hist[b]*8+opdMapped[e][b];
int ipd_idx = ipd_hist[b]*8+ipdMapped[e][b];
float opd_re = PD_RE_SMOOTH[opd_idx];
float opd_im = PD_IM_SMOOTH[opd_idx];
float ipd_re = PD_RE_SMOOTH[ipd_idx];
float ipd_im = PD_IM_SMOOTH[ipd_idx];
opd_hist[b] = opd_idx&0x3F;
ipd_hist[b] = ipd_idx&0x3F;
ipd_adj_re = opd_re*ipd_re+opd_im*ipd_im;
ipd_adj_im = opd_im*ipd_re-opd_re*ipd_im;
h11i = h11*opd_im;
h11 = h11*opd_re;
h12i = h12*ipd_adj_im;
h12 = h12*ipd_adj_re;
h21i = h21*opd_im;
h21 = h21*opd_re;
h22i = h22*ipd_adj_im;
h22 = h22*ipd_adj_re;
H11[1][e+1][b] = h11i;
H12[1][e+1][b] = h12i;
H21[1][e+1][b] = h21i;
H22[1][e+1][b] = h22i;
}
H11[0][e+1][b] = h11;
H12[0][e+1][b] = h12;
H21[0][e+1][b] = h21;
H22[0][e+1][b] = h22;
}
for(k = 0; k<BANDS[use34I]; k++) {
float h11r, h12r, h21r, h22r;
float h11i = 0, h12i = 0, h21i = 0, h22i = 0;
float h11r_step, h12r_step, h21r_step, h22r_step;
float h11i_step = 0, h12i_step = 0, h21i_step = 0, h22i_step = 0;
int begin = borderPositions[e];
int stop = borderPositions[e+1];
float width = 1.f/(stop-begin);
b = k_to_i[k];
h11r = H11[0][e][b];
h12r = H12[0][e][b];
h21r = H21[0][e][b];
h22r = H22[0][e][b];
if(enableIPDOPD) {
if((use34&&k<=13&&k>=9)||(!use34&&k<=1)) {
h11i = -H11[1][e][b];
h12i = -H12[1][e][b];
h21i = -H21[1][e][b];
h22i = -H22[1][e][b];
}
else {
h11i = H11[1][e][b];
h12i = H12[1][e][b];
h21i = H21[1][e][b];
h22i = H22[1][e][b];
}
}
//interpolation
h11r_step = (H11[0][e+1][b]-h11r)*width;
h12r_step = (H12[0][e+1][b]-h12r)*width;
h21r_step = (H21[0][e+1][b]-h21r)*width;
h22r_step = (H22[0][e+1][b]-h22r)*width;
if(enableIPDOPD) {
h11i_step = (H11[1][e+1][b]-h11i)*width;
h12i_step = (H12[1][e+1][b]-h12i)*width;
h21i_step = (H21[1][e+1][b]-h21i)*width;
h22i_step = (H22[1][e+1][b]-h22i)*width;
}
for(n = begin+1; n<=stop; n++) {
//l is s, r is d
float l_re = l[k][n][0];
float l_im = l[k][n][1];
float r_re = r[k][n][0];
float r_im = r[k][n][1];
h11r += h11r_step;
h12r += h12r_step;
h21r += h21r_step;
h22r += h22r_step;
if(enableIPDOPD) {
h11i += h11i_step;
h12i += h12i_step;
h21i += h21i_step;
h22i += h22i_step;
l[k][n][0] = h11r*l_re+h21r*r_re-h11i*l_im-h21i*r_im;
l[k][n][1] = h11r*l_im+h21r*r_im+h11i*l_re+h21i*r_re;
r[k][n][0] = h12r*l_re+h22r*r_re-h12i*l_im-h22i*r_im;
r[k][n][1] = h12r*l_im+h22r*r_im+h12i*l_re+h22i*r_re;
}
else {
l[k][n][0] = h11r*l_re+h21r*r_re;
l[k][n][1] = h11r*l_im+h21r*r_im;
r[k][n][0] = h12r*l_re+h22r*r_re;
r[k][n][1] = h12r*l_im+h22r*r_im;
}
}
}
}
}
private void resetIPDOPD() {
int i;
for(i = 0; i<MAX_IPD_OPD_PARS; i++) {
opd_hist[i] = 0;
ipd_hist[i] = 0;
}
}
}