第1章:语音信号处理基础 / Fundamentals of Speech Signal Processing
语音信号本质上是一种由人类发音器官产生的、携带语言学与副语言学信息的声波,其物理载体是空气分子的周期性振动。理解语音技术的起点,必须追溯到语音产生的生理声学机制与人类听觉感知的独特非线性特性。语音并非随机噪声,而是由肺部气流激励、声带振动(或非周期性摩擦)以及声道(包括咽腔、口腔、鼻腔)的共振滤波共同塑造的复杂时变信号。声道形状的快速变化(通常在10-30毫秒尺度)形成了语音的短时平稳性假设,这一假设构成了所有经典语音信号处理技术的基石。与此同时,人类听觉系统对频率的感知并非线性,而是近似于梅尔(Mel)刻度的对数压缩,对低频区域的频率分辨力远高于高频区域。这种生理与心理声学特性,深刻地影响了后续特征提取算法的设计哲学:语音特征的目标是在保留语言学判别信息的同时,模拟人类听觉的压缩与选择性注意机制,并降低数据的维度与冗余度。本章将系统性地构建从连续声波到离散数字特征表示的完整理论链条,涵盖采样量化、预处理、时频分析、滤波器组与梅尔频率倒谱系数(MFCC)的数学推导,并最终探讨这些传统特征在HMM与神经端到端两种范式下的不同角色与演变逻辑。
---
语音产生的生理声学模型与信号特性
语音产生是一个涉及呼吸系统、发声系统和构音系统的协同物理过程。从信号处理角度,可将其抽象为一个激励-滤波模型(Source-Filter Model),由Fant于1960年代提出,至今仍是语音合成与识别的基础框架。
激励源(Source):对于浊音(如元音、鼻音),激励来源于声带周期性开闭产生的准周期脉冲序列。其基频(Pitch, F0)由声带张力与质量决定,典型范围:男性85-180 Hz,女性165-255 Hz,儿童250-350 Hz。时域上,这表现为类似冲激串的波形,频谱为离散的谐波结构,能量集中在基频整数倍处。对于清音(如/s/, /f/),激励来源于气流通过狭窄通道的湍流噪声,可建模为白噪声或彩色噪声。
滤波器(Filter):声道(从声门到嘴唇的共振腔)对激励源进行共振滤波,其频率响应由声道横截面积函数决定。在某一时刻,声道形状对应一组共振峰(Formants),即频谱包络的峰值,通常用前三个共振峰F1、F2、F3来区分不同元音。例如,英语元音/i/(如“see”)具有低F1(约300 Hz)和高F2(约2300 Hz),而元音/ɑ/(如“father”)具有高F1(约700 Hz)和低F2(约1100 Hz)。声道滤波器的传递函数可近似为全极点模型(即线性预测编码,LPC的基础),其系数与声道形状直接相关。
鼻腔耦合:对于鼻音(如/m/, /n/)和鼻化元音,鼻腔作为额外的共振腔,在频谱上引入约250-300 Hz的额外共振峰(鼻腔共振峰)和特定频率的零值点(反共振峰)。
从信号特性看,语音是非平稳随机过程,但其统计特性在短时(通常10-30 ms)内可视为平稳。这一观察引出了所有后续短时分析技术的合理性。此外,语音能量分布高度不均匀:元音段能量高且谐波结构明显,清擦音段能量低但高频丰富,爆破音段能量集中于瞬态。这种时频能量的剧烈变化要求特征提取算法具备一定的鲁棒性。
---
数字语音信号的采样与量化
连续声波需通过模数转换(ADC)变为离散数字信号才能进行数字处理。根据奈奎斯特-香农采样定理,要无失真地重建带宽有限的连续信号,采样频率$f_s$必须大于信号最高频率分量$f_{max}$的两倍: $$f_s > 2f_{max}$$
语音信号的典型带宽:电话带宽为300-3400 Hz,故$f_s=8$ kHz已足够;宽带语音通常覆盖50-8000 Hz甚至更宽,故$f_s=16$ kHz或$44.1$ kHz(CD质量)成为标准。采样后,信号在时域离散化为序列$x[n]$,其中$n$为整数样本索引。采样间隔$T_s = 1/f_s$。
量化将每个采样值映射为有限精度的数字码。常用线性脉冲编码调制(PCM)使用均匀量化,但语音信号幅度分布接近拉普拉斯分布(对数为高斯分布),均匀量化导致小幅度信号信噪比低。因此,实际通信系统(如G.711)采用A律或μ律非线性量化,对小信号分配更多量化级,在有限比特(通常8 bit)下优化感知质量。
数字语音信号$x[n]$是实序列,其离散时间傅里叶变换(DTFT)为: $$X(e^{j\omega}) = \sum_{n=-\infty}^{\infty} x[n] e^{-j\omega n}$$ 由于实际处理有限长序列,常用离散傅里叶变换(DFT): $$X[k] = \sum_{n=0}^{N-1} x[n] e^{-j 2\pi k n / N}, \quad k=0,1,...,N-1$$ 其中$N$为FFT点数。$X[k]$是复数,幅度谱$|X[k]|$反映各频率分量强度,相位谱$\angle X[k]$反映时间对齐信息,但语音识别中通常仅使用幅度谱或功率谱,因相位信息对内容判别贡献较小且不稳定。
---
预处理:预加重、分帧与加窗
原始语音信号包含两个需要处理的问题:1)高频能量通常低于低频(声带激励的$1/f$特性及口唇辐射效应),导致高频信息相对较弱;2)信号非平稳,但短时平稳假设成立。
预加重(Pre-emphasis)
为补偿高频衰减并提升高频共振峰的可辨识度,施加一阶高通滤波器: $$y[n] = x[n] - \alpha x[n-1]$$ 其中$\alpha$为预加重系数,典型值0.95至0.97(对应约50 Hz截止频率)。该操作在频域对应: $$Y(e^{j\omega}) = (1 - \alpha e^{-j\omega}) X(e^{j\omega})$$ 其幅度响应在$\omega=\pi$(最高频)处比$\omega=0$(直流)高约$20\log_{10}(1+\alpha)$ dB(约3-4 dB)。预加重也使得信号频谱更“平坦”,有利于后续参数提取的稳定性。注意:预加重是可逆的(使用$1/(1-\alpha z^{-1})$),在特征提取后有时需进行逆滤波以进行合成。
分帧(Framing)
利用短时平稳假设,将连续语音切分为重叠的短段(帧)。设帧长$L$(样本数),帧移(或帧步)$H$(样本数)。通常帧长对应20-40 ms时间长度(如$L=400$对应$25$ ms @ $16$ kHz),帧移对应10 ms($H=160$ @ $16$ kHz)。分帧操作可表示为: $$x_m[n] = x[n + mH] \cdot w[n], \quad n=0,1,...,L-1$$ 其中$m$为帧索引,$w[n]$为窗函数,且$x[n]$在$n$超出原始信号范围时补零(zero-padding)。
加窗(Windowing)
直接截断相当于使用矩形窗,其频谱主瓣宽、旁瓣高(约-13 dB),导致频谱泄漏(spectral leakage),即单一频率能量分散到相邻频率 bin。为减少泄漏,对每帧乘以平滑窗函数,使帧两端衰减至零。常用窗函数:
- **汉明窗(Hamming Window)**:
$$w[n] = 0.54 - 0.46 \cos\left(\frac{2\pi n}{L-1}\right), \quad n=0,...,L-1$$ 主瓣宽约$8\pi/L$(弧度),旁瓣峰值约-41 dB。在语音处理中最为常用,因其在时域平滑性与频域主瓣宽度间取得良好平衡。
- **汉宁窗(Hann Window)**:
$$w[n] = 0.5 - 0.5 \cos\left(\frac{2\pi n}{L-1}\right)$$ 旁瓣峰值约-31 dB,主瓣略宽于汉明窗。
- **布莱克曼窗(Blackman Window)**:
$$w[n] = 0.42 - 0.5 \cos\left(\frac{2\pi n}{L-1}\right) + 0.08 \cos\left(\frac{4\pi n}{L-1}\right)$$ 旁瓣峰值约-58 dB,但主瓣更宽,频率分辨率降低。
窗函数的选择是时域平滑性与频域分辨率之间的权衡。对于需要精确共振峰估计的任务(如LPC分析),汉明窗是标准选择。注意:加窗会引入幅度衰减,通常需进行窗增益补偿(如汉明窗增益为0.54)。
算法伪代码 1:预加重、分帧、加窗
``` function preprocess_speech(x, fs, preemph=0.97, frame_len=0.025, frame_shift=0.010, window_type='hamming'):
参数:x为原始采样序列,fs为采样率
1. 预加重
y = np.zeros_like(x) y[0] = x[0] for n in range(1, len(x)): y[n] = x[n] - preemph * x[n-1]
2. 计算帧长和帧移的样本数
L = int(frame_len * fs) # 例如400 H = int(frame_shift * fs) # 例如160 num_frames = 1 + (len(y) - L) // H
3. 生成窗函数
if window_type == 'hamming': w = 0.54 - 0.46 * np.cos(2 * np.pi * np.arange(L) / (L - 1)) elif window_type == 'hann': w = 0.5 - 0.5 * np.cos(2 * np.pi * np.arange(L) / (L - 1)) else: # 矩形窗 w = np.ones(L)
4. 分帧加窗
frames = np.zeros((num_frames, L)) for m in range(num_frames): start = m * H end = start + L frame = y[start:end] if len(frame) < L: # 最后一帧不足则补零 frame = np.pad(frame, (0, L - len(frame))) frames[m, :] = frame * w
return frames, L, H, w ```
---
短时傅里叶变换(STFT)与频谱表示
对每一帧加窗后的信号$x_m[n]$进行离散傅里叶变换(DFT),即得到短时傅里叶变换(STFT),它将时域信号映射到时频平面: $$X[m, k] = \sum_{n=0}^{N-1} x_m[n] e^{-j 2\pi k n / N}, \quad k=0,1,...,N-1$$ 其中$N$为FFT点数,通常取大于等于$L$的2的幂次(如512、1024)以利用快速傅里叶变换(FFT)算法。$X[m, k]$是复数,其幅度平方$|X[m, k]|^2$称为周期图(Periodogram),是功率谱的估计。
STFT的时频分辨率受限于测不准原理(Uncertainty Principle):时域窗长$L$决定频率分辨率$\Delta f \approx f_s / N$,而帧移$H$决定时间分辨率$\Delta t \approx H / f_s$。增大$L$提高频率分辨率但降低时间分辨率(因帧重叠多,更新慢),反之亦然。对于语音,通常需要约10-20 Hz的频率分辨率以分辨共振峰,故$N=512$(@16 kHz时分辨率约31 Hz)或$N=1024$(约16 Hz)常见。时间分辨率需捕捉10-30 ms的瞬态(如爆破音),故帧移10 ms(160样本)是标准。
STFT结果$X[m, k]$可组织为频谱图(Spectrogram):以帧索引$m$为横轴、频率$k$为纵轴、幅度(通常取对数)为颜色强度的二维图像。频谱图是语音可视化的核心工具,能直观显示共振峰轨迹、音素边界和基频(对浊音)。
功率谱密度(PSD)估计:单帧周期图方差大,不稳定。常用梅尔滤波器组能量(Filter Bank Energies) 作为更鲁棒的功率谱估计,这引出了下一节。
---
滤波器组(Filter Bank)与梅尔刻度
人类听觉系统对频率的感知是非线性的,遵循梅尔刻度(Mel Scale)。实验表明,人耳对低频频率变化更敏感,对高频变化较迟钝。梅尔刻度将线性频率$f$(Hz)映射为感知频率$m$(Mel): $$m = 2595 \log_{10}\left(1 + \frac{f}{700}\right) \approx 1127 \ln\left(1 + \frac{f}{700}\right)$$ 反函数为: $$f = 700 \left(10^{m/2595} - 1\right) \approx 700 \left(e^{m/1127} - 1\right)$$ 在低频($f<1000$ Hz),梅尔刻度近似线性;高频区显著压缩。例如,1000 Hz对应约1000 Mel,但4000 Hz仅对应约2000 Mel。
滤波器组设计:在梅尔刻度上均匀分布$K$个三角滤波器(通常$K=20-40$),覆盖0到$f_s/2$(奈奎斯特频率)。第$k$个滤波器的中心频率(以Hz计)为: $$f_k = \frac{k}{K+1} \cdot \text{Mel}(f_{max}) \quad \text{(在梅尔域均匀)}$$ 然后转换回Hz。每个三角形滤波器覆盖一定带宽,相邻滤波器在中心频率处重叠50%。滤波器幅度响应$H_k[f]$定义为:
- 左侧从$f_{k-1}$到$f_k$线性上升至1
- 右侧从$f_k$到$f_{k+1}$线性下降至0
(首尾滤波器为单边三角形)
更精确的滤波器形状可使用重叠的汉明窗或高斯窗,但三角滤波器计算简单且效果良好。
滤波器组能量(Filter Bank Energies):对STFT的幅度谱$|X[m, k]|$,计算每个滤波器覆盖的频带内的功率和: $$E[m, i] = \sum_{k=0}^{N/2} |X[m, k]|^2 \cdot H_i[k], \quad i=1,2,...,K$$ 其中$H_i[k]$是第$i$个滤波器在频率bin $k$的响应(需插值到FFT频率格点)。$E[m, i]$表示第$m$帧在第$i$个梅尔滤波器带的能量。这本质上是将线性频谱非线性地“弯曲”并求和,模拟了人耳基底膜的频率分析。
对数压缩:人耳对声音强度的感知符合斯蒂文斯幂律(Stevens' Power Law),即感知响度与刺激强度的幂次(约0.3-0.6)成正比。为模拟此特性,对滤波器组能量取对数: $$L[m, i] = \log(E[m, i] + \epsilon)$$ 其中$\epsilon$为小常数(如$10^{-8}$)防止取零对数。对数操作同时压缩了动态范围,并具有去相关效果,有利于后续统计建模。
算法伪代码 2:梅尔滤波器组能量提取
``` function mel_filterbank_energies(spectrum, fs, num_filters=26, N_fft=512, low_freq=0, high_freq=None):
spectrum: (num_frames, N_fft//2+1) 幅度谱或功率谱(通常取|X|^2)
返回: (num_frames, num_filters) 对数滤波器组能量
if high_freq is None: high_freq = fs / 2
1. 生成梅尔刻度上的等间距点
mel_low = 2595 * np.log10(1 + low_freq / 700) mel_high = 2595 * np.log10(1 + high_freq / 700) mel_points = np.linspace(mel_low, mel_high, num_filters + 2) # 包括两端点 hz_points = 700 * (10**(mel_points / 2595) - 1)
2. 将Hz频率映射到FFT bin索引(整数)
bin = np.floor((N_fft + 1) * hz_points / fs).astype(int)
3. 构建滤波器矩阵 (num_filters, N_fft//2+1)
fbank = np.zeros((num_filters, N_fft//2 + 1)) for i in range(num_filters): left_bin, center_bin, right_bin = bin[i], bin[i+1], bin[i+2]
左侧上升边
for k in range(left_bin, center_bin): fbank[i, k] = (k - left_bin) / (center_bin - left_bin)
右侧下降边
for k in range(center_bin, right_bin): fbank[i, k] = (right_bin - k) / (right_bin - center_bin)
4. 应用滤波器组
假设输入是功率谱 S = |X|^2, 形状 (num_frames, N_fft//2+1)
filter_energies = np.dot(spectrum, fbank.T) # (num_frames, num_filters)
5. 对数压缩
log_energies = np.log(filter_energies + 1e-8)
return log_energies, fbank ```
---
梅尔频率倒谱系数(MFCC)的数学推导
滤波器组能量$L[m, i]$仍具有较高的维数($K$维,通常20-40),且相邻滤波器带能量存在强相关性。**倒谱分析(Cepstral Analysi