Dit is de eerste in een reeks zelfstudies waarin we een op synthesizer gebaseerde audiomachine zullen maken die geluiden voor retro-gestileerde games kan genereren. De audio-engine genereert alle geluiden tijdens de uitvoering zonder dat externe afhankelijkheden nodig zijn, zoals MP3-bestanden of WAV-bestanden. Het eindresultaat zal een werkbibliotheek zijn die moeiteloos in uw games kan worden geplaatst.
Voordat we de audio-engine kunnen maken, zijn er een paar dingen die we moeten begrijpen; deze omvatten de golfvormen die de audiomotor zal gebruiken om de hoorbare geluiden te genereren, en hoe geluidsgolven worden opgeslagen en weergegeven in digitale vorm.
De programmeertaal die in deze zelfstudie wordt gebruikt, is ActionScript 3.0, maar de gebruikte technieken en concepten kunnen eenvoudig worden vertaald in elke andere programmeertaal die een low-levelgeluid-API biedt.
U moet ervoor zorgen dat Flash Player 11.4 of hoger is geïnstalleerd voor uw browser als u de interactieve voorbeelden in deze zelfstudie wilt gebruiken.
De audio-engine die we gaan maken, zal vier basisgolfvormen gebruiken (ook bekend als periodiek golfvormen, omdat hun basisvormen periodiek worden herhaald), die allemaal buitengewoon vaak voorkomen in zowel analoge als digitale synthesizers. Elke golfvorm heeft zijn eigen unieke hoorbare karakteristiek.
De volgende secties bieden een visuele weergave van elke golfvorm, een hoorbaar voorbeeld van elke golfvorm en de code die nodig is om elke golfvorm te genereren als een reeks voorbeeldgegevens.
De pulsgolf produceert een scherp en harmonisch geluid.
Om een reeks waarden (in het bereik van -1,0 tot 1,0) te genereren die een pulsgolf voorstellen, kunnen we de volgende code gebruiken n
is het aantal waarden dat vereist is om de array te vullen, een
is de array, en p
is de genormaliseerde positie binnen de golfvorm:
var i: int = 0; var n: int = 100; var p: Number; terwijl ik < n ) p = i / n; a[i] = p < 0.5 ? 1.0 : -1.0; i ++;
De zaagtandgolf produceert een scherp en hard geluid.
Voor het genereren van een reeks waarden (in het bereik van -1,0 tot 1,0) die een zaagtandgolf voorstellen, kunnen we de volgende code gebruiken, waarbij: n
is het aantal waarden dat vereist is om de array te vullen, een
is de array, en p
is de genormaliseerde positie binnen de golfvorm:
var i: int = 0; var n: int = 100; var p: Number; terwijl ik < n ) p = i / n; a[i] = p < 0.5 ? p * 2.0 : p * 2.0 - 2.0; i ++;
De sinusgolf produceert een zacht en zuiver geluid.
Om een reeks waarden (in het bereik van -1,0 tot 1,0) te genereren die een sinusgolf voorstellen, kunnen we de volgende code gebruiken n
is het aantal waarden dat vereist is om de array te vullen, een
is de array, en p
is de genormaliseerde positie binnen de golfvorm:
var i: int = 0; var n: int = 100; var p: Number; terwijl ik < n ) p = i / n; a[i] = Math.sin( p * 2.0 * Math.PI ); i ++;
De driehoeksgolf produceert een vloeiend en harmonisch geluid.
Om een reeks waarden te genereren (in het bereik van -1,0 tot 1,0) die een driehoeksgolf voorstellen, kunnen we de volgende code gebruiken n
is het aantal waarden dat vereist is om de array te vullen, een
is de array, en p
is de genormaliseerde positie binnen de golfvorm:
var i: int = 0; var n: int = 100; var p: Number; terwijl ik < n ) p = i / n; a[i] = p < 0.25 ? p * 4.0 : p < 0.75 ? 2.0 - p * 4.0 : p * 4.0 - 4.0; i ++;
Hier is een uitgebreide versie van regel 6:
if (p < 0.25) a[i] = p * 4.0; else if (p < 0.75) a[i] = 2.0 - (p * 4.0); else a[i] = (p * 4.0) - 4.0;
Twee belangrijke eigenschappen van een geluidsgolf zijn de amplitude en frequentie van de golfvorm: deze dicteren de volume en toonhoogte van het geluid, respectievelijk. De amplitude is eenvoudigweg de absolute piekwaarde van de golfvorm, en de frequentie is het aantal keren dat de golfvorm per seconde wordt herhaald, dat normaal wordt gemeten in Hertz (Hz).
De volgende afbeelding is een 200 milliseconden snapshot van een zaagtandgolfvorm met een amplitude van 0,5 en een frequentie van 20 Hertz:
Om u een idee te geven van hoe de frequentie van een golfvorm direct verband houdt met de toonhoogte van het hoorbare geluid, zou een golfvorm met een frequentie van 440 Hertz dezelfde toonhoogte produceren als de standaard A4-noot (middelste A) op een moderne concertpiano. Met die frequentie in gedachten kunnen we de frequentie van elke notitie berekenen met behulp van de volgende code:
f = Math.pow (2, n / 12) * 440.0;
De n
variabele in die code is het aantal tonen van A4 (middelste A) naar de notitie waarin we geïnteresseerd zijn. Om bijvoorbeeld de frequentie van A5, één octaaf boven A4 te vinden, zouden we de waarde van n
naar 12
omdat A5 12 biljetten boven A4 is. Om de frequentie van E2 te bepalen, zouden we de waarde van instellen n
naar -5
omdat E2 5 tonen onder A4 is. We kunnen ook het omgekeerde doen en een noot (ten opzichte van A4) vinden voor een gegeven frequentie:
n = Math.round (12.0 * Math.log (f / 440.0) * Math.LOG2E);
De reden waarom deze berekeningen werken is omdat nootfrequenties logaritmisch zijn - vermenigvuldiging van een frequentie met twee zetten een noot een octaaf hoger, terwijl een frequentie verdelen door twee een noot één octaaf lager te zetten.
In de digitale wereld moeten geluidsgolven worden opgeslagen als binaire gegevens, en de gebruikelijke manier om dat te doen is om periodieke snapshots (of samples) van een geluidsgolf te maken. Het aantal golfsamples dat voor elke seconde van de duur van een geluid wordt gemaakt, staat bekend als sample snelheid, dus een geluid met een samplefrequentie van 44100 bevat 44100 golfsamples (per kanaal) voor elke seconde van de duur van het geluid.
De volgende afbeelding laat zien hoe een geluidsgolf kan worden gesampled:
De witte blokken in die afbeelding vertegenwoordigen de amplitudepunten van de golf die zijn gesampled en opgeslagen in een digitaal formaat. Je kunt dit zien als de resolutie van een bitmapafbeelding: hoe meer pixels een bitmapafbeelding bevat, des te meer visuele informatie deze kan bevatten en hoe meer informatie resulteert in grotere bestanden (negeer nu bestandscompressie). Hetzelfde geldt voor digitale geluiden: hoe meer golfsamples een geluidsbestand bevat, hoe nauwkeuriger de gereconstrueerde geluidsgolf zal zijn.
Naast een samplefrequentie hebben digitale geluiden ook een bitrate die wordt gemeten in bits per seconde. De bitsnelheid bepaalt hoeveel binaire bits worden gebruikt om elk golfvoorbeeld op te slaan. Dit is vergelijkbaar met het aantal bits dat wordt gebruikt voor het opslaan van ARGB-informatie voor elke pixel in een bitmapafbeelding. Een geluid met een samplefrequentie van 44100 en een bitsnelheid van 705600 zou bijvoorbeeld elk van de golfsamples opslaan als een 16-bits waarde, en we kunnen dat gemakkelijk genoeg berekenen met behulp van de volgende code:
bitsPerSample = bitRate / sampleRate;
Hier is een werkvoorbeeld met de hierboven genoemde waarden:
trace (705600/44100); // "16"
Begrijpen wat geluidssamples zijn, is hier het belangrijkste; de audio-engine die we gaan maken, moet onbewerkte geluidsfragmenten genereren en manipuleren.
Nog iets dat we moeten weten voordat we beginnen met het programmeren van de audio-engine modulators, die extreem veel voorkomen in zowel analoge als digitale synthesizers. Een modulator is in wezen slechts een standaard golfvorm, maar in plaats van dat deze wordt gebruikt om een geluid te produceren, worden ze gewoonlijk gebruikt om een of meer eigenschappen van een hoorbare golfvorm (bijvoorbeeld de amplitude of frequentie ervan) te moduleren.
Nemen vibrato, bijvoorbeeld. Vibrato is een regelmatige pulserende verandering van toonhoogte. Om dat effect met een modulator te produceren, zou u de golfvorm van de modulator op een sinusgolf kunnen instellen en de frequentie van de modulator op ongeveer 8 Hz zetten. Als je vervolgens die modulator hebt aangesloten op de frequentie van een hoorbare golfvorm, zou het resultaat een vibrato-effect zijn - de modulator zou de frequentie (toonhoogte) van de hoorbare golfvorm soepel verhogen en verlagen acht keer per seconde.
Met de audio-engine die we gaan maken, kun je modulators aan je geluiden koppelen, zodat je een groot aantal verschillende effecten kunt produceren.
In de volgende tutorial zullen we de kerncode voor de audio-engine maken en alles in gebruik nemen. Volg ons op Twitter, Facebook of Google+ om op de hoogte te blijven van de laatste berichten.