如何创建连续运行的调谐器?

发布于 12-18 15:52 字数 5687 浏览 5 评论 0原文

我正在为 Android 创建一个调音器(类似于吉他调音器),我想知道如何让调音器连续运行(几分钟左右)。我不希望它成为在用户使用我的应用程序时在后台运行的服务。

我已成功使用 AudioRecord 类,并且正在获取似乎正确的数据。我正在过滤这些数据并找到输入信号的基频,但需要帮助弄清楚如何让我的调谐器连续运行。

这是我的代码到目前为止的样子:

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

我是否需要更改有关按钮及其按下时执行的操作的某些内容?它只涉及缓冲区大小吗?我多久计算一次 FFT?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

丶情人眼里出诗心の2024-12-25 15:52:03

除非我误解了,否则您可以使用 while 循环来检查布尔变量。当用户单击停止按钮时,将该变量设置为 false。

while (tuning) {
    trigger();
}

您还应该在这些调用之间引入延迟。在 UI 线程以外的线程上运行此代码也是明智的做法。请参阅http://developer.android.com/resources/articles/painless-threading。 html

一个简单的例子说明了我的意思是要做的

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

,但是你必须担心更新 UI,因为你不能从这个线程中做到这一点。最好的选择是使用 AsyncTask http://developer .android.com/reference/android/os/AsyncTask.html

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文