Buller Skapa en synthesizer för Retro Sound Effects - Ljud Processorer

Det här är den sista delen i vår serie av handledning om att skapa en synthesizerbaserad ljudmotor som kan användas för att generera ljud för retrostilade spel. Ljudmotorn kan generera alla ljud vid körning utan att behöva några externa beroenden, som MP3-filer eller WAV-filer. I denna handledning lägger vi till stöd för ljudprocessorer och kodar a fördröjningsprocessor vilket kan lägga till en förfallande ekoffekt för våra ljud.

Om du inte redan har läst den första handledningen eller den andra handledningen i denna serie borde du göra det innan du fortsätter.

Programmeringsspråket som används i denna handledning är ActionScript 3.0, men de använda teknikerna och koncepten kan enkelt översättas till något annat programmeringsspråk som tillhandahåller en låg ljud API.

Du bör se till att du har Flash Player 11.4 eller senare installerat för din webbläsare om du vill använda de interaktiva exemplen i denna handledning.


Audio Processor Demo

I den här sista handledningen kommer vi att lägga till ljudprocessorer till kärnmotorn och skapa en enkel fördröjningsprocessor. Följande demonstration visar fördröjningsprocessorn i åtgärd:

Endast ett ljud spelas i den demonstrationen men ljudets frekvens slås ihop och ljudproverna som genereras av motorn drivs genom en fördröjningsprocessor vilket ger den den förfallna eko-effekten.


ljudprocessor Klass

Det första vi behöver göra är att skapa en basklass för ljudprocessorerna:

paketljud public class AudioProcessor // public var aktiverat: Boolean = true; // public function AudioProcessor () if (Object (this) .constructor == AudioProcessor) kasta nytt fel ("AudioProcessor-klassen måste utökas");  // intern funktionsprocess (prover: Vector. ): void 

Som du kan se är klassen väldigt enkel; Den innehåller en intern bearbeta() metod som åberopas av Audioengine klass närhelst några prover behöver behandlas och en allmänhet aktiverad egenskap som kan användas för att slå processorn på och av.


audiodelay Klass

De audiodelay klassen är den klass som faktiskt skapar ljudfördröjningen, och den förlänger ljudprocessor klass. Här är den grundläggande tomma klassen som vi ska arbeta med:

paketljud offentlig klass AudioDelay förlänger AudioProcessor // offentlig funktion AudioDelay (tid: Number = 0.5) this.time = time; 

De tid argument som skickas till klasskonstruktorn är tiden (i sekunder) för fördröjningskranen - det vill säga tiden mellan varje ljudfördröjning.

Låt oss nu lägga till de privata egenskaperna:

privat var m_buffer: vektor. = ny vektor.(); privat var m_bufferSize: int = 0; privat var m_bufferIndex: int = 0; privat var m_time: Number = 0.0; privat var m_gain: nummer = 0,8;

De m_buffer vektorn är i princip en återkopplingsslinga: den innehåller alla ljudprover som skickas till bearbeta metod, och dessa prover modifieras (i detta fall reduceras i amplitud) kontinuerligt som m_bufferIndex passerar genom bufferten. Detta kommer att vara vettigt när vi kommer till bearbeta() metod.

De m_bufferSize och m_bufferIndex Egenskaper används för att hålla koll på buffertens tillstånd. De m_time egenskapen är tiden för fördröjningen, i sekunder. De m_gain egenskapen är en multiplikator som används för att minska amplituden hos de buffrade ljudproverna över tiden.

Denna klass har bara en metod, och det är det interna bearbeta() metod som strider mot bearbeta() metod i ljudprocessor klass:

funktion för intern överstyrning (prov: Vector. ): void var i: int = 0; var n: int = samples.length; var v: nummer = 0,0; // medan jag < 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++;  

Slutligen måste vi lägga till getters / setters för den privata m_time och m_gain egenskaper:

