Prima dell'elemento HTML5 <audio>, era necessario Flash o un altro plug-in
per rompere il silenzio del web. Sebbene l'audio sul web non richieda più un plug-in, il tag audio presenta limiti significativi per l'implementazione di giochi sofisticati e applicazioni interattive.
L'API Web Audio è un'API JavaScript di alto livello per l'elaborazione e la sintesi dell'audio nelle applicazioni web. L'obiettivo di questa API è includere le funzionalità presenti nei moderni motori audio di gioco e alcune delle attività di mixaggio, elaborazione e filtraggio presenti nelle moderne applicazioni di produzione audio per computer. Di seguito è riportata una breve introduzione all'utilizzo di questa potente API.
Inizia a utilizzare AudioContext
Un AudioContext serve per gestire e riprodurre tutti i suoni. Per produrre un suono utilizzando l'API Web Audio, crea una o più sorgenti audio e collegale alla destinazione audio fornita dall'istanza AudioContext. Questa connessione non deve essere diretta e può passare attraverso
un numero qualsiasi di AudioNodes intermedi che fungono da moduli di elaborazione
per il segnale audio. Questo routing è descritto in modo più dettagliato nella specifica Web Audio specification.
Una singola istanza di AudioContext può supportare più input audio e grafici audio complessi, quindi ne avremo bisogno solo di una per ogni applicazione audio che creiamo.
Il seguente snippet crea un AudioContext:
var context;
window.addEventListener('load', init, false);
function init() {
try {
context = new AudioContext();
}
catch(e) {
alert('Web Audio API is not supported in this browser');
}
}
Per i browser meno recenti basati su WebKit, utilizza il webkit prefisso, come con
webkitAudioContext.
Molte delle funzionalità interessanti dell'API Web Audio, come la creazione di AudioNode e la decodifica dei dati dei file audio, sono metodi di AudioContext.
Caricare i suoni
L'API Web Audio utilizza un AudioBuffer per i suoni di durata breve o media. L'approccio di base consiste nell'utilizzare XMLHttpRequest per recuperare i file audio.
L'API supporta il caricamento dei dati dei file audio in più formati, come WAV, MP3, AAC, OGG e altri. Il supporto del browser per i diversi formati audio varia.
Il seguente snippet mostra il caricamento di un campione audio:
var dogBarkingBuffer = null;
var context = new AudioContext();
function loadDogSound(url) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.responseType = 'arraybuffer';
// Decode asynchronously
request.onload = function() {
context.decodeAudioData(request.response, function(buffer) {
dogBarkingBuffer = buffer;
}, onError);
}
request.send();
}
I dati dei file audio sono binari (non di testo), quindi impostiamo responseType
della richiesta su 'arraybuffer'. Per ulteriori informazioni su
ArrayBuffers, consulta questo articolo su XHR2.
Una volta ricevuti i dati dei file audio (non decodificati), possono essere conservati per la decodifica successiva oppure possono essere decodificati immediatamente utilizzando il metodo decodeAudioData() di AudioContext. Questo metodo accetta l'ArrayBuffer dei dati dei file audio archiviati in request.response e li decodifica in modo asincrono (senza bloccare il thread di esecuzione JavaScript principale).
Al termine di decodeAudioData(), viene chiamata una funzione di callback che fornisce i dati audio PCM decodificati come AudioBuffer.
Riprodurre i suoni
Una volta caricati uno o più AudioBuffers, siamo pronti per riprodurre i suoni. Supponiamo di aver appena caricato un AudioBuffer con il suono di un cane che abbaia e che il caricamento sia terminato. Quindi possiamo riprodurre questo buffer con il seguente codice.
var context = new AudioContext();
function playSound(buffer) {
var source = context.createBufferSource(); // creates a sound source
source.buffer = buffer; // tell the source which sound to play
source.connect(context.destination); // connect the source to the context's destination (the speakers)
source.noteOn(0); // play the source now
}
Questa funzione playSound() può essere chiamata ogni volta che qualcuno preme un tasto o fa clic su qualcosa con il mouse.
La funzione noteOn(time) semplifica la pianificazione della riproduzione audio precisa per giochi e altre applicazioni in cui il tempo è fondamentale. Tuttavia, per far funzionare correttamente questa pianificazione, assicurati che i buffer audio siano precaricati.
Astrarre l'API Web Audio
Naturalmente, sarebbe meglio creare un sistema di caricamento più generale che non sia codificato per caricare questo suono specifico. Esistono molti approcci per gestire i numerosi suoni di durata breve o media che un'applicazione audio o un gioco utilizzerebbe. Ecco un modo per utilizzare un BufferLoader (non fa parte dello standard web).
Di seguito è riportato un esempio di come utilizzare la classe BufferLoader.
Creiamo due AudioBuffers e, non appena vengono caricati, riproduciamoli contemporaneamente.
window.onload = init;
var context;
var bufferLoader;
function init() {
context = new AudioContext();
bufferLoader = new BufferLoader(
context,
[
'../sounds/hyper-reality/br-jam-loop.wav',
'../sounds/hyper-reality/laughter.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
// Create two sources and play them both together.
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.noteOn(0);
source2.noteOn(0);
}
Gestire il tempo: riprodurre i suoni con il ritmo
L'API Web Audio consente agli sviluppatori di pianificare con precisione la riproduzione. Per dimostrarlo, configuriamo una semplice traccia ritmica. Probabilmente il pattern di batteria più conosciuto è il seguente:
in cui un hi-hat viene riprodotto ogni ottavo di nota e la grancassa e il rullante vengono riprodotti alternativamente ogni quarto, in 4/4.
Supponendo di aver caricato i buffer kick, snare e hihat, il codice per farlo è semplice:
for (var bar = 0; bar < 2; bar++) {
var time = startTime + bar * 8 * eighthNoteTime;
// Play the bass (kick) drum on beats 1, 5
playSound(kick, time);
playSound(kick, time + 4 * eighthNoteTime);
// Play the snare drum on beats 3, 7
playSound(snare, time + 2 * eighthNoteTime);
playSound(snare, time + 6 * eighthNoteTime);
// Play the hi-hat every eighth note.
for (var i = 0; i < 8; ++i) {
playSound(hihat, time + i * eighthNoteTime);
}
}
Qui, eseguiamo una sola ripetizione anziché il loop illimitato che vediamo nello spartito. La funzione playSound è un metodo che riproduce un buffer in un momento specificato, come segue:
function playSound(buffer, time) {
var source = context.createBufferSource();
source.buffer = buffer;
source.connect(context.destination);
source.noteOn(time);
}
Modificare il volume di un suono
Una delle operazioni più basilari che potresti voler eseguire su un suono è modificarne il volume. Utilizzando l'API Web Audio, possiamo indirizzare la nostra sorgente alla sua destinazione tramite un AudioGainNode per manipolare il volume:

Questa configurazione della connessione può essere ottenuta come segue:
// Create a gain node.
var gainNode = context.createGainNode();
// Connect the source to the gain node.
source.connect(gainNode);
// Connect the gain node to the destination.
gainNode.connect(context.destination);
Dopo aver configurato il grafico, puoi modificare il volume in modo programmatico manipolando gainNode.gain.value come segue:
// Reduce the volume.
gainNode.gain.value = 0.5;
Dissolvenza incrociata tra due suoni
Ora, supponiamo di avere uno scenario leggermente più complesso, in cui riproduciamo più suoni, ma vogliamo eseguire la dissolvenza incrociata tra di essi. Questo è un caso comune in un'applicazione simile a un DJ, in cui abbiamo due giradischi e vogliamo essere in grado di eseguire la panoramica da una sorgente audio all'altra.
Questa operazione può essere eseguita con il seguente grafico audio:
Per configurare questa operazione, creiamo semplicemente due AudioGainNodes e colleghiamo ogni sorgente tramite i nodi, utilizzando una funzione simile a questa:
function createSource(buffer) {
var source = context.createBufferSource();
// Create a gain node.
var gainNode = context.createGainNode();
source.buffer = buffer;
// Turn on looping.
source.loop = true;
// Connect source to gain.
source.connect(gainNode);
// Connect gain to destination.
gainNode.connect(context.destination);
return {
source: source,
gainNode: gainNode
};
}
Dissolvenza incrociata a potenza uguale
Un approccio di dissolvenza incrociata lineare ingenuo mostra un calo di volume quando esegui la panoramica tra i campioni.
Per risolvere questo problema, utilizziamo una curva di potenza uguale, in cui le curve di guadagno corrispondenti sono non lineari e si intersecano a un'ampiezza maggiore. In questo modo si riducono al minimo i cali di volume tra le regioni audio, ottenendo una dissolvenza incrociata più uniforme tra le regioni che potrebbero avere un livello leggermente diverso.
Dissolvenza incrociata della playlist
Un'altra applicazione di dissolvenza incrociata comune è per un'applicazione di lettore musicale.
Quando un brano cambia, vogliamo dissolvere la traccia corrente e dissolvere la nuova, per evitare una transizione brusca. Per farlo, pianifica una dissolvenza incrociata in futuro. Sebbene sia possibile utilizzare setTimeout per eseguire questa
pianificazione, questo non è preciso. Con l'API Web Audio, possiamo utilizzare l'interfaccia AudioParam per pianificare i valori futuri per parametri come il valore di guadagno di un AudioGainNode.
Pertanto, data una playlist, possiamo passare da una traccia all'altra pianificando una riduzione del guadagno sulla traccia in riproduzione e un aumento del guadagno sulla traccia successiva, entrambe leggermente prima che la traccia corrente termini la riproduzione:
function playHelper(bufferNow, bufferLater) {
var playNow = createSource(bufferNow);
var source = playNow.source;
var gainNode = playNow.gainNode;
var duration = bufferNow.duration;
var currTime = context.currentTime;
// Fade the playNow track in.
gainNode.gain.linearRampToValueAtTime(0, currTime);
gainNode.gain.linearRampToValueAtTime(1, currTime + ctx.FADE_TIME);
// Play the playNow track.
source.noteOn(0);
// At the end of the track, fade it out.
gainNode.gain.linearRampToValueAtTime(1, currTime + duration-ctx.FADE_TIME);
gainNode.gain.linearRampToValueAtTime(0, currTime + duration);
// Schedule a recursive track change with the tracks swapped.
var recurse = arguments.callee;
ctx.timer = setTimeout(function() {
recurse(bufferLater, bufferNow);
}, (duration - ctx.FADE_TIME) - 1000);
}
L'API Web Audio fornisce un comodo insieme di RampToValue metodi per
modificare gradualmente il valore di un parametro, come
linearRampToValueAtTime e exponentialRampToValueAtTime.
Sebbene la funzione di temporizzazione della transizione possa essere selezionata tra quelle lineari ed esponenziali integrate (come sopra), puoi anche specificare la tua curva di valori tramite un array di valori utilizzando la funzione setValueCurveAtTime.
Applicare un semplice effetto di filtro a un suono

L'API Web Audio consente di inviare l'audio da un nodo audio a un altro, creando una catena di processori potenzialmente complessa per aggiungere effetti complessi alle forme audio.
Un modo per farlo è inserire BiquadFilterNode tra la sorgente audio e la destinazione. Questo tipo di nodo audio può eseguire una serie di filtri di ordine inferiore che possono essere utilizzati per creare equalizzatori grafici e effetti ancora più complessi, principalmente per selezionare quali parti dello spettro di frequenza di un suono enfatizzare e quali attenuare.
I tipi di filtri supportati includono:
- Filtro passa-basso
- Filtro passa-alto
- Filtro passa-banda
- Filtro low-shelf
- Filtro high-shelf
- Filtro peaking
- Filtro notch
- Filtro passa-tutto
Tutti i filtri includono parametri per specificare una certa quantità di guadagno, la frequenza a cui applicare il filtro e un fattore di qualità. Il filtro passa-basso mantiene l'intervallo di frequenza inferiore, ma scarta le frequenze alte. Il punto di interruzione è determinato dal valore della frequenza, e il fattore Q è senza unità e determina la forma del grafico. Il guadagno influisce solo su determinati filtri, come i filtri low-shelf e peaking, e non su questo filtro passa-basso.
Configuriamo un semplice filtro passa-basso per estrarre solo le basi da un campione audio:
// Create the filter
var filter = context.createBiquadFilter();
// Create the audio graph.
source.connect(filter);
filter.connect(context.destination);
// Create and specify parameters for the low-pass filter.
filter.type = 0; // Low-pass filter. See BiquadFilterNode docs
filter.frequency.value = 440; // Set cutoff to 440 HZ
// Playback the sound.
source.noteOn(0);
In generale, i controlli di frequenza devono essere modificati per funzionare su una scala logaritmica, poiché l'udito umano stesso funziona secondo lo stesso principio (ovvero, A4 è 440 Hz e A5 è 880 Hz). Per ulteriori dettagli, consulta la funzione FilterSample.changeFrequency nel codice sorgente al link fornito qui sopra.
Infine, tieni presente che il codice di esempio ti consente di collegare e scollegare il filtro, modificando dinamicamente il grafico AudioContext. Possiamo scollegare AudioNode dal grafico chiamando node.disconnect(outputNumber).
Ad esempio, per reindirizzare il grafico in modo che non passi attraverso un filtro, ma a una connessione diretta, possiamo procedere nel seguente modo:
// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);
Per approfondire
Abbiamo esaminato le nozioni di base dell'API, tra cui il caricamento e la riproduzione di campioni audio. Abbiamo creato grafici audio con nodi di guadagno e filtri e abbiamo pianificato modifiche ai suoni e ai parametri audio per abilitare alcuni effetti sonori comuni. A questo punto, sei pronto per creare alcune fantastiche applicazioni audio web.
Se cerchi ispirazione, molti sviluppatori hanno già creato ottimi lavori utilizzando l'API Web Audio. Alcuni dei miei preferiti includono:
- AudioJedit, uno strumento di splicing audio nel browser che utilizza i permalink di SoundCloud.
- ToneCraft, un sequencer audio in cui i suoni vengono creati impilando blocchi 3D.
- Plink, un gioco di creazione musicale collaborativo che utilizza Web Audio e Web Socket.