2013-07-17 132 views
3

我的目標是通過筆記本電腦麥克風錄製我的聲音,並同時在python中添加效果。我需要的是類似於連接吉他或麥克風的音樂效果踏板,它會添加混響或回聲或失真等。同步錄製來自麥克風的音頻並在python中播放效果

我正在使用'pyaudio'和'wave'來錄製和播放音頻。使用'scikits.audiolab'將音頻導入爲數組,並且可以使用諸如反轉,剪輯,平鋪等功能來編輯該數組。音頻陣列的這種操作使我可以將「添加」效果添加到原始音頻。

我有一個問題,這不是一個真正的問題,它只是不是我想要的效果。假設我記錄單詞「你好」。我有我的記錄功能設置爲記錄3秒。然後我拿起這個音頻陣列並平鋪一次。現在,當我打這個回來,它會打兩次打招呼,延遲效果。但是,在這兩個hello之間有一段時間間隔'空閒空間',這是因爲在我完成打招呼後音頻仍在錄製。因此,當它重複時,單詞之間的空白空間太多。我想消除這個空的空間,以便播放更快地打招呼你好。

我的老師推薦穿線。他說我應該記錄下來,同時抓住前500個樣本,說出一個數字。他建議拍攝這500個樣本,並在錄製時播放它們。我不太清楚如何實現這一點。

我的問題是,如何同時記錄,採取前500個樣本,並創建一個新的陣列與「效果」添加到原始錄音。

import scikits.audiolab as audiolab 
import pyaudio 
import wave 

def recordAudio(): 

    CHUNK = 1024 
    FORMAT = pyaudio.paInt16 
    CHANNELS = 1 
    RATE = 44100 
    RECORD_SECONDS = 3 
    WAVE_OUTPUT_FILENAME = "audioOriginal.wav" 

    p = pyaudio.PyAudio() 

    stream = p.open(format=FORMAT, 
       channels=CHANNELS, 
       rate=RATE, 
       input=True, 
       frames_per_buffer=CHUNK) 

    print("* recording:") 

    frames = [] 

    for i in range(0, int(RATE/CHUNK * RECORD_SECONDS)): 
     data = stream.read(CHUNK) 
     frames.append(data) 

    print("* Finished recording.") 

    stream.stop_stream() 
    stream.close() 
    p.terminate() 

    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb') 
    wf.setnchannels(CHANNELS) 
    wf.setsampwidth(p.get_sample_size(FORMAT)) 
    wf.setframerate(RATE) 
    wf.writeframes(b''.join(frames)) 
    wf.close() 

    # Duplicate audio and save as Actual 
    frames, fs, encoder = audiolab.wavread('audioOriginal.wav') 
    audiolab.wavwrite(frames,'audioActual.wav',fs) 

def playAudio(): 

    import pyaudio 
    import wave 

    CHUNK = 1024 

    wf = wave.open('audioActual.wav', 'rb') 

    p = pyaudio.PyAudio() 

    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), 
     channels=wf.getnchannels(), 
     rate=wf.getframerate(), 
     output=True) 

    data = wf.readframes(CHUNK) 

    while data != '': 
     stream.write(data) 
     data = wf.readframes(CHUNK) 

    stream.stop_stream() 
    stream.close() 
    p.terminate() 

def reverseAudio(): 

    frames, fs, encoder = audiolab.wavread('audioActual.wav') 

    audiolab.wavwrite(frames[::-1],'audioActual.wav',44100) 

def revert(): 
    frames, fs, encoder = audiolab.wavread('audioOriginal.wav') 
    audiolab.wavwrite(frames,'audioActual.wav',fs) 

def errorSelection(): 
    print("\nERROR.") # no option in menu 
def showMenu(): 
    print(""" 
    1. Record audio 
    2. Play audio 
    3. Reverse audio 
    4. Add delay 
    5. Revert to original audio 

    T to end program. 
    """) 

# Menu 
def main(): 
    selecciones = {"1": recordAudio, "2": playAudio, "3": reverseAudio, "5": revert} 
    while True: 
     showMenu() 
     seleccion = raw_input(u'What do you want to do? ') 
     if "t" == seleccion: 
      return 
     elif "T" == seleccion: 
      return 
     toDo = selecciones.get(seleccion, errorSelection) 
     toDo() 

if __name__ == "__main__": 
    main() 
+0

這個問題是關於如何在同一時間開始播放和錄製主題:http://stackoverflow.com/q/13422186/125507雖然有可能直接做到這一點使用[回調模式] (http://people.csail.mit.edu/hubert/pyaudio/docs/#example-callback-mode-audio-io)? – endolith

回答

3

首先,您提出的問題(能夠平鋪音頻樣本,同時自動移除它們之間的靜音空間)並不是可以通過線程解決的問題。您需要分析錄製的聲音以確定存在或不存在靜音的位置,或者只需讓用戶指定錄製結束的時間即可。您可以用簡單的循環完成後者:

  1. 打開音頻硬件並開始錄製。
  2. 創建一個空表來存儲音頻
  3. 請求音頻數據的小塊的塊,追加到列表
  4. 檢查用戶請求的記錄結束。如果不是,則循環回到3.
  5. 完成後,將塊組裝成單個陣列進行回放。

在這個簡單的例子中,使用線程沒有任何好處。

建議的方法,記錄,同時播放,似乎是一個解決不同問題的方法,一個更復雜。在這種情況下,有兩個主要困難:

  1. 並非所有的消費者聲卡都記錄能夠並同時播放。尋找聲稱「全雙工」而不是「半雙工」的卡。
  2. 對着麥克風說話,並短暫地聽到自己的聲音是非常分心分心。爲了使其正常工作,錄製的音頻必須在不到約20毫秒的時間內處理併發送回聲卡。在44。1 kHz,這意味着您應該在每個循環週期內讀取少於880幀的幀,並且如果處理無法跟上,則輸出中會出現間隙。除非你有專門的軟件來幫助,否則這是一個令人驚訝的難題。如果你真的想這樣,你可以看看Jack(http://jackaudio.org/),它在大多數平臺上提供低延遲音頻訪問,並且還有一個簡單的Python庫(http://sourceforge.net/projects/py-jack/)。線程在這種類型的程序中可能沒有幫助。
+0

謝謝,@Luke。這給了我如何解決我的目標的新想法。 –

0

爲了擴大對盧克的回答是:

for i in range(0, int(RATE/CHUNK * RECORD_SECONDS)): 
    data = stream.read(CHUNK) 
    frames.append(data) 

在你的代碼是你承諾記錄一定的時間。如果你寫了一個函數「isSilent」,可以判斷一個塊是無聲的,你的代碼可能會更改爲:

while len(frames) <= 0 or not isSilent(frames[-1]): 
    data = stream.read(CHUNK) 
    frames.append(data) 

如果「isSilent」是要用心去寫,或者如果它是計算方式慢,你可以等待用戶輸入。 KeyboardInterrupt是用這種方法打第一黑客:

try: 
    while true: 
     data = stream.read(CHUNK) 
     frames.append(data) 
except KeyboardInterrupt: 
    pass 

這是一個黑客,而不是尋找在生產用戶輸入正確的方式,但它會讓你開始這個實驗。你會想找到或做一個stopButtonHasBeenPressed功能。

while not stopButtonHasBeenPressed(): 
    data = stream.read(CHUNK) 
    frames.append(data) 
相關問題