Skip to content Skip to navigation Skip to collection information

OpenStax_CNX

You are here: Home » Content » Programmazione di Artefatti Interattivi » Oscillazioni, ritardi, e fluttuazioni del tempo

Navigation

Recently Viewed

This feature requires Javascript to be enabled.
 

Oscillazioni, ritardi, e fluttuazioni del tempo

Module by: Davide Rocchesso. E-mail the author

Summary: La produzione di fenomeni oscillatori è un aspetto frequente di molte realizzazioni interattive. Vengono descritte tecniche di riproduzione di oscillazioni di varia forma e frequenza. Inoltre, si presenta il buffer circolare come struttura di supporto alla manipolazione continua del tempo.

Tra i pattern emergenti nel design dell'interazione, si nota la necessità di produrre fenomeni oscillatori, di tipo uditivo o visuale. La gestione più versatile di questi fenomeni si fa mediante array contenenti un periodo del fenomeno oscillatorio da produrre.

L'oscillatore tabellare

L'approccio più classico e versatile alla sintesi di forme d'onda periodiche è la lettura ciclica di una tabella contenente un periodo della forma d'onda periodica da generare. Detto buf[] il buffer (è un array) contenente la forma d'onda da generare, l'oscillatore tabellare funziona per lettura ciclica della tabella, con un passo di avanzamento I=BfR I B f R , dove B B è la lunghezza del buffer, f f è la frequenza (numero di oscillazioni al secondo) che si vuole generare, e R R è il rate (frequenza di campionamento) al quale si vuole produrre l'oscillazione.

Esempio 1: Onda propagantesi

Per dare l'illusione di una onda prodotta da un gesto di interazione e propagantesi in una certa direzione si può fare uso dell'oscillatore tabellare. Nel codice che segue il buffer viene precaricato con un periodo di sinusoide. Il punto in cui l'utilizzatore clicca sulla finestra determina la posizione iniziale della cresta, e viene generata una oscillazione la cui frequenza è impostata tramite la variabile f.

  
int B = 256; // lunghezza di tabella (risoluzione)
int R = 20;   // rate
float f = 0.8; // frequenza in cicli al secondo
float[] buf = new float[B];
int HEIGHT = 128; // altezza della finestra
float amp; // ampiezza della oscillazione
int readPoint; // posizione orizzontale della cresta
float decayFactor = 0.95; // fattore di decadimento dell'onda

void setup() {
 size(B,HEIGHT); 
 stroke(255); strokeWeight(2); 
 for (int i = 0; i < buf.length; i++) buf[i] = sin(2*PI*(float)i/buf.length);
}

void draw() {
 background(0);

 if ((mouseX >= 0) && (mouseX < B) && (mousePressed)) {
   amp = 2*(HEIGHT/2 - mouseY)/float(HEIGHT);
   readPoint = mouseX;
 }
 for (int i = 0; i < buf.length; i++) {
   point(i, HEIGHT - HEIGHT/2 - HEIGHT/2*amp*buf[(i - readPoint + B/4 + B)%B]);
 }
 amp = decayFactor*amp;
 readPoint = (readPoint + round(f*B/R))%B; // incremento dell'oscillatore
}

	  

Exercise 1

Si renda la frequenza di oscillazione dipendente dalla lunghezza dell'intervallo di tempo durante il quale il tasto del mouse viene tenuto premuto.

Solution



int B = 256; // lunghezza di tabella (risoluzione)
int R = 20;   // rate
float f = 1.1; // frequenza in cicli al secondo
float[] buf = new float[B];
int HEIGHT = 128; // altezza della finestra
float amp; // ampiezza della oscillazione
float readPoint; // posizione orizzontale della cresta
float decayFactor = 0.92; // fattore di decadimento dell'onda
float tempo;

void setup() {
 size(B,HEIGHT); 
 stroke(255); strokeWeight(2); 
 for (int i = 0; i < buf.length; i++) buf[i] = sin(2*PI*(float)i/buf.length);
}

void mousePressed() {
  tempo = millis();
}

void mouseReleased() {
  tempo = millis() - tempo; println(tempo); 
  f = 500/tempo;
}

void draw() {
 background(0);

 if ((mouseX >= 0) && (mouseX < B) && (mousePressed)) {
   amp = 2*(HEIGHT/2 - mouseY)/float(HEIGHT);
   readPoint = mouseX;
 }
 for (int i = 0; i < buf.length; i++) {
   point(i, HEIGHT - HEIGHT/2 - HEIGHT/2*amp*buf[(i - round(readPoint) + B/4 + B)%B]);
 }
 amp = decayFactor*amp;
 readPoint = (readPoint + (f*B/R))%B; // incremento dell'oscillatore
}

Exercise 2

Si imponga un inviluppo di ampiezza sulla oscillazione generata, come se a vibrare fosse una corda vincolata alle estremità.

Solution

Quella che segue è la soluzione a Exercise 1 con i vincoli aggiunti agli estremi:



