Создание MIDI файла
생성일: 2025년 1월 10일
생성일: 2025년 1월 10일
Clarifying MIDI creation
It's worth noting that the code snippet provided opens a virtual MIDI port rather than creating a physical MIDI file on disk.
Evaluating MIDI libraries
To create MIDI files in Python, libraries like mido or pretty_midi are ideal. rtmidi, better for real-time MIDI interaction, isn't suited for file generation by itself.
Coding with Mido
To generate a MIDI file, I suggest using Mido by creating a MidiFile object, adding a MidiTrack, setting tempo, programming events, and saving the file with mid.save(filename)
.
Crafting the sequence
I’m detailing how to replicate MIDI logic: adding program changes, scheduling note events with defined velocities and times, setting ticks per beat, and saving the file with Mido.
Establishing tempo and notation
I’m building upon the MIDI file by setting the tempo, choosing piano as the instrument, defining ticks per beat, and mapping rhythm to quarter notes and eighth notes accordingly.
Clarifying note durations
I’m simplifying the interpretation of time values, assuming each measure consists of 4 quarter notes, with 0.25 representing a single quarter note duration, and focusing solely on note durations for clarity.
Translating note durations
I’m mapping beats to ticks, converting quarter notes and eighth notes to precise tick counts, and scheduling note on and off events in the MIDI track.
Mapping note lengths
Interestingly enough, I'm converting time values to note lengths: 0.25 measure equals a quarter note, and 0.125 measure equals an eighth note, aligning with the traditional note duration hierarchy.
Aligning note timings
I'm setting the measure length to 1920 ticks, converting times to note durations, and planning to iterate through notes in sequence for accurate MIDI file generation.
Prioritizing sequence
Taking note of the user's code, which sequentially plays notes without considering start times, the approach leverages sleep durations to sequence notes, effectively disregarding start time differences.
Syncing playback
I’m ensuring notes play sequentially by translating durations to beats and adjusting Mido's ticks per beat, aligning with the original code's sleep-based sequencing.
Ensuring clarity
Calculating note durations in ticks by setting measure length to 480 ticks for each quarter note, and scheduling note_on
and note_off
events accurately within the MIDI track.
Mapping the pattern
I’m noting that the code will loop for the specified repeat count, convert note durations to ticks, and create a MIDI file, ensuring each note event aligns with the accurately sized measure.
Thought about MIDI file generation for 56 seconds
Ниже приведён пример кода на Python с использованием библиотеки mido, который действительно создаёт MIDI-файл на диск (а не просто выводит MIDI-сообщения в виртуальный порт). Этот пример повторяет логику «ритмического рисунка» из вашего кода, но сохраняет результат в файл:
import mido from mido import Message, MidiFile, MidiTrack, MetaMessage, bpm2tempo def generate_midi(filename="rhythm.mid", tempo=260, num_repeats=2): """ Генерирует MIDI-файл с простым ритмическим рисунком. Args: filename: Имя выходного MIDI-файла. tempo: Темп (ударов в минуту). num_repeats: Сколько раз повторить ритм. """ # Создаем объект MidiFile и добавляем в него один трек mid = MidiFile() track = MidiTrack() mid.tracks.append(track) # Устанавливаем количество «тиков» (шагов) на четвертную ноту # По умолчанию mido ставит 480, можно оставить как есть или изменить mid.ticks_per_beat = 480 # Задаём темп (перевод bpm -> микросекунды на четверть) track.append(MetaMessage('set_tempo', tempo=bpm2tempo(tempo), time=0)) # Устанавливаем инструмент (программа 0 — акустическое пианино) track.append(Message('program_change', program=0, time=0)) # Пояснение к расчёту длительностей: # - По умолчанию mid.ticks_per_beat = 480 означает, что # 1 четвертная нота = 480 тиков. # - В вашем коде каждая «четверть» описана как duration=0.25 (т.к. 1 такт = 1.0), # значит 0.25 такта — это и есть 1 четверть. Но для удобства мы просто будем # интерпретировать duration, умножая её на (4 * 480), если хотим считать 1.0 за полный такт из 4 четвертей. # # Однако ваш код фактически игнорировал start и просто проигрывал ноты подряд. # Поэтому ниже мы делаем так: # - Если note != None, то включаем ноту (note_on) и через (duration) «долю» времени даём note_off. # - Если note == None, то это пауза (rest): никаких note_on/note_off, просто ждём (duration). # Пример ритмического рисунка: # (start, duration, note) # start duration note # 0.00 0.25 60 (четверть) # 0.25 0.25 62 (четверть) # 0.50 0.25 64 (четверть) # 0.75 0.25 65 (четверть) # 1.00 0.125 None (пауза на восьмую) # В исходном коде start никак не использовался для «запаздывания» начала, # поэтому здесь тоже будем играть их подряд. rhythm = [ (0, 0.25, 60), # четверть, нота "До" (0.25, 0.25, 62), # четверть, нота "Ре" (0.5, 0.25, 64), # четверть, нота "Ми" (0.75, 0.25, 65), # четверть, нота "Фа" (1, 0.125, None) # восьмая пауза ] # Определим, сколько «тиков» считать за один полный такт (4 четверти) ticks_per_measure = mid.ticks_per_beat * 4 # 4 четверти в такте for _ in range(num_repeats): for (start, duration, note) in rhythm: # Считаем, что duration — это доля от полного такта (1.0 = весь такт), # значит реальная длительность в тиках будет: duration_ticks = int(duration * ticks_per_measure) if note is not None: # Включаем ноту (с time=0, т.к. нота начинается сразу # после предыдущего события) track.append(Message('note_on', note=note, velocity=100, time=0)) # Выключаем ноту через duration_ticks track.append(Message('note_off', note=note, velocity=0, time=duration_ticks)) else: # Если note=None, то это пауза: просто "пропускаем" нужное кол-во тиков # (никаких событий note_on/note_off), # поэтому вставим пустое событие с тем же интервалом. # Обычно делают MetaMessage('skip'), но проще — # сдвинуть всё на duration_ticks до следующего события. # Реализуется через time в следующем событии. А раз тут нет note_on, # даём временной интервал «в пустоту». track.append(Message('note_off', note=0, velocity=0, time=duration_ticks)) # Сохраняем результат в файл mid.save(filename) # Запуск функции if __name__ == "__main__": generate_midi("my_rhythm.mid", tempo=260, num_repeats=10) print("MIDI-файл 'my_rhythm.mid' успешно создан.")
Установка темпа
С помощью MetaMessage('set_tempo', tempo=bpm2tempo(tempo))
указываем число микросекунд на четвертную ноту, переводя BPM (удары в минуту) в соответствующее значение.
ticks_per_beat
По умолчанию в Mido 1 четверть = 480 «тиков» (шагов). Если хотите, можете изменить это число, например, на 960 и т.д.
Добавление событий (Messages)
note_on
со start_time=0
означает, что нота включится сразу после предыдущего события.note_off
с нужным time
указывает через сколько «тиков» завершить предыдущую ноту (или сделать паузу, если ноты не было).Повтор ритма
В цикле for _ in range(num_repeats)
мы многократно проигрываем один и тот же фрагмент.
Таким образом, при запуске этого скрипта вы получите файл my_rhythm.mid
, который можно открыть любым MIDI-секвенсором или проигрывателем.