Introduzione all'API Web Audio

Boris Smus
Boris Smus

Prima dell'elemento <audio> HTML5, era necessario Flash o un altro plug-in per rompere il silenzio del web. Quando l'audio sul web non è più richiede un plug-in, il tag audio presenta e implementare applicazioni interattive e giochi sofisticati.

L'API Web Audio è un'API JavaScript di alto livello per l'elaborazione e sintetizzando audio nelle applicazioni web. L'obiettivo di questa API è che includono funzionalità presenti nei moderni motori audio dei giochi e alcuni dei le attività di combinazione, elaborazione e filtro presenti nella versione desktop moderna applicazioni di produzione audio. Segue una breve introduzione utilizzando questa potente API.

Introduzione ad AudioContext

Un AudioContext consente di gestire e riprodurre tutti i suoni. Per produrre un suono utilizzando l'API Web Audio, crea una o più sorgenti sonore e collegarli alla destinazione audio fornita da AudioContext in esecuzione in un'istanza Compute Engine. Questa connessione non deve essere diretta e può passare un numero qualsiasi di AudioNodes intermedi che fungono da elaborazione per il segnale audio. Questo routing è descritto in maggiore dettaglio nella specifica di Web Audio.

Una singola istanza di AudioContext può supportare più input audio e grafici audio complessi, perciò avremo bisogno di uno solo di questi l'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 prefisso webkit, come per webkitAudioContext.

Molte delle interessanti funzionalità dell'API Web Audio, come la creazione AudioNodi e decodifica dei dati dei file audio sono metodi di AudioContext.

Caricamento suoni in corso...

L'API Web Audio utilizza un AudioBuffer per video di breve e media durata i suoni. 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. Supporto del browser per i formati audio variano.

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 del file audio sono binari (non testo), quindi impostiamo responseType della richiesta a 'arraybuffer'. Per ulteriori informazioni ArrayBuffers, consulta questo articolo su XHR2.

