OPL2 Audio Board : une carte son AdLib pour Arduino

Vincent Bernat

Dans un article précédent, j’ai présenté l’OPL2LPT, une carte son sur port parallèle dotée d’une puce Yamaha YM3812, aussi connue comme l’OPL2 et présente sur les cartes sons AdLib. L’OPL2 Audio Board pour Arduino est une autre carte son utilisant cette puce. Toutefois, plutôt que reposer sur le port parallèle, elle dispose d’une interface série qui peut être pilotée par un Arduino ou un Raspberry Pi. Alors que l’OPL2LPT cible essentiellement les joueurs disposant du matériel d’époque, l’OPL2 Audio Board ne peut pas être utilisée de la même façon. Toutefois, il est possible de l’exploiter depuis ScummVM et DOSBox!

OPL2 Audio Board for Arduino
L'OPL2 Audio Board sur une boîte « Grim Fandango ».

Découverte#

L’OPL2 Audio Board est disponible sur Tindie, soit sous forme de kit, soit déjà assemblée. Je l’ai associée avec une copie bon marché de l’Arduino Nano. Le code nécessaire pour piloter la carte est disponible sur GitHub avec plusieurs exemples.

L’un d’eux est DemoTune.ino. Il joue une courte mélodie sur trois canaux. Il peut être compilé et envoyé sur l’Arduino à l’aide de PlatformIO (installable avec pip install platformio) en utilisant la commande suivante1 :

$ platformio ci \
>   --board nanoatmega328 \
>   --lib ../../src \
>   --project-option="targets=upload" \
>   --project-option="upload_port=/dev/ttyUSB0" \
>   DemoTune.ino
[…]
PLATFORM: Atmel AVR > Arduino Nano ATmega328
SYSTEM: ATMEGA328P 16MHz 2KB RAM (30KB Flash)
Converting DemoTune.ino
[…]
Configuring upload protocol...
AVAILABLE: arduino
CURRENT: upload_protocol = arduino
Looking for upload port...
Use manually specified: /dev/ttyUSB0
Uploading .pioenvs/nanoatmega328/firmware.hex
[…]
avrdude: 6618 bytes of flash written
[…]
===== [SUCCESS] Took 5.94 seconds =====

Une fois la programmation terminée, l’Arduino joue la mélodie. 🎶

L’autre exemple intéressant est SerialIface.ino. Il transforme l’ensemble en carte son sur port série. Une fois le code programmé dans l’Arduino, vous pouvez utiliser le programme play.py dans le même répertoire pour écouter des fichiers VGM. Il s’agit d’un format de fichiers contenant précisément les commandes envoyées à la puce audio. De nombreux fichiers à ce format sont disponibles sur VGMRips. Prenez garde à ne prendre que ceux pour YM3812/OPL2 ! Voici une courte sélection :

L'OPL2 Audio Board connectée à l'Arduino Nano et interprétant quelques fichiers VGM. Les diodes électroluminescentes clignotent au rythme de la réception des données depuis le port série.

Utilisation avec DOSBox & ScummVM#

Note

