خودکار سازی نسل MIDI با Python: یک راهنمای جامع

MIDI (رابط دیجیتال ابزار موسیقی) یک استاندارد محبوب برای نمایش موسیقی به صورت دیجیتالی است. این امکان را به آهنگسازان و توسعه دهندگان می دهد تا موسیقی را با فرمت دیجیتال ایجاد ، اصلاح و دستکاری کنند. در این مقاله ، ما از طریق یک اسکریپت پایتون قدم می زنیم که می تواند با استفاده از دنباله ای از یادداشت ها ، طول ، سرعت و حتی ماکرو برای الگوهای موسیقی مکرر ، یک فایل MIDI ایجاد کند.
نمای کلی فیلمنامه
فیلمنامه ارائه شده از آن استفاده می کند mido
کتابخانه ، یک بسته قدرتمند پایتون که به شما امکان می دهد با فایلهای MIDI کار کنید. هدف از این اسکریپت تولید یک فایل MIDI بر اساس دنباله ای از یادداشت ها و خصوصیات آنها مانند طول و سرعت است. علاوه بر این ، عملکردی در گسترش ماکروها به توالی های موسیقی را فراهم می کند و استفاده مجدد از الگوهای را آسان تر می کند.
بیایید اجزای اصلی فیلمنامه را تجزیه کنیم و توضیح دهیم که چگونه همه چیز گام به گام کار می کند.
1 تنظیم محیط
ابتدا ماژول های لازم را وارد می کنیم. در mido
کتابخانه برای رسیدگی به ایجاد فایل MIDI ، ارسال پیام و تنظیم سرعت استفاده می شود. در os
ماژول اطمینان می دهد که پوشه خروجی قبل از ذخیره پرونده تولید شده وجود دارد.
import mido
from mido import MidiFile, MidiTrack, Message
import os
2 نقشه برداری از یادداشت ها به شماره های MIDI
در MIDI ، هر یادداشت با یک شماره منحصر به فرد مطابقت دارد. به عنوان مثال ، یادداشت “C4” به شماره 60 اختصاص داده شده است. اسکریپت از یک فرهنگ لغت استفاده می کند NOTE_TO_MIDI
برای نقشه برداری نام یادداشت ها (مانند “C” ، “D#” و غیره) به شماره های یادداشت MIDI.
NOTE_TO_MIDI = {
'C': 0, 'C#': 1, 'D': 2, 'D#': 3, 'E': 4, 'F': 5, 'F#': 6, 'G': 7, 'G#': 8, 'A': 9, 'A#': 10, 'B': 11
}
3 تبدیل یادداشت ها و اکتاو ها به شماره MIDI
در note_to_midi
عملکرد یک یادداشت (مانند “C4” یا “D#3”) می گیرد و شماره MIDI مربوطه را محاسبه می کند. این فرمول برای اکتاو نیز تنظیم می شود ، و اطمینان می دهد که C4 همیشه MIDI شماره 60 است.
def note_to_midi(note):
note_name = note[:-1] # Extract the note name (e.g., 'A', 'B', etc.)
octave = int(note[-1]) # Extract the octave (e.g., '0', '2', etc.)
return NOTE_TO_MIDI[note_name] + 12 * (octave + 1)
4 تبدیل طول به کنه های MIDI
زمان MIDI به کنه تقسیم می شود که فواصل زمانی کوچک را نشان می دهد. در length_to_ticks
عملکرد طول یادداشت های موسیقی (مانند کل ، نیمی ، چهارم و غیره) را به مدت زمان مربوط به آنها در کنه های MIDI ، بر اساس کنه در هر مقدار ضرب و شتم تبدیل می کند.
def length_to_ticks(length, ticks_per_beat):
if length == 1:
return ticks_per_beat * 4 # Whole note
elif length == 2:
return ticks_per_beat * 2 # Half note
elif length == 4:
return ticks_per_beat # Quarter note
elif length == 8:
return ticks_per_beat // 2 # Eighth note
elif length == 16:
return ticks_per_beat // 4 # Sixteenth note
return ticks_per_beat # Default to quarter note
5 تولید پرونده MIDI
در generate_piano_midi
عملکرد با تنظیم اولین آهنگ و تعریف ساز (یک پیانو به طور پیش فرض ، یک فایل MIDI جدید ایجاد می کند (به طور پیش فرض پیانو می شود). سپس دنباله ارائه شده از یادداشت ها ، طول ها و سرعت ها را پردازش می کند و پیام های MIDI مناسب را اضافه می کند (توجه داشته باشید و یادداشت کنید).
def generate_piano_midi(sequence, tempo, output_file="output/piano_piece_2.mid", instrument_program=0):
output_folder = "output"
if not os.path.exists(output_folder):
os.makedirs(output_folder)
midi = MidiFile()
track = MidiTrack()
midi.tracks.append(track)
track.append(Message('program_change', program=instrument_program))
microseconds_per_beat = 60 * 1000000 // tempo
track.append(mido.MetaMessage('set_tempo', tempo=microseconds_per_beat))
ticks_per_beat = 480
current_time = 0 # Start at time 0
for i in range(0, len(sequence), 3):
note = sequence[i] # Note name (e.g., 'A0')
length = sequence[i + 1] # Length (e.g., 4 for quarter note)
velocity = sequence[i + 2] # Velocity (e.g., 100 for volume)
if note == 'X': # Handle pauses (no sound)
track.append(Message('note_on', note=0, velocity=0, time=current_time))
track.append(Message('note_off', note=0, velocity=0, time=length_to_ticks(length, ticks_per_beat)))
current_time = 0
continue
midi_note = note_to_midi(note)
duration = length_to_ticks(length, ticks_per_beat)
track.append(Message('note_on', note=midi_note, velocity=velocity, time=current_time))
track.append(Message('note_off', note=midi_note, velocity=velocity, time=duration))
current_time = 0
midi.save(output_file)
print(f"MIDI file '{output_file}' has been saved!")
6 در حال گسترش ماکرو
یکی از ویژگی های قدرتمند این اسکریپت توانایی آن در گسترش ماکروها است. ماکروها توالی های از پیش تعریف شده ای از یادداشت ها هستند که می توانند با یک علامت کوتاه ارجاع شوند. این به شما امکان می دهد بدون تکرار همان کد ، از الگوهای در کل دنباله موسیقی خود استفاده کنید.
def expand_macros(sequence, macros):
expanded_sequence = []
i = 0
while i < len(sequence):
if isinstance(sequence[i], str) and sequence[i].startswith('M'): # Macro starts with 'M'
macro_name = sequence[i]
if macro_name in macros:
expanded_sequence.extend(macros[macro_name]) # Expand the macro
i += 1 # Skip the macro name
else:
expanded_sequence.append(sequence[i])
i += 1
return expanded_sequence
7 ایجاد یک دنباله موسیقی
می توانید با استفاده از ترکیبی از ماکرو و یادداشت های فردی ، یک دنباله موسیقی را تعریف کنید. به عنوان مثال:
macros = {
'M001': ['C3', 8, 90, 'E3', 8, 85, 'G3', 8, 80, 'C4', 8, 75],
'M002': ['C3', 8, 90, 'F3', 8, 85, 'A3', 8, 80, 'C4', 8, 75],
'M003': ['B2', 8, 90, 'D3', 8, 85, 'G3', 8, 80, 'B3', 8, 75]
}
sequence = ['M001', 'M002', 'M003', 'M003', 'M001']
expanded_sequence = expand_macros(sequence, macros)
8 مراحل نهایی
پس از گسترش دنباله ، می توانید یک سرعت (در ضربان در دقیقه) تنظیم کنید ، یک ساز (به عنوان مثال ، پیانو یا فلوت) را انتخاب کنید و با فراخوانی فایل MIDI تولید کنید generate_piano_midi
عملکرد:
tempo_bpm = 120 # Tempo in beats per minute
instrument_program = 1 # Flute
generate_piano_midi(expanded_sequence, tempo_bpm, instrument_program=instrument_program)
پایان
این اسکریپت پایه ای محکم برای تولید پرونده های MIDI در پایتون فراهم می کند. با استفاده از mido
کتابخانه و ویژگی های اجرای ویژگی هایی مانند تبدیل یادداشت به میانبرها ، گسترش کلان و محاسبه مدت زمان ، می توانید به راحتی ایجاد آهنگ های موسیقی را به راحتی خودکار کنید. این که آیا شما در حال کار بر روی یک پروژه موسیقی ، ایجاد موسیقی متن فیلم هستید یا فقط با MIDI آزمایش می کنید ، این اسکریپت می تواند باعث صرفه جویی در وقت شما شود و امکانات خلاقانه را باز کند.
اگر هنوز این کار را نکرده اید ، آن را امتحان کنید و آهنگ های MIDI خود را با پایتون ایجاد کنید!
رمز کامل
import mido
from mido import MidiFile, MidiTrack, Message
import os
# Mapping of note names to MIDI numbers
NOTE_TO_MIDI = {
'C': 0, 'C#': 1, 'D': 2, 'D#': 3, 'E': 4, 'F': 5, 'F#': 6, 'G': 7, 'G#': 8, 'A': 9, 'A#': 10, 'B': 11
}
# Function to convert note and octave to MIDI number
def note_to_midi(note):
note_name = note[:-1] # Extract the note name (e.g., 'A', 'B', etc.)
octave = int(note[-1]) # Extract the octave (e.g., '0', '2', etc.)
return NOTE_TO_MIDI[note_name] + 12 * (octave + 1)
# Function to convert length notation to MIDI ticks
def length_to_ticks(length, ticks_per_beat):
if length == 1:
return ticks_per_beat * 4 # Whole note
elif length == 2:
return ticks_per_beat * 2 # Half note
elif length == 4:
return ticks_per_beat # Quarter note
elif length == 8:
return ticks_per_beat // 2 # Eighth note
elif length == 16:
return ticks_per_beat // 4 # Sixteenth note
return ticks_per_beat # Default to quarter note
# Function to generate MIDI file based on the given sequence and tempo
def generate_piano_midi(sequence, tempo, output_file="output/piano_piece_2.mid", instrument_program=0):
output_folder = "output"
if not os.path.exists(output_folder):
os.makedirs(output_folder)
midi = MidiFile()
track = MidiTrack()
midi.tracks.append(track)
track.append(Message('program_change', program=instrument_program))
microseconds_per_beat = 60 * 1000000 // tempo
track.append(mido.MetaMessage('set_tempo', tempo=microseconds_per_beat))
ticks_per_beat = 480
current_time = 0
for i in range(0, len(sequence), 3):
note = sequence[i] # Note name (e.g., 'A0')
length = sequence[i + 1] # Length (e.g., 4 for quarter note)
velocity = sequence[i + 2] # Velocity (e.g., 100 for volume)
if note == 'X': # Handle pauses (no sound)
track.append(Message('note_on', note=0, velocity=0, time=current_time))
track.append(Message('note_off', note=0, velocity=0, time=length_to_ticks(length, ticks_per_beat)))
current_time = 0
continue
midi_note = note_to_midi(note)
duration = length_to_ticks(length, ticks_per_beat)
track.append(Message('note_on', note=midi_note, velocity=velocity, time=current_time))
track.append(Message('note_off', note=midi_note, velocity=velocity, time=duration))
current_time = 0
midi.save(output_file)
print(f"MIDI file '{output_file}' has been saved!")
# Function to expand macros
def expand_macros(sequence, macros):
expanded_sequence = []
i = 0
while i < len(sequence):
if isinstance(sequence[i], str) and sequence[i].startswith('M'): # Macro starts with 'M'
macro_name = sequence[i]
if macro_name in macros:
expanded_sequence.extend(macros[macro_name]) # Expand the macro
i += 1 # Skip the macro name
else:
expanded_sequence.append(sequence[i])
i += 1
return expanded_sequence
# Example macros definition
macros = {
'M001': ['C3', 8, 90, 'E3', 8, 85, 'G3', 8, 80, 'C4', 8, 75],
'M002': ['C3', 8, 90, 'F3', 8, 85, 'A3', 8, 80, 'C4', 8, 75],
'M003': ['B2', 8, 90, 'D3', 8, 85, 'G3', 8, 80, 'B3', 8, 75],
'M004': ['C3', 8, 90, 'E3', 8, 85, 'G3', 8, 80, 'C4', 8, 75],
'M005': ['C3', 8, 90, 'E3', 8, 85, 'A3', 8, 80, 'C4', 8, 75],
'M006': ['D3', 8, 90, 'F#3', 8, 85, 'A3', 8, 80, 'C4', 8, 75],
'M007': ['G2', 8, 90, 'B2', 8, 85, 'D3', 8, 80, 'G3', 8, 75],
'M008': [ 'C3', 1, 90 ]
}
# Example sequence
sequence = [
'M001',
'M001',
'M002',
'M002',
'M003',
'M003',
'M004',
'M004',
'M005',
'M005',
'M006',
'M006',
'M007',
'M007',
'M008'
]
# Expand macros in the sequence
expanded_sequence = expand_macros(sequence, macros)
# Tempo in beats per minute (e.g., 120 BPM)
tempo_bpm = 120
# Choose an instrument (e.g., piano, flute, violin, etc.)
instrument_program = 1 # Flute
# Generate the MIDI file with the expanded sequence and tempo
generate_piano_midi(expanded_sequence, tempo_bpm, instrument_program=instrument_program)