当我的波形软件进入静默状态时,如何去除这些噼啪声?
我正在编写一个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导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。