【问题标题】:Android app to record sound in real time and identify frequencyAndroid 应用程序可实时录制声音并识别频率
【发布时间】:2013-06-03 16:09:45
【问题描述】:

我需要开发一个应用程序来使用手机的麦克风实时记录频率,然后显示它们(以文本形式)。我在这里发布我的代码。从http://introcs.cs.princeton.edu/java/97data/FFT.java.htmlhttp://introcs.cs.princeton.edu/java/97data/Complex.java.html 使用了 FFT 和复杂类。问题是当我在模拟器上运行它时,频率从某个随机值开始并不断增加直到 7996。然后重复整个过程。有人可以帮帮我吗?

public class Main extends Activity {

TextView disp;
private static int[] sampleRate = new int[] { 44100, 22050, 11025, 8000 };
short audioData[];
double finalData[];
int bufferSize,srate;
String TAG;
public boolean recording;
AudioRecord recorder;
Complex[] fftArray;
float freq;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    disp = (TextView) findViewById(R.id.display);

    Thread t1 = new Thread(new Runnable(){

        public void run() {

            Log.i(TAG,"Setting up recording");
            for (int rate : sampleRate) {
                try{

                    Log.d(TAG, "Attempting rate " + rate);

            bufferSize=AudioRecord.getMinBufferSize(rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT)*3; //get the buffer size to use with this audio record

            if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {

            recorder = new AudioRecord (MediaRecorder.AudioSource.MIC,rate,AudioFormat.CHANNEL_CONFIGURATION_MONO,
            AudioFormat.ENCODING_PCM_16BIT,2048); //instantiate the AudioRecorder
            Log.d(TAG, "BufferSize " +bufferSize);
            srate = rate;

            }

            } catch (Exception e) {
                Log.e(TAG, rate + "Exception, keep trying.",e);
            }
        }
            bufferSize=2048;
            recording=true; //variable to use start or stop recording
            audioData = new short [bufferSize]; //short array that pcm data is put into.
            Log.i(TAG,"Got buffer size =" + bufferSize);                
            while (recording) {  //loop while recording is needed
                   Log.i(TAG,"in while 1");
            if (recorder.getState()==android.media.AudioRecord.STATE_INITIALIZED) // check to see if the recorder has initialized yet.
            if (recorder.getRecordingState()==android.media.AudioRecord.RECORDSTATE_STOPPED)
            recorder.startRecording();  //check to see if the Recorder has stopped or is not recording, and make it record.

            else {
                   Log.i(TAG,"in else");
                  // audiorecord();
                finalData=convert_to_double(audioData);
                Findfft();
                for(int k=0;k<fftArray.length;k++)
                {
                    freq = ((float)srate/(float) fftArray.length) *(float)k;
                    runOnUiThread(new Runnable(){
                     public void run()
                     {
                         disp.setText("The frequency is " + freq);
                         if(freq>=15000)
                             recording = false;
                     }
                 });


                }


             }//else recorder started

    } //while recording

    if (recorder.getState()==android.media.AudioRecord.RECORDSTATE_RECORDING) 
    recorder.stop(); //stop the recorder before ending the thread
    recorder.release(); //release the recorders resources
    recorder=null; //set the recorder to be garbage collected.

         }//run

    });
    t1.start();
}





private void Findfft() {
    // TODO Auto-generated method stub
    Complex[] fftTempArray = new Complex[bufferSize];
    for (int i=0; i<bufferSize; i++)
    {
        fftTempArray[i] = new Complex(finalData[i], 0);
    }
    fftArray = FFT.fft(fftTempArray);
}


private double[] convert_to_double(short data[]) {
    // TODO Auto-generated method stub
    double[] transformed = new double[data.length];

    for (int j=0;j<data.length;j++) {
    transformed[j] = (double)data[j];
    }

    return transformed;

}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
 }
 }       

【问题讨论】:

  • 你试过在真机上运行它吗?模拟器速度很慢,因此模拟器无法很好地处理此类用例。
  • 我试过但遇到了类似的问题。它显示 2 或 3 个频率,然后卡住。

