Música y sonido
El proceso de crear un canal de sonido, lanzar efectos de sonido y
música en él, y usar puntos de entrada para manejarlos, debería
resultar muy familiar a quienes han leido ya la sección sobre
implementación de ventanas gráficas. A quienes se hayan saltado ese
capítulo para venir directamente aqui, se les recomienda muy
fervientemente que lean antes la sección sobre gráficos, especialmente
la parte sobre manejo de ventanas gráficas.
Otra lectura necesaria es la sección sobre Blorb, ya que usarás Blorb
para hacer que los ficheros de sonido estén disponibles para tu juego,
en la misma forma que lo hiciste para los ficheros de gráficos. Los
formatos de sonido que actualmente soporta Glk, y por tanto Inform
Glulx, son AIFF y MOD. El primero es un formato de sonido sin
compresión, una especie de equivalente multiplataforma del más
conocido WAV
de Microsoft, que produce ficheros que, con los
parámetros adecuados, son de calidad suficiente como para grabar con
ellos un CD de audio. (El inconveniente es que comparados con un
formato con pérdida de calidad como el MP3, son bastante más
enormes). MOD es bastante diferente. Los ficheros MOD se crean con
programas llamados trackers, en los cuales tú cargas algún tipo
de muestra de sonido breve (un golpe de tambor, un acorde de guitarra,
un ladrido de un perro, lo que quieras) y despues compones una especie
de partitura, colocando notas en una rejilla con el tono adecuado,
seleccionando un tempo, aplicando efectos especiales a las
muestras, etc. Puesto que el fichero no almacena el sonido final de la
música, sino sólo las piezas de las que se forma junto con
instrucciones de cómo tocarlas, los ficheros MOD son bastante
pequeños. (Además es muy divertidos de coleccionar y retocar. Si usas
una máquina Windows, te recomiendo encarecidamente el Tracker
Modplug).
Así que pongamos que estás programando un ascensor, y que decides que
cuando el protagonista entre en él, debe sonar música ambiente de
ascensor. El proceso sería como sigue. Primero, necesitas crear un
canal de sonido, y darle un número-roca de 410 o superior (en este
caso, elegiremos 410). Escribimos las líneas siguientes en el
programa:
Constant GG_CANALMUSICA_ROCK 410;
Global gg_canalmusica = 0;
|
Y ya que estamos, añadamos una variable global para almacenar un
identificador de qué música está sonando en cada momento del juego:
Global musica_actual = 0;
|
Y naturalmente, en algún momento necesitaremos abrir el fichero de
recursos Blorb y darle al fichero de sonido un nombre que podamos usar
en el código fuente. Digamos que te apetece mofarte de las leyes del
mercado y llamar al fichero Muzak
. Cómo indicar esto puede
variar; si estás usando iblorb
, la línea será algo como:
SOUND Muzak musicascensor.mod
|
Una vez liquidada la preparación anterior, podemos dedicarnos al
negocio de abrir el canal de sonido y enviarle música. Abrirlo es
fácil: basta añadir lo siguiente en Inicializar
:
if (gg_canalmusica == 0)
gg_canalmusica = glk_schannel_create(GG_CANALMUSICA_ROCK);
}
|
(Personaliza lo anterior en tus propios programas en la forma obvia.)
Con esto, el canal está ahora abierto. Lo siguiente: tocar sonidos en
él. Esta vez el comando clave es glk_schannel_play_ext()
que
necesita cuatro argumentos:
- El nombre del canal de sonido. Observa que puedes tener
múltiples canales de sonido - digamos, uno para efectos especiales
y otro para música - pero en cualquier plataforma llegarás
eventualmente a un límite. Tocar múltiples MODs a la vez es una
perspectiva especialmente dudosa.
- El nombre del sonido a tocar (pero lee más abajo)
- El número de veces que el sonido debe ser repetido. Si quieres
que se repita por siempre, hasta que le digas explícitamente que
pare (o hasta que el juego acabe), pon -1.
- Este debe ser 0, a menos que quieras recibir eventos del tipo
evtype_SounNotify
en tu rutina HandleGlkEvent()
cuando el sonido
finalice su ejecución. Por ejemplo, podrías querer programar una
mansión encantada, en la que suenen al azar espectrales ruidos de
monstruos. El código podría ser así:
[ HandleGlkEvent ev context sonido_nuevo;
context = 0; ! evitar el warning de variable no usada
switch (ev-->0) {
evtype_SoundNotify:
sonido_nuevo = random(Aullido, Chirrido, Grunyido);
glk_schannel_play_ext(gg_canalmusica, sonido_nuevo, 1, 1);
}
];
|
Pero nos estamos saliendo un poco de madre. Volvamos con lo nuestro.
Al igual que ocurría con las ventanas gráficas, no queremos
simplemente lanzar directamente una música a un canal de sonido,
especialmente si va a repetirse en bucle. Si lo hiciéramos, el jugador
podría hacer que el protagonista entrara en el ascensor, después
restaurar un juego salvado en un punto en que el protagonista estaba
en medio de un campo de batalla - ¡y la música del ascensor aún
seguiría sonando! Para evitar estas potenciales pifias, usaremos la
variable que hemos creado antes, y escribiremos una rutina como esta:
[ ReiniciarCanalMusica;
if (gg_canalmusica) {
if (musica_actual == 0) glk_schannel_stop(gg_canalmusica);
else glk_schannel_play_ext(gg_canalmusica, musica_actual, -1, 0);
}
];
|
El código dentro de las llaves hace sonar la música que debería estar
sonando, indicada por la variable musica_actual
, o detiene la musica
completamente si esa variable está a 0 [el valor de esa variable sí se
recupera al cargar una partida]. Por supuesto, si el canal de sonido
no está abierto, no intentará tocar nada. Ahora podemos crear un
objeto ascensor, con una rutina antes
como esta:
antes [;
Meterse: musica_actual = Muzak;
ReiniciarCanalDeMusica();
print "Entras en el ascensor. La selección musical de
hoy: arreglos de Korn para xilófono y flauta de
pan.^";
JugadorA(Dentro_Ascensor);
],
|
Y finalmente, una rutina IdentifyGlkObject()
para asegurarse de que
todo es restaurado correctamente tras un REINICIAR o UNDO. Si ya
tienes una, mézclala con esta otra:
[ IdentifyGlkObject fase tipo ref rock res id;
if (fase == 0) { ! Poner a cero nuestras variables de glk
gg_canalmusica = 0;
return;
}
if (fase == 1) { ! En la fase 1 no se hace nada con los canales
! de sonido.
return;
}
if (fase == 2) {
! Iterar sobre todos los canales de sonido existentes,
! e identificar el nuestro.
id = glk_schannel_iterate(0, gg_arguments);
while (id) {
switch (gg_arguments-->0) {
GG_CANALMUSICA_ROCK: gg_canalmusica = id;
}
id = glk_schannel_iterate(id, gg_arguments);
}
! Ahora, que ya tenemos inicializada la variable
! del canal, necesitamos actualizar la musica que
! suena, o desconectarla
ReiniciarCanalDeMusica();
}
];
|
El código de la fase 2 hace práctiamente lo mismo que lo que hacía el
código de la fase 1 para las ventanas gráficas; la diferencia es que
un canal de sonido no es una ventana, ni un stream (flujo) ni
una referencia a fichero, de modo que la fase 1 no ha de tratar con
ellos.
¡Y esto debería ser todo! Por supuesto, si intentas esto en casa,
puede que te lleves una desilusión ya que, en el momento en que se
escribe esto, sólo un par de plataformas tienen soporte para MOD
(Windows y DOS, y encima el caso DOS tiene bugs). Pero otras
plataformas se subirán al tren enseguida.