Android:AudioRecord类问题:永远不会调用回调

Thi*_*ler 21 audio android callback recording android-audiorecord

我的Android Java应用程序需要将音频数据记录到RAM中并进行处理.这就是我使用"AudioRecord"类而不是"MediaRecorder"(仅记录到文件)的原因.

直到现在,我使用繁忙的循环轮询"read()"来表示音频数据.到目前为止,这一直在起作用,但它过多地占用了CPU.在两次民意调查之间,我将线程置于休眠状态以避免100%的CPU使用率.但是,这并不是一个干净的解决方案,因为无法保证睡眠时间,您必须减去安全时间,以免丢失音频片段.这不是CPU的最佳选择.对于并行运行的线程,我需要尽可能多的空闲CPU周期.

现在我使用"OnRecordPositionUpdateListener"实现了录制.根据SDK Docs,这看起来很有前途,也是正确的方法.一切似乎都有效(打开音频设备,读取()数据等)但从不调用Listner.

有人知道为什么吗?

信息:我正在使用真实的设备,而不是在仿真器下.使用Busy Loop录制基本上可以工作(但不是满意).永远不会调用Callback Listener.

这是我的源代码片段:

public class myApplication extends Activity {

  /* audio recording */
  private static final int AUDIO_SAMPLE_FREQ = 16000;
  private static final int AUDIO_BUFFER_BYTESIZE = AUDIO_SAMPLE_FREQ * 2 * 3; // = 3000ms
  private static final int AUDIO_BUFFER_SAMPLEREAD_SIZE = AUDIO_SAMPLE_FREQ  / 10 * 2; // = 200ms

  private short[] mAudioBuffer = null; // audio buffer
  private int mSamplesRead; // how many samples are recently read
  private AudioRecord mAudioRecorder; // Audio Recorder

  ...

  private OnRecordPositionUpdateListener mRecordListener = new OnRecordPositionUpdateListener() {

    public void onPeriodicNotification(AudioRecord recorder) {
      mSamplesRead = recorder.read(mAudioBuffer, 0, AUDIO_BUFFER_SAMPLEREAD_SIZE);
      if (mSamplesRead > 0) {

        // do something here...

      }
    }

    public void onMarkerReached(AudioRecord recorder) {
      Error("What? Hu!? Where am I?");
    }
  };

  ...

