Aplicația Web Audio API este un aliat puternic pentru oricine creează jocuri JavaScript, dar cu această putere vine complexitatea. Web Audio este un sistem modular; nodurile audio pot fi conectate împreună pentru a forma grafice complexe pentru a gestiona totul, de la redarea unui singur sunet până la o aplicație complet secvențiată de muzică. Acest lucru este impresionant, cel puțin.
Cu toate acestea, când vine vorba de programarea jocurilor, majoritatea dezvoltatorilor doresc un API de bază care să încarce pur și simplu sunete și oferă opțiuni pentru schimbarea volumului, pasului și poziției stereo a acestor sunete. Acest tutorial oferă o soluție elegantă prin împachetarea API-ului Web Audio într-o aplicație rapidă și ușoară Sunet
clasa care se ocupă de toate pentru tine.
Notă: Acest tutorial vizează în primul rând programatorii JavaScript, dar tehnicile folosite pentru a amesteca și manipula audio în cod pot fi aplicate în aproape orice mediu de programare care oferă acces la un API de sunet de nivel scăzut.
Înainte de a începe, aruncăm o privire la demo-ul live al lui Sunet
clasă în acțiune. Puteți să faceți clic pe butoanele din demo pentru a reda sunete:
SFX 01
este un sunet single-shot cu setări implicite. SFX 02
este un sunet single-shot care are poziția sa pan (poziția stereo) și volumul este randomizat de fiecare dată când este redat. SFX 03
este un sunet cu buclă; dând clic pe butonul va activa și dezactiva sunetul, iar poziția cursorului mouse-ului în cadrul butonului va regla pitch-ul sunetului.Notă: Dacă nu auziți niciun sunet care este redat, browserul web pe care îl utilizați nu suportă fluxurile audio Web Audio API sau OGG Vorbis. Utilizarea Chrome sau Firefox ar trebui să rezolve problema.
Următoarea imagine reprezintă un grafic de bază al nodului Web Audio:
Exemplu vizual al unui grafic de nod audio Web.După cum puteți vedea, există câteva noduri audio în grafic pentru a face față redării a patru sunete într-un mod potrivit pentru jocuri. Nivelele de panner și câștig se ocupă de panning și volum și există câteva noduri de compresie dinamice pentru a preveni orice artefacte auditive (clipuri, pop-uri și așa mai departe) dacă graficul se termină prin supraîncărcarea cu sonor puternic.
Fiind capabil să creeze grafice de noduri audio, cum ar fi cele din JavaScript, este minunat, dar trebuie să creați, să conectați și să deconectați în mod constant acele noduri poate deveni o povară reală. Vom simplifica lucrurile prin manipularea programabilă a amestecării și manipulării audio, utilizând un singur nod de procesor de script.
Exemplul vizual al unui grafic simplificat al nodului audio Web.Da, asta este cu siguranță mult mai simplu și, de asemenea, evită operațiunile de procesare implicate în crearea, conectarea și deconectarea unei sarcini de noduri audio de fiecare dată când trebuie să fie redat un sunet.
Există și alte întrebări în API-ul Web Audio, care pot face lucrurile dificile. Nodul panner, de exemplu, este conceput special pentru sunete poziționate în spațiul 3D, nu în spațiul 2D, iar nodurile sursă tampon audio (etichetate "sunet" în imaginea anterioară) pot fi redate numai o singură dată, de aici necesitatea de a crea în mod constant și conectați acele tipuri de noduri.
Nodul procesorului de script unic folosit de către Sunet
clasa cere periodic eșantioane de sunet să fie transmise de la JavaScript, și acest lucru face lucrurile mult mai ușor pentru noi. Putem amesteca și manipula probele de sunet foarte rapid și ușor în JavaScript, pentru a produce funcția de volum, pitch și panning de care avem nevoie pentru jocuri 2D.
În loc să coboare copilul prin crearea unui Sunet
, vom examina părțile principale ale codului care sunt direct legate de API-ul Web Audio și de manipularea probelor de sunet. Fișierele sursă demo includ funcția complet funcțională Sunet
de clasă, pe care le puteți studia și utiliza în mod liber în proiectele proprii.
Sunet
clasa încarcă fișierele audio pe o rețea ca tampoane de array utilizând XMLHttpRequest
obiecte. Tampoanele de matrice sunt apoi decodate în probe de sunet prime printr-un obiect context audio.
request.open ("GET", "sound.ogg"); request.onload = decode; request.responseType = "arraybuffer"; request.open (); funcția decode () if (request.response! == null) audioContext.decodeAudioData (request.response, done); funcția terminată (audioBuffer) ...
Evident, nu există nici o manipulare a erorilor în acel cod, dar demonstrează modul în care fișierele de sunet sunt încărcate și decodificate. audioBuffer
trecut la Terminat()
funcția conține probele de sunet brute din fișierul de sunet încărcat.
Pentru a amesteca și manipula probele de sunet încărcate, Sunet
class atribuie un ascultător unui nod de procesor de script. Acest ascultător va fi sunat periodic pentru a solicita mai multe probe de sunet.
// Calculați mărimea tamponului. // Aceasta va produce o valoare sensibilă care echilibrează // latența audio și utilizarea procesorului pentru jocurile care rulează la 60 Hz. var v = audioContext.sampleRate / 60; var n = 0; în timp ce (v> 0) v >> = 1; n ++; v = Math.pow (2, n); // buffer size // Crearea procesorului de script. procesor = audioContext.createScriptProcessor (v); // Atașați ascultătorul. processor.onaudioprocess = proceseSamples; funcția procesSamples (eveniment) ...
Frecvența la care se aplică processSamples ()
funcția este numită va varia în funcție de setările hardware diferite, dar de obicei este de aproximativ 45 de ori pe secundă. Acest lucru poate părea o mulțime, dar este necesar să păstrați latența audio suficient de mică pentru a fi utilizabilă în jocurile moderne, care de obicei rulează la 60 de cadre pe secundă. Dacă latența audio este prea mare, sunetele vor fi auzite prea târziu pentru a se sincroniza cu ceea ce se întâmplă pe ecran și ar fi o experiență jignitoare pentru oricine care joacă un joc.
În ciuda frecvenței cu care processSamples ()
funcția este numită, utilizarea procesorului rămâne scăzută, deci nu vă faceți griji că prea mult timp este luat de logica jocului și redare. Pe hardware-ul meu (Intel Core i3, 3 GHz), utilizarea CPU rar depășește 2%, chiar și atunci când sunt redate simultan multe sunete.
processSamples ()
funcția conține de fapt carnea din Sunet
clasă; este locul în care probele de sunet sunt amestecate și manipulate înainte de a fi împinse prin intermediul web audio-ului către hardware. Următorul cod demonstrează ce se întâmplă în interiorul funcției:
/ / Prindeți proba de sunet. sampleL = samplesL [soundPoziția >> 0]; probaR = samplesR [soundPosition >> 0]; // Măriți poziția capului de joc al sunetului. soundPosition + = soundScale; // Aplicați volumul global (afectează toate sunetele). sampleL * = globalVolume; sampleR * = globalVolume; // Aplicați volumul sunetului. sampleL * = soundVolume; sampleR * = soundVolume; / / Aplicați panoul de sunet (poziția stereo). sampleL * = 1.0 - soundPan; sampleR * = 1.0 + soundPan;
E mai mult sau mai puțin tot ce există. Aceasta este magia: o mână de operații simple schimbă volumul, poziția și poziția stereo a unui sunet.
Dacă sunteți un programator și sunteți familiarizați cu acest tip de procesare a sunetului, s-ar putea să vă gândiți "că nu poate fi tot ceea ce este acolo" și ați fi corecți: clasa de sunet trebuie să țină evidența instanțelor de sunet, faceți alte câteva lucruri, dar asta este totul!
Următorul cod demonstrează modul de utilizare a clasei de sunet. De asemenea, puteți descărca fișierele sursă pentru demonstrația live care însoțește acest tutorial.
// Creați câteva obiecte de sunet. boom var = sunet nou ("boom.ogg"); var tick = sunet nou ("tick.ogg"); // Transferați opțional un ascultător la clasa de sunet. Sound.setListener (ascultător); // Aceasta va încărca orice obiecte de sunet nou create. Sound.load (); // Ascultătorul. funcția ascultător (sunet, stare) if (state === Sound.State.LOADED) if (sunet === bifați) setInterval (playTick, 1000); altfel dacă (sound === boom) setInterval (playBoom, 4000); altfel dacă (state === Sound.State.ERROR) console.warn ("Eroare de sunet:% s", sound.getPath ()); / / Redă sunetul de bifurcat. funcția playTick () tick.play (); / / Redă sunetul brațului. funcția playBoom () boom.play (); // Randomizați volumul și intensitatea sunetului. boom.setScale (0,8 + 0,4 * Math.random ()); volumul volumului (0.2 + 0.8 * Math.random ());
Simplu și ușor.
Un lucru de reținut: nu contează dacă API-ul Web Audio nu este disponibil într-un browser și nu contează dacă browserul nu poate reda un anumit format de sunet. Puteți continua să apelați Joaca()
și Stop()
funcții pe Sunet
obiect fără a fi aruncate erori. Aceasta este intenționată; vă permite să rulați codul de joc ca de obicei, fără a vă îngrijora probleme legate de compatibilitatea browserului sau ramificați codul pentru a rezolva aceste probleme. Cel mai rău lucru care se poate întâmpla este tăcerea.
Joaca()
Stop()
getPath ()
: Creează calea fișierului de sunet.getState ()
getPan ()
setPan (valoare)
: Setează panoul de sunet (poziția stereo).getScale ()
setScale (valoare)
: Setează scala sunetului (pitch).getVolume ()
setVolume (valoare)
: Setează volumul sunetului.este in asteptare()
se incarca()
isLoaded ()
isLooped ()
Clasa de sunet conține, de asemenea, următoarele funcții statice.
sarcină()
: Încarcă sunetele nou create.Stop()
: Opreste toate sunetele.getVolume ()
setVolume (valoare)
: Setează volumul global (master).getListener ()
setListener (valoare)
: Ține evidența progresului încărcării sonore etc.canPlay (format)
: Verifică dacă pot fi redate diferite formate de sunet.Documentația poate fi găsită în codul sursă al demo-ului.
Redarea efectelor de sunet într-un joc JavaScript ar trebui să fie simplă, iar acest tutorial o face astfel prin împachetarea puternicului API Web Audio într-o clasă de sunet rapidă și ușoară, care gestionează totul pentru tine.
Dacă sunteți interesat să aflați mai multe despre eșantioanele de sunet și cum să le manipulați, am scris o serie de Tuts + care ar trebui să vă țină ocupați pentru o vreme ...
Următoarele linkuri sunt pentru specificațiile standardizate W3C și Khronos care sunt direct legate de API-ul Web Audio: