Connexions

You are here: Home » Content » Programmazione Visuale
Content Actions

Programmazione Visuale

Module by: Davide Rocchesso

Summary: Si presenta una breve introduzione alla programmazione visuale per mezzo del linguaggio Pure Data (PD)

Programmazione Visuale

Un linguaggio di programmazione visuale permette di specificare programmi mediante manipolazione di elementi grafici. I linguaggi più usati in ambito artistico e di produzione multimediale (tra questi Pure Data, MAX/MSP, vvvv, e Quartz Composer sono basati sul paradigma di calcolo dataflow, nel quale i dati (piuttosto che le sequenze di operazioni) sono al centro dell'attenzione. Le operazioni sono "scatole nere" che elaborano l'input, non appena questo è disponibile, producendo un output che può essere dato in input ad altre scatole di elaborazione. Un programma dataflow assomiglia ad una rete di catene di montaggio, esplicita in maniera intrinseca il parallelismo, e non nasconde uno stato. Oltre che prestarsi alla parallelizzazione automatica, i programmi dataflow sono in un certo senso "auto-documentati" e si prestano al debugging mediante sonde.
Pure Data è un linguaggio di programmazione visuale inizialmente concepito da Miller Puckette per la computer music in tempo reale, poi esteso ad opera di una comunità di programmatori ed oggi diffuso anche tra artisti visuali, performer e interaction designer. Pure Data è un software libero, ed ha uno stretto grado di parentela con MAX/MSP. Essendo nato per elaborare e controllare segnale audio, Pure Data supporta la comunicazione dei dati da un operatore all'altro a due rate: il sample rate che per default è di 44100 campioni al secondo, ed il control rate ad un sessantaquattresimo del sample rate. Più precisamente, gli operatori che elaborano segnale audio (caratterizzati dal suffisso ~) prendono campioni di ingresso in maniera sincrona ad audio rate e allo stesso rate producono segnali di uscita. Tutti gli altri operatori elaborano dati non appena questi sono disponibili (dataflow) ad un rate massimo di circa 690 Hz (per default, un sessantaquattresimo del sample rate). Tutti i calcoli sono effettuati su numeri floating-point a 32 bit. La documentazione di Pure Data è disponibile dalla voce Help del suo menu, oppure è consultabile online. Questo modulo è parzialmente basato su tale documentazione e sulla lezione di Gary Scavone.
I programmi Pure Data sono chiamati patch e si presentano come grafi di elaborazione di flussi di dati. Ogni programma ha una finestra principale e può avere un numero arbitrario di sottofinestre. Il software Pure Data ha una interfaccia modale, in quanto ci sono due modi distinti di operazione: run mode e edit mode. Si passa dall'uno all'altro con command-E e il feedback sul modo corrente è dato dalla forma del puntatore del mouse. I blocchi che si possono connettere in forma di grafo di dataflow sono di tipo: object, message, atom (number o symbol), GUI, e comment.
hello.jpg
Figura 1
In Figura 1 è illustrato un semplicissimo patch che stampa sulla consolle la scritta x1: Hello World. Esso contiene due oggetti message (lato destro concavo) ed un oggetto object (rettangolo con comando print x1). L'oggetto object rapresenta un comando con eventuali parametri di default. Il quadrato con cerchio all'interno è uno speciale messaggio senza contenuto, chiamato bang, che invia un evento ai blocchi ad esso collegati.

Idiosincrasie grafiche

depth-first.png
Figura 2
Ogniqualvolta un messaggio è inviato ad un oggetto, questi può diventare mittente a sua volta verso altri oggetti. In altri termini, si configura un albero di ricezioni di messaggi. Secondo il manuale di Pure Data l'ordine di esecuzione utilizzato è depth-first, cioè ogni ramo è esplorato fino alle foglie prima di procedere con gli altri rami. L'ordine di esecuzione tra nodi allo stesso livello dell'albero sarebbe invece da considerarsi indeterminato. In realtà, provando ad eseguire l'esempio di Figura 2 e ridisponendo le connessioni tra i nodi dell'albero con ordini diversi ci si accorge che non si può nemmeno contare sull'ordine depth-first. In Max/MSP la situazione è ancora peggiore perché muovendo messaggi e bang in posti diversi della finestra, a parità di configurazione topologica, si ottengono sequenze di attivazione diverse. L'elaborazione di tipo dataflow può dare luogo a loop non computabili, quale è quello riportato in Figura 3. Pure Data, in questo caso, riporta un messaggio di "stack overflow" sulla consolle. Per rendere il loop computabile è sufficiente inserire un elemento di disaccoppiamento quale è un pipe (si veda 14). Per quanto sia piccolo il parametro ritardo di questo pipe (al limite anche nullo), esso terrà memoria del dato in ingresso per consumarlo al ciclo successivo di calcolo.
noncomploop.png
Figura 3
Nella maggior parte degli oggetti, la inlet di sinistra è hot, nel senso che messaggi che ad essa arrivano scatenano messaggi in output. E' spesso desiderabile inviare messaggi a due o più inlet di un oggetto, ma il risultato dell'operazione può dipendere dall'ordine con cui si sono fatte le connessioni. Ciò mina l'efficacia semantica del programma visuale, in quanto potremmo avere due patch apparentemente identiche che però si comportano diversamente perché costruite con un ordine diverso. Ad esempio, si provi a fare la somma di un numero con sè stesso secondo la Figura 4.
plus.png
Figura 4
La sequenza di creazione delle connessioni determina la correttezza o meno del risultato. Per imporre chiarezza semantica al patch è opportuno inserire un oggetto trigger che forza l'ordine di attivazione delle outlet da destra a sinistra, come indicato in Figura 5.
plust.png
Figura 5

Elementi di GUI

metro.jpg
Figura 6
In Figura 6 è illustrata l'utilizzazione di un generatore periodico di bang. Si noti il parametro di metro che stabilisce l'intervallo (di 500 millisecondi) tra due bang. Il quadrato in alto è un elemento di GUI detto toggle switch, in pratica un interruttore.
slider.jpg
Figura 7
Un altro elemento di GUI, il vslider, è illustrato in Figura 7. In questo patch è anche visibile un oggetto number, il quale invia un numero in uscita (outlet) ogni volta che ne viene cambiato il valore, per dragging con il mouse o per immissione di numeri dal suo imput.

Array

table.jpg
Figura 8
In Pure Data, un array è un contenitore di numeri ed un elemento di visualizzazione al tempo stesso. Infatti, l'oggetto table istanzia l'array e crea un subpatch che ne visualizza la rappresentazione grafica. Questa rappresentazione è anche uno strumento di input, in quanto i valori dell'array possono essere "disegnati" con il mouse. Per leggere e scrivere i singoli elementi dell'array si usano gli oggetti tabread e tabwrite. L'utilizzazione di tabread per scandire un array è illustrata in Figura 8.

Selezioni, routing, multiplexing

comparisons.jpg
Figura 9
In Figura 9 sono illustrati blocchi di confronto e selezione. In particolare, il blocco select attiva l'uscita di sinistra se l'ingresso (inlet) è uguale al suo parametro, altrimenti attiva l'uscita di destra.
routing.jpg
Figura 10
In Figura 10 sono illustrati tre elementi di routing. Il blocco spigot trasmette messaggi dall'inlet di sinistra all'outlet in dipendenza dallo stato dell'ingresso (di controllo) di destra. In sostanza si tratta di un gate con controllo di apertura e chiusura. Invece, moses consente di smistare alle uscite sinistra e destra i valori di ingresso che sono rispettivamente minori o maggiori (o uguali) del valore di controllo passato dalla inlet di destra. Infine, route smista dei messaggi ricevuti in ingresso sulla base del loro primo elemento, mettendolo a confronto con gli argomenti. Un multiplexer si può ottenere combinando la route con la pack.
Problem 1
Realizzare un multiplexer e un demultiplexer utilizzando gli oggetti pack, route e spigot.
[ Click for Solution 1 ]
Solution 1
[ Hide Solution 1 ]

Ritardi, code e gestione del tempo

delays.jpg
Figura 11
In Figura 11 è illustrata l'utilizzazione di ritardi e code. La delay ritarda un bang in ingresso di un numero di millisecondi pari al suo argomento. Se si vuole ritardare un flusso di dati bisogna usare la pipe, la quale è realizzata come coda a buffer circolare.
timing.jpg
Figura 12
In Figura 12 si vedono alcuni oggetti che consentono di scandire, misurare, e avanzare il tempo. Rispettivamente, ciò si ottiene con metro, timer, e line. Quest'ultimo oggetto genera una rampa lineare, da un valore iniziale a uno finale, in un certo tempo.
Problem 2
Nel patch di Figura 8, si provi a rimuovere e ripristinare i collegamenti che arrivano alla inlet di destra dell'oggetto float, e si verifichi che il reset dell'indice una volta raggiunto il limite di 128 può non avere luogo. Perché? Come si può introdurre in maniera deterministica il reset dell'indice?
[ Click for Solution 2 ]
Solution 2
E' sufficiente introdurre un elemento di disaccoppiamento (ad esempio, una pipe 0) che imponga un ordine tra l'immissione del numero incrementato e l'immissione di 0 nella inlet di destra.
[ Hide Solution 2 ]

Message Passing

Il funzionamento dataflow di Pure Data avviene mediante pipe che corrispondono alle connessioni tra oggetti. E' anche possibile operare in regime di message passing, cioè ricevere (receive) e spedire (send) dati tra diverse parti di un patch o tra patch diversi. Ciò è illustrato in Figura 13. Invece, l'oggetto value realizza una sorta di variabile globale.
send.jpg
Figura 13

Modularità

Nella programmazione visuale la modularità si ottiene con i meccanismi di subpatching che, in Pure Data, sono di due tipi:
  • Subpatch Si utilizza l'oggetto pd per dichiarare un subpatch. In quest'ultimo, si specificano le inlet e outlet. Si veda Figura 14.
  • Abstraction Si costruisce un patch come file separato, che a questo punto diventa uno degli oggetti a disposizione di Pure Data. Si veda Figura 15.
sub.jpg
Figura 14
abstraction.jpg
Figura 15

Comments, questions, feedback, criticisms?

Discussion forum

Send feedback