LHDC V5 算法深度解析

本文深入剖析 LHDC V5 编解码所涉及的核心算法原理。


一、变换编码算法:LD-MDCT

1.1 MDCT 的数学定义

MDCT(Modified Discrete Cosine Transform,改进离散余弦变换)是一种线性正交变换,定义为:

Xk=∑n=0N−1xncos⁡πN(n+N4+12)(k+12),k=0,1,...,N2−1 X_k = \sum_{n=0}^{N-1} x_n \cos\left\\frac{\\pi}{N}\\left(n + \\frac{N}{4} + \\frac{1}{2}\\right)\\left(k + \\frac{1}{2}\\right)\\right, \quad k = 0, 1, \ldots, \frac{N}{2}-1 Xk=n=0∑N−1xncosNπ(n+4N+21)(k+21),k=0,1,...,2N−1

其中:

  • NNN = MDCT 尺寸(LHDC V5 中为 480 / 960 / 1920)
  • xnx_nxn = 输入时域采样(加窗后)
  • XkX_kXk = 输出频域系数(共 N/2N/2N/2 个,临界采样)

关键性质 :MDCT 是临界采样变换(N 点输入 → N/2 点输出),看似有信息损失,但通过 50% 重叠 + 加窗 可以实现完全重构(Perfect Reconstruction),这就是 Princen-Bradley 条件。

1.2 为什么用 MDCT 而不是 DFT/FFT?
特性 DFT/FFT MDCT
输入/输出比 N→N(冗余) N→N/2(临界采样)
块效应 严重(不重叠时) 通过重叠相加消除
频率分辨率 N 个频点 N/2 个频点(足够)
编码效率 低(冗余系数) 高(无冗余)
完全重构 需要冗余 50%重叠+窗即可

MDCT 是几乎所有现代音频编码标准(MP3、AAC、Vorbis、Opus、LDAC、LHDC)的核心变换,因为它在频率分辨率和编码效率之间取得了最佳平衡。

1.3 LD-MDCT(低延迟 MDCT)

LHDC V5 使用的 LD-MDCT(Low-Delay MDCT)与标准 MDCT 的区别:

  • 标准 MDCT:帧长 = N/2 采样,但分析窗覆盖 N 采样(前后各 N/4 重叠),算法延迟 = N/2 采样
  • LD-MDCT:通过特殊的窗函数设计(非对称窗或缩短的前向重叠),可以在保持完全重构的同时降低算法延迟
  • LHDC V5 的 5ms 帧长 + LD-MDCT 使其端到端编码延迟远低于传统 AAC/MP3
1.4 快速 MDCT 算法

直接计算 MDCT 的复杂度为 O(N²),实际中通过 FFT 加速到 O(N log N):

N 点 MDCT → N/4 点复数 FFT 的分解步骤:

  1. 预旋转(Pre-twiddle):将 N 点实输入重新排列并乘以复旋转因子,得到 N/4 点复数序列
  2. N/4 点 FFT:对复数序列执行快速傅里叶变换
  3. 后旋转(Post-twiddle):乘以复旋转因子得到 MDCT 系数
  4. 对称展开:利用 MDCT 的对称性生成完整的 N/2 点输出

LHDC V5 解码器的具体实现lhdc_imdct.c):

  • N = 480 → N/4 = 120 点复数 FFT
    • 120 = 8 × 15 → 基 2 第一级(8 点)+ 15 点第二级
    • 15 点 DFT 通过 3×5 Cooley-Tukey 分解实现
  • N = 960 → 240 = 16 × 15
  • N = 1920 → 480 = 32 × 15

这种混合基分解避免了大素数因子导致的低效 DFT,是实际工程中常用的优化技巧。

1.5 加窗与完全重构

分析窗(编码器)合成窗(解码器) 必须满足 Princen-Bradley 条件:

