Zgomot Crearea unui sintetizator pentru efecte de sunet retro - Procesoare audio

Aceasta este ultima parte a seriei de tutoriale despre crearea unui motor audio bazat pe sintetizator care poate fi folosit pentru a genera sunete pentru jocuri retro-stil. Motorul audio poate genera toate sunetele în timpul rulării, fără a fi nevoie de dependențe externe, cum ar fi fișiere MP3 sau fișiere WAV. În acest tutorial, vom adăuga suport pentru procesoarele audio și vom codifica a procesor de întârziere care pot adăuga un efect ecou degradant la sunetele noastre.

Dacă nu ați citit deja primul tutorial sau al doilea tutorial din această serie, ar trebui să faceți acest lucru înainte de a continua.

Limba de programare folosită în acest tutorial este ActionScript 3.0, dar tehnicile și conceptele folosite pot fi traduse cu ușurință în orice alt limbaj de programare care oferă un API de sunet de nivel scăzut.

Ar trebui să vă asigurați că aveți instalat browserul dvs. Flash Player 11.4 sau superior dacă doriți să utilizați exemplele interactive din acest tutorial.


Procesor Audio Demo

În acest tutorial final vom adăuga procesoare audio la motorul de bază și crearea unui procesor simplu de întârziere. Următoarea demonstrație arată procesorul de întârziere în acțiune:

Un singur sunet se joacă în acea demonstrație, dar frecvența sunetului este randomizată, iar probele audio generate de motor sunt împinse printr-un procesor de întârziere, care îi dă efectul ecou degradant.


AudioProcessor Clasă

Primul lucru pe care trebuie să-l facem este să creați o clasă de bază pentru procesoarele audio:

pachetul de zgomot public class AudioProcessor // public var enabled: Boolean = true; // funcția publică AudioProcessor () if (Object (this) .constructor == AudioProcessor) aruncă o nouă eroare ("Clasa AudioProcessor trebuie extinsă");  // proces de funcționare internă (eșantioane: Vector. ): void 

După cum puteți vedea, clasa este foarte simplă; conține o interfață internă proces() metoda invocată de AudioEngine clasa ori de câte ori este necesar să fie procesate probe și publicul activat proprietate care poate fi utilizată pentru a porni și opri procesorul.


AudioDelay Clasă

AudioDelay clasa este clasa care creează de fapt întârzierea audio și extinde AudioProcessor clasă. Aici este clasa de bază goală cu care vom lucra:

pachetul de zgomot public class AudioDelay extinde AudioProcessor // funcția publică AudioDelay (time: Number = 0.5) this.time = time; 

timp argumentul transmis constructorului de clasă este timpul (în secunde) al atingerii de întârziere - adică perioada de timp dintre fiecare întârziere audio.

Acum, să adăugăm proprietățile private:

privat var m_buffer: Vector. = Vector nou.(); privat var m_bufferSize: int = 0; privat var m_bufferIndex: int = 0; privat var m_time: Number = 0.0; privat var m_gain: număr = 0.8;

m_buffer vectorul este, în principiu, o buclă de feedback: conține toate eșantioanele audio transmise către proces , iar acele probe sunt modificate (în acest caz reduse în amplitudine) în mod continuu ca m_bufferIndex trece prin tampon. Acest lucru va avea sens când ajungem la proces() metodă.

m_bufferSize și m_bufferIndex proprietățile sunt utilizate pentru a urmări starea tamponului. m_time proprietatea este momentul atingerii întârzierii, în secunde. m_gain proprietatea este un multiplicator care este utilizat pentru a reduce amplitudinea probelor audio tamponate în timp.

Această clasă are doar o singură metodă, și anume cea internă proces() metodă care are prioritate proces() metodă în AudioProcessor clasă:

procedură internă de suprascriere (eșantioane: Vector. ): void var i: int = 0; var n: int = samples.length; var v: Număr = 0,0; // in timp ce eu < n )  v = m_buffer[m_bufferIndex]; // grab a buffered sample v *= m_gain; // reduce the amplitude v += samples[i]; // add the fresh sample // m_buffer[m_bufferIndex] = v; m_bufferIndex++; // if( m_bufferIndex == m_bufferSize )  m_bufferIndex = 0;  // samples[i] = v; i++;  

În cele din urmă, trebuie să adăugăm căștile / setterii pentru cei privați m_time și m_gain proprietăţi:

funcția publică primește timp (): Număr return m_time;  timpul de setare a funcției publice (valoare: Număr): void // fixați timpul până la intervalul 0.0001 - 8.0 valoarea = valoarea < 0.0001 ? 0.0001 : value > 8,0? 8.0: valoare; // nu trebuie să modificați dimensiunea bufferului dacă timpul nu sa schimbat dacă (m_time == value) return;  // setați ora m_time = valoare; // actualizați dimensiunea bufferului m_bufferSize = Math.floor (44100 * m_time); m_buffer.length = m_bufferSize; 
funcția publică câștigă (): Numărul return m_gain;  câștig stabilit în funcția publică (valoare: număr): void // fixarea câștigului în intervalul 0.0 - 1.0 m_gain = valoare < 0.0 ? 0.0 : value > 1,0? 1.0: valoare; 

