如何删除"弹出"和"点击"音频中的声音是通过将声音音调片段连接在一起构成的吗?
我有这个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()
但是当音调发生变化时,会出现一个与众不同的音符#34;在音频中,我不知道如何删除它。
起初,我认为流行音正在发生,因为我正在播放每个片段,并且在生成片段时每次播放之间的时间足以导致音频变平。但是,当我将所有剪辑连接成一个字符串并播放时,流行音乐仍然存在。
然后,我认为正弦波在每个片段的边界处都不匹配,所以我尝试将当前音频片段的前N帧与前一片段的最后N帧进行平均,但是也没有效果。
我做错了什么?我该如何解决这个问题?
答案 0 :(得分:2)
你为自己写的答案可以解决这个问题,但这并不是解决这类问题的正确方法。
其中一个问题是你检查"提示"或者通过与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)))
答案 1 :(得分:1)
我最初怀疑个别波形没有对齐是正确的,我在Audacity中检查证实了这一点。我的解决方案是修改代码以启动和停止正弦波峰值上的每个波形。
def tone(self, frequency, length=1000, play=False, **kwargs):
number_of_frames = int(self.bitrate * length/1000.)
record = False
x = 0
y = 0
while 1:
x += 1
v = math.sin(x/((self.bitrate/float(frequency))/math.pi))
# Find where the sin tip starts.
if round(v, 3) == +1:
record = True
if record:
self._queue.append(chr(int(v*127+128)))
y += 1
if y > number_of_frames and round(v, 3) == +1:
# Always end on the high tip of the sin wave to clips align.
break
答案 2 :(得分:0)
如果要连接不同属性的剪辑,如果连接点处的两个剪辑的峰不对齐,则可能会听到咔嗒声。
解决此问题的一种方法是在第一个信号结束时执行Fade-out
,然后在第二个信号开始时执行fade-in
。然后通过其余的连接过程继续这种模式。有关Fading
的详细信息,请Check here。
我会在Audacity之类的可视化工具中尝试连接,在您要加入的剪辑上尝试Fade-out
和fade-in
,并使用时间和设置来获得所需的结果。
接下来,我不确定pyAudio
是否有任何简单的实施方式fading
,但是,如果可以的话,您可能需要尝试pyDub。它提供了操作音频的简便方法。它同时包含Fade-in
和Fade-out
方法以及cross-fade
方法,它们基本上只需一步执行淡入和淡出。
您可以将pydub
安装为pip install pydub
以下是pyDub的示例代码:
from pydub import AudioSegment
from pydub.playback import play
#Load first audio segment
audio1 = AudioSegment.from_wav("SineWave_440Hz.wav")
#Load second audio segment
audio2 = AudioSegment.from_wav("SineWave_150Hz.wav")
# 1.5 second crossfade
combinedAudio= audio1.append(audio2, crossfade=1500)
#Play combined Audio
play(combinedAudio)
最后,如果您真的希望在专业级别上清除噪音/弹出,您可能需要查看PSOLA
(Pitch Synchronous Overlap and Add)。
这里可以将音频信号转换为frequency domain
,然后在块上执行PSOLA
以合并音频,并尽可能降低噪音。
那很长,但希望它有所帮助。