RNNoise:极小体积的实时降噪 baseline

RNNoiseXiph.Orglibopus / Vorbis 的娘家)2017 年发布的实时降噪开源实现 · 作者 Jean-Marc Valin。跟主流大模型 + 端到端路线不同 · RNNoise 是混合 DSP + 深度学习 :22 个 Bark 感知带的 gain 用 3 层 GRU 预测 · pitch filter 单独用传统 comb 滤波做谐波间去噪 · 一整个网络只有 215 单元 · 约 88K 参数 · 编译后 100 KB 量级。本文在同一套 5 case benchmark 上跑一遍 · 定位是最小可用降噪 baseline · 而不是 SOTA。

一、RNNoise 是什么

  • 代码 & 权重 https://github.com/xiph/rnnoise · BSD 3-Clause · 纯 C runtime · 可编译成单个动态库 .so / .dll / .dylib · 权重 hard-code 到源码里(约 88 K 参数)· 无需外部下载
  • 采样率 48 kHz 单声道 int16 · 帧长固定 480 samples · 10 ms
  • 训练目标 22 个 Bark 感知带的 ideal gain(按能量比推出的幅度增益)+ 一个辅助 VAD 输出
  • 训练 loss 对 gain 做感知压缩 γ=1/2 后计算 MSE:L(g, ĝ) = (g^0.5 - ĝ^0.5)² · 避免大幅 gain 主导 loss
  • 训练数据 McGill TSP + NTT 多语种 speech(6 h)+ 环境噪声(4 h)· 随机 EQ / SNR 混合展开到 140 h · 小数据也能训动
  • 延迟 20 ms 分析窗 + 50% overlap · 不需要 look-ahead · 算法延迟 10 ms
  • CPU 开销 论文口径单线程 x86 CPU ~0.14% 单核占用

它是 speech-enhancement 模型 · 主要适合稳态 / 低结构背景噪声 :22 个 Bark band gain 只能整体压 / 保 · 拿不到分离流。信号处理层面 · 主干抑制由 22 个 Bark band gain 完成 · 另有基于 pitch 的传统 comb 滤波处理谐波间残留 ------ 这是它能压到 88 K 参数的关键、也是它对复杂结构化噪声(键盘瞬态 / babble / 音乐)表达力有限的根源。Bark 与 ERB 都是感知频率尺度但分带定义不同------后文 PercepNet 从 22 Bark → 30 ERB 不只是数量升级 · 是换了一套听觉尺度。

