Summary: Introduzione alla rappresentazione di suoni e immagini (colori, coordinate, ecc.) in Processing.
Note: Your browser may not currently support MathML. See our browser support page for additional details. You can always view the correct math in the PDF version.
L´elaborazione grafica in Processing utilizza un sistema di coordinate cartesiane 3D, come rappresentato in Figura 1.
| Sistema di coordinate |
|---|
![]() |
size() definisce la dimensione della
finestra di display e stabilisce quale motore di
rendering sarà utilizzato per disegnare su tale
finestra. Il motore di default è JAVA2D, cioè la libreria di
grafica bidimensionale di Java. Se si desidera operare sulle
tre dimensioni è necessario impostare il rendering P3D
(Processing 3D), particolarmente efficiente per la grafica
su web, oppure OPENGL, che consente di delegare alla scheda
grafica molte operazioni tipiche della grafica 3D.
In Processing, una immagine può essere assegnata ad un oggetto
di tipo PImage. La funzione
loadImage("myImage") preleva il file (gif o jpg)
myImage, contenente la codifica di una immagine,
e restituisce in uscita il contenuto in pixel della immagine
stessa, il quale può essere assegnato ad una variabile di tipo
PImage. Il file myImage deve essere
caricato nella cartella data della directory
avente lo stesso nome dello sketch Processing al quale si sta
lavorando.
New, Processing apre una cartella di nome
sketch_???????, all'interno di una directory
Processing, corrispondente al nome
assegnato dal sistema al file al quale si comincia a
lavorare. Tale cartella è accessibile tramite i comandi del
menu Sketch/Add File di Processing.PImage rende accessibili, mediante i campi
width e height, rispettivamente la
larghezza e la altezza della immagine caricata. Il contenuto è
invece accessibile mediante il campo pixels[].
size(400,300);
PImage b;
b = loadImage("gondoliers.jpg");
println("width=" + b.width + " height=" + b.height);
image(b, 0, 0, 400, 300); // position (0,0); width=400; height=300;
image(b, 20, 10, 100, 80); // position (20,10); width=100; height=80;
Poiché i nostri ricettori oculari di colore (coni), ciascuno sintonizzato su una regione di lunghezze d'onda, sono in numero di tre, i modelli di colore sono sempre riferiti ad uno spazio a tre dimensioni. Nei modelli di colore di tipo additivo vengono individuati tre assi coordinati, ciascuno corrispondente ad un colore base, e mediante miscelazione di tre corrispondenti fasci luminosi, si possono ottenere tutti i colori appartenenti ad un volume (gamut) individuato da tali assi. I tre colori base sono scelti in maniera arbitraria o, più spesso, sulla base del campo di applicazione (es., colore di una terna di fosfori o di laser). Nei processi di stampa si usano modelli di tipo sottrattivo, nei quali si parte da una superficie bianca e si usano inchiostri primari per sottrarre colore dal bianco.
In Processing color è un tipo primitivo usato per
specificare il colore. E' realizzato mediante un numero di 32
bit, in cui il primo byte specifica il valore alpha, e gli
altri byte successivi specificano una terna nel modello RGB o
in quello HSB. La scelta di un modello piuttosto che
dell'altro è fatta mediante la funzione
colorMode(). I colori rappresentabili mediante
tre byte sono in numero di
I colori si rappresentano con una terna di numeri, ciascuno
rappresentante rispettivamente le intensità dei colori primari
rosso (Red), verde (Green), e blu (Blue). Ciascun numero può
essere un intero senza segno e quindi assumere valori tra 0 e
255, oppure essere espresso come un numero floating point
compreso tra 0 e 1.0. Queste possibilità possono essere
specificate mediante la funzione colorMode(). Il
modello RGB è di tipo additivo.
I colori si rappresentano con una terna di numeri, ciascuno rappresentante rispettivamente la tinta o lunghezza d'onda dominante(Hue), la saturazione (Saturation), e l'intensità o brillantezza (Brightness).
| Gimp color chooser |
|---|
![]() |
E' un byte di informazione usato per effettuare
interpolazione tra immagini, ad esempio allo scopo di rendere
la trasparenza. Esso si può ottenere, da una variabile di tipo
color, con il metodo alpha(). La
manipolazione dell'alpha channel si svolge mediante il metodo
blend() della classe PImage.
|
|
In Processing, è possibile assegnare un colore ad una
variabile di tipo color mediante la funzione
color(), essendo il modello precedentemente stato
definito mediante colorMode(). Le funzioni
red(), green(), blue(),
hue(), saturation(), e
brightness() consentono di passare da un modello
all'altro.
colorMode(RGB);
color c1 = color(102, 30,29);
colorMode(HSB);
color c2 = color(hue(c1), saturation(c1), brightness(c1));
colorMode(RGB);
color c3 = color(red(c2), green(c2), blue(c2));
// le variabili c1, c2, c3 contengono codifica dello stesso colore
Una immagine può
essere tinteggiata con un colore e resa più o meno trasparente
mediante assegnazione di un valore alpha. La funzione da usare
a questo scopo è tint(). Ad esempio, per dare un
tono blu all'immagine incastonata nel Esempio 1, è sufficiente far precedere il
secondo comando image() da tint(0, 153, 204,
126) .
In computer graphics, punti e vettori sono rappresentati in
La funzione translate() consente di spostare
oggetti sulla finestra di immagine. Ammette due o tre
argomenti, che sono rispettivamente gli spostamenti lungo le
direzioni
In due dimensioni, la funzione
rotate() consente di ruotare oggetti sulla
finestra di immagine. Ciò avviene mediante una moltiplicazione
(a sinistra) delle coordinate di ciascun pixel dell'oggetto
non ruotato per una matrice di rotazione. La rotazione avviene
sempre rispetto all'angolo superiore sinistro (coordinate
rotate(PI/3) prima del secondo comando
image() in Esempio 1. In tre
dimensioni, si possono usare le rotazioni elementari rispetto agli
assi coordinati rotateX(), rotateY(), e
rotateZ().
La
funzione scale() consente di espandere o
contrarre un oggetto mediante moltiplicazione per una costante
delle coordinate dei punti che lo compongono. Se invocata con
due o tre parametri, gli scalamenti possono anche essere
diversi lungo i diversi assi cartesiani.
Ogni strumento o linguaggio per la manipolazione di media offre anche la possibilità di lavorare con la parola scritta e con i suoi elementi visuali fondamentali: i caratteri tipografici.
L'aspetto di un tipo di carattere ha due componenti principali: il font e la dimensione.
Processing mette a disposizione la
classe PFont ed i metodi loadFont()
(per caricare un font ed assegnarlo ad un oggetto di classe
PFont) e textFont() (per attivare un
font con una specifica dimensione). Per poter essere caricato,
il font deve essere stato precedentemente inserito nella
directory data dello sketch corrente. Il tool
Create Font, accessibile dal menu
Tools di Processing, consente di creare le bitmap
dei caratteri che si andranno ad usare e ne colloca il file
relativo nella directory data. In seguito a
queste operazioni preliminari, il font può essere usato per
scrivere del testo, usando la funzione
text(). Questa consente di collocare una stringa
di caratteri nello spazio bidimensionale o tridimensionale,
eventualmente inserendola all'interno di una box
rettangolare. L'allineamento dei caratteri all'interno della
box è governato dalla textAlign(). Nella
configurazione di default, il testo scritto può essere
sottoposto a trasformazioni spaziali come ogni altro oggetto.
Per quanto riguarda il colore dei caratteri, esso può essere
impresso con la funzione fill(), come per ogni
altro oggetto grafico.
|
|
Processing consente anche un controllo
completo dell'occupazione spaziale dei caratteri e della
distanza tra caratteri contigui (si veda Figura 3). La funzione
textWidth() calcola l'estensione orizzontale di
una carattere o di una stringa. Essa può essere usata, insieme
alle coordinate esatte di posizionamento passate alla
text(), per controllare il kerning ed
il tracking
tra caratteri. La textSize() consente di
ridefinire la dimensione dei caratteri. La
textLeading() ridefinisce la distanza in pixel
tra linee contigue di testo. Questa distanza si misura tra le
baseline su cui si adagiano le stringhe
di caratteri. Lettere quali "p" o "q" si estendono al di sotto
della baseline per una quantità di pixel calcolabile con
textDescent(). Invece, la
textAscent() restituisce l'estensione massima al
di sopra della baseline (tipicamente, l'altezza della lettera
"d").
| Metriche di tipo |
|---|
![]() |
Fino alla versione beta 112,
Processing forniva la possibilità di accedere direttamente ad alcune
funzionalità audio, tra le quali la riproduzione dei
file .wav. Nelle versioni più recenti, Processing
affida la gestione del suono a librerie
esterne. Le librerie audio più usate sono Ess, Minim e
Sonia.
Così come per le immagini, anche per poter elaborare o riprodurre
suoni i relativi file audio devono essere archiviati nella
directory data dello sketch corrente.
La libreria Sonia è la
più complessa da utilizzare, ma consente una elaborazione molto
dettagliata ed efficiente. Tale libreria contiene, per esempio,
delle funzioni che permettono di riprodurre campioni audio
(sample playback), di eseguire un'analisi di
Fourier in tempo reale (ovvero un'analisi del suono che viene
captato da un microfono collegato al computer: realtime
FFT), nonchè di salvare dei file .wav
sul disco. Per poter utilizzare la libreria Sonia è
necessario scaricare il file .zip dal sito Sonia. Bisogna poi
decomprimere il tutto e copiare la directory
Sonia_?_? dentro la directory
Processing/libraries. Infine, dopo aver riavviato
Processing, si deve aggiungere un comando import,
selezionandolo direttamente dal menu Sketch / Import Library
/ Sonia_?_?.
In questa sezione proveremo prima ad utilizzare e poi ad analizzare un'applicazione per l'esplorazione dei timbri, simile nella concezione al Color Chooser di Figura 2, denominata Sound Chooser. Per adesso possiamo pensare al timbro di un suono come al "colore" di un'immagine. O se si vuole possiamo pensare al timbro come al diverso "colore" dei diversi strumenti. Nelle lezioni successive vedremo con maggior precisione a cosa corrispondono dal punto di vista fisico e percettivo sia il colore sia il timbro. Nella applet Sound Chooser si possono far suonare quattro suoni con timbri diversi, facendo click con il mouse su uno qualsiasi dei raggi. Ciascuno dei raggi corrisponde ad uno strumento musicale (timbro/colore) diverso. Cambiando posizione lungo il raggio e facendo click, è possible sentire come la brillantezza del timbro corrispondente cambia. Più precisamente, mano a mano che si procede verso il centro, il suono si fa più povero.
Vediamo ora in cosa consiste il codice
Processing, affiancato da Sonia, necessario per implementare il Sound Chooser nei
tratti salienti. Il comando Sonia.start(this) è
indispensabile per attivare il motore audio di Sonia. La linea
Sample mySample1 dichiara una variabile atta a
contenere campioni audio. A tale variabile possono essere
applicati vari metodi, tra i quali il metodo play
per riprodurre il campione. Nel draw() viene
definito l'aspetto grafico della applet. Infine, tramite la
funzione mouseReleased() viene rilevato quando il
mouse viene rilasciato dopo essere stato premuto e in quale
area del cerchiello. A quel punto una successione di
condizioni if stabilisce quale strumento/timbro
debba essere riprodotto a seconda del punto di
interazione. Inoltre, all'interno della funzione
mouseReleased() viene invocata un'altra funzione:
filtra(). Questa funzione, che viene implementata alla fine
del listato di questa applet, esegue un filtraggio sul suono
che viene riprodotto. In particolare si tratta di un
filtraggio passa-basso, vale a dire che lascia passare le basse
frequenze ma non le alte. A seconda se il mouse viene
rilasciato più o meno vicino al centro, l'effetto del
filtraggio cambia. Più in particolare il filtraggio sarà più
drastico (le alte frequenze sono maggiormente attenuate, con
l'effetto di un maggior incupimento del suono) se il mouse
viene rilasciato in prossimità del centro e il suono risulterà
più povero (scolorito).
Una realizzazione del Sound Chooser più snella mediante la libreria Minim è proposta nel problema Exercise 4.
|
|
Il contenuto di un oggetto PImage è
accessibile mediante il suo campo pixels[]. I
pixel della immagine, corrispondenti ad una lettura riga
per riga, sono contenuti in questo array di dimensione
width*height. Modificare il codice di Esempio 2 in modo da utilizzare il campo
pixels[] invece del metodo
get(), senza cambiare il funzionamento del
programma.
Basta sostituire l'invocazione b.set() con la
b.set(i,j,b.pixels[j*b.width+i]+ color(0,0,0, 255 - (int)((1-ramp)*255)) );
Completare il codice riportato in Tabella 3 per ottenere l'applet Sound Chooser completa.
Aggiungere ai raggi del Sound Chooser del colore,
sostituendo le line con dei rect
e colorando le barrette ottenute con una variazione di
brillantezza, crescente dal centro verso l'esterno.
Si provi a realizzare il Sound Chooser di problema Exercise 2 con la libreria Minim. Si noti la maggiore compattezza e semplicità del codice.
import ddf.minim.*;
import ddf.minim.effects.*;
Minim minim;
AudioPlayer mySample1, mySample2, mySample3, mySample4;
LowPassSP lpf1, lpf2, lpf3, lpf4;
float cutoff1, cutoff2, cutoff3, cutoff4;
void setup()
{
size(200, 200);
colorMode(HSB, 360, height, height);
minim = new Minim(this);
mySample1 = minim.loadFile("flauto.aif");
mySample2 = minim.loadFile("oboe.wav");
mySample3 = minim.loadFile("tromba.wav");
mySample4 = minim.loadFile("violino.wav");
lpf1 = new LowPassSP(4000, mySample1.sampleRate());
lpf2 = new LowPassSP(4000, mySample2.sampleRate());
lpf3 = new LowPassSP(4000, mySample3.sampleRate());
lpf4 = new LowPassSP(4000, mySample4.sampleRate());
mySample1.addEffect(lpf1);
mySample2.addEffect(lpf2);
mySample3.addEffect(lpf3);
mySample4.addEffect(lpf4);
}
void draw()
{
stroke(255);
strokeWeight(1);
fill(0, 88, 88);
ellipseMode(CORNER);
ellipse(50,50,100,100);
beginShape(LINES);
vertex(50, 100);
vertex(90, 100);
vertex(110, 100);
vertex(150, 100);
vertex(100, 50);
vertex(100, 90);
vertex(100, 110);
vertex(100, 150);
endShape();
}
void mouseReleased()
{
// FLAUTO
if ((mouseX > 95) && (mouseX < 105)&& (mouseY > 50)&& (mouseY < 90)) {
cutoff1 = map(mouseY, 50, 90, 1000, 30);
lpf1.setFreq(cutoff1);
println(mouseY + " + " +cutoff1);
mySample1.rewind();
mySample1.play();
}
// OBOE
if ((mouseX > 110) && (mouseX < 149)&& (mouseY > 95)&& (mouseY < 105)) {
cutoff2 = map(mouseX, 110, 149, 30, 1000);
lpf2.setFreq(cutoff2);
println(mouseX + " + " +cutoff2);
mySample2.rewind();
mySample2.play();
}
// TROMBA
if ((mouseX > 95) && (mouseX < 105)&& (mouseY > 110)&& (mouseY < 149)) {
cutoff3 = map(mouseY, 110, 149, 30, 1000);
lpf3.setFreq(cutoff3);
println(mouseY + " + " +cutoff3);
mySample3.rewind();
mySample3.play();
}
// VIOLINO
if ((mouseX > 50) && (mouseX < 90)&& (mouseY > 95)&& (mouseY < 105)) {
cutoff4 = map(mouseX, 50, 90, 1000, 30);
lpf4.setFreq(cutoff4);
println(mouseX + " + " +cutoff4);
mySample4.rewind();
mySample4.play();
}
}
// safely stop the Minim engine upon shutdown.
public void stop(){
mySample1.close();
mySample2.close();
mySample3.close();
mySample4.close();
minim.stop();
super.stop();
}
Processing incoraggia l'uso di font codificati come bitmap
in un file dall'estensione .vlw. Questo rende
Processing indipendente dai font effettivamente installati
in una macchina specifica. Tuttavia, è possibile
utilizzare font vettoriali (ad esempio, di tipo
TrueType) inserendone i file (ad esempio, con
estensione .ttf) nella cartella Data. Si
provi ad utilizzare un font di questo tipo mediante la
funzione createFont(). Rinunciando alla
garanzia di comportamento uguale anche su macchine
diverse, si può anche passare come parametro a questa
funzione il nome di un font installato nella macchina e
non presente nella directory Data. Infine, in
modalità di rendering JAVA2D è possibile
anche usare i font logici, scrivendo Serif,
SansSerif, Monospaced,
Dialog, o DialogInput al posto
della stringa di specificazione del font passata come
argomento a createFont(). Anche in questo caso non
è necessario caricare alcun font nella cartella
Data. La corrispondenza tra font logico e
font fisico realmente utilizzato sarà in generale diversa
a seconda dei font installati in un dato sistema. Si
provino i cinque font logici sulla propria macchina.
Questo è un esempio di
soluzione. Verificate la presenza dei font nella vostra
macchina e nella cartella Data.
size(200,200, JAVA2D);
PFont fonte;
fonte = loadFont("HoeflerText-Black-48.vlw"); // font precedentemente creato e
// inserito in Data
textFont(fonte, 12);
fill(10, 20, 250, 80);
textAlign(RIGHT);
text("pippo pippo non lo sa", 10, 14, 35, 70);
textFont(fonte, 94);
textAlign(LEFT);
fill(200, 0, 0, 100);
text("ppnls", 25, 5, 150, 190);
fonte = createFont("Serif", 10, false); // logical font di Java
textFont(fonte, 80);
fill(0, 200, 0, 170);
rotate(PI/6);
text("LO SO", 20, 20, 280, 280);
fonte = createFont("cmsy10", 10, true); // font installato nel sistema
textFont(fonte, 80);
fill(0, 20, 150, 170);
rotate(PI/12);
text("ECCO", 20, 20, 280, 280);
fonte = createFont("grunge.ttf", 10, true); // font vettoriale nella cartella Data
textFont(fonte, 80);
fill(100, 100, 0, 170);
rotate(-PI/6);
text("qui", 20, 20, 280, 280);