0

I am currently writing a parser for midi files so I can generate my own music using a Markov chain.

I'm a bit confused as to why there are so many set_tempo midi meta messages (in the track 0 meta message section) of each file. I would understand if they were set to different delta time, but some are not. Also some seem to set the same tempo for the same delta time which seems weirdly superfluous.

Here's an example...

<meta message set_tempo tempo=857139 time=0>
<meta message set_tempo tempo=857139 time=0>
<meta message set_tempo tempo=857632 time=0>
<meta message set_tempo tempo=857632 time=180224>
<meta message set_tempo tempo=895896 time=438>
<meta message set_tempo tempo=930917 time=438>
<meta message set_tempo tempo=967865 time=438>
<meta message set_tempo tempo=1008868 time=438>
<meta message set_tempo tempo=1053514 time=438>
<meta message set_tempo tempo=1101084 time=438>
<meta message set_tempo tempo=2403785 time=438>
<meta message set_tempo tempo=857632 time=1030>
<meta message set_tempo tempo=895896 time=292>
<meta message set_tempo tempo=930917 time=292>
<meta message set_tempo tempo=967865 time=292>
<meta message set_tempo tempo=1008868 time=292>
<meta message set_tempo tempo=1053514 time=292>
<meta message set_tempo tempo=1101084 time=292>
<meta message set_tempo tempo=2403785 time=292>
<meta message set_tempo tempo=2403785 time=1028>
<meta message end_of_track time=5119>

so,

(1) why are there duplicates?
(2) are the changes in tempo at different delta times important ? (ie. is this because the music speeds up/slows down in various parts
(3) Is it worth implementing a hidden markov chain that deals with tempo changes for my generator

Any help at all would be appreciated. N.B. I know very little about music theory

Cheers

Here is my solution, have I done something wrong (reply to comment in answer below).


import mido

all_mid = [' (Yiruma).mid']

# add time from start to message data (for sorting and adjusted delta time purposes)
def add_cumulative_time(msg, current_time):
    add_on = msg.time
    current_time += add_on
    return current_time, add_on

def clean(mid, all_messages):  # for each track (then message) do the following
    msgwithtempos = []
    for i, track in enumerate(mid.tracks):
        current_time = 0
        for msg in track:
            current_time = add_cumulative_time(msg, current_time)[0]
            allowed_types = ["note_on", "note_off", "program_change", "set_tempo"] 
            if msg.type in allowed_types:
                all_messages.append([msg, current_time])
            else:
                pass
    return all_messages, msgwithtempos


def main():  # for each midi file do the following
    all_lists = []
    for i in range(0, len(all_mid)):
        all_messages = []
        mid = mido.MidiFile(all_mid[i])
        ticksperbeat = mid.ticks_per_beat
        all_messages, msgwithtempos = clean(mid, all_messages)
        final_messages = all_messages + msgwithtempos
        final_messages = sorted(final_messages, key=lambda x: x[1])
        all_lists += final_messages
    for i, item in enumerate(all_lists):
        if all_lists[i][0].type == "set_tempo":
            while all_lists[i+1][0].type == "set_tempo": # talk about this as an error with i-1 being logical but not working
                all_lists.pop(i+1)
    return all_lists, ticksperbeat


if __name__ == '__main__':
    main()

staplegun
  • 79
  • 10

3 Answers3

2
  1. I suppose these duplicates are just artifacts of recording from real instrument or something like this. It's not important.
  2. Yes, changes of tempo cause music is playing slower or faster.
  3. You absolutely must respect all tempo changes. That follows from previous sentence.
Maxim
  • 1,995
  • 1
  • 19
  • 24
  • Do you know why they set different tempos for the same delta time? How should I deal with this. Thanks – staplegun Dec 28 '19 at 14:02
  • I don't know why this happened, but I saw this many times. I just take last tempo value in this case. – Maxim Dec 28 '19 at 19:58
  • My project has since then progressed and though I was taking the last tempo value (and basing the algorithm on that), it still sounded weird. Changed it to take the first and now it works well. Seems very illogical bus alas – staplegun Mar 12 '20 at 11:40
  • @staplegun, Can you provide the file you've tested your program on? I'll check it on my side. But you should take last tempo anyway. I suppose your program is built in wrong way somewhere in your code. MIDI data is always processed according to events position, every new event applied immediately. – Maxim Mar 12 '20 at 15:42
  • Hmm - I think it's best I just post all my MIDI formatting code in my question above. I add cumulative time (as opposed to delta time) to each message for each track, then I order all of these for all the tracks by cumulative time. I then remove the tempo messages by iterating through this whole list. I've tested it on quite a few MIDI files from different websites. – staplegun Mar 13 '20 at 12:17
  • https://www.midiworld.com/search/?q=yiruman this was where I got the Yiruma file from – staplegun Mar 13 '20 at 12:25
  • Actually, I think I must've had a bug before, because I just changed that last line to all_lists.pop(i) rather than all_lists.pop(i+1) and now it works even better, I think I wasn't picking up the difference because the tempos were relatively similar. Does this look good to you? Edit: I was using i-1 ! No wonder it didn't work! – staplegun Mar 13 '20 at 12:37
0

If you are working with format 0 files, the spec for MIDI 1.0 says the following:

For a format 0 file, the tempo will be scattered through the track and the tempo map reader should ignore the intervening events

I think this means the first tempo is the one to be used. I've dragged midi files with multiple tempo events to multiple programs, and they tend to pick out the first tempo, although Ableton seems to be kind of random about which one it chooses sometimes.

John Glen
  • 771
  • 7
  • 24
0

My midi file also has a long string of "set tempo" messages. They appear to be dummy messages, which are only used for their 'time' field values.

The time fields are being used to place "marker" and "key_signature" messages at specific times in first track. This first track has no notes in it. See below. The second and third tracks have notes in this midi file (these two tracks are not shown below).

    MIDI-MESSAGE KEY:VALUE pairs ...
 
    track_name name:Clair de Lune, time:0
    ...
    time_signature numerator:9, denominator:8, clocks_per_click:12, notated_32nd_notes_per_beat:8, time:0
    key_signature key:Db, time:0
    set_tempo tempo:600000, time:0
    marker text:Andante tres expressif, time:0
    set_tempo tempo:1875000, time:240
    set_tempo tempo:888889, time:240
    set_tempo tempo:821918, time:1680
    ...
    set_tempo tempo:789474, time:240
    set_tempo tempo:863309, time:240
    set_tempo tempo:909091, time:460
    ...
    set_tempo tempo:1846154, time:740
    set_tempo tempo:863309, time:240
    marker text:Tempo rubato, time:0
    set_tempo tempo:857756, time:40
    ...
    set_tempo tempo:571429, time:40
    set_tempo tempo:585366, time:600
    set_tempo tempo:535714, time:120
    set_tempo tempo:515818, time:480
    key_signature key:E, time:240
    set_tempo tempo:524017, time:0
    marker text:En animant, time:0
    set_tempo tempo:481618, time:480
    set_tempo tempo:498132, time:240
    ...
    end_of_track
Jon Rifkin
  • 21
  • 3