Ce document ne traite actuellement que des sons echantillonés. Les contributions abordant les synthétiseurs et les tables de mixage sont les bienvenues.
Les applications audio sont en général difficiles à porter, le son étant un domaine dans lequel les interfaces ne sont pas du tout standardisées, même si l'approche ne varie que très peu en fonction des systèmes d'exploitation.
ossaudioossaudio est peut-être le moyen le plus
simple, mais elle ne fonctionne pas toujours, et ce n'est pas
forcément un bon choix.
ioctl. Si le code à porter
n'utilise pas ioctl seulement pour l'audio, vous
aurez à utiliser #undef ioctl ainsi que la forme
dénudée _ossioctl.
sys/audioio.h
sont obsolètes. De plus, de nombreux ports ne sont pas idéalement
programmés, et ne fonctionnent réellement que sur un seul type de
machine. Certains changements deviennent ainsi nécessaires. Lisez
la partie suivante.
VOUS NE DEVRIEZ PARTIR SUR AUCUN PRESUPPOSES CONCERNANT
LE MATERIEL AUDIO UTILISE.
Du code éronné se caractérise par une simple vérification du
champ a_info.play.precision, comportant 8 ou 16
bits, et par le fait qu'il suppose que les échantillons sont
signés ou non-signés en fonction du comportement de
soundblaster. Vous devriez vérifier explicitement l'échantillon
et programmer en fonction. Un simple exemple :
AUDIO_INIT_INFO(&a_info); a_info.play.encoding =
AUDIO_ENCODING_SLINEAR; a_info.play.precision = 16;
a_info.play.sample_rate = 22050; error = ioctl(audio,
AUDIO_SETINFO, &a_info); if (error) /* deal with it */ error
= ioctl(audio, AUDIO_GETINFO, &a_info);
switch(a_info.play.encoding) { case AUDIO_ENCODING_ULINEAR_LE:
case AUDIO_ENCODING_ULINEAR_BE: if (a_info.play.precision == 8)
/* ... */ else /* ... */ break; case ...
default:
/* n'oubliez pas de prendre en compte ce que vous ignorez !!!
* Par exemple,
*/
fprintf(stderr,
"Unsupported audio format (%d), ask ports@ about that\n",
a_info.play.encoding);
}
/* à présent, n'oubliez pas de vérifier la fréquence
* d'échantillonage que vous avez
*/
Ceci concerne le plus petit fragment de code que vous pourriez traiter avec un grand nombre de problèmes.
AUDIO_ENCODING_SLINEAR), et vous réessayez un
encodage avec "endianness" (par exemple,
AUDIO_ENCODING_SLINEAR_LE). Considérant qu'une
carte son ne doit pas forcément utiliser le même "endianness"
que votre plate-forme, vous devriez vous préparer à
rencontrer cela. Le moyen le plus simple est probablement de
préparer un buff audio complet, et d'utiliser
swab(3) si un changement d'endianness est
nécessaire. Traiter des échantillons externes consiste en
général à : Le matériel pourrait avoir d'étranges limitations, comme être incapable de dépasser les 22050 Hz en stereo, mais plus que 44100 en mono. C'est dans ce genre de cas que vous devriez donner à l'utilisateur la possibilité de changer ses préférences, et d'essayer d'avoir les meilleures performances possibles. Par exemple, il est stupide de limiter la fréquence à 22050 Hz sous prétexte que vous produisez un son stereo. Et si l'utilisateur n'avait pas un système de son stereo connecté à la sortie de sa carte son ?
Il est également grotesque de saisir de manière permanente les limitations des compatibles soundblaster dans votre programme. Vous devriez en être conscient, mais essayer d'obtenir un son au delà de la barrière des 22050 Hz/stereo et vérifier les résultats.
Les échantillons n'utilisent pas toujours la gamme complète de valeurs possibles. Premièrement, les échantillons enregistrés avec un faible gain ne sonneront que très faiblement sur la machine, forçant ainsi l'utilisateur à augmenter le volume. Deuxièmement, sur les machines avec un matériel audio mal isolé, le rendu des sons bas signifie que vous entendrez surtout les battements de coeur de votre machine, au lieu du son que vous attendiez. Enfin, la conversion idiote de 16 bits à 8 bits pourrait vous laisser avec seulement 4 bits audio utilisables, ce qui procurerait une qualité désastreuse.
Si cela est possible, la meilleure solution est probablement de
parcourir intégralement le flux que vous êtes en passe de jouer,
et de l'arranger afin qu'il utilise la gamme dynamique complète.
Si vous ne pouvez pas vous le permettre, mais que vous pouvez
gérer ceci afin d'avoir un minimum de regard sur ce que vous
êtes en passe de jouer, vous pouvez ajuster le réglage du volume
à la volée, vous n'avez qu'a vous assurer que ce dernier reste a
une fréquence bien inférieure à celle du son que vous voulez
jouer, et que vous n'obtenez aucun débordement -- ces
derniers sonneront toujours moins bien que les améliorations que
vous essayez de faire.
La perception du volume étant logarithmique, l'utilisation de
décalages logarithmiques est généralement suffisante. Si votre
donnée est signée, vous devriez explicitement coder le décalage
comme une division, l'opérateur C >> n'étant
pas utilisable sur des données signées.
Si tout le reste échoue, vous devriez au moins essayer de fournir à l'utilisateur une option de décalage du volume.
Les applications dotées de fins basses n'ont habituellement pas à s'en soucier. Gardez en tête que certains d'entre nous utilisent OpenBSD en fin basse 68030 et que si une application sonore peut fonctionner sur cela, il faut que ce soit le cas.
N'oubliez pas de faire des comparatifs. Les optimisations théoriques ne sont que ceci : théoriques. Des figures significatives devraient être collectées pour vérifier ce qui est une amélioration majeure et ce qui ne l'est pas.
Pour les applications audio haute performance, comme mpegI-layer3, certains points doivent être pris en compte :
write, en tant qu'appel
système implique un coût élevé comparé aux traitements audio
internes.
ioctl
AUDIO_GETENC devrait être utilisée pour
reprendre tous les formats que l'interface audio procure.
Rendez vous tout spécialement compte du drapeau
AUDIO_ENCODINGFLAG_EMULATED. Si votre
application est d'ores et déjà capable de restituer tous les
types de formats étranges, et raisonnablement optimisée pour
cela, essayez à tout prix d'utiliser un format natif. D'un
autre côté, le code d'émulation présent dans le périphérique
audio peut être considéré comme relativement optimal, et ne
le remplacez pas par un code rapidement mis sur pied. Un modèle que vous pourriez avoir à suivre afin d'obtenir des résultats optimisés est de commencer par compiler un petit programme de test qui s'assure du matériel audio disponible, et procède ensuite à la configuration de votre programme pour qu'il traite de façon optimale le matériel audio disponible. Vous pourriez raisonnablement inciter les personnes voulant de bonnes performances audio à recompiler votre port quand ils changent de matériel, en s'assurant des changements.
Considérant qu'OpenBSD n'est pas temps réel, vous pourriez quand même vouloir écrire des applications audio souvent temps réel, comme par exemple les jeux. Dans ce cas, vous aurez à diminuer la taille des blocs afin que les effets sonores ne soient pas désynchronisés avec le jeu en court. Le problème avec ceci est que l'interface pourrait être affamée, entrainnant des rendements horribles.
Dans le cas ou vous voudriez simplement que audio et graphismes
soient synchronisés, et que le comportement de votre programme est
prévisible, atteindre la synchronisation est chose facile. Vous jouez
juste vos échantillons audio, et demandez au périphérique audio ce que
vous êtes actuellement en train de jouer, via
AUDIO_GETOOFFS, avant d'utiliser cette information
pour synchroniser à l'avance les graphismes. Si vous demandez
ceci suffisament fréquemment (disons, tous les dizièmes de
seconde), et aussi longtemps que vous avez des ressources pour
utiliser votre application, vous pouvez obtenir une très bonne
synchronisation par ce moyen. Vous pourriez avoir à peaufiner
les figures avec un décalage constant, il peut en effet y avoir
des retards entre les informations rapportées par l'audio, ce
qui se joue actuellement, et le temps mis par XWindow pour
afficher quelquechose.
Dans le cas d'applications audio, travailler avec l'auteur du programme est très important. Si son code ne marche par exemple qu'avec les cartes soundblaster, il y a de fortes chances pour qu'il faille faire face à d'autres technologies très prochainement.
Si vous ne lui envoyez pas vos commentaires de cette manière, votre code aura été inutile.
Il se pourrait aussi qu'il ait déjà notifié les problèmes que vous rencontrez, et qu'il s'y attèle dans l'arbre de développement. Si les patches que vous écrivez représentent plus qu'une poignée de lignes, la coopération est certainement une très bonne idée.