Questo è il primo di una serie di tutorial in cui creeremo un motore audio basato su un sintetizzatore in grado di generare suoni per giochi in stile retrò. Il motore audio genererà tutti i suoni in fase di esecuzione senza la necessità di alcuna dipendenza esterna come file MP3 o file WAV. Il risultato finale sarà una libreria funzionante che può essere facilmente spostata nei tuoi giochi.
Prima di iniziare a creare il motore audio ci sono alcune cose che dovremo capire; questi includono le forme d'onda che il motore audio utilizzerà per generare i suoni udibili e come le onde sonore sono memorizzate e rappresentate in forma digitale.
Il linguaggio di programmazione utilizzato in questo tutorial è ActionScript 3.0 ma le tecniche e i concetti utilizzati possono essere facilmente tradotti in qualsiasi altro linguaggio di programmazione che fornisce un'API audio di basso livello.
Assicurati di aver installato Flash Player 11.4 o versioni successive per il tuo browser se desideri utilizzare gli esempi interattivi in questo tutorial.
Il motore audio che creeremo utilizzerà quattro forme d'onda di base (anche conosciute come periodico forme d'onda, poiché le loro forme di base si ripetono periodicamente), tutte estremamente comuni sia nei sintetizzatori analogici che digitali. Ogni forma d'onda ha la sua caratteristica udibile unica.
Le seguenti sezioni forniscono una rappresentazione visiva di ogni forma d'onda, un esempio udibile di ogni forma d'onda e il codice richiesto per generare ogni forma d'onda come una matrice di dati di esempio.
L'onda del polso produce un suono acuto e armonico.
Per generare una matrice di valori (nell'intervallo da -1,0 a 1,0) che rappresentano un'onda di impulso, possiamo usare il seguente codice, dove n
è il numero di valori richiesti per popolare la matrice, un
è la matrice, e p
è la posizione normalizzata all'interno della forma d'onda:
var i: int = 0; var n: int = 100; var p: numero; mentre io < n ) p = i / n; a[i] = p < 0.5 ? 1.0 : -1.0; i ++;
L'onda a dente di sega produce un suono acuto e aspro.
Per generare una matrice di valori (nell'intervallo da -1,0 a 1,0) che rappresenta un'onda a dente di sega possiamo usare il seguente codice, dove n
è il numero di valori richiesti per popolare la matrice, un
è la matrice, e p
è la posizione normalizzata all'interno della forma d'onda:
var i: int = 0; var n: int = 100; var p: numero; mentre io < n ) p = i / n; a[i] = p < 0.5 ? p * 2.0 : p * 2.0 - 2.0; i ++;
L'onda sinusoidale produce un suono fluido e puro.
Per generare una matrice di valori (nell'intervallo da -1,0 a 1,0) che rappresentano un'onda sinusoidale, possiamo usare il seguente codice, dove n
è il numero di valori richiesti per popolare la matrice, un
è la matrice, e p
è la posizione normalizzata all'interno della forma d'onda:
var i: int = 0; var n: int = 100; var p: numero; mentre io < n ) p = i / n; a[i] = Math.sin( p * 2.0 * Math.PI ); i ++;
L'onda triangolare produce un suono armonioso e armonioso.
Per generare una matrice di valori (nell'intervallo da -1,0 a 1,0) che rappresentano un'onda triangolare possiamo usare il seguente codice, dove n
è il numero di valori richiesti per popolare la matrice, un
è la matrice, e p
è la posizione normalizzata all'interno della forma d'onda:
var i: int = 0; var n: int = 100; var p: numero; mentre io < 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 ++;
Ecco una versione estesa della riga 6:
se (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;
Due importanti proprietà di un'onda sonora sono le ampiezza e frequenza della forma d'onda: questi dettano il volume e intonazione del suono, rispettivamente. L'ampiezza è semplicemente il valore di picco assoluto della forma d'onda e la frequenza è il numero di volte in cui la forma d'onda viene ripetuta al secondo, che è normalmente misurata in hertz (Hz).
L'immagine seguente è un'istantanea di 200 millisecondi di una forma d'onda a dente di sega con un'ampiezza di 0,5 e una frequenza di 20 hertz:
Per darti un'idea di come la frequenza di una forma d'onda sia direttamente correlata al tono del suono udibile, una forma d'onda con una frequenza di 440 hertz produrrebbe la stessa altezza della nota standard A4 (centro A) su un pianoforte da concerto moderno. Con questa frequenza in mente, siamo in grado di calcolare la frequenza di ogni nota usando il seguente codice:
f = Math.pow (2, n / 12) * 440.0;
Il n
variabile in quel codice è il numero di note da A4 (medio A) alla nota a cui siamo interessati. Ad esempio, per trovare la frequenza di A5, un'ottava sopra A4, dovremmo impostare il valore di n
a 12
perché A5 è 12 note sopra A4. Per trovare la frequenza di E2, impostiamo il valore di n
a -5
perché E2 è 5 note sotto A4. Possiamo anche fare il contrario e trovare una nota (relativa ad A4) per una determinata frequenza:
n = Math.round (12.0 * Math.log (f / 440.0) * Math.LOG2E);
Il motivo per cui funzionano questi calcoli è perché le frequenze delle note sono logaritmiche - moltiplicando una frequenza per due si sposta una nota su una singola ottava, mentre dividendo una frequenza per due si sposta una nota lungo una singola ottava.
Nel mondo digitale, le onde sonore devono essere memorizzate come dati binari, e il modo comune per farlo è di acquisire istantanee (o campioni) periodici di un'onda sonora. Il numero di campioni d'onda presi per ogni secondo della durata di un suono è noto come frequenza di campionamento, quindi un suono con una frequenza di campionamento di 44100 conterrà 44100 campioni d'onda (per canale) per ogni secondo della durata del suono.
L'immagine seguente mostra come è possibile campionare un'onda sonora:
I blob bianchi in quell'immagine rappresentano i punti di ampiezza dell'onda che sono campionati e memorizzati in un formato digitale. Si può pensare a questo come alla risoluzione di un'immagine bitmap: più pixel un'immagine bitmap contiene più informazioni visive che può contenere, e più informazioni si traducono in file più grandi (per ora ignora la compressione dei file). Lo stesso vale per i suoni digitali: più campioni d'onda contengono un file audio, più accurata sarà l'onda sonora ricostruita.
Oltre ad avere una frequenza di campionamento, i suoni digitali hanno anche un bit rate che viene misurato in bit al secondo. La velocità in bit determina quanti bit binari vengono utilizzati per memorizzare ogni campione di onda. È simile al numero di bit utilizzati per archiviare le informazioni ARGB per ciascun pixel in un'immagine bitmap. Ad esempio, un suono con una frequenza di campionamento di 44100 e una velocità in bit di 705600 memorizzerebbe ciascuno dei suoi campioni d'onda come un valore a 16 bit, e possiamo calcolarlo abbastanza facilmente usando il seguente codice:
bitsPerSample = bitRate / sampleRate;
Ecco un esempio di lavoro che utilizza i valori sopra menzionati:
traccia (705600/44100); // "16"
Capire quali sono i campioni sonori è la cosa più importante qui; il motore audio che creeremo dovrà generare e manipolare campioni di suoni grezzi.
Un'altra cosa di cui dovremmo essere a conoscenza prima di iniziare a programmare il motore audio lo sono modulatori, che sono estremamente comuni sia nei sintetizzatori analogici che digitali. Un modulatore è essenzialmente solo una forma d'onda standard, ma invece di essere usato per produrre un suono sono comunemente usati per modulare una o più proprietà di una forma d'onda udibile (ad esempio la sua ampiezza o frequenza).
Prendere vibrato, per esempio. Il vibrato è un regolare cambiamento di tonalità pulsante. Per produrre quell'effetto usando un modulatore è possibile impostare la forma d'onda del modulatore su un'onda sinusoidale e impostare la frequenza del modulatore su un valore di circa 8 hertz. Se poi hai collegato il modulatore alla frequenza di una forma d'onda udibile il risultato sarebbe un effetto vibrato - il modulatore alzerebbe e abbasserebbe gradualmente la frequenza (intonazione) della forma d'onda udibile otto volte al secondo.
Il motore audio che creeremo ti permetterà di collegare i modulatori ai tuoi suoni per permetterti di produrre un vasto numero di effetti diversi.
Nel prossimo tutorial creeremo il codice core per il motore audio e otterremo tutto in funzione. Seguici su Twitter, Facebook o Google+ per tenerti aggiornato con gli ultimi post.