import tkinter as tk
from tkinter import messagebox, Menu
import mido
import re
note_map = {
'1...': 24, '2...': 26, '3...': 28, '4...': 29, '5...': 31, '6...': 33, '7...': 35,
'1..': 36, '2..': 38, '3..': 40, '4..': 41, '5..': 43, '6..': 45, '7..': 47,
'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*': 72, '2*': 74, '3*': 76, '4*': 77, '5*': 79, '6*': 81, '7*': 83,
'1**': 84, '2**': 86, '3**': 88, '4**': 89, '5**': 91, '6**': 93, '7**': 95,
'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)
score.append((note, duration))
i = k
return score
def generate_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(简谱)
mid = mido.MidiFile()
track = mido.MidiTrack()
mid.tracks.append(track)
tempo = mido.bpm2tempo(速度)
track.append(mido.MetaMessage('set_tempo', tempo=tempo, time=0))
track.append(mido.Message('program_change', program=乐器编号, time=0))
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
ticks_per_beat = mid.ticks_per_beat
note_time = int(duration * ticks_per_beat)
track.append(mido.Message('note_on', note=midi_pitch, velocity=64, time=time))
track.append(mido.Message('note_off', note=midi_pitch, velocity=64, time=note_time))
time += duration
mid.save(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("<>")
return "break"
def paste_text(event):
textbox.event_generate("<>")
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("", show_menu)
textbox.bind("", select_all)
textbox.bind("", copy_text)
textbox.bind("", 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()