  public void onCreate(Bundle savedInstanceState) {

  try {
      mAudioRecorder = new AudioRecord(
          android.media.MediaRecorder.AudioSource.MIC, 
          AUDIO_SAMPLE_FREQ,
          AudioFormat.CHANNEL_CONFIGURATION_MONO,
          AudioFormat.ENCODING_PCM_16BIT,  
          AUDIO_BUFFER_BYTESIZE);
    } catch (Exception e) {
      Error("Unable to init audio recording!");
    }

    mAudioBuffer = new short[AUDIO_BUFFER_SAMPLEREAD_SIZE];
    mAudioRecorder.setPositionNotificationPeriod(AUDIO_BUFFER_SAMPLEREAD_SIZE);
    mAudioRecorder.setRecordPositionUpdateListener(mRecordListener);
    mAudioRecorder.startRecording();

    /* test if I can read anything at all... (and yes, this here works!) */
    mSamplesRead = mAudioRecorder.read(mAudioBuffer, 0, AUDIO_BUFFER_SAMPLEREAD_SIZE);

  }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*ean 14

我认为问题是你仍然需要进行读取循环.如果设置回调,则在读取为回调指定的帧数时将触发.但是你仍然需要进行读取.我试过这个并且回调被调用就好了.设置标记会在记录开始后读取该帧数时产生回调.换句话说,在你的许多读取之后,你可以将标记设置为远期,然后它将会触发.您可以将周期设置为更大的帧数,并且每次读取该帧数时都会触发回调.我认为他们这样做是为了你可以在紧密的循环中对原始数据进行低级处理,然后你的回调经常会进行汇总级处理.


小智 6

这是我用来查找平均噪音的代码.请注意,它基于侦听器通知,因此可以节省设备电池.它绝对基于上面的例子.这个例子为我节省了很多时间,谢谢.

private AudioRecord recorder;
private boolean recorderStarted;
private Thread recordingThread;
private int bufferSize = 800;
private short[][] buffers = new short[256][bufferSize];
private int[] averages = new int[256];
private int lastBuffer = 0;

protected void startListenToMicrophone() {
    if (!recorderStarted) {

        recordingThread = new Thread() {
            @Override
            public void run() {
                int minBufferSize = AudioRecord.getMinBufferSize(8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);
                recorder = new AudioRecord(AudioSource.MIC, 8000, AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 10);
                recorder.setPositionNotificationPeriod(bufferSize);
                recorder.setRecordPositionUpdateListener(new OnRecordPositionUpdateListener() {
                    @Override
                    public void onPeriodicNotification(AudioRecord recorder) {
                        short[] buffer = buffers[++lastBuffer % buffers.length];
                        recorder.read(buffer, 0, bufferSize);
                        long sum = 0;
                        for (int i = 0; i < bufferSize; ++i) {
                            sum += Math.abs(buffer[i]);
                        }
                        averages[lastBuffer % buffers.length] = (int) (sum / bufferSize);
                        lastBuffer = lastBuffer % buffers.length;
                    }

                    @Override
                    public void onMarkerReached(AudioRecord recorder) {
                    }
                });
                recorder.startRecording();
                short[] buffer = buffers[lastBuffer % buffers.length];
                recorder.read(buffer, 0, bufferSize);
                while (true) {
                    if (isInterrupted()) {
                        recorder.stop();
                        recorder.release();
                        break;
                    }
                }
            }
        };
        recordingThread.start();

        recorderStarted = true;
    }
}

private void stopListenToMicrophone() {
    if (recorderStarted) {
        if (recordingThread != null && recordingThread.isAlive() && !recordingThread.isInterrupted()) {
            recordingThread.interrupt();
        }
        recorderStarted = false;
    }
}
Run Code Online (Sandbox Code Playgroud)


Jes*_*don 5

现在我使用"OnRecordPositionUpdateListener"实现了录制.根据SDK Docs,这看起来很有前途,也是正确的方法.一切似乎都有效(打开音频设备,读取()数据等)但从不调用Listner.

有人知道为什么吗?

我发现OnRecordPositionUpdateListener在您第一次执行之前会被忽略.read().

换句话说,我发现如果我按照文档设置所有内容,我的监听器就不会被调用.但是,如果我.read()在完成初始化.start()之后首先调用一个,那么监听器将被调用 - 前提是我.read()每次调用Listener时都会调用它.

换句话说,看起来监听器事件似乎每次.read()或每次都有好处.

我还发现,如果我要求读取任何少于buffSize/2的样本,则不会调用监听器.因此,似乎只.read()在缓冲区大小的一半之后调用侦听器.要继续使用监听器回调,每次运行监听器时都必须调用read.(换句话说,将调用放在Listener代码中.)

但是,似乎在数据尚未准备好的时候调用侦听器,从而导致阻塞.

此外,如果您的通知期限或时间大于您的bufferSize的一半,它们似乎永远不会被调用.

更新:

随着我继续深入挖掘,我发现回调似乎只能在.read()完成时被调用......!

我不知道这是一个错误还是一个功能.我最初的想法是,当我需要阅读时,我想要回调.但也许Android开发人员有相反的想法,你只需要while(1){xxxx.read(...)} 在一个线程中放置一个线程,并且为了避免每次read()完成时都要跟踪,回调基本上可以告诉你什么时候读完了.

哦.也许它只是在我身上恍然大悟.而且我认为其他人已经在我面前指出了这一点,但它没有陷入:位置或周期回调必须对已经读取的字节进行操作......

我想也许我会坚持使用线程.

在这种风格中,一旦它返回,就会有一个线程不断地调用read(),因为读取是阻塞的,并耐心地等待足够的数据返回.

然后,独立地,回调将每x个样本调用您指定的函数.