2017-09-13 48 views
0

我試圖做一個可以檢測鋼琴頻率的應用程序。 我已經看到這個Android Studio實現FFT,但無法檢測到低頻

How to calculate sound frequency in android?

FFT類:https://github.com/fjfdeztoro/fftpack

,並使用示例代碼 https://github.com/sommukhopadhyay/FFTBasedSpectrumAnalyzer/tree/master/

這是我的代碼:

package com.somitsolutions.android.spectrumanalyzer; 

import android.app.Activity; 
import android.content.Context; 
import android.content.Intent; 
import android.content.pm.ActivityInfo; 
import android.graphics.Bitmap; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Point; 
import android.media.AudioFormat; 
import android.media.AudioRecord; 
import android.media.MediaRecorder; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.util.Log; 
import android.view.Display; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.view.ViewGroup.LayoutParams; 
import android.view.ViewGroup.MarginLayoutParams; 
import android.view.Window; 
import android.view.WindowManager; 
import android.widget.Button; 
import android.widget.ImageView; 
import android.widget.LinearLayout; 
import android.widget.TextView; 

import ca.uol.aig.fftpack.RealDoubleFFT; 


public class SoundRecordAndAnalysisActivity extends Activity implements OnClickListener{ 

    //code by yu-che 
    int display_width = 600;//the result of display_width/5 and display_width/50 must be integer 
    int frequency = 44100; 
    //code by yu-che 
    int channelConfiguration = AudioFormat.CHANNEL_IN_MONO; 
    //origin code 
    //int channelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO; 
    int audioEncoding = AudioFormat.ENCODING_PCM_16BIT; 

    AudioRecord audioRecord; 
    private RealDoubleFFT transformer; 
    int blockSize;// = 256; 
    Button startStopButton; 
    boolean started = false; 
    boolean CANCELLED_FLAG = false; 


    RecordAudio recordTask; 
    ImageView imageViewDisplaySpectrum; 
    MyImageView imageViewScale; 
    Bitmap bitmapDisplaySpectrum; 

    Canvas canvasDisplaySpectrum; 

    Paint paintSpectrumDisplay; 

    SoundRecordAndAnalysisActivity mainActivity; 
    LinearLayout main; 
    int width; 
    int height; 
    int left_Of_BitmapScale; 
    int left_Of_DisplaySpectrum; 
    private final static int ID_BITMAPDISPLAYSPECTRUM = 1; 
    private final static int ID_IMAGEVIEWSCALE = 2; 
    TextView tv; 

    /** Called when the activity is first created. */ 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     Display display = getWindowManager().getDefaultDisplay(); 
     //Point size = new Point(); 
     //display.get(size); 
     //code by yu-che 
     Point size = new Point(); 
     display.getSize(size); 
     width = size.x; 
     height = size.y; 
     //origin code 
     //width = display.getWidth(); 
     //height = display.getHeight(); 

