python - 如何从 PyAudio 中的连续声音数据中删除爆音

标签 python audio pyaudio

如何去除通过将声音音调片段连接在一起而构建的音频中的“爆裂”和“咔嗒”声?

我有这个用于生成一系列音调的 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 是采样率。

对于一个不错的数字,例如 48kHz 采样率下的 1kHz,当 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)))

关于python - 如何从 PyAudio 中的连续声音数据中删除爆音,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36438850/

相关文章:

python - 使用 -= 时字典值未按预期更新

python - Beautiful Stone Soup 对 XML 标签名称区分大小写

algorithm - 检测环境声音中是否存在音乐

Flutter audioplayers plugin - 如何从此链接播放在线广播?

python : how to change audio volume?

python - 使用pyaudio在linux/ubuntu上运行Flask应用程序时出现ALSA错误

python - 递归错误 : maximum recursion depth exceeded while calling a Python object when using pickle. 加载()

python - 如何解决类型错误: cannot serialize float Python Elementtree

python - 使用pyaudio产生振幅增加的音调

python - 如何在PyAudio中获取 channel 数据