第01课:声音与信号基础
信号处理 第1课/30
🎯 本课目标
理解声音的物理本质,掌握信号的基本概念,学会用Python生成和可视化声音信号。
📖 声音是什么?
声音是机械波在介质中的传播。声波通过空气粒子的振动传递能量,到达人耳后引发鼓膜振动,最终被大脑感知为声音。
声音的三个核心属性:
- 频率 (Frequency):每秒振动次数,单位Hz。人耳可听范围约20Hz-20kHz
- 振幅 (Amplitude):振动幅度,决定响度。常用分贝(dB)衡量
- 相位 (Phase):波形的起始位置,影响波的叠加效果
📐 信号的数学描述
x(t) = A · sin(2πft + φ)
其中A为振幅,f为频率,φ为初相位,t为时间。这是最基本的信号模型,所有复杂的声音都可以看作多个简单正弦波的叠加。
连续信号 vs 离散信号
现实世界中的声音是连续信号(模拟信号),但计算机只能处理离散信号(数字信号)。通过采样将连续信号转为离散信号是数字音频处理的基础。
采样定理(Nyquist定理):采样频率必须 ≥ 2倍信号最高频率,才能无失真还原原信号。
💻 代码:生成和可视化基本声音信号
import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
sr = 16000; duration = 0.05
t = np.linspace(0, duration, int(sr * duration), endpoint=False)
freqs = [261.63, 440.0, 880.0]
labels = ['C4 (261Hz)', 'A4 (440Hz)', 'A5 (880Hz)']
colors = ['#f97316', '#3b82f6', '#22c55e']
fig, axes = plt.subplots(3, 1, figsize=(12, 8), facecolor='#0f172a')
for i, (freq, label, color) in enumerate(zip(freqs, labels, colors)):
signal = np.sin(2 * np.pi * freq * t)
axes[i].plot(t * 1000, signal, color=color, linewidth=1.5)
axes[i].set_title(label, color=color, fontsize=14)
axes[i].set_facecolor('#0f172a'); axes[i].tick_params(colors='#94a3b8'); axes[i].grid(True, alpha=0.2)
plt.tight_layout()
plt.savefig('/var/www/ttl/speech/fig_01_waves.png', dpi=150, facecolor='#0f172a')
plt.close(); print("✅ 基本波形图已保存")
🔊 声音的物理特性
分贝标度
L = 20 · log₁₀(P/P₀) dB
其中P₀ = 20μPa 是参考声压(人耳听觉阈值)。分贝标度是对数标度,意味着每增加20dB,声压增加10倍。
| 声源 | 声压级(dB) |
| 听觉阈值 | 0 |
| 耳语 | 30 |
| 正常对话 | 60 |
| 交通噪声 | 80 |
| 摇滚乐 | 120 |
| 疼痛阈值 | 130 |
💻 代码:分贝计算与信号能量
import numpy as np
def amplitude_to_db(signal, ref=1.0, eps=1e-10):
rms = np.sqrt(np.mean(signal ** 2))
return 20 * np.log10(rms / ref + eps)
sr = 16000; t = np.linspace(0, 1, sr, endpoint=False)
for amp in [1.0, 0.5, 0.1, 0.01]:
sig = amp * np.sin(2 * np.pi * 440 * t)
db = amplitude_to_db(sig)
print(f"振幅 A={amp}: RMS={np.sqrt(np.mean(sig**2)):.4f}, {db:.2f} dB")
print("\n✅ 分贝计算验证通过")
振幅 A=1.0: RMS=0.7071, -3.01 dB
振幅 A=0.5: RMS=0.3536, -9.03 dB
振幅 A=0.1: RMS=0.0707, -23.01 dB
振幅 A=0.01: RMS=0.0071, -43.01 dB
✅ 分贝计算验证通过
💻 代码:不同类型信号生成
import numpy as np
import matplotlib; matplotlib.use('Agg')
import matplotlib.pyplot as plt
sr = 16000; t = np.linspace(0, 0.05, int(sr*0.05), endpoint=False)
square_wave = np.sign(np.sin(2 * np.pi * 440 * t))
np.random.seed(42); white_noise = np.random.randn(len(t))
sawtooth = 2 * (440 * t - np.floor(0.5 + 440 * t))
triangle = 2 * np.abs(2 * (440 * t - np.floor(440 * t + 0.5))) - 1
fig, axes = plt.subplots(2, 2, figsize=(14, 8), facecolor='#0f172a')
for ax, (title, sig, color) in zip(axes.flat,
[('方波 440Hz', square_wave, '#f97316'), ('白噪声', white_noise, '#3b82f6'),
('锯齿波 440Hz', sawtooth, '#22c55e'), ('三角波 440Hz', triangle, '#fbbf24')]):
ax.plot(t * 1000, sig, color=color, linewidth=1.2)
ax.set_title(title, color=color, fontsize=12)
ax.set_facecolor('#0f172a'); ax.tick_params(colors='#94a3b8'); ax.grid(True, alpha=0.2)
plt.tight_layout()
plt.savefig('/var/www/ttl/speech/fig_01_signaltypes.png', dpi=150, facecolor='#0f172a')
plt.close(); print("✅ 信号类型图已保存")
💡 关键理解:现实中的声音几乎都不是纯正弦波,而是多个频率成分的叠加。傅里叶分析就是将复杂信号分解为简单正弦波的工具——我们将在第3课深入学习。
📝 练习
🧪 练习1:生成和弦
同时生成C大三和弦(C4=261.63Hz, E4=329.63Hz, G4=392.00Hz),绘制波形并计算RMS能量
🧪 练习2:声压级比较
计算振幅为0.5和0.1的两个正弦波的声压级差,验证是否约为14dB
🧪 练习3:采样率实验
用4000Hz采样率对4000Hz正弦波采样,观察混叠现象
🔍 深入理解:声波的传播
声波是纵波——空气粒子沿传播方向振动。声速约340m/s(取决于温度)。波长λ与频率f的关系:
λ = v / f (v为声速)
例如440Hz音叉产生的声波波长约0.77m。人耳能感知的最低频率20Hz对应波长17m,最高频率20kHz对应波长1.7cm。
声压级计算实例
如果两个声源的声压级分别为60dB和70dB,总声压级不是130dB,而是:
L_total = 10·log₁₀(10^(60/10) + 10^(70/10)) = 70.4 dB
对数相加意味着70dB声源比60dB声源响10倍,几乎完全主导。
💡 扩展阅读
psychoacoustics(心理声学)
人耳对声音的感知并非线性:
- 等响曲线:人耳对3-4kHz最敏感,低频和高频需要更高声压才能感知同等响度
- 掩蔽效应:强信号会掩蔽附近的弱信号,这是MP3压缩的原理
- 临界频带:人耳将频率分成约24个临界频带,这是梅尔标度的生理学基础
🔬 数学背景
欧拉公式
e^(jθ) = cos(θ) + j·sin(θ)
欧拉公式是傅里叶分析的基石。任何实信号都可以用复指数的线性组合表示。这看起来是复数运算,但实际上对应着物理世界中正弦和余弦波的叠加。
卷积定理
x(t) * h(t) ⟺ X(f) · H(f)
时域卷积等于频域乘积。这意味着滤波操作在频域中变成了简单的乘法,这就是FFT在实际中如此重要的原因。
能量与功率
E = ∫|x(t)|²dt (能量信号)
P = lim(T→∞) (1/T)∫₀ᵀ|x(t)|²dt (功率信号)
语音信号是功率信号——它的总能量是无限的,但平均功率是有限的。
📜 历史回顾
信号处理的发展
- 1807 - 傅里叶提出热传导的数学理论,包含傅里叶级数
- 1920s - Nyquist提出采样定理
- 1949 - Shannon严格证明采样定理
- 1965 - Cooley-Tukey提出快速傅里叶变换(FFT)算法
- 1970s - 数字信号处理器(DSP)芯片出现
- 1980s - MFCC特征提出,成为语音识别标准
- 2010s - 深度学习革新信号处理方法
🌍 实际应用
信号处理在生活中的应用
- 手机通话:8kHz采样+编码压缩+噪声抑制
- 音乐流媒体:44.1kHz采样+MP3/AAC压缩
- 语音助手:16kHz采样+特征提取+ASR
- 助听器:实时信号增强+频率补偿
- 医疗超声:信号发射+回波处理+成像
- 地震监测:低频信号分析+模式识别
🔑 关键概念总结
本课核心概念:正弦波、采样定理、分贝标度、Nyquist频率、混叠、量化
掌握这些概念是理解后续课程的基础。建议用自己的话总结每个概念的含义和作用。
📚 参考资料
- Oppenheim《离散时间信号处理》
- Rabiner《数字语音处理理论基础》
- Gold《语音与音频信号处理》
🔬 进阶实验:声音叠加与拍频
拍频现象
当两个频率接近的正弦波叠加时,会产生"拍"——幅度周期性变化的现象:
x(t) = sin(2πf₁t) + sin(2πf₂t) = 2cos(2π(f₁-f₂)t/2) · sin(2π(f₁+f₂)t/2)
拍频 f_beat = |f₁ - f₂|。这是乐器调音的原理——调到同频时拍频消失。
实验代码
import numpy as np
sr = 16000; t = np.linspace(0, 2, 2*sr, endpoint=False)
f1, f2 = 440, 442 # 差2Hz
beat = np.sin(2*np.pi*f1*t) + np.sin(2*np.pi*f2*t)
# 每秒拍2次 = |442-440|
envelope = np.abs(np.sin(2*np.pi*1*t)) # 包络
print(f"拍频: {abs(f2-f1)}Hz, 每秒{abs(f2-f1)}次脉动")
print("✅ 拍频实验验证通过")
🧮 信号运算
线性时不变(LTI)系统
语音处理系统大多是LTI系统。LTI系统的核心性质:
- 线性:T[ax₁+bx₂] = aT[x₁]+bT[x₂]
- 时不变:T[x(t-τ)] = y(t-τ)
- 卷积:输出=输入*系统冲激响应
常见信号运算
| 运算 | 时域 | 频域 |
| 加法 | x₁(t)+x₂(t) | X₁(f)+X₂(f) |
| 乘法 | x₁(t)·x₂(t) | X₁(f)*X₂(f) |
| 卷积 | x₁(t)*x₂(t) | X₁(f)·X₂(f) |
| 时移 | x(t-τ) | X(f)·e^(-j2πfτ) |
| 缩放 | x(at) | |a|⁻¹X(f/a) |
🛠️ 工具链
Python信号处理工具
| 库 | 用途 | 安装 |
| numpy | 数值计算基础 | pip install numpy |
| scipy | 信号处理算法 | pip install scipy |
| librosa | 音频分析 | pip install librosa |
| soundfile | 音频IO | pip install soundfile |
| matplotlib | 可视化 | pip install matplotlib |
🐛 调试技巧
常见问题与解决
1) 检查信号范围和类型 2) 验证采样率一致性 3) 确认FFT输出对称性 4) 用已知信号(正弦波)测试 5) 可视化每一步结果
📋 阶段总览
信号处理是语音识别的基石。从声音的物理本质出发,我们学会了用数学描述信号(正弦波模型),通过采样将模拟信号数字化(Nyquist定理),用傅里叶变换从时域进入频域(FFT),用STFT分析时变信号(语谱图),提取人耳感知相关的特征(MFCC),以及完整的音频预处理流程。这些知识将贯穿整个课程——每一次ASR系统都从信号处理开始。
📖 知识拓展
与本课相关的核心论文
本课涉及的核心学术贡献是语音信号处理领域的重要里程碑。理解这些工作的动机、方法和贡献,有助于建立对领域的全局认知。
工程实践建议
在实际项目中,以下几点特别重要:
- 数据质量:花80%的时间在数据清洗上,比调模型更有效
- 基线优先:先建立简单基线,再逐步改进
- 度量驱动:用WER/CER/SNR等客观指标指导优化
- 版本控制:记录每次实验的配置和结果
- 可复现:固定随机种子,记录所有超参数
常见误区
- 盲目增加模型复杂度而不改进数据
- 忽略采样率和编码格式的差异
- 在训练集上过拟合,忽视跨域泛化
- 忽略推理速度和内存约束
- 不做A/B测试就上线新模型
🔗 跨课关联
本课内容与课程其他部分的联系:
- 本课的信号处理基础将在第4课(STFT)和第5课(MFCC)中深入应用
- 理解频域分析是掌握声学模型(第7-12课)的前提
- 音频预处理技术(第6课)直接影响端到端模型(第13-18课)的性能
- 语音合成(第19-24课)的声码器也依赖信号处理原理
- 实战项目(第25-30课)需要综合运用本课所有知识
💡 学习建议
如何高效学习本课
- 先理解概念:不要急于写代码,先确保理解每个概念的物理意义
- 动手实验:修改代码参数,观察输出变化,建立直觉
- 可视化思考:画出信号流程图,将抽象概念具象化
- 教别人:用自己的话解释概念,是检验理解的最好方法
- 做练习:完成本课练习题,巩固知识
🔬 实验:声音的物理测量
用Python测量声音特性
import numpy as np
# 测量不同乐音的谐波结构
sr = 44100; t = np.linspace(0, 0.5, sr//2, endpoint=False)
# 钢琴音色:丰富的奇次谐波
piano = np.sin(2*np.pi*261.63*t)
for h in [2,3,4,5,6,7,8]:
piano += (0.8**h) * np.sin(2*np.pi*261.63*h*t)
# 长笛音色:主要为基频+少量谐波
flute = np.sin(2*np.pi*261.63*t) + 0.15*np.sin(2*np.pi*261.63*2*t)
# 小提琴音色:强奇次谐波+揉弦
violin = np.zeros_like(t)
for h in [1,3,5,7]:
vibrato = np.sin(2*np.pi*5.5*t) * 3 # 5.5Hz揉弦
violin += (0.7**h) * np.sin(2*np.pi*(261.63*h + vibrato)*t)
for name, sig in [('钢琴', piano), ('长笛', flute), ('小提琴', violin)]:
sig = sig / np.max(np.abs(sig))
rms = np.sqrt(np.mean(sig**2))
peak = np.max(np.abs(sig))
crest = peak / rms # 峰值因子
print(f"{name}: RMS={rms:.4f}, Peak={peak:.4f}, Crest Factor={crest:.2f}")
print("✅ 音色分析完成")
🧮 声学基础计算
声压级与声强级
SPL = 20·log₁₀(P/P₀) dB (声压级)
SIL = 10·log₁₀(I/I₀) dB (声强级)
在空气中,SPL ≈ SIL。声强I与声压P的关系:I = P²/(ρc),其中ρ为空气密度,c为声速。
混响时间
T₆₀ = 0.161·V/A (Sabine公式)
V为房间体积(m³),A为总吸声量(m²)。语音识别中,混响时间>0.5秒会显著降低准确率。
📋 代码模板:信号生成器
import numpy as np
class SignalGenerator:
def __init__(self, sr=16000):
self.sr = sr
def sine(self, freq, duration, amp=1.0, phase=0):
t = np.linspace(0, duration, int(self.sr*duration), endpoint=False)
return amp * np.sin(2*np.pi*freq*t + phase)
def chirp(self, f0, f1, duration):
t = np.linspace(0, duration, int(self.sr*duration), endpoint=False)
return np.sin(2*np.pi*(f0*t + (f1-f0)/(2*duration)*t**2))
def noise(self, duration, amp=0.1):
return amp * np.random.randn(int(self.sr*duration))
def composite(self, freqs_amps, duration):
sig = np.zeros(int(self.sr*duration))
t = np.linspace(0, duration, len(sig), endpoint=False)
for freq, amp in freqs_amps:
sig += amp * np.sin(2*np.pi*freq*t)
return sig
gen = SignalGenerator()
sig = gen.composite([(440,1.0),(880,0.5),(1320,0.25)], 1.0)
print(f"合成信号: {len(sig)}样本, RMS={np.sqrt(np.mean(sig**2)):.4f}")
print("✅ 信号生成器验证通过")
✅ 随堂测验
Q1: 人耳可听频率范围是?
→ 20Hz-20kHz
Q2: Nyquist定理要求采样率至少是?
→ 信号最高频率的2倍
Q3: 分贝增加20dB对应声压增加多少倍?
→ 10倍
🎵
🏆 成就解锁:声音探索者
恭喜完成本课!
✅ 信号基础 ✅ 波形生成 ✅ 分贝计算