Friday, September 21, 2007

libmikmod deep test

I turned down the volume of my PC yesterday and let the DS be the music center, using a couple of updates on my "file server" (mostly truncating filenames and ungzipping on the fly submitted files). That was a fun time, but it revealed several oddities in the playback, so i have decided to delve deeper in the mikmod player library this lunch time to see whether i could improve playback.

The whole story is atm. only detailed in french, but to put it shortly, i mnaged to play a couple of .IT files from my brother thanks to a small patch (skip rendering in the first few ticks). I wish i could have opted for hardware playback, too, but this one seems completely buggy atm (understand: it completely locks down the system :P)


Hier, j'ai baissé le son de mon PC: c'est la DS qui a pris le relai, moyennant deux ou trois petits ajouts au programme servant de "serveur" côté PC (notamment, décompression à la volée et troncage approprié des noms de fichiers).

C'était sympa, mais le player a méchamment tendance à bugger, notamment au démarrage. J'ai essayé d'y voir un peu plus clair dans le code de mikmod (cf le post plus long). Il y a pas mal de positifs (j'arrive à jouer "Prehis2.it" de mon frère, par exemple ou la "pipe zone"), mais je crains que le player software ne devienne vite trop gourmand. Et le player hardware ? bin il crashe tout. oui, ma bonne dame.



note that while the job of hardware driver's update is straightforward, the software driver will actually try to fill the playout buffer at each step.



Software Mixing

Bon, le mixage hardware plante tout. comme ça au moins, c'est réglé. Dommage: le mixage software va être beaucoup plus tordu. Ici, le fonctionnement du mixeur est guidé par le playout buffer, une zone de mémoire dans lequel notre processeur principal construit un "wav" que le processeur secondaire (ARM7 et les circuits de la DS) transmettent vers nos oreilles.

Tout ce joue à l'aide de deux indicateurs de position (les "curseurs" dixit la bibliothèque mikmod) qui repère où en est l'écriture et où en est la lecture dans ce qu'il est commun d'appeler un buffer circulaire. Notre driver software va donc, à chaque appel de sa méthode Update(), calculer le nombre de bytes libres dans ce buffer et faire appel au virtual channel qui est chargé d'avancer dans le pattern et de produire des bytes.


#if 0
if (isfirst) {
isfirst--;
return;
}
#endif
il y avait dans Player_HandleTick() ce curieux bout de code avec comme commentaire "don't handle the very first ticks, this allows the other hardware to
settle down so we don't loose any starting notes"
. En clair, le "#if 0" désactive le bloc en question, qui avait pour effet de ne pas générer de données sur les "isfirst" premiers appels à HandleTick(). Comme quasiment tous mes mods démarraient de travers, je l'ai simplement réactivé, et je ne suis pas mécontent du résultat.


Mikmod_Update()


while(1) {
ge.handle();
MikMod_Update();
ge.animate();
}
Bion. Donc chaque appel à MikMod_Update(), pour la version "software mixing", le player va estimer combien de bytes du buffer ont déjà été joués et les remplacera par des nouveaux. Reste à quelle fréquence on va appeler cette fonction (plus elle sera appelée souvent, plus son temps d'exécution sera court, du coup).
La méthode la plus directe (et utilisée dans Tetris Attack) consiste à la placer après le traitement d'une nouvelle image.

Dans cette approche, le programme va alterner les tâches suivantes:
  1. lire l'état des boutons, de l'écran tactile, etc. et réagir en conséquence
  2. attendre le prochain rafraichissement (d'écran, hein. pas un soda-glacé)
  3. remettre à jour les tables de sprites (non, c'est pas non plus un rafraichissement. m'enfin)
  4. appeler mikmod_update pour produire plus de son dans le buffer
  5. traiter les animations
A première vue, c'est sous-optimal: on voudrait avoir le rendu du son comme tâche de fond, p.ex. pendant que l'on attend le rafraichissement d'écran (tant qu'à attendre, ...)


No comments: