键盘响应播放音乐程序

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,于是出现“所有音频通道已满,播放失败”。
要想永远有位置放新声音,可以:
  1. 把通道数调大(最简单有效)
  2. 或者强制抢占最旧的通道(不怕打断也行)
下面给你两种写法,照抄即可用。

方案 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。

 

 

 

全部评论

·