src/main/java/net/sourceforge/jaad/aac/ps/PS.java
private void ps_decorrelate(float[][][] X_left, float[][][] X_right,
		float[][][] X_hybrid_left, float[][][] X_hybrid_right) {
		int gr, n, m, bk;
		int temp_delay = 0;
		int sb, maxsb;
		int[] temp_delay_ser = new int[NO_ALLPASS_LINKS];
		float P_SmoothPeakDecayDiffNrg, nrg;
		float[][] P = new float[32][34];
		float[][] G_TransientRatio = new float[32][34];
		float[] inputLeft = new float[2];


		/* chose hybrid filterbank: 20 or 34 band case */
		float[][] Phi_Fract_SubQmf;
		if(this.use34hybrid_bands) {
			Phi_Fract_SubQmf = Phi_Fract_SubQmf34;
		}
		else {
			Phi_Fract_SubQmf = Phi_Fract_SubQmf20;
		}

		/* clear the energy values */
		for(n = 0; n<32; n++) {
			for(bk = 0; bk<34; bk++) {
				P[n][bk] = 0;
			}
		}

		/* calculate the energy in each parameter band b(k) */
		for(gr = 0; gr<this.num_groups; gr++) {
			/* select the parameter index b(k) to which this group belongs */
			bk = (~NEGATE_IPD_MASK)&this.map_group2bk[gr];

			/* select the upper subband border for this group */
			maxsb = (gr<this.num_hybrid_groups) ? this.group_border[gr]+1 : this.group_border[gr+1];

			for(sb = this.group_border[gr]; sb<maxsb; sb++) {
				for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {

					/* input from hybrid subbands or QMF subbands */
					if(gr<this.num_hybrid_groups) {
						inputLeft[0] = X_hybrid_left[n][sb][0];
						inputLeft[1] = X_hybrid_left[n][sb][1];
					}
					else {
						inputLeft[0] = X_left[n][sb][0];
						inputLeft[1] = X_left[n][sb][1];
					}

					/* accumulate energy */
					P[n][bk] += (inputLeft[0]*inputLeft[0])+(inputLeft[1]*inputLeft[1]);
				}
			}
		}

		/* calculate transient reduction ratio for each parameter band b(k) */
		for(bk = 0; bk<this.nr_par_bands; bk++) {
			for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {
				float gamma = 1.5f;

				this.P_PeakDecayNrg[bk] = (this.P_PeakDecayNrg[bk]*this.alpha_decay);
				if(this.P_PeakDecayNrg[bk]<P[n][bk])
					this.P_PeakDecayNrg[bk] = P[n][bk];

				/* apply smoothing filter to peak decay energy */
				P_SmoothPeakDecayDiffNrg = this.P_SmoothPeakDecayDiffNrg_prev[bk];
				P_SmoothPeakDecayDiffNrg += ((this.P_PeakDecayNrg[bk]-P[n][bk]-this.P_SmoothPeakDecayDiffNrg_prev[bk])*alpha_smooth);
				this.P_SmoothPeakDecayDiffNrg_prev[bk] = P_SmoothPeakDecayDiffNrg;

				/* apply smoothing filter to energy */
				nrg = this.P_prev[bk];
				nrg += ((P[n][bk]-this.P_prev[bk])*this.alpha_smooth);
				this.P_prev[bk] = nrg;

				/* calculate transient ratio */
				if((P_SmoothPeakDecayDiffNrg*gamma)<=nrg) {
					G_TransientRatio[n][bk] = 1.0f;
				}
				else {
					G_TransientRatio[n][bk] = (nrg/(P_SmoothPeakDecayDiffNrg*gamma));
				}
			}
		}

		/* apply stereo decorrelation filter to the signal */
		for(gr = 0; gr<this.num_groups; gr++) {
			if(gr<this.num_hybrid_groups)
				maxsb = this.group_border[gr]+1;
			else
				maxsb = this.group_border[gr+1];

			/* QMF channel */
			for(sb = this.group_border[gr]; sb<maxsb; sb++) {
				float g_DecaySlope;
				float[] g_DecaySlope_filt = new float[NO_ALLPASS_LINKS];

				/* g_DecaySlope: [0..1] */
				if(gr<this.num_hybrid_groups||sb<=this.decay_cutoff) {
					g_DecaySlope = 1.0f;
				}
				else {
					int decay = this.decay_cutoff-sb;
					if(decay<=-20 /* -1/DECAY_SLOPE */) {
						g_DecaySlope = 0;
					}
					else {
						/* decay(int)*decay_slope(frac) = g_DecaySlope(frac) */
						g_DecaySlope = 1.0f+DECAY_SLOPE*decay;
					}
				}

				/* calculate g_DecaySlope_filt for every m multiplied by filter_a[m] */
				for(m = 0; m<NO_ALLPASS_LINKS; m++) {
					g_DecaySlope_filt[m] = g_DecaySlope*filter_a[m];
				}


				/* set delay indices */
				temp_delay = this.saved_delay;
				for(n = 0; n<NO_ALLPASS_LINKS; n++) {
					temp_delay_ser[n] = this.delay_buf_index_ser[n];
				}

				for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {
					float[] tmp = new float[2], tmp0 = new float[2], R0 = new float[2];

					if(gr<this.num_hybrid_groups) {
						/* hybrid filterbank input */
						inputLeft[0] = X_hybrid_left[n][sb][0];
						inputLeft[1] = X_hybrid_left[n][sb][1];
					}
					else {
						/* QMF filterbank input */
						inputLeft[0] = X_left[n][sb][0];
						inputLeft[1] = X_left[n][sb][1];
					}

					if(sb>this.nr_allpass_bands&&gr>=this.num_hybrid_groups) {
						/* delay */

						/* never hybrid subbands here, always QMF subbands */
						tmp[0] = this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][0];
						tmp[1] = this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][1];
						R0[0] = tmp[0];
						R0[1] = tmp[1];
						this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][0] = inputLeft[0];
						this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][1] = inputLeft[1];
					}
					else {
						/* allpass filter */
						//int m;
						float[] Phi_Fract = new float[2];

						/* fetch parameters */
						if(gr<this.num_hybrid_groups) {
							/* select data from the hybrid subbands */
							tmp0[0] = this.delay_SubQmf[temp_delay][sb][0];
							tmp0[1] = this.delay_SubQmf[temp_delay][sb][1];

							this.delay_SubQmf[temp_delay][sb][0] = inputLeft[0];
							this.delay_SubQmf[temp_delay][sb][1] = inputLeft[1];

							Phi_Fract[0] = Phi_Fract_SubQmf[sb][0];
							Phi_Fract[1] = Phi_Fract_SubQmf[sb][1];
						}
						else {
							/* select data from the QMF subbands */
							tmp0[0] = this.delay_Qmf[temp_delay][sb][0];
							tmp0[1] = this.delay_Qmf[temp_delay][sb][1];

							this.delay_Qmf[temp_delay][sb][0] = inputLeft[0];
							this.delay_Qmf[temp_delay][sb][1] = inputLeft[1];

							Phi_Fract[0] = Phi_Fract_Qmf[sb][0];
							Phi_Fract[1] = Phi_Fract_Qmf[sb][1];
						}

						/* z^(-2) * Phi_Fract[k] */
						tmp[0] = (tmp[0]*Phi_Fract[0])+(tmp0[1]*Phi_Fract[1]);
						tmp[1] = (tmp0[1]*Phi_Fract[0])-(tmp0[0]*Phi_Fract[1]);

						R0[0] = tmp[0];
						R0[1] = tmp[1];
						for(m = 0; m<NO_ALLPASS_LINKS; m++) {
							float[] Q_Fract_allpass = new float[2], tmp2 = new float[2];

							/* fetch parameters */
							if(gr<this.num_hybrid_groups) {
								/* select data from the hybrid subbands */
								tmp0[0] = this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][0];
								tmp0[1] = this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][1];

								if(this.use34hybrid_bands) {
									Q_Fract_allpass[0] = Q_Fract_allpass_SubQmf34[sb][m][0];
									Q_Fract_allpass[1] = Q_Fract_allpass_SubQmf34[sb][m][1];
								}
								else {
									Q_Fract_allpass[0] = Q_Fract_allpass_SubQmf20[sb][m][0];
									Q_Fract_allpass[1] = Q_Fract_allpass_SubQmf20[sb][m][1];
								}
							}
							else {
								/* select data from the QMF subbands */
								tmp0[0] = this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][0];
								tmp0[1] = this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][1];

								Q_Fract_allpass[0] = Q_Fract_allpass_Qmf[sb][m][0];
								Q_Fract_allpass[1] = Q_Fract_allpass_Qmf[sb][m][1];
							}

							/* delay by a fraction */
							/* z^(-d(m)) * Q_Fract_allpass[k,m] */
							tmp[0] = (tmp0[0]*Q_Fract_allpass[0])+(tmp0[1]*Q_Fract_allpass[1]);
							tmp[1] = (tmp0[1]*Q_Fract_allpass[0])-(tmp0[0]*Q_Fract_allpass[1]);

							/* -a(m) * g_DecaySlope[k] */
							tmp[0] += -(g_DecaySlope_filt[m]*R0[0]);
							tmp[1] += -(g_DecaySlope_filt[m]*R0[1]);

							/* -a(m) * g_DecaySlope[k] * Q_Fract_allpass[k,m] * z^(-d(m)) */
							tmp2[0] = R0[0]+(g_DecaySlope_filt[m]*tmp[0]);
							tmp2[1] = R0[1]+(g_DecaySlope_filt[m]*tmp[1]);

							/* store sample */
							if(gr<this.num_hybrid_groups) {
								this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][0] = tmp2[0];
								this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][1] = tmp2[1];
							}
							else {
								this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][0] = tmp2[0];
								this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][1] = tmp2[1];
							}

							/* store for next iteration (or as output value if last iteration) */
							R0[0] = tmp[0];
							R0[1] = tmp[1];
						}
					}

					/* select b(k) for reading the transient ratio */
					bk = (~NEGATE_IPD_MASK)&this.map_group2bk[gr];

					/* duck if a past transient is found */
					R0[0] = (G_TransientRatio[n][bk]*R0[0]);
					R0[1] = (G_TransientRatio[n][bk]*R0[1]);

					if(gr<this.num_hybrid_groups) {
						/* hybrid */
						X_hybrid_right[n][sb][0] = R0[0];
						X_hybrid_right[n][sb][1] = R0[1];
					}
					else {
						/* QMF */
						X_right[n][sb][0] = R0[0];
						X_right[n][sb][1] = R0[1];
					}

					/* Update delay buffer index */
					if(++temp_delay>=2) {
						temp_delay = 0;
					}

					/* update delay indices */
					if(sb>this.nr_allpass_bands&&gr>=this.num_hybrid_groups) {
						/* delay_D depends on the samplerate, it can hold the values 14 and 1 */
						if(++this.delay_buf_index_delay[sb]>=this.delay_D[sb]) {
							this.delay_buf_index_delay[sb] = 0;
						}
					}

					for(m = 0; m<NO_ALLPASS_LINKS; m++) {
						if(++temp_delay_ser[m]>=this.num_sample_delay_ser[m]) {
							temp_delay_ser[m] = 0;
						}
					}
				}
			}
		}

		/* update delay indices */
		this.saved_delay = temp_delay;
		for(m = 0; m<NO_ALLPASS_LINKS; m++) {
			this.delay_buf_index_ser[m] = temp_delay_ser[m];
		}
	}
