برنامه نویسی

خودکار سازی نسل 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)
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا