Fizeste uma killer bassline e um fat beat. Como os tocar ao mesmo tempo? Uma solução é interligar os dois manualmente - tocar algum baixo, depois um pouco de bateria, e mais algum baixo… No entanto, o tempo torna-se difícil de pensar, especialmente se começares a entrelaçar mais elementos.
E se o Sonic Pi pode-se entrelaçar as coisas por ti automaticamente? Bem, ele pode, e fazes isso com algo chamado thread.
Para manter este exemplo simples, terás que imaginar que isto é um fat beat e uma killer bassline:
loop do
sample :drum_heavy_kick
sleep 1
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Como discutimos previamente, loops são buracos negros para o programa. Uma vez neles nunca poderás sair até premires stop. Então como tocar ambos os loops ao mesmo tempo? Teremos que dizer ao Sonic Pi que queremos começar algo ao mesmo tempo que o resto do código. É ai que os threads vêm ao nosso auxilio.
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
Envolvendo o primeiro loop num in_thread
do bloco do/end dizemos aos Sonic Pi para correr o conteúdo do bloco exactamente ao mesmo tempo que a declaração seguinte ao bloco (que até é o segundo loop). Experimenta e ouvirás a bateria e o baixo entrelaçados.
Agora e se queremos adicionar um synth por cima. Algo como:
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
loop do
use_synth :zawa
play 52, release: 2.5, phase: 2, amp: 0.5
sleep 2
end
Teremos o mesmo problema que anteriormente. O primeiro loop é tocado ao mesmo tempo que o segundo devido ao in_thread
. No entanto, o terceiro loop nunca é alcançado. Assim necessitamos de outro thread:
in_thread do
loop do
sample :drum_heavy_kick
sleep 1
end
end
in_thread do
loop do
use_synth :fm
play 40, release: 0.2
sleep 0.5
end
end
loop do
use_synth :zawa
play 52, release: 2.5, phase: 2, amp: 0.5
sleep 2
end
O que te pode supreender é que quando carregas no botão Run, está a criar um novo thread para o código correr. É por isso que carregando múltiplas vezes irá adicionar camadas de som umas sobre as outras. As próprias corridas são threads, elas irão entrelaçar automaticamente os sons por ti.
Quando aprenderes a conquistar o Sonic Pi, aprenderas que os threads são o bloco de construção mais importante da tua musica. Um dos importantes trabalhos que fazem é isolar a noção de settings actuais de outros threads.
O que significa isso? Bem, quando trocas de synth usando o use_synth
apenas estás a mudar de sintetizador na thread actual - nenhum outro thread terá a mudança de sintetizador. Vamos ver isto em pratica:
play 50
sleep 1
in_thread do
use_synth :tb303
play 50
end
sleep 1
play 50
Repara como o som do meio foi diferente dos restantes? A declaração use_synth
apenas afectou o thread onde estava e não o thread principal exterior que corre.
Quando crias um novo thread com in_thread
, o novo thread irá automaticamente herdar todas as settings actuais do thread corrente. Vamos ver isso:
use_synth :tb303
play 50
sleep 1
in_thread do
play 55
end
Repara como a segunda nota foi tocada com o sintetizador :tb303
apesar de ter sido tocada de uma thread separada? Todas as settings modificadas com as várias funções use_*
irão se comportar da mesma maneira.
Quando os threads são criados eles herdam todas as settings dos seus pais mas não passam para eles qualquer mudança que tenham.
Finalmente, podemos dar nomes aos nossos threads:
in_thread(name: :bass) do
loop do
use_synth :prophet
play chord(:e2, :m7).choose, release: 0.6
sleep 0.5
end
end
in_thread(name: :drums) do
loop do
sample :elec_snare
sleep 1
end
end
Vê o painel de registro quando corres este código. Vê como o regsitro reporta o nome do thread na mensagem?
[Run 36, Time 4.0, Thread :bass]
|- synth :prophet, {release: 0.6, note: 47}
Uma ultima coisa sobre nomear threads é que apenas um thread de um determinado nome pode estar a correr ao mesmo tempo. Vamos explorar isto. Considera o seguinte código:
in_thread do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Vamos lá e cola isso num buffer e carrega o butão Run. Carrega-o uma série de vezes. Ouve a cacofonia de vários amen break looping fora de tempo entre eles. Ok, podes carregar o Stop agora.
Este é o comportamento que vemos sempre - se carregares o botão Run, as camadas de som sobrepõem-se às do som existente. Assim se tiveres um loop e carregares o botão Run 3 vezes, terás 3 camadas de loops a correr simultaneamente.
No entanto, com threads com nome é diferente:
in_thread(name: :amen) do
loop do
sample :loop_amen
sleep sample_duration :loop_amen
end
end
Experimenta carregar o botão Run 3 vezes com este código. Apenas irás ouvir um amen break loop. Verás também isto no registro:
==> Skipping thread creation: thread with name :amen already exists.
Sonic Pi diz que um thread com o nome :amen
já está a tocar, e assim não irá criar outro.
Este comportamento pode não ser imediatamente útil para ti por enquanto - mas será muito quando começarmos com o live code…