private void ps_decorrelate(float[][][] X_left, float[][][] X_right,
		float[][][] X_hybrid_left, float[][][] X_hybrid_right) {
		int gr, n, m, bk;
		int temp_delay = 0;
		int sb, maxsb;
		int[] temp_delay_ser = new int[NO_ALLPASS_LINKS];
		float P_SmoothPeakDecayDiffNrg, nrg;
		float[][] P = new float[32][34];
		float[][] G_TransientRatio = new float[32][34];
		float[] inputLeft = new float[2];


		/* chose hybrid filterbank: 20 or 34 band case */
		float[][] Phi_Fract_SubQmf;
		if(this.use34hybrid_bands) {
			Phi_Fract_SubQmf = Phi_Fract_SubQmf34;
		}
		else {
			Phi_Fract_SubQmf = Phi_Fract_SubQmf20;
		}

		/* clear the energy values */
		for(n = 0; n<32; n++) {
			for(bk = 0; bk<34; bk++) {
				P[n][bk] = 0;
			}
		}

		/* calculate the energy in each parameter band b(k) */
		for(gr = 0; gr<this.num_groups; gr++) {
			/* select the parameter index b(k) to which this group belongs */
			bk = (~NEGATE_IPD_MASK)&this.map_group2bk[gr];

			/* select the upper subband border for this group */
			maxsb = (gr<this.num_hybrid_groups) ? this.group_border[gr]+1 : this.group_border[gr+1];

			for(sb = this.group_border[gr]; sb<maxsb; sb++) {
				for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {

					/* input from hybrid subbands or QMF subbands */
					if(gr<this.num_hybrid_groups) {
						inputLeft[0] = X_hybrid_left[n][sb][0];
						inputLeft[1] = X_hybrid_left[n][sb][1];
					}
					else {
						inputLeft[0] = X_left[n][sb][0];
						inputLeft[1] = X_left[n][sb][1];
					}

					/* accumulate energy */
					P[n][bk] += (inputLeft[0]*inputLeft[0])+(inputLeft[1]*inputLeft[1]);
				}
			}
		}

		/* calculate transient reduction ratio for each parameter band b(k) */
		for(bk = 0; bk<this.nr_par_bands; bk++) {
			for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {
				float gamma = 1.5f;

				this.P_PeakDecayNrg[bk] = (this.P_PeakDecayNrg[bk]*this.alpha_decay);
				if(this.P_PeakDecayNrg[bk]<P[n][bk])
					this.P_PeakDecayNrg[bk] = P[n][bk];

				/* apply smoothing filter to peak decay energy */
				P_SmoothPeakDecayDiffNrg = this.P_SmoothPeakDecayDiffNrg_prev[bk];
				P_SmoothPeakDecayDiffNrg += ((this.P_PeakDecayNrg[bk]-P[n][bk]-this.P_SmoothPeakDecayDiffNrg_prev[bk])*alpha_smooth);
				this.P_SmoothPeakDecayDiffNrg_prev[bk] = P_SmoothPeakDecayDiffNrg;

				/* apply smoothing filter to energy */
				nrg = this.P_prev[bk];
				nrg += ((P[n][bk]-this.P_prev[bk])*this.alpha_smooth);
				this.P_prev[bk] = nrg;

				/* calculate transient ratio */
				if((P_SmoothPeakDecayDiffNrg*gamma)<=nrg) {
					G_TransientRatio[n][bk] = 1.0f;
				}
				else {
					G_TransientRatio[n][bk] = (nrg/(P_SmoothPeakDecayDiffNrg*gamma));
				}
			}
		}

		/* apply stereo decorrelation filter to the signal */
		for(gr = 0; gr<this.num_groups; gr++) {
			if(gr<this.num_hybrid_groups)
				maxsb = this.group_border[gr]+1;
			else
				maxsb = this.group_border[gr+1];

			/* QMF channel */
			for(sb = this.group_border[gr]; sb<maxsb; sb++) {
				float g_DecaySlope;
				float[] g_DecaySlope_filt = new float[NO_ALLPASS_LINKS];

				/* g_DecaySlope: [0..1] */
				if(gr<this.num_hybrid_groups||sb<=this.decay_cutoff) {
					g_DecaySlope = 1.0f;
				}
				else {
					int decay = this.decay_cutoff-sb;
					if(decay<=-20 /* -1/DECAY_SLOPE */) {
						g_DecaySlope = 0;
					}
					else {
						/* decay(int)*decay_slope(frac) = g_DecaySlope(frac) */
						g_DecaySlope = 1.0f+DECAY_SLOPE*decay;
					}
				}

				/* calculate g_DecaySlope_filt for every m multiplied by filter_a[m] */
				for(m = 0; m<NO_ALLPASS_LINKS; m++) {
					g_DecaySlope_filt[m] = g_DecaySlope*filter_a[m];
				}


				/* set delay indices */
				temp_delay = this.saved_delay;
				for(n = 0; n<NO_ALLPASS_LINKS; n++) {
					temp_delay_ser[n] = this.delay_buf_index_ser[n];
				}

				for(n = this.border_position[0]; n<this.border_position[this.num_env]; n++) {
					float[] tmp = new float[2], tmp0 = new float[2], R0 = new float[2];

					if(gr<this.num_hybrid_groups) {
						/* hybrid filterbank input */
						inputLeft[0] = X_hybrid_left[n][sb][0];
						inputLeft[1] = X_hybrid_left[n][sb][1];
					}
					else {
						/* QMF filterbank input */
						inputLeft[0] = X_left[n][sb][0];
						inputLeft[1] = X_left[n][sb][1];
					}

					if(sb>this.nr_allpass_bands&&gr>=this.num_hybrid_groups) {
						/* delay */

						/* never hybrid subbands here, always QMF subbands */
						tmp[0] = this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][0];
						tmp[1] = this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][1];
						R0[0] = tmp[0];
						R0[1] = tmp[1];
						this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][0] = inputLeft[0];
						this.delay_Qmf[this.delay_buf_index_delay[sb]][sb][1] = inputLeft[1];
					}
					else {
						/* allpass filter */
						//int m;
						float[] Phi_Fract = new float[2];

						/* fetch parameters */
						if(gr<this.num_hybrid_groups) {
							/* select data from the hybrid subbands */
							tmp0[0] = this.delay_SubQmf[temp_delay][sb][0];
							tmp0[1] = this.delay_SubQmf[temp_delay][sb][1];

							this.delay_SubQmf[temp_delay][sb][0] = inputLeft[0];
							this.delay_SubQmf[temp_delay][sb][1] = inputLeft[1];

							Phi_Fract[0] = Phi_Fract_SubQmf[sb][0];
							Phi_Fract[1] = Phi_Fract_SubQmf[sb][1];
						}
						else {
							/* select data from the QMF subbands */
							tmp0[0] = this.delay_Qmf[temp_delay][sb][0];
							tmp0[1] = this.delay_Qmf[temp_delay][sb][1];

							this.delay_Qmf[temp_delay][sb][0] = inputLeft[0];
							this.delay_Qmf[temp_delay][sb][1] = inputLeft[1];

							Phi_Fract[0] = Phi_Fract_Qmf[sb][0];
							Phi_Fract[1] = Phi_Fract_Qmf[sb][1];
						}

						/* z^(-2) * Phi_Fract[k] */
						tmp[0] = (tmp[0]*Phi_Fract[0])+(tmp0[1]*Phi_Fract[1]);
						tmp[1] = (tmp0[1]*Phi_Fract[0])-(tmp0[0]*Phi_Fract[1]);

						R0[0] = tmp[0];
						R0[1] = tmp[1];
						for(m = 0; m<NO_ALLPASS_LINKS; m++) {
							float[] Q_Fract_allpass = new float[2], tmp2 = new float[2];

							/* fetch parameters */
							if(gr<this.num_hybrid_groups) {
								/* select data from the hybrid subbands */
								tmp0[0] = this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][0];
								tmp0[1] = this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][1];

								if(this.use34hybrid_bands) {
									Q_Fract_allpass[0] = Q_Fract_allpass_SubQmf34[sb][m][0];
									Q_Fract_allpass[1] = Q_Fract_allpass_SubQmf34[sb][m][1];
								}
								else {
									Q_Fract_allpass[0] = Q_Fract_allpass_SubQmf20[sb][m][0];
									Q_Fract_allpass[1] = Q_Fract_allpass_SubQmf20[sb][m][1];
								}
							}
							else {
								/* select data from the QMF subbands */
								tmp0[0] = this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][0];
								tmp0[1] = this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][1];

								Q_Fract_allpass[0] = Q_Fract_allpass_Qmf[sb][m][0];
								Q_Fract_allpass[1] = Q_Fract_allpass_Qmf[sb][m][1];
							}

							/* delay by a fraction */
							/* z^(-d(m)) * Q_Fract_allpass[k,m] */
							tmp[0] = (tmp0[0]*Q_Fract_allpass[0])+(tmp0[1]*Q_Fract_allpass[1]);
							tmp[1] = (tmp0[1]*Q_Fract_allpass[0])-(tmp0[0]*Q_Fract_allpass[1]);

							/* -a(m) * g_DecaySlope[k] */
							tmp[0] += -(g_DecaySlope_filt[m]*R0[0]);
							tmp[1] += -(g_DecaySlope_filt[m]*R0[1]);

							/* -a(m) * g_DecaySlope[k] * Q_Fract_allpass[k,m] * z^(-d(m)) */
							tmp2[0] = R0[0]+(g_DecaySlope_filt[m]*tmp[0]);
							tmp2[1] = R0[1]+(g_DecaySlope_filt[m]*tmp[1]);

							/* store sample */
							if(gr<this.num_hybrid_groups) {
								this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][0] = tmp2[0];
								this.delay_SubQmf_ser[m][temp_delay_ser[m]][sb][1] = tmp2[1];
							}
							else {
								this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][0] = tmp2[0];
								this.delay_Qmf_ser[m][temp_delay_ser[m]][sb][1] = tmp2[1];
							}

							/* store for next iteration (or as output value if last iteration) */
							R0[0] = tmp[0];
							R0[1] = tmp[1];
						}
					}

					/* select b(k) for reading the transient ratio */
					bk = (~NEGATE_IPD_MASK)&this.map_group2bk[gr];

					/* duck if a past transient is found */
					R0[0] = (G_TransientRatio[n][bk]*R0[0]);
					R0[1] = (G_TransientRatio[n][bk]*R0[1]);

					if(gr<this.num_hybrid_groups) {
						/* hybrid */
						X_hybrid_right[n][sb][0] = R0[0];
						X_hybrid_right[n][sb][1] = R0[1];
					}
					else {
						/* QMF */
						X_right[n][sb][0] = R0[0];
						X_right[n][sb][1] = R0[1];
					}

					/* Update delay buffer index */
					if(++temp_delay>=2) {
						temp_delay = 0;
					}

					/* update delay indices */
					if(sb>this.nr_allpass_bands&&gr>=this.num_hybrid_groups) {
						/* delay_D depends on the samplerate, it can hold the values 14 and 1 */
						if(++this.delay_buf_index_delay[sb]>=this.delay_D[sb]) {
							this.delay_buf_index_delay[sb] = 0;
						}
					}

					for(m = 0; m<NO_ALLPASS_LINKS; m++) {
						if(++temp_delay_ser[m]>=this.num_sample_delay_ser[m]) {
							temp_delay_ser[m] = 0;
						}
					}
				}
			}
		}

		/* update delay indices */
		this.saved_delay = temp_delay;
		for(m = 0; m<NO_ALLPASS_LINKS; m++) {
			this.delay_buf_index_ser[m] = temp_delay_ser[m];
		}
	}
/* decorrelate the mono signal using an allpass filter