Package net.sourceforge.jaad.aac.sbr2

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

/*
*  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.Arrays;
import net.sourceforge.jaad.aac.AACException;
import net.sourceforge.jaad.aac.SampleFrequency;

//stores and calculates frequency tables, TODO: make arrays final with max sizes
class FrequencyTables implements SBRConstants {

  private static final int[] MFT_START_MIN = {7, 7, 10, 11, 12, 16, 16, 17, 24};
  private static final int[] MFT_STOP_MIN = {13, 15, 20, 21, 23, 32, 32, 35, 48};
  private static final int[] MFT_SF_OFFSETS = {5, 5, 4, 4, 4, 3, 2, 1, 0};
  private static final int[][] MFT_START_OFFSETS = {
    {-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7}, //16000
    {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13}, //22050
    {-5, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16}, //24000
    {-6, -4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16}, //32000
    {-4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20}, //44100-64000
    {-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, 24} //>64000
  };
  private static final int[][] MFT_STOP_OFFSETS = {
    {2, 4, 6, 8, 11, 14, 18, 22, 26, 31, 37, 44, 51},
    {2, 4, 6, 8, 11, 14, 18, 22, 26, 31, 36, 42, 49},
    {2, 4, 6, 9, 11, 14, 17, 21, 25, 29, 34, 39, 44},
    {2, 4, 6, 9, 11, 14, 17, 21, 24, 28, 33, 38, 43},
    {2, 4, 6, 9, 11, 14, 17, 20, 24, 28, 32, 36, 41},
    {2, 4, 6, 8, 10, 12, 14, 17, 20, 23, 26, 29, 32},
    {2, 4, 6, 8, 10, 12, 14, 17, 20, 23, 26, 29, 32},
    {2, 3, 5, 7, 9, 11, 13, 16, 18, 21, 23, 26, 29},
    {1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 15, 16}
  };
  private static final int[] MFT_INPUT1 = {12, 10, 8};
  private static final float[] MFT_INPUT2 = {1.0f, 1.3f};
  private static final float[] LIM_BANDS_PER_OCTAVE_POW = {
    1.32715174233856803909f, //2^(0.49/1.2)
    1.18509277094158210129f, //2^(0.49/2)
    1.11987160404675912501f //2^(0.49/3)
  };
  private static final float GOAL_SB_FACTOR = 2.048E6f;
  int k0, k2; //TODO: private
  //master
  private int[] mft;
  private int nMaster;
  //frequency tables
  private final int[][] fTable;
  private final int[] n;
  private int m, mPrev, kx, kxPrev;
  //noise table
  private int[] fNoise;
  int nq; //TODO: private
  //limiter table
  private int[] fLim;
  private int nl;
  //patches
  private int patchCount;
  private final int[] patchSubbands, patchStartSubband;
  private int[] patchBorders;

  FrequencyTables() {
    n = new int[2];
    fTable = new int[2][];
    patchSubbands = new int[MAX_PATCHES];
    patchStartSubband = new int[MAX_PATCHES];
    kx = 0;
    kxPrev = 0;
    m = 0;
    mPrev = 0;
  }

  void calculate(SBRHeader header, int sampleRate) throws AACException {
    calculateMFT(header, sampleRate);
    calculateFrequencyTables(header);
    calculateNoiseTable(header);
    calculatePatches(sampleRate);
    calculateLimiterTable(header);
  }

  private void calculateMFT(SBRHeader header, int sampleRate) throws AACException {
    //lower border k0
    final int sfIndex = SampleFrequency.forFrequency(sampleRate).getIndex();
    final int sfOff = MFT_SF_OFFSETS[sfIndex];
    k0 = MFT_START_MIN[sfIndex]+MFT_START_OFFSETS[sfOff][header.getStartFrequency(false)];
    //higher border k2
    final int stop = header.getStopFrequency(false);
    final int x;
    if(stop==15) x = 3*k0;
    else if(stop==14) x = 2*k0;
    else x = MFT_STOP_MIN[sfIndex]+MFT_STOP_OFFSETS[sfOff][header.getStopFrequency(false)-1];
    k2 = Math.min(MAX_BANDS, x);

    if(k0>=k2) throw new AACException("SBR: MFT borders out of range: lower="+k0+", higher="+k2);
    //check requirement (4.6.18.3.6):
    final int max;
    if(sampleRate==44100) max = 35;
    else if(sampleRate>=48000) max = 32;
    else max = 48;
    if((k2-k0)>max) throw new AACException("SBR: too many subbands: "+(k2-k0)+", maximum number for samplerate "+sampleRate+": "+max);

    //MFT calculation
    if(header.getFrequencyScale(false)==0) calculateMFT1(header, k0, k2);
    else calculateMFT2(header, k0, k2);

    //check requirement (4.6.18.3.6):
    if(header.getXOverBand(false)>=nMaster) throw new AACException("SBR: illegal length of master frequency table: "+nMaster+", xOverBand: "+header.getXOverBand(false));
  }

  //MFT calculation if frequencyScale==0
  private void calculateMFT1(SBRHeader header, int k0, int k2) throws AACException {
    final int dk;
    if(header.isAlterScale(false)) {
      dk = 2;
      nMaster = 2*Math.round((float) (k2-k0)/4.0f);
    }
    else {
      dk = 1;
      nMaster = 2*(int) ((float) (k2-k0)/2.0f);
    }
    //check requirement (4.6.18.6.3):
    if(nMaster<=0) throw new AACException("SBR: illegal number of bands for master frequency table: "+nMaster);

    final int k2Achieved = k0+nMaster*dk;
    int k2Diff = k2-k2Achieved;

    final int[] vDk = new int[nMaster];
    Arrays.fill(vDk, dk);

    if(k2Diff!=0) {
      final int incr = (k2Diff>0) ? -1 : 1;
      int k = (k2Diff>0) ? nMaster-1 : 0;
      while(k2Diff!=0) {
        vDk[k] -= incr;
        k += incr;
        k2Diff += incr;
      }
    }

    mft = new int[nMaster+1];
    mft[0] = k0;
    for(int i = 1; i<=nMaster; i++) {
      mft[i] = mft[i-1]+vDk[i-1];
    }
  }

  //MFT calculation if frequencyScale>0
  private void calculateMFT2(SBRHeader header, int k0, int k2) throws AACException {
    final int bands = MFT_INPUT1[header.getFrequencyScale(false)-1];
    final float warp = MFT_INPUT2[header.isAlterScale(false) ? 1 : 0];

    float div1 = (float) k2/(float) k0;
    final boolean twoRegions;
    final int k1;
    if(div1>2.2449) {
      twoRegions = true;
      k1 = 2*k0;
    }
    else {
      twoRegions = false;
      k1 = k2;
    }

    final float div2 = (float) k1/(float) k0;
    float log = (float) Math.log(div2)/(float) (2*LOG2);
    final int bandCount0 = 2*Math.round(bands*log);
    //check requirement (4.6.18.6.3):
    if(bandCount0<=0) throw new AACException("SBR: illegal band count for master frequency table: "+bandCount0);

    final int[] vDk0 = new int[bandCount0];
    float pow1, pow2;
    for(int i = 0; i<bandCount0; i++) {
      pow1 = (float) Math.pow(div2, (float) (i+1)/bandCount0);
      pow2 = (float) Math.pow(div2, (float) i/bandCount0);
      vDk0[i] = Math.round(k0*pow1)-Math.round(k0*pow2);
      //check requirement (4.6.18.6.3):
      if(vDk0[i]<=0) throw new AACException("SBR: illegal value in master frequency table: "+vDk0[i]);
    }
    Arrays.sort(vDk0);

    final int[] vk0 = new int[bandCount0+1];
    vk0[0] = k0;
    for(int i = 1; i<=bandCount0; i++) {
      vk0[i] = vk0[i-1]+vDk0[i-1];
    }

    if(twoRegions) {
      div1 = (float) k2/(float) k1;
      log = (float) Math.log(div1);
      final int bandCount1 = 2*(int) Math.round(bands*log/(2*LOG2*warp));
      final int[] vDk1 = new int[bandCount1];
      int min = -1;
      for(int i = 0; i<bandCount1; i++) {
        pow1 = (float) Math.pow(div1, (float) (i+1)/bandCount1);
        pow2 = (float) Math.pow(div1, (float) i/bandCount1);
        vDk1[i] = Math.round(k1*pow1)-Math.round(k1*pow2);
        if(min<0||vDk1[i]<min) min = vDk1[i];
        //check requirement (4.6.18.6.3):
        else if(vDk1[i]<=0) throw new AACException("SBR: illegal value in master frequency table: "+vDk1[i]);
      }

      if(min<vDk0[vDk0.length-1]) {
        Arrays.sort(vDk1);
        int change = vDk0[vDk0.length-1]-vDk1[0];
        final int x = (int) (vDk1[bandCount1-1]-(float) vDk1[0]/2.0);
        if(change>x) change = x;
        vDk1[0] += change;
        vDk1[bandCount1-1] -= change;
      }

      Arrays.sort(vDk1);
      final int[] vk1 = new int[bandCount1+1];
      vk1[0] = k1;
      for(int i = 1; i<=bandCount1; i++) {
        vk1[i] = vk1[i-1]+vDk1[i-1];
      }

      nMaster = bandCount0+bandCount1;
      mft = new int[nMaster+1];
      System.arraycopy(vk0, 0, mft, 0, bandCount0+1);
      System.arraycopy(vk1, 1, mft, bandCount0+1, bandCount1);
    }
    else {
      nMaster = bandCount0;
      mft = new int[nMaster+1];
      System.arraycopy(vk0, 0, mft, 0, nMaster+1);
    }
  }

  private void calculateFrequencyTables(SBRHeader header) throws AACException {
    final int xover = header.getXOverBand(false);
    n[HIGH] = getNMaster()-xover;
    fTable[HIGH] = new int[n[HIGH]+1];
    System.arraycopy(mft, xover, fTable[HIGH], 0, n[HIGH]+1);

    kxPrev = kx;
    kx = fTable[HIGH][0];
    mPrev = m;
    m = fTable[HIGH][getN(HIGH)]-kx;
    //check requirements (4.6.18.3.6):
    if(kx>32) throw new AACException("SBR: start frequency border out of range: "+kx);
    if((kx+m)>64) throw new AACException("SBR: stop frequency border out of range: "+(kx+m));

    final int half = (int) ((float) n[HIGH]/2.0);
    n[LOW] = half+(n[HIGH]-2*half);
    fTable[LOW] = new int[n[LOW]+1];
    fTable[LOW][0] = fTable[HIGH][0];
    final int div = n[HIGH]&1;
    for(int i = 1; i<=n[LOW]; i++) {
      fTable[LOW][i] = fTable[HIGH][2*i-div];
    }
  }

  private void calculateNoiseTable(SBRHeader header) throws AACException {
    final float log = (float) Math.log((float) k2/(float) kx)/(float) LOG2;
    final int x = Math.round(header.getNoiseBands(false)*log);
    nq = Math.max(1, x);
    //check requirement (4.6.18.6.3):
    if(nq>5) throw new AACException("SBR: too many noise floor scalefactors: "+nq);

    fNoise = new int[nq+1];
    fNoise[0] = fTable[LOW][0];
    int i = 0;
    for(int k = 1; k<=nq; k++) {
      i += (int) ((float) (n[LOW]-i)/(float) (nq+1-k));
      fNoise[k] = fTable[LOW][i];
    }
  }

  private void calculatePatches(int sampleRate) throws AACException {
    //patch construction (flowchart 4.46, p231)
    int msb = k0;
    int usb = kx;
    patchCount = 0;

    int goalSb = Math.round(GOAL_SB_FACTOR/(float) sampleRate); //TODO: replace with table
    int k;
    if(goalSb<kx+m) {
      k = 0;
      for(int i = 0; mft[i]<goalSb; i++) {
        k = i+1;
      }
    }
    else k = nMaster;

    int sb, j, odd;
    do {
      j = k+1;
      do {
        j--;
        sb = mft[j];
        odd = (sb-2+k0)&1;
      }
      while(sb>(k0-1+msb-odd));

      patchSubbands[patchCount] = Math.max(sb-usb, 0);
      patchStartSubband[patchCount] = k0-odd-patchSubbands[patchCount];

      if(patchSubbands[patchCount]>0) {
        usb = sb;
        msb = sb;
        patchCount++;
      }
      else msb = kx;

      if(mft[k]-sb<3) k = nMaster;
    }
    while(sb!=(kx+m));

    if(patchSubbands[patchCount-1]<3&&patchCount>1) patchCount--;

    //check requirement (4.6.18.6.3):
    if(patchCount>5) throw new AACException("SBR: too many patches: "+patchCount);
  }

  private void calculateLimiterTable(SBRHeader header) throws AACException {
    //calculation of fTableLim (figure 4.40, p.213)
    final int bands = header.getLimiterBands();
    if(bands==0) {
      fLim = new int[]{fTable[LOW][0], fTable[LOW][n[LOW]]};
      nl = 1;
      patchBorders = new int[0];
    }
    else {
      final float limBandsPerOctaveWarped = LIM_BANDS_PER_OCTAVE_POW[header.getLimiterBands()-1];

      patchBorders = new int[patchCount+1];
      patchBorders[0] = kx;
      for(int i = 1; i<=patchCount; i++) {
        patchBorders[i] = patchBorders[i-1]+patchSubbands[i-1];
      }

      int[] limTable = new int[n[LOW]+patchCount];
      System.arraycopy(fTable[LOW], 0, limTable, 0, n[LOW]+1);
      if(patchCount>1) System.arraycopy(patchBorders, 1, limTable, n[LOW]+1, patchCount-1);
      Arrays.sort(limTable);

      int in = 1;
      int out = 0;
      int lims = n[LOW]+patchCount-1;
      while(out<lims) {
        if(limTable[in]>=limTable[out]*limBandsPerOctaveWarped) {
          limTable[++out] = limTable[in++];
        }
        else if(limTable[in]==limTable[out]
            ||!inArray(patchBorders, limTable[in])) {
          in++;
          lims--;
        }
        else if(!inArray(patchBorders, limTable[out])) {
          limTable[out] = limTable[in++];
          lims--;
        }
        else {
          limTable[++out] = limTable[in++];
        }
      }

      fLim = new int[lims+1];
      System.arraycopy(limTable, 0, fLim, 0, lims+1);
      nl = lims;
    }
  }

  private boolean inArray(int[] a, int x) {
    boolean found = false;
    for(int i = 0; !found&&i<a.length; i++) {
      if(a[i]==x) found = true;
    }
    return found;
  }

  //lower MFT border: k0
  public int getK0() {
    return k0;
  }

  //higher MFT border: k2
  public int getK2() {
    return k2;
  }

  //the master frequency table: fMaster
  public int[] getMFT() {
    return mft;
  }

  //bands in master frequency table: Nmaster
  public int getNMaster() {
    return nMaster;
  }

  //the frequency tables: fTableHigh, fTableLow
  public int[] getFrequencyTable(int i) {
    return fTable[i];
  }

  //bands in frequency tables: Nhigh, Nlow
  public int getN(int i) {
    return n[i];
  }

  //both ns
  public int[] getN() {
    return n;
  }

  //first subband in fTableHigh: kx
  public int getKx(boolean previous) {
    return previous ? kxPrev : kx;
  }

  //number of SBR subbands: M
  public int getM(boolean previous) {
    return previous ? mPrev : m;
  }

  //the noise floor frequency table: fTableNoise
  public int[] getNoiseTable() {
    return fNoise;
  }

  //bands in noise table: Nq
  public int getNq() {
    return nq;
  }

  public int getPatchCount() {
    return patchCount;
  }

  public int[] getPatchSubbands() {
    return patchSubbands;
  }

  public int[] getPatchStartSubband() {
    return patchStartSubband;
  }

  public int[] getPatchBorders() {
    return patchBorders;
  }

  //the limiter frequency band table: fTableLim
  public int[] getLimiterTable() {
    return fLim;
  }

  //bands in limiter table: Nl
  public int getNl() {
    return nl;
  }
}
TOP

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

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.