功能介绍:
该代码可将单轨道的midi文件转换为以最小音符长度为单位的ROM文件(格式为Quartus的mif文件)。
具体转换方式:
例如一段简谱为:1 22 33 4,则输出为:11 22 33 44,即:音符个数=音符长度/最小音符长度。
注:MIDI文件需为单轨且为单音。
使用说明:
将MIDI文件放置在.py文件相同目录下,运行python文件即可生成:test1.txt与test2.txt(midi音符信息)、test3.txt(将音符长度转换为最小音符长度)、ROM.txt(适用与Quartus的ROM序列)。
具体文件内容如图所示:
具体代码如下:
import mido
import os
# MIDI音符值到音乐音符的映射
notes = ['C', 'Cs', 'D', 'Ds', 'E', 'F', 'Fs', 'G', 'Gs', 'A', 'As', 'B']
# 删除文件
if os.path.exists("test1.txt"):
os.remove("test1.txt")
if os.path.exists("test2.txt"):
os.remove("test2.txt")
if os.path.exists("test3.txt"):
os.remove("test3.txt")
if os.path.exists("test4.txt"):
os.remove("test4.txt")
# 读取MIDI文件
mid = mido.MidiFile('untitled.mid')
min = 12 # 音符最小长度
f = open("test1.txt", "a") # 原文本形式
# 遍历每个音轨
for i, track in enumerate(mid.tracks):
f.write('\n----------------------\n')
last_note_on_time = {}
absolute_time = 0
last_event_time = 0
for msg in track:
# 累加时间戳
absolute_time += msg.time
# 检查是否是音符事件
if msg.type in ['note_on', 'note_off']:
# 将MIDI音符值转换为音乐音符
note = notes[msg.note % 12] + str((msg.note // 12)) if msg.note != 0 else '0' # 音符
if msg.type == 'note_on':
# 如果这个note_on事件和上一个事件之间的时间间隔不为0,那么这个时间间隔就表示了一个休止符
if absolute_time - last_event_time > 0:
print('音高: 0, 速度: 64, 长度: {}'.format(absolute_time - last_event_time))
f.write('音高: 0, 速度: 64, 长度: {}\n'.format(absolute_time - last_event_time))
# 记录note_on事件的绝对时间戳
last_note_on_time[note] = absolute_time
elif msg.type == 'note_off' and note in last_note_on_time:
# 计算音符的长度
note_length = absolute_time - last_note_on_time[note]
# 打印音符的音高、速度和长度
print('音高: {}, 速度: {}, 长度: {}'.format(note, msg.velocity, note_length))
# 将音符的音高、速度和长度写入文件
f.write('音高: {}, 速度: {}, 长度: {}\n'.format(note, msg.velocity, note_length))
# if note_length < min:
# min = min
# 记录这个事件的时间戳
last_event_time = absolute_time
print("最小音符长度:", min)
f2 = open("test2.txt", "a") # 原文本形式
# 读取音符文件
with open('test1.txt', 'r') as f:
notes = f.readlines()
# 遍历文件
for i in range(0, len(notes)):
# 获取音符长度
note_parts = notes[i].split(' ')
if note_parts[-1].strip().isdigit():
note_length = int(note_parts[-1])
# 判断音符长度如果大于最小音符x倍,则输出x个该音符
num = note_length / min
for j in range(int(num)): # 输出x个该音符
f2.write(notes[i]) # 写入文件
print(notes[i])
else:
print(f"Skipping line {i}: not a valid note")
f.close()
f2.close()
# 遍历文件,读取音高
f2 = open("test2.txt", "r")
notes = f2.readlines()
f2.close()
f3 = open("test3.txt", "a") # 音高
for i in range(0, len(notes)):
note_parts = notes[i].split(' ')
note = note_parts[1]
f3.write(note + '\n')
print(note)
f3.close()
# 音高到rom的映射
# C2 -> 1, Cs2 -> 2, D2 -> 3, Ds2 -> 4, E2 -> 5, F2 -> 6, Fs2 -> 7, G2 -> 8, Gs2 -> 9, A2 -> 10, As2 -> 11, B2 -> 12,
# C3 -> 13, Cs3 -> 14, D3 -> 15, Ds3 -> 16, E3 -> 17, F3 -> 18, Fs3 -> 19, G3 -> 20, Gs3 -> 21, A3 -> 22, As3 -> 23, B3 -> 24,
# C4 -> 25, Cs4 -> 26, D4 -> 27, Ds4 -> 28, E4 -> 29, F4 -> 30, Fs4 -> 31, G4 -> 32, Gs4 -> 33, A4 -> 34, As4 -> 35, B4 -> 36,
# C5 -> 37, Cs5 -> 38, D5 -> 39, Ds5 -> 40, E5 -> 41, F5 -> 42, Fs5 -> 43, G5 -> 44, Gs5 -> 45, A5 -> 46, As5 -> 47, B5 -> 48,
# C6 -> 49, Cs6 -> 50, D6 -> 51, Ds6 -> 52, E6 -> 53, F6 -> 54, Fs6 -> 55, G6 -> 56, Gs6 -> 57, A6 -> 58, As6 -> 59, B6 -> 60,
# LL -> 0
rom_notes = ['0,', 'C4,', 'Cs4,', 'D4,', 'Ds4,', 'E4,', 'F4,', 'Fs4,', 'G4,', 'Gs4,', 'A4,', 'As4,', 'B4,',
'C5,', 'Cs5,', 'D5,', 'Ds5,', 'E5,', 'F5,', 'Fs5,', 'G5,', 'Gs5,', 'A5,', 'As5,', 'B5,',
]
# 遍历文件,读取音高
f3 = open("test3.txt", "r")
notes = f3.readlines()
# 将音高转换为rom
f4 = open("ROM.txt", "a") # rom
for i in range(0, len(notes)):
note = notes[i].strip()
if note == '0':
rom = 'LL'
else:
rom = rom_notes.index(note) + 1
f4.write(str(i) + " : " + str(rom) + ';\n')
print(rom)
print("最小音符长度:", min)
f3.close()