import tkinter as tk
import pygame.midi
import time
初始化 pygame.midi
pygame.midi.init()
获取 MIDI 输出设备
midi_out = pygame.midi.Output(0)
乐器名称映射
instrument_names = {
0: “大钢琴(声学钢琴)”,
1: “明亮的钢琴”,
2: “电钢琴”,
3: “酒吧钢琴”,
4: “柔和的电钢琴”,
5: “加合唱效果的电钢琴”,
6: “羽管键琴(拨弦古钢琴)”,
7: “科拉维科特琴(击弦古钢琴)”,
8: “钢片琴”,
9: “钟琴”,
10: “八音盒”,
11: “颤音琴”,
12: “马林巴”,
13: “木琴”,
14: “管钟”,
15: “大扬琴”,
16: “击杆风琴”,
17: “打击式风琴”,
18: “摇滚风琴”,
19: “教堂风琴”,
20: “簧管风琴”,
21: “手风琴”,
22: “口琴”,
23: “探戈手风琴”,
24: “尼龙弦吉他”,
25: “钢弦吉他”,
26: “爵士电吉他”,
27: “清音电吉他”,
28: “闷音电吉他”,
29: “加驱动效果的电吉他”,
30: “加失真效果的电吉他”,
31: “吉他和音”,
32: “大贝司(声学贝司)”,
33: “电贝司(指弹)”,
34: “电贝司(拨片)”,
35: “无品贝司”,
36: “掌击贝司”,
37: “掌击贝司”,
38: “电子合成贝司”,
39: “电子合成贝司”,
40: “小提琴”,
41: “中提琴”,
42: “大提琴”,
43: “低音大提琴”,
44: “弦乐群颤音音色”,
45: “弦乐群拨弦音色”,
46: “竖琴”,
47: “定音鼓”,
48: “弦乐合奏音色”,
49: “弦乐合奏音色”,
50: “合成弦乐合奏音色”,
51: “合成弦乐合奏音色”,
52: “人声合唱啊”,
53: “人声嘟”,
54: “合成人声”,
55: “管弦乐敲击齐奏”,
56: “小号”,
57: “长号”,
58: “大号”,
59: “加弱音器小号”,
60: “法国号(圆号)”,
61: “铜管组(铜管乐器合奏音色)”,
62: “合成铜管音色”,
63: “合成铜管音色”,
64: “高音萨克斯风”,
65: “次中音萨克斯风”,
66: “中音萨克斯风”,
67: “低音萨克斯风”,
68: “双簧管”,
69: “英国管”,
70: “巴松(大管)”,
71: “单簧管(黑管)”,
72: “短笛”,
73: “长笛”,
74: “竖笛”,
75: “排箫”,
76: “吹瓶声”,
77: “日本尺八”,
78: “口哨声”,
79: “奥卡雷那”,
80: “合成主音(方波)”,
81: “合成主音(锯齿波)”,
82: “合成主音”,
83: “合成主音”,
84: “合成主音”,
85: “合成主音(人声)”,
86: “合成主音(平行五度)”,
87: “合成主音(贝司加主音)”,
88: “合成音色(新世纪)”,
89: “合成音色(温暖)”,
90: “合成音色”,
91: “合成音色(合唱)”,
92: “合成音色”,
93: “合成音色(金属声)”,
94: “合成音色(光环)”,
95: “合成音色”,
96: “合成效果雨声”,
97: “合成效果音轨”,
98: “合成效果水晶”,
99: “合成效果大气”,
100: “合成效果明亮”,
101: “合成效果鬼怪”,
102: “合成效果回声”,
103: “合成效果科幻”,
104: “西塔尔(印度)”,
105: “班卓琴(美洲)”,
106: “三昧线(日本)”,
107: “十三弦筝(日本)”,
108: “卡林巴”,
109: “风笛”,
110: “民族提琴”,
111: “唢呐”,
112: “叮当铃”,
113: “阿哥哥鼓”,
114: “钢鼓”,
115: “木鱼”,
116: “太鼓”,
117: “古高音鼓”,
118: “合成鼓”,
119: “铜钹”,
120: “磨弦”,
121: “呼吸声”,
122: “海浪声”,
123: “鸟鸣”,
124: “电话铃”,
125: “直升机”,
126: “鼓掌声”,
127: “枪声”
}
def play_note(note_number, duration, instrument):
midi_out.set_instrument(instrument)
midi_out.note_on(note_number, 125)
pygame.time.wait(duration)
midi_out.note_off(note_number, 125)
root = tk.Tk()
root.title(“Digital Music Player”)
设置窗口大小
root.geometry(“800x600”)
创建主旋律文本框
entry_main = tk.Text(root, wrap=“word”, height=5)
entry_main.pack(pady=10)
entry_main.place(x=10, y=10, width=300, height=100)
entry_main.insert(tk.END, “d5d5d3/,e5e5e3/,d3d5d5d1d3d3/,d1d3d3d2d1d2c6/,c6c2d1c6d1d1c6”)
创建伴奏文本框
entry_accompaniment = tk.Text(root, wrap=“word”, height=5)
entry_accompaniment.pack(pady=10)
entry_accompaniment.place(x=10, y=200, width=300, height=100)
为主旋律选择乐器的下拉菜单
main_instrument_var = tk.StringVar(root)
default_main_instrument = 2
main_instrument_var.set(f"{default_main_instrument} - {instrument_names.get(default_main_instrument, ‘Unknown’)}“)
main_instrument_menu = tk.OptionMenu(root, main_instrument_var, *[f”{i} - {instrument_names.get(i, ‘Unknown’)}" for i in range(128)])
main_instrument_menu.pack(pady=10)
main_instrument_menu.place(x=320, y=40, width=150, height=50)
为伴奏选择乐器的下拉菜单
accompaniment_instrument_var = tk.StringVar(root)
default_accompaniment_instrument = 116
accompaniment_instrument_var.set(f"{default_accompaniment_instrument} - {instrument_names.get(default_accompaniment_instrument, ‘Unknown’)}“)
accompaniment_instrument_menu = tk.OptionMenu(root, accompaniment_instrument_var, *[f”{i} - {instrument_names.get(i, ‘Unknown’)}" for i in range(128)])
accompaniment_instrument_menu.pack(pady=10)
accompaniment_instrument_menu.place(x=320, y=230, width=150, height=50)
为主旋律选择调式的下拉菜单
main_tune_var = tk.StringVar(root)
default_main_tune = “D 大调”
main_tune_var.set(default_main_tune)
main_tune_menu = tk.OptionMenu(root, main_tune_var, “C 大调”, “D 大调”, “E 大调”, “F 大调”, “G 大调”, “A 大调”, “B 大调”)
main_tune_menu.pack(pady=10)
main_tune_menu.place(x=500, y=40, width=150, height=50)
为伴奏选择调式的下拉菜单
accompaniment_tune_var = tk.StringVar(root)
default_accompaniment_tune = “D 大调”
accompaniment_tune_var.set(default_accompaniment_tune)
accompaniment_tune_menu = tk.OptionMenu(root, accompaniment_tune_var, “C 大调”, “D 大调”, “E 大调”, “F 大调”, “G 大调”, “A 大调”, “B 大调”)
accompaniment_tune_menu.pack(pady=10)
accompaniment_tune_menu.place(x=500, y=230, width=150, height=50)
创建文本框的右键菜单
def show_textbox_menu(event):
menu = tk.Menu(root, tearoff=0)
menu.add_command(label=“剪切”, command=lambda: event.widget.event_generate(“<>”))
menu.add_command(label=“复制”, command=lambda: event.widget.event_generate(“<>”))
menu.add_command(label=“粘贴”, command=lambda: event.widget.event_generate(“<>”))
menu.add_separator()
menu.add_command(label=“全选”, command=lambda: event.widget.event_generate(“<>”))
menu.post(event.x_root, event.y_root)
entry_main.bind(“”, show_textbox_menu)
entry_accompaniment.bind(“”, show_textbox_menu)
def play_music():
main_music_notes = entry_main.get(“1.0”, “end-1c”)
accompaniment_music_notes = entry_accompaniment.get(“1.0”, “end-1c”)
base_duration = 300 # 基础音符时长
main_note_index = 0
accompaniment_note_index = 0
pause_duration = 0.5 # 初始暂停时长
while main_note_index < len(main_music_notes):
main_note_char = main_music_notes[main_note_index:main_note_index + 2]
if main_note_char == ‘/,’:
time.sleep(pause_duration)
main_note_index += 1
continue
main_note_number = convert_note(main_note_char)
if main_note_number is not None:
main_tune = main_tune_var.get()
main_note_number = convert_tune(main_note_number, main_tune)
main_duration = base_duration
play_note(main_note_number, main_duration, int(main_instrument_var.get().split(" - “)[0]))
main_note_index += 1
while accompaniment_note_index < len(accompaniment_music_notes):
accompaniment_note_char = accompaniment_music_notes[accompaniment_note_index:accompaniment_note_index + 2]
if accompaniment_note_char == ‘/,’:
time.sleep(pause_duration)
accompaniment_note_index += 1
continue
accompaniment_note_number = convert_note(accompaniment_note_char)
if accompaniment_note_number is not None:
accompaniment_tune = accompaniment_tune_var.get()
accompaniment_note_number = convert_tune(accompaniment_note_number, accompaniment_tune)
accompaniment_duration = base_duration
play_note(accompaniment_note_number, accompaniment_duration, int(accompaniment_instrument_var.get().split(” - ")[0]))
accompaniment_note_index += 1
def convert_note(note_char):
note_mapping = {‘c1’: 53, ‘c2’: 54, ‘c3’: 55, ‘c4’: 56, ‘c5’: 57, ‘c6’: 58, ‘c7’: 59,
‘d1’: 60, ‘d2’: 61, ‘d3’: 62, ‘d4’: 63, ‘d5’: 64, ‘d6’: 65, ‘d7’: 66,
‘e1’: 67, ‘e2’: 68, ‘e3’: 69, ‘e4’: 70, ‘e5’: 71, ‘e6’: 72, ‘e7’: 73,
‘f1’: 74, ‘f2’: 75, ‘f3’: 76, ‘f4’: 77, ‘f5’: 78, ‘f6’: 79, ‘f7’: 80,
‘g1’: 81, ‘g2’: 82, ‘g3’: 83, ‘g4’: 84, ‘g5’: 85, ‘g6’: 86, ‘g7’: 87,
‘a1’: 39, ‘a2’: 40, ‘a3’: 41, ‘a4’: 42, ‘a5’: 43, ‘a6’: 44, ‘a7’: 45,
‘b1’: 46, ‘b2’: 47, ‘b3’: 48, ‘b4’: 49, ‘b5’: 50, ‘b6’: 51, ‘b7’: 52}
return note_mapping.get(note_char)
def convert_tune(note_number, tune):
if tune == “D 大调”:
return note_number
elif tune == “E 大调”:
return note_number + 7
elif tune == “F 大调”:
return note_number + 14
elif tune == “G 大调”:
return note_number + 21
elif tune == “A 大调”:
return note_number - 21
elif tune == “B 大调”:
return note_number - 14
elif tune == “C 大调”:
return note_number - 7
创建播放按钮
play_button = tk.Button(root, text=“播放”, command=play_music)
play_button.pack(pady=20)
play_button.place(x=330, y=130, width=100, height=50)
root.mainloop()
关闭 MIDI 输出设备
midi_out.close()
pygame.midi.quit()