第01课:声音与信号基础

信号处理 第1课/30

🎯 本课目标

理解声音的物理本质,掌握信号的基本概念,学会用Python生成和可视化声音信号。

📖 声音是什么?

声音是机械波在介质中的传播。声波通过空气粒子的振动传递能量,到达人耳后引发鼓膜振动,最终被大脑感知为声音。

声音的三个核心属性:

📐 信号的数学描述

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(心理声学)

人耳对声音的感知并非线性:

🔬 数学背景

欧拉公式

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 (功率信号)

语音信号是功率信号——它的总能量是无限的,但平均功率是有限的。

📜 历史回顾

信号处理的发展

🌍 实际应用

信号处理在生活中的应用

🔑 关键概念总结

本课核心概念:正弦波、采样定理、分贝标度、Nyquist频率、混叠、量化

掌握这些概念是理解后续课程的基础。建议用自己的话总结每个概念的含义和作用。

📚 参考资料

🔬 进阶实验:声音叠加与拍频

拍频现象

当两个频率接近的正弦波叠加时,会产生"拍"——幅度周期性变化的现象:

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系统的核心性质:

常见信号运算

运算时域频域
加法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音频IOpip install soundfile
matplotlib可视化pip install matplotlib

🐛 调试技巧

常见问题与解决

1) 检查信号范围和类型 2) 验证采样率一致性 3) 确认FFT输出对称性 4) 用已知信号(正弦波)测试 5) 可视化每一步结果

📋 阶段总览

信号处理是语音识别的基石。从声音的物理本质出发,我们学会了用数学描述信号(正弦波模型),通过采样将模拟信号数字化(Nyquist定理),用傅里叶变换从时域进入频域(FFT),用STFT分析时变信号(语谱图),提取人耳感知相关的特征(MFCC),以及完整的音频预处理流程。这些知识将贯穿整个课程——每一次ASR系统都从信号处理开始。

📖 知识拓展

与本课相关的核心论文

本课涉及的核心学术贡献是语音信号处理领域的重要里程碑。理解这些工作的动机、方法和贡献,有助于建立对领域的全局认知。

工程实践建议

在实际项目中,以下几点特别重要:

常见误区

🔗 跨课关联

本课内容与课程其他部分的联系:

💡 学习建议

如何高效学习本课

  1. 先理解概念:不要急于写代码,先确保理解每个概念的物理意义
  2. 动手实验:修改代码参数,观察输出变化,建立直觉
  3. 可视化思考:画出信号流程图,将抽象概念具象化
  4. 教别人:用自己的话解释概念,是检验理解的最好方法
  5. 做练习:完成本课练习题,巩固知识

🔬 实验:声音的物理测量

用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倍

🎵

🏆 成就解锁:声音探索者

恭喜完成本课!

✅ 信号基础 ✅ 波形生成 ✅ 分贝计算