标签: java android fft android-audiorecord frequency-analysis


【解决方案1】:

你的问题就在这里:

Findfft();
for(int k=0;k<fftArray.length;k++) {
    freq = ((float)srate/(float) fftArray.length) *(float)k;
    runOnUiThread(new Runnable() {
        public void run() {
            disp.setText("The frequency is " + freq);
            if(freq>=15000) recording = false;
        }
    });
}

所有这些 for 循环所做的就是遍历您的 FFT 值数组,将数组索引转换为以 Hz 为单位的频率,然后打印出来。

如果你想输出你正在记录的频率,你至少应该看看你数组中的数据——最粗略的方法是计算平方实数并找到最大的频率箱。

除此之外,我认为您使用的 FFT 算法不会进行任何预先计算 - 还有其他算法会这样做,并且当您正在为移动设备进行开发时,您可能想要占用 CPU 使用率和考虑到用电量。

JTransforms 是一个使用预计算来降低 CPU 负载的库,它的文档非常完整。

您还可以在Wikipedia 找到有关如何解释从 FFT 返回的数据的有用信息 - 无意冒犯,但看起来您不太确定自己在做什么,所以我给出了指点。

最后,如果您希望将此应用程序用于音符,我似乎记得很多人说 FFT 不是最好的方法,但我不记得是什么。也许其他人可以添加那一点?

【讨论】:

    【解决方案2】:

    您的问题已经得到了简洁的回答,但是,为了进一步实现您的目标并完成循环......

    是的,FFT 在用于音高/频率识别的有限 CPU 上不是最佳选择。更优化的方法是 YIN 描述的here。你可以在Tarsos 找到一个实现。 您将面临的问题是 ADK 中缺少 javax.sound.sampled,因此将 AudioRecord 中的短片/字节转换为引用实现所需的浮点数。

    【讨论】:

      【解决方案3】:

      几天后我找到了这个解决方案 - 获得 Hrz 频率的最佳方法:

      同时下载 Jtransformsthis Jar - Jtransforms 需要它。

      然后我使用这个任务:

      public class MyRecorder extends AsyncTask<Void, short[], Void> {
      
      int blockSize = 2048;// = 256;
      private static final int RECORDER_SAMPLERATE = 8000;
      private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
      private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
      int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
      int BytesPerElement = 2;
      
      
      
      @Override
      protected Void doInBackground(Void... params) {
      
          try {
              final AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
                      RECORDER_SAMPLERATE, RECORDER_CHANNELS,
                      RECORDER_AUDIO_ENCODING, BufferElements2Rec * BytesPerElement);
              if (audioRecord == null) {
                  return null;
              }
      
              final short[] buffer = new short[blockSize];
              final double[] toTransform = new double[blockSize];
              audioRecord.startRecording();
              while (started) {
                  Thread.sleep(100);
                  final int bufferReadResult = audioRecord.read(buffer, 0, blockSize);
                  publishProgress(buffer);
              }
              audioRecord.stop();
              audioRecord.release();
          } catch (Throwable t) {
              Log.e("AudioRecord", "Recording Failed");
          }
          return null;
      }
      
      
      
      @Override
      protected void onProgressUpdate(short[]... buffer) {
          super.onProgressUpdate(buffer);
          float freq = calculate(RECORDER_SAMPLERATE, buffer[0]);
          }
      
      
      public static float calculate(int sampleRate, short [] audioData)
      {
          int numSamples = audioData.length;
          int numCrossing = 0;
          for (int p = 0; p < numSamples-1; p++)
          {
              if ((audioData[p] > 0 && audioData[p + 1] <= 0) ||
                      (audioData[p] < 0 && audioData[p + 1] >= 0))
              {
                  numCrossing++;
              }
          }
      
      
          float numSecondsRecorded = (float)numSamples/(float)sampleRate;
          float numCycles = numCrossing/2;
          float frequency = numCycles/numSecondsRecorded;
      
      
          return frequency;
      }
      

      【讨论】:

        猜你喜欢
        • 2015-09-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-18
        • 2017-02-07
        • 2022-08-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多