[旧日谈] 再考 IIR 与 FIR 滤波器对相位影响的定量分析

IIR 与 FIR 濾波器对音频相位的影响 先前我有写过一个简单的文章分析过两种滤波器对音频相位的影响,但是我只是知其然不知其所以然。对于音频,我虽然知道相位是一个很重要的概念,但是我始终不知道相位对实际音频的印象是什么水平的。这个问题在这些年的开发过程中始终萦绕在心头。虽然不做音频了,但是我仍然对这个问题保持好奇,综上,这也是为什么有了这个文章。 一、从一个问题开始 假设我们有一个 1kHz 的正弦信号,经过一个低通滤波器之后,输出还是 1kHz 的正弦信号,幅度变小了——这很好理解,滤波器嘛,该衰减的衰减。 但仔细看输出波形,会发现它相对于输入信号产生了一个时间上的延迟。这个延迟不是简单的"整体往后挪了 N 个采样点",而是不同频率的信号延迟不一样。 1kHz 的信号延迟了 0.5ms,500Hz 的信号延迟了 0.8ms,2kHz 的信号延迟了 0.3ms——每个频率成分的延迟都不一样。 这就是相位失真。 对于音频处理来说,这个问题比听起来严重得多。人耳对相位差的感知不如幅度那么直接,但当不同频率成分的延迟差异大到一定程度时,会导致: 瞬态信号(比如鼓点、齿音)的波形被"模糊化" 立体声声像偏移 某些频段的"堆叠"或"空洞" 所以,理解滤波器的相位特性,是做音频处理的基本功。 先说结论,IIR 的相位响应受幅度响应约束(最小相位特性),无法独立控制;FIR 可以独立控制幅度和相位,因此能实现线性相位或任意指定相位。 但是至于为什么音频行业常用IIR滤波器,这个问题我将在补充后说明。 二、先回顾一下:FIR 和 IIR 是什么 FIR(有限脉冲响应) FIR 滤波器的差分方程: $$y[n] = \sum_{k=0}^{M} b_k , x[n-k]$$ 输出只依赖于当前和过去的输入,没有反馈。脉冲响应是有限长的(长度 M+1)。 IIR(无限脉冲响应) IIR 滤波器的差分方程: $$y[n] = \sum_{k=0}^{M} b_k , x[n-k] - \sum_{k=1}^{N} a_k , y[n-k]$$ 输出同时依赖于输入和过去的输出(反馈)。脉冲响应理论上是无限长的。 两者的核心区别在于有没有反馈。这个结构上的差异,直接决定了它们的相位特性。 三、相位响应的推导 从频率响应说起 一个 LTI(线性时不变)系统的频率响应可以写成: ...

June 18, 2026 · 17 min · Leventure

NSF-HiFiGAN-声码器学习笔记

