Python语言简谱生成mid音乐程序代码ZXQZQ


import tkinter as tk
from tkinter import messagebox,Menu
from midiutil import MIDIFile
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] == '-':
            # 处理休止符或延长符(连续横杠为延长时值)
            j = i
            while j < len(s) and s[j] == '-':
                j += 1
            duration = j - i  # 横杠数量即延长拍数
            score.append(('-', duration))
            i = j
        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 = MIDIFile(numTracks=1)
        midi.addTempo(0, 0, 速度)
        midi.addProgramChange(0, 0, 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

            midi.addNote(
                track=0, channel=0, pitch=midi_pitch,
                time=time, duration=duration, volume=80
            )
            time += duration

        # 保存文件,使用用户输入的文件名
        with open(f"{文件名}.mid", "wb") as f:
            midi.writeFile(f)
        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()





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EYYLTV

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值