En la actualidad este documento sólo trata sobre temas relacionados con muestras de sonido, pero se aceptan contribuciones sobre sintetizadores y tablas de ondas.
Las aplicaciones de audio suelen ser difíciles de portar ya que éste es un terreno en donde no existen normativas que regulen las interfaces, aunque no existe mucha diferencia entre cómo se aborda el tema en diferentes sistemas operativos.
ossaudio
La emulación ossaudio es la posibilidad más
simple, pero no siempre funciona y generalmente no es una buena idea.
ioctl. Si el código que va a ser
portado usa ioctl para más de un audio,
tendrá que #undef ioctl y usar la forma simple
_ossioctl.
Como compartimos parte de la interfaz de audio con NetBSD y FreeBSD,
comenzar con un porte de NetBSD es razonable. Hay que tener cuidado por
que algunos ficheros cambian de sitio, y algunas entradas en
sys/audioio.h son obsoletas. Además, muchos portes
tienden a estar codificados de modo incorrecto y sólo funcionan
en un tipo de máquina. Algunos cambios son necesarios. Siga
leyendo.
NO SE DEBE ASUMIR NADA SOBRE EL HARDWARE DE AUDIO QUE SE
UTILICE
Código incorrecto es el código que sólo verifica el
campo a_info.play.precision con 8 ó 16 bits, y que
asume como buenas las muestras de audio firmadas o no, basándose
en el comportamiento de soundblaster. Siempre se debe verificar el tipo
de muestra de modo explícito, y codificar de acuerdo con
ésta. Un ejemplo simple:
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:
/* don't forget to deal with what you don't know !!! For instance, */
fprintf(stderr,
"Unsupported audio format (%d), ask ports@ about that\n",
a_info.play.encoding);
}
/* now don't forget to check what sampling frequency you actually got */
Esto viene a ser el fragmento de código más pequeño con que se tendrá que tratar en la mayoría de casos.
Para uso normal simplemente se pide el tipo de condificación
(v.g., AUDIO_ENCODING_SLINEAR) y se saca una
codificación con tipo de "endian" (v.g.,
AUDIO_ENCODING_SLINEAR_LE).
Teniendo en cuenta que una tarjeta de sonido no tiene que usar el mismo
tipo de "endian" que la plataforma, hay que estar preparado
para esto. El modo más fácil es, probablemente, preparar
una memoria intermedia de audio entera, y usar swab(3) si
se requiere un cambio del tipo de "endian". Al tratar con
muestras externas suele ser:
Si se está jugando con una muestra de sonido que se encuentra en el formato de la tarjeta de sonido, se pueden eliminar los pasos 3 y 5.
El hardware puede tener unas limitaciones extrañas, como que no pueda obtener más de 22050 Hz en estéreo y sin embargo obtenga hasta 44100 en mono. En casos como éste y similares hay que ofrecer un cambio al usuario para que escoja sus preferencias, e intentar dar el mejor rendimiento posible. Por ejemplo, sería estúpido limitar la frecuencia a 22050 Hz ya que la salida es en estéreo. ¿Y si el usuario no tiene un sistema de sonido en estéreo conectado a la salida de su tarjeta de audio?
También sería estúpido codificar las limitaciones de soundblaster en su programa. Intente sobrepasar la barrera de 22050 Hz/stereo y verifique los resultados.
Las muestras de frecuencia devueltas por la tarjeta de sonido siempre deben ser verificadas. Una discrepancia del 5% es medio tono, y algunas personas tienen un oído más fino que eso, aunque la mayoría de nosotros no notemos nada. Su aplicación debería ser capaz de llevar a cabo un «remuestreo» al instante, posiblemente de forma nativa o a través de aplicaciones de «remuestreo» de Shannon.
Los muestreos no siempre usan el campo de valores entero. Primero, las muestras grabadas con un volumen bajo no sonarán muy altas en la máquina, forzando al usuario a subir el volumen. Segundo, en máquinas con audio mal aislado, la salida de sonido bajo significa que la mayor parte de lo que se oye es el latido de la máquina y no el sonido esperado. Finalmente, la conversión muda de 16 bits a 8 bits le puede dejar con tan sólo 4 bits de audio utilizables, lo que da como resultado una pésima calidad.
Si es posible, la mejor solución está en escanear todo lo
que se vaya a escuchar con anterioridad, y ponerlo en escala de modo que
entre dentro del campo dinámico. Si no se puede permitir esto
pero puede planear con anterioridad lo que vaya a escuchar, puede
ajustar la potencia del volumen; sólo debe asegurarse de que el
factor de potencia se mantiene en una frecuencia baja en
comparación con la del sonido que quiera escuchar, y que no haya
ningún desbordamiento (éstos siempre
sonarán mucho peor que la mejora que intente conseguir).
Ya que la percepción del volumen del sonido es
logarítmica, generalmente bastará con usar cambios
aritméticos. Si sus datos están firmados, debería
codificar el cambio de forma explícita como una división,
ya que el operador de C >> no se puede portar a datos
firmados.
Si lo demás fallara, debería por lo menos intentar proveer al usuario con una opción escalable de sonido.
Las aplicaciones de poca demanda no deben preocupar mucho. Tenga en cuenta que algunos de nosotros usamos OpenBSD en 68030 de baja demanda, y que si una aplicación de sonido puede funcionar, debería hacerlo.
No se olvide de llevar a cabo bancos de pruebas. Las optimizaciones teoréticas son sólo eso: teoría. Debería recolectar algunas cifras para poder comprobar qué se puede mejorar ampliamente y qué no.
Para aplicaciones de audio de alto rendimiento, como mpegI-layer3, hay que tener en consideración algunos puntos:
write, como una llamada del sistema, incurre en un coste
alto comparado con el proceso interno de audio.
ioctl AUDIO_GETENC
para obtener todos los formatos con los que esté provisto el
dispositivo de audio. Tenga un cuidado especial con el indicador
AUDIO_ENCODINGFLAG_EMULATED. Si ya puede obtener
salidas de todo tipo de formatos extraños de su
aplicación y se encuentra bastante optimizada para ello,
intente usar un formato nativo a cualquier coste. Por otra parte, el
código de emulación presente en el dispositivo de audio
se puede decir que está suficientemente optimizado en ese
caso, y no tiene porqué substituirlo con código nuevo
programado con prisas.
Un modelo que es posible que tenga que seguir para obtener resultados óptimos es el de compilar primero un pequeño programa de pruebas que investigue sobre el hardware de audio disponible, y entonces pasar a configurar su programa de modo que trate de forma óptima con este hardware. Se puede asumir que los usuarios que esperan unos resultados de audio buenos recompilarán su porte cuando cambien de hardware.
Aun cuando OpenBSD no sea un sistema en tiempo real, es posible programar aplicaciones de audio para tiempo real, como puedan ser juegos. En tal caso es necesario rebajar el tamaño de los bloques para que los efectos de sonido no se desincronicen con el juego. El problema con esto es que el dispositivo de audio puede sufrir «inanición», lo que daría unos resultados terribles.
Si solamente quiere sincronizar el audio con la salida de algunos
gráficos, pero se puede predecir el comportamiento de su
programa, es más fácil alcanzar la sincronización.
Ejecute las muestras de audio y pregunte al dispositivo de audio
qué es lo que está ejecutando con
AUDIO_GETOOFFS, y entonces use esa información para
postsicronizar los gráficos. Si se lo pide con bastante
frecuencia (digamos que cada décima de segundo), y siempre que
tenga suficiente potencia para ejecutar su aplicación, puede
obtener una sincronización bastante buena de este modo. Es
posible que tenga que trastocar un poco las cifras por una constante de
contrapeso, ya que existe desincronización entre los informes del
audio, lo que se está ejecutando, y el tiempo que tarda XWindow
en mostrar algo.
En el caso de las aplicaciones de audio, trabajar con el autor del programa original es muy importante. Si su código sólo funciona, por ejemplo, con tarjetas soundblaster, es muy posible que tenga que tratar con otra tecnología en breve.
Si no le envía sus comentarios en ese momento, el trabajo que Vd. haga habrá sido en vano.
También es posible que el autor ya se haya dado cuenta de los problemas con los que esté tratando y que esté trabajando en ellos en su árbol de desarrollo actual. Si los parches que Vd. está escribiendo son de más de un par de líneas, es una buena idea que intente cooperar.