从 HiFi-GAN 到 NSF-HiFi-GAN:声码器学习笔记 本文基于 RVC(Retrieval-based Voice Conversion)项目的实际代码,从零开始梳理 HiFi-GAN 声码器的原理,再过渡到 RVC 中真正使用的 NSF-HiFi-GAN 变体。 代码位置:infer/lib/infer_pack/models.py 和 infer/lib/infer_pack/modules.py 一、先搞清楚声码器在干什么 在语音合成或语音转换的流程里,声码器处在最后一环。它的上游会输出某种"中间表示"——可能是 mel 频谱图,也可能是某个隐空间的向量。声码器要做的事情就一件:把这个中间表示变回可以听的音频波形。 说得直白点:频谱图是一张"图",声码器要把这张图"念"出来。 传统做法(Griffin-Lim 之类的)靠数学迭代来恢复相位信息,结果通常比较糊。HiFi-GAN 走的是神经网络的路线——用一个生成器直接输出波形采样点,同时用判别器来监督生成质量。 二、HiFi-GAN 的基本结构 HiFi-GAN 的论文是 2020 年发的(Jungil Kong 等人),核心思路可以用一句话概括: 转置卷积做上采样,残差块做波形精炼,多尺度判别器做质量监督。 2.1 生成器的整体流程 在 RVC 的代码里,标准 HiFi-GAN 生成器对应的是 Generator 类(models.py:204)。它的结构其实很规整: HiFi-GAN 生成器结构(40kHz 配置为例) ┌─────────────────────────────────────────────────────────────┐ │ 输入:隐变量 z │ │ [batch, 192, T帧] │ └────────────────────┬────────────────────────────────────────┘ │ ▼ ┌────────────────────┐ │ 预处理卷积层 │ │ Conv1d(192→512) │ ← 把输入投射到高维空间 │ kernel_size=7 │ └────────┬───────────┘ │ ┌────────────┴────────────┐ │ 上采样 Stage 1 (×10) │ │ ConvTranspose1d(512→256)│ ← 时间轴拉长到 T×10 └────────┬─────────────────┘ │ ┌────────┴──────────────────────────────────┐ │ MRF (多感受野融合) │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐│ │ │ResBlock │ │ResBlock │ │ResBlock ││ │ │kernel=3 │ │kernel=7 │ │kernel=11 ││ │ └──────────┘ └──────────┘ └──────────┘│ │ 输出取平均 │ └────────┬──────────────────────────────────┘ │ ┌────────┴────────────┐ │ 上采样 Stage 2 (×10)│ │ ConvTranspose1d(256→128)│ ← 时间轴拉长到 T×100 └────────┬─────────────┘ │ ┌────────┴──────────────────────────────────┐ │ MRF 融合 │ │ ResBlock×3 (kernel=3/7/11) → 平均 │ └────────┬──────────────────────────────────┘ │ ┌────────┴────────────┐ │ 上采样 Stage 3 (×2) │ │ ConvTranspose1d(128→64)│ ← 时间轴拉长到 T×200 └────────┬─────────────┘ │ ┌────────┴──────────────────────────────────┐ │ MRF 融合 │ │ ResBlock×3 (kernel=3/7/11) → 平均 │ └────────┬──────────────────────────────────┘ │ ┌────────┴────────────┐ │ 上采样 Stage 4 (×2) │ │ ConvTranspose1d(64→32) │ ← 时间轴拉长到 T×400 └────────┬─────────────┘ │ ┌────────┴──────────────────────────────────┐ │ MRF 融合 │ │ ResBlock×3 (kernel=3/7/11) → 平均 │ └────────┬──────────────────────────────────┘ │ ┌────┴────────────┐ │ 后处理卷积层 │ │ Conv1d(32→1) │ ← 压缩到单声道 │ kernel_size=7 │ └────┬─────────────┘ │ ▼ ┌────────────┐ │ Tanh │ ← 限幅到 [-1, 1] └─────┬──────┘ │ ▼ ┌──────────────────┐ │ 输出波形 │ │ [batch, 1, T×400]│ └──────────────────┘ 上采样的总倍率 = 10 × 10 × 2 × 2 = 400。这个数字等于 hop_length(帧移),含义是:输入的每一帧对应输出的 400 个采样点。对于 40kHz 采样率来说,一帧就是 10ms。 ...

February 14, 2026 · 13 min · Konpaku Youran

rvc结构简介

RVC结构简介 推理流程 输入音频 (16kHz) │ ▼ ┌─────────────────┐ │ HuBERT │ 提取内容特征,输出256维(v1)或768维(v2) └────────┬────────┘ │ ▼ ▼ ┌─────────────────┐ │ F0 Extractor │ 提取基频,支持RMVPE/CREPE/Harvest/PM └────────┬────────┘ │ ▼ ▼ ┌─────────────────┐ │ Index Search │ 可选,用faiss做音色检索,混合特征 └────────┬────────┘ │ ▼ ▼ ┌─────────────────┐ │ Synthesizer │ VITS架构,生成目标音色波形 └────────┬────────┘ │ ▼ 输出音频 (32k/40k/48k) Synthesizer结构 主类是SynthesizerTrnMs256NSFsid(v1)和SynthesizerTrnMs768NSFsid(v2),区别只在输入维度。 SynthesizerTrnMs*NSFsid ├── enc_p (TextEncoder) │ ├── emb_phone: Linear(256/768 → hidden) │ ├── emb_pitch: Embedding(256, hidden) # pitch量化到256级 │ ├── encoder: Transformer Encoder │ └── proj: Conv1d → (mean, log_var) │ ├── flow (ResidualCouplingBlock) │ └── 4层 ResidualCouplingLayer + Flip │ 每层内部是WaveNet结构 │ ├── dec (GeneratorNSF) │ ├── m_source (SourceModuleHnNSF) │ │ └── SineGen: 根据F0生成正弦激励信号 │ ├── conv_pre │ ├── ups: 多级上采样 (ConvTranspose1d) │ ├── resblocks: HiFiGAN残差块 │ └── conv_post │ └── emb_g: Embedding(spk_num, gin_channels) # speaker embedding 推理时的数据流: ...

February 14, 2026 · 2 min · Konpaku Youran

GTCRN 轻量化的流式方案的演进思路

