4、音频采集:音频约束详解(sampleRate、channelCount、echoCancellation)、获取麦克风音频流。

好,咱们接着聊音频采集。上一节我们把摄像头搞定了,画面有了,但光有画面没声音,那叫默片。今天咱们就深入聊聊音频约束,把麦克风的声音也抓进来。

说实话,音频这块儿坑比视频还多。视频顶多是卡顿、模糊,音频要是出了问题,那就是刺啦刺啦的噪音,或者干脆没声。我在项目里就吃过这个亏,后面会跟大家细说。

音频约束的核心参数

调用 getUserMedia 的时候,我们传一个 audio: true 就能拿到音频流。但这样拿到的,是浏览器的「默认配置」。这个默认配置,往往不是最优的。

我们需要用更精细的约束对象来控制它。就像这样:

const constraints = {
    audio: {
        sampleRate: 48000,
        channelCount: 2,
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true
    }
};

这几个参数,我一个个拆开讲。

sampleRate:采样率

采样率,说白了就是一秒钟采集多少个声音样本。单位是 Hz。

常见的值有:

采样率 常见场景 我的建议
8000 Hz 传统电话 别用,音质太差
16000 Hz VoIP、语音通话 够用,省带宽
44100 Hz CD 音质 音乐场景可用
48000 Hz 专业音频、WebRTC 默认 我推荐用这个

为什么我推荐 48000 Hz?因为 WebRTC 内部处理音频时,默认就是 48000 Hz。你设成 16000 Hz,浏览器还得做一次重采样,多一道转换就多一分延迟和损耗。

小技巧: 如果你不确定麦克风支持哪些采样率,可以用 MediaDevices.getSupportedConstraints() 查一下。不过大部分现代设备都支持 48000 Hz。

channelCount:声道数

声道数,就是同时录几个声道。

  • 1(单声道):所有声音混成一个轨道。省带宽,适合语音通话。
  • 2(立体声):左右声道分开。音质好,但数据量翻倍。

我个人习惯,做语音通话用单声道就够了。为什么?因为 WebRTC 的音频处理模块(比如回声消除、降噪)都是基于单声道设计的。你传立体声进去,它反而要混成单声道再处理,多此一举。

但如果你在做音乐直播、远程乐器演奏,那就必须用立体声。这个看场景。

echoCancellation:回声消除

这个参数,我愿称之为「音频采集的灵魂」。

回声是怎么来的?你想想看:对方说话的声音,从你的扬声器放出来,又被你的麦克风录进去,再传回给对方。对方就听到了自己的回声,烦不烦?

echoCancellation: true 就是让浏览器帮你把这个回声干掉。它通过分析扬声器播放的声音和麦克风采集的声音,把重复的部分抵消掉。

注意: 回声消除不是万能的。我曾经在一个项目里,用户用外放音箱开视频会议,回声消除怎么调都没用。最后只能建议用户戴耳机。硬件上的问题,软件有时候真救不了。

另外两个参数:noiseSuppression(降噪)和 autoGainControl(自动增益控制),我一般也设为 true。降噪能滤掉风扇声、键盘声;自动增益能保证你离麦克风远一点时,声音不会太小。

获取麦克风音频流

参数讲完了,咱们写代码。获取音频流和获取视频流,套路是一样的:

async function getAudioStream() {
    try {
        const constraints = {
            audio: {
                sampleRate: 48000,
                channelCount: 1,
                echoCancellation: true,
                noiseSuppression: true,
                autoGainControl: true
            }
        };

        const stream = await navigator.mediaDevices.getUserMedia(constraints);
        console.log('音频流获取成功', stream);

        // 把音频流挂到 audio 元素上播放(本地监听)
        const audioElement = document.querySelector('audio');
        audioElement.srcObject = stream;
        audioElement.play();

        return stream;
    } catch (err) {
        console.error('获取音频流失败:', err);
        // 常见的错误:NotAllowedError(用户拒绝)、NotFoundError(没麦克风)
    }
}

这里有个细节:本地监听。你把麦克风的声音直接播出来,会听到自己的声音,而且可能产生啸叫。所以调试的时候,建议戴耳机。

核心要点: 获取音频流后,记得检查 stream.getAudioTracks() 的长度。如果为 0,说明没拿到音频轨道。我在项目中遇到过,用户系统里明明有麦克风,但浏览器就是识别不到——后来发现是权限被系统安全软件拦截了。

音频约束的整体逻辑

为了让大家更直观地理解,我画了一张图:

音频采集约束核心逻辑 getUserMedia 音频约束参数 sampleRate channelCount echoCancellation noiseSuppression 48000 Hz 推荐 1 或 2 声道 true 必开 true 推荐 MediaStream

从图里能看出来,约束参数就是一道关卡。你设得好,拿到的音频流质量高、延迟低。设得不好,后面做再多的处理也白搭。

避坑指南

最后,分享几个我踩过的坑:

  • 别设死值:比如你设 sampleRate: { exact: 48000 },如果设备不支持,直接报错。用 ideal 或者直接给数值,让浏览器自己选。
  • 检查轨道设置:拿到流之后,用 track.getSettings() 看看浏览器实际用了什么参数。有时候你设了 48000,它给你降到 44100,你都不知道。
  • 移动端注意:手机上的麦克风,采样率可能被系统锁定。我在 Android 上遇到过,设 48000 没用,实际只有 16000。这时候别纠结,能用就行。

嗯,音频约束这块儿就这些。说白了就是几个参数,但每个参数背后都有讲究。你把它调明白了,音频采集就稳了。


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