简体   繁体   中英

Android Studio implement FFT,but can't detect the low frequency

I'm trying to do a app that can detect the piano's frequency. I already see this

How to calculate sound frequency in android?

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

and use the example code https://github.com/sommukhopadhyay/FFTBasedSpectrumAnalyzer/tree/master/

it is my code:

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);
            }
        }
    }
}

But when the frequency is lower than A3(220Hz).

The fundamental can't be caught. Just caught it's overtones.

I try to change sample rate and blocksize,but I don't get a better result.

Is something wrong with my code?Or some details I don't find? Someone can teach me I will be appreciated >.<

A peak magnitude frequency estimator is not a musical pitch detector/estimator. So the incorrect results have nothing to do with the code. Except it being the wrong algorithm choice for the OPs purpose.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM