音频基础:波形、采样与傅里叶变换


文档摘要

音频基础:波形、采样与傅里叶变换 本节摘要:波形(Waveform)是原始信号,频谱图(Spectrogram)是它的表示,梅尔特征(Mel Features)是机器学习友好的形态。每一条现代 ASR(自动语音识别)和 TTS(文本转语音)流水线都在攀爬这架阶梯,而第一级就是搞懂采样(Sampling)与傅里叶变换(Fourier Transform)。麦克风输出的是「声压随时间变化」的连续信号,神经网络吃的是张量,两者之间夹着一整套约定——一旦违反,bug 是沉默的:模型照常训练,词错率(WER)却悄悄翻倍;TTS 上线后全是嘶嘶声;语音克隆系统记住了麦克风特性而不是说话人。

音频基础:波形、采样与傅里叶变换

本节摘要:波形(Waveform)是原始信号,频谱图(Spectrogram)是它的表示,梅尔特征(Mel Features)是机器学习友好的形态。每一条现代 ASR(自动语音识别)和 TTS(文本转语音)流水线都在攀爬这架阶梯,而第一级就是搞懂采样(Sampling)与傅里叶变换(Fourier Transform)。麦克风输出的是「声压随时间变化」的连续信号,神经网络吃的是张量,两者之间夹着一整套约定——一旦违反,bug 是沉默的:模型照常训练,词错率(WER)却悄悄翻倍;TTS 上线后全是嘶嘶声;语音克隆系统记住了麦克风特性而不是说话人。本节将从零讲透采样率、奈奎斯特定理、位深、离散傅里叶变换(DFT)、快速傅里叶变换(FFT)、分帧加窗与短时傅里叶变换(STFT),为后续 16 节铺平地基。读完本节,你能手写 DFT、解释混叠(Aliasing)成因,并知道为什么 Whisper 一定要吃 16 kHz 单声道 float32。

学习目标

阅读完本节,你应当能够:

  1. 解释波形、采样率、位深三者的物理含义与数值范围,并能在不同格式间正确换算。
  2. 运用奈奎斯特-香农采样定理,判断给定信号能否被无歧义重建,并解释混叠的成因与规避方法。
  3. 手写离散傅里叶变换(DFT),理解频率箱(Frequency Bin)与赫兹的映射关系,并能定位主频。
  4. 掌握分帧、加窗、STFT的处理流程,为第 02 节的频谱图与梅尔特征做准备。
  5. 在生产环境中正确选择音频读写、重采样、STFT 的库,并遵循「先对齐采样率,再对齐其他」的铁律。

一、问题与直觉

麦克风把空气的压强变化转成「压强-时间」的连续电信号,模数转换器(ADC)再以固定频率对它采样,得到一串数字。神经网络吃的则是张量。在「连续声波」与「张量」之间,横亘着一整套约定:采样率是多少、是否已混叠、信号是原始样本还是频域表示。

语音系统里几乎每一个 bug,都能追溯到这三个问题之一:

  1. 数据录制时的采样率是多少?模型期望的采样率又是多少?
  2. 信号是否发生了混叠?
  3. 你操作的是原始时域样本,还是频域表示?

这三件事做对了,Phase 6 剩下的部分就顺水推舟;做错了,即便是 Whisper-Large-v4 也会吐出垃圾结果。

波形:信号的本来面目

波形是一维浮点数组,取值范围在 [-1.0, 1.0] 之间,按下标索引样本序号。把样本序号换算成秒,除以采样率即可:t = n / sr。一段 10 秒、16 kHz 的音频,就是 160 000 个浮点数。

采样率:每秒取多少点

采样率(Sample Rate, sr)即每秒采样次数。2026 年常见的几档:

采样率 用途
8 kHz 电话、老式 VOIP。奈奎斯特只到 4 kHz,辅音被切掉,ASR 慎用。
16 kHz ASR 标准。Whisper、Parakeet、SeamlessM4T v2 都吃 16 kHz。
22.05 kHz 老式 TTS 声码器训练。
24 kHz 现代 TTS(Kokoro、F5-TTS、xTTS v2)。
44.1 kHz CD 音质、音乐。
48 kHz 影视、专业音频、高保真 TTS(VALL-E 2、NaturalSpeech 3)。

奈奎斯特-香农定理:采样率的硬天花板

采样率为 sr 时,能无歧义表示的最高频率是 sr/2,这个边界叫奈奎斯特频率(Nyquist Frequency)。高于奈奎斯特的能量会被混叠——折叠回低频,污染信号。所以下采样前一定要先低通滤波

💡 一个常被忽略的推论:人耳听觉上限约 20 kHz,因此 CD 采样率定在 44.1 kHz(略高于 40 kHz)以保留全部可闻频段。把 8 kHz 电话录音重采样到 16 kHz,绝不会凭空恢复出 4~8 kHz 的信息——上采样无法创造信息。

位深:每个样本用多少比特

16 位 PCM(有符号 int16,范围 ±32 767)是通用交换格式;音乐用 24 位;内部数字信号处理(DSP)常用 32 位浮点。像 soundfile 这类库读 int16 文件时,会自动把数据暴露为 [-1, 1] 区间的 float32 数组。

傅里叶变换:任何信号都是正弦波的叠加

任意有限信号都可以分解成不同频率正弦波的叠加。离散傅里叶变换(DFT)对 N 个样本算出 N 个复数系数,每个系数对应一个频率箱(Frequency Bin)。箱 k 对应的频率是 k · sr / N 赫兹;系数的模是该频率上的振幅,辐角是相位。

FFT:DFT 的加速版

快速傅里叶变换(FFT)是 DFT 在 N 为 2 的幂时的 O(N log N) 算法。所有音频库底层都跑 FFT。一个 1024 点的 FFT,在 16 kHz 下给出 512 个可用频率箱,覆盖 0~8 kHz,分辨率约 15.6 Hz。

分帧加窗:不要对整段音频做 FFT

实际工程中,我们不会对整段音频做一次 FFT,而是把它切成重叠的帧(典型 25 ms 帧、10 ms 步长),每帧乘一个窗函数(汉宁窗 Hann、汉明窗 Hamming)消除边缘不连续,再逐帧 FFT。这就是短时傅里叶变换(STFT),第 02 节从这里接棒。

⚠️ 为什么必须加窗?直接对一段截断的信号做 FFT,等于乘了一个矩形窗,而矩形窗的频谱有大量旁瓣,会把相邻频率的能量「漏」出去(频谱泄漏)。Hann 窗让帧两端平滑衰减到 0,大幅压低旁瓣,代价是频率分辨率略降。这是工程权衡,不是可选步骤。

二、从零实现

下面用纯标准库 wave 模块演示零依赖流程。生产环境请改用 soundfiletorchaudio.load,两者都返回 (waveform, sr) 元组。

第 1 步:读取音频并画波形

import soundfile as sf waveform, sr = sf.read("clip.wav", dtype="float32") # shape (T,), sr 为 int

第 2 步:从第一性原理合成正弦波

import math def sine(freq_hz, sr, seconds, amp=0.5): n = int(sr * seconds) return [amp * math.sin(2 * math.pi * freq_hz * i / sr) for i in range(n)]

一个 440 Hz(音乐会标准音 A)的正弦波,16 kHz 下录 1 秒,就是 16 000 个浮点数。可用 wave.open(..., "wb") 以 16 位 PCM 编码写入文件。

第 3 步:手写 DFT

def dft(x): N = len(x) out = [] for k in range(N): re = sum(x[n] * math.cos(-2 * math.pi * k * n / N) for n in range(N)) im = sum(x[n] * math.sin(-2 * math.pi * k * n / N) for n in range(N)) out.append((re, im)) return out

复杂度 O(N²),对 N=256 验证正确性没问题,真实音频根本跑不动。生产代码调用 numpy.fft.rffttorch.fft.rfft

设计要点:DFT 公式的本质,是把信号 x 与频率为 k 的正弦/余弦基做内积——内积越大,说明信号里这个频率的成分越多。复数表达把正弦和余弦合并成一个旋转矢量,模长即能量,辐角即相位。

第 4 步:找主频

幅度谱的峰值下标 k_star 对应频率 k_star * sr / N。把上一步的 440 Hz 正弦波喂给 DFT,峰值应出现在 440 * N / sr 这个箱上。

第 5 步:演示混叠

用 10 kHz 采样一个 7 kHz 的正弦波(奈奎斯特只有 5 kHz)。7 kHz 高于奈奎斯特,折叠成 10 − 7 = 3 kHz。FFT 峰值会出现在 3 kHz 处。这就是经典的混叠演示,也是每个数模/模数转换器都内置「砖墙」低通滤波器的原因。

三、框架对比

2026 年实际要上线的工具栈:

