3. 音频信号特性

好,咱们进入正题。这一章,我带你亲手摸一摸音频数据长什么样。说白了,就是让你学会用 Python 把音频文件“拆开”,看看里面到底是什么东西。

我个人习惯把音频处理比作“听诊”。你总得先学会怎么拿听诊器,怎么听心跳,才能去诊断问题。Librosa 就是咱们的听诊器。

3.1 Librosa 库的安装与基础环境

安装这事,其实没啥好说的。但我还是想提醒一句:千万别用系统自带的 Python 环境。我刚开始学的时候,直接在系统 Python 里 pip install,结果把系统依赖搞崩了,重装了两次系统……血的教训。

建议你先建一个虚拟环境:

# 创建虚拟环境
python -m venv audio_dsp_env

# 激活(Windows)
audio_dsp_env\Scripts\activate

# 激活(Mac/Linux)
source audio_dsp_env/bin/activate

# 安装核心库
pip install librosa
pip install numpy matplotlib soundfile
小提示:Librosa 依赖底层音频解码库。如果你在 Windows 上遇到安装失败,去下载 Microsoft Visual C++ Redistributable 装上就好。我踩过这个坑,折腾了一下午。

装好之后,验证一下:

import librosa
print(librosa.__version__)
# 输出类似:0.10.1

能打印出版本号,说明环境通了。嗯,这一步很简单,但别跳过。

3.2 读取与写入音频文件

音频文件读取,是后续所有操作的基础。Librosa 的 load 函数,干的就是这事。

3.2.1 读取音频文件

import librosa

# 读取音频文件
# 返回两个东西:音频时间序列 y,采样率 sr
y, sr = librosa.load('example.wav', sr=22050)

print(f"音频时长: {len(y)/sr:.2f} 秒")
print(f"采样率: {sr} Hz")
print(f"样本点数: {len(y)}")

这里有个关键参数 sr。默认是 22050 Hz,也就是 Librosa 会把所有音频重采样到这个采样率。为什么是 22050?因为人耳能听到的频率范围大约是 20 Hz ~ 20 kHz,根据奈奎斯特定理,采样率至少要是最高频率的两倍。22050 Hz 刚好能覆盖 20 kHz,而且文件大小适中。

注意:如果你需要保留原始采样率,传 sr=None。我曾经在处理 48 kHz 录音时忘了这个参数,结果分析出来的频谱全偏了,排查了半天才发现是重采样搞的鬼。

3.2.2 写入音频文件

处理完音频,有时候需要保存结果。Librosa 本身不直接提供写入功能,但可以用 soundfile 库:

import soundfile as sf

# 写入 WAV 文件
sf.write('output.wav', y, sr)

# 写入 FLAC 文件(无损压缩)
sf.write('output.flac', y, sr)

写入时要注意数据类型。默认是 float64,但很多播放器只认 int16。你可以这样转:

import numpy as np

# 转成 int16
y_int16 = (y * 32767).astype(np.int16)
sf.write('output_int16.wav', y_int16, sr)

3.3 音频可视化:波形图与频谱图

光看数字没感觉。你得把音频画出来,一眼就能看出门道。

3.3.1 波形图

波形图就是音频信号的“心电图”。横轴是时间,纵轴是振幅。

import matplotlib.pyplot as plt
import librosa.display

# 读取音频
y, sr = librosa.load('example.wav')

# 绘制波形图
plt.figure(figsize=(12, 4))
librosa.display.waveshow(y, sr=sr)
plt.title('波形图')
plt.xlabel('时间 (秒)')
plt.ylabel('振幅')
plt.tight_layout()
plt.show()

波形图能告诉你什么?

  • 静音段:振幅接近 0 的区域
  • 瞬态:振幅突然变大的尖峰,比如鼓点、拍手声
  • 包络:整体振幅的起伏,能看出声音的“响度轮廓”

我在做语音端点检测时,就是靠波形图先肉眼观察静音段的位置,再写算法去自动检测。说白了,可视化是调试的“眼睛”。

3.3.2 频谱图

波形图只告诉你“响不响”,但没告诉你“是什么频率在响”。频谱图(Spectrogram)补上了这个缺口。

# 计算频谱图
D = librosa.amplitude_to_db(
    np.abs(librosa.stft(y)), 
    ref=np.max
)

# 绘制频谱图
plt.figure(figsize=(12, 6))
librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='hz')
plt.colorbar(format='%+2.0f dB')
plt.title('频谱图')
plt.xlabel('时间 (秒)')
plt.ylabel('频率 (Hz)')
plt.tight_layout()
plt.show()

