如何在Python中无阻塞顺序播放音乐,支持交互且无外部依赖?
纯Python实现无阻塞顺序音频播放方案
改进Pygame实现(无阻塞+顺序播放+交互)
你之前用sleep()导致程序阻塞的问题,核心是没有利用Pygame的事件循环机制。改用主循环监听播放状态+处理用户交互,就能实现非阻塞的顺序播放,同时支持暂停、停止等操作:
from pygame import mixer, pygame, KEYDOWN, K_p, K_s, K_q def main(): mixer.init() pygame.init() pygame.display.set_mode((200, 100)) # 仅用于触发事件循环,可最小化 clock = pygame.time.Clock() # 定义播放队列 song_queue = ["track1.mp3", "track2.mp3", "track3.mp3"] current_song_idx = 0 # 初始化第一首曲目 if song_queue: mixer.music.load(song_queue[current_song_idx]) mixer.music.play() running = True while running: # 处理用户交互事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False if event.type == KEYDOWN: # 暂停/继续播放 if event.key == K_p: if mixer.music.get_busy() and mixer.music.get_pos() > 0: mixer.music.pause() else: mixer.music.unpause() # 停止当前播放 elif event.key == K_s: mixer.music.stop() # 退出程序 elif event.key == K_q: running = False # 自动播放下一首(当前曲目播放完毕时) if not mixer.music.get_busy() and current_song_idx < len(song_queue) - 1: current_song_idx += 1 mixer.music.load(song_queue[current_song_idx]) mixer.music.play() clock.tick(30) # 控制循环帧率,避免CPU占用过高 mixer.quit() pygame.quit() if __name__ == "__main__": main()
这个方案的优势:
- 基于Pygame纯Python库,无外部依赖,便于分发
- 主循环非阻塞,可实时响应暂停、停止等交互操作
- 自动按队列顺序播放,不会出现曲目重叠
其他纯Python库选项
1. playsound(极简异步方案)
playsound 2.x版本支持异步播放,配合线程和队列可实现顺序播放,适合对功能复杂度要求较低的场景:
from playsound import playsound import threading import time song_queue = ["track1.mp3", "track2.mp3"] current_song_idx = 0 pause_flag = False def play_queue(): global current_song_idx, pause_flag while current_song_idx < len(song_queue): if not pause_flag: playsound(song_queue[current_song_idx]) current_song_idx += 1 time.sleep(0.1) # 启动播放线程 play_thread = threading.Thread(target=play_queue) play_thread.start() # 命令行交互示例 while True: cmd = input("输入命令:p(暂停/继续) | s(停止) | q(退出): ").strip().lower() if cmd == 'p': pause_flag = not pause_flag print("已暂停" if pause_flag else "继续播放") elif cmd == 's': current_song_idx = len(song_queue) print("已停止播放队列") elif cmd == 'q': current_song_idx = len(song_queue) play_thread.join() print("退出程序") break
注意:playsound的暂停是通过跳过当前曲目实现的模拟效果,无法精确暂停在当前播放位置。
2. pyaudio(底层自定义方案)
如果需要高度自定义音频控制(比如精确暂停到毫秒级),可以用pyaudio直接操作音频流,但代码复杂度较高,且默认仅支持WAV格式(MP3需额外解码处理):
import pyaudio import wave import threading import time class AudioPlayer: def __init__(self): self.p = pyaudio.PyAudio() self.stream = None self.wf = None self.paused = False self.stop_flag = False self.current_pos = 0 def play_song(self, song_path): self.wf = wave.open(song_path, 'rb') self.stream = self.p.open( format=self.p.get_format_from_width(self.wf.getsampwidth()), channels=self.wf.getnchannels(), rate=self.wf.getframerate(), output=True ) self.wf.setpos(self.current_pos) data = self.wf.readframes(1024) while data and not self.stop_flag: if not self.paused: self.stream.write(data) data = self.wf.readframes(1024) self.current_pos = self.wf.tell() else: time.sleep(0.1) self.stream.stop_stream() self.stream.close() self.wf.close() self.current_pos = 0 def pause(self): self.paused = True def unpause(self): self.paused = False def stop(self): self.stop_flag = True def close(self): self.p.terminate() # 队列播放示例 song_queue = ["track1.wav", "track2.wav"] player = AudioPlayer() current_idx = 0 def play_queue(): global current_idx while current_idx < len(song_queue) and not player.stop_flag: player.play_song(song_queue[current_idx]) current_idx += 1 player.close() play_thread = threading.Thread(target=play_queue) play_thread.start() # 交互逻辑 while True: cmd = input("命令:p(暂停/继续) | s(停止) | q(退出): ").strip().lower() if cmd == 'p': player.pause() if not player.paused else player.unpause() print("已暂停" if player.paused else "继续播放") elif cmd == 's': player.stop() print("已停止") elif cmd == 'q': player.stop() play_thread.join() print("退出") break
总结
- 优先选择改进后的Pygame方案:支持MP3等常见格式,代码简洁,能满足顺序播放、非阻塞、交互等核心需求,无外部依赖。
- 追求极简选playsound:适合对暂停精度要求不高的场景,代码量极少。
- 需要高度自定义选pyaudio:适合专业音频控制场景,但MP3播放需额外解码处理。
内容的提问来源于stack exchange,提问作者Nathan Vidal
相关产品推荐
相关产品推荐

