第28章 录制与效果:AudioRecord与效果处理、实时监听与效果反馈、回声消除与噪声抑制

各位同学,今天我们来聊聊录制过程中的效果处理。很多人觉得录音就是把麦克风的声音存下来,其实没那么简单。你想想看,如果录的是K歌,没点混响效果,那干巴巴的声音谁受得了?如果是视频通话,回声和噪声不处理,对方听着也难受。

我个人习惯把录制效果处理分成三个层次:基础录制实时效果环境优化。今天我们就一层层把它讲透。

28.1 AudioRecord与效果处理的基本流程

先看最基础的东西。Android里用AudioRecord采集音频,流程其实很固定:

// 1. 配置参数
int sampleRate = 44100;
int channelConfig = AudioFormat.CHANNEL_IN_STEREO;
int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

// 2. 创建AudioRecord实例
AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
        sampleRate, channelConfig, audioFormat, bufferSize);

// 3. 开始录制
recorder.startRecording();

// 4. 循环读取数据
byte[] buffer = new byte[bufferSize];
while (isRecording) {
    int bytesRead = recorder.read(buffer, 0, buffer.length);
    // 这里可以对buffer做效果处理
    processAudioEffect(buffer, bytesRead);
    // 写入文件或发送网络
    fileOutputStream.write(buffer, 0, bytesRead);
}

// 5. 停止释放
recorder.stop();
recorder.release();

嗯,这里要注意一点:getMinBufferSize返回的值不一定是最优的。我在项目中遇到过,某些低端机用最小值会导致频繁的音频断流。我建议你在这个值基础上乘以2或4,留点余量。

核心要点:AudioRecord读取的是原始PCM数据,所有效果处理都要在PCM域完成。一旦编码成AAC或MP3,再处理就晚了。

28.2 实时监听与效果反馈

什么叫实时监听?说白了就是「边录边听」。比如你在K歌软件里唱歌,耳机里能听到带混响的自己声音,这就是实时监听。

实现方式有两种:

  1. 直接路由法:用AudioRecord读数据,直接写到AudioTrack播放。延迟最低,但没法做效果处理。
  2. 处理路由法:读数据→做效果→写AudioTrack。延迟稍高,但能加混响、均衡器等。

我一般用第二种。代码大概长这样:

// 创建AudioTrack用于监听输出
AudioTrack track = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
        sampleRate, AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT, bufferSize,
        AudioTrack.MODE_STREAM);

track.play();

// 录制循环中加入监听
while (isRecording) {
    int bytesRead = recorder.read(buffer, 0, buffer.length);
    
    // 做效果处理(比如加个简单的混响)
    applyReverb(buffer, bytesRead);
    
    // 写入文件
    fileOutputStream.write(buffer, 0, bytesRead);
    
    // 同时送到监听输出
    track.write(buffer, 0, bytesRead);
}
避坑指南:我曾经在实时监听里直接用同一个buffer做读写,结果出现了严重的回声。后来改成双buffer交替使用,问题就解决了。记住:读和写不要用同一块内存。

28.3 回声消除与噪声抑制

这部分是录制效果里最头疼的。回声怎么来的?你想想看,扬声器放出来的声音又被麦克风收回去,再传给对方,对方就听到了自己的回声。噪声就更不用说了,风扇声、空调声、马路声,全进来了。

Android提供了两种处理方式:

方式 API 适用场景 优缺点
硬件AEC MediaRecorder.AudioSource.VOICE_COMMUNICATION VoIP、视频通话 系统自带,延迟低,但效果因厂商而异
软件AEC WebRTC AEC模块 自定义录制、直播 可控性强,但需要集成第三方库

我个人更推荐用WebRTC的方案。为什么?因为硬件AEC在不同手机上表现天差地别。我记得有一次测试,某款手机用VOICE_COMMUNICATION源录制,回声是没了,但声音变得像在水缸里说话一样。后来换成WebRTC的AEC,调了几个参数就好了。

WebRTC的AEC集成大概是这样:

// 初始化WebRTC音频处理模块
AudioProcessing ap = AudioProcessing.create();
ap.setExtCaptureSampleRate(sampleRate);
ap.setExtRenderSampleRate(sampleRate);

// 配置回声消除
ap.echo_cancellation().setEnable(true);
ap.echo_cancellation().setSuppressionLevel(EchoCancellation.SuppressionLevel.HIGH);

// 配置噪声抑制
ap.noise_suppression().setEnable(true);
ap.noise_suppression().setLevel(NoiseSuppression.Level.HIGH);

// 在录制循环中处理
while (isRecording) {
    // 读取麦克风数据
    recorder.read(captureBuffer, 0, captureBuffer.length);
    
    // 读取扬声器参考数据(用于回声消除)
    // 这里需要从AudioTrack的回调中获取renderBuffer
    
    // 处理音频
    ap.processStream(captureBuffer, renderBuffer);
    
    // 处理后的数据在captureBuffer中
    fileOutputStream.write(captureBuffer, 0, captureBuffer.length);
}
重要提醒:WebRTC的AEC需要同时提供麦克风输入和扬声器参考信号。如果你只给了麦克风数据,没给参考信号,回声消除是无效的。这个坑我踩过,调试了两天才发现是参考信号没接对。

28.4 知识体系总览

说了这么多,我们来画个图,把整个录制效果框架串起来。这样你脑子里就有个全局概念了。

录制与效果处理知识体系 麦克风输入 AudioRecord采集 效果处理 混响 / 均衡器 / 变声 实时监听反馈 PCM域处理 环境优化 回声消除 (AEC) 噪声抑制 (NS) 自动增益控制 (AGC) 编码 / 存储 / 发送

从这张图你能看到,整个流程是串行的:麦克风→AudioRecord→效果处理+环境优化→输出。其中效果处理和环境优化是并行的,可以同时做。我个人习惯先做环境优化(降噪、消回声),再做效果处理(混响、变声),这样效果更干净。

28.5 实战中的几个关键点

  • 延迟控制:实时监听最怕延迟。我建议buffer size不要超过100ms的数据量,否则人耳能明显感觉到「慢半拍」。
  • 线程模型:AudioRecord的读取和AudioTrack的写入要在不同线程。我曾经图省事放一个线程里,结果卡得不行。
  • 参数调优:回声消除的抑制级别别拉太高。HIGH级别虽然消得干净,但容易把正常语音也削掉。我一般用MODERATE。
  • 兼容性测试:不同手机的麦克风灵敏度、扬声器音量都不一样。建议在设置里加个「回声测试」功能,让用户自己调。
我的小经验:如果你做的是K歌类应用,实时监听里加个「湿声比例」调节。干声(原始声音)和湿声(效果处理后的声音)混合比例可调,用户会更喜欢。这个功能我加完之后,用户反馈明显好了很多。

好了,这一章的内容就到这里。录制效果处理说难不难,说简单也不简单,关键是把基础流程走通,再根据场景选对方案。下一章我们会聊更高级的音频处理技巧,到时候再见。


公众号:蓝海资料掘金营,微信deep3321