You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 21:19:50