/*
* 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.tools;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.SampleFrequency;
import net.sourceforge.jaad.aac.syntax.BitStream;
import net.sourceforge.jaad.aac.syntax.Constants;
import net.sourceforge.jaad.aac.syntax.ICSInfo;
import net.sourceforge.jaad.aac.syntax.ICStream;
import java.util.logging.Level;
/**
* Intra-channel prediction used in profile Main
* @author in-somnia
*/
public class ICPrediction {
private static final float SF_SCALE = 1.0f/-1024.0f;
private static final float INV_SF_SCALE = 1.0f/SF_SCALE;
private static final int MAX_PREDICTORS = 672;
private static final float A = 0.953125f; //61.0 / 64
private static final float ALPHA = 0.90625f; //29.0 / 32
private boolean predictorReset;
private int predictorResetGroup;
private boolean[] predictionUsed;
private PredictorState[] states;
private static final class PredictorState {
float cor0 = 0.0f;
float cor1 = 0.0f;
float var0 = 0.0f;
float var1 = 0.0f;
float r0 = 1.0f;
float r1 = 1.0f;
}
public ICPrediction() {
states = new PredictorState[MAX_PREDICTORS];
resetAllPredictors();
}
public void decode(BitStream in, int maxSFB, SampleFrequency sf) throws AACException {
final int predictorCount = sf.getPredictorCount();
if(predictorReset = in.readBool()) predictorResetGroup = in.readBits(5);
final int maxPredSFB = sf.getMaximalPredictionSFB();
final int length = Math.min(maxSFB, maxPredSFB);
predictionUsed = new boolean[length];
for(int sfb = 0; sfb<length; sfb++) {
predictionUsed[sfb] = in.readBool();
}
Constants.LOGGER.log(Level.WARNING, "ICPrediction: maxSFB={0}, maxPredSFB={1}", new int[]{maxSFB, maxPredSFB});
/*//if maxSFB<maxPredSFB set remaining to false
for(int sfb = length; sfb<maxPredSFB; sfb++) {
predictionUsed[sfb] = false;
}*/
}
public void setPredictionUnused(int sfb) {
predictionUsed[sfb] = false;
}
public void process(ICStream ics, float[] data, SampleFrequency sf) {
final ICSInfo info = ics.getInfo();
if(info.isEightShortFrame()) resetAllPredictors();
else {
final int len = Math.min(sf.getMaximalPredictionSFB(), info.getMaxSFB());
final int[] swbOffsets = info.getSWBOffsets();
int k;
for(int sfb = 0; sfb<len; sfb++) {
for(k = swbOffsets[sfb]; k<swbOffsets[sfb+1]; k++) {
predict(data, k, predictionUsed[sfb]);
}
}
if(predictorReset) resetPredictorGroup(predictorResetGroup);
}
}
private void resetPredictState(int index) {
if(states[index]==null) states[index] = new PredictorState();
states[index].r0 = 0;
states[index].r1 = 0;
states[index].cor0 = 0;
states[index].cor1 = 0;
states[index].var0 = 0x3F80;
states[index].var1 = 0x3F80;
}
private void resetAllPredictors() {
int i;
for(i = 0; i<states.length; i++) {
resetPredictState(i);
}
}
private void resetPredictorGroup(int group) {
int i;
for(i = group-1; i<states.length; i += 30) {
resetPredictState(i);
}
}
private void predict(float[] data, int off, boolean output) {
if(states[off]==null) states[off] = new PredictorState();
final PredictorState state = states[off];
final float r0 = state.r0, r1 = state.r1;
final float cor0 = state.cor0, cor1 = state.cor1;
final float var0 = state.var0, var1 = state.var1;
final float k1 = var0>1 ? cor0*even(A/var0) : 0;
final float k2 = var1>1 ? cor1*even(A/var1) : 0;
final float pv = round(k1*r0+k2*r1);
if(output) data[off] += pv*SF_SCALE;
final float e0 = (data[off]*INV_SF_SCALE);
final float e1 = e0-k1*r0;
state.cor1 = trunc(ALPHA*cor1+r1*e1);
state.var1 = trunc(ALPHA*var1+0.5f*(r1*r1+e1*e1));
state.cor0 = trunc(ALPHA*cor0+r0*e0);
state.var0 = trunc(ALPHA*var0+0.5f*(r0*r0+e0*e0));
state.r1 = trunc(A*(r0-k1*e0));
state.r0 = trunc(A*e0);
}
private float round(float pf) {
return Float.intBitsToFloat((Float.floatToIntBits(pf)+0x00008000)&0xFFFF0000);
}
private float even(float pf) {
int i = Float.floatToIntBits(pf);
i = (i+0x00007FFF+(i&0x00010000>>16))&0xFFFF0000;
return Float.intBitsToFloat(i);
}
private float trunc(float pf) {
return Float.intBitsToFloat(Float.floatToIntBits(pf)&0xFFFF0000);
}
}