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

pygame更换Sound播放的音频数据时如何避免爆音实现平滑过渡

爆音问题原因与解决方案

问题原因

你的代码每次调整频率后直接终止旧音频、播放全新生成的正弦波,爆音来源于两个核心问题:

  • 波形相位不连续:旧音频被切断时的瞬时幅值不一定为0,新生成的正弦波默认从0相位(幅值0)起始,两个波形的幅值跳变就会产生爆音
  • 播放硬切换:旧声音直接截断、新声音直接满音量启动,没有过渡缓冲

解决方案

我们通过记忆相位偏移保证波形连续性,搭配短时间交叉淡入实现平滑切换,修改后的代码如下:

# -*- coding: utf-8 -*-
import numpy
import pygame
import pygame.freetype

freq = 100
sampleRate = 44100
# 新增:记录上一次音频结束的相位偏移
phase_offset = 0
# 过渡帧长度,20ms的过渡时间人耳基本感知不到,同时避免爆音
fade_len = int(0.02 * sampleRate)

pygame.freetype.init()
(width, height) = (1280,720)
screen = pygame.display.set_mode((width, height))
myfont = pygame.freetype.Font('Ubuntu-Regular.ttf',220)
myfont.antialiased = True
pygame.mixer.init(44100,-16,2,512)

if __name__ == "__main__":
    pygame.display.set_caption('Signaaligeneraator')
    screen.fill((255,255,255))
    pygame.display.flip()
    running = True
    # 首次生成音频
    arr = numpy.array([4096 * numpy.sin(2.0 * numpy.pi * freq * x / sampleRate + phase_offset) for x in range(0, sampleRate)]).astype(numpy.int16)
    # 计算当前使用的最终相位,作为下一次生成的偏移
    phase_offset = (2.0 * numpy.pi * freq * sampleRate / sampleRate + phase_offset) % (2*numpy.pi)
    arr2 = numpy.c_[arr,arr]
    sound = pygame.sndarray.make_sound(arr2)
    channel = sound.play(-1) # 循环播放,不用每次重新调用play

    while running:
        screen.fill((255,255,255))
        unit = "Hz"
        if freq > 1000:
            unit = "kHz" # 修正原代码中单位逻辑错误
        myfont.render_to(screen, ((width/2)-(len(str(freq)+" "+unit)*120)/2, (height/2)-120/2), str(freq)+" "+unit, (0, 0, 0))
        pygame.display.flip()
      
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_UP:
                    freq += 10  
                    # 生成新音频,用上一次的相位偏移作为起始
                    new_arr = numpy.array([4096 * numpy.sin(2.0 * numpy.pi * freq * x / sampleRate + phase_offset) for x in range(0, sampleRate)]).astype(numpy.int16)
                    # 更新相位偏移
                    phase_offset = (2.0 * numpy.pi * freq * sampleRate / sampleRate + phase_offset) % (2*numpy.pi)
                    # 交叉淡入处理:前fade_len帧旧音从1降到0,新音从0升到1,后面正常播放
                    fade_curve = numpy.linspace(0, 1, fade_len)
                    new_arr[:fade_len] = (arr[-fade_len:] * (1 - fade_curve) + new_arr[:fade_len] * fade_curve).astype(numpy.int16)
                    arr = new_arr
                    arr2 = numpy.c_[arr,arr]
                    new_sound = pygame.sndarray.make_sound(arr2)
                    # 替换当前播放的声音,无缝衔接
                    channel.queue(new_sound)
    pygame.quit()

额外优化说明

如果需要更丝滑的频率调整效果,也可以改用pygame的音频流回调模式实时生成音频,不需要预先生成1秒的音频块,调整频率时直接修改参数即可,相位偏移的计算会更精准,适合需要高频调整参数的场景。

内容的提问来源于stack exchange,提问作者Rando Hinn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 00:06:04