Come creare un oscillatore audio con l'API Web Audio

Cosa starai creando

L'API Web Audio è un modello completamente separato dal

Cosa stiamo costruendo

Guarda l'API Pen WebAudio con Oscilloscopio di Dennis Gaebel (@dennisgaebel) su CodePen.

La nostra demo sopra contiene tre ingressi radio che, se selezionati, riproducono l'audio correlato a cui ciascuno fa riferimento. Quando viene selezionato un canale, verrà riprodotto il nostro audio e verrà visualizzato il grafico della frequenza. 

Non spiegherò ogni riga del codice della demo; tuttavia, spiegherò i bit principali che aiutano a visualizzare la sorgente audio e il suo grafico di frequenza. Per iniziare, avremo bisogno di un po 'di markup.

Il markup

La parte più importante del markup è il tela, quale sarà l'elemento che mostra il nostro oscilloscopio. Se non ti è familiare tela, Suggerisco di leggere questo articolo intitolato "Introduzione al lavoro con la tela".

Con lo stage impostato per la visualizzazione del grafico, abbiamo bisogno di creare l'audio.

Creare l'audio

Inizieremo definendo un paio di variabili importanti per il contesto audio e il guadagno. Queste variabili verranno utilizzate per fare riferimento in un punto successivo del codice.

lascia audioContext, masterGain;

Il audioContext rappresenta un grafico di elaborazione audio (una descrizione completa di una rete di elaborazione del segnale audio) costruita da moduli audio collegati tra loro. Ognuno è rappresentato da un AudioNode, e quando sono collegati insieme, creano un grafico di routing audio. Questo contesto audio controlla sia la creazione del nodo (i) che contiene sia l'esecuzione dell'elaborazione e decodifica dell'audio. 

Il AudioContext deve essere creato prima di ogni altra cosa, poiché tutto avviene all'interno di un contesto.

Nostro masterGain accetta un ingresso di una o più sorgenti audio e emette il volume dell'audio, che è stato regolato in guadagno a un livello specificato dal nodo GainNode.gain parametro a-rate. Puoi pensare al guadagno principale come al volume. Ora creeremo una funzione per consentire la riproduzione da parte del browser.

function audioSetup () let source = 'http://ice1.somafm.com/seventies-128-aac'; audioContext = new (window.AudioContext || window.webkitAudioContext) (); 

Inizio definendo a fonte variabile che verrà utilizzata per fare riferimento al file audio. In questo caso sto utilizzando un URL per un servizio di streaming, ma potrebbe anche essere un file audio. Il audioContext line definisce un oggetto audio ed è il contesto che abbiamo discusso in precedenza. Controllo anche la compatibilità usando il WebKit prefisso, ma il supporto è ampiamente adottato in questo momento con l'eccezione di IE11 e Opera Mini.

function audioSetup () masterGain = audioContext.createGain (); masterGain.connect (audioContext.destination); 

Con la nostra configurazione iniziale completa, avremo bisogno di creare e connettere il masterGain alla destinazione audio. Per questo lavoro, useremo il Collegare() metodo, che consente di collegare uno degli output del nodo a un target.

function audioSetup () let song = new Audio (source), songSource = audioContext.createMediaElementSource (song); songSource.connect (masterGain); song.play (); 

Il canzone variabile crea un nuovo oggetto audio usando il Audio() costruttore. Avrai bisogno di un oggetto audio in modo che il contesto abbia una sorgente da riprodurre per gli ascoltatori.

Il songSource variabile è la salsa magica che riproduce l'audio ed è dove passeremo nella nostra sorgente audio. Usando createMediaElementSource (), l'audio può essere riprodotto e manipolato come desiderato. La variabile finale collega la nostra sorgente audio al guadagno principale (volume). La linea finale song.play () è la chiamata a dare effettivamente il permesso di riprodurre l'audio.

lascia audioContext, masterGain; function audioSetup () let source = 'http://ice1.somafm.com/seventies-128-aac'; audioContext = new (window.AudioContext || window.webkitAudioContext) (); masterGain = audioContext.createGain (); masterGain.connect (audioContext.destination); let song = new Audio (source), songSource = audioContext.createMediaElementSource (song); songSource.connect (masterGain); song.play ();  audioSetup ();

