当我的波形软件进入静默状态时,如何去除这些噼啪声?

编程语言 2026-07-12

我正在编写一个Python脚本,在预设频率下生成各种波形。为实现这一目标,我使用PyAudio库(C的 PortAudio的封装)来输出基于8 位整数的音频。实际的音频输出工作得非常好,可能只有极少数例外并非我关注的焦点。

问题在于程序启动或结束时会有很大的爆音与噼啪声。我读到这可能由音量的突变引起,但我在程序结束前尝试降低音量的举措要么没有作用,要么反而让问题恶化。

我在下方附上了我的源代码。噪音的原因是什么?是不是有某个尚未更新的PyAudio的更深层问题?是不是我存储强度值的方式有问题?是不是存在库冲突?我是不是漏掉了某个关键步骤?

import pyaudio
import random
import pygame
import copy
pygame.init()

framerate = 60
frequency = 440 #Hertz (Oscillations per Second)
samplerate = 16000 #Samples Per Second
volume = 4 #out of 256

SPU = samplerate//framerate #Samples Per Update
SPO = samplerate//frequency #Samples Per Oscillation

#0: Pulse Wave
#1: Triangle Wave
#2: Sawtooth Wave
#3: Noise
mode = 0

lastvalue = 0
gentimer = 0
pulseswitch = 1
tritone = 0
noisemode = False
noisetimer = 0

timer = pygame.time.Clock()
window = pygame.display.set_mode((100,200))
speaker = pyaudio.PyAudio()
channel = speaker.open(format=pyaudio.paUInt8,frames_per_buffer=SPU,channels=1,rate=samplerate,output=True)
data = bytearray()
lastdata = bytearray()

killtimer = -1
while True:
    events = pygame.event.get()
    for event in events:
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_1:
                mode = 1
            elif event.key == pygame.K_2:
                mode = 2
            elif event.key == pygame.K_3:
                mode = 3
            elif event.key == pygame.K_0:
                mode = 0
            elif event.key == pygame.K_m:
                if noisemode:
                    noisemode = False
                else:
                    noisemode = True
            elif event.key == pygame.K_SPACE:
                frequency = 261.6256
                SPO = samplerate//frequency
            gentimer = 0
        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_SPACE:
                frequency = 440
                SPO = samplerate//frequency
                gentimer = 0
        elif event.type == pygame.QUIT:
            killtimer = 20
    if mode == 0:
        for frame in range(SPU):
            gentimer += 1
            if gentimer >= (samplerate/frequency)/2:
                gentimer = gentimer - ((samplerate/frequency)/2)
                if pulseswitch == 1:
                    pulseswitch = 0
                else:
                    pulseswitch = 1
            data.append(int(volume*pulseswitch/2))
    elif mode == 1:
        for frame in range(SPU):
            gentimer += (1-2*pulseswitch)
            if gentimer >= (samplerate/frequency)/2:
                gentimer = ((samplerate/frequency))-gentimer
                pulseswitch = 1
            elif gentimer < 0:
                gentimer = -gentimer
                pulseswitch = 0
            tritone = gentimer/((samplerate/frequency)/2)
            data.append(int(volume*tritone))
    elif mode == 2:
        for frame in range(SPU):
            gentimer += 1
            if gentimer >= (samplerate/frequency):
                gentimer = gentimer-(samplerate/frequency)
                #print(gentimer)
            tritone = gentimer/(samplerate/frequency)
            data.append(int(volume*tritone))
    elif mode == 3:
        for frame in range(SPU):
            gentimer += 1
            if gentimer >= (samplerate/frequency):
                gentimer = 0
                if noisemode:
                    noisetimer += 1
                if noisetimer >= 31:
                    noisetimer = 0
                    random.seed(0)
                tritone = random.randint(0,255)/255
            data.append(int(volume*tritone))    
    channel.write(bytes(data))
    data = bytearray()
    timer.tick(framerate)
    if killtimer != -1:
        volume = volume*0.9
        if volume < 0.1:
            break
channel.stop_stream()
channel.close()
speaker.terminate()

解决方案

我终于找出问题出在哪里,想在这里发帖,以防其他人遇到同样的问题。

实际问题源自我生成音调的方法——因为我使用的是无符号的8 位整数,中点不是0,而是128。我在代码中没有对此进行补偿,这导致爆音成为不可避免的。解决这个问题其实很简单,只需把生成的波值作为从128减去半个音量的偏移量使用(也就是说如果音量是4,且脉冲波生成的值为0 或4,输出不应该是0 和4,而应该是126(128 - volume/2 + 0)和130(128 - volume/2 + 4))。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章