GTCRN 演进路径 记录 v1 → v2 → v3 → v3.1/v3.2 → v4 → v4.1 的改动和原因。 版本概览 版本 改动点 参数量 质量指标 内存 实时 v1 baseline 基线 139K DNSMOS 3.15 — × v2 transient 换损失函数 139K DNSMOS 3.15 — × v3 causal 因果化改造 145K DNSMOS 2.98 — √ v3.1 precision KD + QAT 压缩 41.6K PESQ 2.041 228 KB (INT8) √ v3.2 transient 宽度1.5× + 瞬态损失 ~83K PESQ ~2.15 ~355 KB (INT8) √ v4 network opt 架构精简 (4层GTConv) ~87K PESQ 2.147 683 KB (FP32) √ v4.1 int8 INT8 混合精度 C 推理 ~87K PESQ 2.037 464 KB √ 网络结构 (v1/v2 共用) 输入 spec (B, 513, T, 2) │ ├─ 可学习频带权重 (513,) │ ▼ ERB_48k.bm(): 513 → 219 │ 低频171保留,高频342→48 ERB band │ ▼ SFE_Lite: DWConv(1×5) → PWConv → BN │ ▼ ┌─ Encoder ─────────────────────────────┐ │ DSConv: 219→110 (stride=2) ← skip1 │ │ DSConv: 110→55 (stride=2) ← skip2 │ │ GTConvLite×6 (d=1,2,4,8,4,2) ← skip3-8 │ SubbandAttention │ └───────────────────────────────────────┘ │ ▼ DPGRNN_Enhanced × 2 │ intra: 双向GRU (频率轴) │ inter: 单向GRU (时间轴) │ ▼ ┌─ Decoder ─────────────────────────────┐ │ GTConvLite×6 + skip (逆序) │ │ DSDeconv: 55→110 + skip2 │ │ DSDeconv: 110→219 + skip1 │ └───────────────────────────────────────┘ │ ▼ ERB_48k.bs(): 219 → 513 │ ▼ CRM掩码 → 输出 GTConvLite 内部 x → DWConv(3×3, dilation) → PWConv → BN → PReLU → TRALite (时序注意力) → SEBlock (通道注意力) → + x (残差) DPGRNN 内部 x (B,C,T,F) → reshape (B*T, F, C) → Linear → 双向GRU (频率轴) → Linear → reshape + LayerNorm → reshape (B*F, T, C) → Linear → 单向GRU (时间轴) → Linear → reshape + LayerNorm → 输出 v1 → v2: 换损失函数 问题 v1 用的是标准 SpecRIMAGLoss,对所有帧一视同仁。但实际听感上,键盘敲击、鼠标点击这类突发噪音处理得不好。DNSMOS 是整段平均,掩盖了这个问题。 ...

February 13, 2026 · 7 min · Konpaku Youran

GTCRN轻量化方案

GTCRN-Light v3 技术说明书 0. 扼要(Executive Summary) GTCRN-Light v3(以下简称 v3)是在原生 GTRCN 基础上进行的等价轻量化实现:完整保留“ERB→SFE→Encoder(频轴两次 /2)→DPGRNN(intra→inter)→Decoder(镜像+跳连)→ERB⁻¹→复域 CRM”的主干数据流与功能语义,通过算子级设计收缩参数与 MACs,同时增强形状稳定性与工程可部署性。 核心收益: 结构等价:无语义重构、无路径删减;对齐原版的时/频建模顺序与接口。 计算瘦身:卷积 DW-Separable 化、RNN 低秩瓶颈、门控去 RNN 化、ERB 固定权重化。 工程稳态:严格的频轴上/下采样闭环(33→65→129),对齐安全,易于导出与部署。 1. 设计目标与边界(Design Goals & Constraints) 不改变 GTRCN 的任务假设与编解码语义:复域 CRM、ERB 子带、频轴二次下采样、DPGRNN(先 intra 后 inter)、镜像解码与跳连。 降低参数与 MACs,但不牺牲 DPGRNN 的双路径长程/跨频建模。 形状稳定:频轴整数对齐,杜绝奇偶差累积;跳连前天然同维。 部署友好:避免难以量化/导出的算子(极小化状态化 RNN、减少不必要的线性层)。 2. 与原生 GTRCN 保持一致的“架构不变量” 数据流: (B,F,T,2) → [|S|, Re, Im] → ERB(bm) → SFE → Encoder(freq /2 ×2) → DPGRNN(intra→inter) → Decoder → ERB(bs) → CRM × S(复域) 采样策略:ERB 后 F=129;编码两次在频轴 /2:129→65→33;解码反向:33→65→129(确保 33→65→129 的闭环)。 时/频耦合:瓶颈处严格遵循 intra-(per time, across F) → inter-(per freq, across T) 的双路径顺序。 输出语义:预测 CRM(实/虚) 并在复域与输入逐点相乘。 3. 轻量化的四大支柱(Pillars of Lightweighting) 3.1 卷积主干 DW-Separable 化 + 轻量 GT-ConvLite 动机:将 2D 卷积的通道耦合与空间(T/F)卷积解耦,保留感受野与局部子带建模能力的同时,将参数与 MACs 近似按 1/通道数 降低。 ...

February 13, 2026 · 3 min · Konpaku Youran