Android如何为音频播放添加延迟?语音干扰器开发求助
Android 实现音频录制后延迟500ms播放的解决方案
问题背景
开发语音干扰器应用,需要实现麦克风录制音频后延迟约500ms再通过耳机播放。当前已实现实时录制播放,但添加延迟的尝试未成功:
- 延迟启动
audioTrack.play(),无法播放启动前录制的音频 - 将录制的
shortAudioData存入ArrayList后再播放,未达到预期效果
现有核心代码:
val audioRecord = AudioRecord( MediaRecorder.AudioSource.MIC, intRecordSampleRate, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, intBufferSize ) audioTrack = AudioTrack( AudioManager.STREAM_MUSIC, intRecordSampleRate, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, intBufferSize, AudioTrack.MODE_STREAM, ) audioTrack.playbackRate = intRecordSampleRate audioRecord.startRecording() audioTrack.play() while (isActive){ audioRecord.read(shortAudioData, 0, shortAudioData.size) shortAudioList.add(shortAudioData) audioTrack.write(shortAudioData, 0, shortAudioData.size) }
问题分析
- 延迟启动播放:
AudioTrack启动前的录制数据未被缓存,自然无法回放 ArrayList存储失败:直接添加shortAudioData的引用,而该数组会被每次read操作覆盖,导致列表中所有元素都是同一数组的最新数据,没有保留历史录制内容
解决方案
通过缓存音频数据到队列,先积累足够500ms的音频量后再开始播放,同时避免数组引用覆盖问题:
- 计算500ms所需的音频样本总量:基于采样率和声道数计算缓存阈值
- 使用队列存储复制后的音频数组,确保历史数据不被覆盖
- 缓存达到阈值后,同步进行"存新数据-取旧数据播放"的循环,维持稳定延迟
修改后的完整代码:
val audioRecord = AudioRecord( MediaRecorder.AudioSource.MIC, intRecordSampleRate, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, intBufferSize ) audioTrack = AudioTrack( AudioManager.STREAM_MUSIC, intRecordSampleRate, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, intBufferSize, AudioTrack.MODE_STREAM, ) audioTrack.playbackRate = intRecordSampleRate audioRecord.startRecording() audioTrack.play() // 配置延迟参数 val delayMs = 500 // 计算500ms对应的short样本数:采样率 * 声道数(2) * 延迟秒数 val requiredCacheSize = (intRecordSampleRate * 2 * (delayMs / 1000.0)).toInt() var cachedSampleCount = 0 val audioQueue = LinkedList<ShortArray>() while (isActive) { val readCount = audioRecord.read(shortAudioData, 0, shortAudioData.size) if (readCount > 0) { // 复制录制的音频数据,避免原数组被后续read覆盖 val audioCopy = shortAudioData.copyOf(readCount) audioQueue.add(audioCopy) cachedSampleCount += readCount // 当缓存的样本量达到延迟要求后,开始播放 if (cachedSampleCount >= requiredCacheSize) { val playData = audioQueue.poll() playData?.let { audioTrack.write(it, 0, it.size) cachedSampleCount -= it.size } } } } // 停止时清理资源 audioRecord.stop() audioRecord.release() audioTrack.stop() audioTrack.release()
关键细节说明
- 数组复制:必须通过
copyOf()复制录制的音频数据,否则队列中存储的都是同一数组的引用,数据会被后续录制操作覆盖 - 缓存阈值计算:确保缓存足够500ms的音频后再开始播放,精准控制延迟时间
- 队列同步:通过
LinkedList实现队列的先进先出,保证播放顺序和录制顺序一致
内容的提问来源于stack exchange,提问作者Kaan Karagöz
相关产品推荐
相关产品推荐

