news 2026/4/30 21:27:42

别再手动扒谱了!教你用Python把MIDI音乐转成可编辑的JSON数据

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
别再手动扒谱了!教你用Python把MIDI音乐转成可编辑的JSON数据

用Python实现MIDI与JSON互转:音乐数据的自由编辑之道

你是否曾经遇到过这样的情况——手头有一段喜欢的MIDI音乐,想要分析它的和弦走向,或者调整某个音符的时值,却苦于没有专业的音乐制作软件?又或者,你希望用编程的方式批量处理大量MIDI文件,却对复杂的二进制格式望而却步?今天,我将带你用Python搭建一座桥梁,让MIDI音乐和可读性极强的JSON数据自由转换,从此音乐编辑变得像修改文本一样简单。

1. 准备工作:理解MIDI与JSON的差异

在开始编码之前,我们需要清楚两种格式的本质区别:

  • MIDI文件:一种二进制格式的音乐协议标准,记录了音符开/关、力度、音色等事件信息。优点是体积小、兼容性强,但人类直接阅读和编辑极为困难。
  • JSON文件:轻量级的数据交换格式,采用纯文本表示,结构清晰可读。虽然文件体积较大,但非常适合程序处理和人工修改。

关键工具选择

# 核心库安装命令 pip install music21 # 强大的音乐分析库 pip install python-rtmidi # MIDI实时处理支持(可选)

提示:music21库不仅支持MIDI解析,还能直接显示乐谱、分析音乐理论特征,是我们实现转换的瑞士军刀。

2. MIDI转JSON:解码音乐数据结构

让我们先实现从MIDI到JSON的转换过程。这个过程中,我们需要提取音符的三个核心属性:

  1. 音高:用MIDI编号表示(中央C=60)
  2. 时值:以四分音符为单位的持续时间
  3. 类型:区分单音、和弦与休止符
import music21 as m21 import json def midi_to_json(midi_path, output_json): # 解析MIDI文件 score = m21.converter.parse(midi_path) music_data = { "metadata": { "title": getattr(score.metadata, 'title', 'Untitled'), "tempo": find_tempo(score) }, "notes": [] } # 遍历所有音符和休止符 for element in score.flat.notesAndRests: if isinstance(element, m21.note.Rest): music_data["notes"].append({ "type": "rest", "duration": float(element.duration.quarterLength) }) elif isinstance(element, m21.note.Note): music_data["notes"].append({ "type": "note", "pitch": element.pitch.midi, "duration": float(element.duration.quarterLength), "velocity": element.volume.velocity }) elif isinstance(element, m21.chord.Chord): music_data["notes"].append({ "type": "chord", "pitches": [n.pitch.midi for n in element.notes], "duration": float(element.duration.quarterLength), "velocity": element.volume.velocity }) # 写入JSON文件 with open(output_json, 'w') as f: json.dump(music_data, f, indent=2) def find_tempo(stream): for item in stream.flat: if isinstance(item, m21.tempo.MetronomeMark): return item.number return 120 # 默认120BPM

转换后的JSON结构示例:

{ "metadata": { "title": "Sample Song", "tempo": 120 }, "notes": [ { "type": "note", "pitch": 60, "duration": 1.0, "velocity": 80 }, { "type": "chord", "pitches": [60, 64, 67], "duration": 2.0, "velocity": 90 } ] }

3. JSON转MIDI:从数据重建音乐

逆向转换时,我们需要特别注意音乐时间的准确性。这里使用Fraction来精确处理时值:

from fractions import Fraction def json_to_midi(json_path, output_midi): with open(json_path) as f: data = json.load(f) stream = m21.stream.Stream() # 设置速度 stream.append(m21.tempo.MetronomeMark(number=data['metadata']['tempo'])) for note_data in data['notes']: duration = m21.duration.Duration(Fraction(note_data['duration'])) if note_data['type'] == 'rest': stream.append(m21.note.Rest(duration=duration)) elif note_data['type'] == 'note': note = m21.note.Note( note_data['pitch'], duration=duration ) note.volume.velocity = note_data.get('velocity', 80) stream.append(note) elif note_data['type'] == 'chord': chord = m21.chord.Chord( note_data['pitches'], duration=duration ) chord.volume.velocity = note_data.get('velocity', 90) stream.append(chord) stream.write('midi', fp=output_midi)

4. 实战应用:音乐编辑的无限可能

