Android Open Source - audio-analyzer-for-android S T F T






From Project

Back to project page audio-analyzer-for-android.

License

The source code is released under:

Apache License

If you think the Android project audio-analyzer-for-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.google.corp.productivity.specialprojects.android.samples.fft;
/*from   ww w. j a v  a 2s.  c om*/
import java.util.Arrays;

import android.util.Log;

import com.google.corp.productivity.specialprojects.android.fft.RealDoubleFFT;

// Short Time Fourier Transform
public class STFT {
  // data for frequency Analysis
  private double[] spectrumAmpOutCum;
  private double[] spectrumAmpOutTmp;
  private double[] spectrumAmpOut;
  private double[] spectrumAmpOutDB;
  private double[] spectrumAmpIn;
  private double[] spectrumAmpInTmp;
  private double[] wnd;
  private double wndEnergyFactor = 1;           // used to keep energy invariant under different window
  private int sampleRate;
  private int fftLen;
  private int spectrumAmpPt;
  private double[][] spectrumAmpOutArray;
  private int spectrumAmpOutArrayPt = 0;        // Pointer for spectrumAmpOutArray
  private int nAnalysed = 0;
  private RealDoubleFFT spectrumAmpFFT;
  private boolean boolAWeighting = false;
  private double cumRMS = 0;
  private int    cntRMS = 0;
  private double outRMS = 0;
  
  private double[] dBAFactor;    // multiply to power spectrum to get A-weighting
  
  private double sqr(double x) { return x*x; }
  
  // Generate multiplier for A-weighting
  private void initDBAFactor(int fftlen, double sampleRate) {
    dBAFactor = new double[fftlen/2+1];
    for (int i = 0; i < fftlen/2+1; i++) {
      double f = (double)i/fftlen * sampleRate;
      double r = sqr(12200)*sqr(sqr(f)) / ((f*f+sqr(20.6)) * Math.sqrt((f*f+sqr(107.7)) * (f*f+sqr(737.9))) * (f*f+sqr(12200)));
      dBAFactor[i] = r*r*1.58489319246111;  // 1.58489319246111 = 10^(1/5)
    }
  }
  
  private void initWindowFunction(int fftlen, String wndName) {
    wnd = new double[fftlen];
    if (wndName.equals("Bartlett")) {
      for (int i=0; i<wnd.length; i++) {  // Bartlett
        wnd[i] = Math.asin(Math.sin(Math.PI*i/wnd.length))/Math.PI*2;
      }
    } else if (wndName.equals("Hanning")) {
      for (int i=0; i<wnd.length; i++) {  // Hanning, hw=1
        wnd[i] = 0.5*(1-Math.cos(2*Math.PI*i/(wnd.length-1.))) *2;
      }
    } else if (wndName.equals("Blackman")) {
      for (int i=0; i<wnd.length; i++) {  // Blackman, hw=2
        wnd[i] = 0.42-0.5*Math.cos(2*Math.PI*i/(wnd.length-1))+0.08*Math.cos(4*Math.PI*i/(wnd.length-1));
      }
    } else if (wndName.equals("Blackman Harris")) {
      for (int i=0; i<wnd.length; i++) {  // Blackman_Harris, hw=3
        wnd[i] = (0.35875-0.48829*Math.cos(2*Math.PI*i/(wnd.length-1))+0.14128*Math.cos(4*Math.PI*i/(wnd.length-1))-0.01168*Math.cos(6*Math.PI*i/(wnd.length-1))) *2;
      }
    } else {
      for (int i=0; i<wnd.length; i++) {
        wnd[i] = 1;
      }
    }
    double normalizeFactor = 0;
    for (int i=0; i<wnd.length; i++) {
      normalizeFactor += wnd[i];
    }
    normalizeFactor = wnd.length / normalizeFactor;
    wndEnergyFactor = 0;
    for (int i=0; i<wnd.length; i++) {
      wnd[i] *= normalizeFactor;
      wndEnergyFactor += wnd[i]*wnd[i];
    }
    wndEnergyFactor = wnd.length / wndEnergyFactor;
  }
  
  public void setAWeighting(boolean e_isAWeighting) {
    boolAWeighting = e_isAWeighting;
  }
  
  public boolean getAWeighting() {
    return boolAWeighting;
  }
  
