Hur man skapar en ljudoscillator med Web Audio API

Vad du ska skapa

Web Audio API är en modell som är helt skild från

Vad vi bygger

Se Penna WebAudio API w / Oscilloskop av Dennis Gaebel (@dennisgaebel) på CodePen.

Vår demo ovan innehåller tre radioingångar som när de väljs kommer att spela det korrelerande ljudet som var och en refererar till. När en kanal väljs kommer vårt ljud att spelas och frekvensgrafen visas. 

Jag kommer inte att förklara varje rad i demo-koden; Jag kommer dock att förklara de primära bitarna som hjälper till att visa ljudkällan och dess frekvensdiagram. För att börja, behöver vi en liten bit av uppmärkning.

Markup

Den viktigaste delen av markeringen är duk, vilket kommer att vara det element som visar vårt oscilloskop. Om du inte är bekant med duk, Jag föreslår att du läser den här artikeln med titeln "En introduktion till arbete med kanfas".

Med scenen som anges för att visa grafen måste vi skapa ljudet.

Skapa ljudet

Vi börjar med att definiera några viktiga variabler för ljudkontext och förstärkning. Dessa variabler kommer att användas för att hänvisa vid en senare punkt i koden.

låt audioContext, masterGain;

De audioContext representerar ett ljudbehandlingsdiagram (en komplett beskrivning av ett ljudsignalbehandlingsnätverk) byggt från ljudmoduler kopplade ihop. Var och en representeras av en AudioNode, och när de är anslutna tillsammans skapar de ett ljuddirigeringsdiagram. Detta ljudkontext kontrollerar både skapandet av noden (erna) som den innehåller och utförandet av ljudbehandlingen och avkodningen. 

De AudioContext måste skapas före något annat, eftersom allt händer inom ett sammanhang.

Vår masterGain accepterar en ingång av en eller flera ljudkällor och matar ut ljudets volym, som har justerats i förstärkningen till en nivå som anges av nodens GainNode.gain en-rate parameter. Du kan tänka på huvudförstärkningen som volymen. Nu skapar vi en funktion för att tillåta uppspelning av webbläsaren.

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

Jag börjar med att definiera en källa variabel som används för att referera till ljudfilen. I det här fallet använder jag en webbadress till en streamingtjänst, men det kan också vara en ljudfil. De audioContext rad definierar ett ljudobjekt och är det sammanhang vi diskuterade tidigare. Jag kontrollerar också för kompatibilitet med hjälp av WebKit prefix, men stöd är allmänt antaget vid denna tidpunkt med undantag för IE11 och Opera Mini.

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

Med vår första installationen måste vi skapa och ansluta masterGain till ljuddestinationen. För det här jobbet använder vi ansluta() metod som låter dig ansluta en av nodens utgångar till ett mål.

funktion audioSetup () låt låt = ny ljud (source), songSource = audioContext.createMediaElementSource (låt); songSource.connect (masterGain); song.play (); 

De låt variabel skapar ett nytt ljudobjekt med hjälp av Audio() konstruktör. Du behöver ett ljudobjekt så att kontexten har en källa att spela upp för lyssnare.

De songSource variabel är den magiska såsen som spelar ljudet och är där vi kommer att passera i vår ljudkälla. Genom att använda createMediaElementSource (), ljudet kan spelas och manipuleras efter önskemål. Den slutliga variabeln kopplar vår ljudkälla till huvudförstärkningen (volym). Slutlinjen song.play () är uppmaningen att faktiskt ge tillstånd att spela ljudet.

låt audioContext, masterGain; funktionen audioSetup () let source = 'http://ice1.somafm.com/seventies-128-aac'; audioContext = nytt (window.AudioContext || window.webkitAudioContext) (); masterGain = audioContext.createGain (); masterGain.connect (audioContext.destination); låt låt = ny ljud (source), songSource = audioContext.createMediaElementSource (låt); songSource.connect (masterGain); song.play ();  audioSetup ();

Här är vårt slutresultat med alla koden vi har diskuterat fram till den här tiden. Jag är också säker på att ringa till den här funktionen på den sista raden. Därefter skapar vi ljudvågformen.

Skapa ljudvågan

För att kunna visa frekvensvåg för vår valda ljudkälla behöver vi skapa vågformen.

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

Den första hänvisningen till createAnalyser () exponerar ljudtid och frekvensdata för att generera datavisualiseringar. Denna metod kommer att producera en AnalyserNode som överför ljudströmmen från ingången till utgången, men låter dig förvärva genererade data, bearbeta den och konstruera ljudvisningar som har exakt en ingång och en utgång. Analysernoden kommer att kopplas till mastergin som är utgången från vår signalväg och ger möjlighet att analysera en källa.

const waveform = ny Float32Array (analyser.frequencyBinCount); analyser.getFloatTimeDomainData (vågform);

Detta Float32Array () konstruktorn representerar en uppsättning av ett 32-bitars flytpunktsnummer. De frequencyBinCount egenskapen hos AnalyserNode gränssnittet är ett osignerat långt värde som är halva det för FFT (Fast Fourier Transform) -storleken. Detta motsvarar i allmänhet det antal datavärden du kommer att använda för visualisering. Vi använder denna metod för att samla våra frekvensdata upprepade gånger.

