Web Audio API: po co komponować, skoro można kodować?
Opublikowany: 2022-03-11Pierwszy projekt internetowego API audio pojawił się w W3C w 2011 roku. Chociaż dźwięk na stronach internetowych był obsługiwany przez długi czas, właściwy sposób generowania dźwięku z przeglądarki internetowej nie był dostępny do niedawna. Osobiście przypisuję to Google Chrome, ponieważ w interesie Google przeglądarka zaczęła stawać się najważniejszą częścią komputera. Być może pamiętasz, królestwo przeglądarki internetowej nie zaczęło się zbytnio zmieniać, dopóki nie pojawił się Google Chrome. Jeśli w tym czasie użyłbyś dźwięku na stronie internetowej, byłaby to zła decyzja projektowa. Ale odkąd pojawiła się idea eksperymentów w sieci, audio w sieci znów zaczęło nabierać sensu. Przeglądarki internetowe są obecnie kolejnym narzędziem ekspresji artystycznej, a wideo i audio w przeglądarce internetowej odgrywają w tym istotną rolę.
Web Audio API może być dość trudne w użyciu do niektórych celów, ponieważ jest wciąż w fazie rozwoju, ale wiele bibliotek JavaScript już istnieje, aby ułatwić. W tym przypadku pokażę, jak rozpocząć korzystanie z Web Audio API przy użyciu biblioteki o nazwie Tone.js. Dzięki temu będziesz w stanie zaspokoić większość potrzeb dźwiękowych swojej przeglądarki, poznając tylko podstawy.
Witaj Web Audio API
Pierwsze kroki
Zaczniemy bez korzystania z biblioteki. Nasz pierwszy eksperyment będzie polegał na wykonaniu trzech fal sinusoidalnych. Ponieważ będzie to prosty przykład, utworzymy tylko jeden plik o nazwie hello.html, czysty plik HTML z niewielką ilością znaczników.
<!DOCTYPE html> <html> <head> <meta charset="utf‐8"> <title> Hello web audio </title> </head> <body> </body> <script> </script> </html>
Rdzeniem Web Audio API jest kontekst audio. Kontekst audio to obiekt, który będzie zawierał wszystko, co dotyczy audio w sieci. Posiadanie więcej niż jednego kontekstu audio w jednym projekcie nie jest uważane za dobrą praktykę. Zaczniemy od utworzenia instancji kontekstu audio zgodnie z zaleceniami podanymi w dokumentacji Web Audio API Mozilli.
var audioCtx = new (window.AudioContext || window.webkitAudioContext);
Tworzenie oscylatora
Po utworzeniu instancji kontekstu audio masz już składnik audio: audioCtx.destination. To jest jak twój głośnik. Aby wydobyć dźwięk, musisz podłączyć go do audioCtx.destination. Teraz, aby wydobyć dźwięk, stwórzmy oscylator:
var sine = audioCtx.createOscillator();
Świetnie, ale za mało. Należy go również uruchomić i połączyć z naszym audioCtx.destination:
sine.start(); sine.connect(audioCtx.destination);
Dzięki tym czterem linijkom będziesz mieć dość irytującą stronę internetową, która odtwarza dźwięk sinusoidalny, ale teraz rozumiesz, jak moduły mogą się ze sobą łączyć. W poniższym skrypcie będą trzy ton w kształcie sinus, podłączone do wyjścia, każdy z innym tonem. Kod jest bardzo oczywisty:
//create the context for the web audio var audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //create, tune, start and connect each oscillator sinea, sineb and sinec var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(audioCtx.destination); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(audioCtx.destination); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(audioCtx.destination);
Oscylatory nie są ograniczone do fal sinusoidalnych, ale mogą mieć również kształt trójkątny, piłokształtny, kwadratowy i niestandardowy, jak podano w MDN.
Łatanie logiki Web Audio
Następnie do naszej orkiestry komponentów Web Audio dodamy moduł wzmocnienia. Ten moduł pozwala nam zmieniać amplitudę naszych dźwięków. To jak gałka siły głosu. Wykorzystaliśmy już funkcję connect, aby podłączyć oscylator do wyjścia audio. Możemy użyć tej samej funkcji łączenia do podłączenia dowolnego komponentu audio. Jeśli używasz przeglądarki Firefox i spojrzysz na internetową konsolę audio, zobaczysz następujące informacje:
Jeśli chcemy zmienić głośność, nasz patch powinien wyglądać tak:
Oznacza to, że oscylatory nie są już podłączone do miejsca docelowego audio, ale zamiast tego do modułu wzmocnienia, a ten moduł wzmocnienia jest podłączony do miejsca docelowego. Dobrze jest zawsze wyobrażać sobie, że robisz to za pomocą pedałów gitarowych i kabli. Kod będzie wyglądał tak:
var audioCtx = new (window.AudioContext || window.webkitAudioContext) // we create the gain module, named as volume, and connect it to our var volume = audioCtx.createGain(); volume.connect(audioCtx.destination); //these sines are the same, exept for the last connect statement. //Now they are connected to the volume gain module and not to the au var sinea = audioCtx.createOscillator(); sinea.frequency.value = 440; sinea.type = "sine"; sinea.start(); sinea.connect(volume); var sineb = audioCtx.createOscillator(); sineb.frequency.value = 523.25; sineb.type = "sine"; sineb.start(); sineb.connect(volume); var sinec = audioCtx.createOscillator(); sinec.frequency.value = 698.46; sinec.type = "sine"; sinec.start(); sinec.connect(volume); volume.gain.value=0.2;
Rozwiązanie znajdziesz na https://github.com/autotel/simple-webaudioapi/.
GainNode to najbardziej podstawowa jednostka efektów, ale jest też delay, convolver, filtr biquadratic, stereo panner, wave shaper i wiele innych. Możesz pobierać nowe efekty z bibliotek takich jak Tone.js.
Przechowywanie jednej z tych łatek dźwiękowych we własnych obiektach pozwoli na ich ponowne wykorzystanie w razie potrzeby i tworzenie bardziej złożonych orkiestracji przy mniejszej ilości kodu. To może być temat na przyszły post.
Łatwiejsze rzeczy dzięki Tone.js
Teraz, gdy przyjrzeliśmy się pokrótce, jak działają waniliowe moduły Web Audio, przyjrzyjmy się niesamowitemu frameworkowi Web Audio: Tone.js. Dzięki temu (i NexusUI dla komponentów interfejsu użytkownika) możemy bardzo łatwo zbudować ciekawsze syntezatory i dźwięki. Aby to wypróbować, stwórzmy próbnik i zastosujmy do niego kilka interaktywnych efektów użytkownika, a następnie dodamy kilka prostych kontrolek dla tego przykładu.
Próbnik Tone.js
Możemy zacząć od stworzenia prostej struktury projektu:
simpleSampler |-- js |-- nexusUI.js |-- Tone.js |-- noisecollector_hit4.wav |-- sampler.html
Nasze biblioteki JavaScript będą znajdować się w katalogu js . Na potrzeby tego demo możemy użyć pliku hit4.wav NoiseCollectora, który można pobrać ze strony Freesound.org.

