2016-11-29 2 views
1

Я хотел бы иметь возможность воспроизвести идею, содержащуюся в коде ниже, но со звуком, который не является ужасно суровым для прослушивания. В идеале я хотел бы использовать решение, которое использует код, который могут понять школьные дети (функция play_note может быть импортирована, поэтому им не нужно беспокоиться о том, как это работает). Один ответ here предложил щелчок между последовательными нотами из-за неполных циклов звука, но я не знаю, как исправить это для изменения продолжительности.Плавно «Audialize» Рекурсия в Python с черепахой и 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-20 мс образцов. – Linuxios

+0

Спасибо @Linuxios. Любой шанс, который вы могли бы дать мне знать, как изменить мой код, чтобы достичь этого? – Robin

+0

Я отправил ответ, который должен быть разумной отправной точкой, сообщите мне, есть ли что-нибудь в этом я могу расширить или объяснить больше. – Linuxios

ответ

0

Я точно не знаю, что вам нужно. Я попытался запустить ваш код. Это столкнулось с некоторыми проблемами. Поэтому я исправил это. Затем собрал все переменные для удобства доступа. Модифицированные функции для переменных, чтобы они могли быть установлены из основной функции. импортированные необходимые модули.

Я предоставил freq и duration вне рекурсии. Следовательно, они фиксированы. В исходном коде freq увеличивается и длительность уменьшается по отношению к длине дерева. Это одна из причин шума, так как freq и duration не умножаются на 60 секунд и, следовательно, создают зазор, который приводит к шуму. С фиксированными freq и duration шум минимален.

Там будет некоторый шум из-за задержки микросекунд при создании новой ветки и рекурсии пнуть в. Вот 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» в разные движения направления, чтобы перестроить этот путь. Я также исправил старый код, чтобы дать оригинальную переменную «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, чтобы избежать ошибки, и пробовал разные времена выпуска. Не уверен, что я могу изменить re 'fade-curve', но клики все еще там. Вы можете больше помочь? – Robin