Credeți sau nu, că este AudioDelay clasa finalizată. Întârzierile audio sunt de fapt foarte ușoare odată ce ați înțeles modul în care buclă de feedback ( m_buffer proprietate).


Actualizarea mesajului AudioEngine Clasă

Ultimul lucru pe care trebuie să-l facem este actualizarea AudioEngine astfel încât procesoarele audio pot fi adăugate la acesta. Mai întâi, să adăugăm un vector pentru stocarea instanțelor procesorului audio:

static privat var m_procesorList: Vector. = Vector nou.();

Pentru a adăuga și a elimina procesoarele de la și de la AudioEngine vom folosi două metode publice:

AudioEngine.addProcessor ()

funcția publică statică addProcessor (procesor: AudioProcessor): void if (m_processorList.indexOf (procesor) == -1) m_processorList.push (procesor); 

AudioEngine.removeProcessor ()

funcția publică statică removeProcesor (procesor: AudioProcesor): void var i: int = m_procesorList.indexOf (procesor); dacă (i! = -1) m_processorList.splice (i, 1); 

Destul de ușor - toate aceste metode sunt de adăugare și eliminare AudioProcessor instanțe către sau de la m_processorList vector.

Ultima metodă pe care o vom adăuga în lista procesoarelor audio și, dacă procesorul este activat, va transmite eșantioane audio la procesoare proces() metodă:

procedura statică a funcției private Exemple (): void var i: int = 0; var n: int = m_procesorList.Lungime; // in timp ce eu < n )  if( m_processorList[i].enabled )  m_processorList[i].process( m_sampleList );  i++;  

Acum este timpul să adăugăm bitul final al codului și aceasta este o singură linie de cod care trebuie adăugată în privat onSampleData () metodă în AudioEngine clasă:

dacă (m_soundChannel == null) în timp ce (i < n )  b.writeFloat( 0.0 ); b.writeFloat( 0.0 ); i++;  return;  // generateSamples(); processSamples(); // while( i < n )  s = m_sampleList[i] * m_amplitude; b.writeFloat( s ); b.writeFloat( s ); m_sampleList[i] = 0.0; i++; 

Linia evidențiată de cod este cea care trebuie adăugată la clasă; pur și simplu invocă processSamples () metodă pe care am adăugat-o anterior.


Concluzie

Asta, așa cum se spune, este asta. În primul tutorial am aruncat o privire asupra diferitelor forme de undă și asupra modului în care sunetul sonor este stocat digital, apoi am construit codul de bază al motorului audio în cel de-al doilea tutorial și acum am încheiat lucrurile cu adăugarea de procesoare audio.

Există mult mai multe lucruri care pot fi făcute cu acest cod sau cu o variantă a acestui cod, dar este important să țineți cont de cantitatea de lucru pe care un motor audio trebuie să o facă în timpul execuției. Dacă împingeți un motor audio prea departe (și acest lucru este ușor de făcut), atunci performanța generală a jocului dvs. poate suferi ca o consecință - chiar dacă mutați un motor audio în firul său propriu (sau muncitor ActionScript 3.0), va fi încă fericit mușcați bucăți din procesor dacă nu sunteți atenți.

Cu toate acestea, o mulțime de jocuri profesionale și nu atât de profesioniste fac o mulțime de procesare audio la timpul de execuție, deoarece au efecte sonore dinamice și muzică într-un joc poate adăuga o mulțime la experiența generală și poate atrage jucătorul mai adânc în joc lume. Motorul audio pe care l-am construit în această serie de tutoriale ar putea funcționa la fel de ușor cu eșantioane normale (non-generate) de efect de sunet încărcate din fișiere: în esență, toate audio digitale reprezintă o secvență de mostre în cea mai de bază formă.

Un ultim lucru de gândit: audio-ul este un aspect foarte important al designului jocului, este la fel de important și puternic ca și aspectul vizual al lucrurilor și nu este ceva care ar trebui să fie aruncat împreună sau înșurubat într-un joc în ultimul moment al dacă îți pasă foarte mult de calitatea producției jocurilor tale. Luați-vă timp cu designul audio pentru jocurile dvs. și veți culege recompensele.

Sper că vă bucurați de această serie de tutoriale și puteți lua ceva pozitiv departe de ea: chiar dacă vă gândiți doar la audio în jocurile dvs. mai mult de acum înainte, atunci mi-am făcut treaba.

Întregul cod sursă al motorului audio este disponibil în descărcarea sursei.

A se distra!