Den slutliga metoden getFloatTimeDomainData kopierar nuvarande vågforms- eller tidsdomändata till en Float32Array array passerade in i den.

funktionuppdateringWaveform () requestAnimationFrame (updateWaveform); analyser.getFloatTimeDomainData (vågform); 

Hela denna mängd data och bearbetning använder requestAnimationFrame () att samla in tidsdomändata upprepat och dra en "oscilloskopstil" -utgång från den nuvarande ljudingången. Jag gör också ett nytt samtal till getFloatTimeDomainData () eftersom det måste uppdateras kontinuerligt eftersom ljudkällan är dynamisk.

const analyzer = audioContext.createAnalyser (); masterGain.connect (analysator); const waveform = ny Float32Array (analyser.frequencyBinCount); analyser.getFloatTimeDomainData (vågform); funktionuppdateringWaveform () requestAnimationFrame (updateWaveform); analyser.getFloatTimeDomainData (vågform); 

Genom att kombinera all kod som diskuterats hittills resulterar hela funktionen ovan. Samtalet till denna funktion kommer att placeras inuti vårt audioSetup funktionen nedanför song.play (). Med vågformen på plats behöver vi fortfarande dra denna information till skärmen med vår duk element, och det här är nästa del av vår diskussion.

Ritning av ljudvågan

Nu när vi har skapat vår vågform och har de data vi behöver, måste vi dra den till skärmen. det här är där duk element införs.

funktion drawOscilloscope () requestAnimationFrame (drawOscilloscope); const scopeCanvas = document.getElementById ("oscilloskop"); const scopeContext = scopeCanvas.getContext ('2d'); 

Koden ovan klarar bara duk element så vi kan referera det till vår funktion. Samtalet till requestAnimationFrame högst upp i denna funktion schemalägger nästa animationsram. Detta placeras först så att vi kan få så nära 60FPS som möjligt.

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

Jag har implementerat grundläggande styling som kommer att dra bredden och höjden på duk. Höjden är inställd på ett absolut värde, medan bredden blir längden på den vågform som produceras av ljudkällan.

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

De clearRect (x, y, bredd, höjd) Metoden kommer att rensa eventuellt tidigare ritat innehåll så att vi kontinuerligt kan rita frekvensdiagrammet. Du måste också se till att ringa beginPath () innan du börjar rita den nya ramen efter att du har ringt clearRect (). Den här metoden startar en ny sökväg genom att tömma listan över alla undervägar. Slutstycket i detta pussel är en slinga för att springa igenom de data vi har erhållit så att vi kontinuerligt kan dra denna frekvensgraf till skärmen.

funktion drawOscilloscope () for (låt 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(); 

Denna slinga ovan drar vår vågform till duk element. Om vi ​​loggar in vågformens längd till konsolen (medan du spelar ljudet) kommer det att rapportera 1024 upprepade gånger. Detta motsvarar i allmänhet det antal datavärden du måste spela med för visualiseringen. Om du kommer ihåg från föregående avsnitt för att skapa vågformen får vi det här värdet Float32Array (analyser.frequencyBinCount). Så här kan vi hänvisa till 1024-värdet som vi ska gå igenom.

De flytta till() Metoden kommer bokstavligen att flytta utgångspunkten för en ny delväg till den uppdaterade (x, y) koordinater. De lineTo () Metoden kopplar den sista punkten i undervägen till x, y koordinater med en rak linje (men faktiskt inte ritar den). Slutstycket ringer stroke() tillhandahålls av duk så vi kan faktiskt dra frekvenslinjen. Jag lämnar den del som innehåller matte som en läsareutmaning, så se till att du skickar ditt svar i kommentarerna nedan.

funktion drawOscilloscope () requestAnimationFrame (drawOscilloscope); const scopeCanvas = document.getElementById ("oscilloskop"); const scopeContext = scopeCanvas.getContext ('2d'); scopeCanvas.width = waveform.length; scopeCanvas.height = 200; scopeContext.clearRect (0, 0, scopeCanvas.width, scopeCanvas.height); scopeContext.beginPath (); för (låt jag = 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(); 

Det här är hela funktionen som vi skapat för att rita den vågform som vi ska ringa efter song.play () placeras inom vårt audioSetup funktion, som också inkluderar vår updateWaveForm funktionssamtal också.

Skilja tankar

Jag har bara förklarat de viktiga bitarna för demo, men se till att läsa igenom andra delar av min demo för att få en bättre förståelse för hur radioknapparna och startknappen fungerar i förhållande till koden ovan, inklusive CSS-styling.

Web Audio API är verkligen kul för alla som är intresserade av ljud av något slag, och jag uppmuntrar dig att gå djupare. Jag har också samlat några riktigt roliga exempel från CodePen som använder Web Audio API för att bygga några riktigt intressanta exempel. Njut av!

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

referenser

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