this article is to record How I create a midi file based on the below input

  • ballPlay = [[3], [3], [4], [3], [6]]
  • frameIndex = [35, 101, 127, 136, 177]
  • step = 70
  • frameAspect = 50
  • longest = 2 ##let it be half second

Firstly I create a animation, then I will create midi file from the ballPlay & frameIndex, which mean in the frame 35,101,127,136, 177 I am expected to listen the sound 3, 3, 4, 3,6. frameAspect is to define the frame ratio in the video, should be matched with animation video. step is to tune to sound pitch.

the blow code might have some condition not well considered. bug might exists. take care.

from mido import Message, MidiFile, MidiTrack, MetaMessage
from mido import second2tick, bpm2tempo


# Create a new MIDI file and a track
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)


ballPlay = [[3], [3], [4], [3], [6]]
frameIndex = [35, 101, 127, 136, 177]
step = 70
frameAspect = 50
longest = 2 ##let it be half second

ticks_per_beat = mid.ticks_per_beat # default: 480
bpm = 240 # default midi tempo

tempo = bpm2tempo(bpm)


def beat(second,tempo=tempo,ticks_per_beat=ticks_per_beat):
    """Convert absolute time in seconds to ticks.

        Returns absolute time in ticks for a chosen MIDI file time resolution
        (ticks/pulses per quarter note, also called PPQN) and tempo (microseconds
        per quarter note). Normal rounding applies.
        """
    scale = tempo * 1e-6 / ticks_per_beat
    print("{}, {}".format(second ,int(round(second / scale))))
    return int(round(second / scale))

durationList = []

messages = []
track.append(MetaMessage('set_tempo', tempo=tempo))
track.append(MetaMessage('time_signature', numerator=4, denominator=4))



for index in range(len(frameIndex)-1):
    if index == 0:

        begin_time = beat((frameIndex[index]) / frameAspect)
        duration = frameIndex[index + 1] - frameIndex[index]
        if duration >=longest:
            duration = longest
        if len(ballPlay[index]) == 1:

            track.append(Message('note_on', note=ballPlay[index][0] + step, velocity=64,
                                 time=begin_time))
            track.append(Message('note_off', note=ballPlay[index][0] + step, velocity=64,
                                 time=beat( duration / frameAspect)))
        else:
            for tt in range(len(ballPlay[index])):
                if tt == 0:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                         time=begin_time))
                else:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                         time=0))
            for tt in range(len(ballPlay[index])):
                if tt == 0:
                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=beat(duration / frameAspect)))
                else:

                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=0))
        durationList.append(duration)

    if index > 0 and frameIndex[index + 1] - frameIndex[index] < longest:
        durationList.append(frameIndex[index + 1] - frameIndex[index])
        begin_time = 0
        if frameIndex[index] - frameIndex[index - 1] >= longest:
            begin_time = beat((frameIndex[index] - frameIndex[index - 1] - longest) / frameAspect)

        if len(ballPlay[index]) == 1:
            track.append(Message('note_on', note=ballPlay[index][0] + step, velocity=64,
                                 time=begin_time))
            track.append(Message('note_off', note=ballPlay[index][0] + step, velocity=64,
                                 time=beat((frameIndex[index + 1] - frameIndex[index]) / frameAspect)))
        else:
            for tt in range(len(ballPlay[index])):
                if tt==0:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                         time=begin_time))
                else:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                     time=0))
            for tt in range(len(ballPlay[index])):
                if tt == 0:
                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=beat(
                                             (frameIndex[index + 1] - frameIndex[index]) / frameAspect)))
                else:

                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=0))
        # messages.append("turn of time = {}, note = {}".format())

    if index > 0 and frameIndex[index + 1] - frameIndex[index] >= longest:
        durationList.append(longest)
        begin_time = 0
        if frameIndex[index] - frameIndex[index - 1] >= longest:
            begin_time = beat((frameIndex[index] - frameIndex[index - 1] - longest) / frameAspect)
        else:
            begin_time = 0

        if len(ballPlay[index]) == 1:

            track.append(Message('note_on', note=ballPlay[index][0] + step, velocity=64,
                                 time=begin_time))
            track.append(Message('note_off', note=ballPlay[index][0] + step, velocity=64,
                                 time=beat((longest) / frameAspect)))

        else:
            for tt in range(len(ballPlay[index])):
                if tt==0:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                         time=begin_time))
                else:
                    track.append(Message('note_on', note=ballPlay[index][tt] + step, velocity=64,
                                     time=0))
            for tt in range(len(ballPlay[index])):
                if tt == 0:
                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=beat((longest) / frameAspect)))
                else:
                    track.append(Message('note_off', note=ballPlay[index][tt] + step, velocity=64,
                                         time=0))
        # messages.append("turn of time = {}, note = {}".format())

mid.save('midiFile.mid')
messages.append("it is over")

By Admin

Think-Math Website