android - AudioRecord 有时会跳过音频数据

标签 android mediarecorder android-audiorecord

我使用 AudioRecord 在 wav 文件中录制音频。我在辅助线程上的循环中执行 read() 操作,数据在另一秒内传递到队列中。用于写入文件的线程。

问题如下: 当我选择将音频数据保存在外部 SD 卡中时,音频包含一些跳音/失真(仅有时)。其他用户在内部 SD 卡上保存时也遇到此问题

这是我实例化 AudioRecord 对象的方式:

initBufferSize = assureMinBufferSize();
if (minBufferSize == AudioRecord.ERROR_BAD_VALUE) { 
    throw new Exception("" + ErrorCodes.ERROR_CODE_0);
}
else if (minBufferSize == AudioRecord.ERROR) {
    throw new Exception("" + ErrorCodes.ERROR_CODE_0);
}

buffer = new byte[initBufferSize];
aRecorder = new AudioRecord(mSource, sRate, nChannel, AudioFormat.ENCODING_PCM_16BIT, initBufferSize);

这就是我池化 AudioRecorder 对象的方式:

private class AudioReaderRunnable implements Runnable {
    private volatile boolean stopped;

    @Override
    public void run() {                         
        while (!stopped) {
            while(mState == State.RECORDING){
                read();
            }
        }           

        Log.i(getClass().getName(), "AudioReade thread stopped!");
    }

    private void read(){

        if(aRecorder == null)
            return;

        // "dirty" patch for some null crash for some users
        if(buffer == null)
            buffer = new byte[initBufferSize];

        //int x = aRecorder.read(buffer, 0, buffer.length);
        int x = readFully(buffer, 0, buffer.length);
        if(x <= 0)
            return;

        payloadSize += x;

        mWavData.arrayCopy(buffer);

        mWavData.setGain(rGain);
        mWavData.setBitsPerSamples(bitsPerSample);
        mWavData.setNrChannels(nChannelsNumber);

        // send audio to another thread for writing
        mAudioWritter.add(audioData);               
    }

    private int readFully(byte[] data, int off, int length) {
        int read = 0;
        int result = 0;
        int requestedSize = length;

        if(aRecorder == null){
            return read;
        }

        try {           
            result = aRecorder.read(data, off, requestedSize);
        } catch (Exception e) {
            Log.e(getClass().getName(), e.getMessage(), e);
            if(aRecorder == null)
                return read;
        }

        read = result;

        while (requestedSize != result && !stopped) {
            if(aRecorder == null)
                return read;

            requestedSize -= result;                
            try {
                result = aRecorder.read(data, result - 1, requestedSize);   
            } catch (Exception e) {
                Log.e(getClass().getName(), e.getMessage(), e);
                if(aRecorder == null)
                    return read;
            }
            read += result;
        }

        return read;
    }

    public void stopThread() {
        stopped = true;
    }
}

最佳答案

我在 Xamarin.Android 上遇到了类似的问题,这就是我解决它的方法:

AudioRecord 缓冲区就像一个窗口 - 当缓冲区被填满时,它会从头开始重写。因此,我们需要确保缓冲区足够大,以便我们可以在重写之前读取它。

这里有一个大问题 - 当垃圾收集器启动时,它会停止所有应用线程大约一秒钟 - 这足以让记录器将大约 100KB 的数据写入缓冲区(当使用 44100Hz 单声道 Pcm16bit 时),但在此期间我们没有读取,因为应用程序线程已停止!因此,如果缓冲区不够大,数据就会在我们有机会读取数据并将其写入文件之前被重写。因此,音频文件丢失了垃圾收集期间录制的音频。

因此,您需要使用足够大的 bufferSize 来初始化 AudioRecord,以使其比垃圾回收事件的生命周期更长。对于不同的音频配置,这将是不同的大小。对我来说(使用 44100Hz、单声道、PCM16 位),缓冲区大小 512,000 就可以了。

我不确定 GC 在原生 Android 上是如何工作的,但我相信它可能是类似的。

关于android - AudioRecord 有时会跳过音频数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29233388/

相关文章:

android - 如何在Android中实例化AudioFormat类?

javascript - MediaRecorder.ondataavailable 数据返回 0

Android 音频录制到 wav

javascript - iOS 和 Android 中深度链接的版本支持

android - 如果调用了 onApplicationDisconnected,如何断开 Android 设备与 Chromecast 的连接

ios - 我可以在我的 iOS 应用程序中使用实验性 WebKit 功能吗?

android - AudioSource.VOICE_CALL 不适用于 android 4.0 但适用于 android 2.3

android - 音频记录 : obtainBuffer timed out

android - 如何使用 pagertabstrip 更新 View 寻呼机中的 fragment View ?

android - 如何使用 ADB 将文件复制到多个设备?