  private void init(int fftlen, int sampleRate, int minFeedSize, String wndName) {
    if (minFeedSize <= 0) {
      throw new IllegalArgumentException("STFT::init(): should minFeedSize >= 1.");
    }
    if (((-fftlen)&fftlen) != fftlen) {
      // error: fftlen should be power of 2
      throw new IllegalArgumentException("STFT::init(): Currently, only power of 2 are supported in fftlen");
    }
    this.sampleRate = sampleRate;
    fftLen = fftlen;
    spectrumAmpOutCum= new double[fftlen/2+1];
    spectrumAmpOutTmp= new double[fftlen/2+1];
    spectrumAmpOut   = new double[fftlen/2+1];
    spectrumAmpOutDB = new double[fftlen/2+1];
    spectrumAmpIn    = new double[fftlen];
    spectrumAmpInTmp = new double[fftlen];
    spectrumAmpFFT   = new RealDoubleFFT(spectrumAmpIn.length);
    spectrumAmpOutArray = new double[(int)Math.ceil((double)minFeedSize / (fftlen/2))][]; // /2 since half overlap
    for (int i = 0; i < spectrumAmpOutArray.length; i++) {
      spectrumAmpOutArray[i] = new double[fftlen/2+1];
    }
    
    initWindowFunction(fftlen, wndName);
    initDBAFactor(fftlen, sampleRate);
    boolAWeighting = false;
  }
  
  public STFT(int fftlen, int sampleRate, int minFeedSize, String wndName) {
    init(fftlen, sampleRate, minFeedSize, wndName);
  }

  public STFT(int fftlen, int sampleRate, String wndName) {
    init(fftlen, sampleRate, 1, wndName);
  }

  public void feedData(short[] ds) {
    feedData(ds, ds.length);
  }
  public void feedData(short[] ds, int dsLen) {
    if (dsLen > ds.length) {
      Log.e("STFT", "dsLen > ds.length !");
      dsLen = ds.length;
    }
    int inLen = spectrumAmpIn.length;
    int outLen = spectrumAmpOut.length;
    int dsPt = 0;           // input data point to be read
    while (dsPt < dsLen) {
      while (spectrumAmpPt < inLen && dsPt < dsLen) {
        double s = ds[dsPt++] / 32768.0;
        spectrumAmpIn[spectrumAmpPt++] = s;
        cumRMS += s*s;
        cntRMS++;
      }
      if (spectrumAmpPt == inLen) {    // enough data for one FFT
        for (int i = 0; i < inLen; i++) {
          spectrumAmpInTmp[i] = spectrumAmpIn[i] * wnd[i];
        }
        spectrumAmpFFT.ft(spectrumAmpInTmp);
        fftToAmp(spectrumAmpOutTmp, spectrumAmpInTmp);
        System.arraycopy(spectrumAmpOutTmp, 0, spectrumAmpOutArray[spectrumAmpOutArrayPt], 0,
                         spectrumAmpOutTmp.length);
        spectrumAmpOutArrayPt = (spectrumAmpOutArrayPt+1) % spectrumAmpOutArray.length;
        for (int i = 0; i < outLen; i++) {
          spectrumAmpOutCum[i] += spectrumAmpOutTmp[i];
        }
        nAnalysed++;
        // half overlap  (set spectrumAmpPt = 0 for no overlap)
        int n2 = spectrumAmpIn.length / 2;
        System.arraycopy(spectrumAmpIn, n2, spectrumAmpIn, 0, n2);
        spectrumAmpPt = n2;
      }
    }
  }

  private void fftToAmp(double[] dataOut, double[] data) {
    // data.length should be even number
    double scaler = 2.0*2.0 / (data.length * data.length);  // *2 since there are positive and negative frequency part
    dataOut[0] = data[0]*data[0] * scaler / 4.0;
    int j = 1;
    for (int i = 1; i < data.length - 1; i += 2, j++) {
      dataOut[j] = (data[i]*data[i] + data[i+1]*data[i+1]) * scaler;
    }
    dataOut[j] = data[data.length-1]*data[data.length-1] * scaler / 4.0;
  }
  
  final public double[] getSpectrumAmp() {
    if (nAnalysed != 0) {    // no new result
      int outLen = spectrumAmpOut.length;
      double[] sAOC = spectrumAmpOutCum;
      for (int j = 0; j < outLen; j++) {
        sAOC[j] /= nAnalysed;
      }
      if (boolAWeighting) {
        for (int j = 0; j < outLen; j++) {
          sAOC[j] *= dBAFactor[j];
        }
      }
      System.arraycopy(sAOC, 0, spectrumAmpOut, 0, outLen);
      Arrays.fill(sAOC, 0.0);
      nAnalysed = 0;
      for (int i = 0; i < outLen; i++) {
        spectrumAmpOutDB[i] = 10.0 * Math.log10(spectrumAmpOut[i]);
      }
    }
    return spectrumAmpOut;
  }
  
