2. 声学基础

说实话,很多初学者一上来就急着写代码,结果卡在环境配置上半天出不来。我见过太多人因为Python版本冲突、库装不上、音频设备打不开,直接劝退了。这一章,咱们就把这些坑一个个填平。

环境搭建这事儿,说白了就是给你的电脑装上合适的工具。就像做木工活之前得先备好刨子、凿子一样。咱们做麦克风阵列信号处理,需要一套专门的Python工具箱。

本章核心目标:搭建一个稳定、可复用的Python音频处理环境,掌握音频文件读写、播放、录制的基础操作。

2.1 Python虚拟环境:为什么必须用?

我刚开始做项目时,所有库都装在全局环境里。结果有一次,项目A需要NumPy 1.19,项目B需要NumPy 1.24,两个库互相打架,搞得我重装了三次系统。从那以后,我养成了一个习惯——每个项目单独建虚拟环境。

虚拟环境说白了就是一个隔离的Python运行空间。每个环境有自己的库,互不干扰。你想想看,这就像每个项目有自己的工具箱,不会把螺丝刀和扳手混在一起。

创建虚拟环境的两种方式

方式 命令 特点
venv(内置) python -m venv myenv Python 3.3+自带,轻量,够用
conda conda create -n myenv python=3.9 适合科学计算,管理非Python依赖更方便

我个人习惯用venv,因为它轻量、不需要额外装东西。但如果你做大型项目,conda的包管理确实更省心。

小技巧:虚拟环境的名字最好跟项目名相关,比如 mic_array_env。这样时间长了也不会搞混。

激活与退出

# Windows
myenv\Scripts\activate

# macOS / Linux
source myenv/bin/activate

# 退出
deactivate

激活后,终端前面会出现 (myenv) 的提示。嗯,看到这个就说明你已经在虚拟环境里了,可以放心装库了。

2.2 NumPy与SciPy:音频计算的基石

做音频信号处理,NumPy和SciPy是绕不开的两个库。NumPy提供高效的数组操作,SciPy则封装了大量信号处理算法。说白了,NumPy是咱们的"手",SciPy是咱们的"工具箱"。

安装命令

pip install numpy scipy

这里有个坑——如果你用conda,建议用 conda install numpy scipy,因为conda会自动处理底层依赖(比如BLAS库),性能会好一些。我在项目中遇到过,同样的FFT计算,conda装的NumPy比pip装的快了将近30%。

快速验证

import numpy as np
import scipy.signal as signal

# 生成一个1kHz的正弦波
fs = 44100  # 采样率
t = np.arange(0, 1, 1/fs)
x = np.sin(2 * np.pi * 1000 * t)

print(f"数组长度: {len(x)}")
print(f"数据类型: {x.dtype}")

# 用SciPy做低通滤波
b, a = signal.butter(4, 500, fs=fs, btype='low')
y = signal.filtfilt(b, a, x)
print(f"滤波完成,输出长度: {len(y)}")

这段代码虽然简单,但已经包含了音频处理最核心的两个操作:生成信号和滤波。后面的章节,我们会反复用到这些基础操作。

2.3 SoundFile与PyAudio:音频I/O的左右手

音频处理离不开文件的读写和设备的交互。SoundFile负责读写音频文件,PyAudio负责跟麦克风、扬声器打交道。这两个库,一个管文件,一个管设备,分工明确。

安装

pip install soundfile pyaudio

注意:PyAudio在Windows上可能需要预装PortAudio。如果pip安装失败,可以去 Christoph Gohlke的页面 下载对应的whl文件。我曾经在这上面折腾了两个小时,后来发现直接下载whl是最省事的。

SoundFile基础用法

import soundfile as sf
import numpy as np

# 写入音频文件
fs = 44100
t = np.arange(0, 2, 1/fs)
data = np.sin(2 * np.pi * 440 * t)  # 440Hz A音
sf.write('test.wav', data, fs)

# 读取音频文件
data_read, fs_read = sf.read('test.wav')
print(f"采样率: {fs_read}, 数据形状: {data_read.shape}")

SoundFile支持多种格式:WAV、FLAC、OGG等。我个人建议在项目里统一用WAV,兼容性最好,处理起来也最直接。

PyAudio基础用法

import pyaudio
import numpy as np

# 列出所有音频设备
p = pyaudio.PyAudio()
for i in range(p.get_device_count()):
    info = p.get_device_info_by_index(i)
    print(f"设备 {i}: {info['name']}, 输入通道: {info['maxInputChannels']}")

p.terminate()

这段代码会列出你电脑上所有的音频设备。做麦克风阵列的时候,你需要从这里找到你的阵列设备编号。我一般会先跑一遍这个代码,确认设备编号后再写采集程序。

2.4 音频I/O基础操作实战

理论说完了,咱们来点实际的。下面这个例子,演示了从麦克风采集音频、保存到文件、再读取播放的完整流程。

完整流程:采集 → 保存 → 读取 → 播放

import pyaudio
import soundfile as sf
import numpy as np
import time

# 参数设置
FORMAT = pyaudio.paInt16  # 16位整数
CHANNELS = 1              # 单声道
RATE = 44100              # 采样率
CHUNK = 1024              # 每次读取的帧数
RECORD_SECONDS = 3        # 录制时长

# 初始化PyAudio
p = pyaudio.PyAudio()

# 打开输入流
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("开始录制...")
frames = []

# 采集数据
for _ in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("录制结束")

# 关闭流
stream.stop_stream()
stream.close()
p.terminate()

# 将字节数据转为NumPy数组
audio_data = np.frombuffer(b''.join(frames), dtype=np.int16)

# 保存为WAV文件
sf.write('recorded_audio.wav', audio_data, RATE)
print(f"文件已保存,长度: {len(audio_data)/RATE:.2f}秒")

# 读取并验证
data_check, fs_check = sf.read('recorded_audio.wav')
print(f"读取验证: 采样率={fs_check}, 数据长度={len(data_check)}")

这段代码虽然不长,但涵盖了音频I/O的四个核心操作:打开设备、采集数据、保存文件、读取验证。后面的项目里,你会反复用到这个模式。

避坑指南:我曾经在采集时忘记把字节数据转成NumPy数组,结果后面做FFT分析时一直报错。记住,stream.read()返回的是字节串,必须用np.frombuffer()转成数值数组才能做信号处理。

2.5 本章知识体系总览

下面这张图,把本章的核心知识点串起来了。你可以把它当作一张地图,后面遇到问题就知道该查哪个部分。

Python音频处理环境搭建知识体系 Python音频处理环境搭建 · 知识体系 Python虚拟环境 NumPy · 数组与计算 SciPy · 信号处理 SoundFile · 文件读写 PyAudio · 设备交互 读取 · 写入 · 格式转换 采集 · 播放 · 设备枚举 麦克风阵列信号处理实战 从环境到实战的完整链路

这张图展示了从虚拟环境到底层库、再到音频I/O操作、最后到实战应用的完整链路。你可以看到,虚拟环境是地基,NumPy/SciPy是计算核心,SoundFile和PyAudio是跟音频打交道的接口。后面的所有章节,都会建立在这个基础之上。

2.6 常见问题与避坑指南

  • PyAudio安装失败:Windows用户优先下载whl文件,别在pip上死磕。我曾经在conda环境里装PyAudio,结果发现conda-forge版本更稳定。
  • 音频设备找不到:先跑设备枚举代码,确认设备编号。麦克风阵列通常有多个输入通道,选设备时注意看maxInputChannels。
  • 采样率不匹配:采集和播放的采样率必须一致。我遇到过采集用44100,播放用48000,结果声音变调了。
  • 数据类型搞混:SoundFile默认返回float64,PyAudio采集返回int16。做FFT分析前,记得统一数据类型。

重要提醒:虚拟环境一定要在项目一开始就建好。中途换环境,所有库都得重装,依赖关系还可能出问题。我有个同事,项目做了一半才想起来建虚拟环境,结果迁移时花了整整一天。