【问题标题】:How to remove pops from concatented sound data in PyAudio如何从 PyAudio 中的连接声音数据中删除爆音
【发布时间】:2016-07-26 03:09:03
【问题描述】:

如何去除由音调声音片段连接在一起构成的音频中的“爆裂声”和“咔嗒声”?

我有这个 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 帧进行平均,但这也有没有效果。

我做错了什么?我该如何解决这个问题?

【问题讨论】:

  • 你看过波形了吗?
  • 在这里猜测.. 可能会分离出一个简单而单一的哔哔声。声音就像放大器正在打开和关闭。此外,88000 似乎是哔哔声的高比特率。这是唧唧喳喳吗?比特率编码电压幅度,其中频率带宽用于保真度。

标签: python audio pyaudio


【解决方案1】:

如果您正在连接不同属性的剪辑,如果两个剪辑在连接点处的峰值未对齐,您可能会听到咔嗒声。

解决此问题的一种方法是在第一个信号的末尾执行Fade-out,然后在第二个信号的开头执行fade-in。然后通过其余的串联过程继续此模式。 Check here 了解Fading 的详细信息。

我会在 Audacity 等可视化工具中尝试连接,在您想要加入的剪辑上尝试 Fade-outfade-in 并使用时间和设置进行播放以获得所需的结果。

接下来,我不确定pyAudio 是否有任何简单的实现方式fading,但是,如果可以,您可能想尝试pyDub。它提供了操作音频的简单方法。它既有Fade-inFade-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,以将音频合并到尽可能少的噪音。

这很长,但希望对您有所帮助。

【讨论】:

    【解决方案2】:

    我最初怀疑各个波形没有对齐是正确的,我通过在 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
    

    【讨论】:

    • 太棒了!我一直在寻找完全相同的解决方案这么久!非常感谢。
    【解决方案3】:

    您为自己编写的答案可以解决问题,但实际上并不是做这类事情的正确方法。

    其中一个问题是您通过与 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)))
    

    【讨论】:

      猜你喜欢
      • 2020-09-14
      • 1970-01-01
      • 1970-01-01
      • 2019-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多