Una volta ricevuti i dati del file audio (non decodificati), puoi conservarli per la decodifica in un secondo momento oppure può essere decodificato subito utilizzando Metodo decodeAudioData() AudioContext. Questo metodo utilizza ArrayBuffer di dati dei file audio memorizzati in request.response e la decodifica in modo asincrono (non bloccando l'esecuzione principale del codice JavaScript) thread).

Al termine decodeAudioData(), viene chiamata una funzione di callback che fornisce i dati audio PCM decodificati sotto forma di AudioBuffer.

Riproduzione di suoni

Un semplice audiografico
Un semplice audiografico

Puoi iniziare a giocare dopo aver caricato uno o più AudioBuffers i suoni. Supponiamo di aver caricato un AudioBuffer con l'audio di un cane che abbaia e che il caricamento è terminato. Poi possiamo giocare 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 programmazione di un suono preciso per i giochi e altre applicazioni urgenti. Tuttavia, per ottenere questa programmazione funziona correttamente, assicurati che i sound buffer siano sono precaricati.

Estrazione dell'API Web Audio

Naturalmente, sarebbe meglio creare un sistema di caricamento più generico che non è hardcoded per caricare questo suono specifico. Esistono molti per gestire i molti suoni di breve e media durata che un'applicazione o un gioco audio utilizzerebbe un gioco o un'applicazione audio. Ecco un modo per utilizzare un BufferLoader (che 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, riproducili 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: suonare i suoni seguendo il ritmo

L'API Web Audio consente agli sviluppatori di programmare con precisione la riproduzione. A dimostrare questo, impostiamo una traccia ritmica semplice. Probabilmente la pattern di drumkit più noto è il seguente:

Un semplice pattern di musica rock
Un semplice pattern di musica rock

in cui viene suonato un hihat ogni ottava nota, mentre il tasto kick e rullante sono giocate in modo alternato ogni trimestre, in 4/4.

Supponendo di aver caricato i buffer kick, snare e hihat, 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 facciamo una sola ripetizione invece del loop illimitato che vediamo gli spartiti. La funzione playSound è un metodo che riproduce un buffer in un determinato momento, come segue:

function playSound(buffer, time) {
    var source = context.createBufferSource();
    source.buffer = buffer;
    source.connect(context.destination);
    source.noteOn(time);
}

Regolare il volume di un suono

Una delle operazioni più basilari che potresti eseguire su un suono è regolare il volume. Utilizzando l'API Web Audio, possiamo indirizzare la nostra sorgente a la sua destinazione attraverso un AudioGainNode al fine di manipolare volume:

Grafico audio con un nodo di guadagno
Grafico audio con un nodo di guadagno

Questa configurazione di connessione può essere eseguita nel seguente modo:

// 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 in modo programmatico il valore manipolando gainNode.gain.value come segue:

// Reduce the volume.
gainNode.gain.value = 0.5;

Dissolvenza incrociata tra due suoni

Supponiamo ora di avere uno scenario leggermente più complesso, in cui riproducendo più suoni ma con una dissolvenza incrociata tra i suoni. Si tratta di un caso comune in un'applicazione simile a quella da DJ, in cui abbiamo due giradischi e vuoi poter eseguire la panoramica da una sorgente audio all'altra.

Per farlo, puoi utilizzare il seguente grafico audio:

Grafico audio con due sorgenti collegate tramite nodi di guadagno
Grafico audio con due sorgenti collegate tramite nodi di guadagno

Per configurarlo, basta creare due AudioGainNodes e connettere ciascuna sorgente attraverso 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 ingenuo a dissolvenza incrociata lineare mostra un calo del volume durante la panoramica tra i campioni.

Una dissolvenza incrociata lineare
Una dissolvenza incrociata lineare

Per risolvere questo problema, utilizziamo una curva di potenza di parità, in cui le curve di guadagno corrispondenti non sono lineari e si intersecano a un di ampiezza. In questo modo vengono ridotti al minimo i cali di volume tra le regioni dell'audio, in una dissolvenza incrociata più uniforme tra regioni che potrebbero essere di livello diverso.

Una dissolvenza incrociata di uguale potenza.
Una dissolvenza incrociata a potenza uguale

Dissolvenza incrociata della playlist

Un'altra applicazione crossfader comune è quella di un lettore musicale. Quando un brano cambia, vogliamo applicare una dissolvenza alla traccia corrente e per evitare una transizione sgradevole. A questo scopo, pianifica una dissolvenza incrociata nel futuro. Per farlo potremmo utilizzare setTimeout pianificazione, questo dato non è preciso. Con l'API Web Audio, puoi utilizzare l'interfaccia AudioParam per pianificare valori futuri per come il valore di guadagno di AudioGainNode.

Di conseguenza, data una playlist, possiamo passare da una traccia all'altra programmando una diminuisci il guadagno sulla traccia attualmente in riproduzione e un aumento del guadagno sulle quella successiva, entrambe leggermente prima del termine della riproduzione della traccia corrente:

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 offre un pratico insieme di metodi RampToValue per modificare gradualmente il valore di un parametro, linearRampToValueAtTime e exponentialRampToValueAtTime.

La funzione di tempo di transizione può essere scelta dalla funzione e esponenziali (come sopra), puoi anche specificare il tuo valore della curva tramite un array di valori utilizzando la funzione setValueCurveAtTime.

Applicare un semplice effetto filtro a un suono

Un grafico audio con un nodo BiquadFilterNode
Un grafico audio con un componente BiquadFilterNode

L'API Web Audio consente di trasmettere l'audio da un nodo audio all'altro, creando una catena di processori potenzialmente complessa per aggiungere effetti sonori alle tue forme sonore.

Un modo per farlo è inserire BiquadFilterNode tra il suono sorgente e destinazione. Questo tipo di nodo audio può svolgere filtri di ordine inferiore che possono essere utilizzati per creare equalizzatori grafici e perfino più complessi, legati principalmente alla selezione delle parti lo spettro di frequenza di un suono da enfatizzare e da sottomettere.

I tipi di filtri supportati includono:

  • Filtro passa basso
  • Filtro superamento pass
  • Filtro passa banda
  • Filtro barra delle app in basso
  • Filtro sezione superiore
  • Filtro picco
  • Filtro tacca
  • Filtro tutte le tessere

Tutti i filtri includono parametri per specificare una certa quantità guadagno, la frequenza con cui applicare il filtro e un fattore di qualità. Il filtro passa basso mantiene l'intervallo di frequenza più basso, ma ignora i valori elevati di frequenza cardiaca. Il punto di interruzione è determinato dal valore della frequenza, mentre il fattore Q è senza unità e determina la forma del grafico. Il guadagno interessa solo alcuni filtri, come lo scaffale basso e filtri di picco e non questo filtro passa basso.

Impostiamo un semplice filtro passa-basso per estrarre solo le basi da una esempio 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 della frequenza devono essere modificati per funzionare scala logaritmica, poiché l'udito umano si basa sullo stesso principio (ovvero, A4 è 440 Hz, e A5 è 880 Hz). Per ulteriori dettagli, consulta FilterSample.changeFrequency nel link del codice sorgente riportato sopra.

Infine, tieni presente che il codice campione ti consente di collegare e scollegare che modifica dinamicamente il grafico AudioContext. Possiamo scollegare AudioNodi dal grafico chiamando node.disconnect(outputNumber). Ad esempio, per reindirizzare il grafico da un filtro a una connessione diretta, possiamo fare quanto segue:

// Disconnect the source and filter.
source.disconnect(0);
filter.disconnect(0);
// Connect the source directly.
source.connect(context.destination);

Maggiore ascolto

Abbiamo esaminato le nozioni di base dell'API, tra cui il caricamento e la riproduzione di audio i campioni. Abbiamo creato grafici audio con nodi e filtri di guadagno. suoni programmati e modifiche dei parametri audio per consentire alcuni suoni comuni e gli effetti sonori. A questo punto, sei pronto per creare un sito web applicazioni audio.

Se sei in cerca di ispirazione, molti sviluppatori hanno già creato un ottimo lavoro con l'utilizzo dell'API Web Audio. Alcune delle mie preferite include:

  • AudioJedit, uno strumento di giunzione audio integrato nel browser che utilizza permalink SoundCloud.
  • ToneCraft, un sequencer di suoni in cui i suoni vengono creati impilare blocchi 3D.
  • Plink, un gioco collaborativo per la creazione di musica che utilizza Web Audio e Web Socket.