int B = 256; // lunghezza di tabella (risoluzione)
int R = 20;   // rate
float f = 1.1; // frequenza in cicli al secondo
float[] buf = new float[B];
int HEIGHT = 128; // altezza della finestra
float amp; // ampiezza della oscillazione
float readPoint; // posizione orizzontale della cresta
float decayFactor = 0.92; // fattore di decadimento dell'onda
float tempo;
float ampli=0;

void setup() {
 size(B,HEIGHT); 
 stroke(255); strokeWeight(2); 
 for (int i = 0; i < buf.length; i++) buf[i] = sin(2*PI*(float)i/buf.length);
}

void mousePressed() {
  tempo = millis();
}

void mouseReleased() {
  tempo = millis() - tempo; println(tempo); 
  f = 500/tempo;
}

void draw() {
 background(0);

 if ((mouseX >= 0) && (mouseX < B) && (mousePressed)) {
   amp = 2*(HEIGHT/2 - mouseY)/float(HEIGHT);
   readPoint = mouseX;
 }
 for (int i = 0; i < buf.length; i++) {
   if (i < readPoint) ampli += 1/(float)readPoint;
   else ampli -= 1/(float)(B - readPoint);
   point(i, HEIGHT - HEIGHT/2 - HEIGHT/2*amp*ampli*buf[(i - round(readPoint) + B/4 + B)%B]);
 }
 ampli = 0;
 amp = decayFactor*amp;
 readPoint = (readPoint + (f*B/R))%B; // incremento dell'oscillatore
}

Exercise 3

Si renda l'oscillazione più fluida sostituendo l'arrotondamento (round()) con l'interpolazione lineare.

Oscillazioni video

L'oscillatore tabellare può essere la struttura di riferimento anche per ripetizioni cicliche, a frequenza controllabile, di frammenti video. In questo caso la tabella da leggere ciclicamente contiene una sequenza di immagini.

Esempio 2: Iterazione di frammento video

In questo esempio, quando si manda in esecuzione il programma un frammento di 4 secondi di video viene ripreso dalla videocamera a 16 frame al secondo. Poi tale frammento è riprodotto ciclicamente a 20 frame al secondo, con la possibilità di controllare la velocità di riproduzione (frequenza di oscillazione) cliccando in diverse posizioni orizzontali del mouse.

  
import processing.video.*; 
Capture myCapture; 
int captureRate = 16;
float f=1; 
int B = 64;
int R = 20;
PImage[] sequenza = new PImage[B];

int i;
float j=0;
float inc = f*B/R;
boolean via = false;

void setup() { 
  for (int i=0; i<B; i++) sequenza[i]=loadImage("vetro.jpg"); // immagine dummy, per inizializzare
  size(200, 200); 
  String s = "IIDC FireWire Video"; 
  myCapture = new Capture(this, width, height, s, 30);
  myCapture.frameRate(captureRate);
  frameRate(R);
} 
 
void mousePressed(){
  f = float(mouseX)/width; inc = f*B/R;
} 

void draw() { 
  if (!via) {
    if(myCapture.available()) { 
      // Reads the new frame
      myCapture.read(); 
      sequenza[i].copy(myCapture,0,0,myCapture.width,myCapture.height,0,0,sequenza[i].width,sequenza[i].height);
      i = (i+1)%B;
      println(i);
      if (i==0) via = true;
    }
  }
  if (via) {  image(sequenza[floor(j)], 0, 0,200,200); 
              j = (j+inc);  if (j>=B) j=j-B;
            }
}


	  

Oscillazioni nel visual programming

Nel visual programming le oscillazioni tabellari spesso si costruiscono utilizzando un segnale sawtooth che consente di percorre ciclicamente gli indici dell'array, ad una velocità dipendente dalla frequenza del segnale stesso.

Figura 1
Figura 1 (oscilla.png)
In questo patch sono dichiarati due array onda1 e onda2 di 64 elementi ciascuno. Il segnale a dente di sega viene generato ad una frequenza di 139 cicli al secondo. Il suo range viene moltiplicato per 64 in maniera da occupare tutto il campo degli indici (da 0 a 63) dell'array onda1. Ogni volta che si preme il bottone di bang, 64 campioni del segnale prodotto dalla lettura della tabella onda1 vengono scritti nella tabella onda2. Il segnale prodotto dalla lettura ciclica della tabella onda1 è udibile attraverso conversione da digitale ad analogico (dac~). Si noti che in Pd tutti gli operatori che hanno il simbolo di tilde lavorano in maniera sincrona ad audio rate, e quindi la lettura dalla tabella onda1 fornisce campioni al sample rate di funzionamento (per default 44100 Hz). Si noti anche la scalinatura del segnale scritto nella tabella onda2, dovuto alla lettura non interpolata dei campioni di onda1.

Exercise 4

Come si può evitare la scalinatura nella forma d'onda prodotta dall'oscillatore tabellare di Figura 1?

Solution

Basta sostituire tabread~ con tabread4~, in questo modo facendo una lettura interpolata di ordine 3. In altri termini, l'interpolatore fa passare un polinomio di terzo grado (una cubica) per quattro punti contigui della tabella.

Ritardi variabili