Ecco il nostro risultato finale contenente tutte le linee di codice che abbiamo discusso fino a questo punto. Mi assicuro inoltre di effettuare la chiamata a questa funzione scritta sull'ultima riga. Successivamente, creeremo la forma d'onda audio.

Creare l'onda audio

Per visualizzare l'onda di frequenza per la nostra sorgente audio scelta, dobbiamo creare la forma d'onda.

const analyzer = audioContext.createAnalyser (); masterGain.connect (analizzatore);

Il primo riferimento a createAnalyser () espone i dati di tempo e frequenza audio per generare visualizzazioni di dati. Questo metodo produrrà un AnalyserNode che passa il flusso audio dall'input all'output, ma consente di acquisire i dati generati, elaborarli e costruire visualizzazioni audio che hanno esattamente un input e un output. Il nodo dell'analizzatore sarà collegato al guadagno principale che è l'uscita del nostro percorso del segnale e dà la possibilità di analizzare una sorgente.

const waveform = new Float32Array (analyser.frequencyBinCount); analyser.getFloatTimeDomainData (forma d'onda);

Questo Float32Array () costruttore rappresenta una matrice di un numero in virgola mobile a 32 bit. Il frequencyBinCount proprietà del AnalyserNode l'interfaccia è un valore lungo non firmato pari a metà della dimensione FFT (Fast Fourier Transform). Questo generalmente equivale al numero di valori di dati che si avranno per l'uso con la visualizzazione. Usiamo questo approccio per raccogliere ripetutamente i nostri dati di frequenza.

Il metodo finale getFloatTimeDomainData copia la forma d'onda corrente o i dati del dominio del tempo in a Float32Array array passato dentro.

function updateWaveform () requestAnimationFrame (updateWaveform); analyser.getFloatTimeDomainData (forma d'onda); 

Questa intera quantità di dati e di elaborazione utilizza requestAnimationFrame () per raccogliere dati sul dominio del tempo ripetutamente e disegna un output in stile "oscilloscopio" dell'attuale ingresso audio. Faccio anche un'altra chiamata a getFloatTimeDomainData () dal momento che questo deve essere continuamente aggiornato in quanto la sorgente audio è dinamica.

const analyzer = audioContext.createAnalyser (); masterGain.connect (analizzatore); const waveform = new Float32Array (analyser.frequencyBinCount); analyser.getFloatTimeDomainData (forma d'onda); function updateWaveform () requestAnimationFrame (updateWaveform); analyser.getFloatTimeDomainData (forma d'onda); 

La combinazione di tutto il codice discusso finora produce l'intera funzione di cui sopra. La chiamata a questa funzione sarà collocata all'interno della nostra audioSetup funzione appena sotto song.play (). Con la forma d'onda in atto, dobbiamo ancora disegnare queste informazioni sullo schermo usando il nostro tela elemento, e questa è la prossima parte della nostra discussione.

Disegnare l'onda audio

Ora che abbiamo creato la nostra forma d'onda e possediamo i dati che richiediamo, dovremo disegnarla sullo schermo; questo è dove il tela elemento è stato introdotto.

function drawOscilloscope () requestAnimationFrame (drawOscilloscope); const scopeCanvas = document.getElementById ('oscilloscopio'); const scopeContext = scopeCanvas.getContext ('2d'); 

Il codice sopra semplicemente afferra il tela elemento in modo che possiamo fare riferimento nella nostra funzione. La chiamata a requestAnimationFrame nella parte superiore di questa funzione verrà programmata la successiva cornice di animazione. Questo è il primo posto in modo che possiamo arrivare il più vicino possibile a 60FPS.

function drawOscilloscope () scopeCanvas.width = waveform.length; scopeCanvas.height = 200; 

Ho implementato uno stile di base che disegnerà la larghezza e l'altezza del tela. L'altezza è impostata su un valore assoluto, mentre la larghezza sarà la lunghezza della forma d'onda prodotta dalla sorgente audio.

function drawOscilloscope () scopeContext.clearRect (0, 0, scopeCanvas.width, scopeCanvas.height); scopeContext.beginPath (); 

Il clearRect (x, y, larghezza, altezza) il metodo cancellerà qualsiasi contenuto precedentemente disegnato in modo da poter disegnare continuamente il grafico della frequenza. Dovrai anche assicurarti di chiamare BeginPath () prima di iniziare a disegnare il nuovo fotogramma dopo aver chiamato clearRect (). Questo metodo avvia un nuovo percorso svuotando l'elenco di tutti i percorsi secondari. Il pezzo finale di questo puzzle è un ciclo che ti permette di scorrere i dati che abbiamo ottenuto in modo da poter continuamente disegnare questo grafico di frequenza sullo schermo.

function drawOscilloscope () for (lascia che i = 0; i < waveform.length; i++)  const x = i; const y = ( 0.5 + (waveform[i] / 2) ) * scopeCanvas.height; if(i == 0)  scopeContext.moveTo(x, y);  else  scopeContext.lineTo(x, y);   scopeContext.stroke(); 

Questo ciclo sopra disegna la nostra forma d'onda al tela elemento. Se si registra la lunghezza del modulo d'onda sulla console (durante la riproduzione dell'audio), verrà segnalato ripetutamente 1024. Questo generalmente equivale al numero di valori di dati con cui dovrai giocare per la visualizzazione. Se richiamate dalla sezione precedente per creare il modulo d'onda, otteniamo questo valore da Float32Array (analyser.frequencyBinCount). Questo è il modo in cui possiamo fare riferimento al valore 1024 che attraverseremo.

Il moveTo () il metodo metterà letteralmente il punto di partenza di un nuovo sottotracciato nell'aggiornato (x, y) coordinate. Il lineTo () metodo collega l'ultimo punto nel sottotracciato al x, y coordina con una linea retta (ma in realtà non la disegna). L'ultimo pezzo sta chiamando ictus() fornito da tela quindi possiamo effettivamente disegnare la linea di frequenza. Lascerò la parte che contiene la matematica come una sfida per il lettore, quindi assicurati di pubblicare la tua risposta nei commenti qui sotto.

function drawOscilloscope () requestAnimationFrame (drawOscilloscope); const scopeCanvas = document.getElementById ('oscilloscopio'); const scopeContext = scopeCanvas.getContext ('2d'); scopeCanvas.width = waveform.length; scopeCanvas.height = 200; scopeContext.clearRect (0, 0, scopeCanvas.width, scopeCanvas.height); scopeContext.beginPath (); for (let i = 0; i < waveform.length; i++)  const x = i; const y = ( 0.5 + (waveform[i] / 2) ) * scopeCanvas.height; if(i == 0)  scopeContext.moveTo(x, y);  else  scopeContext.lineTo(x, y);   scopeContext.stroke(); 

Questa è l'intera funzione che abbiamo creato per disegnare la forma d'onda che chiameremo dopo song.play () posto all'interno del nostro audioSetup funzione, che include anche il nostro updateWaveForm chiamata di funzione pure.

Pensieri di separazione

Ho solo spiegato i bit importanti per la demo, ma assicurati di leggere le altre parti della mia demo per capire meglio come funzionano i pulsanti di opzione e il pulsante di avvio in relazione al codice precedente, incluso lo stile CSS.

L'API Web Audio è davvero divertente per chiunque sia interessato all'audio di qualsiasi tipo e ti incoraggio ad andare più a fondo. Ho anche raccolto alcuni esempi divertenti di CodePen che utilizzano l'API Web Audio per creare esempi davvero interessanti. Godere!

  • https://codepen.io/collection/XLYyWN
  • https://codepen.io/collection/nNqdoR
  • https://codepen.io/collection/XkNgkE
  • https://codepen.io/collection/ArxwaW

Riferimenti

  • http://webaudioapi.com
  • https://webaudio.github.io/web-audio-api
  • http://chimera.labs.oreilly.com/books/1234000001552/ch01.html