     blockSize = 8196; 



    } 

    @Override 
    public void onWindowFocusChanged (boolean hasFocus) { 
     //left_Of_BitmapScale = main.getC.getLeft(); 
     MyImageView scale = (MyImageView)main.findViewById(ID_IMAGEVIEWSCALE); 
     ImageView bitmap = (ImageView)main.findViewById(ID_BITMAPDISPLAYSPECTRUM); 
     left_Of_BitmapScale = scale.getLeft(); 
     left_Of_DisplaySpectrum = bitmap.getLeft(); 
    } 
    private class RecordAudio extends AsyncTask<Void, double[], Boolean> { 

     @Override 
     protected Boolean doInBackground(Void... params) { 

      int bufferSize = AudioRecord.getMinBufferSize(frequency, 
        channelConfiguration, audioEncoding); 
      audioRecord = new AudioRecord(
        MediaRecorder.AudioSource.DEFAULT, frequency, 
        channelConfiguration, audioEncoding, bufferSize); 
      int bufferReadResult; 
      short[] buffer = new short[blockSize]; 
      double[] toTransform = new double[blockSize]; 
      try { 
       audioRecord.startRecording(); 
      } catch (IllegalStateException e) { 
       Log.e("Recording failed", e.toString()); 

      } 
      while (started) { 

       if (isCancelled() || (CANCELLED_FLAG == true)) { 

        started = false; 
        //publishProgress(cancelledResult); 
        Log.d("doInBackground", "Cancelling the RecordTask"); 
        break; 
       } else { 
        bufferReadResult = audioRecord.read(buffer, 0, blockSize); 

        for (int i = 0; i < blockSize && i < bufferReadResult; i++) { 
         toTransform[i] = (double) buffer[i]/32768.0; // signed 16 bit 
        } 

        transformer.ft(toTransform); 

        publishProgress(toTransform); 

       } 

      } 
      return true; 
     } 
     @Override 
     protected void onProgressUpdate(double[]...progress) { 
      Log.e("RecordingProgress", "Displaying in progress"); 

      Log.d("Test:", Integer.toString(progress[0].length)); 


      if (width > 512) { 
       int i = 0; 
       int intensity = 0; 
       double scale =0; 
       while (true){ 

        //scale = 1600/(2000/50)*(display_width/50); 
        //canvasDisplaySpectrum.drawLine((int)scale, 150, (int)scale,150-50 , paintSpectrumDisplay); 
        if (0 == i){ 
         intensity = (int)(progress[0][0]* progress[0][0]); 
         canvasDisplaySpectrum.drawLine(0, 150, 0,150-intensity , paintSpectrumDisplay); 
         i++; 
        } else if ((blockSize-1) == i) { 
         intensity = (int)(progress[0][(blockSize-1)]* progress[0][(blockSize-1)]); 
         scale = (i/2+0.5)*frequency/(blockSize-1)/(2000/50)*(display_width/50); 
         canvasDisplaySpectrum.drawLine((int)scale, 150, (int)scale,150-intensity , paintSpectrumDisplay); 
         break; 
        } else{ 
         intensity = (int)(progress[0][i]*progress[0][i]+ progress[0][(i+1)]*progress[0][(i+1)]); 
         scale = (i/2+0.5)*frequency/(blockSize-1)/(2000/50)*(display_width/50); 
         canvasDisplaySpectrum.drawLine((int)scale, 150, (int)scale,150-intensity , paintSpectrumDisplay); 
         i = i +2; 
        } 

       } 
       /* 
       for (int i = 0; i < progress[0].length; i++) { 

        //int x = 2 * i; 
        //int downy = (int) (150 - (progress[0][i] * 10)); 
        //int upy = 150; 
        //canvasDisplaySpectrum.drawLine(x, downy, x, upy, paintSpectrumDisplay); 


        canvasDisplaySpectrum.drawLine(0, 120, 0, 150, paintSpectrumDisplay); 

        canvasDisplaySpectrum.drawLine(display_width/50*10, 170, display_width/50*10, 150, paintSpectrumDisplay); 
       } 
       */ 

       imageViewDisplaySpectrum.invalidate(); 
      } else { 
       for (int i = 0; i < progress[0].length; i++) { 
        int x = i; 
        int downy = (int) (150 - (progress[0][i] * 10)); 
        int upy = 150; 
        canvasDisplaySpectrum.drawLine(x, downy, x, upy, paintSpectrumDisplay); 
       } 

       imageViewDisplaySpectrum.invalidate(); 
      } 



     } 
     @Override 
     protected void onPostExecute(Boolean result) { 
      super.onPostExecute(result); 
      try{ 
       audioRecord.stop(); 
      } 
      catch(IllegalStateException e){ 
       Log.e("Stop failed", e.toString()); 

      } 

      canvasDisplaySpectrum.drawColor(Color.BLACK); 
      imageViewDisplaySpectrum.invalidate(); 

     } 
    } 

    protected void onCancelled(Boolean result){ 

     try{ 
      audioRecord.stop(); 
     } 
     catch(IllegalStateException e){ 
      Log.e("Stop failed", e.toString()); 

     } 
      /* //recordTask.cancel(true); 
      Log.d("FFTSpectrumAnalyzer","onCancelled: New Screen"); 
      Intent intent = new Intent(Intent.ACTION_MAIN); 
      intent.addCategory(Intent.CATEGORY_HOME); 
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
      startActivity(intent); 
*/ 
    } 

    public void onClick(View v) { 
     if (started == true) { 
      //started = false; 
      CANCELLED_FLAG = true; 
      //recordTask.cancel(true); 
      try{ 
       audioRecord.stop(); 
      } 
      catch(IllegalStateException e){ 
       Log.e("Stop failed", e.toString()); 

      } 
      startStopButton.setText("Start"); 

      canvasDisplaySpectrum.drawColor(Color.BLACK); 

     } 

     else { 
      started = true; 
      CANCELLED_FLAG = false; 
      startStopButton.setText("Stop"); 
      recordTask = new RecordAudio(); 
      recordTask.execute(); 
     } 

    } 
    //code by yu-che 
    /* 
    static SoundRecordAndAnalysisActivity getMainActivity(){ 

     return mainActivity; 
    } 
    */ 
    public void onStop(){ 
     super.onStop(); 
      /* try{ 
       audioRecord.stop(); 
      } 
      catch(IllegalStateException e){ 
       Log.e("Stop failed", e.toString()); 

      }*/ 
     recordTask.cancel(true); 
     Intent intent = new Intent(Intent.ACTION_MAIN); 
     intent.addCategory(Intent.CATEGORY_HOME); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     startActivity(intent); 
    } 

    public void onStart(){ 

     super.onStart(); 
     main = new LinearLayout(this); 
     main.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,android.view.ViewGroup.LayoutParams.MATCH_PARENT)); 
     main.setOrientation(LinearLayout.VERTICAL); 
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 
     requestWindowFeature(Window.FEATURE_NO_TITLE); 
     getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
       WindowManager.LayoutParams.FLAG_FULLSCREEN); 

     transformer = new RealDoubleFFT(blockSize); 

     imageViewDisplaySpectrum = new ImageView(this); 
     if(width > 512){ 
      bitmapDisplaySpectrum = Bitmap.createBitmap((int)512,(int)300,Bitmap.Config.ARGB_8888); 
     } 
     else{ 
      bitmapDisplaySpectrum = Bitmap.createBitmap((int)256,(int)150,Bitmap.Config.ARGB_8888); 
     } 
     LinearLayout.LayoutParams layoutParams_imageViewScale = null; 
     //Bitmap scaled = Bitmap.createScaledBitmap(bitmapDisplaySpectrum, 320, 480, true); 
     canvasDisplaySpectrum = new Canvas(bitmapDisplaySpectrum); 
     //canvasDisplaySpectrum = new Canvas(scaled); 
     paintSpectrumDisplay = new Paint(); 
     paintSpectrumDisplay.setColor(Color.GREEN); 
     imageViewDisplaySpectrum.setImageBitmap(bitmapDisplaySpectrum); 
     if(width >512){ 
      //imageViewDisplaySpectrum.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 
      LinearLayout.LayoutParams layoutParams_imageViewDisplaySpectrum=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
      ((MarginLayoutParams) layoutParams_imageViewDisplaySpectrum).setMargins(100, 600, 0, 0); 
      imageViewDisplaySpectrum.setLayoutParams(layoutParams_imageViewDisplaySpectrum); 
      layoutParams_imageViewScale= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
      //layoutParams_imageViewScale.gravity = Gravity.CENTER_HORIZONTAL; 
      ((MarginLayoutParams) layoutParams_imageViewScale).setMargins(100, 20, 0, 0); 

     } 

     else if ((width >320) && (width<512)){ 
      LinearLayout.LayoutParams layoutParams_imageViewDisplaySpectrum=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
      ((MarginLayoutParams) layoutParams_imageViewDisplaySpectrum).setMargins(60, 250, 0, 0); 
      //layoutParams_imageViewDisplaySpectrum.gravity = Gravity.CENTER_HORIZONTAL; 
      imageViewDisplaySpectrum.setLayoutParams(layoutParams_imageViewDisplaySpectrum); 

      //imageViewDisplaySpectrum.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 
      layoutParams_imageViewScale=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
      ((MarginLayoutParams) layoutParams_imageViewScale).setMargins(60, 20, 0, 100); 
      //layoutParams_imageViewScale.gravity = Gravity.CENTER_HORIZONTAL; 
     } 

     else if (width < 320){ 
       /*LinearLayout.LayoutParams layoutParams_imageViewDisplaySpectrum=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
       ((MarginLayoutParams) layoutParams_imageViewDisplaySpectrum).setMargins(30, 100, 0, 100); 
       imageViewDisplaySpectrum.setLayoutParams(layoutParams_imageViewDisplaySpectrum);*/ 
      imageViewDisplaySpectrum.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 
      layoutParams_imageViewScale=new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); 
      //layoutParams_imageViewScale.gravity = Gravity.CENTER; 
     } 
     imageViewDisplaySpectrum.setId(ID_BITMAPDISPLAYSPECTRUM); 
     main.addView(imageViewDisplaySpectrum); 

     imageViewScale = new MyImageView(this); 
     imageViewScale.setLayoutParams(layoutParams_imageViewScale); 
     imageViewScale.setId(ID_IMAGEVIEWSCALE); 

     //imageViewScale.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 
     main.addView(imageViewScale); 

     tv=new TextView(this); 
     tv.setText("frequency"); 
     tv.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 


     startStopButton = new Button(this); 
     startStopButton.setText("Start"); 
     startStopButton.setOnClickListener(this); 
     startStopButton.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT)); 

     main.addView(startStopButton); 
     main.addView(tv); 
     setContentView(main); 

     mainActivity = this; 

    } 
    @Override 
    public void onBackPressed() { 
     super.onBackPressed(); 

     try{ 
      audioRecord.stop(); 
     } 
     catch(IllegalStateException e){ 
      Log.e("Stop failed", e.toString()); 

     } 
     recordTask.cancel(true); 
     Intent intent = new Intent(Intent.ACTION_MAIN); 
     intent.addCategory(Intent.CATEGORY_HOME); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     startActivity(intent); 
    } 

    @Override 
    protected void onDestroy() { 
     // TODO Auto-generated method stub 
     super.onDestroy(); 
     try{ 
      audioRecord.stop(); 
     } 
     catch(IllegalStateException e){ 
      Log.e("Stop failed", e.toString()); 

     } 
     recordTask.cancel(true); 
     Intent intent = new Intent(Intent.ACTION_MAIN); 
     intent.addCategory(Intent.CATEGORY_HOME); 
     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 
     startActivity(intent); 
    } 
    //Custom Imageview Class 
    //底下的座標軸 
    public class MyImageView extends ImageView { 
     Paint paintScaleDisplay; 
     Bitmap bitmapScale; 
     Canvas canvasScale; 
     public MyImageView(Context context) { 
      super(context); 
      // TODO Auto-generated constructor stub 
      if(width >display_width){ 
       bitmapScale = Bitmap.createBitmap(display_width,50,Bitmap.Config.ARGB_8888); 
      } 
      else{ 
       bitmapScale = Bitmap.createBitmap(256,50,Bitmap.Config.ARGB_8888); 
      } 
      //坐標軸 
      paintScaleDisplay = new Paint(); 
      paintScaleDisplay.setColor(Color.BLUE); 
      paintScaleDisplay.setStyle(Paint.Style.FILL); 

      canvasScale = new Canvas(bitmapScale); 

      setImageBitmap(bitmapScale); 
      invalidate(); 
     } 
     @Override 
     protected void onDraw(Canvas canvas) 
     { 
      // TODO Auto-generated method stub 
      super.onDraw(canvas); 

      if(width > 512){ 
       canvasScale.drawLine(0, 30, display_width, 30, paintScaleDisplay); 
       for(int i = 0,j = 0; i< display_width; i=i+display_width/5, j++){ 
        for (int k = i; k<(i+display_width/5); k=k+display_width/50){ 
         canvasScale.drawLine(k, 30, k, 23, paintScaleDisplay); 
        } 
        canvasScale.drawLine(i, 40, i, 20, paintScaleDisplay); 
        String text = Integer.toString(j*400) + " Hz"; 
        canvasScale.drawText(text, i, 45, paintScaleDisplay); 
       } 
       canvas.drawBitmap(bitmapScale, 0, 0, paintScaleDisplay); 
      } 
      else if ((width >320) && (width<512)){ 
       canvasScale.drawLine(0, 30, 0 + 256, 30, paintScaleDisplay); 
       for(int i = 0,j = 0; i<256; i=i+64, j++){ 
        for (int k = i; k<(i+64); k=k+8){ 
         canvasScale.drawLine(k, 30, k, 25, paintScaleDisplay); 
        } 
        canvasScale.drawLine(i, 40, i, 25, paintScaleDisplay); 
        String text = Integer.toString(j) + " KHz"; 
        canvasScale.drawText(text, i, 45, paintScaleDisplay); 
       } 
       canvas.drawBitmap(bitmapScale, 0, 0, paintScaleDisplay); 
      } 

      else if (width <320){ 
       canvasScale.drawLine(0, 30, 256, 30, paintScaleDisplay); 
       for(int i = 0,j = 0; i<256; i=i+64, j++){ 
        for (int k = i; k<(i+64); k=k+8){ 
         canvasScale.drawLine(k, 30, k, 25, paintScaleDisplay); 
        } 
        canvasScale.drawLine(i, 40, i, 25, paintScaleDisplay); 
        String text = Integer.toString(j) + " KHz"; 
        canvasScale.drawText(text, i, 45, paintScaleDisplay); 
       } 
       canvas.drawBitmap(bitmapScale, 0, 0, paintScaleDisplay); 
      } 
     } 
    } 
} 

但當頻率低於A3(220Hz)。

根本無法捕捉。剛剛發現它是泛泛之音。

我嘗試更改採樣率和塊大小,但我沒有得到更好的結果。

我的代碼有問題嗎?或者我找不到某些細節? 有人可以教我,我會感激>。 <

+0

將採樣率更改爲更高採樣率只會幫助您捕捉更高的諧波。能量的基本要低於泛音嗎?如果是,那麼嘗試調整一個閾值,以便甚至具有低能量的箱/頻率被檢索。 –

+0

噢,我看到頻譜很低或幾乎沒有頻率很低。所以,我認爲調整閾值無法解決我的問題><。我想也許是設備的限制或somthing錯誤與我的代碼目前,我不確定。 –

+0

根本不一定有最大的幅度 - 如果你正在尋找* pitch *,那麼稍微好一點的方法是測量連續諧波之間的頻率。但是,對於可靠的基音檢測,您需要使用適當的算法,如諧波積譜,而不是基於FFT的* ad hoc *方法。 –

回答

0

峯值幅度頻率估計器不是音調檢測器/估計器。所以不正確的結果與代碼無關。除了它是OPs目的的錯誤算法選擇。