Jusqu'à présent, nous avons vu les maigres capacités de calcul et graphiques de la NES. Mais les jeux vidéo ne seraient pas ce qu'ils sont sans les ambiances sonores, que ce soit via les musiques ou simplement, les bruitages. Dans cet article, nous allons donc nous intéresser à la façon dont le son était géré sur les toutes premières consoles de jeux, comme la NES.
De nos jours, pour jouer un son ou une musique dans un jeu, on ne s'embête pas trop et il suffit d'appeler la routine qui permet de jouer un fichier .wav, voire .mp3 dans le thread qui gère le son, et le tour est joué. Rien de tout cela sur la NES, et ce, pour plein de raisons. Les fichiers sons non compressés, de type .wav par exemple, prennent beaucoup trop de place et toute la mémoire de la NES suffirait à peine pour quelques secondes de musique, au mieux. Les fichiers sons compressés comme les MP3 pourraient sembler mieux adaptés, mais le code pour les décompresser à la volée serait à la fois trop volumineux et trop lent pour être exécuté sur une NES.
Les machines de l'époque utilisaient des générateurs programmables aux capacités assez limitées, mais très peu gourmands en mémoire et en code. Dans le cas de la NES, c'est une puce intégrée avec le 6502 pour former le 2A03 ou 2A07 qui est chargée de tout cela et elle est astucieusement nommée APU pour Audio Processing Unit.
1. Des carrés, un triangle et du bruit
L'APU est donc capable de générer des signaux analogiques dont on peut paramétrer la fréquence et d'autres aspects. La forme de ces signaux est fixe, mais cela suffit pour réaliser des bruitages et/ou des musiques simples. L'APU de la NES permet d'utiliser en même temps deux signaux carrés (avec des réglages différents), un signal triangulaire et un signal de bruit, et un autre un peu bizarre que je présenterai à la fin de cet article : le DPCM. Chacun de ces signaux est géré par une partie de l'APU que l'on appelle canal.
Comme pour le PPU, on peut accéder aux registres [1] de l'APU depuis le code 6502, en écrivant dans une partie spécifique de la mémoire. Pour l'APU, il s'agit des adresses de $4000 à $4015 (en hexadécimal). Ces 21 registres sont répartis en 4 registres pour chaque signal/canal et un pour les gouverner tous. Ce dernier est accessible à l'adresse $4015 et chacun de ses cinq bits de poids faible permet d'activer ou de désactiver l'un des signaux. Les bits 0 et 1 concernent les deux canaux carrés, le bit 2 le triangulaire, le bit 3 celui du bruit et le bit 4 celui du DPCM.
Par exemple, si on écrit la valeur 13 (00001101 en binaire) à l'adresse $4015, cela activera le premier canal carré, le canal triangulaire et celui du bruit, en désactivant les autres.
Ce registre, nommé APU_STATUS, est aussi le seul qui soit accessible en lecture.
2. Les canaux carrés
Commençons par les signaux des deux canaux carrés. Ces deux canaux fonctionnent de la même façon : en générant des signaux périodiques carrés ou presque (voir figure 1). On peut choisir la fréquence (la période, en fait) du son, sa durée, le rapport cyclique (voir figure 1) et la possibilité de faire glisser la fréquence pour des effets étonnants.
Comme on peut le voir sur la figure 1, la forme carrée ressemble à une sinusoïde qui serait tronquée, ce qui fait que l'on a tendance à entendre ces signaux comme s'ils étaient saturés. Les canaux carrés sont donc souvent utilisés pour simuler des sons de type guitare électrique.
La hauteur du son est donnée par une période codée sur 11 bits (de 0 à 2047) multipliée par 9,6235276 µs environ pour la version PAL de la NES.
Le rapport cyclique (duty cycle en anglais) ne peut avoir que 4 valeurs et donne un son plus ou moins métallique. Il s'agit du rapport entre la durée où l'onde sonore est au plus haut et la période totale. Une valeur de 50 % donne un son carré, très métallique alors que 25 % et 12,5 % donnent des sons légèrement plus doux à l'oreille. La valeur 75 % est assez anecdotique, car on ne fait pas la différence à l'oreille avec la valeur 25 %.
Le glissement de fréquence (sweep en anglais) permet de changer rapidement la fréquence pendant la durée du son, vers les hautes fréquences ou les basses. L'effet obtenu est assez typique de ce qu'on trouve dans les dessins animés ou les jeux de l'époque.
Mais le mieux pour comprendre tout ça est d'expérimenter en utilisant le programme pulse.nes présent sur le site du magazine [2] et dont on voit une capture d'écran sur la figure 2. Je vous engage à tester ce programme et à comprendre les effets de chaque valeur. Il est possible de changer chaque valeur à l'aide des touches fléchées du joypad, puis d'entendre le son correspondant en pressant le bouton A.
Le code en lui-même n'a rien de particulier, il écrit simplement dans les registres adresses $4000 à $4003 qui correspondent aux registres APU_SQ1_CONTROL, APU_SQ1_SWEEP, APU_SQ1_NOTE_LOW et APU_SQ1_NOTE_HIGH. Ces registres sont les quatre associés au premier canal carré et voici le détail de leur contenu :
- APU_SQ1_CONTROL regroupe les données des quatre premières lignes du programme pulse, avec le volume du son sur 4 bits, le rapport cyclique sur 2 bits et 2 autres bits pour activer ou désactiver le bouclage du son et le contrôle du volume ;
- APU_SQ1_SWEEP (les quatre lignes suivantes du programme pulse) permet de contrôler toute la partie glissement de fréquence, avec un bit pour l'activer ou pas, un autre bit pour indiquer si le glissement est vers le haut ou le bas, 3 bits pour la vitesse du glissement et 3 bits pour l'amplitude du glissement ;
- APU_SQ1_NOTE_LOW contient les 8 bits (sur 11) de poids faible de la période de la note jouée ;
- APU_SQ1_NOTE_HIGH contient les 3 bits de poids fort de la période de la note ainsi que la durée du son sur 5 bits, exprimée en nombre de périodes.
Pour la partie note, je n'ai pas proposé les 2048 possibilités, mais juste une table de « vraies » notes en notation anglo-saxonne, et il y a une table de conversion dans le programme.
3. Le canal triangulaire
Après les deux canaux carrés, le troisième canal propose une forme d'onde triangulaire (voir figure 3). Là encore, la forme théorique n'est pas exactement celle que l'on a en pratique et on peut s'apercevoir que cette forme d'onde est finalement assez proche d'une forme sinusoïdale. C'est pourquoi le son de ce canal semble assez pur à l'oreille, sans beaucoup d'harmoniques, comme le son d'une flûte, par exemple.
Ce canal est souvent utilisé comme base de la mélodie principale, avec des notes graves et longues pendant que les deux canaux carrés viennent décorer la musique avec des touches plus chargées en harmoniques.
Le canal triangulaire est beaucoup moins paramétrable que les canaux carrés. En plus de la note (la période) et la longueur de la note, on ne peut en effet contrôler qu'un simple compteur de répétition de la note (voir figure 4).
Comme pour les signaux carrés, vous pouvez tester les paramètres du signal triangulaire à l'aide du programme triangle.nes que vous trouverez sur le GitHub du magazine [2].
Le code de ce programme est bien évidemment très proche du précédent, mais avec encore moins de possibilités. L'APU n'a en effet que 3 registres consacrés au canal triangulaire, accessibles aux adresses $4008, $400A et $400B :
- APU_TRI_CONTROL contient juste un bit pour activer ou non la répétition de la note et 7 autres pour une valeur jusqu'à 127 pour le nombre de répétitions. Ce registre n'est pas vraiment utilisé ;
- APU_TRI_NOTE_LOW contient les 8 bits de poids faible de la période de la note, comme pour les signaux carrés ;
- APU_TRI_NOTE_HIGH contient les bits de poids fort et la durée du son.
Le canal triangle est facile à comprendre, mais aussi très limité, comme vous pouvez le voir.
4. Le canal de bruit
Le quatrième canal de l'APU de la NES est un simple générateur de bruits blancs. Cela permet de générer des sons allant du souffle à un bruit de percussion comme un tambour. C'est assez limité et je n'ai pas fait de programme permettant d'explorer les possibilités de ce canal, mais ce serait assez simple à réaliser.
Ce canal est contrôlé par les registres aux adresses $400C (APU_NOISE_CTRL), $400E (APU_NOISE_PERIOD) et $400F (APU_NOISE_COUNT).
Le registre APU_NOISE_PERIOD permet de choisir un son parmi 16 plus ou moins graves.
Comme ce canal est assez limité, il est surtout restreint aux sons de type batterie.
5. Des instruments
Bon, c'est bien beau tout ça, mais cela semble terriblement pauvre pour composer des musiques intéressantes.
Dans la pratique, on n'utilise pas vraiment les longueurs des sons ou les glissements de fréquences, à part pour certains bruitages. Au lieu de cela, on profitera d'avoir une interruption VBL toutes les 20 millisecondes (sur les NES PAL) pour modifier les paramètres de chacun des canaux et réaliser « à la main » les effets voulus.
Par exemple, si à chaque VBL on garde la même note sur un canal carré, mais que l'on change le volume, il devient possible d'imiter le comportement de certains instruments. La figure 5 montre trois évolutions de volume au cours du temps. La première représente un son assez bref, dont l'intensité chute rapidement. La deuxième permet de modéliser un instrument qui aurait de l'écho, alors que la troisième a un profil ressemblant à une note de piano.
Il est assez facile de définir différentes séquences pour se rapprocher du son de tel ou tel instrument. Cette technique est d'ailleurs très utilisée dans le monde de la musique « chiptune » et on appelle ces séquences de changement de volume des « enveloppes de volumes ».
Mais jouer sur l'intensité du volume n'est pas la seule façon de modifier la perception d'une note. Une autre approche consiste à modifier la période (et donc la fréquence) de la note rapidement. Cela permet de reproduire l'effet de glissement, mais aussi de jouer des accords arpégés. Par exemple, si on joue un DO, on peut jouer rapidement les notes DO-MI-SOL-DO pour un accord majeur parfait et ajouter ce son typique des jeux des années 1980. Là aussi, c'est assez simple à programmer, puisqu'il suffit de se décaler dans la table des notes à chaque VBL.
Le programme instruments.nes (à récupérer sur le site du magazine [2]) ne fait que jouer la gamme de DO, mais propose de choisir le type de canal (carré ou triangulaire), une enveloppe de volume parmi 12 et un style d'arpège parmi 10 avec des accords majeurs ou mineurs, avec rebouclage ou pas, etc. Si vous testez ce programme, vous pourrez vous rendre compte de la grande diversité des sons que l'on peut obtenir de cette façon. Les plus courageux d'entre vous pourront même assez facilement ajouter des enveloppes de volume ou des arpèges, pour encore plus de possibilités.
6. DPCM
J'ai indiqué en début d'article qu'il n'était pas question de jouer des MP3 ou des WAV sur une NES. Il existe toutefois une possibilité de jouer des sons échantillonnés à l'aide de l'APU, mais cela est truffé de restrictions.
Tout d'abord, les échantillons que l'on pourra jouer doivent être en mono (pas de stéréo sur la NES), en 8 bits, avec une fréquence d'échantillonnage comprise entre 4000 et 33000 Hertz, ce qui pourrait être tolérable. Cependant l'encodage est tel qu'il est impossible d'avoir réellement une telle fréquence et que même avec la pire qualité, on ne peut avoir qu'une trentaine de secondes de son qui ne peut pas être modulé (une seule note).
Les sons de ce type sont donc réservés pour des cas assez particuliers, comme un message parlé très très court ou un son de batterie plus réaliste qu'avec le canal de bruit.
Habituellement, pour jouer un son échantillonné, on dispose des valeurs d'intensité de ce son que l'on transmet directement au processeur sonore. Par exemple, si un son est composé des échantillons 45, 27 et 85, ces valeurs sont mises en mémoire quelque part et envoyées telles quelles à un convertisseur numérique/analogique. Dans le cas de l'APU, le canal DPCM (pour Delta-Pulse Modulation Channel), on part toujours de la valeur 127 et chaque bit de l'échantillon permet de faire évoluer la valeur courante soit en l'augmentant de 1 (si le bit est à 1) soit en la diminuant de 1 (si le bit est à 0). Il est donc hors de question de passer de 45 à 27 rapidement, comme dans mon exemple.
L'idée de n'utiliser qu'un seul bit par échantillon est bien évidemment de gagner un maximum de place, mais la qualité s'en ressent, puisque cela diminue grandement les fréquences maximales. Il faut en effet 255 étapes pour passer de l'amplitude minimale à l'amplitude maximale, et réciproquement. Donc, même avec une fréquence d'échantillonnage de 33000 Hz, jouer un signal triangulaire d'amplitude maximale ne peut se faire que pour une note de fréquence max 33000/(2*255) = 66 Hz environ, ce qui est vraiment dans les graves. Le DPCM ne permet donc pas d'avoir des sons avec une bonne dynamique. Ceci dit, on arrive tout de même à approximer différentes courbes de son assez fidèlement, comme on peut le voir sur la figure 6.
À la faible qualité sonore des signaux DPCM s'ajoute quelques autres limitations. L'adresse de début des échantillons doit forcément être entre $C000 et $FFC0 et à un multiple de 64 en mémoire. Ça ne laisse pas beaucoup de place et c'est une zone où l'on stocke en général une bonne partie du code et des données d'un jeu. Ces restrictions font que le canal DPCM est assez peu utilisé dans les jeux NES qui n'utilisent pas de mappers particuliers (voir l'article précédent).
Tout cela est contrôlé dans l'APU par 4 registres particuliers qui constituent le cinquième canal :
- APU_DPCM_FREQ ($4010) permet de donner la fréquence de l'échantillon dans ses 4 bits de poids faible. Une valeur de 0 indique une fréquence de 4000 Hz environ et une valeur de 15, une fréquence de 33000 Hz environ (voir [4]) ;
- APU_DPCM_DIRECT ($4011) permet de donner une valeur directe d'échantillon, tellement gourmand en mémoire et en CPU que cela n'est jamais utilisé ;
- APU_DPCM_ADDRESS ($4012) indique 8 bits de l'adresse de l'échantillon, sachant que les deux bits de poids fort sont forcément à 1 et que les 6 de poids faible sont forcément à 0, on n'indique donc que les 8 bits intermédiaires ;
- APU_DPCM_LENGTH ($4013) permet d'indiquer la longueur de l'échantillon, en nombre de bits divisé par 16.
Ceci peut sembler incroyablement tordu, mais tout a été fait pour optimiser au mieux l'utilisation mémoire et la faible puissance de calcul disponible.
Cependant, utiliser le DPCM pour jouer un échantillon introduit notamment un bug dans la lecture des boutons du joypad. Il est alors recommandé de lire leurs états plusieurs fois à la suite et de considérer les différences comme des erreurs de lecture.
Le programme dpcm.nes (voir [2]) prend tout ceci en compte et permet de jouer trois samples différents en utilisant le DPCM. Il s'agit de deux sons de batterie et d'un effet sonore d'un personnage qui se cogne. Rien de bien génial, mais cela prend tout de même une place importante.
7. FamiTracker / FamiStudio
Dans les années 1980, pour réaliser des bruitages ou des musiques, on utilisait toutes ces informations pour créer des données directement dans un fichier texte aux côtés du code assembleur correspondant. Depuis, les choses ont heureusement évoluées et si l'on désire écrire des musiques pour de vielles consoles, on peut utiliser des logiciels interactifs comme FamiTracker [5] ou FamiStudio [6] (voir figure 7).
Avec ce genre de programme, il est possible de définir des instruments en termes d'enveloppes de volume et de type d'arpèges, ou d'échantillons sonores (en haut à droite). On peut ensuite définir des enchaînements de notes sur les canaux souhaités (de 1 à 5). C'est ce que l'on voit dans la partie basse de l'interface. On peut voir qu'il y a plus d'informations que juste la note jouée, comme le numéro de l'instrument ou des codes pour certains effets. Il est ensuite possible de rejouer ces enchaînements à la suite.
Le programme permet donc de composer toute une musique et ensuite de l'exporter avec les données et le code permettant de rejouer la musique à l'intérieur d'un jeu en développement. C'est la méthode la plus souvent utilisée dans les productions homebrew récentes.
8. Le cas de la Famicom
Les lecteurs attentifs auront probablement remarqué sur la figure 7 qu'il y a des canaux en plus des 5 dont j'ai parlé jusqu'à maintenant. C'est que la NES avait une sœur jumelle destinée au marché japonais [7]. Les deux consoles avaient des caractéristiques très proches, la principale différence étant la disponibilité de certaines broches sur les bus de la Famicom et un port d'extension qui sera vraiment utilisé (à la différence de celui de la NES, qui reste caché par un bout de plastique !).
La Famicom (pour FAMIly COMputer) verra ainsi fleurir pléthore de périphériques que l'on ne trouvera jamais pour la NES comme un clavier, un modem ou même un lecteur de disquettes 3 pouces : les mêmes que sur Amstrad 664 !
Mais ce qui nous intéresse ici, c'est que la Famicom possède sur son port cartouche deux broches permettant aux cartouches de discuter avec l'APU. Les cartouches de jeux destinées à la Famicom pouvaient donc contenir des puces spécialisées dans la production de son de bien meilleure qualité que l'APU, ce qui est impossible à produire sur la NES.
On a ainsi pu voir des catégories entières de mappers (voir article précédent) permettant de créer des jeux avec des musiques sur plein de canaux et souvent avec des qualités sonores nettement plus poussées que ce que propose la NES.
Dans l'exemple de la figure 7, la musique en cours d'édition utilise les canaux supplémentaires que l'on trouve fréquemment sur les jeux (Famicom uniquement, donc) de la marque Namco.
9. Le mot de la fin
Il y aurait encore beaucoup à dire sur cette toute première console de jeux à cartouches Nintendo.
En effet, 36 ans après son apparition, l'actualité de la NES est toujours florissante. On trouve de nouveaux jeux produits en cartouche encore à l'heure actuelle comme Micro Mages ou Twin Dragons produits par Broke Studio [8]. De nouveaux effets étonnants sont créés, en repoussant les limites de la NES comme un raycasteur [9] (la technique utilisée pour les jeux comme Wolfenstein 3D ou Doom).
De plus, la simplicité de base de cette console en fait l'une de celles pour lesquelles on trouve le plus d'émulateurs.
La Famicom mériterait aussi qu'on s'attarde plus à ses possibilités d'extension. Et les consoles suivantes de la marque : SuperNES / SuperFamicom seraient infiniment plus complexes à détailler. De quoi faire rêver des générations entières de rétrogameurs et de rétrodéveloppeurs.
Références
[1] La description des registres de l’APU : https://wiki.nesdev.com/w/index.php/APU_registers
[2] Le GitHub du magazine : https://github.com/Hackable-magazine/Hackable37
[3] Une bible pour tous ceux qui s'intéressent à la NES : https://wiki.nesdev.com/
[4] La description de la partie DPCM de l'APU (canal 5) : https://wiki.nesdev.com/w/index.php/APU_DMC
[5] Le site de FamiTracker : http://famitracker.com/
[6] Le site de FamiStudio, un tracker plus moderne que FamiTracker : https://famistudio.org/
[7] Les principales différences entre la NES et la Famicom : https://fr.wikipedia.org/wiki/Nintendo_Entertainment_System#Diff%C3%A9rences_r%C3%A9gionales
[8] Broke Studio, un studio français de production de jeux NES actuels : https://www.brokestudio.fr/fr/
[9] Une vidéo montrant une démo de type Wolfenstein 3D : https://www.youtube.com/watch?v=rf6_rjygSnM