Aula de 31/05/2022 - Metaprogramação/Dynamic Patching


Objetivo: Ilustrar o conceito de metaprogramação em Pure Data, também chamada de Dynamic Patching, através de um teclado qwerty-musical com osciladores "efêmeros", que existem somente durante o acionamento das teclas. Isso permite um número arbitrário de vozes simultâneas sem a necessidade do gerenciamento inerente à alocação de vozes em um banco de osciladores de tamanho fixo. A implementação final está no patch sinteseaditiva.pdImportante: para o teclado funcionar como desenhado, o recurso de "teclas repetidas" do sistema operacional deve estar desabilitado (ou seja, quando se segura uma tecla do teclado, o caractere correspondente deve aparecer uma única vez).

Introdução: definição do timbre do oscilador (subpatch [pd timbre] do sinteseaditiva.pd).

Usamos um modelo de timbre por síntese aditiva, a partir de uma coleção de 32 harmônicos (tabela 'timbre'), que define o espectro de magnitude da forma de onda a ser usada em um oscilador [tabosc4~] (tabela 'onda', obtida através da ressíntese [tabplay~ timbre]->[cyclone/poltocar~]->[rifft~]->[/~ 64]. Para adequar o formato da tabela 'onda' ao [tabosc4~], que requer 2^N+3 amostras (3 amostras iniciais repetidas no fim), usamos um objeto [ofelia] (ao invés de um simples [tabwrite~]) para amazenar estes valores.

Primeiro passo: entender a representação interna dos objetos em Pd.

Começamos construindo um simples oscilador, meu_osc~.pd, que produz um fluxo periódico com forma de onda dada pela tabela 'onda' e de frequência determinada pelo objeto [sig~]. Esse patch não será usado no sintetizador, ele serve apenas para conhecermos o formato das mensagens usadas pelo Pd para criar outros objetos semelhantes.

O arquivo meu_osc~.pd contém o seguinte texto:

#N canvas 0 0 450 300 12;
#X obj 30 30 sig~ 400;
#X obj 30 60 tabosc4~ onda;
#X obj 30 180 *~;
#X obj 30 220 dac~;
#X obj 50 100 r vol;
#X msg 50 125 \$1 50;
#X obj 50 150 line~;
#X obj 100 100 r vol\$1;
#X connect 0 0 1 0;
#X connect 1 0 2 0;
#X connect 2 0 3 0;
#X connect 2 0 3 1;
#X connect 4 0 5 0;
#X connect 5 0 6 0;
#X connect 6 0 2 1;
#X connect 7 0 5 0;

A primeira linha do arquivo .pd define o canvas ou espaço de programação; seus parâmetros são pos_x, pos_y, largura e altura da janela, e tamanho da fonte. Cada linha começada com #X define um objeto, mensagem ou conexão. Objetos e mensagens têm como parâmetros pos_x pos_y e conteúdo. As conexões têm como parâmetros obj_1 out_1 obj_2 in_2, onde os objetos e os inlets são indexados a partir de 0, e os objetos são indexados na ordem do arquivo (ordem em que foram criados).

Essa sintaxe também pode ser usada para criar objetos e conexões, trocando-se o #X pelo identificador pd-patch.pd ou pd-subpatch (o sufixo está associado ao 'canvas' do patch ou subpatch, e a extensão .pd só se aplica a 'arquivos').

É importante destacar que é fácil criar objetos e conexões, mas é bem complicado mudá-los, assim como é complicado criar estruturas grandes ou repetidas em um subpatch (por causa da indexação dos objetos). Assim, é sempre mais fácil criar subpatches "descartáveis" para aplicar dynamic patching neles.

Segundo passo: vincular teclas à criação de subpatches.

Aqui usamos os objetos [key] e [moses] para filtrar os valores correspondentes às teclas 'a'...'z'. Fizemos um mapeamento simples entre 'a'=C4, 'b'=C#4', ... através de um [- 37]→[mtof]. Cada tecla apertada configura as mensagens para criar dinamicamente um subpatch [pd osc$1] onde $1 é o número da tecla. Após a criação dos objetos, mensagens e conexões, um comando 'pd-osc$1 vis 0' torna o subpatch invisível (minimizado), e a mensagem 'vol$1 $3' faz o instrumento começar a soar.

Terceiro passo: vincular soltura de teclas à destruição dos subpatches

O objeto [keyup]  detecta o código da tecla levantada, que aciona as mensagens para abaixar o volume do sintetizador correspondente ('pd-osc$1 vol$1 0') e apagar todos os objetos do subpatch ('pd-osc$1 clear'). Note que aqui está sendo usado um receptor de volume independente para cada oscilador, assim podemos soltar uma nota e deixar as outras soando.

Quarto passo: controle de volume em função do número de vozes.

Uma variável global 'polifonia' é usada como contador de osciladores ativos, sendo incrementada a cada keydown e decrementada a cada keyup. O valor polifonia é usado para determinar um volume adequado (=1/polifonia) para todos os osciladores ativos (através dos receptores idênticos [r vol]). A máxima polifonia permitida depende do teclado: como teclados qwerty não foram desenhados para esse uso, em geral o máximo de independência que as teclas possuem depende do hardware. Meu teclado por exemplo consegue rastrear 6 teclas pressionadas simultâneas (o que é usado para Braille e também em algumas línguas), porém não quaisquer 6 teclas (as combinações admissíveis provavelmente se relacionam com os usos textuais).

Quinto passo: evitar recriação de subsubpatches já existentes.

Para isso usamos um vetor binário osc_existe onde cada entrada osc_existe[$1] diz se o subsubpatch osc$1 já existe, assim apenas as mensagens de criação dos objetos e conexões é enviada, evitando a existência de múltiplos subpatches com o mesmo nome (que responderiam simultaneamente às mensagens de criação e destruição de objetos).

Links adicionais:

Documentação integrada:
https://forum.pdpatchrepo.info/uploads/files/1497602807047-pd-msg.zip

Recomenda-se instalar esses arquivos em doc/7.Stuff/pd-msg (no diretório da documentação do puredata, por exemplo /usr/lib/puredata/doc/7.stuff/pd-msg/)

Tutorial de Fede Camara Halac:
https://github.com/fdch/pd-messages

Vá até o diretório bin e abra o arquivo main.pd. Se não aparecer nada, o tutorial deve estar invisível: crie um novo patch (Ctrl-N) e acione uma mensagem |;pd-main.pd vis 1< para o tutorial aparecer...

Descrição do formato de arquivo .pd:
https://puredata.info/docs/developer/PdFileFormat

Lista de mensagens internas:
https://puredata.info/community/pdwiki/PdInternalMessages

Dica de como mandar mensagens para uma instância específica de uma abstração:
https://puredata.info/docs/tutorials/TipsAndTricks#instance-specific-dynamic-patching

Exemplos adicionais:
https://forum.pdpatchrepo.info/uploads/files/1497645800908-stuff.zip



Última atualização: terça-feira, 31 mai. 2022, 15:37