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!
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 :
- 0:00, Railroad Story qui fait partie des démos livrées avec la carte son AdLib (1987)
- 1:04, l’introduction de Star Wars: X-Wing (1993)
- 2:19, l’introduction de The Secret of Monkey Island (1990)
- 3:58, l’introduction de Indiana Jones and the Last Crusade (1989)
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 ! 🎶