Tone.js udostępnia swoje funkcje poprzez obiekty Player. Podstawową zdolnością obiektu jest załadowanie próbki i odtworzenie jej w pętli lub jednorazowo. Naszym pierwszym krokiem jest utworzenie obiektu odtwarzacza w zmiennej „sampler” w pliku sampler.html:
<!doctype html> <html> <head> <title> Sampler </title> <script type="text/javascript" src="js/nexusUI.js" ></script> <script type="text/javascript" src="js/Tone.js" ></script> <script> var sampler = new Tone.Player("noisecollector_hit4.wav", function() { console.log("samples loaded"); }); </script> </head> <body> </body> </html>
Zauważ, że pierwszy parametr konstruktora odtwarzacza to nazwa pliku WAV, a drugi to funkcja zwrotna. WAV nie jest jedynym obsługiwanym typem pliku, a kompatybilność zależy bardziej od przeglądarki internetowej niż od biblioteki. Funkcja wywołania zwrotnego zostanie uruchomiona, gdy odtwarzacz zakończy ładowanie próbki do swojego bufora.
Do wyjścia musimy również podłączyć nasz sampler. Sposób wykonania tego przez Tone.js to:
sampler.toMaster();
… gdzie sampler to obiekt Tone.Player, po wierszu 10. Funkcja toMaster jest skrótem dla connect(Tone.Master).
Jeśli otworzysz przeglądarkę internetową z otwartą konsolą programisty, powinieneś zobaczyć komunikat „załadowano próbki”, wskazujący, że odtwarzacz został utworzony poprawnie. W tym momencie możesz chcieć usłyszeć próbkę. Aby to zrobić, musimy dodać przycisk do strony internetowej i zaprogramować go do odtwarzania próbki po naciśnięciu. Użyjemy przycisku NexusUI w ciele:
<canvas nx="button"></canvas>
Powinieneś teraz zobaczyć zaokrąglony przycisk renderowany w dokumencie. Aby zaprogramować go do odtwarzania naszej próbki, dodajemy listener NexusUI, który wygląda tak:
button1.on('*',function(data) { console.log("button pressed!"); })
Coś wyjątkowego w NexusUI polega na tym, że tworzy zmienną globalną dla każdego elementu NexusUI. Możesz ustawić NexusUI, aby tego nie robił i zamiast tego miał te zmienne tylko w nx.widgets[], ustawiając nx.globalWidgets na false. Tutaj stworzymy tylko kilka elementów, więc będziemy się trzymać tego zachowania.
Tak samo jak w jQuery, możemy umieścić te zdarzenia .on, a pierwszym argumentem będzie nazwa zdarzenia. Tutaj po prostu przypisujemy funkcję do wszystkiego, co zostanie zrobione z przyciskiem. To wszystko, co jest napisane jako „*”. Możesz dowiedzieć się więcej o zdarzeniach dla każdego elementu w interfejsie API NexusUI. Aby odtworzyć próbkę zamiast rejestrować komunikaty po naciśnięciu przycisku, powinniśmy uruchomić funkcję startową naszego samplera.
nx.onload = function() { button1.on('*',function(data) { console.log("button pressed!"); sampler.start(); }); }
Zauważ też, że słuchacz wchodzi do wywołania zwrotnego onload. Elementy NexusUI są rysowane na kanwie i nie można się do nich odwoływać, dopóki nx nie wywoła funkcji onload. Tak jak w przypadku elementów DOM w jQuery.
Zdarzenie jest wyzwalane po naciśnięciu myszy i po zwolnieniu. Jeśli chcesz, aby było uruchamiane tylko po naciśnięciu, musisz ocenić, czy event.press jest równy jeden.
Dzięki temu powinieneś mieć przycisk, który odtwarza próbkę po każdym naciśnięciu. Jeśli ustawisz sampler.retrigger na true, pozwoli ci to odtworzyć próbkę niezależnie od tego, czy jest odtwarzana, czy nie. W przeciwnym razie musisz poczekać, aż próbka się zakończy, aby ją ponownie uruchomić.
Stosowanie efektów
Dzięki Tone.js możemy łatwo stworzyć opóźnienie:
var delay= new Tone.FeedbackDelay("16n",0.5).toMaster();
Pierwszym argumentem jest czas opóźnienia, który można zapisać w notacji muzycznej, jak pokazano tutaj. Drugi to poziom wet, który oznacza mieszankę oryginalnego dźwięku z dźwiękiem, który ma na niego wpływ. W przypadku opóźnień zwykle nie chcesz mieć 100% mokrego dźwięku, ponieważ opóźnienia są interesujące w odniesieniu do oryginalnego dźwięku, a samo mokre nie jest zbyt atrakcyjne, ponieważ oba razem.
Następnym krokiem jest odłączenie naszego samplera od mastera i podłączenie go do delaya. Dostosuj linię, do której podłączony jest próbnik do wzorca:
sampler.connect(delay);
Teraz spróbuj ponownie przycisk i zobacz różnicę.
Następnie do treści naszego dokumentu dodamy dwie tarcze:
<canvas nx="dial"></canvas> <canvas nx="dial"></canvas>
I stosujemy wartości pokręteł do efektu opóźnienia za pomocą NexusUIlistener:
dial1.on('*',function(data) { delay.delayTime.value=data.value; }) dial2.on('*',function(data) { delay.feedback.value=data.value; })
Parametry, które możesz dostosować przy każdym zdarzeniu, znajdziesz w dokumentacji Tone.js. Na zwłokę jest tutaj. Teraz jesteś gotowy, aby wypróbować przykład i dostosować parametry opóźnienia za pomocą pokręteł NexusUI. Ten proces można łatwo wykonać z każdym elementem NexusUI, nie ograniczając się tylko do efektów. Na przykład spróbuj dodać kolejną tarczę i dodać jej słuchacza w następujący sposób:
dial3.on('*',function(data) { sampler.playbackRate=data.value; })
Możesz znaleźć te pliki na github.com/autotel/simpleSampler
Wniosek
Kiedy przejrzałem te API, zacząłem czuć się przytłoczony wszystkimi możliwościami i pomysłami, które przyszły mi do głowy. Duża różnica między tą implementacją dźwięku a tradycyjnymi implementacjami dźwięku cyfrowego nie tkwi w samym dźwięku, ale w kontekście. Nie ma tu nowych metod syntezy. Raczej innowacją jest to, że tworzenie dźwięku i muzyki spotyka się teraz z technologiami sieciowymi.
Osobiście jestem zaangażowany w tworzenie muzyki elektronicznej, a ten obszar zawsze miał ten paradoks dwuznaczności między rzeczywistym wykonywaniem muzyki a po prostu naciśnięciem odtwarzania do nagranego utworu. Jeśli naprawdę chcesz tworzyć muzykę elektroniczną na żywo, musisz być w stanie stworzyć własne narzędzia performatywne lub „roboty tworzące muzykę” do improwizacji na żywo. Ale jeśli wykonanie muzyki elektronicznej staje się po prostu dostosowywaniem parametrów we wcześniej przygotowanych algorytmach tworzenia muzyki, publiczność również może być zaangażowana w ten proces. Pracowałem nad małymi eksperymentami dotyczącymi integracji sieci i dźwięku dla muzyki z crowdsourcingu i być może wkrótce będziemy uczestniczyć w imprezach, na których muzyka pochodzi od publiczności przez ich smartfony. W końcu nie różni się to aż tak bardzo od rytmicznych zagrywek, którymi moglibyśmy się cieszyć w wiekach jaskiniowych.
Dalsza lektura na blogu Toptal Engineering:
- WebAssembly/Rust Tutorial: Doskonałe przetwarzanie dźwięku
- Samouczek MIDI: tworzenie aplikacji audio opartych na przeglądarce kontrolowanych przez sprzęt MIDI