2013-07-02 55 views
4

它已經問了很多,但我仍然停留約在Android上導入 我需要使用FFT處理我的音頻數據FFT頻率類WAV音頻...開始使用FFT複雜的階級

我已經讀了差不多同樣的問題在這裏How can I get frequency data from PCM using FFT 這裏How to get frequency from fft result? 多的問題,但仍然覺得即使我試圖給出的答案沒有答案......

FFT類我使用: http://www.cs.princeton.edu/introcs/97data/FFT.java

的C omplex類去用它:http://introcs.cs.princeton.edu/java/97data/Complex.java.html

這裏是我的代碼

import java.io.File; 
import java.io.FileInputStream; 
import java.io.FileNotFoundException; 
import java.io.FileOutputStream; 
import java.io.IOException; 

import android.app.Activity; 
import android.app.AlertDialog; 
import android.content.DialogInterface; 
import android.media.AudioFormat; 
import android.media.AudioRecord; 
import android.media.MediaRecorder; 
import android.os.Bundle; 
import android.os.Environment; 
import android.view.View; 
import android.widget.Button; 

public class Latihan extends Activity{ 
     private static final int RECORDER_BPP = 16; 
     private static final String AUDIO_RECORDER_FILE_EXT_WAV = ".wav"; 
     private static final String AUDIO_RECORDER_FOLDER = "AudioRecorder"; 
     private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw"; 
     private static final int RECORDER_SAMPLERATE = 44100; 
     private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_STEREO; 
     private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT; 
     short[] audioData; 