Nei sistemi interattivi c'è spesso la necessità di ritardare nel tempo un segnale, un processo, o un evento. Ad esempio, se ci sono più flussi di video o audio in streaming, ciascuno viaggiante su canali indipendenti per i quali non è possibile avere lo stesso tempo di propagazione da trasmittente a fruitore, è opportuno ritardare diversamente ciascun flusso in maniera da avere un riallineamento ed una sincronizzazione in ricezione. In campo audio, le linee di ritardo a lunghezza variabile sono alla base di molti effetti, quali sono echi e riverberazione.

La struttura di supporto per la realizzazione delle linee di ritardo è il buffer circolare, cioè un array ai cui elementi si accede con aritmetica circolare dei puntatori. In particolare, c'è una variabile IN, chiamata puntatore di ingresso, che contiene l'indice dell'elemento dell'array nel quale si va a scrivere. Una variabile OUT, chiamata puntatore di uscita, contiene l'indice dell'elemento dell'array dal quale si va a leggere. Si tratta di incrementare i puntatori di accesso in maniera circolare, mantenendo la loro distanza relativa pari al numero di passi temporali di cui si vuole che sia fatto il ritardo. Ad ogni passo temporale (o istante di campionamento) il segnale di ingresso è scritto nella locazione puntata da IN e letto dalla locazione puntata da OUT, D passi indietro. Quindi, i due puntatori sono aggiornati con le operazioni


	  IN = (IN + 1) % B;
	  OUT = (OUT + 1) % B;
	
dove B è la lunghezza del buffer, scelta in maniera da essere maggiore del più grande valore di ritardo D che si intende usare. Il ritardo D può variare dinamicamente, ad esempio oscillando tra un minimo ed un massimo, ma è chiaro che se esso assume un valore non intero bisognerà adottare una strategia di interpolazione per la lettura del segnale ritardato. Ad esempio, si può ancora una volta fare interpolazione lineare tra locazioni adiacenti, oppure scegliere l'intero immediatamente inferiore a D.

Exercise 5

Si costruisca un programma Processing che legge caratteri dalla tastiera e ne fornisce una eco sulla finestra grafica con un ritardo crescente mano a mano che si aggiungono linee di testo.

Solution


  
PFont font;
int B = 1000;
int INTERLINEA = 30;
int xpos=0, ypos=INTERLINEA;
char[] buffer = new char[B];
int OUT = 0; 
int IN = 0;
int D = 0;
char carattere;
char inchar = '\0';

void setup() {
  size(800,800);
  font = loadFont("Courier-48.vlw"); 
  textFont(font, 32); 
  frameRate(30);
}

void keyReleased() {
  inchar = key;
}

void draw() {
    if (inchar=='\n') {
      D+=10; 
      IN = (OUT + D)%B; 
      xpos = 0; 
      ypos += INTERLINEA; 
      inchar='\0';
      }
    else {
      buffer[IN] = inchar;
      carattere = buffer[OUT];
      text(carattere, xpos, ypos); 
      xpos += textWidth(carattere); 
      IN = (IN + 1)%B; OUT = (OUT + 1)%B;
    }
    inchar = '\0';  
}

Collection Navigation

Content actions

Download:

Collection as:

PDF | EPUB (?)

What is an EPUB file?

EPUB is an electronic book format that can be read on a variety of mobile devices.

Downloading to a reading device

For detailed instructions on how to download this content's EPUB to your specific device, click the "(?)" link.

| More downloads ...

Module as:

PDF | EPUB (?)

What is an EPUB file?

EPUB is an electronic book format that can be read on a variety of mobile devices.

Downloading to a reading device

For detailed instructions on how to download this content's EPUB to your specific device, click the "(?)" link.

| More downloads ...

Add:

Collection to:

My Favorites (?)

'My Favorites' is a special kind of lens which you can use to bookmark modules and collections. 'My Favorites' can only be seen by you, and collections saved in 'My Favorites' can remember the last module you were on. You need an account to use 'My Favorites'.

| A lens I own (?)

Definition of a lens

Lenses

A lens is a custom view of the content in the repository. You can think of it as a fancy kind of list that will let you see content through the eyes of organizations and people you trust.

What is in a lens?

Lens makers point to materials (modules and collections), creating a guide that includes their own comments and descriptive tags about the content.

Who can create a lens?

Any individual member, a community, or a respected organization.

What are tags? tag icon

Tags are descriptors added by lens makers to help label content, attaching a vocabulary that is meaningful in the context of the lens.

| External bookmarks

Module to:

My Favorites (?)

'My Favorites' is a special kind of lens which you can use to bookmark modules and collections. 'My Favorites' can only be seen by you, and collections saved in 'My Favorites' can remember the last module you were on. You need an account to use 'My Favorites'.

| A lens I own (?)

Definition of a lens

Lenses

A lens is a custom view of the content in the repository. You can think of it as a fancy kind of list that will let you see content through the eyes of organizations and people you trust.

What is in a lens?

Lens makers point to materials (modules and collections), creating a guide that includes their own comments and descriptive tags about the content.

Who can create a lens?

Any individual member, a community, or a respected organization.

What are tags? tag icon

Tags are descriptors added by lens makers to help label content, attaching a vocabulary that is meaningful in the context of the lens.

| External bookmarks