首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Android AudioRecord类-快速处理麦克风直播音频,设置回调函数

Android AudioRecord类-快速处理麦克风直播音频,设置回调函数
EN

Stack Overflow用户
提问于 2010-12-24 16:48:42
回答 4查看 60.2K关注 0票数 67

我想从麦克风中录制音频,并访问它,以便几乎实时地播放。我不确定如何使用Android AudioRecord类来录制一些麦克风音频并快速访问它。

对于AudioRecord类,官方网站说“应用程序及时轮询AudioRecord对象”,“正在填充的缓冲区大小决定了在过度运行未读数据之前记录的时间长度”。后来有人建议,当轮询频率较低时,应该使用更大的缓冲区。他们从来不会在代码中实际展示一个例子。

我在一本书中看到的一个例子是,使用AudioRecord类连续读取新填充了实时麦克风音频的缓冲区,然后应用程序将这些数据写入SD文件。伪代码看起来像这样-

代码语言:javascript
复制
set up AudioRecord object with buffer size and recording format info
set up a file and an output stream
myAudioRecord.startRecording();
while(isRecording)
{
    // myBuffer is being filled with fresh audio
    read audio data into myBuffer
    send contents of myBuffer to SD file
}
myAudioRecord.stop();

这段代码如何将其读数与记录速率同步尚不清楚-布尔值"isRecording“是否在其他地方正确地进行了排序?这段代码可能读得太频繁,也可能读得太少,这取决于读写所用的时间。

站点文档还说,AudioRecord类有一个名为OnRecordPositionUpdateListener的嵌套类,它被定义为一个接口。这些信息表明,通过某种方式,您可以指定希望收到记录进度通知的时间段,以及事件处理程序的名称,然后会以指定的频率自动调用事件处理程序。我认为伪代码的结构应该是这样的-

代码语言:javascript
复制
set target of period update message = myListener
set period to be about every 250 ms
other code

myListener()
{
    if(record button was recently tapped)
        handle message that another 250 ms of fresh audio is available
        ie, read it and send it somewhere
)

我需要找到一些特定的代码,允许我捕获和处理麦克风音频,延迟小于500毫秒。安卓提供了另一个名为MediaRecorder的类,但它不支持流媒体,我可能想通过Wi-Fi网络近乎实时地流传输实时麦克风音频。我在哪里可以找到一些具体的例子?

EN

回答 4

Stack Overflow用户

发布于 2011-01-28 19:58:33

在尝试了很多通知和其他一些技术之后,我选择了下面的代码:

代码语言:javascript
复制
private class AudioIn extends Thread { 
     private boolean stopped    = false;

     private AudioIn() { 

             start();
          }

     @Override
     public void run() { 
            android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
            AudioRecord recorder = null;
            short[][]   buffers  = new short[256][160];
            int         ix       = 0;

            try { // ... initialise

                  int N = AudioRecord.getMinBufferSize(8000,AudioFormat.CHANNEL_IN_MONO,AudioFormat.ENCODING_PCM_16BIT);

                   recorder = new AudioRecord(AudioSource.MIC,
                                              8000,
                                              AudioFormat.CHANNEL_IN_MONO,
                                              AudioFormat.ENCODING_PCM_16BIT,
                                              N*10);

                   recorder.startRecording();

                   // ... loop

                   while(!stopped) { 
                      short[] buffer = buffers[ix++ % buffers.length];

                      N = recorder.read(buffer,0,buffer.length);
                      //process is what you will do with the data...not defined here
                      process(buffer);
                  }
             } catch(Throwable x) { 
               Log.w(TAG,"Error reading voice audio",x);
             } finally { 
               close();
             }
         }

      private void close() { 
          stopped = true;
        }

    }

到目前为止,它在我试过的六部安卓手机上运行得相当好。

票数 34
EN

Stack Overflow用户

发布于 2011-01-29 10:01:44

我想知道你是否可以用下面的方式组合这些答案...

在while循环之前使用setPositionNotificationPeriod(160)。这应该会导致每次读取160帧时都会调用回调。从回调中调用process(buffer),而不是在执行读取循环的线程中调用process(buffer)。使用一个变量来跟踪最后一个读缓冲区,以便处理正确的缓冲区。就像现在一样,你在读上阻塞了,那么你在处理的时候就不是在读了。我想把这两个分开可能会更好。

票数 22
EN

Stack Overflow用户

发布于 2012-01-27 18:33:02

以下是使用OnRecordPositionUpdateListener和通知期间所需的代码。

我注意到,在实践中,它不会一致地在相同的时间发送通知,我想,但它足够接近了。

关于detectAfterEvery

detectEvery的大小需要足够大,才能容纳所需的数据量。对于这个例子,我们有一个44100 Hz的采样率,这意味着我们需要每秒44100个样本。通过将setPositionNotificationPeriod设置为44100,代码将告诉安卓在记录44100个样本后进行回调,大约每1秒。

完整的代码是here

代码语言:javascript
复制
        final int sampleRate = 44100;
        int bufferSize =
                AudioRecord.getMinBufferSize(sampleRate,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT);

//aim for 1 second
        int detectAfterEvery = (int)((float)sampleRate * 1.0f);

        if (detectAfterEvery > bufferSize)
        {
            Log.w(TAG, "Increasing buffer to hold enough samples " + detectAfterEvery + " was: " + bufferSize);
            bufferSize = detectAfterEvery;
        }

        recorder =
                new AudioRecord(AudioSource.MIC, sampleRate,
                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
                        AudioFormat.ENCODING_PCM_16BIT, bufferSize);
        recorder.setPositionNotificationPeriod(detectAfterEvery);

        final short[] audioData = new short[bufferSize];
        final int finalBufferSize = bufferSize;

        OnRecordPositionUpdateListener positionUpdater = new OnRecordPositionUpdateListener()
        {
            @Override
            public void onPeriodicNotification(AudioRecord recorder)
            {
                Date d = new Date();
//it should be every 1 second, but it is actually, "about every 1 second"
//like 1073, 919, 1001, 1185, 1204 milliseconds of time.
                Log.d(TAG, "periodic notification " + d.toLocaleString() + " mili " + d.getTime());
                recorder.read(audioData, 0, finalBufferSize);

                //do something amazing with audio data
            }

            @Override
            public void onMarkerReached(AudioRecord recorder)
            {
                Log.d(TAG, "marker reached");
            }
        };
        recorder.setRecordPositionUpdateListener(positionUpdater);

        Log.d(TAG, "start recording, bufferSize: " + bufferSize);
        recorder.startRecording(); 

//remember to still have a read loop otherwise the listener won't trigger
while (continueRecording)
        {
            recorder.read(audioData, 0, bufferSize);
        }
票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4525206

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档