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.