2016-11-29 19 views
1

我希望能夠重現下面代碼背後的想法,但是聽起來並不可怕。理想情況下,我想要一個使用學校孩子可以理解的代碼的解決方案(可以導入play_note函數,這樣他們就不必擔心它是如何工作的)。一個回答here建議連續音符之間的點擊是由於聲音的不完整週期,但我不知道如何解決改變持續時間。順利地「Audialize」在Python中與Turtle和PyAudio一起遞歸

任何人都可以幫忙嗎?它可以使一些調整工作,或者是某種方式有缺陷嗎?

import turtle 
import pyaudio 
import numpy as np 

def play_note(freq, dur): 

    p = pyaudio.PyAudio() 

    volume = 0.5  # range [0.0, 1.0] 
    fs = 44100  # sampling rate, Hz, must be integer 
    duration = dur # in seconds, may be float 
    f = freq  # sine frequency, Hz, may be float 

    # generate samples, note conversion to float32 array 
    samples = (np.sin(2*np.pi*np.arange(fs*duration)*f/fs)).astype(np.float32) 

    # for paFloat32 sample values must be in range [-1.0, 1.0] 
    stream = p.open(format=pyaudio.paFloat32, 
        channels=1, 
        rate=fs, 
        output=True) 

    # play. May repeat with different volume values (if done interactively) 
    stream.write(volume*samples) 

    stream.stop_stream() 
    stream.close() 

    p.terminate() 

def tree(branchLen,t): 
    if branchLen > 5: 
     freq = branchLen * 2 + 400 
     dur = branchLen/100.0 
     print freq, dur 
     play_note(freq, dur) 
     t.forward(branchLen) 
     t.right(20) 
     tree(branchLen-15,t) 
     t.left(40) 
     tree(branchLen-15,t) 
     t.right(20) 
     t.backward(branchLen) 

def main(): 
    t = turtle.Turtle() 
    t.speed(0) 
    myWin = turtle.Screen() 
    t.left(90) 
    t.up() 
    t.backward(100) 
    t.down() 
    t.color("green") 
    tree(75,t) 
    myWin.exitonclick() 

main() 
+1

當幅度突然變化時,音頻會發生點擊,如果您在一個週期中間切斷了正弦波,則會發生這種情況。解決辦法是添加一個信封,在你的情況下,這意味着淡出最後一個樣本,例如10-20ms的樣本。 – Linuxios

+0

謝謝@Linuxios。任何機會,你可以讓我知道如何修改我的代碼來實現? – Robin

+0

我發佈了一個答案,應該是一個合理的出發點,讓我知道是否有任何內容可以擴展或解釋更多。 – Linuxios

回答

0

我不確定你到底需要什麼。我試圖運行你的代碼。它遇到了一些問題。所以我修好了。然後彙集所有變量以便於訪問。修改變量的函數,以便可以從主函數中設置它們。導入所需的模塊。

我在遞歸之外提供了freqduration。因此它們是固定的。在原始代碼中,freq增加並且持續時間相對於樹長度減少。這是噪音的原因之一,因爲freqduration然後不是60秒的倍數,並因此產生導致噪音的差距。使用固定的freqduration,噪音最小。

由於創建新分支和遞歸啓動需要幾微秒的延遲,會有一些噪音。 這裏是EDITED代碼。

import pyaudio 
import turtle 
import numpy as np 

def play_note(freq, dur): 

    p = pyaudio.PyAudio() 

    volume = 0.5  # range [0.0, 1.0] 
    fs = 44100  # sampling rate, Hz, must be integer 
    duration = dur # in seconds, may be float 
    f = freq  # sine frequency, Hz, may be float 

    # generate samples, note conversion to float32 array 
    samples = (np.sin(2*np.pi*np.arange(fs*duration)*f/fs)).astype(np.float32) 

    # for paFloat32 sample values must be in range [-1.0, 1.0] 
    stream = p.open(format=pyaudio.paFloat32, 
        channels=1, 
        rate=fs, 
        output=True) 

    # play. May repeat with different volume values (if done interactively) 
    stream.write(volume*samples) 

    stream.stop_stream() 
    stream.close() 

    p.terminate() 

