How to create tuner that runs continuously?

♀尐吖头ヾ 提交于 2019-12-08 05:34:13

问题


I am creating a tuner for Android (similar to a guitar tuner) and I am wondering how to allow the tuner to run continuously (for a couple minutes or so). I don't want it to be a service that runs in the background, just while the user is in my app.

I have successfully used the AudioRecord class and am obtaining data that seems correct. I am in the process of filtering this data and finding the fundamental frequency of the input signal, but need help figuring out how to allow my tuner to run continuously.

This is what my code looks like so far:

import android.app.Activity;
import android.graphics.Color;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.dustin.tuner2.FFT;
import com.dustin.tuner2.Complex;


public class Tuner2 extends Activity implements OnClickListener {
    Button btnTune;
    TextView fft;
    TextView freq;
    TextView results;
    MediaRecorder recorder;
    AudioRecord tuner;
    boolean startTuning = true;
    int audioSource = MediaRecorder.AudioSource.MIC;
    int sampleRateInHz = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_SYSTEM);
    int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
    int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    int bufferSizeInBytes;
    int samples;
    short[] audioBuffer;
    short[] audioData;
    double[] temp;
    String fileName;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btnTune = (Button)findViewById(R.id.btnTune);
        freq = (TextView)findViewById(R.id.freq);
        btnTune.setOnClickListener(this);
        bufferSizeInBytes = 4096;
        //bufferSizeInBytes = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
        results = (TextView)findViewById(R.id.results);
        fft = (TextView)findViewById(R.id.fft);
    }

    @Override
    public void onClick(View v) {
        // TODO Auto-generated method stub

        if (v == btnTune)
        {
            onTune(startTuning);
            if (startTuning) {
                ((Button)v).setText("Stop Tuning");
            }
            else {
                ((Button)v).setText("Start Tuninig");
            }
            startTuning = !startTuning;
        }
    }

    //------------------------------------------------------------>
    private void onTune(boolean start) {
        if(start) {
            startTuning();
        } else {
            Toast.makeText(getApplicationContext(), "Tuning Stopped", Toast.LENGTH_SHORT).show();
            tuner.stop();
        }
    }

    private void startTuning()
    {
        tuner = new AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes);

        audioData = new short[bufferSizeInBytes];
        trigger();
    }

    public void trigger(){
        acquire();
        computeFFT();
        display();
    }

    public void acquire(){
        try {
            tuner.startRecording();
            samples = tuner.read(audioData, 0, bufferSizeInBytes);
        }
        catch (Throwable t){

        }   
    }

    public void computeFFT(){
        //Conversion from short to double
        double[] micBufferData = new double[bufferSizeInBytes];//size may need to change
        final int bytesPerSample = 2; // As it is 16bit PCM
        final double amplification = 100.0; // choose a number as you like
        for (int index = 0, floatIndex = 0; index < bufferSizeInBytes - bytesPerSample + 1; index += bytesPerSample, floatIndex++) {
            double sample = 0;
            for (int b = 0; b < bytesPerSample; b++) {
                int v = audioData[index + b];
                if (b < bytesPerSample - 1 || bytesPerSample == 1) {
                    v &= 0xFF;
                }
                sample += v << (b * 8);
            }
            double sample32 = amplification * (sample / 32768.0);
            micBufferData[floatIndex] = sample32;
        }

        //Create Complex array for use in FFT
        Complex[] fftTempArray = new Complex[bufferSizeInBytes];
        for (int i=0; i<bufferSizeInBytes; i++)
        {
            fftTempArray[i] = new Complex(micBufferData[i], 0);
        }

        //Obtain array of FFT data
        final Complex[] fftArray = FFT.fft(fftTempArray);
        final Complex[] fftInverse = FFT.ifft(fftTempArray);

        //Create an array of magnitude of fftArray
        double[] magnitude = new double[fftArray.length];
        for (int i=0; i<fftArray.length; i++){
            magnitude[i]= fftArray[i].abs();
        }

        fft.setTextColor(Color.GREEN);
        fft.setText("fftArray is "+ fftArray[500] +" and fftTempArray is "+fftTempArray[500] + " and fftInverse is "+fftInverse[500]+" and audioData is "+audioData[500]+ " and magnitude is "+ magnitude[1] + ", "+magnitude[500]+", "+magnitude[1000]+" You rock dude!");
        for(int i = 2; i < samples; i++){
            fft.append(" " + magnitude[i] + " Hz");
        }
    }

    public void display(){
        results.setTextColor(Color.BLUE);
        results.setText(audioData[1]+"");
        for(int i = 2; i < samples; i++){
            results.append(" " + audioData[i]);
        }
        results.invalidate();
        //fft.setTextColor(Color.GREEN);
        //fft.setText("Buffer size is "+bufferSizeInBytes);
        //fft.setText(fftArray[1]+" Hz");
        //for(int i = 2; i < samples; i++){
        //fft.append(" " + fftArray[i] + " Hz");
        //}
        //fft.invalidate();
    }

Do I need to change something concerning the button and what it does when pressed? Would it just involve the buffer size? How often I compute the FFT?


回答1:


Unless I am misunderstanding, you could just use a while loop that checks a boolean variable. When the user clicks the stop button set that variable to false.

while (tuning) {
    trigger();
}

you should also probably introduce a delay between these calls. It would also be wise to run this code on a thread other than the UI thread. See http://developer.android.com/resources/articles/painless-threading.html

A simple example of what I mean would be to do

new Thread(new Runnable() {
    @Override
    public void run() {
        while (tuning) {
            trigger();
            try {
                Thread.sleep(SLEEP_TIME_MS);
            } catch (InterruptedException e) {
                // handle exception
            }
        }
    }
}).start();

but then you have to worry about updating the UI as you cannot do that from this Thread. The best option is to use AsyncTask http://developer.android.com/reference/android/os/AsyncTask.html



来源:https://stackoverflow.com/questions/8325656/how-to-create-tuner-that-runs-continuously

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!