Проблемы с созданием midi - файла (Python)

Рейтинг: 3Ответов: 3Опубликовано: 13.08.2025

Нижеуказанный код создаёт файл, в котором ноты первого аккорда звучат одновременно, последующих - поочерёдно.

import mido
from mido import MidiFile, MidiTrack
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

track.append(mido.Message('program_change', program=36, time=0, channel=0))
track.append(mido.Message('note_on', note=48, velocity=64, time=0))
track.append(mido.Message('note_on', note=52, velocity=64, time=0))
track.append(mido.Message('note_on', note=55, velocity=64, time=0))
track.append(mido.Message('note_off', note=48, velocity=64, time=64))
track.append(mido.Message('note_off', note=52, velocity=64, time=64))
track.append(mido.Message('note_off', note=55, velocity=64, time=64))
track.append(mido.Message('note_on', note=48, velocity=64, time=192))
track.append(mido.Message('note_on', note=52, velocity=64, time=192))
track.append(mido.Message('note_on', note=55, velocity=64, time=192))
track.append(mido.Message('note_off', note=48, velocity=64, time=256))
track.append(mido.Message('note_off', note=52, velocity=64, time=256))
track.append(mido.Message('note_off', note=55, velocity=64, time=256))
track.append(mido.Message('note_on', note=48, velocity=64, time=384))
track.append(mido.Message('note_on', note=52, velocity=64, time=384))
track.append(mido.Message('note_on', note=55, velocity=64, time=384))
track.append(mido.Message('note_off', note=48, velocity=64, time=448))
track.append(mido.Message('note_off', note=52, velocity=64, time=448))
track.append(mido.Message('note_off', note=55, velocity=64, time=448))
mid.save('output.mid')

Как решить эту проблему?

Ответы

▲ 5Принят

Впервые вижу эту библиотеку, но подметил, что track.append очень похож по своей логике на инструменты по написанию макросов. А в макросах, есть такая особенность, что параметр time - это время с момента предыдущего события, а не абсолютное время от начало программы.

import mido
from mido import MidiFile, MidiTrack

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

track.append(mido.Message("program_change", program=36, time=0, channel=0))

track.append(mido.Message("note_on", note=48, velocity=64, time=0))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))
track.append(mido.Message("note_off", note=48, velocity=64, time=192))
track.append(mido.Message("note_off", note=52, velocity=64, time=0))
track.append(mido.Message("note_off", note=55, velocity=64, time=0))
track.append(mido.Message("note_on", note=48, velocity=64, time=192))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))
track.append(mido.Message("note_off", note=48, velocity=64, time=192))
track.append(mido.Message("note_off", note=52, velocity=64, time=0))
track.append(mido.Message("note_off", note=55, velocity=64, time=0))
track.append(mido.Message("note_on", note=48, velocity=64, time=192))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))
track.append(mido.Message("note_off", note=48, velocity=64, time=192))
track.append(mido.Message("note_off", note=52, velocity=64, time=0))
track.append(mido.Message("note_off", note=55, velocity=64, time=0))
mid.save("output.mid")

Собственно что выходит? Зажимаем аккорд:

track.append(mido.Message("note_on", note=48, velocity=64, time=0))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))

Так как у всех нот параметр time=0, они звучат одновременно.

Далее мы удерживаем аккорд:

track.append(mido.Message("note_off", note=48, velocity=64, time=192))
track.append(mido.Message("note_off", note=52, velocity=64, time=0))
track.append(mido.Message("note_off", note=55, velocity=64, time=0))

Удерживаем мы его time=192 от предыдущего события. Первая нота отпускается, а за ней отпускаются и остальные, а так как от предыдущего действия у них задержка time=0 - всё выходит одновременно.

Собственно дальше подбирайте нужные тайминги, аккорды и просто держите в голове, что параметр time - это время с момента предыдущего события.

▲ 3

В дополнение к ответу Amgarak я бы ещё предложил сократить код за счёт выноса повторяющихся действий в отдельную функцию. Код не проверял, но как-то так должно получиться:

import mido
from mido import MidiFile, MidiTrack

def add_accord(track, accord, velocity, time1, time2):
    for i, note in enumerate(accord):
        track.append(mido.Message("note_on", note=note, velocity=velocity, time=(0 if i else time1)))
    for i, note in enumerate(accord):
        track.append(mido.Message("note_of", note=note, velocity=velocity, time=(0 if i else time2)))

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

accord = (48, 52, 55)
velocity = 64
time_shift = 192

add_accord(track, accord, velocity, 0, time_shift)
add_accord(track, accord, velocity, time_shift, time_shift)
add_accord(track, accord, velocity, time_shift, time_shift)

mid.save("output.mid")
▲ 2

Логика рабочего кода (создан на основе ответа Amgarak):

import mido
from mido import MidiFile, MidiTrack
mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

track.append(mido.Message("note_on", note=48, velocity=64, time=0))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))
track.append(mido.Message("note_off", note=48, velocity=64, time=64))
track.append(mido.Message("note_off", note=52, velocity=64, time=64))
track.append(mido.Message("note_off", note=55, velocity=64, time=64))
track.append(mido.Message("note_on", note=48, velocity=64, time=0))
track.append(mido.Message("note_on", note=52, velocity=64, time=0))
track.append(mido.Message("note_on", note=55, velocity=64, time=0))
track.append(mido.Message("note_off", note=48, velocity=64, time=64))
track.append(mido.Message("note_off", note=52, velocity=64, time=64))
track.append(mido.Message("note_off", note=55, velocity=64, time=64))

Данный код создаст midi - файл с повторяющимся аккордом.