//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(), config.isSBREnabled(), config.isSmallFrameUsed());
        //curFIL++;

        //if(prev!=null&&prev.isSBRPresent()) {
        //sbrPresent = true;
        //if(!psPresent&&prev.getSBR().isPSUsed()) psPresent = true;
        //}
        //}

        public void process(FilterBank filterBank)
        {
            Profile         profile = config.getProfile();
            SampleFrequency sf      = config.getSampleFrequency();
            //ChannelConfiguration channels = config.getChannelConfiguration();

            int chs = (int)config.getChannelConfiguration();

            if (chs == 1 && psPresent)
            {
                chs++;
            }
            int mult = sbrPresent ? 2 : 1;

            //only reallocate if needed
            if (data == null || chs != data.Length || (mult * config.getFrameLength()) != data[0].Length)
            {
                data = Enumerable.Range(0, chs).Select(x => new float[mult * config.getFrameLength()]).ToArray();
            }

            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 is SCE_LFE)
                {
                    scelfe = (SCE_LFE)e;
                    throw new NotImplementedException();
                    // channel += processSingle(scelfe, filterBank, channel, profile, sf);
                }
                else if (e is CPE)
                {
                    cpe = (CPE)e;
                    processPair(cpe, filterBank, channel, profile, sf);
                    channel += 2;
                }
                else if (e is CCE)
                {
                    //applies invquant and save the result in the CCE
                    throw new NotImplementedException();
                    // ((CCE)e).process();
                    channel++;
                }
            }
        }
        /// <summary>
        /// Initializes the decoder with a MP4 decoder specific info.
        ///
        /// After this the MP4 frames can be passed to the
        /// <code>decodeFrame(byte[], SampleBuffer)</code> method to decode them.
        /// </summary>
        /// <param name="decoderSpecificInfo">decoderSpecificInfo a byte array containing the decoder specific info from an MP4 container</param>
        public Decoder(byte[] decoderSpecificInfo)
        {
            config = DecoderConfig.parseMP4DecoderSpecificInfo(decoderSpecificInfo);
            if (config == null)
            {
                throw new ArgumentException("illegal MP4 decoder specific info");
            }

            if (!canDecode(config.getProfile()))
            {
                throw new AACException("unsupported profile: " + config.getProfile());
            }

            syntacticElements = new SyntacticElements(config);
            filterBank        = new FilterBank(config.isSmallFrameUsed(), (int)config.getChannelConfiguration());

            Logger.LogInfo(string.Format("profile: {0}", config.getProfile()));
            Logger.LogInfo(string.Format("sf: {0}", config.getSampleFrequency().getFrequency()));
            Logger.LogInfo(string.Format("channels: {0}", config.getChannelConfiguration()));
        }
        public void process(ICStream ics, float[] data, FilterBank filterBank, SampleFrequency sf)
        {
            var info = ics.getInfo();

            if (!info.isEightShortFrame())
            {
                int samples = frameLength << 1;
                var inf     = new float[2048];
                var outf    = new float[2048];

                for (int i = 0; i < samples; i++)
                {
                    inf[i] = states[samples + i - lag] * CODEBOOK[coef];
                }

                throw new NotImplementedException();

                //filterBank.processLTP(info.getWindowSequence(), info.getWindowShape(ICSInfo.CURRENT), info.getWindowShape(ICSInfo.PREVIOUS), inf, outf);

                //if (ics.isTNSDataPresent()) ics.getTNS().process(ics, outf, sf, true);

                var swbOffsets   = info.getSWBOffsets();
                int swbOffsetMax = info.getSWBOffsetMax();
                for (int sfb = 0; sfb < lastBand; sfb++)
                {
                    if (longUsed[sfb])
                    {
                        int low  = swbOffsets[sfb];
                        int high = Math.Min(swbOffsets[sfb + 1], swbOffsetMax);

                        for (int bin = low; bin < high; bin++)
                        {
                            data[bin] += outf[bin];
                        }
                    }
                }
            }
        }
        //private int processSingle(SCE_LFE scelfe, FilterBank filterBank, int channel, Profile profile, SampleFrequency sf) throws AACException {
        //ICStream ics = scelfe.getICStream();
        //ICSInfo info = ics.getInfo();
        //LTPrediction ltp = info.getLTPrediction1();
        //int elementID = scelfe.getElementInstanceTag();

        ////inverse quantization
        //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&&config.isSBREnabled()) {
        //if(data[channel].Length==config.getFrameLength()) LOGGER.log(Level.WARNING, "SBR data present, but buffer has normal size!");
        //SBR sbr = scelfe.getSBR();
        //if(sbr.isPSUsed()) {
        //chs = 2;
        //scelfe.getSBR().process(data[channel], data[channel+1], false);
        //}
        //else scelfe.getSBR().process(data[channel], false);
        //}
        //return chs;
        //}

        void processPair(CPE cpe, FilterBank filterBank, int channel, Profile profile, SampleFrequency sf)
        {
            var ics1      = cpe.getLeftChannel();
            var ics2      = cpe.getRightChannel();
            var info1     = ics1.getInfo();
            var info2     = ics2.getInfo();
            var ltp1      = info1.getLTPrediction1();
            var ltp2      = cpe.isCommonWindow() ? info1.getLTPrediction2() : info2.getLTPrediction1();
            int elementID = cpe.getElementInstanceTag();

            // inverse quantization
            var iqData1 = ics1.getInvQuantData();
            var iqData2 = ics2.getInvQuantData();

            // MS
            if (cpe.isCommonWindow() && cpe.isMSMaskPresent())
            {
                MS.process(cpe, iqData1, iqData2);
            }
            // main prediction
            if (profile.type == Profile.ProfileType.AAC_MAIN)
            {
                if (info1.isICPredictionPresent())
                {
                    throw new NotImplementedException();
                    // info1.getICPrediction().process(ics1, iqData1, sf);
                }
                if (info2.isICPredictionPresent())
                {
                    throw new NotImplementedException();
                    // 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 && config.isSBREnabled())
            {
                if (data[channel].Length == config.getFrameLength())
                {
                    Logger.LogServe("SBR data present, but buffer has normal size!");
                }
                cpe.getSBR().process(data[channel], data[channel + 1], false);
            }
        }