package net.sourceforge.jaad.aac.syntax; import static net.sourceforge.jaad.aac.Profile.*; import org.jcodec.platform.Platform; import net.sourceforge.jaad.aac.AACException; import net.sourceforge.jaad.aac.DecoderConfig; import net.sourceforge.jaad.aac.Profile; import net.sourceforge.jaad.aac.SampleFrequency; import net.sourceforge.jaad.aac.tools.ICPrediction; import static java.lang.System.arraycopy; import org.jcodec.platform.Platform; import net.sourceforge.jaad.aac.AACException; import net.sourceforge.jaad.aac.Profile; import net.sourceforge.jaad.aac.SampleFrequency; import net.sourceforge.jaad.aac.filterbank.FilterBank; import net.sourceforge.jaad.aac.syntax.IBitStream; import net.sourceforge.jaad.aac.syntax.ICSInfo; import net.sourceforge.jaad.aac.syntax.ICStream; import net.sourceforge.jaad.aac.syntax.SyntaxConstants; /** * 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 */ public class ICSInfo implements SyntaxConstants, ScaleFactorBands { public static class LTPrediction implements SyntaxConstants { private static final float[] CODEBOOK = { 0.570829f, 0.696616f, 0.813004f, 0.911304f, 0.984900f, 1.067894f, 1.194601f, 1.369533f }; private final int frameLength; private final int[] states; private int coef, lag, lastBand; private boolean lagUpdate; private boolean[] shortUsed, shortLagPresent, longUsed; private int[] shortLag; public LTPrediction(int frameLength) { this.frameLength = frameLength; states = new int[4*frameLength]; } public void decode(IBitStream _in, ICSInfo info, Profile profile) throws AACException { lag = 0; if(profile.equals(Profile.AAC_LD)) { lagUpdate = _in.readBool(); if(lagUpdate) lag = _in.readBits(10); } else lag = _in.readBits(11); if(lag>(frameLength<<1)) throw new AACException("LTP lag too large: "+lag); coef = _in.readBits(3); final int windowCount = info.getWindowCount(); if(info.isEightShortFrame()) { shortUsed = new boolean[windowCount]; shortLagPresent = new boolean[windowCount]; shortLag = new int[windowCount]; for(int w = 0; w<windowCount; w++) { if((shortUsed[w] = _in.readBool())) { shortLagPresent[w] = _in.readBool(); if(shortLagPresent[w]) shortLag[w] = _in.readBits(4); } } } else { lastBand = Math.min(info.getMaxSFB(), MAX_LTP_SFB); longUsed = new boolean[lastBand]; for(int i = 0; i<lastBand; i++) { longUsed[i] = _in.readBool(); } } } public void setPredictionUnused(int sfb) { if(longUsed!=null) longUsed[sfb] = false; } public void process(ICStream ics, float[] data, FilterBank filterBank, SampleFrequency sf) { final ICSInfo info = ics.getInfo(); if(!info.isEightShortFrame()) { final int samples = frameLength<<1; final float[] _in = new float[2048]; final float[] out = new float[2048]; for(int i = 0; i<samples; i++) { _in[i] = states[samples+i-lag]*CODEBOOK[coef]; } filterBank.processLTP(info.getWindowSequence(), info.getWindowShape(ICSInfo.CURRENT), info.getWindowShape(ICSInfo.PREVIOUS), _in, out); if(ics.isTNSDataPresent()) ics.getTNS().process(ics, out, sf, true); final int[] swbOffsets = info.getSWBOffsets(); final int swbOffsetMax = info.getSWBOffsetMax(); int low, high, bin; for(int sfb = 0; sfb<lastBand; sfb++) { if(longUsed[sfb]) { low = swbOffsets[sfb]; high = Math.min(swbOffsets[sfb+1], swbOffsetMax); for(bin = low; bin<high; bin++) { data[bin] += out[bin]; } } } } } public void updateState(float[] time, float[] overlap, Profile profile) { int i; if(profile.equals(Profile.AAC_LD)) { for(i = 0; i<frameLength; i++) { states[i] = states[i+frameLength]; states[frameLength+i] = states[i+(frameLength*2)]; states[(frameLength*2)+i] = Math.round(time[i]); states[(frameLength*3)+i] = Math.round(overlap[i]); } } else { for(i = 0; i<frameLength; i++) { states[i] = states[i+frameLength]; states[frameLength+i] = Math.round(time[i]); states[(frameLength*2)+i] = Math.round(overlap[i]); } } } public static boolean isLTPProfile(Profile profile) { return profile.equals(Profile.AAC_LTP)||profile.equals(Profile.ER_AAC_LTP)||profile.equals(Profile.AAC_LD); } public void copy(LTPrediction ltp) { arraycopy(ltp.states, 0, states, 0, states.length); coef = ltp.coef; lag = ltp.lag; lastBand = ltp.lastBand; lagUpdate = ltp.lagUpdate; shortUsed = Platform.copyOfBool(ltp.shortUsed, ltp.shortUsed.length); shortLagPresent = Platform.copyOfBool(ltp.shortLagPresent, ltp.shortLagPresent.length); shortLag = Platform.copyOfInt(ltp.shortLag, ltp.shortLag.length); longUsed = Platform.copyOfBool(ltp.longUsed, ltp.longUsed.length); } } public static final int WINDOW_SHAPE_SINE = 0; public static final int WINDOW_SHAPE_KAISER = 1; public static final int PREVIOUS = 0; public static final int CURRENT = 1; public static enum WindowSequence { ONLY_LONG_SEQUENCE, LONG_START_SEQUENCE, EIGHT_SHORT_SEQUENCE, LONG_STOP_SEQUENCE; } public static WindowSequence windowSequenceFromInt(int i) throws AACException { WindowSequence[] values = WindowSequence.values(); if (i >= values.length) { throw new AACException("unknown window sequence type"); } return values[i]; } private final int frameLength; private WindowSequence windowSequence; private int[] windowShape; private int maxSFB; //prediction private boolean predictionDataPresent; private ICPrediction icPredict; boolean ltpData1Present, ltpData2Present; private LTPrediction ltPredict1, ltPredict2; //windows/sfbs private int windowCount; private int windowGroupCount; private int[] windowGroupLength; private int swbCount; private int[] swbOffsets; public ICSInfo(int frameLength) { this.frameLength = frameLength; windowShape = new int[2]; windowSequence = WindowSequence.ONLY_LONG_SEQUENCE; windowGroupLength = new int[MAX_WINDOW_GROUP_COUNT]; ltpData1Present = false; ltpData2Present = false; } /* ========== decoding ========== */ public void decode(IBitStream _in, DecoderConfig conf, boolean commonWindow) throws AACException { final SampleFrequency sf = conf.getSampleFrequency(); if(sf.equals(SampleFrequency.SAMPLE_FREQUENCY_NONE)) throw new AACException("invalid sample frequency"); _in.skipBit(); //reserved windowSequence = windowSequenceFromInt(_in.readBits(2)); windowShape[PREVIOUS] = windowShape[CURRENT]; windowShape[CURRENT] = _in.readBit(); windowGroupCount = 1; windowGroupLength[0] = 1; if(windowSequence.equals(WindowSequence.EIGHT_SHORT_SEQUENCE)) { maxSFB = _in.readBits(4); int i; for(i = 0; i<7; i++) { if(_in.readBool()) windowGroupLength[windowGroupCount-1]++; else { windowGroupCount++; windowGroupLength[windowGroupCount-1] = 1; } } windowCount = 8; swbOffsets = SWB_OFFSET_SHORT_WINDOW[sf.getIndex()]; swbCount = SWB_SHORT_WINDOW_COUNT[sf.getIndex()]; predictionDataPresent = false; } else { maxSFB = _in.readBits(6); windowCount = 1; swbOffsets = SWB_OFFSET_LONG_WINDOW[sf.getIndex()]; swbCount = SWB_LONG_WINDOW_COUNT[sf.getIndex()]; predictionDataPresent = _in.readBool(); if(predictionDataPresent) readPredictionData(_in, conf.getProfile(), sf, commonWindow); } } private void readPredictionData(IBitStream _in, Profile profile, SampleFrequency sf, boolean commonWindow) throws AACException { if (AAC_MAIN == profile) { if(icPredict==null) icPredict = new ICPrediction(); icPredict.decode(_in, maxSFB, sf); } else if (AAC_LTP == profile) { if(ltpData1Present = _in.readBool()) { if(ltPredict1==null) ltPredict1 = new LTPrediction(frameLength); ltPredict1.decode(_in, this, profile); } if(commonWindow) { if(ltpData2Present = _in.readBool()) { if(ltPredict2==null) ltPredict2 = new LTPrediction(frameLength); ltPredict2.decode(_in, this, profile); } } } else if(ER_AAC_LTP == profile) { if(!commonWindow) { if(ltpData1Present = _in.readBool()) { if(ltPredict1==null) ltPredict1 = new LTPrediction(frameLength); ltPredict1.decode(_in, this, profile); } } } else { throw new AACException("unexpected profile for LTP: "+profile); } } /* =========== gets ============ */ public int getMaxSFB() { return maxSFB; } public int getSWBCount() { return swbCount; } public int[] getSWBOffsets() { return swbOffsets; } public int getSWBOffsetMax() { return swbOffsets[swbCount]; } public int getWindowCount() { return windowCount; } public int getWindowGroupCount() { return windowGroupCount; } public int getWindowGroupLength(int g) { return windowGroupLength[g]; } public WindowSequence getWindowSequence() { return windowSequence; } public boolean isEightShortFrame() { return windowSequence.equals(WindowSequence.EIGHT_SHORT_SEQUENCE); } public int getWindowShape(int index) { return windowShape[index]; } public boolean isICPredictionPresent() { return predictionDataPresent; } public ICPrediction getICPrediction() { return icPredict; } public boolean isLTPrediction1Present() { return ltpData1Present; } public LTPrediction getLTPrediction1() { return ltPredict1; } public boolean isLTPrediction2Present() { return ltpData2Present; } public LTPrediction getLTPrediction2() { return ltPredict2; } public void unsetPredictionSFB(int sfb) { if(predictionDataPresent) icPredict.setPredictionUnused(sfb); if(ltpData1Present) ltPredict1.setPredictionUnused(sfb); if(ltpData2Present) ltPredict2.setPredictionUnused(sfb); } public void setData(ICSInfo info) { windowSequence = WindowSequence.valueOf(info.windowSequence.name()); windowShape[PREVIOUS] = windowShape[CURRENT]; windowShape[CURRENT] = info.windowShape[CURRENT]; maxSFB = info.maxSFB; predictionDataPresent = info.predictionDataPresent; if(predictionDataPresent) icPredict = info.icPredict; ltpData1Present = info.ltpData1Present; if(ltpData1Present) { ltPredict1.copy(info.ltPredict1); ltPredict2.copy(info.ltPredict2); } windowCount = info.windowCount; windowGroupCount = info.windowGroupCount; windowGroupLength = Platform.copyOfInt(info.windowGroupLength, info.windowGroupLength.length); swbCount = info.swbCount; swbOffsets = Platform.copyOfInt(info.swbOffsets, info.swbOffsets.length); } }