/* * 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.syntax; import net.sourceforge.jaad.aac.AACException; import net.sourceforge.jaad.aac.ChannelConfiguration; import net.sourceforge.jaad.aac.DecoderConfig; import net.sourceforge.jaad.aac.Profile; import net.sourceforge.jaad.aac.SampleBuffer; import net.sourceforge.jaad.aac.SampleFrequency; import net.sourceforge.jaad.aac.filterbank.FilterBank; import net.sourceforge.jaad.aac.tools.LTPrediction; import net.sourceforge.jaad.aac.sbr2.SBR; import net.sourceforge.jaad.aac.tools.IS; import net.sourceforge.jaad.aac.tools.MS; import java.util.logging.Level; public class SyntacticElements implements Constants { //global properties private DecoderConfig config; private boolean sbrPresent, psPresent; private int bitsRead; //elements private final PCE pce; private final Element[] elements; //SCE, LFE and CPE private final CCE[] cces; private final DSE[] dses; private final FIL[] fils; private int curElem, curCCE, curDSE, curFIL; private float[][] data; public SyntacticElements(DecoderConfig config) { this.config = config; pce = new PCE(); elements = new Element[4*MAX_ELEMENTS]; cces = new CCE[MAX_ELEMENTS]; dses = new DSE[MAX_ELEMENTS]; fils = new FIL[MAX_ELEMENTS]; startNewFrame(); } public final void startNewFrame() { curElem = 0; curCCE = 0; curDSE = 0; curFIL = 0; sbrPresent = false; psPresent = false; bitsRead = 0; } public void decode(BitStream in) throws AACException { final int start = in.getPosition(); //should be 0 int type; Element prev = null; boolean content = true; if(!config.getProfile().isErrorResilientProfile()) { while(content&&(type = in.readBits(3))!=ELEMENT_END) { switch(type) { case ELEMENT_SCE: case ELEMENT_LFE: LOGGER.finest("SCE"); prev = decodeSCE_LFE(in); break; case ELEMENT_CPE: LOGGER.finest("CPE"); prev = decodeCPE(in); break; case ELEMENT_CCE: LOGGER.finest("CCE"); decodeCCE(in); prev = null; break; case ELEMENT_DSE: LOGGER.finest("DSE"); decodeDSE(in); prev = null; break; case ELEMENT_PCE: LOGGER.finest("PCE"); decodePCE(in); prev = null; break; case ELEMENT_FIL: LOGGER.finest("FIL"); decodeFIL(in, prev); prev = null; break; } } LOGGER.finest("END"); content = false; prev = null; } else { //error resilient raw data block switch(config.getChannelConfiguration()) { case CHANNEL_CONFIG_MONO: decodeSCE_LFE(in); break; case CHANNEL_CONFIG_STEREO: decodeCPE(in); break; case CHANNEL_CONFIG_STEREO_PLUS_CENTER: decodeSCE_LFE(in); decodeCPE(in); break; case CHANNEL_CONFIG_STEREO_PLUS_CENTER_PLUS_REAR_MONO: decodeSCE_LFE(in); decodeCPE(in); decodeSCE_LFE(in); break; case CHANNEL_CONFIG_FIVE: decodeSCE_LFE(in); decodeCPE(in); decodeCPE(in); break; case CHANNEL_CONFIG_FIVE_PLUS_ONE: decodeSCE_LFE(in); decodeCPE(in); decodeCPE(in); decodeSCE_LFE(in); break; case CHANNEL_CONFIG_SEVEN_PLUS_ONE: decodeSCE_LFE(in); decodeCPE(in); decodeCPE(in); decodeCPE(in); decodeSCE_LFE(in); break; default: throw new AACException("unsupported channel configuration for error resilience: "+config.getChannelConfiguration()); } } in.byteAlign(); bitsRead = in.getPosition()-start; } private Element decodeSCE_LFE(BitStream in) throws AACException { if(elements[curElem]==null) elements[curElem] = new SCE_LFE(config.getFrameLength()); ((SCE_LFE) elements[curElem]).decode(in, config); curElem++; return elements[curElem-1]; } private Element decodeCPE(BitStream in) throws AACException { if(elements[curElem]==null) elements[curElem] = new CPE(config.getFrameLength()); ((CPE) elements[curElem]).decode(in, config); curElem++; return elements[curElem-1]; } private void decodeCCE(BitStream in) throws AACException { if(curCCE==MAX_ELEMENTS) throw new AACException("too much CCE elements"); if(cces[curCCE]==null) cces[curCCE] = new CCE(config.getFrameLength()); cces[curCCE].decode(in, config); curCCE++; } private void decodeDSE(BitStream in) throws AACException { if(curDSE==MAX_ELEMENTS) throw new AACException("too much CCE elements"); if(dses[curDSE]==null) dses[curDSE] = new DSE(); dses[curDSE].decode(in); curDSE++; } private void decodePCE(BitStream in) throws AACException { pce.decode(in); config.setProfile(pce.getProfile()); config.setSampleFrequency(pce.getSampleFrequency()); config.setChannelConfiguration(ChannelConfiguration.forInt(pce.getChannelCount())); } private void decodeFIL(BitStream in, Element prev) throws AACException { if(curFIL==MAX_ELEMENTS) throw new AACException("too much FIL elements"); if(fils[curFIL]==null) fils[curFIL] = new FIL(config.isSBRDownSampled()); fils[curFIL].decode(in, prev, config.getSampleFrequency()); curFIL++; if(prev!=null&&prev.isSBRPresent()) { sbrPresent = true; if(!psPresent&&prev.getSBR().isPSUsed()) psPresent = true; } } public void process(FilterBank filterBank) throws AACException { final Profile profile = config.getProfile(); final SampleFrequency sf = config.getSampleFrequency(); //final ChannelConfiguration channels = config.getChannelConfiguration(); int chs = config.getChannelConfiguration().getChannelCount(); if(chs==1&&psPresent) chs++; final int mult = sbrPresent ? 2 : 1; //only reallocate if needed if(data==null||chs!=data.length||(mult*config.getFrameLength())!=data[0].length) data = new float[chs][mult*config.getFrameLength()]; int channel = 0; Element e; SCE_LFE scelfe; CPE cpe; for(int i = 0; i<elements.length&&channel<chs; i++) { e = elements[i]; if(e==null) continue; if(e instanceof SCE_LFE) { scelfe = (SCE_LFE) e; channel += processSingle(scelfe, filterBank, channel, profile, sf); } else if(e instanceof CPE) { cpe = (CPE) e; processPair(cpe, filterBank, channel, profile, sf); channel += 2; } else if(e instanceof CCE) { //applies invquant and save the result in the CCE ((CCE) e).process(); channel++; } } } private int processSingle(SCE_LFE scelfe, FilterBank filterBank, int channel, Profile profile, SampleFrequency sf) throws AACException { final ICStream ics = scelfe.getICStream(); final ICSInfo info = ics.getInfo(); final LTPrediction ltp = info.getLTPrediction1(); final int elementID = scelfe.getElementInstanceTag(); //inverse quantization final float[] iqData = ics.getInvQuantData(); //prediction if(profile.equals(Profile.AAC_MAIN)&&info.isICPredictionPresent()) info.getICPrediction().process(ics, iqData, sf); if(LTPrediction.isLTPProfile(profile)&&info.isLTPrediction1Present()) ltp.process(ics, iqData, filterBank, sf); //dependent coupling processDependentCoupling(false, elementID, CCE.BEFORE_TNS, iqData, null); //TNS if(ics.isTNSDataPresent()) ics.getTNS().process(ics, iqData, sf, false); //dependent coupling processDependentCoupling(false, elementID, CCE.AFTER_TNS, iqData, null); //filterbank filterBank.process(info.getWindowSequence(), info.getWindowShape(ICSInfo.CURRENT), info.getWindowShape(ICSInfo.PREVIOUS), iqData, data[channel], channel); if(LTPrediction.isLTPProfile(profile)) ltp.updateState(data[channel], filterBank.getOverlap(channel), profile); //dependent coupling processIndependentCoupling(false, elementID, data[channel], null); //gain control if(ics.isGainControlPresent()) ics.getGainControl().process(iqData, info.getWindowShape(ICSInfo.CURRENT), info.getWindowShape(ICSInfo.PREVIOUS), info.getWindowSequence()); //SBR int chs = 1; if(sbrPresent) { if(data[channel].length==config.getFrameLength()) LOGGER.log(Level.WARNING, "SBR data present, but buffer has normal size!"); final SBR sbr = scelfe.getSBR(); if(sbr.isPSUsed()) { chs = 2; scelfe.getSBR().process(data[channel], data[channel+1], false); } else scelfe.getSBR().process(data[channel], null, false); } return chs; } private void processPair(CPE cpe, FilterBank filterBank, int channel, Profile profile, SampleFrequency sf) throws AACException { final ICStream ics1 = cpe.getLeftChannel(); final ICStream ics2 = cpe.getRightChannel(); final ICSInfo info1 = ics1.getInfo(); final ICSInfo info2 = ics2.getInfo(); final LTPrediction ltp1 = info1.getLTPrediction1(); final LTPrediction ltp2 = cpe.isCommonWindow() ? info1.getLTPrediction2() : info2.getLTPrediction1(); final int elementID = cpe.getElementInstanceTag(); //inverse quantization final float[] iqData1 = ics1.getInvQuantData(); final float[] iqData2 = ics2.getInvQuantData(); //MS if(cpe.isCommonWindow()&&cpe.isMSMaskPresent()) MS.process(cpe, iqData1, iqData2); //main prediction if(profile.equals(Profile.AAC_MAIN)) { if(info1.isICPredictionPresent()) info1.getICPrediction().process(ics1, iqData1, sf); if(info2.isICPredictionPresent()) info2.getICPrediction().process(ics2, iqData2, sf); } //IS IS.process(cpe, iqData1, iqData2); //LTP if(LTPrediction.isLTPProfile(profile)) { if(info1.isLTPrediction1Present()) ltp1.process(ics1, iqData1, filterBank, sf); if(cpe.isCommonWindow()&&info1.isLTPrediction2Present()) ltp2.process(ics2, iqData2, filterBank, sf); else if(info2.isLTPrediction1Present()) ltp2.process(ics2, iqData2, filterBank, sf); } //dependent coupling processDependentCoupling(true, elementID, CCE.BEFORE_TNS, iqData1, iqData2); //TNS if(ics1.isTNSDataPresent()) ics1.getTNS().process(ics1, iqData1, sf, false); if(ics2.isTNSDataPresent()) ics2.getTNS().process(ics2, iqData2, sf, false); //dependent coupling processDependentCoupling(true, elementID, CCE.AFTER_TNS, iqData1, iqData2); //filterbank filterBank.process(info1.getWindowSequence(), info1.getWindowShape(ICSInfo.CURRENT), info1.getWindowShape(ICSInfo.PREVIOUS), iqData1, data[channel], channel); filterBank.process(info2.getWindowSequence(), info2.getWindowShape(ICSInfo.CURRENT), info2.getWindowShape(ICSInfo.PREVIOUS), iqData2, data[channel+1], channel+1); if(LTPrediction.isLTPProfile(profile)) { ltp1.updateState(data[channel], filterBank.getOverlap(channel), profile); ltp2.updateState(data[channel+1], filterBank.getOverlap(channel+1), profile); } //independent coupling processIndependentCoupling(true, elementID, data[channel], data[channel+1]); //gain control if(ics1.isGainControlPresent()) ics1.getGainControl().process(iqData1, info1.getWindowShape(ICSInfo.CURRENT), info1.getWindowShape(ICSInfo.PREVIOUS), info1.getWindowSequence()); if(ics2.isGainControlPresent()) ics2.getGainControl().process(iqData2, info2.getWindowShape(ICSInfo.CURRENT), info2.getWindowShape(ICSInfo.PREVIOUS), info2.getWindowSequence()); //SBR if(sbrPresent) { //if(data[channel].length==config.getFrameLength()) LOGGER.log(Level.WARNING, "SBR data present, but buffer has normal size!"); cpe.getSBR().process(data[channel], data[channel+1], false); } } private void processIndependentCoupling(boolean channelPair, int elementID, float[] data1, float[] data2) { int index, c, chSelect; CCE cce; for(int i = 0; i<cces.length; i++) { cce = cces[i]; index = 0; if(cce!=null&&cce.getCouplingPoint()==CCE.AFTER_IMDCT) { for(c = 0; c<=cce.getCoupledCount(); c++) { chSelect = cce.getCHSelect(c); if(cce.isChannelPair(c)==channelPair&&cce.getIDSelect(c)==elementID) { if(chSelect!=1) { cce.applyIndependentCoupling(index, data1); if(chSelect!=0) index++; } if(chSelect!=2) { cce.applyIndependentCoupling(index, data2); index++; } } else index += 1+((chSelect==3) ? 1 : 0); } } } } private void processDependentCoupling(boolean channelPair, int elementID, int couplingPoint, float[] data1, float[] data2) { int index, c, chSelect; CCE cce; for(int i = 0; i<cces.length; i++) { cce = cces[i]; index = 0; if(cce!=null&&cce.getCouplingPoint()==couplingPoint) { for(c = 0; c<=cce.getCoupledCount(); c++) { chSelect = cce.getCHSelect(c); if(cce.isChannelPair(c)==channelPair&&cce.getIDSelect(c)==elementID) { if(chSelect!=1) { cce.applyDependentCoupling(index, data1); if(chSelect!=0) index++; } if(chSelect!=2) { cce.applyDependentCoupling(index, data2); index++; } } else index += 1+((chSelect==3) ? 1 : 0); } } } } public void sendToOutput(SampleBuffer buffer) { final boolean be = buffer.isBigEndian(); final int chs = data.length; final int mult = (sbrPresent) ? 2 : 1; final int length = mult*config.getFrameLength(); final int freq = mult*config.getSampleFrequency().getFrequency(); byte[] b = buffer.getData(); if(b.length!=chs*length*2) b = new byte[chs*length*2]; float[] cur; int i, j, off; short s; for(i = 0; i<chs; i++) { cur = data[i]; for(j = 0; j<length; j++) { s = (short) Math.max(Math.min(Math.round(cur[j]), Short.MAX_VALUE), Short.MIN_VALUE); off = (j*chs+i)*2; if(be) { b[off] = (byte) ((s>>8)&BYTE_MASK); b[off+1] = (byte) (s&BYTE_MASK); } else { b[off+1] = (byte) ((s>>8)&BYTE_MASK); b[off] = (byte) (s&BYTE_MASK); } } } buffer.setData(b, freq, chs, 16, bitsRead); } }