music21: read MIDI file's BPM and instrument info and write it back to file

2.8k views Asked by At

I'm trying to implement a code that reads MIDI file and writes it back.

I have the following code to parse the duration,pitch,and position.

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)

Then I do some conversion and write it back to a file, extending t if pitch is -1 ('isRest') (Velocity is set to 90 for all notes):

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()

However, the reading part does not contain the overall tempo/BPM or the specific instrument source of the MIDI file ('bestName' seems to be just a guess), and as such, writing part does not enforce any BPM or instrument source info.

Is there a way to read/parse and write/enforce the same tempo and instrument for the new midi file?

I looked at MidiFile and MidiTrack parts from the documentation (http://web.mit.edu/music21/doc/moduleReference/moduleMidi.html#midifile) but could only find information on channels or ticksPerQuarterNote, which are not quite the ones I'm looking for.

**************EDIT**********

I found a way to get the BPM of the track, though it is a very very clumsy way to do it.

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)

On top of the original question, I also need to figure out the channel number for each track, so I can distinguish between percussion and non-percussion tracks..

1

There are 1 answers

0
Michael Scott Asato Cuthbert On

The easiest code in music21 for reading a file and writing it back is:

s = converter.parse('filein.mid')
s.write('midi', fp='fileout.mid')

In between the two lines you can do whatever you want to the intermediate step:

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

this will do everything you say in your question, but then you'll probably have a followup question, "Some MIDI events are disappearing between loading MIDI into music21 and writing it out." And you'll be right. Music21's not meant to be a full-featured MIDI editor that puts everything exactly back in its place. There are some such programs, but they don't have the music-theory/notation knowledge of music21. You'll need to decide what tradeoffs you're willing to live with, or subclass the MIDI conversion modules in music21.