2017-07-03 70 views
1

我的目標是使用Python在計算機遊戲環境中播放符合以下要求的聲音。播放不同變異音調的聲音

  1. 取一些輸入WAV文件並隨機將音高變化爲原來的+/- 50%。改變採樣率似乎是用PyDub做這件事的一個簡單方法。

  2. 播放聲音。

  3. 能夠快速調用此功能,以便在實際播放中長時間和短時間的聲音重疊。

我花了24個工作小時尋找一種方法來滿足所有這些要求。我之前在Visual Basic中完成了這個工作,我很驚訝它在Python中的困難程度。

以下是我目前所知:

  1. PyGame.Mixer可以玩重疊同時發出,但它必須相同的採樣率播放所有。似乎沒有辦法改變音調。

  2. PyDub可以通過更改採樣率來改變音高,但不能用重放聲音播放重疊聲音。而且,我必須將輸出聲音寫入文件,然後立即將其加載回去,這會讓人覺得很浪費。

  3. WinSound可以播放PyDub的變化採樣率聲音,但不能與併發播放,甚至不能使用線程。

  4. Playsound軟件包不適用於python 3.6。

  5. 如果我使用線程技術,PyAudio可以同時播放PyDub的變化採樣率聲音,但是,如果使用線程的時間不止兩次,並且會導致可怕的內存問題,並很快導致Python崩潰。

我的問題:我怎樣才能達到我的3個目標而不會造成問題?

這裏是最好的結果是我到目前爲止(這是PyAudio版本,這會導致如果測試超過一次以上的崩潰兩次):

from pydub import AudioSegment 
from random import random, seed 
from time import sleep 
import os 
import threading 
import pyaudio 
import wave 

def PlayAsyncWithRandPitch(WavPath): 
    MyBaseFilename = os.path.basename(WavPath) 
    sound = AudioSegment.from_file(WavPath, format="wav") 
    seed() 
    octaves = ((random()-0.50)) 
    print("random octave factor for this sound is: "+str(octaves)) 
    print("current sound frame rate:"+str(sound.frame_rate)) 
    new_sample_rate = int(sound.frame_rate * (2.0 ** octaves)) 
    print("new sound frame rate:"+str(new_sample_rate)) 
    newpitchsound = sound._spawn(sound.raw_data, overrides={'frame_rate': new_sample_rate}) 
    MyTotalNewPath = os.getcwd()+"\\Soundfiles\\Temp\\Mod_"+MyBaseFilename 
    newpitchsound.export(MyTotalNewPath, format="wav") 
    SoundThread = threading.Thread(target=PAPlay, args=(MyTotalNewPath,)) 
    SoundThread.start() 
#======================================================================================= 


#This function is just code for playing a sound in PyAudio 
def PAPlay(filename): 
    CHUNK = 1024 
    wf = wave.open(filename, '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() 
    return 


if __name__ == "__main__": 
    #Example sounds to test if more than one can play at once 
    PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\RifleMiss.WAV') 
    sleep(0.2) 
    PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\splash.wav') 
    sleep(0.2) 
    PlayAsyncWithRandPitch(os.getcwd()+'\\Soundfiles\\sparkhit1.WAV') 
    sleep(5.0) 

預先感謝您對你的幫助!

+0

將音頻剪輯預處理爲各種採樣率排列,以便在運行時直接渲染它們 –

+0

我不知道該怎麼做。但謝謝你的回覆,我會考慮它:) – Zaorish9

回答

1

感謝另一小時的谷歌搜索,我找到了一個關於PyDub的晦澀難懂的筆記,可以解決它。有一種方法可以實際改變採樣率,但「實際上」並不改變採樣率。這就是所謂的花栗鼠方法。

https://github.com/jiaaro/pydub/issues/157#issuecomment-252366466

我真的不假裝瞭解這裏的細微差別,但似乎這個概念是「拿一聲響,設置的採樣率,以一些修改後的值,然後轉換採樣率回到傳統的44,100 HZ值。「

他們舉這個例子,其工作得非常好:

from pydub import AudioSegment 
sound = AudioSegment.from_file('./test/data/test1.mp3') 
# shift the pitch up by half an octave (speed will increase proportionally) 
octaves = 0.5 
new_sample_rate = int(sound.frame_rate * (2.0 ** octaves)) 
# keep the same samples but tell the computer they ought to be played at the 
# new, higher sample rate. This file sounds like a chipmunk but has a weird sample rate. 
chipmunk_sound = sound._spawn(sound.raw_data, overrides={'frame_rate': new_sample_rate}) 
# now we just convert it to a common sample rate (44.1k - standard audio CD) to 
# make sure it works in regular audio players. Other than potentially losing audio quality (if 
# you set it too low - 44.1k is plenty) this should now noticeable change how the audio sounds. 
chipmunk_ready_to_export = chipmunk_sound.set_frame_rate(44100) 

這沒有多大意義的我,但它確實工作:)希望這可以幫助別人那裏

0

這種方法。似乎有點可疑我解釋了我如何使用Java在以下link上使用Java變量:

主要想法是使用線性插值從樣本之間獲取值,並通過樣本數據除1之外的比率乘1.如果你要去150%並且需要樣本0,那麼樣本1.5(1和2之間的一半),插值。

+0

感謝您的回覆!事實是,我不知道如何使用Java或C++。下面的方法在Python中以很好的速度工作,所以我會使用它,直到聽到更好的聲音。 – Zaorish9

+0

算法的要點不依賴於代碼。 –