频谱图里,横轴还是时间,纵轴变成了频率。颜色越亮,表示该频率的能量越强。

你想想看,一段钢琴声和一段吉他声,波形图可能看起来差不多,但频谱图绝对不一样。钢琴的泛音更丰富,频谱上能看到很多等间距的亮线。

核心概念:STFT(短时傅里叶变换)是频谱图的基础。它把音频切成一小段一小段(帧),对每一帧做 FFT,然后把结果拼起来。Librosa 的 stft 函数默认帧长 2048 点,帧移 512 点。这些参数会影响频谱图的时间分辨率和频率分辨率——帧越长,频率分辨率越高,但时间分辨率越低。这是个 trade-off。

3.4 基本特征提取

可视化是给人看的。但机器看不懂图,它需要数字特征。这一节,我带你提取两个最基础的特征:过零率和短时能量。

3.4.1 过零率(Zero Crossing Rate, ZCR)

过零率,说白了就是单位时间内,音频信号穿过零轴的次数。计算公式很简单:

ZCR = (1 / (N-1)) * Σ |sign(x[n]) - sign(x[n-1])|

其中 sign 是符号函数,x[n] 是第 n 个样本点。

Librosa 一行搞定:

# 计算过零率
zcr = librosa.feature.zero_crossing_rate(y)

print(f"过零率形状: {zcr.shape}")
print(f"平均过零率: {zcr.mean():.4f}")

过零率有什么用?

  • 区分有声/无声:无声段的过零率通常很低(接近 0),有声段会高一些
  • 区分浊音/清音:浊音(元音)过零率低,清音(摩擦音如 /s/)过零率高
  • 音乐类型识别:金属乐过零率通常比古典乐高

我记得有一次做语音活动检测(VAD),单纯用能量阈值效果很差,背景噪声一高就误判。后来加上过零率做辅助判断,准确率直接提升了 15%。

3.4.2 短时能量(Short-Time Energy, STE)

短时能量衡量的是某一帧内信号的能量大小。公式:

E = Σ (x[n])²

Librosa 没有直接的短时能量函数,但我们可以自己算:

import numpy as np

# 分帧
frame_length = 2048
hop_length = 512

# 计算每帧能量
energy = np.array([
    np.sum(np.square(y[i:i+frame_length]))
    for i in range(0, len(y) - frame_length, hop_length)
])

# 或者用 Librosa 的 rms(均方根)来算
rms = librosa.feature.rms(y=y, frame_length=frame_length, hop_length=hop_length)
# rms 是均方根,能量 = rms² * frame_length
energy_from_rms = (rms[0] ** 2) * frame_length

print(f"短时能量形状: {energy.shape}")
print(f"最大能量: {energy.max():.4f}")

短时能量的应用场景:

  • 端点检测:能量突然上升的地方,往往是语音的开始
  • 节拍检测:音乐中鼓点的能量峰值很明显
  • 动态范围压缩:根据能量调整增益
经验之谈:短时能量对幅度非常敏感。同一个人的同一句话,离麦克风近一点和远一点,能量能差好几倍。所以实际项目中,我通常会对能量做归一化,或者用对数能量(dB)来替代线性能量。

3.5 本章知识体系总览

下面这张图,把本章的核心知识点串起来了。你可以把它当作一个“地图”,随时回来对照。

第3章 知识体系总览 第3章:Python音频处理入门 - 知识体系 音频数据 (y, sr) 读取与写入 可视化 特征提取 load() write() 波形图 频谱图 过零率 短时能量 核心流程 1. 读取音频 → 得到 y (波形) 和 sr (采样率) 2. 可视化 → 波形图看幅度变化,频谱图看频率分布 3. 特征提取 → 过零率看信号“粗糙度”,短时能量看“响度” 4. 写入 → 处理完的结果保存为音频文件

3.6 本章小结

这一章,咱们干了四件事:

  1. 装了 Librosa——记住用虚拟环境,别像我一样踩坑
  2. 读了写了音频文件——load 和 write 是基本功,采样率参数要留心
  3. 画了波形图和频谱图——波形图看“响不响”,频谱图看“是什么频率在响”
  4. 提取了过零率和短时能量——两个最基础的特征,但非常实用

这些东西,说白了就是音频 DSP 的“加减乘除”。后面所有的算法——滤波、降噪、特征工程、深度学习——都建立在这些基础操作之上。你把这章的东西练熟了,后面的路就好走了。

嗯,去打开你的 Python 环境,找一段音频文件试试吧。动手才是最好的学习方式。