wa2(n)+ws2(n+N/2)=1,∀n w_a^2(n) + w_s^2(n + N/2) = 1, \quad \forall n wa2(n)+ws2(n+N/2)=1,∀n

当分析窗 = 合成窗时,条件简化为 w2(n)+w2(n+N/2)=1w^2(n) + w^2(n + N/2) = 1w2(n)+w2(n+N/2)=1。

LHDC V5 使用 KBD 窗(Kaiser-Bessel Derived Window)

  • 由 Kaiser-Bessel 窗推导而来,具有良好的频域能量集中性
  • 主瓣窄、旁瓣衰减快,减少频谱泄漏
  • 480 点 KBD 窗预计算为常量表存储在 flash 中,零 RAM 占用

二、感知编码算法:SNS(Spectral Noise Shaping)

2.1 心理声学基础

SNS 的理论基础是人耳的频域掩蔽效应(Frequency-domain Masking):

  • 同时掩蔽(Simultaneous Masking):一个强音(掩蔽音)可以使同时存在的弱音(被掩蔽音)变得听不见
  • 掩蔽阈值(Masking Threshold):在强音频率附近,听阈会被抬高,低于该阈值的声音不可闻
  • 临界频带(Critical Band):人耳的频率分析可以建模为约 24~40 个重叠的带通滤波器(Bark 尺度),每个频带内的掩蔽效应最强

感知编码的核心思想:将量化噪声控制在掩蔽阈值以下,这样噪声虽然客观存在,但主观上不可闻。

2.2 SNS 的算法原理

LHDC V5 的 SNS 不直接计算心理声学模型(如 AAC 中的 PNS/Perceptual Noise Substitution),而是采用一种更简洁的频谱包络整形方法:

步骤 1:子带能量计算

将频谱划分为 MMM 个子带(24 或 32 段,子带边界由 band_off[] 表定义,近似模拟 Bark 尺度),计算每个子带的能量:

Es=∑k∈band s∣Xk∣2×scales E_s = \sum_{k \in \text{band } s} |X_k|^2 \times \text{scale}_s Es=k∈band s∑∣Xk∣2×scales

步骤 2:能量包络平滑

对子带能量进行加权移动平均,得到平滑的频谱包络 PsP_sPs。这模拟了人耳掩蔽效应的扩散性(一个频带的能量会影响相邻频带的掩蔽阈值)。

步骤 3:自适应步长 DPCM 编码(核心)

这是 SNS 最精巧的部分。用一个 ADPCM(Adaptive Differential PCM) 编码器编码频谱包络:

  • 状态变量:state(当前步长索引,0~63),run(连续同方向计数)
  • 步长表:JUMP_TABLE[64],从 6 到 4096 指数增长(近似对数等比)
  • 对每个子带 sss (从第 2 个开始):
    • 计算差分: ds=Ps−P^s−1d_s = P_s - \hat{P}_{s-1}ds=Ps−P^s−1 (实际包络与预测值的差)
    • 方向位: bs=(ds<0)?1:0b_s = (d_s < 0) ? 1 : 0bs=(ds<0)?1:0 (1 bit,编码方向)
    • 状态更新:
      • 如果 bs==bs−1b_s == b_{s-1}bs==bs−1 (同方向):run++state = min(state + run, 63)(步长增大,加速跟踪)
      • 如果 bs≠bs−1b_s \neq b_{s-1}bs=bs−1 (方向翻转):state = (3*state + 2) >> 2run = 1(步长减小,精细调整)
    • 预测更新: P^s=P^s−1+(−1)bs×JUMP_TABLEstate\hat{P}s = \hat{P}{s-1} + (-1)^{b_s} \times \text{JUMP\_TABLEstate}P^s=P^s−1+(−1)bs×JUMP_TABLEstate

只传输 1 bit/子带的方向信息,解码器用完全相同的 ADPCM 状态机重建频谱包络。

步骤 4:后处理

  • 均值移除:消除 DC 偏移
  • 非对称移动平均平滑
  • 钳位到 -2560, 1024,取反

