Python语言用tkinter播放beep音符简谱程序代码


import numpy as np
import pygame
import tkinter as tk
from tkinter import Menu, messagebox

# 初始化音频系统
pygame.init()
pygame.mixer.init()

# 标准简谱音符频率(修正版)
NOTES = {
    '1.': 130.81, '2.': 146.83, '3.': 164.81, '4.': 174.61, '5.': 196.00, '6.': 220.00, '7.': 246.94,
    '1': 261.63,  '2': 293.66,  '3': 329.63,  '4': 349.23,  '5': 392.00,  '6': 440.00,  '7': 493.88,
    '1*': 523.25, '2*': 587.33, '3*': 659.25, '4*': 698.46, '5*': 783.99, '6*': 880.00, '7*': 987.77,
    '-': 0         # 休止符(连写表示时长)
}

# 默认简谱(无空格,含换行)
DEFAULT_SCORE = """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------"""

def generate_sound(freq, duration=0.3):
    if freq == 0: return None
    t = np.linspace(0, duration, int(44100*duration))
    mono = np.sin(2*np.pi*freq*t)
    stereo = np.column_stack((mono, mono))
    return pygame.sndarray.make_sound((stereo*32767).astype(np.int16))

def parse_score(score_text):
    """解析连续音符,自动识别音符边界和休止符"""
    notes = []
    i = 0
    n = len(score_text)
    
    while i < n:
        char = score_text[i]
        if char == '-':
            # 处理休止符(连写-)
            start = i
            while i < n and score_text[i] == '-':
                i += 1
            notes.append('-'*(i-start))
        else:
            # 识别音符(可能包含.或*)
            start = i
            if char in '1234567':
                i += 1
                if i < n and score_text[i] in ('.', '*'):
                    i += 1  # 处理低音/高音标记
            notes.append(score_text[start:i])
    return notes

def play_sequence():
    try:
        speed = float(speed_scale.get())  # 获取速度值
        score_text = entry.get("1.0", tk.END).strip() or DEFAULT_SCORE  
        score_text = ''.join([c for c in score_text if c in '1234567.*-'])
        notes = parse_score(score_text)
        
        valid_notes = []
        for note in notes:
            if note.startswith('-'):
                valid_notes.append(note)
            else:
                note_upper = note.upper()
                if note_upper in NOTES:
                    valid_notes.append(note_upper)
                else:
                    print(f"无效音符: {note}")
        
        if not valid_notes:
            messagebox.showwarning("提示", "无有效音符!")
            return
        
        duration_base = 0.3 * speed  # 根据速度调整时长(速度=1为默认,0.5为加快2倍)
        
        for note in valid_notes:
            if note.startswith('-'):
                length = len(note)
                pygame.time.delay(int(length * duration_base * 1000))
            else:
                freq = NOTES[note]
                sound = generate_sound(freq, duration_base)
                if sound:
                    sound.play()
                    pygame.time.delay(int(duration_base * 1000) + 20)  # 微小延迟防重叠
    except Exception as e:
        messagebox.showerror("错误", f"播放失败:{str(e)}")

def create_text_right_click_menu(text_widget):
    """创建文本框右键菜单"""
    right_click_menu = Menu(root, tearoff=0)
    right_click_menu.add_command(label="全选", command=lambda: text_widget.tag_add(tk.SEL, 1.0, tk.END))
    right_click_menu.add_command(label="复制", command=lambda: text_widget.event_generate("<<Copy>>"))
    right_click_menu.add_command(label="粘贴", command=lambda: text_widget.event_generate("<<Paste>>"))
    right_click_menu.add_command(label="剪切", command=lambda: text_widget.event_generate("<<Cut>>"))
    right_click_menu.add_separator()
    right_click_menu.add_command(label="删除", command=lambda: text_widget.delete(tk.SEL_FIRST, tk.SEL_LAST))
    
    def show_menu(event):
        try:
            right_click_menu.tk_popup(event.x_root, event.y_root)
        finally:
            right_click_menu.grab_release()
    
    text_widget.bind("<Button-3>", show_menu)  # 绑定右键事件(Windows/Linux)
    text_widget.bind("<Button-2>", show_menu)  # 绑定中键事件(macOS可能需要)

# 创建界面
root = tk.Tk()
root.title("简谱播放器(带速度控制)")
root.geometry("800x500")

# -------------------- 速度控制滑块 --------------------
speed_frame = tk.Frame(root)
speed_frame.pack(pady=10, padx=20, fill=tk.X)

speed_label = tk.Label(speed_frame, text="播放速度(0.5-2.0倍):")
speed_label.pack(side=tk.LEFT, padx=5)

speed_scale = tk.Scale(speed_frame, from_=0.5, to=2.0, resolution=0.1, 
                       orient=tk.HORIZONTAL, length=100, showvalue=1)
speed_scale.set(1.0)  # 默认速度1倍
speed_scale.pack(side=tk.LEFT, padx=10)

# -------------------- 多行文本框(带右键菜单) --------------------
label = tk.Label(root, text="输入简谱(无空格,-表示休止符,.低音*高音,支持右键菜单):")
label.pack(pady=10)

entry = tk.Text(root, font=('Arial', 12), width=40, height=10, wrap=tk.WORD)       # 自动换行(按单词分割)) 

entry.pack(pady=10, padx=20)  # pack()仅控制布局,不涉及尺寸      # 自动换行(按单词分割)
entry.insert(tk.END, DEFAULT_SCORE)  # 设置默认简谱

# 绑定右键菜单
create_text_right_click_menu(entry)

# -------------------- 播放按钮 --------------------
play_btn = tk.Button(root, text="开始播放", command=play_sequence,
                     font=('Arial', 12), bg="#4CAF50", fg="white", width=20)
play_btn.pack(pady=10)

root.mainloop()
pygame.quit()




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

EasySoft易软

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

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

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

打赏作者

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

抵扣说明:

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

余额充值