     private AudioRecord recorder = null; 
     private int bufferSize = 0; 
     private Thread recordingThread = null; 
     private boolean isRecording = false; 
     Complex[] fftTempArray; 
     Complex[] fftArray; 
     int[] bufferData; 
     int bytesRecorded; 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.p1); 

     setButtonHandlers(); 
     enableButtons(false); 

     bufferSize = AudioRecord.getMinBufferSize 
       (RECORDER_SAMPLERATE,RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING)*3; 

     audioData = new short [bufferSize]; //short array that pcm data is put into. 

     } 


    private void setButtonHandlers() { 
     ((Button)findViewById(R.id.btStart)).setOnClickListener(btnClick); 
     ((Button)findViewById(R.id.btStop)).setOnClickListener(btnClick); 
     } 


     private void enableButton(int id,boolean isEnable){ 
       ((Button)findViewById(id)).setEnabled(isEnable); 
     } 

     private void enableButtons(boolean isRecording) { 
       enableButton(R.id.btStart,!isRecording); 
       enableButton(R.id.btStop,isRecording); 
     } 

     private String getFilename(){ 
       String filepath = Environment.getExternalStorageDirectory().getPath(); 
       File file = new File(filepath,AUDIO_RECORDER_FOLDER); 

       if(!file.exists()){ 
         file.mkdirs(); 
       } 

       return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + AUDIO_RECORDER_FILE_EXT_WAV); 
     } 


     public void convert(){ 



     } 

     public void calculate(){ 
      Complex[] fftTempArray = new Complex[bufferSize]; 
      for (int i=0; i<bufferSize; i++) 
      { 
       fftTempArray[i] = new Complex(audioData[i], 0); 
      } 
      Complex[] fftArray = FFT.fft(fftTempArray); 

      double[] micBufferData = new double[bufferSize]; 
      final int bytesPerSample = 2; 
      final double amplification = 100.0; 
      for (int index = 0, floatIndex = 0; index < bytesRecorded - bytesPerSample + 1; index += bytesPerSample, floatIndex++) { 
       double sample = 0; 
       for (int b = 0; b < bytesPerSample; b++) { 
        int v = bufferData[index + b]; 
        if (b < bytesPerSample - 1 || bytesPerSample == 1) { 
         v &= 0xFF; 
        } 
        sample += v << (b * 8); 
       } 
       double sample32 = amplification * (sample/32768.0); 
       micBufferData[floatIndex] = sample32; 
      } 


    } 


     private String getTempFilename(){ 
       String filepath = Environment.getExternalStorageDirectory().getPath(); 
       File file = new File(filepath,AUDIO_RECORDER_FOLDER); 

       if(!file.exists()){ 
         file.mkdirs(); 
       } 

       File tempFile = new File(filepath,AUDIO_RECORDER_TEMP_FILE); 

       if(tempFile.exists()) 
         tempFile.delete(); 

       return (file.getAbsolutePath() + "/" + AUDIO_RECORDER_TEMP_FILE); 
     } 

     private void startRecording(){ 
      recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 
               RECORDER_SAMPLERATE, RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize); 

       recorder.startRecording(); 

       isRecording = true; 

       recordingThread = new Thread(new Runnable() { 

         public void run() { 
           writeAudioDataToFile(); 
         } 
       },"AudioRecorder Thread"); 

       recordingThread.start(); 
     } 

     private void writeAudioDataToFile(){ 
       byte data[] = new byte[bufferSize]; 
       String filename = getTempFilename(); 
       FileOutputStream os = null; 

       try { 
         os = new FileOutputStream(filename); 
       } catch (FileNotFoundException e) { 
         // TODO Auto-generated catch block 
         e.printStackTrace(); 
       } 

       int read = 0; 

       if(null != os){ 
         while(isRecording){ 
           read = recorder.read(data, 0, bufferSize); 

           if(AudioRecord.ERROR_INVALID_OPERATION != read){ 
             try { 
               os.write(data); 
             } catch (IOException e) { 
               e.printStackTrace(); 
             } 
           } 
         } 

         try { 
           os.close(); 
         } catch (IOException e) { 
           e.printStackTrace(); 
         } 
       } 
     } 

     private void stopRecording(){ 
       if(null != recorder){ 
         isRecording = false; 

         recorder.stop(); 
         recorder.release(); 

         recorder = null; 
         recordingThread = null; 
       } 

       copyWaveFile(getTempFilename(),getFilename()); 
       // deleteTempFile(); 
     } 

     private void deleteTempFile() { 
       File file = new File(getTempFilename()); 
       file.delete(); 
     } 

     private void copyWaveFile(String inFilename,String outFilename){ 
       FileInputStream in = null; 
       FileOutputStream out = null; 
       long totalAudioLen = 0; 
       long totalDataLen = totalAudioLen + 36; 
       long longSampleRate = RECORDER_SAMPLERATE; 
       int channels = 2; 
       long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8; 

       byte[] data = new byte[bufferSize]; 

       try { 
         in = new FileInputStream(inFilename); 
         out = new FileOutputStream(outFilename); 
         totalAudioLen = in.getChannel().size(); 
         totalDataLen = totalAudioLen + 36; 

         AppLog.logString("File size: " + totalDataLen); 

         WriteWaveFileHeader(out, totalAudioLen, totalDataLen, 
             longSampleRate, channels, byteRate); 

         while(in.read(data) != -1){ 
           out.write(data); 
         } 

         in.close(); 
         out.close(); 
       } catch (FileNotFoundException e) { 
         e.printStackTrace(); 
       } catch (IOException e) { 
         e.printStackTrace(); 
       } 
     } 

     private void WriteWaveFileHeader(
         FileOutputStream out, long totalAudioLen, 
         long totalDataLen, long longSampleRate, int channels, 
         long byteRate) throws IOException { 
      //another code  

     } 

     private View.OnClickListener btnClick = new View.OnClickListener() { 
       public void onClick(View v) { 
        switch(v.getId()){ 
           case R.id.btStart:{ 
             AppLog.logString("Start Recording"); 
             enableButtons(true); 
             startRecording(); 
             break; 
           } 
           case R.id.btStop:{ 
            AppLog.logString("Stop Recording"); 
             enableButtons(false); 
             stopRecording(); 
             calculate(); 
             break; 

           } 
         } 
       } 
     }; 
} 

我承擔audioData數組包含原始音頻數據,但我的代碼捕獲異常,並返回「N不是2的冪」

