music21:读取MIDI文件的BPM和乐器信息并将其写回文件

ytr*_*ewq 8 python midi music21

我正在尝试实现一个读取 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)
Run Code Online (Sandbox Code Playgroud)

然后我进行一些转换并将其写回文件,如果音高为 -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()
Run Code Online (Sandbox Code Playgroud)

然而,读取部分不包含 MIDI 文件的整体速度/BPM 或特定乐器源(“bestName”似乎只是猜测),因此,写入部分不强制执行任何 BPM 或乐器源信息。

有没有办法为新的 midi 文件读取/解析和写入/执行相同的速度和乐器?

我查看了文档中的 MidiFile 和 MidiTrack 部分(http://web.mit.edu/music21/doc/moduleReference/moduleMidi.html#midifile),但只能找到有关通道或 ticksPerQuarterNote 的信息,这与我所了解的不完全一样。正在寻找。

**************编辑**********

我找到了一种获取曲目 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)
Run Code Online (Sandbox Code Playgroud)

除了最初的问题之外,我还需要找出每个轨道的通道号,这样我就可以区分打击乐和非打击乐轨道。

Mic*_*ert 0

music21 中用于读取文件并将其写回的最简单的代码是:

s = converter.parse('filein.mid')
s.write('midi', fp='fileout.mid')
Run Code Online (Sandbox Code Playgroud)

在这两行之间,您可以对中间步骤执行任何您想要的操作:

for n in s[note.Note]:  # or in < v7: s.recurse().getElementsByClass('Note')
    n.midi += 2
s = s.augmentOrDiminish(2)  # twice as slow
Run Code Online (Sandbox Code Playgroud)

这将执行您在问题中所说的所有内容,但您可能会有一个后续问题,“在将 MIDI 加载到 music21 和将其写出之间,某些 MIDI 事件正在消失。” 你是对的。Music21 并不是一个功能齐全的 MIDI 编辑器,可以将所有内容准确地放回原位。有一些这样的程序,但它们不具备 music21 的音乐理论/记谱知识。您需要决定您愿意接受哪些权衡,或者对 music21 中的 MIDI 转换模块进行子类化。