L'API Web Audio è un modello completamente separato dal
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.
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.
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.
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.
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.
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!