Tema 16

So i visualització (II)

Submergeix-te en l'espectre sonor. Aprèn a utilitzar l'AnalyserNode per analitzar freqüències, visualitza la forma d'ona i l'espectre, i crea equalitzadors, paisatges sonors i molt més.

Què és l'AnalyserNode?

L'AnalyserNode de la Web Audio API descompon un senyal d'àudio en les seves freqüències components. És com desfer un pastís per veure'n els ingredients: separa els greus (baixos), els mitjos i els aguts (alts).

Amb l'AnalyserNode podem obtenir:

  • Forma d'ona (waveform): valors d'amplitud al llarg del temps (domini temporal).
  • Espectre (spectrum): intensitat de cada banda de freqüència (domini freqüencial).
Idea clau: L'AnalyserNode ens permet "veure" la música: cada nota, cada tambor, cada veu té una empremta visual única que podem convertir en art.

Configurar l'AnalyserNode

Per analitzar l'espectre, creem un AnalyserNode, el connectem a l'oscil·lador, i després cridem getByteFrequencyData() o getByteTimeDomainData() per obtenir les dades.

let ctx, osc, gain, anal;

function setup() {
  createCanvas(400, 200);
  ctx = new AudioContext();
  osc = ctx.createOscillator();
  gain = ctx.createGain();
  anal = ctx.createAnalyser();
  anal.fftSize = 256; // nombre de bandes (potència de 2)
  osc.connect(anal);
  anal.connect(gain);
  gain.connect(ctx.destination);
  osc.frequency.value = 200;
  gain.gain.value = 0.15;
  osc.start();
}

function draw() {
  let bufferLength = anal.frequencyBinCount; // meitat de fftSize
  let dataArray = new Uint8Array(bufferLength);
  anal.getByteFrequencyData(dataArray); // espectre (0-255)
  // dataArray conté les amplituds per cada banda de freqüència
}

fftSize defineix la resolució de l'anàlisi (32, 64, 128, 256, 512, 1024, 2048...). frequencyBinCount és la meitat d'fftSize.

Dibuixar l'espectre i la forma d'ona

Amb getByteFrequencyData() obtenim l'espectre. Per a la forma d'ona, utilitzem getByteTimeDomainData().

Exemple bàsic: dibuixar les barres de l'espectre i la forma d'ona sobreposada.

// Espectre (barres)
anal.getByteFrequencyData(dataArray);
let barWidth = width / bufferLength;
for (let i = 0; i < bufferLength; i++) {
  let barHeight = map(dataArray[i], 0, 255, 0, height * 0.6);
  fill(100, 150, 255, 200);
  rect(i * barWidth, height - barHeight, barWidth - 1, barHeight);
}

// Forma d'ona (línia)
anal.getByteTimeDomainData(waveform);
stroke(255, 200, 0); noFill();
beginShape();
for (let i = 0; i < bufferLength; i++) {
  let x = map(i, 0, bufferLength, 0, width);
  let y = map(waveform[i], 0, 255, height * 0.8, height * 0.2);
  vertex(x, y);
}
endShape();

Mapejar bandes de freqüència a visuals

No cal utilitzar totes les bandes. Podem agrupar-les en rangs: greus (índex 0-20), mitjos (20-80), aguts (80-127). Així controlem diferents elements visuals segons el tipus de so.

// Agrupem bandes
let greus = 0, mitjos = 0, aguts = 0;
for (let i = 0; i < 8; i++) greus += e[i];
for (let i = 8; i < 24; i++) mitjos += e[i];
for (let i = 24; i < bufferLength; i++) aguts += e[i];
greus /= 8; mitjos /= 16; aguts /= (bufferLength - 24);

// Utilitzem els valors per controlar mides
fill(255, 0, 0); ellipse(80, height/2, map(greus, 0, 255, 10, 80));
fill(0, 255, 0); ellipse(160, height/2, map(mitjos, 0, 255, 10, 80));
fill(0, 0, 255); ellipse(240, height/2, map(aguts, 0, 255, 10, 80));

Visualització radial: cercle espectral

En lloc de barres lineals, podem disposar les freqüències en cercle. Cada banda es representa com una línia que surt del centre.

