android实时获取声音频率?

时间:2013-12-03 01:36:08

标签: java android audio real-time frequency

我一直在尝试使用fft实时获取声音频率(数字),并且我遇到了运行时错误。任何人都可以帮忙吗?

package com.example.recordsound;

import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

import ca.uol.aig.fftpack.RealDoubleFFT;

public class MainActivity extends Activity implements OnClickListener{

int audioSource = MediaRecorder.AudioSource.MIC;    // Audio source is the device MIC
int channelConfig = AudioFormat.CHANNEL_IN_MONO;    // Recording in mono
int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; // Records in 16bit

private DoubleFFT_1D fft;                           // The fft double array
private RealDoubleFFT transformer;
int blockSize = 256;                               // deal with this many samples at a time
int sampleRate = 8000;                             // Sample rate in Hz
public double frequency = 0.0;                      // the frequency given

RecordAudio recordTask;                             // Creates a Record Audio command
TextView tv;                                        // Creates a text view for the frequency
boolean started = false;
Button startStopButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    tv = (TextView)findViewById(R.id.textView1);  
    startStopButton= (Button)findViewById(R.id.button1);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}


private class RecordAudio extends AsyncTask<Void, Double, Void>{
    @Override
    protected Void doInBackground(Void... params){      

        /*Calculates the fft and frequency of the input*/
        //try{
            int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioEncoding);                // Gets the minimum buffer needed
            AudioRecord audioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, audioEncoding, bufferSize);   // The RAW PCM sample recording



            short[] buffer = new short[blockSize];          // Save the raw PCM samples as short bytes

          //  double[] audioDataDoubles = new double[(blockSize*2)]; // Same values as above, as doubles
       //   ----------------------------------------------- 
            double[] re = new double[blockSize];
            double[] im = new double[blockSize];
            double[] magnitude = new double[blockSize];
       //   ----------------------------------------------------
            double[] toTransform = new double[blockSize];

            tv.setText("Hello");
           // fft = new DoubleFFT_1D(blockSize);


            try{
            audioRecord.startRecording();  //Start
            }catch(Throwable t){
                Log.e("AudioRecord", "Recording Failed");
            }

            while(started){
                /* Reads the data from the microphone. it takes in data 
                 * to the size of the window "blockSize". The data is then
                 * given in to audioRecord. The int returned is the number
                 * of bytes that were read*/

                int bufferReadResult = audioRecord.read(buffer, 0, blockSize);

                // Read in the data from the mic to the array
                for(int i = 0; i < blockSize && i < bufferReadResult; i++) {

                    /* dividing the short by 32768.0 gives us the 
                     * result in a range -1.0 to 1.0.
                     * Data for the compextForward is given back 
                     * as two numbers in sequence. Therefore audioDataDoubles
                     * needs to be twice as large*/

                   // audioDataDoubles[2*i] = (double) buffer[i]/32768.0; // signed 16 bit
                    //audioDataDoubles[(2*i)+1] = 0.0;
                    toTransform[i] = (double) buffer[i] / 32768.0; // signed 16 bit

                }

                //audiodataDoubles now holds data to work with
               // fft.complexForward(audioDataDoubles);
                transformer.ft(toTransform);
   //------------------------------------------------------------------------------------------
                // Calculate the Real and imaginary and Magnitude.
                for(int i = 0; i < blockSize; i++){
                    // real is stored in first part of array
                    re[i] = toTransform[i*2];
                    // imaginary is stored in the sequential part
                    im[i] = toTransform[(i*2)+1];
                    // magnitude is calculated by the square root of (imaginary^2 + real^2)
                    magnitude[i] = Math.sqrt((re[i] * re[i]) + (im[i]*im[i]));
                }

                double peak = -1.0;
                // Get the largest magnitude peak
                for(int i = 0; i < blockSize; i++){
                    if(peak < magnitude[i])
                        peak = magnitude[i];
                }
                // calculated the frequency
                frequency = (sampleRate * peak)/blockSize;
//----------------------------------------------------------------------------------------------
                /* calls onProgressUpdate
                 * publishes the frequency
                 */
                publishProgress(frequency);
                try{
                    audioRecord.stop();
                }
                catch(IllegalStateException e){
                    Log.e("Stop failed", e.toString());

                }
            }

    //    } 
        return null;
    }

    protected void onProgressUpdate(Double... frequencies){
        //print the frequency 
        String info = Double.toString(frequencies[0]);
        tv.setText(info);
    }

}

