网友作品 #000130 · 提交于 2026/08/11
音色分析工具:示波器CSV声音特征分析对比







1 / 7
作品简介
这是一款分析声音音色的桌面工具:导入示波器导出的「时间、电压」两列 CSV(如 SDS2202X Plus 等型号),程序自动完成基频估计、音名换算、谐波与频谱特征计算,并以文字结论、特征指标表、可视化图表三种形式呈现结果,还支持多个文件叠加对比音色差异。已用 PyInstaller 打包成单文件 exe,双击即可运行,无需安装任何环境。
界面左侧为已加载文件列表,右侧提供概览、波形、频谱、谐波、语谱图、对比六个标签页,随时切换查看;点「导出报告」可把当前文件的分析结论、数据表和图片一次性写出。
核心亮点
- 一站式分析:自动解析示波器 CSV(识别采样间隔、通道等元数据,多层容错降级),输出 20 余项音色特征
- 音高识别:用谐波积谱法估计基频,按十二平均律换算音名、八度与音分偏差
- 指标齐全:频谱质心、带宽、85% 滚降、平坦度、过零率、THD、谐波能量占比等 15 项特征指标表,附逐次谐波数据表
- 五类图表:波形(整体+细节)、频谱、谐波柱状图、语谱图、多文件归一化频谱对比,支持缩放平移
- 中文文字结论:自动生成亮度、纯净度、谐波丰富度的人类可读描述,可直接引用进报告
- 多文件对比:一次加载多个 CSV 后台分析,对比页汇总各文件的基频、音名、质心、THD、平坦度差异
- 一键导出报告:文字结论 TXT、特征表与谐波表 CSV、图表 PNG 一次写出
- 免安装运行:打包为单文件 exe 双击即用,CSV 导出采用 UTF-8-BOM,Excel 打开不乱码
技术与工具
- 技术栈:Python 3.12 + tkinter 界面,numpy/scipy 做信号处理,matplotlib 绘图,uv 管理依赖
- 架构分层:分析核心、绘图、界面三个模块解耦,核心模块不依赖 GUI、可单独测试复用;文件解析在后台线程执行,界面不卡顿
- 质量保障:pytest 单元测试用合成信号校验基频精度与谐波检测,ruff/mypy 做静态检查
- 日志系统:按日期分文件、单文件超 5MB 自动轮转、启动时清理 30 天前的旧日志
- 开发方式:作者为业余水平,借助 AI 编程类工具与国内免费大模型完成
开发过程
- 项目按「核心计算—可视化—界面」分层搭建,约 1300 行源码、6 个 Python 文件
- 先用 440Hz、200Hz 加谐波等合成信号做单元测试验证算法精度,再用 PyInstaller 打包成约 95MB 的单文件 exe 交付
适用场景
- 乐器与发声装置的音色测评:对比不同样品的基频、亮度、谐波结构,辅助调音与选材
- 声学课程实验与科研:把示波器采集的声音数据快速变成量化指标和报告图表
原始提交内容(点开查看,可复制全文去问 AI)
timbre-analyzer — 声音音色分析工具
项目定位
一个基于 Python + tkinter + matplotlib 的桌面 GUI 程序,用于分析示波器导出的声音电压 CSV 数据,自动提取音色特征并以文字结论、特征表格、可视化图表三种形式展示,并附带多文件频谱对比功能。已完成打包,双击 .exe 即可运行,无需安装任何环境。
目录结构
timbre_analyzer/
src/timbre_analyzer/ 源代码(6 个 .py 文件,~1300 行)
__init__.py 包入口
main.py 程序入口(配置日志 启动 GUI)
logging_setup.py 日志系统(按日期分文件、自动轮转、过期清理)
core.py 音色分析核心(423 行,无 GUI 依赖)
plotting.py 可视化绘制(232 行,5 张图)
ui/
app.py tkinter GUI 界面(401 行)
tests/
test_core.py pytest 单元测试(3 项,合成信号验证)
scripts/
build_exe.py PyInstaller 打包脚本(产出一个 .exe)
dist/
音色分析工具.exe 打包成品(~95MB,双击运行)
assets/ 资源目录(图标等)
logs/ 运行时日志
pyproject.toml 项目元数据、依赖、脚本入口、lint 配置
uv.lock 锁定依赖版本
音色分析工具.spec PyInstaller 规范文件(自动生成)
模块职责
1. core.py — 分析核心(423 行)
不依赖任何 GUI / matplotlib,可脱离界面单独测试或复用。内含 6 个公开函数、5 个 dataclass 数据结构:
| 类/函数 | 职责 |
|---|---|
SignalData |
信号数据对象(时间轴、电压、采样率、来源通道、文件名) |
HarmonicRow |
单个谐波信息(次数、频率、幅值、相对 dB/百分比) |
TimbreFeatures |
音色特征汇总(20+ 项指标) |
AnalysisResult |
完整分析结果(信号 + 特征 + 谐波表 + 频谱数据) |
load_oscilloscope_csv() |
解析 SDS2202X Plus 等示波器的 CSV(自动识别元数据 + 数据分界,退化为全文件扫描) |
analyze_timbre() |
一站式分析入口:基频 音名 谐波 频谱特征 时域特征 文字描述 |
build_summary_text() |
生成人类可读的中文分析结论 |
build_feature_table() |
生成 15 项特征指标表(指标名/数值/说明) |
build_harmonic_table() |
生成谐波数据表(次数/频率/相对幅值/相对 dB) |
build_comparison_table() |
生成多文件对比表(文件名/基频/音名/质心/THD/平坦度/丰富度) |
分析算法
| 步骤 | 方法 | 细节 |
|---|---|---|
| CSV 解析 | 正向扫描 + 退化为数值行检测 | 先识别元数据(Sample Interval / Source / Second 表头),找不到表头则退化扫描首行含两个浮点数的位置 |
| 基频 F0 | 谐波积谱 HPS(Harmonic Product Spectrum) | 对幅度谱做 2/3/4/5 倍降采样后相乘,突出真正的基频;再用抛物线插值细化峰值位置 |
| 音名换算 | 十二平均律公式 | MIDI = 69 + 12·log₂(f/440),反算音名 + 八度 + 音分偏差 |
| 谐波提取 | 局部峰值搜索 | 在 f0×n ± 3% 范围内取最大值,容忍轻微失谐;约束至奈奎斯特频率,最多 12 次谐波 |
| THD | (Σ谐波功率) / 基频幅值 |
涵盖 2–12 次谐波 |
| 频谱质心 | Σ(freqs × mag) / Σ(mag) |
亮度指标,越高越明亮 |
| 频谱带宽 | (Σ((f-centroid)² × mag) / Σ(mag)) |
频带集中程度 |
| 频谱滚降 85% | 累积能量 searchsorted | 85% 能量所在的频率上限 |
| 频谱平坦度 | geometric_mean(power²) / arithmetic_mean(power²) |
0纯乐音,1白噪声 |
| 过零率 | `Σ( | Δsign |
| 文字描述 | 三级阈值决策 | 质心亮度,平坦度纯净度,THD谐波丰富度 |
2. plotting.py — 可视化(232 行)
5 个函数,统一签名 (result, fig=None) Figure,可被 GUI 嵌入或独立 savefig 导出:
| 函数 | 图表 | 说明 |
|---|---|---|
plot_waveform() |
波形图 | 上下双图:整体概览 + 几个周期的细节(自动抽稀防卡顿) |
plot_spectrum() |
频谱图 | 幅度谱,标注基频、各次谐波位置、频谱质心 |
plot_harmonics() |
谐波柱状图 | 各次谐波相对基频百分比,柱上标注数值 |
plot_spectrogram() |
语谱图 | 时频能量分布 heatmap(scipy spectrogram) |
plot_spectrum_comparison() |
多文件频谱对比 | 多条频谱叠加,按各自峰值归一化后对比音色形状 |
技术细节:
- 全局配置
rcParams设置中文字体(Microsoft YaHei / SimHei fallback),支持负号显示 - 波形抽稀采用均匀步长采样(
x[::step]),最大 4000 点 - 频谱横轴上限自适应:
max(F0×12, 2000),不丢高频成分 - 对比图横轴上限取所有文件奈奎斯特频率的最小值,保证所有曲线完整展示
3. ui/app.py — GUI 界面(401 行)
TimbreApp 类,基于 tkinter + ttk + matplotlib 嵌入。界面布局:
[选择 CSV 文件…] [导出报告] [打开日志目录] 提示文字 工具栏
已加载 概览 波形 频谱 谐波 语谱图 对比
文件 文字 图像 图像 柱状图 heatmap 叠加谱
列表 结论 +工具条 +工具条 +数据表 +工具条 +对比表
+表格
状态栏:就绪 | 日志目录:...
关键交互:
- 多文件加载:一次可选中多个 CSV,后台线程解析分析(
threading.Thread),完成后root.after回调更新 UI - 文件列表切换:左侧 Listbox,点击切换当前查看的文件,自动刷新所有标签页
- 对比标签页:始终展示全体已加载文件的频谱叠加 + 特征对比表
- matplotlib 导航工具栏:每个图像标签页附带缩放/平移/保存等标准按钮
- 导出报告:一键将当前文件的文字结论(
.txt)、特征表 + 谐波表(.csv)、4 张图片(.png)写入指定目录 - 状态栏:实时显示日志目录路径与当前操作状态
4. logging_setup.py — 日志系统(143 行)
自研 DateSizedFileHandler(继承 logging.Handler),核心特性:
| 特性 | 实现 |
|---|---|
| 按日期分文件 | 格式 app-YYYY-MM-DD[-N].log |
| 大小轮转 | 单文件超过 5MB 自动写 -1、-2 后缀文件 |
| 过期清理 | 每次启动时删除 30 天前修改的日志 |
| 双通道输出 | 同时写文件 + 控制台(StreamHandler) |
| frozen 兼容 | 打包后 sys._MEIPASS / sys.executable 自适应路径 |
.env 支持 |
通过 LOG_LEVEL 环境变量控制日志级别 |
| 一键打开目录 | open_log_dir() 跨平台(Windows/macOS/Linux)用系统资源管理器打开日志目录 |
5. main.py — 程序入口(19 行)
configure_logging() LOGGER.info("启动") launch_app() root.mainloop()
同时注册为 pyproject.toml 中的 console_scripts 入口 timbre-analyzer,支持 uv run timbre-analyzer 启动。
分析维度汇总(15 项)
| 类别 | 指标 | 含义 |
|---|---|---|
| 基本信息 | 采样率、时长、样本数 | 数据质量参考 |
| 时域 | RMS 有效值、峰峰值、直流偏置、峰值因数 | 信号幅度特征 |
| 音高 | 基频 F0(HPS 估计)、音名、音分偏差 | 音调判断 |
| 频谱 | 质心、带宽、85% 滚降、平坦度、过零率 | 音色亮度与纯净度 |
| 谐波 | 各次谐波频率与幅值、THD、能量占比 | 音色厚度与丰富度 |
| 主观 | 亮度/纯净度/谐波丰富度文字描述 | 人类可读的结论 |
技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| Python | 3.12 | 运行环境 |
| uv | latest | 包管理、虚拟环境、脚本入口 |
| tkinter / ttk | 内置 | 图形界面(窗口、控件、主题) |
| numpy | 2.5.1 | 数值计算、FFT 频谱 |
| scipy | 1.18.0 | 信号处理(spectrogram、统计) |
| matplotlib | 3.11.1 | 图表渲染(嵌入 tkinter) |
| python-dotenv | 1.0.1 | .env 环境变量加载 |
| hatchling | — | 构建系统(pyproject.toml) |
| PyInstaller | 6.21.0 | 打包成单文件 exe |
| pytest | — | 单元测试 |
| ruff / mypy | — | lint 与类型检查 |
开发与测试
运行
cd D:\BACKUP\ZF\李伟坚\python-dev_FXYS\timbre_analyzer
uv run timbre-analyzer
测试
uv run pytest -q
3 项测试:
test_estimate_fundamental_frequency— 合成 440Hz 信号,验证 F0 精度 ±3Hztest_harmonics_and_tables— 合成 200Hz + 2/3 次谐波信号,验证谐波检测与表格生成test_log_file_name— 验证日志文件名格式
打包
uv run python scripts/build_exe.py
产物:dist/音色分析工具.exe(单文件,~95MB,windowed 模式无控制台)。
输入数据格式
支持 SDS2202X Plus 等示波器导出的 CSV,结构为:
Record Length,Sample Interval, ...
200000,2.5E-05
Source,CH1
Second,Value
-2.499988,-0.000010
-2.499962,-0.000010
...
解析策略:
- 扫描元数据行(大小写不敏感匹配
sample interval/source) - 定位数据表头(匹配
second开头的行) - 若无显式表头,退化扫描第一个可解析为两个浮点数的行
- 调用
np.genfromtxt加载数据列 - 采样率优先取元数据中的
Sample Interval倒数,次选时间列中位数差推导
设计原则
- 分层解耦:
core纯计算plotting纯绘图ui纯界面,三个模块可独立测试和复用 - 线程安全:文件解析与分析在后台线程执行,UI 更新通过
root.after回主线程 - 自校验启动:
configure_logging()在main入口即调用,任何后续异常都会被日志捕获 - frozen 兼容:
sys.frozen+sys._MEIPASS判断,打包后路径自适应 - 中文友好:matplotlib 配置中文字体 fallback 链,UI 全中文,CSV 导出使用 UTF-8-BOM 确保 Excel 打开不乱码
- 防御性解析:CSV 解析多层 fallback(元数据 表头 退化扫描),单文件失败不影响其他文件继续分析