Quando começares a escrever muito código, podes querer encontrar uma maneira de organizar e estruturar as coisas que forma elegante e de fácil compreensão. Funções são maneiras muito poderosas de conseguir isso. Elas dão-nos a habilidade de dar nome a uma serie de código. Vamos dar uma olhada.
define :foo do
play 50
sleep 1
play 55
sleep 2
end
Aqui definimos uma nova função chamada foo
. Fazemos isso com o nosso amigo bloco do/end e a palavra magica define
seguida pelo nome que queremos dar à nossa função. Não temos que a chamar foo
, podemos chamar-lhe o que quisermos como bar
, baz
ou idealmente algo com sentido para ti como secção_principal
ou lead_riff
.
Lembra-te de acrescentar dois pontos :
ao nome da tua função quando a definires.
Depois de definirmos a nossa função podemos a chamar escrevendo o seu nome:
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
Até podemos usar foo
dentro de um bloco de iteração ou em qualquer lado que tenhamos escrito play
ou sample
. Isto dá-nos uma excelente maneira de nos expressar e cria palavras significativas para usar nas nossas composições.
Até agora, cada vez que carregas no botão Run, o Sonic Pi começa completamente do zero. Ele não sabe nada excepto o que está no buffer. Não podes referir código noutro buffer ou noutra thread. No entanto as funções mudam isso. Quando defines a função, o Sonic Pi lembra-se dela. Vamos experimentar. Apaga todo o código na teu buffer e substitui por:
foo
Carrega no botão Run - ouve a tua função a tocar. Onde foi o código? Como é que o Sonic Pi sabe o que tocar? O Sonic Pi lembra-se da tua função - assim mesmo que apagues do buffer, ele lembra-se do que escreveste. Este comportamento apenas funciona com funções criadas usando o define
(e defonce
).
Podes estar interessado em saber que tal como tu podes passar valores mínimos e máximos a rrand
, tu também podes ensinar as tuas funções a aceitar argumentos. Vamos dar uma olhada:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Isto não é muito excitante, mas ilustra o ponto. Criamos a nossa versão de play
chamada my_player
que é parametrizada.
Os parâmetros devem vir a seguir ao do
do bloco define
, rodeados pela barra vertical |
e separados por virgulas ,
. Podes usar qualquer palavra para o nome dos parâmetros.
A magia acontece dentro do bloco define
. Podes querer usar nomes de parâmetros como se fossem valores reais. Neste exemplo estou a tocar a nota n
. Podes considerar que os parâmetros são uma espécie de promessa que quando o código corre serão substituídos por valores actuais. Fazes isso passando um parâmetro às função quando a chamas. Eu faço isso com my_player 80
para tocar a nota 80. Dentro da definição da função, n
é então substituído por 80, assim play n
fica play 80
. Quando a chamas novamente com my_play 90
, o n
é substituído por 90 e play n
fica play 90
.
Vamos ver um exemplo mais interessante:
define :chord_player do |root, repeats|
repeats.times do
play chord(root, :minor), release: 0.3
sleep 0.5
end
end
chord_player :e3, 2
sleep 0.5
chord_player :a3, 3
chord_player :g3, 4
sleep 0.5
chord_player :e3, 3
Onde usei repeat
como se fosse um número na linha repeat.times do
. Também usei root
como se fosse o nome de uma nota na minha chamada a play
.
Vê como podemos escrever algo muito expressivo e facil à leitura movendo muito da nossa lógica para uma função!