  final public double[] getSpectrumAmpDB() {
    getSpectrumAmp();
    return spectrumAmpOutDB;
  }

  public double getRMS() {
    if (cntRMS > 8000/30) {
      outRMS = Math.sqrt(cumRMS / cntRMS * 2.0);  // "* 2.0" normalize to sine wave.
      cumRMS = 0;
      cntRMS = 0;
    }
    return outRMS;
  }
  
  public double getRMSFromFT() {
    getSpectrumAmpDB();
    double s = 0;
    for (int i = 1; i < spectrumAmpOut.length; i++) {
      s += spectrumAmpOut[i];
    }
    return Math.sqrt(s * wndEnergyFactor);
  }
  
  public int nElemSpectrumAmp() {
    return nAnalysed;
  }
  
  public double maxAmpFreq = Double.NaN, maxAmpDB = Double.NaN;
  
  public void calculatePeak() {
    getSpectrumAmpDB();
    // Find and show peak amplitude
    maxAmpDB  = 20 * Math.log10(0.125/32768);
    maxAmpFreq = 0;
    for (int i = 1; i < spectrumAmpOutDB.length; i++) {  // skip the direct current term
      if (spectrumAmpOutDB[i] > maxAmpDB) {
        maxAmpDB  = spectrumAmpOutDB[i];
        maxAmpFreq = i;
      }
    }
    maxAmpFreq = maxAmpFreq * sampleRate / fftLen;
    
    // Slightly better peak finder
    // The peak around spectrumDB should look like quadratic curve after good window function
    // a*x^2 + b*x + c = y
    // a - b + c = x1
    //         c = x2
    // a + b + c = x3
    if (sampleRate / fftLen < maxAmpFreq && maxAmpFreq < sampleRate/2 - sampleRate / fftLen) {
      int id = (int)(Math.round(maxAmpFreq/sampleRate*fftLen));
      double x1 = spectrumAmpOutDB[id-1];
      double x2 = spectrumAmpOutDB[id];
      double x3 = spectrumAmpOutDB[id+1];
      double c = x2;
      double a = (x3+x1)/2 - x2;
      double b = (x3-x1)/2;
      if (a < 0) {
        double xPeak = -b/(2*a);
        if (Math.abs(xPeak) < 1) {
          maxAmpFreq += xPeak * sampleRate / fftLen;
          maxAmpDB = (4*a*c - b*b)/(4*a);
        }
      }
    }
  }

  public void clear() {
    spectrumAmpPt = 0;
    Arrays.fill(spectrumAmpOut, 0.0);
    Arrays.fill(spectrumAmpOutDB, Math.log10(0));
    Arrays.fill(spectrumAmpOutCum, 0.0);
    for (int i = 0; i < spectrumAmpOutArray.length; i++) {
      Arrays.fill(spectrumAmpOutArray[i], 0.0);
    }
  }

}




Java Source Code List

com.google.corp.productivity.specialprojects.android.fft.RealDoubleFFT_Mixed.java
com.google.corp.productivity.specialprojects.android.fft.RealDoubleFFT.java
com.google.corp.productivity.specialprojects.android.samples.fft.AnalyzeActivity.java
com.google.corp.productivity.specialprojects.android.samples.fft.AnalyzeView.java
com.google.corp.productivity.specialprojects.android.samples.fft.ColorMapArray.java
com.google.corp.productivity.specialprojects.android.samples.fft.DoubleSineGen.java
com.google.corp.productivity.specialprojects.android.samples.fft.FramesPerSecondCounter.java
com.google.corp.productivity.specialprojects.android.samples.fft.InfoRecActivity.java
com.google.corp.productivity.specialprojects.android.samples.fft.MyPreferences.java
com.google.corp.productivity.specialprojects.android.samples.fft.RecorderMonitor.java
com.google.corp.productivity.specialprojects.android.samples.fft.SBNumFormat.java
com.google.corp.productivity.specialprojects.android.samples.fft.STFT.java
com.google.corp.productivity.specialprojects.android.samples.fft.SelectorText.java
com.google.corp.productivity.specialprojects.android.samples.fft.WavWriter.java