步骤 5:增益应用

将每个子带的缩放因子 sfssf_ssfs 转换为增益 gs=2sfs/16g_s = 2^{sf_s / 16}gs=2sfs/16 (通过查找表),对该子带内所有频谱系数乘以 gsg_sgs 。

2.3 SNS 的本质

SNS 本质上是一种频域预测 + 自适应量化的组合:

  • 用 ADPCM 编码频谱包络(1 bit/子带,极低成本)
  • 用包络信息对频谱进行预加重/去加重(pre-emphasis/de-emphasis)
  • 强能量子带被衰减(降低该子带的信号幅度,从而降低量化噪声的绝对水平,但噪声/信号比不变------因为信号也被衰减了,逆量化后恢复)
  • 弱能量子带被提升到噪声底以上(避免"音洞")

与 AAC 的对比

  • AAC 使用完整的心理声学模型(计算扩散函数、绝对听阈、掩蔽阈值),然后根据阈值分配每个频带的量化步长
  • LHDC V5 的 SNS 更简洁,用 ADPCM 包络编码替代了完整的心理声学模型,计算量更小,适合蓝牙低延迟场景
  • 两者的目标一致:将量化噪声控制在听阈以下

三、量化与码率控制算法

3.1 均匀标量量化

LHDC V5 使用均匀标量量化(Uniform Scalar Quantization):

qk=round(Xk×L) q_k = \text{round}(X_k \times L) qk=round(Xk×L)

其中 LLL 是全局量化步长(由 global_gain 索引通过分段线性函数 e(g)e(g)e(g) 转换为 L=2e(g)L = 2^{e(g)}L=2e(g) )。

为什么用均匀量化而不是非均匀量化(如 μ-law / A-law)?

  • 均匀量化在频域系数上更有效:经过 SNS 整形后,频谱系数的动态范围已经被压缩,均匀量化足够
  • 均匀量化的逆量化简单( Xk=qk/LX_k = q_k / LXk=qk/L ),计算量小
  • 非均匀量化更适合时域信号(如电话语音的 μ-law),因为时域信号的动态范围大且小信号出现概率高
3.2 全局增益的分段线性映射

global_gain(9 bit,0~511)到量化步长指数 eee 的映射是分段线性函数:

