如何删除通过将声音音调片段连接在一起构成的音频中的“弹出”和“单击”声音?
我有这个PyAudio代码用于生成一系列音调:
import time
import math
import pyaudio
class Beeper(object):
def __init__(self, **kwargs):
self.bitrate = kwargs.pop('bitrate', 16000)
self.channels = kwargs.pop('channels', 1)
self._p = pyaudio.PyAudio()
self.stream = self._p.open(
format = self._p.get_format_from_width(1),
channels = self.channels,
rate = self.bitrate,
output = True,
)
self._queue = []
def __enter__(self):
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.stream.stop_stream()
self.stream.close()
def tone(self, frequency, length=1000, play=False, **kwargs):
number_of_frames = int(self.bitrate * length/1000.)
##TODO:fix pops?
g = get_generator()
for x in xrange(number_of_frames):
self._queue.append(chr(int(math.sin(x/((self.bitrate/float(frequency))/math.pi))*127+128)))
def play(self):
sound = ''.join(self._queue)
self.stream.write(sound)
time.sleep(0.1)
with Beeper(bitrate=88000, channels=2) as beeper:
i = 0
for f in xrange(1000, 800-1, int(round(-25/2.))):
i += 1
length = log(i+1) * 250/2./2.
beeper.tone(frequency=f, length=length)
beeper.play()
但是当音调改变时,音频中会出现一个独特的“弹出”,我不知道如何删除它.
起初,我认为流行音正在发生,因为我正在播放每个片段,并且在生成片段时每次播放之间的时间足以导致音频平滑.但是,当我将所有剪辑连接成一个字符串并播放时,流行音乐仍然存在.
然后,我认为正弦波在每个片段的边界处不匹配,所以我尝试将当前音频片段的前N帧与前一片段的最后N帧进行平均,但这也没有效果.
我究竟做错了什么?我该如何解决?
解决方法:
你为自己写的答案可以解决这个问题但实际上并不是做这种事情的正确方法.
其中一个问题是您通过与1进行比较来检查正弦波的“尖端”或峰值.并非所有正弦频率都会达到该值,或者可能需要大量的周期才能这样做.
从数学上讲,对于K的所有整数值,正弦的峰值为sin(pi / 2 2piK).
要计算给定频率的正弦值,请使用公式y = sin(2pi * x * f0 / fs),其中x是样本编号,f0是正弦频率,fs是采样率.
对于一个很好的数字,如1kHz,48kHz采样率,当x = 12时,则:
sin(2pi * 12 * 1000/48000) = sin(2pi * 12/48) = sin(pi/2) = 1
然而,在997Hz的频率下,真实峰值落在样品12之后的样品的一部分.
sin(2pi * 12 * 997/48000) = 0.99087178042
sin(2pi * 12 * 997/48000) = 0.99998889671
sin(2pi * 12 * 997/48000) = 0.99209828673
将波形拼接在一起的更好方法是从一个音调跟踪相位并将其用作下一个音调的起始相位.
首先,对于给定的频率,您需要计算出相位增量,请注意它与您使用样本分解的内容相同:
phInc = 2*pi*f0/fs
接下来,计算正弦并更新表示当前阶段的变量.
for x in xrange(number_of_frames):
y = math.sin(self._phase);
self._phase += phaseInc;
把它们放在一起:
def tone(self, frequency, length=1000, play=False, **kwargs):
number_of_frames = int(self.bitrate * length/1000.)
phInc = 2*math.pi*frequency/self.bitrate
for x in xrange(number_of_frames):
y = math.sin(self._phase)
_phase += phaseInc;
self._queue.append(chr(int(y)))