演进 :作者 Valin 后来出了 PercepNet (2020 · https://arxiv.org/abs/2008.04259) 加 pitch-aware 后置滤波 · 30 ERB 带 · 复杂度更高;再往后 LACE / NoLACE 迭代到 2023-2024。RNNoise 本身在开源侧仍是最小可行降噪的经典 baseline · PipeWire / WebRTC / Discord noise gate 类实时管线里最常见的开源实现之一。

RNNoise 的整体处理管线(DSP 主体 + RNN 只出 gain):

42 维手工特征 → 88 K RNN → 22 band gain + 1 VAD · 跟现代 SE 的输入 / 输出流最大的差别一张图看清:

核心机制

  • 42 个手工输入特征22 BFCC + 6 Δ + 6 ΔΔ + 6 pitch-correlation DCT + 1 pitch period + 1 spectral variability = 42 ------ 全是 30 年信号处理沉淀下来的物理特征 · 不学 raw spectrum · 不学 mel。这是它体积能压到 88K 参数的关键
  • 网络结构 · 3 GRU + 分支输出 · Dense tanh(24) → GRU ReLU(24) → Dense sig(1) 作为 VAD 输出 · 同时输入通过 GRU ReLU(48) 做噪声谱估计 → GRU ReLU(96) 做谱减法 → Dense sig(22) 输出 22 个 Bark band gain · 总共 4 hidden layer · 215 units · 88K params
  • 两条互补分支 · RNN 分支 估计 22 个 Bark band gain 处理频谱包络;Pitch 分支 (传统 pitch analysis + comb filter · 无神经网络参与)单独处理谐波间 inter-harmonic 的残留噪声 ------ 只对语音 pitch 附近的能量做谐波间衰减 · 最后在频域合成。不是端到端网络输出 waveform · 是 RNN 只估 22 band gain · 加上传统 pitch DSP 一起在频域耦合输出
  • VAD 辅助头 · 只用 24 additional weights · 作为训练期辅助任务 (training-time auxiliary loss)------ 让共享的中间 GRU 表示同时学到 speech vs noise 二分类 · 可能起正则化 / 表示学习作用;推理输出 speech_prob 可顺手拿来做粗门控(需要按业务阈值校准)
  • 辅助 loss :VAD 用交叉熵 · gain 用感知加权 MSE (g^0.5 - ĝ^0.5)² · noise-only 段和高频段 gain 标为 "undefined" 不参与 loss ------ 避免网络过度压制

为什么不直接用 STFT + GRU + 复数 mask:这是 2020 年之后的现代 SE 主流路线(DFN / DCCRN / FRCRN 都走这条)· 而 RNNoise 是 2017 年 · 处在深度学习刚接入 SE 的早期阶段。两条路线的对照:

维度 RNNoise(手工特征 + Bark gain) 现代 SE(STFT / ERB 特征 + 网络学 mask / filter)
输入表达力 42 个物理特征 · 强先验 · 但表达上界低 · 音乐 / 键盘瞬态 / babble 直接翻车 完整复数 STFT 或 ERB 特征 · 网络自己学更高粒度表示
输出粒度 22 个 Bark band 实数 gain · 每帧只 22 个自由度 STFT 频率维几百点 · 复数 mask(cIRM)或多帧复数 FIR(DF)· 表达粒度高一到两个数量级
相位处理 RNN 只学 22 个幅度 gain · 相位不由网络重建;系统另有传统 pitch comb filter 会改动谐波间频谱细节 · 但不显式建模相位 网络显式学复数 mask 或多帧 FIR · 同时估计幅度和相位 · 高 SNR 段相位重建带来额外收益
训练数据 140 h 展开 · 小数据也能训动 DFN 系列常用 DNS Challenge 大规模 clean speech + noise 混合;FRCRN 见对应篇
本文 5 case OVRL 1.85-3.28 DFN3 2.05-3.41 · FRCRN 2.76-3.47

RNNoise 至今没被淘汰的原因 只有一个 ------ 88 K 参数 · 100 KB 权重 · <1% 单核 CPU · 10 ms 延迟 。这些指标在本文比较的现代 SE baseline 里做不到 :DFN3 已经算轻了也要 2.3 M 参数 / 10 MB 权重 / 40 ms 延迟(参数量约 26× RNNoise · 权重体积约 100× );FRCRN 6.9 M 参数 / 55 MB 权重 / 30 ms(参数量约 78× RNNoise · 权重体积约 550× )。到嵌入式(几十 KB 权重 · 几百 KFLOPs · <20 ms 延迟)这一档 · 本文比较的 4 个模型里只有 RNNoise 能跑 (更小的方案可能存在于闭源商用 SDK 或传统 DSP · 本文不覆盖)。所以 RNNoise 是极致约束下的最优解 · 不是通用最优 ------ 大多数 RTC / 服务器场景(几 MB 权重能接受 · 40 ms 延迟能接受)· 可以优先考虑 STFT / ERB 特征 + 网络学 mask / filter 这条路线。

二、Python 与 CLI 两种用法

安装(命令示意 · pip 包装了 C 实现的 ctypes wrapper):

复制代码
pip install pyrnnoise

方式 1 · CLIrnnoise_demo 是原始 C repo 提供的命令行 · 读 raw pcm 输出 raw pcm · 需要用户自己 wrap 上 WAV header):