e(g)={g,g≤splitsplit+(g−split)×slope,split<g≤503e(503)+(g−504),g>503 e(g) = \begin{cases} g, & g \leq \text{split} \\ \text{split} + (g - \text{split}) \times \text{slope}, & \text{split} < g \leq 503 \\ e(503) + (g - 504), & g > 503 \end{cases} e(g)=⎩ ⎨ ⎧g,split+(g−split)×slope,e(503)+(g−504),g≤splitsplit<g≤503g>503

  • splitslope 由帧长、位深、采样率决定
  • 低索引区(g ≤ split):每档 1 bit(精细调节,用于低码率)
  • 中索引区:斜率 < 1(粗调,用于中高码率,大步进覆盖大范围)
  • 高索引区(g > 503):每档 1 bit,钳位到 30

这种分段设计在有限的 9 bit 索引范围内实现了大范围的步长调节,同时在关键区域保持精细分辨率。

3.3 码率控制循环(Rate Loop)

编码器的核心挑战:找到最优的全局量化步长,使得编码后的比特数恰好等于目标比特数

LHDC V5 使用迭代搜索 + 牛顿法加速的码率控制:

复制代码
初始化 offset_size(从上一帧预测)
循环:
    1. 用当前 offset_size 对应的步长量化频谱
    2. 统计非零系数个数 nzc
    3. 估算编码这些系数需要的比特数(算术编码估算 + 尾数估算)
    4. 比较估算值与目标范围:
       - 超出 target_range → 增大 offset_size(减小步长,减少比特)
       - 低于 target_range_bottom → 减小 offset_size(增大步长,增加比特)
       - 在范围内 → 收敛,退出
    5. 用牛顿法更新 offset_size:
       offset = fast_calc_step(...)  # 估算步长-比特曲线的斜率
       offset_size += (estimated_bits - target_range) / offset

fast_calc_step 利用前两次迭代的结果估算 d(bits)/d(offset)d(\text{bits})/d(\text{offset})d(bits)/d(offset),实现牛顿法快速收敛(通常 3~5 次迭代即可收敛)。

3.4 系数比特数预测(coef3 / coef7)

为了精确估算编码比特数,需要预测每个非零系数的尾数位数。使用两种一阶 IIR 预测器:

coef3:pi=3⋅pi−1+5⋅bi8 \text{coef3}: \quad p_i = \frac{3 \cdot p_{i-1} + 5 \cdot b_i}{8} coef3:pi=83⋅pi−1+5⋅bi

coef7:pi=7⋅pi−1+bi8 \text{coef7}: \quad p_i = \frac{7 \cdot p_{i-1} + b_i}{8} coef7:pi=87⋅pi−1+bi

其中 bi=log⁡2(∣qi∣)≪7b_i = \log_2(|q_i|) \ll 7bi=log2(∣qi∣)≪7(Q7 定点的系数幅值比特数)。

  • coef3:快响应(5/8 权重给当前值),跟踪快速变化
  • coef7:慢响应(7/8 权重给历史值),平滑稳定
  • 编码器选择累计绝对预测误差更小的预测器,用 1 bit symbol_flag 通知解码器

这是一种自适应预测编码(Adaptive Predictive Coding)的思想,与语音编码中的 ADPCM(如 G.726)同源。


四、熵编码算法:FAC 算术编码 + Rice 编码

4.1 算术编码基础

算术编码(Arithmetic Coding)是一种熵编码方法,与霍夫曼编码相比,能更接近信源的熵极限(特别是当符号概率不是 2 的负整数次幂时)。

基本原理

  • 将输入符号序列映射到实数区间 [0, 1) 中的一个子区间
  • 每个符号有一个概率区间 [low, high)
  • 编码时不断缩小区间:[low, high) = [low + range*cum_low, low + range*cum_high)
  • 最终输出区间内任意一个数的二进制表示

自适应算术编码 :概率模型不是固定的,而是根据已编码符号的统计动态更新。LHDC V5 使用滑动窗口自适应模型(Sliding Window Adaptive Model):

  • 维护最近 window 个符号的频率统计
  • 新符号加入窗口,最老的符号移出
  • 频率归一化到 2^15 = 32768
4.2 FAC(Fast Adaptive arithmetic Coding)

LHDC V5 的 FAC 编码器是一种三符号(ternary)自适应算术编码器,专门优化用于三进制符号流 {0, 1, 2}:

两个独立模型

  • 模型 1(Stream 1 / Marker Plane)

    • 初始频率:{54, 12, 1}(符号 0 概率最高,约 80%)
    • 滑动窗口:ma_win1(48k 时约 57)
    • 编码前 nzc 个符号(marker plane,标记哪些系数需要 escape 编码)
  • 模型 2(Stream 2 / Quotient)

    • 初始频率:{90, 16, 1}(符号 0 概率更高,约 84%)
    • 滑动窗口:ma_win2 = ma_win1 × 8(约 456,更长的记忆)
    • 编码 Rice 商值的三进制展开

范围解码的 Xtensa 优化

标准范围解码需要计算 target = code / (range >> 15),但 Xtensa LX6 没有硬件除法指令(软件除法约 30+ 周期)。解码器使用恒等式优化:

⌊coder⌋≥c  ⟺  code≥r×c \left\lfloor \frac{\text{code}}{r} \right\rfloor \geq c \iff \text{code} \geq r \times c ⌊rcode⌋≥c⟺code≥r×c

用乘法替代除法,每次符号解码最多 2 次乘法(三符号),比特精确一致。

4.3 Rice 编码

Rice 编码是一种针对拉普拉斯分布(Laplacian Distribution)的前缀码,特别适合编码预测残差和量化系数。

基本 Rice 编码

  • 将非负整数 xxx 编码为:q=⌊x/2k⌋q = \lfloor x / 2^k \rfloorq=⌊x/2k⌋ 个 1 + 1 个 0 + kkk bit 余数
  • kkk 是 Rice 参数,需根据信号分布选择

LHDC V5 的 Rice 商编码

LHDC V5 不是直接使用标准 Rice 编码,而是将 Rice 商值展开为三进制符号流,然后用 FAC 算术编码。具体展开规则:

  • 商值 q=0q = 0q=0 → 符号 0
  • 商值 q=1q = 1q=1 → 符号 1
  • 商值 q=2q = 2q=2 → 符号 1, 1
  • 商值 q=3q = 3q=3 → 符号 1, 1, 1
  • 商值 q≥4q \geq 4q≥4 → 符号 2(escape)+ 2 bit 低位(循环)+ ... + 连续 1 的个数 + 0 结束

这种三进制展开 + 算术编码的组合比标准 Rice 编码的压缩效率更高,因为算术编码可以精确匹配符号概率,而 Rice 编码只能匹配几何分布。

4.4 尾数位平面编码(Mantissa Bit-Plane)

算术编码只编码了系数的高位(Rice 商),低位(尾数)使用因果预测位平面编码

shiftk=IIR(calc_bits(∣Mk−1∣)) \text{shift}_k = \text{IIR}(\text{calc\bits}(|M{k-1}|)) shiftk=IIR(calc_bits(∣Mk−1∣))

Mk=(qk≪shiftk)∣mantissa_bits M_k = (q_k \ll \text{shift}_k) | \text{mantissa\_bits} Mk=(qk≪shiftk)∣mantissa_bits

  • shift_k:当前系数的尾数位数,由前一系数的幅值通过 IIR 预测器预测
  • 预测器模式(flag2 选择):
    • 快模式:pred = (5*x + 3*pred) >> 3
    • 慢模式:pred = (7*pred + x) >> 3
  • 每个非零系数后跟 1 bit 符号位

这是一种上下文相关的位平面编码(Context-dependent Bit-Plane Coding),利用了频谱系数幅值的相关性(相邻系数幅值通常相近),用前一系数预测当前系数的尾数位数,减少需要传输的位数。


五、帧结构与加扰算法

5.1 帧结构
复制代码
┌─────────────┬─────────────────────────┬─────────────────────────┐
│  帧头 (2B)   │  声道 0 编码数据 (N B)  │  声道 1 编码数据 (N B)    │
│  u16 LE     │                         │                         │
└─────────────┴─────────────────────────┴─────────────────────────┘

帧头:低 10 位 = payload_len / 2,高 6 位 = 标志位。

每个声道数据内部结构:

复制代码
┌──────────────────────────────────────────────────────────────┐
│ 前导信息 (Leading Section)                                    │
│   flag(1b) + global_gain(9b) + sns_mode(4b) + 方向位 +        │
│   nzc(7~9b) + symbol_flag(1b) + resid(0/4b)                  │
├──────────────────────────────────────────────────────────────┤
│ FAC 算术编码流(三进制符号流 → 字节流)                         │
├──────────────────────────────────────────────────────────────┤
│ 尾数 + 符号位平面                                             │
├──────────────────────────────────────────────────────────────┤
│ 填充(0 填充到固定帧长)                                       │
└──────────────────────────────────────────────────────────────┘
5.2 加扰(Scrambling)

每个声道前 8 字节加扰:

  1. 异或固定掩码:byte[i] ^= XOR_MASK[i]XOR_MASK = {0xFF, 0xE7, 0x7A, 0xB3, 0xDA, 0xE5, 0xCD, 0x73}
  2. 字节重排:按 SCRAMBLE_ORDER[sel][] 排列,sel = byte[8] & 1(两种排列)

加扰的目的(不是加密):

  • 避免编码后的帧头出现固定模式(如全 0 或固定同步字),便于蓝牙接收器的同步和误码检测
  • 破坏数据中的长 0/长 1 序列,有利于基带的白化和时钟恢复
  • 两种排列由数据本身选择,提供一定的鲁棒性

六、算法复杂度与数学基础总结

算法模块 数学基础 复杂度 研究领域
LD-MDCT 线性代数、傅里叶分析、滤波器组理论 O(N log N) 数字信号处理、多速率信号处理
快速 IMDCT FFT 算法、Cooley-Tukey 分解、数论 O(N log N) 算法设计、计算数学
SNS 心理声学、ADPCM、预测编码、自适应滤波 O(M)(M=子带数) 音频信号处理、感知编码
均匀量化 量化理论、率失真理论(Rate-Distortion) O(N/2) 信息论、信源编码
码率控制 优化理论、牛顿法、二分搜索 O(K × N/2)(K=迭代次数) 优化算法、控制理论
系数预测 线性预测、IIR 滤波器、自适应滤波 O(N/2) 数字信号处理、预测编码
FAC 算术编码 信息论、熵编码、概率模型 O(S)(S=符号数) 信息论、数据压缩
Rice 编码 前缀码、拉普拉斯分布、Golomb 编码 O(S) 信息论、数据压缩
尾数位平面 上下文建模、位平面编码、因果预测 O(N/2) 图像/视频编码、嵌入式编码
加窗重叠相加 滤波器组理论、完全重构条件 O(N) 数字信号处理、多速率系统

总的来说,LHDC V5 编解码算法是数字信号处理 + 信息论 + 心理声学的交叉产物,核心算法包括:

  • LD-MDCT:时频变换(傅里叶分析 + 滤波器组理论)
  • SNS:感知频谱整形(心理声学 + ADPCM 自适应预测)
  • 均匀量化 + 码率控制:率失真优化(信息论 + 数值优化)
  • FAC 算术编码 + Rice 编码:熵编码(信息论 + 数据压缩)
  • 尾数位平面编码:上下文相关位平面编码(预测编码)

音频编码是一个既有深厚理论基础、又有广泛工程应用的领域,随着无线音频、空间音频、AI 语音的发展,该方向的人才需求将持续增长。

相关推荐
csdn_aspnet1 小时前
C# 第k个最小元素(K’th Smallest Element)
算法·c#·排序算法
BioRunYiXue1 小时前
RACE技术全攻略:从全长克隆到靶基因验证
java·javascript·网络·人工智能·科技·算法·eclipse
ZPC82102 小时前
joy 及sensor_msgs
算法
Three_ST2 小时前
沐神-动手学习深度学习-习题答案4.4模型选择,欠拟合,过拟合
人工智能·python·深度学习·学习·算法
CIO_Alliance2 小时前
AI深度系列(2)| CNN卷积池化感受野原理:从局部感知到全局视野
人工智能·深度学习·线性代数·算法·计算机视觉·企业ai转型·企业cio联盟
探物 AI2 小时前
机器人清理墙角蜘蛛网,背后需要哪些算法?——从视觉分割到贴墙力控
算法·机器人
青 春 记 忆2 小时前
LeetCode 226. 翻转二叉树|Python 解法详解
python·算法·leetcode
民乐团扒谱机2 小时前
【微科普】压缩感知(CS):违反了奈奎斯特采样定理?不先采集也能还原信号?大白话讲透稀疏采样、L1重建与OMP,一文吃透附代码
python·神经网络·线性代数·算法·数学建模·压缩感知·奈奎斯特
88号技师2 小时前
2026年SCI-分数牛顿衍生优化算法Fractional Newton-derived optimizer-附Matlab免费代码
开发语言·算法·数学建模·matlab·优化算法