python - 如何使用马尔可夫链矩阵生成音乐?
问题描述
我有一个矩阵 (square - nxn) <--取决于有多少和弦。
------------------------------------------
Transition Matrix: (FROM)
------------------------------------------
A Bm C#m D E F#m G#
------------------------------------------
0.07 0.00 0.00 0.00 0.66 0.14 0.86 | A |
0.15 0.17 0.00 0.05 0.07 0.30 0.00 | Bm |
0.00 0.00 0.00 0.00 0.00 0.37 0.00 | C#m |
0.19 0.00 0.39 0.16 0.14 0.00 0.00 | D | (TO)
0.52 0.83 0.29 0.43 0.00 0.19 0.00 | E |
0.07 0.00 0.32 0.36 0.14 0.00 0.14 | F#m |
0.00 0.00 0.00 0.00 0.00 0.00 0.00 | G# |
该矩阵乘以另一个矩阵,即:
1
0
0
0
0
0
0
这意味着起始音符是 A(第一行对应于 A)。
下一个音符由马尔可夫转移矩阵的列确定,该列表示存在:
7% 的机会成为 A
15% 几率成为 Bm
0% 的机会成为 C#m 等等等等。
(通过矩阵乘法完成,使用 numPy)。
所以得到的矩阵是一个 1col x nrows 矩阵。
这个矩阵看起来像这样:
0.07
0.15
0.00
0.19
0.52
0.07
0.00
^ 这意味着下一个音符是 A 的概率为 0.07%,以此类推。
^ 使用这些概率我可以播放那些音符吗?或者根据它生成一个midi文件?
我应该只取回一个音符(加上第一个音符),并且根据它应该是 E 的概率。
解决方案
我编写了一个小程序来演示马尔可夫生成,包括使用 music21 库输出到 MIDI 文件。
请注意,您的转换矩阵有两个问题:
- 第 5 列中的概率总和不等于 1.00。我用 0.65 替换了 0.66 以使其工作。
- 这是和弦转换概率的矩阵,而不是音符转换。所以我的例子涉及和弦(三和弦),而不是单个音符。您的问题多次使用“音符”一词,而我认为您的意思是“和弦”,因为您的所有示例都涉及和弦,例如B小调。
我编写了一个程序,将每个和弦重复 4 次,这样每个和弦就会持续 4 拍的整个小节。
我选择了 C 大调而不是你的 A 大调。如果您想以不同的键生成和弦,您可以使用“root_midi”参数将调用 chord_to_names 的音阶根更改为 chord_to_midi。例如,用 57 而不是默认的 60 调用它来获得 A 大调。
一旦你运行这个程序,它会生成chords.mid
,你可以在 MuseScore 或 midi 编辑器等程序中打开它。
样本输出:
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()
推荐阅读
- html - Css 在单词之间创建空格
- c++ - 如何在 C++ 代码中灵活使用和替换 std::shared_ptr 或 std::unique_ptr 或原始指针?
- node.js - 如何使用nodejs验证来自joi的“位数”?
- java - Spring Boot数据JPA持久性到数据库错误
- php - Laravel Notification Mailable 在测试中不起作用
- java - JAVA中的XML文件比较忽略节点顺序
- android - 为什么 androidx.preference 库会阻止我的自定义复选框正确显示?
- javascript - 如何用 JavaScript 编写 RSocket 客户端
- python - Pandas 使用 pandas 数据帧的索引来更新同一索引上的另一个数据帧
- gulp - 如何使用 gulp 生成文件?