import tkinter as tk
from tkinter import messagebox, Menu
import pretty_midi
import re
# 音符映射(保持不变)
note_map = {
# 超低音区(3个点)
'1...': 24, '2...': 26, '3...': 28, '4...': 29, '5...': 31, '6...': 33, '7...': 35,
# 低2音区(2个点)
'1..': 36, '2..': 38, '3..': 40, '4..': 41, '5..': 43, '6..': 45, '7..': 47,
# 低音区(1个点)
'1.': 48, '2.': 50, '3.': 52, '4.': 53, '5.': 55, '6.': 57, '7.': 59,
# 中音区(无符号)
'1': 60, '2': 62, '3': 64, '4': 65, '5': 67, '6': 69, '7': 71,
# 高音区(1个*)
'1*': 72, '2*': 74, '3*': 76, '4*': 77, '5*': 79, '6*': 81, '7*': 83,
# 高2音区(2个*)
'1**': 84, '2**': 86, '3**': 88, '4**': 89, '5**': 91, '6**': 93, '7**': 95,
# 高3音区(3个*)
'1***': 96, '2***': 98, '3***': 100, '4***': 101, '5***': 103, '6***': 105, '7***': 107,
'0': None, '-': None # 休止符
}
# 解析简谱字符串为(音符, 时值)列表
def parse_score(s):
score = []
i = 0
while i < len(s):
if s[i] in ['-', '\n']: # 新增处理换行符
if s[i] == '-':
# 处理休止符或延长符(连续横杠为延长时值)
j = i
while j < len(s) and s[j] == '-':
j += 1
duration = j - i # 横杠数量即延长拍数
score.append(('-', duration))
i = j
else: # 换行符直接跳过
i += 1
else:
# 处理音符(数字+*或.)
j = i + 1
while j < len(s) and s[j] in ['.', '*']:
j += 1
note = s[i:j]
# 计算音符后的横杠时值(延长部分)
k = j
while k < len(s) and s[k] == '-':
k += 1
duration = 1 + (k - j) # 音符本身1拍,横杠每根+1拍
score.append((note, duration))
i = k
return score
def generate_midi():
"""生成MIDI文件"""
try:
# 获取输入值
简谱 = 简谱文本框.get("1.0", tk.END).strip()
速度 = int(速度文本框.get())
乐器编号 = int(乐器编号文本框.get())
文件名 = 文件名文本框.get().strip()
if not 简谱:
messagebox.showwarning("警告", "请输入简谱")
return
if not 文件名:
messagebox.showwarning("警告", "请输入文件名")
return
# 解析简谱
parsed_score = parse_score(简谱)
# 生成MIDI
midi = pretty_midi.PrettyMIDI()
instrument = pretty_midi.Instrument(program=乐器编号)
# 根据速度计算每拍的时间(秒)
beat_duration = 60 / 速度
time = 0
for note_str, duration in parsed_score:
if note_str in ['0', '-']:
time += duration
continue
midi_pitch = note_map.get(note_str)
if not midi_pitch:
messagebox.showwarning("警告", f"未知音符:{note_str}")
continue
# 根据每拍时间调整音符的起始时间和结束时间
start_time = time * beat_duration
end_time = (time + duration) * beat_duration
note = pretty_midi.Note(
velocity=80, pitch=midi_pitch, start=start_time, end=end_time
)
instrument.notes.append(note)
time += duration
midi.instruments.append(instrument)
# 保存文件,使用用户输入的文件名
midi.write(f"{文件名}.mid")
messagebox.showinfo("成功", f"MIDI文件生成成功:{文件名}.mid")
except Exception as e:
messagebox.showerror("错误", f"生成失败:{str(e)}")
def create_textbox_menu(textbox):
"""创建文本框右键菜单"""
menu = Menu(textbox, tearoff=0)
# 全选功能
def select_all(event):
textbox.tag_add(tk.SEL, "1.0", tk.END)
textbox.icursor(tk.END)
return "break" # 阻止默认右键菜单
# 复制功能
def copy_text(event):
textbox.event_generate("<<Copy>>")
return "break"
# 粘贴功能
def paste_text(event):
textbox.event_generate("<<Paste>>")
return "break"
# 绑定右键事件
def show_menu(event):
try:
menu.tk_popup(event.x_root, event.y_root)
finally:
menu.grab_release()
# 菜单选项
menu.add_command(label="全选", command=lambda: select_all(None))
menu.add_separator()
menu.add_command(label="复制", command=lambda: copy_text(None))
menu.add_command(label="粘贴", command=lambda: paste_text(None))
# 绑定右键点击事件
textbox.bind("<Button-3>", show_menu)
# 绑定Ctrl+A全选
textbox.bind("<Control-a>", select_all)
# 保留原有Ctrl+C/Ctrl+V功能
textbox.bind("<Control-c>", copy_text)
textbox.bind("<Control-v>", paste_text)
# 创建主窗口
窗口 = tk.Tk()
窗口.title("简谱转MIDI工具")
窗口.geometry("500x400")
# 简谱输入区域
tk.Label(窗口, text="请输入简谱(使用.-*表示音高):").pack(pady=5)
简谱文本框 = tk.Text(窗口, height=8, width=50)
简谱文本框.pack(pady=5, padx=10)
简谱文本框.insert(tk.END, "5-351*--76-1*-5---5-123-212----5-351*--76-1*-5---5-234--7.1----6-1*-1*---7-671*---671*665312----5-351*--76-1*-5---5-234--7.1------")
# 初始化右键菜单
create_textbox_menu(简谱文本框)
# 速度输入
tk.Label(窗口, text="速度(BPM,默认120):").pack(pady=3)
速度文本框 = tk.Entry(窗口)
速度文本框.pack(pady=3, padx=10)
速度文本框.insert(tk.END, "120")
# 乐器编号输入
tk.Label(窗口, text="乐器编号(GM标准,如钢琴=0):").pack(pady=3)
乐器编号文本框 = tk.Entry(窗口)
乐器编号文本框.pack(pady=3, padx=10)
乐器编号文本框.insert(tk.END, "0")
# 新增文件名输入文本框
tk.Label(窗口, text="请输入文件名:").pack(pady=3)
文件名文本框 = tk.Entry(窗口)
文件名文本框.pack(pady=3, padx=10)
# 生成按钮
生成按钮 = tk.Button(窗口, text="生成MIDI文件", command=generate_midi, bg="#4CAF50", fg="white")
生成按钮.pack(pady=15)
窗口.mainloop()
手机端Python语言pretty_midi简谱生成mid音频文件程序代码QZQ
最新推荐文章于 2025-06-03 19:16:49 发布