任务 为什么选它
读写 WAV/FLAC/OGG soundfile(libsndfile 封装) 最快、最稳,直接返回 float32。
重采样 torchaudio.transforms.Resamplelibrosa.resample 内置正确的抗混叠滤波。
STFT / 梅尔 torchaudiolibrosa GPU 友好,PyTorch 生态。
实时流式采集 sounddevicepyaudio 跨平台的 PortAudio 绑定。
检视音频文件 ffprobesoxi 命令行、快,报告采样率/声道/编码。

决策铁律:先对齐采样率,再对齐其他一切。Whisper 期望 16 kHz 单声道 float32;喂给它 44.1 kHz 立体声,会得到看起来像模型 bug、实则是数据 bug 的垃圾输出。

# 错误:直接喂 44.1 kHz 立体声 → Whisper 输出乱码 # 正确:先重采样到 16 kHz 单声道 import torchaudio wav, sr = torchaudio.load("clip.wav") # 可能是 44.1k / 立体声 if sr != 16000: wav = torchaudio.transforms.Resample(sr, 16000)(wav) if wav.shape[0] > 1: wav = wav.mean(dim=0, keepdim=True) # 立体声混成单声道 # wav 现在是 16 kHz 单声道,可送入 Whisper

💡 librosa.load(..., sr=16000) 会在读取时自动重采样,看似省事,但隐式行为在工程里是大忌——你不知道它何时改了采样率。生产代码请显式调用 Resample,把重采样这一步暴露在代码里。

四、可复用产物

本节产出一个技能文档(原课程 outputs/skill-audio-loader.md),帮你检查音频输入是否匹配下游模型的期望,并在不匹配时正确重采样。核心检查清单:

  • 文件采样率是否等于模型期望(Whisper = 16 kHz,Kokoro = 24 kHz)?
  • 声道数是否匹配(多数 ASR 要单声道)?
  • 数值范围是否在 [-1, 1]?(int16 文件读出来要除以 32768)
  • 下采样前是否做了抗混叠低通滤波?
  • 是否裁剪了静音段(避免无效计算)?

这份清单可复用于任何语音项目的前处理模块,是「数据进入模型前的最后一道防线」。

五、练习

  1. 基础:合成一段 1 秒、220 Hz + 440 Hz + 880 Hz 混合波(16 kHz 采样)。跑 DFT,确认三个峰值出现在预期频率箱上。

  2. 进阶:录一段 3 秒自己说话的 WAV(48 kHz)。用 torchaudio.transforms.Resample(带抗混叠)下采样到 16 kHz;再用「每隔三个样本取一个」的朴素抽样下采样到 16 kHz。分别 FFT,比较两者的频谱——混叠能量出现在哪里?为什么朴素抽样在高频处出现毛刺?

  3. 挑战:仅用 math 和第 3 步的 DFT,从零搭一个 STFT。帧长 400、步长 160、Hann 窗,用 matplotlib.pyplot.imshow 画出幅度矩阵。这就是第 02 节的频谱图。

本节要点回顾

  1. 波形是 [-1,1] 的一维 float 数组,样本序号除以采样率即得秒数;10 秒 16 kHz 音频 = 160 000 个浮点数。
  2. 采样率决定可表示的最高频——8 kHz 只到 4 kHz(ASR 慎用),16 kHz 是 ASR 标准,24~48 kHz 用于现代高保真 TTS。
  3. 奈奎斯特定理:采样率 sr 能无歧义表示的最高频率是 sr/2,超过就混叠;下采样前必须先低通滤波
  4. 位深:int16 是通用交换格式,float32 是内部 DSP 格式,库会自动在两者间转换。
  5. DFT 把 N 个样本变成 N 个复数频率系数,箱 k 对应 k·sr/N Hz,模是振幅、辐角是相位;手写复杂度 O(N²)
  6. FFT 是 DFT 的 O(N log N) 加速版,要求 N 为 2 的幂,所有音频库底层都用它。
  7. STFT = 分帧 + 加窗 + 逐帧 FFT,典型 25 ms 帧、10 ms 步长;加窗是为压低频谱泄漏,不是可选项。
  8. 生产铁律:先对齐采样率,再对齐其他——Whisper 要 16 kHz 单声道 float32,喂错格式会得到看似模型 bug 的垃圾输出。

下一节,我们把 STFT 的复数矩阵转成频谱图,再用梅尔尺度(Mel Scale)把它压缩成人耳感知友好的梅尔特征——这是几乎所有现代语音模型的输入形态。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U