Я написал небольшую программу для демонстрации генерации Маркова, включая вывод в MIDI-файл с использованием библиотеки music21.
Обратите внимание, что у вашей матрицы перехода есть две проблемы:
- Вероятности в 5-м столбце не суммируйте до 1,00. Я заменил 0,66 на 0,65, чтобы все заработало.
- Это матрица вероятностей хордовых переходов, а не переходов нот. Так что мой пример включает аккорды (триады), а не отдельные ноты. Ваш вопрос использует слово «примечание» много раз, когда я думаю, что вы вместо этого имели в виду «аккорд», потому что все ваши примеры включают аккорды, такие как си минор.
Я написал программу для повторения каждого аккорда 4 раза , так что каждый аккорд длится целую меру из 4 ударов.
Я выбрал C Major как значение по умолчанию вместо вашего A Major. Если вы хотите создать аккорды в другом ключе, вы можете изменить масштаб root в вызовах chord_to_names на chord_to_midi, используя параметр root_midi. Например, назовите его 57 вместо значения по умолчанию 60, чтобы получить мажор.
Как только вы запустите эту программу, она сгенерирует chords.mid
, которую вы можете открыть в программе, такой как MuseScore или редактор миди.
Пример вывода:
Generated chords: [0, 0, 1, 4, 0, 3, 4, 5]
Note names:
['C', 'E', 'G']
['C', 'E', 'G']
['D', 'F', 'A']
['G', 'B', 'D']
['C', 'E', 'G']
['F', 'A', 'C']
['G', 'B', 'D']
['A', 'C', 'E']
MIDI Notes:
[60, 64, 67]
[60, 64, 67]
[62, 65, 69]
[55, 59, 62]
[60, 64, 67]
[53, 57, 60]
[55, 59, 62]
[57, 60, 64]
Что выглядит так, как показано в MuseScore:
import numpy as np
from music21.chord import Chord
from music21.stream import Part
from music21.midi.translate import streamToMidiFile
CHORD_TRANSITIONS = np.matrix([
# Transition Matrix: (FROM)
# ---------------------------------------------
# I ii iii IV V vi viio
# ---------------------------------------------
[0.07, 0.00, 0.00, 0.00, 0.65, 0.14, 0.86], # | I |
[0.15, 0.17, 0.00, 0.05, 0.07, 0.30, 0.00], # | ii |
[0.00, 0.00, 0.00, 0.00, 0.00, 0.37, 0.00], # | iii |
[0.19, 0.00, 0.39, 0.16, 0.14, 0.00, 0.00], # | IV | (TO)
[0.52, 0.83, 0.29, 0.43, 0.00, 0.19, 0.00], # | V |
[0.07, 0.00, 0.32, 0.36, 0.14, 0.00, 0.14], # | vi |
[0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00]]) # | viio |
CHORD_TRANSITIONS = CHORD_TRANSITIONS.T # swap axes for simpler lookup based on current chord
TRIADS = [
(0, 4, 7), # I
(2, 5, 9), # ii
(4, 7, 11), # iii
(-7, -3, 0), # IV
(-5, -1, 2), # V
(-3, 0, 4), # vi
(-1, 2, 5), # viio
]
PC_TO_NAME = ('C', 'C#', 'D', 'Eb', 'E', 'F', 'F#', 'G', 'Ab', 'A', 'Bb', 'B')
def midi_to_pc(midi_note):
return midi_note % 12
def midi_to_name(midi_note):
return PC_TO_NAME[midi_to_pc(midi_note)]
def chord_to_midi(chord_index, root_midi=60): # chord_index is from 0 to 6
return [root_midi + x for x in TRIADS[chord_index]]
def chord_to_names(chord_index, root_midi=60): # chord_index is from 0 to 6
return [midi_to_name(note) for note in chord_to_midi(chord_index, root_midi)]
num_chords = len(CHORD_TRANSITIONS)
def generate(start_chord=0, length=8):
cur = start_chord
chords = [cur]
for _ in range(length - 1):
probs = CHORD_TRANSITIONS[cur,:].tolist()[0]
chord = np.random.choice(num_chords, p=probs)
chords.append(chord)
cur = chord
return chords
chords = generate()
print(f'Generated chords: {chords}')
print(f'Note names:')
for chord in chords:
print(chord_to_names(chord))
print(f'MIDI notes:')
for chord in chords:
print(chord_to_midi(chord))
print('Converting to MIDI')
part = Part()
for chord in chords:
midis = chord_to_midi(chord)
# Repeat each chord 4 times.
for i in range(4):
part.append(Chord(midis, quarterLength=1))
mf = streamToMidiFile(part)
mf.open('chords.mid', 'wb')
mf.write()
mf.close()