public void sendToOutput(SampleBuffer buffer) {
    final boolean be = buffer.isBigEndian();

    final int chs = data.length;
    final int mult = (sbrPresent && config.isSBREnabled()) ? 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);
  }
  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 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());
    curFIL++;

    if (prev != null && prev.isSBRPresent()) {
      sbrPresent = true;
      if (!psPresent && prev.getSBR().isPSUsed()) psPresent = true;
    }
  }
  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 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 && config.isSBREnabled()) {
      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]);
    }
  }
  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 && config.isSBREnabled()) {
      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]);
      } else scelfe.getSBR().process(data[channel], null);
    }
    return chs;
  }
 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 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 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];
 }