88键钢琴音

172 2025-11-13 10:09 2025-11-13 20:20

  

我问问题的步骤是

sound_objects.get(key)区分大小写吗?

 

对非会员隐藏

 

为什么我按Q会发出双音

我需要大写字母为半音补充

然后kimi给了我答案

明白了!
你的需求是:
  • 小写字母 → 正常音(白键)
  • 大写字母(按住 Shift 再按字母)→ 对应高半音(黑键)
  • 不能出现“按一次 Q 触发 q 和 Q 两个音”的双音 bug

✅ 正确做法:

只监听物理按键,在回调里实时判断 Shift 状态,决定播放小写还是大写对应的文件。

✅ 修改后的完整代码(支持半音,不双触发)

 

对非会员隐藏

✅ 使用说明

表格
 
复制
按键 效果
q C5(白键)
Shift+q C#5(黑键)
ESC 退出

✅ 文件准备

把你对应的 mp3 文件按命名放好即可,例如:
 
复制
53.mp3  # C#5
55.mp3  # D#5
...
 

需要我帮你自动生成黑键文件名或者做音量/复音优化吗?

 

 

for key in SOUNDS:
    keyboard.on_press_key

和keyboard.hook(on_key)方案分析

 

下面把两种做法底层机制、优缺点、适用场景一次性对比清楚,方便你直接选型。
(先给结论,再给细节)

一、一句话结论
表格
 
复制
方案 是否推荐 理由
for key in SOUNDS: keyboard.on_press_key(key, callback) 不推荐 物理键监听,大小写同名键会双触发;注册次数多;无法动态判断 Shift
keyboard.hook(on_key) 推荐 一次注册捕获所有按键事件事件自带 name;可实时看 Shift;无双音 bug

二、底层机制对比
  1. on_press_key(key, callback)
  • 内部调用 OS 接口给单个物理键注册回调。
  • 参数 key物理键名,例如 'q''Q' 被当成同一把物理键;因此:
    – 你注册 'q''Q' 两个回调,按一次 Q 键,两个回调都会进队,于是双音。
  • 回调里拿不到“当前这次按键到底是 q 还是 Q”——因为 OS 只告诉你“Q 键被按下”。
  • 注册次数 = len(SOUNDS),每次导入都会遍历,>=60 次系统调用;事件多时有微小延迟累积。
  1. keyboard.hook(on_key)
  • 一次性向 OS 申请全局键盘事件流(Windows 的 LowLevelKeyboardProc / Linux evdev / macOS CGEventTap)。
  • 每个事件自带字段:
    event.name 字符侧名称(已根据 Shift/Caps 解析成 'q''Q'
    event.scan_code 物理侧
  • 因此在回调里实时判断 Shift 即可决定播放哪个样本,绝对不会双触发
  • 只注册一次,后续全是 Python 层分发,延迟最低

三、实测现象
表格
 
复制
操作 on_press_key hook
单按 Q 同时触发 'q''Q' 两个回调 → 双音 仅得到 'q' → 单音
Shift+Q 同上,仍然双音 得到 'Q' → 黑键单音
快速跑句 偶发多音叠加 干净

四、额外差异
表格
 
复制
维度 on_press_key hook
代码量 需两层循环(注册/卸载) 一个函数 + 一行 hook
动态改键 需先 unhook_key 再注册 直接改 on_key 内部字典
非字母键 需额外列名单 同一套逻辑
线程安全 每个回调独立线程 单线程顺序派发,无竞态
退出时清理 必须 keyboard.unhook_all() 同样一句即可

五、最小可运行模板(hook 版)
Python
 
复制
import keyboard, pygame, os

pygame.mixer.init()
sound = {k: pygame.mixer.Sound(v) for k, v in SOUNDS.items() if os.path.exists(v)}

def on_key(e):
    if e.event_type != 'down':
        return
    k = e.name.lower() if not keyboard.is_pressed('shift') else e.name.upper()
    if k in sound:
        pygame.mixer.find_channel(True).play(sound[k])

keyboard.hook(on_key)
keyboard.wait('esc')
 

六、什么时候可以用 on_press_key?
  • 只做单字符、无大小写区分的快捷键(如 F1、ctrl+s)
  • 明确给不同物理键注册不同功能(数字小键盘 vs 主键盘)
  • 需要单独卸载某一个键的监听(hook 只能全拆)
但凡涉及**“同一物理键、不同逻辑键”**的场景(钢琴、游戏热键、Shift 组合),优先 hook

选 hook,安心写音乐逻辑即可。

 

 

 

 

 

 

 

全部评论

·