我的代碼有什麼問題嗎? 如何將它傳遞給FFT.java類並獲得fftResult?

或者還有其他方法可以將時域數據轉換爲更容易的頻率數據嗎?

這是一個幾個月,因爲我卡住這個...我的項目是太比較2音頻* .wav文件, 任何幫助,將不勝感激... :)

+0

對於那些「N不是2的冪」類似的錯誤,這意味着您使用的FFT算法需要一個大小爲2^n的數組,其中'n'是大於0的整數FT算法通過使用Cooley-Tukey FFT變換(其使用2個基線進行操作)對這些尺寸的算法最有效地工作。 一個解決方案是搜索也包含Bluestein變換的FFT算法,該算法可以用於任意長度的數組。一個這樣的FFT可以在這裏找到:https://www.nayuki.io/page/free-small-fft-in-multiple-languages。 –

回答

6

我已經找到了答案... :)

我創建的方法來計算從音頻數組值...

public double[] calculateFFT(byte[] signal) 
    {   
     final int mNumberOfFFTPoints =1024; 
     double mMaxFFTSample; 

     double temp; 
     Complex[] y; 
     Complex[] complexSignal = new Complex[mNumberOfFFTPoints]; 
     double[] absSignal = new double[mNumberOfFFTPoints/2]; 

     for(int i = 0; i < mNumberOfFFTPoints; i++){ 
      temp = (double)((signal[2*i] & 0xFF) | (signal[2*i+1] << 8))/32768.0F; 
      complexSignal[i] = new Complex(temp,0.0); 
     } 

     y = FFT.fft(complexSignal); // --> Here I use FFT class 

     mMaxFFTSample = 0.0; 
     mPeakPos = 0; 
     for(int i = 0; i < (mNumberOfFFTPoints/2); i++) 
     { 
      absSignal[i] = Math.sqrt(Math.pow(y[i].re(), 2) + Math.pow(y[i].im(), 2)); 
      if(absSignal[i] > mMaxFFTSample) 
      { 
       mMaxFFTSample = absSignal[i]; 
       mPeakPos = i; 
      } 
     } 

     return absSignal; 

    } 

然後我把它叫做類寫音頻..

private void writeAudioDataToFile(){ 

     byte data[] = new byte[bufferSize]; 
     String filename = getTempFilename(); 
     FileOutputStream os = null; 

     try { 
       os = new FileOutputStream(filename); 
     } catch (FileNotFoundException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
     } 

     int read = 0;     
     if(null != os){ 
       while(isRecording){ 
         read = recorder.read(data, 0, bufferSize); 
         if(read > 0){ 
          absNormalizedSignal = calculateFFT(data); // --> HERE ^__^ 
         } 

         if(AudioRecord.ERROR_INVALID_OPERATION != read){ 
           try { 
             os.write(data); 
           } catch (IOException e) { 
             e.printStackTrace(); 
           } 
         } 
       } 

       try { 
         os.close(); 
       } catch (IOException e) { 
         e.printStackTrace(); 
       } 
     } 
} 
0

這聽起來像你直接的問題是「N不是2的冪」。在這種情況下,N可能是指您將數據放入FFT的大小。大多數FFT算法僅適用於大小爲2的冪的數據塊。

您是否試圖將整個文件一次放入FFT?如果是這樣,你可能需要閱讀更多的背景材料,以瞭解你在做什麼。也許從這裏開始:http://blog.bjornroche.com/2012/07/frequency-detection-using-fft-aka-pitch.html

+0

非常感謝你的回覆... :) 哦,是的...我已經閱讀過這篇文章之前... 和另一篇文章從那個博客太... 這是你的博客對嗎? :) 但我仍然不完全瞭解有關窗口削減數據,然後做FFT部分... 你能給我樣本嗎? 我真的很新,在這個音頻數據處理世界... 我得到的理論,但我真的不明白的實施,也許除非我看到示例代碼... :( –

+0

是的,這是我的博客。 https://github.com/bejayoharen/guitartuner –

+0

我真的不明白,,,但我會嘗試,:) 謝謝4你的建議... –