2016-11-29 17 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