Aggiornamenti dell'audio web in Chrome 49

Chris Wilson
Chris Wilson

Chrome ha migliorato costantemente e silenziosamente il supporto dell'API Web Audio. In Chrome 49 (beta da febbraio 2016 e dovrebbe essere stabile a marzo 2016) abbiamo aggiornato diverse funzionalità per monitorare la specifica e abbiamo anche aggiunto un nuovo nodo.

decodeAudioData() ora restituisce una promessa

Il metodo decodeAudioData() su AudioContext ora restituisce un Promise, consentendo la gestione di pattern asincroni basati su promesse. Il metodo decodeAudioData() ha sempre accettato come parametri le funzioni di callback di successo ed errore:

context.decodeAudioData( arraybufferData, onSuccess, onError);

Ora, però, puoi utilizzare il metodo Promise standard per gestire la natura asincrona della decodifica dei dati audio:

context.decodeAudioData( arraybufferData ).then(
        (buffer) => { /* store the buffer */ },
        (reason) => { console.log("decode failed! " + reason) });

Sebbene in un singolo esempio possa sembrare più verboso, le promesse semplificano e rendono più coerente la programmazione asincrona. Per motivi di compatibilità, le funzioni di callback Success e Error sono ancora supportate, come da specifica.

OfflineAudioContext ora supporta suspend() e resume()

A prima vista, potrebbe sembrare strano avere la chiamata suspend() su un OfflineAudioContext. Dopotutto, suspend() è stato aggiunto a AudioContext per consentire di mettere l'hardware audio in modalità standby, il che sembra inutile negli scenari in cui esegui il rendering in un buffer (ovviamente, è lo scopo di OfflineAudioContext). Tuttavia, lo scopo di questa funzionalità è poter costruire solo una parte di un "punteggio" alla volta, per ridurre al minimo l'utilizzo della memoria. Puoi creare altri nodi mentre il rendering è sospeso.

Ad esempio, la Sonata al chiaro di luna di Beethoven contiene circa 6500 note. È probabile che ogni "nota" venga decostruita in almeno un paio di nodi del grafico audio (ad es. un AudioBuffer e un nodo Gain). Se vuoi eseguire il rendering dell'intero minuto e mezzo in un buffer con OfflineAudioContext, probabilmente non vuoi creare tutti questi nodi contemporaneamente. In alternativa, puoi crearli in blocchi di tempo:

var context = new OfflineAudioContext(2, length, sampleRate);
scheduleNextBlock();
context.startRendering().then( (buffer) => { /* store the buffer */ } );

function scheduleNextBlock() {
    // create any notes for the next blockSize number of seconds here
    // ...

    // make sure to tell the context to suspend again after this block;
    context.suspend(context.currentTime + blockSize).then( scheduleNextBlock );

    context.resume();
}

In questo modo, potrai ridurre al minimo il numero di nodi che devono essere pre-creati all'inizio del rendering e diminuire le richieste di memoria.

IIRFilterNode

La specifica ha aggiunto un nodo per gli audiofili che vogliono creare il proprio filtro a risposta infinita specificato con precisione: il IIRFilterNode. Questo filtro completa il BiquadFilterNode, ma consente di specifiche complete dei parametri di risposta del filtro (anziché l'intuitivo AudioParams di BiquadFilterNode per tipo, frequenza, Q e simili). IIRFilterNode consente di specificare con precisione i filtri che non potevano essere creati in precedenza, come i filtri di ordine singolo. Tuttavia, l'utilizzo di IIRFilterNode richiede una conoscenza approfondita del funzionamento dei filtri IIR e non sono nemmeno pianificabili come BiquadFilterNode.

Modifiche precedenti

Voglio anche menzionare un paio di miglioramenti apportati in precedenza: in Chrome 48, l'automazione dei nodi BiquadFilter ha iniziato a funzionare alla frequenza audio. L'API non è cambiata per questo, ma significa che le scansioni dei filtri avranno un suono ancora più fluido. Inoltre, in Chrome 48 abbiamo aggiunto il collegamento al metodo AudioNode.connect() retornando il nodo a cui ci stiamo connettendo. In questo modo è più semplice creare catene di nodi, come in questo esempio:

sourceNode.connect(gainNode).connect(filterNode).connect(context.destination);

Per il momento è tutto. Continua a farti sentire.