Android AudioRecord 缓冲区在有意义的值之前以多个 0 开头

Android AudioRecord buffer starts with a number of 0s before meaningful values

我正在尝试使用 AudioRecord 录制大约 500 帧,但似乎 AudioRecord 的缓冲区最初在捕获有意义的值之前部分填充了一些 0。

我必须使用数组读取最初的 10000 帧才能获得实际值。

AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
        44100,
        AudioFormat.CHANNEL_IN_MONO,
        AudioFormat.ENCODING_PCM_16BIT,
        2*44100);
audioRecord.startRecording();
audioRecord.read(new short[10000], 0, 10000); // have to include this to remove redundant values
audioRecord.read(audio, 0, 500);
audioRecord.stop();

如果我省略第三行,我将得到 500 个零。这个解决方案并不简洁,我需要知道我是否做错了什么。另外,重要的是要注意,在调用 read() 方法之前,stateSTATE_INITIALIZEDrecordingStateRECORDSTATE_RECORDINGread() 方法 return 正是他们应该读取的帧数,所以没问题。

您正在正确使用 AudioRecord。问题似乎是麦克风上的一些 AGC;正如您所观察到的,从缓冲区中读出的值在最初的几毫秒内逐渐变大。这很可能是硬件 AGC,可能是由制造商添加的,以在每次新录音开始时抑制尖锐的 "crack"。

顺便说一句:我有一个带有 AGC 的旧 RAZR,它非常激进,如果你在麦克风附近打响指,它会安静一整秒,然后慢慢淡入。

解决此问题的一个方法是长期保留 AudioRecord 记录。然后,当您决定需要抓取 500 帧时,它已经是 "warmed up",并且值应该是完整的。

编辑

我刚刚对其进行了单元测试,如果您忽略足够快地读出数据,AudioRecord 似乎不会覆盖其内部缓冲区中的数据。或者,至少,不清楚这些数据到底发生了什么。因此,需要一个更复杂的解决方案。

在这种情况下,您似乎必须确保缓冲区永远不会溢出。这意味着以足够快的速度调用 read()。根据您的体系结构,您可能会发现为此目的专用一个线程是最简单的。

如果您只是在每次读取中使用 500 帧缓冲区,那么到时候,您可以只获取该缓冲区的副本,这将相当接近来自 "most recent possible" 的数据流麦克风。这假设您的阅读速度足够快,以至于您的下一次阅读会阻塞。

我说"reasonably close"是因为音频数据以getMinBufferSize()/2大小的块放在缓冲区中,如果我没记错的话,这也是OnRecordPositionUpdateListener分辨率的限制。所以,你将接近终点,但很难说到底有多近。

缓冲区中的初始零可能是由缓冲本身或重新采样引起的。查看相关帖子:
1. Android AudioRecord Initialization delay
2. AudioRecord returns some empty data after start