Tempo e a variável now
Voltando aos exemplos dados nos posts anteriores, observe as últimas linhas de código. Tem lá algo parecido com isto:
2::second => now;
O que ela comanda é bem intuitivo, avance o tempo por dois segundos. O now
é uma variável especial que guarda o tempo atual de um processo ChucK e chuckar (uma tradução livre de to chuck que significa aplicar o operador ChucK) um número para ela significa incrementar o seu valor por este número. Este avanço no tempo é percebido pelo usuário porque todos os sons que estiverem sendo sintetizados e mandados para o dac
serão ouvidos. Sem avançar o tempo o processo terminaria e não emitiria nenhum som. Isto é um conceito importante: o fluxo da cadeia de processamento só avança se o tempo avançar.
Sincronia
Para rodar código concorrentemente na máquina virtual podemos fazer:
chuck codigo1.ck codigo2.ck ...
Todos os arquivos dados como parâmetro para o interpretador serão carregados e rodados simultaneamente. A vantagem disto é que todas as ocorrências da palavra now
, como observado anteriormente, serão tratadas como o tempo local de cada programa e quando os códigos rodarem concorrentemente estarão sincronizados de maneira perfeita (ou pelo menos boa o suficiente para ouvidos humanos).
Tome este código como exemplo :
//t1.ck SinOsc s => dac;
.5 => s.gain;
// Std.mtof retorna a frequência de uma nota em notação MIDI
while (true) {
Std.mtof(72) => s.freq;
2::second => now;
Std.mtof(80) => s.freq;
2::second => now;
Std.mtof(84) => s.freq;
2::second => now;
Std.mtof(80) => s.freq;
2::second => now;
}
//t2.ck
SawOsc s => dac;
.5 => s.gain;
while (true) {
Std.mtof(72) => s.freq;
500::ms => now;
Std.mtof(75) => s.freq;
500::ms => now;
Std.mtof(77) => s.freq;
500::ms => now;
Std.mtof(70) => s.freq;
500::ms => now;
}
Rodando os dois juntos e fazendo a matemática simples, podemos perceber que eles deveriam tocar perfeitamente bem sincronizados e a cada quatro notas da melodia (t2.ck) o acompanhamento seria mudado (t1.ck). A máquina virtual do ChucK nos garante isso. Para testar, salve os arquivos e:
$ chuck t1.ck t2.ck
Este artifício é bastante útil para dividir nossos programas em pequenos pedaços não acoplados e obter a sincronia “de graça”.
O tipo dur
Escrever as durações do nosso processamento explicitamente por todo o código acaba ficando chato e criando programas de difícil manutenção e customização. No entanto podemos usar variáveis para dar uma ajuda. Olhe:
SqrOsc s => dac;
.5::second => dur wait;
while (true) {
55 => s.freq;
2 *s.freq() => s.freq;
1::wait => now;
2 *s.freq() => s.freq;
1::wait => now;
2 *s.freq() => s.freq;
2::wait => now;
}
Note que nós podemos usar a variável wait
de forma relativa, e apenas dizer “espere 2 vezes este tempo aqui”. Mais genericamente podemos dizer, “espere o tempo wait
por n
vezes” e mais genericamente ainda, podemos dizer que ::
é um operador que recebe do lado esquerdo um número e do lado direito uma duração e resulta em uma nova uma duração. Parece complicado? Leia o exemplo anterior, rode ele. Escreva variações e tente entender. Pontos bônus: o valor à esquerda do ::
pode ser uma variável?
Usando estes artifícios nós paramos de nos preocupar com o valor da espera em si e fica mais fácil de mudar o script dinamicamente. Os nomes second
, minute
, day
e afins que já vimos nos exemplos são apenas valores pré-definidos no ChucK para facilitar nossa vida.
Referências
Conclusão
Depois dessa aula você já tem o básico para fazer alguns sons e ritmos legais com ChucK. Dê uma olhada nos posts anteriores e na documentação para continuar a partir daqui. Não se esqueça, é claro, de deixar comentários/críticas/sugestões.
No próximo post, funções e concorrência.