@Override
public void onClick(View v) {
    // TODO Auto-generated method stub
    if(started){
           started = false;
           startStopButton.setText("Start");
           recordTask.cancel(true);
       } else {
           started = true;
           startStopButton.setText("Stop");
           recordTask = new RecordAudio();
           recordTask.execute();
       }

}

}

当我运行该程序时,OnClick崩溃了 我为fft尝试了两个库,但是一次运行一个库以查看库是否正常工作 一旦到达我将块大小分配给FFT对象的行,它就会崩溃 任何人都可以提供帮助

3 个答案:

答案 0 :(得分:12)

试试这个FFT:

public class FFT {

  int n, m;

  // Lookup tables. Only need to recompute when size of FFT changes.
  double[] cos;
  double[] sin;

  public FFT(int n) {
      this.n = n;
      this.m = (int) (Math.log(n) / Math.log(2));

      // Make sure n is a power of 2
      if (n != (1 << m))
          throw new RuntimeException("FFT length must be power of 2");

      // precompute tables
      cos = new double[n / 2];
      sin = new double[n / 2];

      for (int i = 0; i < n / 2; i++) {
          cos[i] = Math.cos(-2 * Math.PI * i / n);
          sin[i] = Math.sin(-2 * Math.PI * i / n);
      }

  }

  public void fft(double[] x, double[] y) {
      int i, j, k, n1, n2, a;
      double c, s, t1, t2;

      // Bit-reverse
      j = 0;
      n2 = n / 2;
      for (i = 1; i < n - 1; i++) {
          n1 = n2;
          while (j >= n1) {
              j = j - n1;
              n1 = n1 / 2;
          }
          j = j + n1;

          if (i < j) {
              t1 = x[i];
              x[i] = x[j];
              x[j] = t1;
              t1 = y[i];
              y[i] = y[j];
              y[j] = t1;
          }
      }

      // FFT
      n1 = 0;
      n2 = 1;

      for (i = 0; i < m; i++) {
          n1 = n2;
          n2 = n2 + n2;
          a = 0;

          for (j = 0; j < n1; j++) {
              c = cos[a];
              s = sin[a];
              a += 1 << (m - i - 1);

              for (k = j; k < n; k = k + n2) {
                  t1 = c * x[k + n1] - s * y[k + n1];
                  t2 = s * x[k + n1] + c * y[k + n1];
                  x[k + n1] = x[k] - t1;
                  y[k + n1] = y[k] - t2;
                  x[k] = x[k] + t1;
                  y[k] = y[k] + t2;
              }
          }
      }
  }
}

它应该解决你的想法。如果您决定重复使用它,请向作者提供适当的信用。

来源/作者:EricLarch

答案 1 :(得分:5)

如果您真的想要执行实时音频分析,那么基于Java的方法就无法做到。我在2013年第四季度为我的公司做了类似的任务,我们决定使用Kiss FFT(也许是最简单的带有BSD许可证的FFT库),使用NDK为Android编译。

本机C / C ++方法比Java对应方法吨快。使用前者,我们几乎可以在每个中高端设备上执行实时音频解码音频功能分析,后者显然是不可能的

强烈建议您将原生方法视为执行此任务的最佳选择。 Kiss FFT是一个非常简单的库(字面意思是Keep It Simple FFT),你在Android上编译和使用它时不会遇到太多麻烦。你不会对表现结果感到失望。

答案 2 :(得分:0)

你解决了这个问题吗?由于ArrayIndexOutOfBoundsException而发生了压缩。

因此,请将您的代码修改为:

                double[] re = new double[blockSize];
                double[] im = new double[blockSize];
                double[] magnitude = new double[blockSize];

                // Calculate the Real and imaginary and Magnitude.
                for(int i = 0; i < blockSize+1; i++){
                    try {
                        // real is stored in first part of array
                        re[i] = toTransform[i * 2];
                        // imaginary is stored in the sequential part
                        im[i] = toTransform[(i * 2) + 1];
                        // magnitude is calculated by the square root of (imaginary^2 + real^2)
                        magnitude[i] = Math.sqrt((re[i] * re[i]) + (im[i] * im[i]));
                    }catch (ArrayIndexOutOfBoundsException e){
                        Log.e("test", "NULL");
                    }
                }

                double peak = -1.0;
                // Get the largest magnitude peak
                for(int i = 0; i < blockSize; i++){
                    if(peak < magnitude[i])
                        peak = magnitude[i];
                }
                // calculated the frequency
                frequency = Double.toString((sampleRate * peak)/blockSize);