Package net.sourceforge.jaad.aac.sbr2

Source Code of net.sourceforge.jaad.aac.sbr2.SBR

/*
*  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.sbr2;

import java.util.logging.Level;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.SampleFrequency;
import net.sourceforge.jaad.aac.ps2.PS;
import net.sourceforge.jaad.aac.syntax.BitStream;
import net.sourceforge.jaad.aac.syntax.Constants;

public class SBR implements SBRConstants {

  //arguments
  private boolean stereo;
  private int sampleFrequency;
  private boolean downSampled;
  //data
  private final SBRHeader header;
  private final ChannelData[] cd;
  private final FrequencyTables tables;
  private boolean coupling;
  //processing buffers
  private final float[][][][] W; //analysis QMF output
  private final float[][][] Xlow, Xhigh;
  private final float[][][][] Y;
  private final float[][][][] X;
  //filterbanks
  private final AnalysisFilterbank qmfA;
  private final SynthesisFilterbank qmfS;
  //PS extension
  private PS ps;
  private boolean psUsed;

  public SBR(SampleFrequency sf, boolean downSampled) {
    sampleFrequency = sf.getFrequency()*2;
    this.downSampled = downSampled;

    header = new SBRHeader();
    cd = new ChannelData[2];
    cd[0] = new ChannelData();
    cd[1] = new ChannelData();
    tables = new FrequencyTables();

    W = new float[2][TIME_SLOTS_RATE][TIME_SLOTS_RATE][2]; //for both channels
    Xlow = new float[32][40][2];
    Xhigh = new float[64][40][2];
    Y = new float[2][38+MAX_LTEMP][64][2]; //for both channels
    X = new float[2][64][38][2]; //for both channels

    qmfA = new AnalysisFilterbank();
    qmfS = new SynthesisFilterbank();

    psUsed = false;
  }

  /*========================= decoding =========================*/
  public void decode(BitStream in, int count, boolean stereo, boolean crc) throws AACException {
    this.stereo = stereo;
    final int pos = in.getPosition();

    if(crc) {
      Constants.LOGGER.info("SBR CRC bits present");
      in.skipBits(10); //TODO: implement crc check
    }

    if(in.readBool()) {
      header.decode(in);
      if(header.isReset()) tables.calculate(header, sampleFrequency);
    }

    //if at least one header was present yet: decode, else skip
    if(header.isDecoded()) {
      decodeData(in, stereo);

      //check for remaining bits (byte-align) and skip them
      final int len = in.getPosition()-pos;
      final int bitsLeft = count-len;
      if(bitsLeft>=8) Constants.LOGGER.log(Level.WARNING, "SBR: bits left: {0}", bitsLeft);
      else if(bitsLeft<0) throw new AACException("SBR data overread: "+bitsLeft);
      in.skipBits(bitsLeft);
    }
    else {
      final int left = count-pos+in.getPosition();
      in.skipBits(left);
      Constants.LOGGER.log(Level.INFO, "SBR frame without header, skipped {0} bits", left);
    }
  }

  private void decodeData(BitStream in, boolean stereo) throws AACException {
    if(stereo) decodeChannelPairElement(in);
    else decodeSingleChannelElement(in);

    //extended data
    if(in.readBool()) {
      int count = in.readBits(4);
      if(count==15) count += in.readBits(8);
      int bitsLeft = 8*count;

      int extensionID;
      while(bitsLeft>7) {
        bitsLeft -= 2;
        extensionID = in.readBits(2);
        bitsLeft -= decodeExtension(in, extensionID);
      }
      if(bitsLeft>0) in.skipBits(bitsLeft);
    }
  }

  private void decodeSingleChannelElement(BitStream in) throws AACException {
    if(in.readBool()) in.skipBits(4); //reserved

    cd[0].decodeGrid(in, header, tables);
    cd[0].decodeDTDF(in);
    cd[0].decodeInvf(in, header, tables);
    cd[0].decodeEnvelope(in, header, tables, false, false);
    cd[0].decodeNoise(in, header, tables, false, false);
    cd[0].decodeSinusoidal(in, header, tables);

    dequantSingle(0);
  }

  private void decodeChannelPairElement(BitStream in) throws AACException {
    if(in.readBool()) in.skipBits(8); //reserved
    if(coupling = in.readBool()) {
      cd[0].decodeGrid(in, header, tables);
      cd[1].copyGrid(cd[0]);
      cd[0].decodeDTDF(in);
      cd[1].decodeDTDF(in);
      cd[0].decodeInvf(in, header, tables);
      cd[1].copyInvf(cd[0]);
      cd[0].decodeEnvelope(in, header, tables, false, coupling);
      cd[0].decodeNoise(in, header, tables, false, coupling);
      cd[1].decodeEnvelope(in, header, tables, true, coupling);
      cd[1].decodeNoise(in, header, tables, true, coupling);

      dequantCoupled();
    }
    else {
      cd[0].decodeGrid(in, header, tables);
      cd[1].decodeGrid(in, header, tables);
      cd[0].decodeDTDF(in);
      cd[1].decodeDTDF(in);
      cd[0].decodeInvf(in, header, tables);
      cd[1].decodeInvf(in, header, tables);
      cd[0].decodeEnvelope(in, header, tables, false, coupling);
      cd[1].decodeEnvelope(in, header, tables, true, coupling);
      cd[0].decodeNoise(in, header, tables, false, coupling);
      cd[1].decodeNoise(in, header, tables, true, coupling);

      dequantSingle(0);
      dequantSingle(1);
    }

    cd[0].decodeSinusoidal(in, header, tables);
    cd[1].decodeSinusoidal(in, header, tables);
  }

  private int decodeExtension(BitStream in, int extensionID) throws AACException {
    final int start = in.getPosition();

    switch(extensionID) {
      case EXTENSION_ID_PS:
        if(ps==null) ps = new PS();
        ps.decode(in);
        if(!psUsed&&ps.hasHeader()) psUsed = true;
        break;
      default:
        in.skipBits(6); //extension data
        break;
    }
    return in.getPosition()-start;
  }

  /*============ dequantization/stereo decoding (4.6.18.3.5) =============*/
  private void dequantSingle(int ch) {
    //envelopes
    final float a = cd[ch].getAmpRes() ? 1f : 0.5f;
    final float[][] e = cd[ch].getEnvelopeScalefactors();
    final int[] freqRes = cd[ch].getFrequencyResolutions();
    final int[] n = tables.getN();

    for(int l = 0; l<cd[ch].getEnvCount(); l++) {
      for(int k = 0; k<n[freqRes[l]]; k++) {
        e[l][k] = (float) Math.pow(2.0f, e[l][k]*a+6.0f);
      }
    }

    //noise
    final int nq = tables.getNq();
    final int lq = cd[ch].getNoiseCount();
    final float[][] q = cd[ch].getNoiseFloorData();

    for(int l = 0; l<lq; l++) {
      for(int k = 0; k<nq; k++) {
        q[l][k] = (float) Math.pow(2.0f, NOISE_FLOOR_OFFSET-q[l][k]);
      }
    }
  }

  //dequantization of coupled channel pair
  private void dequantCoupled() {
    //envelopes
    final float a = cd[0].getAmpRes() ? 1f : 0.5f;
    final int panOffset = PAN_OFFSETS[cd[0].getAmpRes() ? 1 : 0];
    final float[][] e0 = cd[0].getEnvelopeScalefactors();
    final float[][] e1 = cd[1].getEnvelopeScalefactors();
    final int[] r = cd[0].getFrequencyResolutions();
    final int le = cd[0].getEnvCount();
    final int[] n = tables.getN();

    float f1, f2, f3;
    for(int l = 0; l<le; l++) {
      for(int k = 0; k<n[r[l]]; k++) {
        f1 = (float) Math.pow(2.0f, (e0[l][k]*a)+7.0f);
        f2 = (float) Math.pow(2.0f, (panOffset-e1[l][k])*a);
        f3 = f1/(1.0f+f2);
        e0[l][k] = f3;
        e1[l][k] = f3*f2;
      }
    }

    //noise
    final float[][] q0 = cd[0].getNoiseFloorData();
    final float[][] q1 = cd[1].getNoiseFloorData();
    final int lq = cd[0].getNoiseCount();
    final int nq = tables.getNq();

    for(int l = 0; l<lq; l++) {
      for(int k = 0; k<nq; k++) {
        f1 = (float) Math.pow(2.0f, NOISE_FLOOR_OFFSET-q0[l][k]+1.0f);
        f2 = (float) Math.pow(2.0f, PAN_OFFSETS[1]-q1[l][k]);
        f3 = f1/(1.0f+f2);
        q0[l][k] = f3;
        q0[l][k] = f3*f2;
      }
    }
  }

  /*========================= processing =========================*/
  public boolean isPSUsed() {
    return psUsed;
  }

  //left/right: 1024 time samples
  public void process(float[] left, float[] right, boolean downSampled) throws AACException {
    processChannel(0, left);
    if(stereo) processChannel(1, right);
    else if(psUsed) {
      int k;
      for(int l = TIME_SLOTS_RATE; l<TIME_SLOTS_RATE+6; l++) {
        for(k = 0; k<5; k++) {
          X[0][k][l][0] = Xlow[k][l+T_HF_ADJ][0];
          X[0][k][l][1] = Xlow[k][l+T_HF_ADJ][1];
        }
      }
      ps.process(X[0], X[1]);
    }

    qmfS.process(X[0], left, 0);
    if(stereo||psUsed) qmfS.process(X[1], right, 0);
  }

  private void processChannel(int ch, float[] data) throws AACException {
    //1. old W -> Xlow (4.6.18.5)
    final int kxPrev = tables.getKx(true);
    int l, k;
    for(l = 0; l<T_HF_GEN; l++) {
      for(k = 0; k<kxPrev; k++) {
        Xlow[k][l][0] = W[ch][k][l+TIME_SLOTS_RATE-T_HF_GEN][0];
        Xlow[k][l][1] = W[ch][k][l+TIME_SLOTS_RATE-T_HF_GEN][1];
      }
      for(k = kxPrev; k<32; k++) {
        Xlow[k][l][0] = 0;
        Xlow[k][l][1] = 0;
      }
    }

    //2. analysis QMF (data -> W)
    qmfA.process(data, W[ch], 0);

    //3. new W -> Xlow (4.6.18.5)
    final int kx = tables.getKx(false);
    for(l = T_HF_GEN; l<TIME_SLOTS_RATE+T_HF_GEN; l++) {
      for(k = 0; k<kx; k++) {
        Xlow[k][l][0] = W[ch][l-T_HF_GEN][k][0];
        Xlow[k][l][1] = W[ch][l-T_HF_GEN][k][1];
      }
      for(k = kx; k<32; k++) {
        Xlow[k][l][0] = 0;
        Xlow[k][l][1] = 0;
      }
    }

    //4. HF generation (Xlow -> Xhigh)
    HFGenerator.process(tables, cd[ch], Xlow, Xhigh);

    //5. old Y -> X
    final int lTemp = cd[ch].getLTemp();
    final int mPrev = tables.getM(true);
    final int m = tables.getM(false);
    for(l = 0; l<lTemp; l++) {
      for(k = 0; k<kxPrev; k++) {
        X[ch][k][l][0] = Xlow[k][l+T_HF_ADJ][0];
        X[ch][k][l][1] = Xlow[k][l+T_HF_ADJ][1];
      }
      for(k = kxPrev; k<kxPrev+mPrev; k++) {
        X[ch][k][l][0] = Y[ch][l+T_HF_ADJ+TIME_SLOTS_RATE][k][0];
        X[ch][k][l][1] = Y[ch][l+T_HF_ADJ+TIME_SLOTS_RATE][k][1];
      }
      for(k = kxPrev+mPrev; k<64; k++) {
        X[ch][k][l][0] = 0;
        X[ch][k][l][1] = 0;
      }
    }

    //6. HF adjustment (Xhigh -> Y)
    HFAdjuster.process(header, tables, cd[ch], Xhigh, Y[ch]);

    //7. new Y -> X
    for(l = lTemp; l<TIME_SLOTS_RATE; l++) {
      //System.out.println("lTemp: "+lTemp);
      for(k = 0; k<kx; k++) {
        X[ch][k][l][0] = Xlow[k][l+T_HF_ADJ][0];
        X[ch][k][l][1] = Xlow[k][l+T_HF_ADJ][1];
      }
      for(k = kx; k<kx+m; k++) {
        X[ch][k][l][0] = Y[ch][l+T_HF_ADJ][k][0];
        X[ch][k][l][1] = Y[ch][l+T_HF_ADJ][k][1];
      }
      for(k = kx+m; k<64; k++) {
        X[ch][k][l][0] = 0;
        X[ch][k][l][1] = 0;
      }
    }

    //save data for next frame
    cd[ch].savePreviousData();
  }
}
TOP

Related Classes of net.sourceforge.jaad.aac.sbr2.SBR

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.