Aggiornamenti dell'audio web in Chrome 49

Andrea Rossi
Chris Wilson

Chrome ha migliorato in modo costante e discreto il supporto dell'API Web Audio. In Chrome 49 (beta a partire da febbraio 2016, che dovrebbe essere stabile a marzo 2016) abbiamo aggiornato diverse funzionalità per monitorare la specifica e abbiamo aggiunto un nuovo nodo.

decodeAudioData() ora restituisce una promessa

Il metodo decodeAudioData() su AudioContext ora restituisce Promise, consentendo la gestione asincrona dei pattern basata su Promise. Il metodo decodeAudioData() ha sempre assunto funzioni di callback di successo ed errore come parametri:

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 sembri più dettagliato, Promises rende più semplice e coerente la programmazione asincrona. Per motivi di compatibilità, le funzioni di callback Successo ed Errore sono ancora supportate, in base alla specifica.

OfflineAudioContext ora supporta sospendi() e resume()

A prima vista, potrebbe sembrare strano l'uso della funzione Pause() in OfflineAudioContext. Dopotutto, l'elemento suspend() è stato aggiunto a AudioContext per consentire l'attivazione della modalità standby dell'hardware audio, il che sembra inutile negli scenari in cui esegui il rendering in un buffer (il che è ovviamente OfflineAudioContext). Tuttavia, lo scopo di questa funzionalità è poter creare solo una parte di un "punteggio" alla volta, per ridurre al minimo l'utilizzo della memoria. Puoi creare più nodi mentre la sospensione è in corso al centro di un rendering.

Ad esempio, la sonata al chiaro di luna di Beethoven contiene circa 6500 note. Ogni "nota" probabilmente si scompone in almeno un paio di nodi di grafici audio (ad es. un nodo AudioBuffer e Gain). Se volessi eseguire il rendering di tutti i sette minuti e mezzo in un buffer con OfflineAudioContext, probabilmente non vorrai creare tutti quei nodi contemporaneamente. Puoi invece 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();
}

Ciò ti consentirà di ridurre al minimo il numero di nodi che devono essere precreati all'inizio del rendering e la richiesta di memoria.

IIRFilterNode

Le specifiche hanno aggiunto un nodo per gli audiofili che vogliono creare una propria risposta infinita-impulse-response esattamente specificata: l'IIRFilterNode. Questo filtro completa il BiquadFilterNode, ma consente la specifica completa dei parametri di risposta del filtro (anziché l'AudioParams facile da usare di BiquadFilterNode per tipo, frequenza, Q e simili). IIRFilterNode consente di specificare con precisione i filtri che non è stato possibile creare prima, come i filtri per ordine singolo. Tuttavia, l'utilizzo di IIRFilterNode richiede una conoscenza approfondita di come funzionano i filtri IIR e non sono pianificabili come BiquadFilterNodes.

Modifiche precedenti

Vorrei anche citare un paio di miglioramenti apportati in precedenza: in Chrome 48, l'automazione dei nodi BiquadFilter ha iniziato a funzionare alla velocità audio. L'API non è cambiata per fare questo, ma questo significa che la ricerca dei filtri sarà ancora più fluida. Sempre in Chrome 48, abbiamo aggiunto il concatenamento al metodo AudioNode.connect() restituendo il nodo a cui ci stiamo connettendo. Ciò semplifica la creazione di catene di nodi, come in questo esempio:

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

Per il momento è tutto. Continua così!