allmän funktion få tid (): Nummer return m_time;  public function set time (värde: Number): void // klämma tiden till intervallet 0.0001 - 8.0 värde = värde < 0.0001 ? 0.0001 : value > 8,0? 8,0: värde; // Inget behov av att ändra buffertstorleken om tiden inte har ändrats om (m_time == värde) return;  // ställa in tiden m_time = värde; // uppdatera buffertstorleken m_bufferSize = Math.floor (44100 * m_time); m_buffer.length = m_bufferSize; 
offentlig funktion få gain (): Number return m_gain;  public function set gain (värde: Number): void // klämma förstärkningen till intervallet 0.0 - 1.0 m_gain = value < 0.0 ? 0.0 : value > 1,0? 1,0: värde; 

Tro eller inte, det är audiodelay klassen avslutad. Ljudförseningar är faktiskt mycket enkla när du förstår hur återkopplingsslingan (the m_buffer egendom) fungerar.


Uppdatering av Audioengine Klass

Det sista vi behöver göra är att uppdatera Audioengine klass så kan ljudprocessorer läggas till det. Låt oss först och främst lägga till en vektor för att lagra ljudprocessorns instanser:

statisk privat var m_processorList: Vector. = ny vektor.();

Att faktiskt lägga till och ta bort processorer till och från Audioengine klass kommer vi att använda två offentliga metoder:

Audioengine.addProcessor ()

statisk offentlig funktion addProcessor (processor: AudioProcessor): void if (m_processorList.indexOf (processor) == -1) m_processorList.push (processor); 

Audioengine.removeProcessor ()

statisk offentlig funktion removeProcessor (processor: AudioProcessor): void var i: int = m_processorList.indexOf (processor); om (i! = -1) m_processorList.splice (i, 1); 

Lätt nog - alla dessa metoder är att lägga till och ta bort ljudprocessor instanser till eller från m_processorList vektor.

Den sista metoden som vi lägger till rullar via listan över ljudprocessorer och, om processorn är aktiverad, skickar ljudprover till processorns bearbeta() metod:

statisk privatfunktionsprocessSamples (): void var i: int = 0; var n: int = m_processorList.length; // medan jag < n )  if( m_processorList[i].enabled )  m_processorList[i].process( m_sampleList );  i++;  

Nu är det dags att lägga till den sista koden, och det här är en enda kodkod som måste läggas till den privata onSampleData () metod i Audioengine klass:

om (m_soundChannel == null) medan (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++; 

Den markerade koden är den som måste läggas till i klassen. det åberopar helt enkelt processSamples () metod som vi tidigare lagt till.


Slutsats

Det är, som de säger. I den första handledningen tog vi en titt på olika vågformar och hur ljudvågor lagras digitalt, då byggde vi kärnljudmotorkoden i den andra handledningen och nu har vi förpackat saker med tillägg av ljudprocessorer.

Det finns mycket mer som kan göras med den här koden eller en variant av den här koden, men det viktiga att tänka på är hur mycket arbete en ljudmotor måste göra vid körning. Om du trycker på en ljudmotor för långt (och det är lätt att göra) kan din spelets övergripande prestanda leda till följd - även om du flyttar en ljudmotor till en egen tråd (eller ActionScript 3.0-arbetare) kommer det ändå lyckligtvis bita bitar av CPU om du inte är försiktig.

Många professionella och icke-professionella spel gör emellertid mycket ljudbehandling vid körning eftersom det finns dynamiska ljudeffekter och musik i ett spel kan lägga mycket till den övergripande upplevelsen och det kan dra spelaren djupare in i spelet värld. Den ljudmotor vi lägger ihop i denna serie av handledning kan lika enkelt arbeta med vanliga (icke genererade) ljudeffektprover som laddas från filer: huvudsakligen är allt digitalt ljud en sekvens av prover i sin mest grundläggande form.

En sista sak att tänka på: ljud är en mycket viktig aspekt av speldesign, den är lika viktig och kraftfull som den visuella sidan av saker och det är inte något som ska kastas ihop eller bultas på ett spel i sista minuten av utveckling om du verkligen bryr dig om produktionskvaliteten på dina spel. Ta dig tid med ljuddesignen för dina spel och du kommer att skörda belöningarna.

Jag hoppas att du njöt av den här serien av handledning och kan ta något positivt bort från det: även om du bara tänker på ljudet i dina spel lite mer från och med då har jag gjort mitt jobb.

Alla ljudkällkodar är tillgängliga i källnedladdning.

Ha så kul!