Le protocole série utilisé dans cette section n’a pas été intégré en amont (PR#20) : à la place, un protocole encore plus simple a été conçu. Vous devez récupérer le fichier SerialIface.ino issu de ma proposition : git checkout 50e1717.

Quand l’Arduino est configuré avec SerialIface.ino, la carte peut être pilotée via le port série avec un protocole simple. Après avoir modifié DOSBox et ScummVM, ils peuvent utiliser cette carte son inhabituelle. Voici quelques exemples de jeux l’utilisant :

  • 0:00, avec DOSBox, le premier niveau de Doom 🎮 (1993)
  • 1:06, avec DOSBox, l’introduction de Loom 🎼 (1990)
  • 2:38, avec DOSBox, le premier niveau de Lemmings 🐹 (1991)
  • 3:32, avec DOSBox, l’introduction de Legend of Kyrandia 🃏 (1992)
  • 6:47, avec ScummVM, l’introduction de Day of the Tentacle ☢️ (1993)
  • 11:10, avec DOSBox, l’introduction de Another World 🐅 (1991)

Another World, réalisé par Éric Chahi, utilise des sons échantillonnés à 5 kHz ou 10 kHz. Avec un port série opérant à 115 200 bits/s, l’option des 5 kHz est juste à notre portée. Toutefois, je ne sais pas si le rendu est fidèle.

Mise à jour (05.2018)

Après discussion avec Walter van Niftrik, nous sommes arrivés à la conclusion que, au-dessus de 1 kHz, DOSBox n’exécute pas les commandes OPL avec la précision temporelle adéquate. Il utilise une tranche de temps d’une milliseconde au cours de laquelle il exécute soit un nombre fixe de cycles CPU, soit autant que possible (avec cycles=max). Dans les deux cas, les instructions CPU émulées sont exécutées aussi rapidement que possible et les retards d’E/S sont simulés en supprimant un nombre fixe de cycles de l’allocation pour la tranche de temps en cours.

DOSBox#

Le protocole série est décrit dans le fichier SerialIface.ino :

/*
 * A very simple serial protocol is used.
 *
 * - Initial 3-way handshake to overcome reset delay / serial noise issues.
 * - 5-byte binary commands to write registers.
 *   - (uint8)  OPL2 register address
 *   - (uint8)  OPL2 register data
 *   - (int16)  delay (milliseconds); negative -> pre-delay; positive -> post-delay
 *   - (uint8)  delay (microseconds / 4)
 *
 * Example session:
 *
 * Arduino: HLO!
 * PC:      BUF?
 * Arduino: 256 (switches to binary mode)
 * PC:      0xb80a014f02 (write OPL register and delay)
 * Arduino: k
 *
 * A variant of this protocol is available without the delays. In this
 * case, the BUF? command should be sent as B0F? The binary protocol
 * is now using 2-byte binary commands:
 *   - (uint8)  OPL2 register address
 *   - (uint8)  OPL2 register data
 */

Ajouter la prise en charge de celui-ci dans DOSBox est relativement simple (rustine). Pour obtenir de bonnes performances, nous utilisons la version du protocole à 2 octets (5000 opérations par seconde). Les commandes sont canalisées et un fil d’exécution dédié collecte les acquittements. Un sémaphore représente le nombre de places libres dans le tampon de réception. Comme il n’est pas possible de lire les registres, nous nous reposons sur DOSBox pour l’émulation des minuteurs qui sont essentiellement utilisés pour permettre la détection de l’OPL2.

La modification est testée uniquement sous Linux mais devrait fonctionner sur la plupart des systèmes POSIX, mais pas sous Windows. Pour tester, vous devez compiler DOSBox depuis les sources :

$ sudo apt build-dep dosbox
$ git clone https://github.com/vincentbernat/dosbox.git -b feature/opl2audioboard
$ cd dosbox
$ ./autogen.sh
$ ./configure && make

Remplacez la section sblaster du fichier ~/.dosbox/dosbox-SVN.conf :

[sblaster]
sbtype=none
oplmode=opl2
oplrate=49716
oplemu=opl2arduino
opl2arduino=/dev/ttyUSB0

Ensuite, exécutez DOSBox avec ./src/dosbox. C’est tout !

Il est probable que vous obteniez le message “OPL2Arduino: too slow, consider increasing buffer” de manière répétée. Pour corriger ceci, il vous faut recompiler SerialIface.ino avec un tampon de réception plus important :

$ platformio ci \
>   --board nanoatmega328 \
>   --lib ../../src \
>   --project-option="targets=upload" \
>   --project-option="upload_port=/dev/ttyUSB0" \
>   --project-option="build_flags=-DSERIAL_RX_BUFFER_SIZE=512" \
>   SerialIface.ino

ScummVM#

Le code utilisé dans DOSBox peut être adapté à ScummVM (rustine). Pour tester, vous devez compiler ScummVM depuis les sources :

$ sudo apt build-dep scummvm
$ git clone https://github.com/vincentbernat/scummvm.git -b feature/opl2audioboard
$ cd scummvm
$ ./configure --disable-all-engines --enable-engine=scumm && make

Démarrez ensuite ScummVM avec ./scummvm. Sélectionnez « AdLib Emulator » comme périphérique musical et « OPL2 Arduino » comme émulateur AdLib2. Comme pour DOSBox, surveillez la console pour vérifier que le tampon de réception est bien dimensionné.

Amusez-vous bien ! 🎶


  1. Cette commande n’est valide que pour l’Arduino Nano. Pour une autre variation, jetez un œil sur la sortie de platformio boards arduino↩︎

  2. Pour spécifier un port série autre que /dev/ttyUSB0, ajoutez une ligne opl2arduino_device= dans le fichier ~/.scummvmrc↩︎