def tree(branchLen,t): 
    if branchLen > 5: 

     freq = branchLen * 2 + 420 
     dur = branchLen/60.0 
     print "branchLen={} , Freq = {}, Duration={}".format(branchLen, freq, dur) 
     t.forward(branchLen) 
     play_note(freq, dur) 
     t.right(20) 
     tree(branchLen-15,t) 
     play_note(freq, dur) 
     t.left(40) 
     play_note(freq, dur) 
     tree(branchLen-15,t) 
     play_note(freq, dur) 
     t.right(20) 
     play_note(freq, dur) 
     t.backward(branchLen) 
     play_note(freq, dur) 


def main(): 
    t = turtle.Turtle() 
    t.speed(turtle_speed) 
    myWin = turtle.Screen() 
    t.left(branch_left_turn) 
    t.up() 
    t.backward(branch_back_turn) 
    t.down() 
    t.color(tree_color) 
    tree(tree_length,t) 
    myWin.exitonclick() 

if __name__ == '__main__': 
    #Global Variables brought together for easy setting 
    turtle_speed = 0.5 #speed of turtle graphics <1 -> faster ; >1 -> slower 
    tree_length = 60 #keep within multiples of 60 for smoothness 
    branch_left_turn = 90 
    branch_back_turn = 100 
    tree_color = "green" #can experiment with "red" "blue" etc 
    #Call main function 
    main() 

輸出數據:

Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win32 
Type "copyright", "credits" or "license()" for more information. 
>>> ================================ RESTART ================================ 
>>> 
branchLen=60 , Freq = 540, Duration=1.0 
branchLen=45 , Freq = 510, Duration=0.75 
branchLen=30 , Freq = 480, Duration=0.5 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=30 , Freq = 480, Duration=0.5 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=45 , Freq = 510, Duration=0.75 
branchLen=30 , Freq = 480, Duration=0.5 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=30 , Freq = 480, Duration=0.5 
branchLen=15 , Freq = 450, Duration=0.25 
branchLen=15 , Freq = 450, Duration=0.25 
>>> 

輸出圖像時的振幅急劇變化,例如,當一個正弦波的一個的峯值附近打斷 enter image description here

+0

謝謝你的嘗試。我希望音高和/或持續時間隨分支長度而變化,以試驗聲學表示程序的不同方式。較短的分支應該有較短的持續時間。即使用你的解決方案,每個音符都有一個難看的中斷。有沒有辦法平滑每個筆記的結尾? – Robin

+0

我相信要真正擺脫點擊,你需要看看像傅里葉變換這樣的高級技術。但是,僅僅通過觀察,我發現點擊就是烏龜箭頭向後退縮。因爲我們只有'play_note'用於向前移動。我爲其他運動添加了'play_note',這似乎減少了點擊次數。你可能會爲不同的方向運動賦予不同的「頻率」來重組這種方式。我還修復了舊代碼,給出了不同的'freq',並修改了我的回覆。希望這有助於進一步。 –

0

次數發生在音頻其週期。解決方案是平滑淡出(可能是正弦波)。通常,當合成聲音時,這些音量變化稱爲聲音的包絡線

爲您的代碼的簡單(但可能不是很有效)信封看起來是這樣的:

def play_note(freq, dur): 

    p = pyaudio.PyAudio() 

    volume = 0.5  # range [0.0, 1.0] 
    fs = 44100  # sampling rate, Hz, must be integer 
    duration = dur # in seconds, may be float 
    f = freq  # sine frequency, Hz, may be float 

    # generate samples, note conversion to float32 array 
    samples = (np.sin(2*np.pi*np.arange(fs*duration)*f/fs)).astype(np.float32) 

    #Fade out the end 
    release_time = 0.020 #(seconds) 
    release_samples = np.ceil(release_time * fs) 
    fade_curve = np.linspace(1.0, 0.0, num=release_samples) 
    samples[-release_samples:] *= fade_curve 

這可以通過改變釋放時間或使用不同的褪色曲線進行調整。它也可以擴展爲在整個筆記上應用ADSR信封。

+0

我試過了,它不起作用 - 據我所知,聲音的振幅不會隨時間變化。我錯過了什麼嗎?我把'release_samples'變成了一個int來避免錯誤,並嘗試了不同的發佈時間。不知道我可以改變'淡化曲線',但點擊仍然存在。請給我更多的幫助嗎? – Robin