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

如何用pygame.mixer调整MP3播放速度?现有方案失效求解决

50%速度播放MP3并支持播放控制的解决方案

为什么修改pygame.mixer.init(frequency)无效

你尝试通过修改初始化采样率来变速的思路不对——pygame.mixer.music会自动将MP3的采样率重新采样到初始化的频率,这只是改变了音频的播放采样率,并不会改变播放速度或音调,所以无论怎么设置都不会有效果。

方案一:基于pygame.Sound+手动进度管理(无需额外依赖)

这个方案通过numpy处理音频变速,再用线程手动管理播放进度,实现暂停、停止、跳转功能:

1. 音频变速函数

import numpy as np
import pygame

def speed_change(sound: pygame.mixer.Sound, speed: float = 1.0) -> pygame.mixer.Sound:
    # 将音频转换为numpy数组
    sound_array = np.frombuffer(sound.get_raw(), dtype=np.int16)
    # 计算变速后的数组长度
    new_length = int(len(sound_array) / speed)
    # 线性插值实现变速
    sound_array = np.interp(
        np.linspace(0, len(sound_array), new_length),
        np.arange(len(sound_array)),
        sound_array
    ).astype(np.int16)
    # 转换回pygame Sound对象
    return pygame.mixer.Sound(sound_array.tobytes())

2. 播放控制逻辑

import threading
import time

# 初始化pygame mixer
pygame.mixer.init()

# 加载原始音频并生成变速版本
original_sound = pygame.mixer.Sound("track.mp3")
slow_sound = speed_change(original_sound, 0.5)

# 播放状态变量
current_pos = 0.0
is_playing = False
is_paused = False

def _play_loop():
    """后台播放线程"""
    global current_pos, is_playing, is_paused
    channel = pygame.mixer.Channel(0)
    while is_playing:
        if is_paused:
            time.sleep(0.1)
            continue
        # 计算剩余播放时长
        remaining = slow_sound.get_length() - current_pos
        if remaining <= 0:
            is_playing = False
            break
        # 从当前位置开始播放
        channel.play(slow_sound, start=current_pos)
        start_time = time.time()
        # 跟踪播放进度,直到播放结束、被暂停或停止
        while channel.get_busy() and is_playing and not is_paused:
            current_pos = time.time() - start_time + current_pos
            time.sleep(0.01)
        # 暂停时停止当前播放
        if is_paused:
            channel.stop()

# 对外暴露的控制函数
def start():
    global is_playing, is_paused
    if not is_playing:
        is_playing = True
        is_paused = False
        threading.Thread(target=_play_loop, daemon=True).start()

def pause():
    global is_paused
    is_paused = True

def resume():
    global is_paused
    is_paused = False

def stop():
    global is_playing, is_paused, current_pos
    is_playing = False
    is_paused = False
    current_pos = 0.0
    pygame.mixer.Channel(0).stop()

def seek(seconds: float):
    global current_pos, is_paused
    # 确保跳转位置在有效范围内
    current_pos = max(0.0, min(seconds, slow_sound.get_length()))
    # 如果正在播放,停止当前片段,线程会从新位置继续
    if is_playing and not is_paused:
        pygame.mixer.Channel(0).stop()

# 示例用法
if __name__ == "__main__":
    start()
    time.sleep(5)  # 播放5秒
    pause()
    time.sleep(2)  # 暂停2秒
    resume()
    time.sleep(3)  # 继续播放3秒
    seek(10)  # 跳转到10秒位置
    time.sleep(10)  # 播放10秒
    stop()

方案二:用pydub处理变速+pygame控制播放(更稳定的变速效果)

pydub提供了更成熟的音频变速处理,不需要手动写插值逻辑,但需要依赖ffmpeg:

1. 安装依赖

pip install pydub
# 还需要安装ffmpeg,根据系统自行安装:
# Windows: 下载ffmpeg并添加到环境变量
# macOS: brew install ffmpeg
# Linux: sudo apt install ffmpeg

2. 实现代码

from pydub import AudioSegment
import pygame
import threading
import time

# 加载音频并变速(0.5倍速度)
audio = AudioSegment.from_mp3("track.mp3")
# speedup方法的playback_speed是倍数,0.5就是降速到一半
slow_audio = audio.speedup(playback_speed=0.5, chunk_size=150, crossfade=25)
# 导出为WAV格式(pygame对WAV支持更好)
slow_audio.export("slow_track_temp.wav", format="wav")

# 初始化pygame并加载变速后的音频
pygame.mixer.init()
slow_sound = pygame.mixer.Sound("slow_track_temp.wav")

# 以下播放控制逻辑和方案一完全相同,直接复用start/pause/resume/stop/seek函数即可
# ...(复制方案一中的_play_loop和控制函数)

这个方案的优点是变速效果更自然,缺点是需要生成临时WAV文件,且依赖外部工具ffmpeg。


内容的提问来源于stack exchange,提问作者Simon Mayrshofer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 12:45:49