music21:读取 MIDI 文件的 BPM 和乐器信息并将其写回文件
Posted
技术标签:
【中文标题】music21:读取 MIDI 文件的 BPM 和乐器信息并将其写回文件【英文标题】:music21: read MIDI file's BPM and instrument info and write it back to file 【发布时间】:2017-01-03 20:49:57 【问题描述】:我正在尝试实现一个读取 MIDI 文件并将其写回的代码。
我有以下代码来解析持续时间、音高和位置。
import music21
from music21 import *
piece=converter.parse('input.mid')
all_parts=[]
for part in piece.parts:
part_tuples=[]
try:
track_name = part[0].bestName()
except AttributeError:
track_name = 'None'
part_tuples.append(track_name)
for event in part:
for y in event.contextSites():
if y[0] is part:
offset=y[1]
if getattr(event,'isNote',None) and event.isNote:
part_tuples.append([event.quarterLength,event.pitch.midi,offset])
if getattr(event,'isRest',None) and event.isRest:
part_tuples.append([event.quarterLength,'Rest',offset])
all_parts.append(part_tuples)
然后我进行一些转换并将其写回文件,如果音高为 -1 ('isRest'),则扩展 t(所有音符的速度设置为 90):
mt = midi.MidiTrack(1)
t=0
tLast=0
for d,p,v in converted_notes:
if p!=-1:
dt = midi.DeltaTime(mt)
dt.time = t-tLast
#add to track events
mt.events.append(dt)
me=midi.MidiEvent(mt)
me.type="NOTE_ON"
me.channel=1
me.time= None #d
me.pitch = p
me.velocity = v
mt.events.append(me)
# add note off / velocity zero message
dt = midi.DeltaTime(mt)
dt.time = d
# add to track events
mt.events.append(dt)
me=midi.MidiEvent(mt)
me.type="NOTE_ON"
me.channel=1
me.time= None #d
me.pitch = p
me.velocity = 0
mt.events.append(me)
tLast = t+d
# t +=2*d
t+=d
else:
t+=d
dt=midi.DeltaTime(mt)
dt.time = 0
mt.events.append(dt)
me = midi.MidiEvent(mt)
me.type = "END_OF_TRACK"
me.channel = 1
me.data ='' # must set data to empty string
mt.events.append(me)
mf = midi.MidiFile()
mf.ticksPerQuarterNote = 1024 # cannot use: 10080
mf.tracks.append(mt)
#mf.tracks.append(mt2)
mf.open('writeback.mid', 'wb')
mf.write()
mf.close()
但是,阅读部分不包含 MIDI 文件的整体速度/BPM 或特定乐器源(“bestName”似乎只是猜测),因此,写作部分不强制执行任何 BPM 或乐器来源信息。
有没有办法为新的 midi 文件读取/解析和写入/执行相同的速度和乐器?
我查看了文档 (http://web.mit.edu/music21/doc/moduleReference/moduleMidi.html#midifile) 中的 MidiFile 和 MidiTrack 部分,但只能找到有关通道或 ticksPerQuarterNote 的信息,这不是我要查找的。p>
**************编辑**********
我找到了一种获取曲目 BPM 的方法,尽管这是一种非常笨拙的方法。
for i in range(0,20):
bpm =str(part[i])
if 'MetronomeMark' in bpm:
eq_ind=bpm.index('=')
bpm=bpm[eq_ind+1:]
bpm=bpm.replace('>','')
break
bpm=float(bpm)
在最初的问题之上,我还需要弄清楚每个音轨的通道号,这样我就可以区分打击乐和非打击乐。
【问题讨论】:
在 6.7.1 中还有问题吗?我在 6.5 中为此添加了一个功能。还有一个用于保存 MIDI 频道信息的封闭 PR,您可以挑选或提倡:github.com/cuthbertLab/music21/pull/712 【参考方案1】:music21 中最简单的读取文件和写回文件的代码是:
s = converter.parse('filein.mid')
s.write('midi', fp='fileout.mid')
在这两行之间你可以做任何你想做的中间步骤:
for n in s[note.Note]: # or in < v7: s.recurse().getElementsByClass('Note')
n.midi += 2
s = s.augmentOrDiminish(2) # twice as slow
这将完成您在问题中所说的一切,但随后您可能会有一个后续问题,“在将 MIDI 加载到 music21 和写出之间,一些 MIDI 事件正在消失。”你是对的。 Music21 并不是一个功能齐全的 MIDI 编辑器,它可以将所有内容完全放回原处。有一些这样的程序,但他们没有音乐的音乐理论/符号知识21。您需要决定您愿意接受哪些权衡,或者将 music21 中的 MIDI 转换模块子类化。
【讨论】:
以上是关于music21:读取 MIDI 文件的 BPM 和乐器信息并将其写回文件的主要内容,如果未能解决你的问题,请参考以下文章
使用 music21 显示 Mid 文件中的 midi 音高数字
如何使用 Python 中的长笛乐器从音符制作 MIDI 文件(music21 库)