Как я могу разделить файл midi программно?

Я использую библиотеку mido на python для работы с файлом midi.

Учитывая любой миди-файл, я хотел бы вывести миди-файл, состоящий только из первых 10 секунд ввода. Я не вижу никаких функций для этого в библиотеке, поэтому я пытаюсь сделать это, просматривая каждый из треков.

Я нахожу несколько вещей запутанными. Например, ниже показано, что вы получите, изучив один трек в своем миди-объекте.

<meta message track_name name='Slow Strings' time=0>
program_change channel=0 program=49 time=0
program_change channel=0 program=49 time=0
control_change channel=0 control=7 value=71 time=0
control_change channel=0 control=10 value=75 time=0
note_on channel=0 note=55 velocity=89 time=15360
note_on channel=0 note=59 velocity=95 time=0
note_on channel=0 note=62 velocity=92 time=0
note_off channel=0 note=55 velocity=64 time=1324
note_off channel=0 note=62 velocity=64 time=12

Итак, я просматриваю этот список сообщений в дорожке и подсчитываю атрибут времени до тех пор, пока текущая сумма не превысит 10000 (миллисекунд)? Я так не думаю, потому что, глядя на кусок на musescore, ничего существенного не происходит через 15,36 секунды после начала песни.

Я прочитал здесь , что время указано не в миллисекундах, а в дельта-времени, которое необходимо преобразовать в миллисекунды через PPQ (импульсы на четверть). Правильно ли это, и если да, то является ли использование текущей суммы времени правильным подходом к созданию выходного миди продолжительностью 10 секунд?

Это должно быть внутри python...? Вы можете просто зайти на midi.mathewvp.com/midiTrim.htm , загрузить весь миди-файл, ввести от 0 до 10 секунд и снова загрузить урезанную версию. Это может быть самый быстрый способ, но вы также можете сделать это в любой DAW, если хотите.

Ответы (2)

Время указано не в миллисекундах, а в миди-тиках, которые вы можете преобразовать в секунды с помощью функции tick2second(ticks, ticks_per_beat, tempo). Ваш код должен выглядеть примерно так:

from mido import MidiFile
from mido import tick2second

mid = MidiFile('input.mid')

for i, track in enumerate(mid.tracks):
    total_time = 0
    for msg in track:
        if msg.type == 'set_tempo':
            tempo = msg.tempo
        total_time  += tick2second(msg.time, mid.ticks_per_beat, tempo)
        print(msg, total_time) # or copy to output file
        if total_time > 10:
            break

Примечание: это предполагает отсутствие изменений темпа в течение первых 10 секунд ваших входных файлов. Если есть, ваш код должен быть более сложным.

Если вы согласны с C# вместо Python, вы можете использовать библиотеку DryWetMIDI . Код для первых 10 секунд MIDI-файла очень прост:

var midiFile = MidiFile.Read("My MIDI file.mid");
var newMidiFile = midiFile.TakePart(new MetricTimeSpan(0, 0, 10));
newMidiFile.Write("10sec version.mid");