键盘响应播放音乐程序
18 2025-08-30 13:45
写一个python程序监视键盘事件,当按下h,播放answerMe.mp3,当按下j,播放lookInMyEyes.mp3,当按下k,播放tellmeWhyBaby.mp3,当按下l,播放neng.mp3.要求,按下按键即播放,不打断之前正在播放的mp3
下面这段代码用到了两个第三方库:
-
keyboard:跨平台监听键盘事件
-
playsound:播放 mp3 文件(不阻塞,因此不会打断先前正在播放的声音)
先确保安装依赖:
bash
复制
pip install keyboard playsound==1.3.0
然后运行下面的脚本即可:
Python
复制
# monitor_play.py
import os
import keyboard
from playsound import playsound
# 把四个 mp3 文件放在和脚本同目录下,也可以填绝对路径
SOUNDS = {
'h': 'answerMe.mp3',
'j': 'lookInMyEyes.mp3',
'k': 'tellmeWhyBaby.mp3',
'l': 'neng.mp3'
}
def play_async(file):
"""异步播放,不阻塞主线程,因此不会打断之前的音乐"""
if os.path.isfile(file):
playsound(file, block=False)
else:
print(f'文件不存在: {file}')
# 为每个键注册回调
for key, mp3 in SOUNDS.items():
keyboard.on_press_key(key, lambda e, mp3=mp3: play_async(mp3))
print('已启动键盘监听:h j k l 分别播放对应 mp3,按 ESC 退出...')
keyboard.wait('esc')
运行后:
-
按
h / j / k / l
会立即播放对应 mp3,互不干扰; -
按
ESC
退出监听。
这段代码可以运行,但有一个小问题:
pygame.mixer.music
是单通道的,每次 load()
会打断正在播放的音乐,而你想要的是“不打断之前正在播放的 mp3”。✅ 正确做法:使用 pygame.mixer.Sound
,它可以多通道并发播放
下面是修正后的版本,支持多个音频同时播放,不会互相打断:
Python
复制
import os
import keyboard
import pygame
# 初始化 pygame mixer
pygame.mixer.init()
SOUNDS = {
'h': 'answerMe.mp3',
'j': 'lookInMyEyes.mp3',
'k': 'tellmeWhyBaby.mp3',
'l': 'neng.mp3'
}
# 预加载所有音频对象,提升响应速度
sound_objects = {}
for key, file in SOUNDS.items():
if os.path.isfile(file):
try:
sound_objects[key] = pygame.mixer.Sound(file)
except Exception as e:
print(f'加载失败: {file}, 错误: {e}')
else:
print(f'文件不存在: {file}')
def play_async(key):
"""播放音频(不打断其他)"""
if key in sound_objects:
# 每次播放都新建一个 Channel,避免打断
channel = pygame.mixer.find_channel()
if channel:
channel.play(sound_objects[key])
else:
print("所有音频通道已满,播放失败")
# 注册键盘回调
for key in SOUNDS:
keyboard.on_press_key(key, lambda e, k=key: play_async(k))
print('已启动键盘监听:h j k l 分别播放对应 mp3,按 ESC 退出...')
keyboard.wait('esc')
✅ 特点
-
使用
pygame.mixer.Sound
而非music
,支持多通道并发播放 -
每次按键都新建一个通道播放,不会打断之前的音频
-
提前加载音频,响应更快
-
按
ESC
退出监听
如仍有播放失败,请确认:
-
文件路径正确(建议用绝对路径或放在脚本同目录)
-
音频格式为 MP3 且无损编码(可先用 VLC 播放测试)
如何避免所有音频通道已满,播放失败
pygame.mixer
默认只给 8 条“同时发声通道”(channel)。当 8 条都被占用时再调用
find_channel()
就会返回 None
,于是出现“所有音频通道已满,播放失败”。要想永远有位置放新声音,可以:
-
把通道数调大(最简单有效)
-
或者强制抢占最旧的通道(不怕打断也行)
下面给你两种写法,照抄即可用。
方案 A:启动时一次性把通道调大(推荐)
Python
复制
import os
import keyboard
import pygame
pygame.mixer.init()
pygame.mixer.set_num_channels(64) # ← 关键:一次性开 64 条通道
# 想开 128、256 随意
SOUNDS = {
'h': 'answerMe.mp3',
'j': 'lookInMyEyes.mp3',
'k': 'tellmeWhyBaby.mp3',
'l': 'neng.mp3'
}
sound_objects = {}
for key, file in SOUNDS.items():
if os.path.isfile(file):
sound_objects[key] = pygame.mixer.Sound(file)
def play_async(key):
snd = sound_objects.get(key)
if snd:
ch = pygame.mixer.find_channel()
if ch is None:
# 理论上不会进来,因为我们开了 64 条
ch = pygame.mixer.Channel(0) # 兜底:用 0 号通道
ch.play(snd)
for k in SOUNDS:
keyboard.on_press_key(k, lambda e, kk=k: play_async(kk))
print('监听中:h j k l 播放对应 mp3,按 ESC 退出')
keyboard.wait('esc')
方案 B:通道满就抢占最早那条(不怕打断)
Python
复制
def play_async(key):
snd = sound_objects.get(key)
if snd:
ch = pygame.mixer.find_channel(force=True) # force=True 会抢占
ch.play(snd)
小结
-
绝大多数场景直接
set_num_channels(64)
就够用,简单又安全。 -
如果机器性能很差或怕内存占用,可以改成 32、16,再不行就用方案 B。
全部评论