I'm attempting to create a simple Python script using Mido to execute the writing of a midi file that has tempo changes embedded. Right now I think I'm getting stuck on how to operate "delta time" when iterating over the different tempo changes. I'll post the code I've currently got. The issue I keep running into is that the first tempo change occurs at the correct time (referring to the first tempo CHANGE of 98 bpm, not the INITIAL tempo of 98b bpm, but all of the tempo changes after that) but all of the changes after that are too late and spread out. Any help is greatly appreciated:
import mido
from mido import MidiFile, MidiTrack, MetaMessage
def create_tempo_map_midi(positions, tempos, sample_rate=44100, output_midi_file='tempo_map.mid'):
midi = MidiFile()
track = MidiTrack()
midi.tracks.append(track)
ticks_per_beat = 480
current_ticks = 0 # Track the cumulative ticks
for position, tempo in zip(positions, tempos):
bpm_to_microseconds = 60_000_000 / tempo # Convert BPM to microseconds per beat
time_seconds = position / sample_rate
# Calculate the ticks directly without using mido.second2tick
time_ticks = int(time_seconds * ticks_per_beat * tempo / 60)
# Ensure that the calculated time_ticks is non-negative
delta_ticks = max(0, time_ticks - current_ticks)
# Add the tempo change event to the track
track.append(MetaMessage('set_tempo', tempo=int(bpm_to_microseconds), time=delta_ticks))
# Update current_ticks
current_ticks = time_ticks
# Save the MIDI file
midi.save(output_midi_file)
if __name__ == "__main__":
tempo_values = [98.0, 98.0, 101.5467, 103.3155, 105.0865, 106.8571, 108.6168, 110.3756, 112.1227, 113.8698, 115.6076, 117.3423, 119.0782, 120.8079, 122.5382, 124.2676, 126.0, 156.0, 156.0, 152.5883, 149.1766, 145.7649, 142.3532, 138.9415, 135.5298, 132.1181, 128.7064, 125.2947, 121.883, 118.4713, 115.0596, 111.6479, 108.2362, 104.8245, 98.0]
pos_values = [0, 1404000, 1417500, 1430528, 1443333, 1455922, 1468303, 1480483, 1492469, 1504268, 1515886, 1527329, 1538603, 1549713, 1560664, 1571460, 1592242, 1592745, 8309514, 8317994, 8326664, 8335532, 8344608, 8353901, 8363422, 8373183, 8383196, 8393475, 8404034, 8414888, 8426055, 8437553, 8449402, 8461625, 8474246]
create_tempo_map_midi(pos_values, tempo_values)
I've tried multiple variation of how to calculate and apply "delta_ticks". I'd expect the tempos to line up properly, but they don't.
I'm not sure if this will answer your question. I just want to point out that there's a wonderful little book called "The Technology of Computer Music", written by Max Matthews (and other famous people you may recognize) in 1969, reprinted in 1974. (It's available as a PDF scan on several sites.)
On pages 87-90 he shows an example of an accelerando. It's all arcane Fortran-style field inputs needed for punched cards, but the math and equations are easily used today.
Here is his example re-done in Python:
And the output (edited for nicer columns):
I'm not sure how to use this with your problem, though. Perhaps it will help with the timing and spacing of the BPM markers? I hope at least it shows you something useful.
That was a good question -- I had fun working up the example.
Good luck!