PrettyMIDI:让 Python 处理 MIDI 文件从繁琐变简单的实用库
2026/8/24 17:30:56 网站建设 项目流程

PrettyMIDI:让 Python 处理 MIDI 文件从繁琐变简单的实用库

【免费下载链接】pretty-midiUtility functions for handling MIDI data in a nice/intuitive way.项目地址: https://gitcode.com/gh_mirrors/pr/pretty-midi

打开一个.mid文件,你期望得到"音符、时间、乐器",实际得到的却是一堆事件流。标准 MIDI 文件的底层结构面向的是"tick(时基)+ 通道 + 事件":一个音要拆成 note-on/note-off 两个事件,时间要按 tick 换算,速度变化是另一条 meta 事件。想做个简单的事——提取前 10 秒的钢琴卷帘、把所有音移调 5 个半音、或导出音频听个响——先要写一大段解析代码。

PrettyMIDI(包名pretty_midi)就是为省掉这段解析而生的 Python MIDI 处理库。把.mid文件交给它,拿到的是一组乐器对象,每个乐器里按秒排好序的Note列表;想取钢琴卷帘、chroma 特征、节拍位置,一行方法调用就能得到;改完还能直接合成音频或写回文件。它构建在mido之上,核心依赖只有 numpy 和 mido 两个。

先把"事件流"变成"带秒数的音符"

读一个 MIDI 文件,PrettyMIDI会替你做完三件脏活:

  • 把每个通道解析成Instrument,通道号翻译成乐器名,鼓通道自动标记is_drum
  • 把 note-on/note-off 配对成Note对象,start/end直接是,不需要你自己做 tick 换算;
  • 收集速度变化、拍号、调号,提供get_beats()get_downbeats()等按音乐逻辑取点的方法。
import pretty_midi midi = pretty_midi.PrettyMIDI('example.mid') # 仓库自带示例文件 print(midi.estimate_tempo()) # 经验估计全局速度 beats = midi.get_beats() # 按当前速度换算是秒的节拍时间

如果文件里没有速度标记,estimate_tempo()会基于音符 onset 做经验估计,这在标注缺失的素材里经常救场。

从事件流到特征:钢琴卷帘与音级统计

做音乐信息检索(MIR)时,最常要的其实是"特征",而不是事件本身。PrettyMIDI把几类常用特征做成了方法,输入是采样率,输出是二维数组:

  • get_piano_roll(fs=100):128 个音高行 × 时间列的二值/力度矩阵,直接喂给序列模型;
  • get_chroma(fs=100):每帧 12 个半音的强度曲线,是分析调性的起点;
  • get_pitch_class_histogram():全曲音高类别直方图,可作为调性的粗略代理;
  • get_onsets():所有音符起点,做节奏分析的基础。

这些方法在单个Instrument上同样存在,需要按乐器分别统计时不用再手动切通道。

改完谱面,还能"听到"结果

修改 MIDI 的常见操作——移调、裁剪、变速——在 PrettyMIDI 里都是直接操作对象:

for instrument in midi.instruments: if not instrument.is_drum: # 鼓不需要移调 for note in instrument.notes: note.pitch += 5 # 整体移调 5 个半音 audio = midi.synthesize() # 纯 numpy 正弦波合成,返回波形数组 midi.write('out.mid') # 写回标准 MIDI 文件
  • synthesize()不依赖任何外部程序,用正弦波把整个谱面渲染成波形,适合快速验证算法改动是否如预期;
  • 想要更真实的音色,fluidsynth()可以走 FluidSynth + 音源文件(SF2)路径,安装包内已附带一份 General MIDI 音源TimGM6mb.sf2
  • 时间维度的重采样用adjust_times(original_times, new_times),它会把音符、控制器、meta 事件按时间映射整体搬移,比逐个改note.start可靠;
  • crop(start_time, end_time)则负责截取片段,做数据集切分时很省事。

最短上手路径:从零写一个 .mid 文件

不需要任何输入文件,几行代码就能造出一段 MIDI 并导出:

import pretty_midi midi = pretty_midi.PrettyMIDI() cello = pretty_midi.Instrument(program=pretty_midi.instrument_name_to_program('Cello')) for name in ['C5', 'E5', 'G5']: # C 大调主和弦 note = pretty_midi.Note(velocity=100, pitch=pretty_midi.note_name_to_number(name), start=0, end=0.5) cello.notes.append(note) midi.instruments.append(cello) midi.write('cello-C-chord.mid')

音名到 MIDI 音数(note_name_to_number)、音数到频率(note_number_to_hz)、程序号到乐器名(program_to_instrument_name)这类换算都放在utilities模块里,谱面上常见的"人话 ↔ 机器数"转换基本不用自己写查表。

谁适合用它,什么时候换别的

适合:MIR 研究中的数据预处理(特征提取、对齐、切分)、教学演示、音乐数据管道的原型阶段、给音频/文本模型准备 MIDI 侧输入。tests/ 里的用例和 examples/ 能覆盖大部分日常操作。

不适合:

  • 要生产级音质时,synthesize()的正弦波渲染只够"听个响",正式发布请走fluidsynth()或专门的合成引擎(后者需自行安装 FluidSynth 程序与pyfluidsynth);
  • 素材是音频而不是 MIDI 时,特征提取应该用 librosa 这类音频 DSP 库,MIDI 解析帮不上忙;
  • 需要严格贴合标准 MIDI 规范、处理大量 raw 事件时,可以绕过它直接使用底层的 mido 库——PrettyMIDI 本质上是对 mido 的一层"人性化"封装,不是替代品;
  • 它是轻量库(当前版本 0.2.11,API 稳定但仍在演进),不适合作为大型实时音频服务的地基。

继续深入的路径

  • 文档:docs/ 下按模块拆分的 Sphinx 页面,pretty_midi.py核心类、Instrument、各类容器和utilities换算函数都有独立文档;
  • 示例:examples/ 里有四个可运行的脚本——chiptunes.py(纯 numpy 的 chiptune 风格合成)、chord_catalog.py(和弦目录)、reverse_pianoroll.py(从钢琴卷帘反向生成 MIDI)、align_midi.py(MIDI 对齐);
  • 教程:仓库根目录的 Tutorial.ipynb 是一份可交互的 Jupyter 教程,从加载到合成逐段演示;
  • 源码:核心实现在 pretty_midi/ 包内,pretty_midi.pyinstrument.pycontainers.py三个文件即可读完全貌;
  • 需要源码时,仓库地址:https://gitcode.com/gh_mirrors/pr/pretty-midi

读完一遍example.mid的加载—取特征—移调—合成流程,基本就能判断它能不能接进你自己的数据管道了。

【免费下载链接】pretty-midiUtility functions for handling MIDI data in a nice/intuitive way.项目地址: https://gitcode.com/gh_mirrors/pr/pretty-midi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询