2016-04-05 16 views
2

如何刪除通過連接聲音音調剪輯連接在一起構建的音頻中的「彈出」和「單擊」聲音?如何從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幀平均,但也是這樣沒有效果。

我在做什麼錯?我該如何解決?

+0

你看波形嗎? –

+0

在這裏猜測..也許隔離一個簡單的和單一的嘟嘟聲。聲音就像放大器一樣打開和關閉。另外88000似乎是蜂鳴聲的高比特率。這是一個唧唧聲嗎?比特率編碼的電壓幅度,其中頻率帶寬用於保真度。 – wbg

回答

0

如果您連接不同屬性的剪輯,如果連接點處的兩個剪輯的峯值不匹配,您可能會聽到點擊聲音。

解決此問題的一種方法是在第一個信號結束時執行Fade-out,然後在第二個信號開始時執行fade-in。然後通過連接過程的其餘部分繼續這個模式。 Check here有關Fading的詳細信息。

我會嘗試在可視化工具(如Audacity)上嘗試使用連接,嘗試Fade-outfade-in您想要加入的剪輯並播放時間和設置以獲得期望的結果。

接下來,我不確定pyAudio有沒有簡單的執行fading的方法,但是,如果可以的話,你可能要試試pyDub。它提供了簡單的方法來操作音頻。它同時具有Fade-inFade-out方法以及cross-fade方法,該方法基本上一步執行淡入和淡出。

您可以安裝pydubpip 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) 

最後,如果你真的想在專業級的清除噪聲/噼噗聲,你可能想看看PSOLAPitch Synchronous Overlap and Add)。 在這裏,您可以將音頻信號轉換爲frequency domain,然後在塊上執行PSOLA以合併具有最小噪聲的音頻。

這很長,但希望它有幫助。

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

你爲自己寫的答案會做的伎倆,但不是真的做這種類型的事情的正確方法。

其中的一個問題是你的「小費」或正弦波峯值由反對1.並非所有的正弦頻率比較會打的值檢查或可能需要大量的循環這樣做。

從數學上來說,正弦的峯值是在罪爲K.

的所有整數值(PI/2 + 2piK)來計算正弦您使用的公式y = SIN(2PI給定頻率* x * f0/fs)其中x是樣本數量,f0是正弦頻率,fs是採樣率。

對於在48kHz的採樣率一個很好的數目像1kHz時,當x = 12,那麼:在像997Hz的頻率

sin(2pi * 12 * 1000/48000) = sin(2pi * 12/48) = sin(pi/2) = 1 

然而那麼真正的峯值落在樣品的一小部分樣品後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))) 
相關問題