//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++;
                }
            }
        }
        // --- ========== decoding ========== ---
        public void decode(BitStream inStream, DecoderConfig conf, bool commonWindow)
        {
            var sf = conf.getSampleFrequency();

            if (sf.getIndex() == -1)
            {
                throw new AACException("invalid sample frequency");
            }

            inStream.skipBit(); //reserved
            windowSequence        = (WindowSequence)inStream.readBits(2);
            windowShape[PREVIOUS] = windowShape[CURRENT];
            windowShape[CURRENT]  = inStream.readBit();

            windowGroupCount     = 1;
            windowGroupLength[0] = 1;
            if (windowSequence == WindowSequence.EIGHT_SHORT_SEQUENCE)
            {
                maxSFB = inStream.readBits(4);
                int i;
                for (i = 0; i < 7; i++)
                {
                    if (inStream.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                = inStream.readBits(6);
                windowCount           = 1;
                swbOffsets            = SWB_OFFSET_LONG_WINDOW[sf.getIndex()];
                swbCount              = SWB_LONG_WINDOW_COUNT[sf.getIndex()];
                predictionDataPresent = inStream.readBool();
                if (predictionDataPresent)
                {
                    readPredictionData(inStream, conf.getProfile(), sf, commonWindow);
                }
            }
        }
        /// <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 decode(BitStream inStream, DecoderConfig conf)
        {
            var profile = conf.getProfile();
            var sf      = conf.getSampleFrequency();

            if (sf.getIndex() == -1)
            {
                throw new AACException("invalid sample frequency");
            }

            readElementInstanceTag(inStream);

            commonWindow = inStream.readBool();

            var info = icsL.getInfo();

            if (commonWindow)
            {
                info.decode(inStream, conf, commonWindow);
                icsR.getInfo().setData(info);

                msMask = (MSMask)inStream.readBits(2);
                if (msMask == MSMask.TYPE_USED)
                {
                    int maxSFB           = info.getMaxSFB();
                    int windowGroupCount = info.getWindowGroupCount();

                    for (int idx = 0; idx < windowGroupCount * maxSFB; idx++)
                    {
                        msUsed[idx] = inStream.readBool();
                    }
                }
                else if (msMask == MSMask.TYPE_ALL_1)
                {
                    for (int i = 0; i < msUsed.Length; i++)
                    {
                        msUsed[i] = true;
                    }
                }
                else if (msMask == MSMask.TYPE_ALL_0)
                {
                    for (int i = 0; i < msUsed.Length; i++)
                    {
                        msUsed[i] = false;
                    }
                }
                else
                {
                    throw new AACException("reserved MS mask type used");
                }
            }
            else
            {
                msMask = MSMask.TYPE_ALL_0;
                for (int i = 0; i < msUsed.Length; i++)
                {
                    msUsed[i] = false;
                }
            }

            if (profile.isErrorResilientProfile() && (info.isLTPrediction1Present()))
            {
                if (info.ltpData2Present = inStream.readBool())
                {
                    info.getLTPrediction2().decode(inStream, info, profile);
                }
            }

            icsL.decode(inStream, commonWindow, conf);
            icsR.decode(inStream, commonWindow, conf);
        }