复制代码
# 从原始 C repo 编译 · 输入输出都是 48 kHz mono 16-bit LE PCM
./rnnoise_demo in.raw out.raw

pip 装完的 pyrnnoise 提供的 CLI 是 Python 封装 · 但底层依赖 audiolab 有版本冲突(本文实测 numpy 2.x 下 av.option API 不兼容)· 落地不建议走 CLI · 直接调 Python。

方式 2 · Python API (本文实测走这条 · 通过原始 ctypes 绕过 pyrnnoise 上层的 audiolab 依赖 · 用的是 pyrnnoise==<版本> 内部 wrapper · 不是公开稳定 API · 跨版本可能失效):

复制代码
import importlib.util
import numpy as np
import soundfile as sf
import librosa

# 直接 load ctypes wrapper · 跳过依赖有问题的 __init__.py
spec = importlib.util.spec_from_file_location(
    "rn", "/path/to/site-packages/pyrnnoise/rnnoise.py"
)
rn = importlib.util.module_from_spec(spec); spec.loader.exec_module(rn)

FRAME = rn.FRAME_SIZE      # 480 · 10 ms @ 48 kHz
SR    = rn.SAMPLE_RATE     # 48000

audio, sr = sf.read("audio.wav", dtype="float32", always_2d=True)
audio = audio.mean(-1)                             # 混单声道
if sr != SR:
    audio = librosa.resample(audio, orig_sr=sr, target_sr=SR)
audio_i16 = (audio * 32767).clip(-32768, 32767).astype(np.int16)

