Ceci est la dernière partie de notre série de didacticiels sur la création d’un moteur audio basé sur un synthétiseur pouvant être utilisé pour générer des sons pour des jeux de style rétro. Le moteur audio peut générer tous les sons au moment de l'exécution sans recourir à des dépendances externes telles que des fichiers MP3 ou des fichiers WAV. Dans ce tutoriel, nous ajouterons le support pour les processeurs audio et coderons un processeur de retard qui peut ajouter un effet d'écho en décomposition à nos sons.
Si vous n'avez pas encore lu le premier ou le deuxième tutoriel de cette série, faites-le avant de continuer..
Le langage de programmation utilisé dans ce didacticiel est ActionScript 3.0, mais les techniques et les concepts utilisés peuvent facilement être traduits dans tout autre langage de programmation fournissant une API audio de bas niveau..
Si vous souhaitez utiliser les exemples interactifs de ce didacticiel, assurez-vous que Flash Player 11.4 ou version ultérieure est installé sur votre navigateur..
Dans ce dernier tutoriel, nous ajouterons processeurs audio au moteur principal et à la création d’un processeur de retard simple. La démonstration suivante montre le processeur de délai en action:
Un seul son est joué dans cette démonstration, mais la fréquence du son est randomisée et les échantillons audio générés par le moteur sont poussés dans un processeur de retard, ce qui lui confère un effet d'écho décroissant..
AudioProcessor
ClasseLa première chose à faire est de créer une classe de base pour les processeurs audio:
paquet noise public class AudioProcessor // var publique activée: Boolean = true; // fonction publique AudioProcessor () if (Object (this) .constructor == AudioProcessor) renvoie une nouvelle erreur ("La classe AudioProcessor doit être étendue"); // processus de fonction interne (exemples: vecteur.): void
Comme vous pouvez le constater, le cours est très simple. il contient un interne processus()
méthode qui est invoquée par le AudioEngine
classe chaque fois que des échantillons doivent être traités, et un public activée
propriété pouvant être utilisée pour allumer et éteindre le processeur.
AudioDelay
Classele AudioDelay
classe est la classe qui crée réellement le retard audio, et elle étend la AudioProcessor
classe. Voici la classe de base vide avec laquelle nous allons travailler:
paquet noise public class AudioDelay étend AudioProcessor // fonction publique AudioDelay (time: Number = 0.5) this.time = time;
le temps
L'argument passé au constructeur de la classe est le temps (en secondes) du délai tap: c'est-à-dire la durée entre chaque délai audio..
Ajoutons maintenant les propriétés privées:
var privé m_buffer: vecteur.= nouveau vecteur. (); var privée m_bufferSize: int = 0; var privée m_bufferIndex: int = 0; var privée m_time: Number = 0.0; private var m_gain: Number = 0.8;
le m_buffer
Le vecteur est essentiellement une boucle de rétroaction: il contient tous les échantillons audio transmis au processus
méthode, et ces échantillons sont modifiés (dans ce cas, d’amplitude réduite) de manière continue, m_bufferIndex
passe à travers le tampon. Cela fera sens quand nous arriverons à la processus()
méthode.
le m_bufferSize
et m_bufferIndex
les propriétés sont utilisées pour garder trace de l'état du tampon. le m_time
propriété est l'heure du délai tapé, en secondes. le m_gain
propriété est un multiplicateur utilisé pour réduire l'amplitude des échantillons audio mis en mémoire tampon avec le temps.
Cette classe a seulement une méthode, et c'est la interne processus()
méthode qui annule la processus()
méthode dans le AudioProcessor
classe:
processus de la fonction de remplacement interne (exemples: vecteur.): void var i: int = 0; var n: int = samples.length; var v: nombre = 0,0; // alors que je < 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++;
Enfin, nous devons ajouter les getters / setters pour le privé m_time
et m_gain
Propriétés:
fonction publique get time (): Number return m_time; public function set time (valeur: Number): void // ajustez l'heure dans la plage 0,0001 - 8,0 valeur = valeur < 0.0001 ? 0.0001 : value > 8,0? 8,0: valeur; // pas besoin de modifier la taille de la mémoire tampon si l'heure n'a pas changé if (m_time == valeur) return; // définir l'heure m_time = value; // met à jour la taille du tampon m_bufferSize = Math.floor (44100 * m_time); m_buffer.length = m_bufferSize;
fonction publique get gain (): Number return m_gain; public function set gain (value: Number): void // verrouille le gain dans la plage 0.0 - 1.0 m_gain = value < 0.0 ? 0.0 : value > 1,0? 1,0: valeur;
Croyez ou non, c'est le AudioDelay
classe terminée. Les retards audio sont en réalité très faciles une fois que vous avez compris comment la boucle de retour (le m_buffer
propriété).
AudioEngine
ClasseLa dernière chose à faire est de mettre à jour le AudioEngine
classe afin que des processeurs audio puissent y être ajoutés. Tout d’abord, ajoutons un vecteur pour stocker les instances de processeur audio:
statique privée var m_processorList: Vector.= nouveau vecteur. ();
Pour ajouter et supprimer des processeurs de et vers le AudioEngine
classe nous allons utiliser deux méthodes publiques:
AudioEngine.addProcessor ()
Fonction publique statique addProcessor (processeur: AudioProcessor): void if (m_processorList.indexOf (processeur) == -1) m_processorList.push (processeur);
AudioEngine.removeProcessor ()
Fonction publique statique removeProcessor (processeur: AudioProcessor): void var i: int = m_processorList.indexOf (processeur); if (i! = -1) m_processorList.splice (i, 1);
Assez facile - toutes ces méthodes consistent à ajouter et à supprimer AudioProcessor
instances à ou de la m_processorList
vecteur.
La dernière méthode que nous allons ajouter parcourt la liste des processeurs audio et, si le processeur est activé, transmet les échantillons audio au processeur. processus()
méthode:
fonction privée statique processSamples (): void var i: int = 0; var n: int = m_processorList.length; // alors que je < n ) if( m_processorList[i].enabled ) m_processorList[i].process( m_sampleList ); i++;
Il est maintenant temps d’ajouter le dernier bit de code. Il s’agit d’une seule ligne de code qui doit être ajoutée au code privé. onSampleData ()
méthode dans le AudioEngine
classe:
if (m_soundChannel == null) while (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++;
La ligne de code en surbrillance est celle qui doit être ajoutée à la classe. il invoque simplement le processSamples ()
méthode que nous avons précédemment ajoutée.
Comme on dit, c’est ça. Dans le premier tutoriel, nous avons examiné différentes formes d'onde et la manière dont les ondes sonores sont stockées numériquement. Nous avons ensuite construit le code de moteur audio principal dans le second tutoriel. Nous avons maintenant terminé avec l'ajout de processeurs audio..
Il est possible de faire beaucoup plus avec ce code, ou une variante de ce code, mais il est important de garder à l'esprit que c'est tout le travail qu'un moteur audio doit effectuer à l'exécution. Si vous poussez un moteur audio trop loin (et que cela est facile à faire), les performances globales de votre jeu peuvent en souffrir - même si vous déplacez un moteur audio dans son propre thread (ou dans un opérateur ActionScript 3.0), il sera toujours heureux. mordre des morceaux de la CPU si vous ne faites pas attention.
Cependant, beaucoup de jeux professionnels et non professionnels font beaucoup de traitement audio au moment de l'exécution, car avoir des effets sonores et de la musique dynamiques dans un jeu peut ajouter beaucoup à l'expérience globale, et peut entraîner le joueur plus profondément dans le jeu. monde. Le moteur audio que nous avons mis au point dans cette série de didacticiels pourrait tout aussi bien fonctionner avec des échantillons d'effets sonores normaux (non générés) chargés à partir de fichiers: l'essentiel de l'audio numérique est une séquence d'échantillons dans sa forme la plus élémentaire..
Une dernière chose à laquelle il faut penser: le son est un aspect très important de la conception de jeux. Il est tout aussi important et puissant que le côté visuel des choses et ne doit pas être jeté à la loupe ou collé à un jeu à la dernière minute. développement si vous vous souciez vraiment de la qualité de production de vos jeux. Prenez votre temps avec la conception audio de vos jeux et vous en récolterez les fruits.
J'espère que vous avez apprécié cette série de tutoriels et que vous pouvez en retirer quelque chose de positif: même si vous pensez un peu plus à l'audio dans vos jeux, à partir de maintenant, j'ai fait mon travail..
Tout le code source du moteur audio est disponible dans le téléchargement source.
S'amuser!