有了这套转换工具,你可以轻松实现各种音乐处理需求:

批量修改示例

# 将所有C音升高半音 with open('music.json') as f: data = json.load(f) for note in data['notes']: if note['type'] in ('note', 'chord'): if note['type'] == 'note': if note['pitch'] % 12 == 0: # C音 note['pitch'] += 1 else: note['pitches'] = [p+1 if p%12==0 else p for p in note['pitches']] with open('music_modified.json', 'w') as f: json.dump(data, f)

音乐分析应用

def analyze_chords(json_path): with open(json_path) as f: data = json.load(f) chord_progression = [] for note in data['notes']: if note['type'] == 'chord': chord_progression.append('-'.join(str(p%12) for p in note['pitches'])) print("和弦进行分析:") print(" -> ".join(chord_progression))

常见问题处理表格

问题现象可能原因解决方案
转换后音高错误MIDI音高偏移设置问题检查music21的pitch转换设置
时值不准确浮点数精度丢失使用Fraction保持分数时值
和弦解析异常音符重叠检测阈值调整music21的chord识别参数

5. 高级技巧:扩展音乐元数据

为了让JSON数据包含更多音乐信息,我们可以扩展元数据字段:

def enhanced_conversion(midi_path): score = m21.converter.parse(midi_path) result = { "metadata": { "title": getattr(score.metadata, 'title', None), "composer": getattr(score.metadata, 'composer', None), "time_signature": str(score.flat.getTimeSignatures()[0]), "key_signature": str(score.flat.getKeySignatures()[0]) }, "tracks": [] } # 处理多轨MIDI for part in score.parts: track = { "name": part.partName, "instrument": str(part.getInstrument()), "notes": [] } # ... 添加音符数据 ... result["tracks"].append(track) return result

注意:处理复杂MIDI文件时,建议分轨存储数据,这样在转回MIDI时能保留原始乐器分配信息。

在实际项目中,我发现music21对某些MIDI文件的解析可能存在差异,这时可以尝试先导出为MusicXML再处理。对于电子音乐制作,还可以考虑添加CC控制器数据的转换支持,让JSON能够保存弯音、调制轮等表现力参数。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 21:25:37

AI驱动三维建模:大语言模型与Rhino的智能融合实践

1. 项目概述:当AI大模型遇上三维建模最近在三维建模和AIGC的交叉领域,一个名为“GOLEM-3DMCP-Rhino”的项目引起了我的注意。这个项目名本身就充满了信息量:“GOLEM”让人联想到那个被赋予生命的泥人传说,暗示着某种“创造”或“赋…

作者头像 李华
网站建设 2026/4/30 21:23:25

Windows虚拟串口驱动:com0com零成本设备模拟解决方案

Windows虚拟串口驱动:com0com零成本设备模拟解决方案 【免费下载链接】com0com Null-modem emulator - The virtual serial port driver for Windows. Brought to you by: vfrolov [Vyacheslav Frolov](http://sourceforge.net/u/vfrolov/profile/) 项目地址: htt…

作者头像 李华
网站建设 2026/4/30 21:12:22

终极指南:如何使用ROFL播放器轻松查看所有英雄联盟回放文件

终极指南:如何使用ROFL播放器轻松查看所有英雄联盟回放文件 【免费下载链接】ROFL-Player (No longer supported) One stop shop utility for viewing League of Legends replays! 项目地址: https://gitcode.com/gh_mirrors/ro/ROFL-Player 你是否曾经因为游…

作者头像 李华
网站建设 2026/4/30 21:08:25

别再死记硬背了!一张图帮你理清电力通信规约(IEC104/Modbus/MQTT)的应用场景与选择逻辑

电力通信规约实战指南:IEC104/Modbus/MQTT的工程化选择逻辑 站在光伏电站的控制室里,工程师小王盯着屏幕上闪烁的告警信息皱起了眉头——新部署的通信系统频繁出现数据丢失,而隔壁风电场采用不同协议的设备却运行平稳。这个场景揭示了电力通信…

作者头像 李华
网站建设 2026/4/30 21:06:30

构建可进化AI工作区:三层架构、故障自愈与自我迭代实践

1. 项目概述:一个可进化的AI工作区如果你和我一样,长期在AI Agent领域折腾,从早期的AutoGPT到后来的LangChain、CrewAI,再到现在的OpenClaw,你肯定明白一个道理:一个真正好用的AI工作区,绝不仅仅…

作者头像 李华