translate(width/2, height/2);
for (let i = 0; i < bufferLength; i += 2) {
  let angle = map(i, 0, bufferLength, 0, TWO_PI);
  let r = map(e[i], 0, 255, 30, 130);
  let x = cos(angle) * r;
  let y = sin(angle) * r;
  stroke(100, 200, 255, 180); strokeWeight(2);
  line(cos(angle)*20, sin(angle)*20, x, y);
  fill(255, 200, 0, 150); ellipse(x, y, 4, 4);
}

Exemples creatius pas a pas

Equalitzador de barres amb reflexos

Barres d'espectre amb un efecte de reflex simètric, com un mirall d'aigua.

for (let i = 0; i < bufferLength; i++) {
  let alt = map(e[i], 0, 255, 0, height/2 - 10);
  let to = map(i, 0, bufferLength, 200, 320);
  fill(to, 80, 90, 200);
  rect(i * w, height/2 - alt, w - 1, alt); // barra superior
  fill(to, 80, 90, 80);
  rect(i * w, height/2, w - 1, alt);       // reflex inferior
}

Paisatge sonor: muntanyes de freqüències

Convertim l'espectre en un perfil de muntanyes, amb colors que varien segons l'àudio.

beginShape(); vertex(0, height - 20);
for (let i = 0; i < bufferLength; i += 2) {
  let x = map(i, 0, bufferLength, 0, width);
  let y = height - 20 - map(e[i], 0, 255, 0, 120);
  vertex(x, y);
}
vertex(width, height - 20); endShape(CLOSE);

Consells per treballar amb l'AnalyserNode

  • fftSize: valors més alts (1024) donen més resolució en freqüència però menys en temps. Per a visuals, 64 o 128 solen ser suficients.
  • Agrupa bandes: per a equalitzadors senzills, fes la mitjana de grups de bandes per reduir el soroll visual.
  • Escala logarítmica: l'oïda humana percep les freqüències logarítmicament. Pots aplicar log(i+1) per espaiar les bandes de manera més natural.
  • Combina amb el ratolí: utilitza mouseX per controlar la freqüència de l'oscil·lador i veure com canvia l'espectre en temps real.
  • Tipus d'ona: 'sine' (so pur), 'square' (so buit), 'sawtooth' (so brillant), 'triangle' (so suau).

Exercicis per practicar

  1. Dibuixa l'espectre com un gràfic de línies en lloc de barres: connecta els punts amb vertex() dins d'un beginShape().
  2. Crea un equalitzador circular: barres que surten d'un cercle central cap enfora, amb angle fix i longitud variable segons l'espectre.
  3. Utilitza les tres bandes (greus, mitjos, aguts) per controlar el color RGB d'un fons: greus→vermell, mitjos→verd, aguts→blau.
  4. Implementa un efecte de partícules que reaccionin a l'espectre: cada banda de freqüència genera partícules amb velocitat i color diferents.
  5. Repte: Crea un visualitzador dual: la meitat esquerra mostra l'espectre d'un oscil·lador, i la meitat dreta mostra l'espectre d'un altre oscil·lador amb forma d'ona diferent.

Reflexió artística

L'espectre sonor és com un paisatge invisible que només podem veure amb les eines adequades. Cada cançó té el seu propi perfil de muntanyes, les seves pròpies tempestes de greus i pluges d'aguts. Com a artista-programador, ara tens el poder de traduir aquest paisatge efímer en formes, colors i moviments. La visualització d'àudio no és només una traducció mecànica; és una interpretació poètica. Pots decidir si els greus són terra ferma o un abisme, si els aguts són estrelles o espurnes. L'AnalyserNode et dóna les dades, però la teva sensibilitat els dóna significat. Experimenta amb diferents formes d'ona, amb la teva pròpia veu, amb el silenci. Observa com cada so dibuixa una imatge diferent al teu llenç.

Mini-projecte del Tema 16

Crea un "Paisatge sonor generatiu" amb les següents característiques:

  • Utilitza un AnalyserNode per analitzar un oscil·lador interactiu controlat amb el ratolí/teclat.
  • Visualitza l'espectre de almenys dues maneres diferents: per exemple, barres lineals + cercle radial, o forma d'ona + muntanyes espectrals.
  • Agrupa les freqüències en greus, mitjos i aguts i assigna a cada grup un element visual diferent (color, mida, posició, rotació...).
  • Incorpora interactivitat: el ratolí o el teclat modifiquen algun paràmetre del so o de la visualització.
  • Inclou una llegenda visual (text o icones) que indiqui què representa cada element.