state = rn.create()
out = []
for i in range(len(audio_i16) // FRAME):
    frame = audio_i16[i*FRAME:(i+1)*FRAME]
    proc, speech_prob = rn.process_mono_frame(state, frame)
    out.append(proc)
rn.destroy(state)

denoised = np.concatenate(out).astype(np.float32) / 32767.0
sf.write("out.wav", denoised, SR)

几点工程侧要点

  • 10 ms 单帧处理 · 天然流式 · 每个 frame 是 480 samples · 逐 frame 前向 · 中间维护 GRU hidden state · 天然是 online causal · 算法延迟 10 ms(20 ms 分析窗 - 10 ms hop = 10 ms 一进一出的延迟)· 助听器 / 双向 walkie-talkie 这类 <20 ms 硬约束场景合适
  • 输出附带 VAD 概率 · 每帧 speech_prob0, 1 · 可做粗门控(比如触发下游 ASR)· 但需要按业务阈值校准 · 本文 case02 白噪场景实测均值 0.674 · 存在误判倾向 · 别当精确 VAD 用
  • 无 GPU · 无 Python torch 依赖 · 底层 .so 是纯 C · 一个 20 s 音频本文实测远快于实时(Python 端到端 · 含 wrapper 开销 · 未严格 profile)· 部署侧只需要 100 KB 权重 + libc
  • 对训练分布外的噪声几乎没有泛化 · 22 Bark band gain 表达能力有限 · 遇到复杂结构化噪声(比如键盘瞬态 / babble)会显著降级

三、5 条 case · 降噪前后

沿用同一套 benchmark(benchmark/*.wav)· 同一套 DNSMOS 打分口径。注意 RNNoise 输出是 48 kHz · DNSMOS 计算前 downsample 到 16 kHz。

RNNoise 是单路输出 SE 模型------只出一路 denoised speech · 没有分离出的 no_speech / noise stem。所以下面每条 case 只放「降噪前 / 降噪后」两路对比 · 频谱图也是两栏。

# case(场景) DNSMOS OVRL 前 → 后 BAK 前 → 后 判决
01 加州旅馆(音乐 BGM) 1.12 → 1.85 (+0.73) 1.14 → 3.25 (+2.11) ✅ 大幅提升 · 音乐部分有明显压制痕迹
02 白噪(稳态宽带) 2.10 → 3.28 (+1.18) 2.01 → 4.09 (+2.08) ✅ 大幅提升 · 稳态噪声主场
03 键盘打字(瞬态噪声) 2.17 → 3.18 (+1.01) 1.90 → 4.06 (+2.16) ⚠️ BAK 高 · 但键盘瞬态有可听残留
04 会议室(室内环境混合) 2.12 → 2.97 (+0.85) 2.14 → 3.97 (+1.83) ✅ 明显提升
05 餐厅 babble(多说话人混叠) 2.17 → 2.75 (+0.58) 2.19 → 3.88 (+1.69) ⚠️ BAK 提升是把 babble 压掉 · 目标说话人不保证

一句话结论 :5 条 case 里 5 条 BAK 都有 +1.7 到 +2.2 分的显著提升 · 后值全部落在 3.2-4.1 区间 ------ 对一个 88 K 参数 · 100 KB 编译体积的模型来说是不错的表现。case01 音乐场景压制过头 · 副歌段人声被削得很薄;case03 键盘瞬态处理不干净 · 频谱能看到打字瞬间的残留 ------ 22 Bark band gain 对亚帧级瞬态响应慢。case05 餐厅 babble 场景不真正分离说话人 · 而是按能量把非主导说话人一起压掉。

3.1 case01 · 加州旅馆 · BGM + vocals 混合

降噪前

降噪后

听感:intro 段前奏 + 引入的 vocals 一起被大量削减 · 语音剪切严重、能量断续 ------ 22 Bark band 把整段音乐当作稳态噪声压 · 副歌人声再度进来时也没有恢复。RNNoise 训练分布里没有音乐 · 遇到 non-speech 结构化谐波会激进压制。

3.2 case02 · 白噪 · 稳态宽带噪声

降噪前

降噪后

看点:宽带底噪整体下移 · speech 谐波保留清晰 · 稳态噪声是 22 Bark band gain 的舒适区。

3.3 case03 · 键盘打字 · 瞬态噪声

降噪前

降噪后

看点 :键盘敲击的宽带瞬态被削弱但仍有可闻残留------10 ms 帧 + Bark band gain 对亚帧级瞬态响应慢 · 频谱图上打字瞬间的短竖线没完全压干净。

直观看 Bark gain 的时变模式玩具数据示意 · 不来自 RNNoise 内部 trace · 颜色和数字都只是为了展示可能模式 )------ 网络对 speech 段通常保留、对 pause 段大幅压低、遇到瞬态噪声时高频压得比低频狠 · VAD 副产物在瞬态段可能被误判为中等 probability。要拿到真实 gain 数值需要 hook xiph/rnnoise C repo 的 compute_rnn() 中间层 · 本文不覆盖:

3.4 case04 · 会议室 · 室内环境混合

降噪前

降噪后

看点:低频环境噪底 + 空调 hum 明显下降 · speech 保留清晰 · 环境类近稳态噪声在 RNNoise 上也有效。

3.5 case05 · 餐厅 babble · 多说话人混叠

降噪前

降噪后

看点 :主导说话人保留 · 但 babble 压得不干净(Bark band gain 对多个说话人混叠的谐波结构分辨力有限)· 目标说话人不占优时会翻车。

四、四模型对比 · RNNoise 适合什么场景

下面是全系列 4 篇公用的一张 DNSMOS 对比图 · 5 case × 4 模型(htdemucs / DFN3 / RNNoise / FRCRN)· 每篇都放这张 · 别的模型的具体行为详见对应篇。

RNNoise 在这套 benchmark 上的落点

  • case02 白噪 · OVRL 3.28 · case03 键盘 · 3.18 · case04 会议室 · 2.97 ------ 3 条稳态 / 瞬态 / 环境 case 都在 3.0 附近或以上 · 是 4 个模型里最「够用」的最小方案
  • case05 餐厅 babble · OVRL 2.75 ------ 能压 babble 但 BAK 3.88 比 DFN3/FRCRN 略低 · 按能量选主
  • case01 音乐 BGM · OVRL 1.85 ------ 4 个模型里最低 · 22 Bark band 表达力不足以处理音乐 · 副歌段压制过头
  • 整体分数 普遍比 DFN3 / FRCRN 略低 ------ 但参数量差 25×(88K vs 2.3M / 6.9M)· 权重体积差 100×(100 KB vs 10 MB / 55 MB)

RNNoise 适合

  • 端侧极致轻量场景 ------ <100 KB weights · <1% CPU · <20 ms latency · 视频会议 / 通话 / WebRTC / Discord noise gate / 麦克风驱动层至今常见默认
  • 助听器 / 双向 walkie-talkie 这类 <20 ms 硬约束场景 ------ 10 ms 算法延迟 · 是 4 个模型里唯一能进这个区间的
  • 稳态背景噪声主场 ------ case02 白噪 / case04 会议室这类环境噪声上分数够用
  • 需要免费 VAD 的场景 ------ 每帧 speech_prob 拿来做粗粒度门控

RNNoise 不适合

  • 音乐场景剥人声 ------ case01 分数明显低 · 副歌段压制过头
  • 瞬态噪声(键盘 / 敲击 · 见 case03 频谱残留)· 10 ms 帧 + Bark band gain 对亚帧级瞬态响应慢
  • 听感极致自然 ------ 22 Bark band 硬拉低会出「频谱空洞感」
  • 多说话人 babble 要指定目标 ------ 按能量选主 · 需要 TSE 类

五、写在最后

RNNoise 是小模型也能做实用降噪的经典证明 ------ 用 30 年 DSP 沉淀(Bark scale / BFCC / pitch analysis / comb filter)打底 · 只用 GRU 学最难手工调的那部分(每帧 22 个 band 该给多少 gain)· 换来 100 KB 的编译体积和 10 ms 延迟。这是端侧 real-time 场景(视频会议 · 通话 · WebRTC · Discord noise gate · 麦克风驱动层)至今仍常见的默认选择。

工程侧几个提醒:RNNoise 输出的每帧 speech_prob 是免费的 VAD (case02 白噪场景本文实测均值 0.674 · 说明网络在纯噪声段也倾向于误判为 speech ------ VAD 输出适合做「有语音时激活下游 ASR」这类粗粒度门控 · 别当作精确 VAD 用)。观察都基于 5 条样本 · 落地前用自己业务的一批 case 验证

相关推荐
2601_958352901 个月前
AU-48 双模拟麦多功能语音处理模组:一颗芯片搞定降噪、消回音、USB 通话全场景
语音识别·降噪·回音消除·通话对讲
2601_958352902 个月前
双麦 DSP 音频拾音模块 A-68:多场景远场语音交互的声学解决方案
嵌入式硬件·音视频·降噪·回音消除·音频处理模块
2601_958352903 个月前
手撕环境噪音:双麦降噪模块AN-93上板实测,降噪36dB是真是假?
人工智能·音视频·嵌入式·降噪
一勺汤4 个月前
YOLO26 改进、魔改| 空间-通道特征调制器SCFM,通过空间与通道双维度注意力调制,融合全局与局部特征,提升视觉任务的特征质量与模型效果。
yolo·注意力机制·降噪·小目标·yolo26·yolo26改进
胡耀超1 年前
音频降噪技术:从原理到工具的完整指南(scipy librosa noisereduce soundfile pedalboard)
音视频·音频·scipy·降噪·soundfile·noisereduce·pedalboard
地狱为王1 年前
在Unity中实现DTLN-AEC处理音频文件的功能
unity·aec·降噪
zhuweisky1 年前
C#实现语音预处理:降噪、静音检测、自动增益(附Demo源码)
vad·静音检测·降噪·agc·语音活动检测·自动增益
lulinhao1 年前
优化WAV音频文件
wav·降噪
阿赵3D2 年前
图片预处理技术介绍4——降噪
图像处理·1024程序员节·降噪