|
|
Artículo realizado por
Ya tenemos lo necesario para mandar la información a la tarjeta
de sonido usando el DMA. Ahora veremos los dos tipos de ficheros que vamos
a "interpretar" mediante la SB:
Los ficheros VOC fueron creados por Creative Labs como un medio
para almacenar los sonidos digitalizados y posteriormente poder interpretarlos
cómodamente, independientemente del método escogido.
Los VOC los vamos a utilizar aquí para interpretar la voz humana
con una frecuencia de 8khz, que abarca la totalidad de los sonidos que
podemos transmitir con nuestra voz, y aunque se podrían interpretar
a 20khz, la calidad sería la misma y el tamaño necesario
para almacenarlo crecería demasiado. Veamos esto:
Supongamos que este es el sonido real de nuestra voz:
José Antonio Suárez.
Capítulo 12.
Programación de la Sound Blaster (III).
3.4.- Ficheros de Sonido
3.4.1.- Ficheros VOC
Y éste el sonido digitalizado:
La calidad de la digitalización dependerá del número de muestras por segundo (puntos del gráfico) que tomemos. A mayor calidad, mayor número de muestras, por lo tanto mayor número de puntos (valores) y espacio necesario para almacenarlo.
Los ficheros VOC se hallan divididos en 2 bloques principales: cabecera y datos.
El bloque de cabecera es pequeño, y su contenido es básicamente informativo.
El bloque de datos, por el contrario, incluye el sonido digitalizado y otros datos de interés. Veámoslo con detalle:
¿Por qué existen varios subbloques? Porque en un mismo archivo de voz digitalizada pueden darse varios eventos: por ejemplo, puede haber un período de silencio en la digitalización, o un bucle de repetición (sobre todo en digitalizaciones de fragmentos musicales). Los posibles tipos de subbloques son 8, pero aquí los VOC se usarán exclusivamente para un único sonido digitalizado, sea voz humana o sonido musical, el único tipo implementado será el de Tipo 1.
Tipos de subbloques:
Tipo 1: Datos de voz. Contiene los datos de la digitalización en sí, e incorpora una pequeña cabecera que informa al driver de la frecuencia de muestreo y del método de compresión usado en este bloque (aquí se usan los que están sin comprimir).
Tipo 2: Continuación de voz. Continúa con los datos de voz del último subbloque.
Tipo 3: Silencio. Define un período de silencio en la digitalización. La longitud del período se incluye en los dos bytes siguientes a la longitud del bloque, y viene dada en unidades de ciclos de muestreo.
Tipo 4: Marca. Se trata de un bloque especial que define una marca en el fichero. Dicha marca puede ser usada para informar a nuestro programa de eventos especiales en el archivo de voz; los valores 0 y FFFFh están reservados para el driver de la SB.
Tipo 5: Texto ASCII. En este bloque se puede incluir cualquier texto que pueda ser de ayuda o información, como nombre del creador, o comentarios sobre el fichero.
Tipo 6: Bucle de repetición. Se informa al driver, del comienzo de un bucle que repite el sonido contenido entre el siguiente bloque de datos y el siguiente bloque de final de bucle.
Tipo 7: Fin de bucle de repetición. Indica el fin, del bucle de repetición, explicado en el tipo anterior.
Tipo 8: Bloque extendido. Incluye atributos del siguiente subbloque de datos, como frecuencia de muestreo, compresión, etc.
// Carga el fichero VOC en la memoria reservada
void CargaVoc(char nombre[12])
{
FILE *ficherovoc;
ficherovoc=fopen(nombre,"rb");
if (!ficherovoc)
{ printf("\nNo se encuentra el fichero %s",nombre);
exit (0); }
fseek(ficherovoc,0L,SEEK_END);
// 32: 20 de cabecera global+12 de cabecera
de bloque.
// Suprimo 100 para evitar el chasquido del
final de bloque de forma que al
// interpretar otro VOC posteriormente, suene
limpiamente sin necesidad de
// resetear de nuevo la SB
longitud=ftell(ficherovoc)-32-100; fseek(ficherovoc,30,0);
frecuencia=getc(ficherovoc); frecuencia=1000000L/(256l-(frecuencia));
fseek(ficherovoc,32L,SEEK_SET); fread(zona5,longitud,1,ficherovoc);
fclose(ficherovoc);
}
Y ahora, por fin, el código que interpreta un bloque de datos digitalizados de hasta 64 Kbytes usando el acceso directo a memoria.
void VocOn(WORD voc)
{
WORD dmaoffset,pagina,segmento,desplazamiento,tiempo;
BYTE tamanoalta,tamanobaja,canaldma=1;
segmento=FP_SEG(zona5);
desplazamiento=FP_OFF(zona5);
asm{
// Transforma la dirección
mov ax,word ptr segmento
mov cl,4
shl ax,cl
add ax,word ptr desplazamiento
mov word ptr dmaoffset,ax
mov ax,word ptr desplazamiento
mov cl,4
shr ax,cl
add ax,word ptr segmento
mov cl,12
shr ax,cl
mov word ptr pagina,ax
mov dx,0xa
// Configura el DMA
mov al,5
out dx,al
mov dx,0xc
xor al,al
out dx,al
mov dx,0xb
mov al,0x49
out dx,al
mov ax,word ptr dmaoffset
mov dx,2
out dx,al
xchg ah,al
out dx,al
mov dx,0x83
mov al,byte ptr pagina
out dx,al
mov dx,3
mov ax,word ptr longitud
// Se indica al DMA cuántos bytes debe transmitir
dec ax
out dx,al
xchg ah,al
out dx,al
mov dx,0xa
mov al,byte ptr canaldma
out dx,al
// Habilita el canal de DMA
mov bx,word ptr longitud
mov byte ptr tamanobaja,bl
mov byte ptr tamanoalta,bh
}
if (voc==0) tiempo=256-(1000000/8000);
else tiempo=256-(1000000/voc);
// Espera para que
la SB se reajuste a lo indicado en Ensamblador, arriba
delay(100);
// Establece la frecuencia
del DSP
EscribeEnDSP(0x40);
EscribeEnDSP(tiempo);
EscribeEnDSP(0x14);
// Empieza la transferencia
EscribeEnDSP(tamanobaja);
EscribeEnDSP(tamanoalta);
}
Ya hemos visto qué son los ficheros VOC y cómo manejarlos.
Ahora vamos a analizar otro tipo de ficheros de sonido pero decenas de
veces más complejo.
3.4.2.- Ficheros MOD
Un fichero MOD es un conjunto de información que por sí sola y con el auxilio del código pertinente, puede generar música.
Todos sabemos que cualquier producto software que incorpore una música
que vaya acorde con el tema que se esté tratando, no sólo
ameniza la sesión, sino que invita a seguir con ella paliando un
poco el aburrimiento.
Hay muchos tipos de formatos para el concepto de MOD. Esto es debido
a que es un formato universal que nació con los revolucionarios
ordenadores Commodore Amiga y que se ha trasladado al mundo del
PC.
Cada grupo de programación importante que lo ha manejado, ha
ido aportando mejoras con respecto al original, de forma que determinar
un estándar es algo bastante arriesgado. Por esto, se ha decidido
implementar el tipo de MOD que es el básico en todo tipo de programas
que soportan este formato y cuyas características son:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Este formato MOD es conocido como el de ProTracker, que es una utilidad para su interpretación y cuyos siguientes 12 campos de cabecera (dentro de un mismo archivo MOD) pueden observarse en la siguiente tabla.
Los campos 2 al 7 se encuentran presentes en todos los samples, mientras que el resto se refiere a todo el archivo MOD y sólo aparecen una vez.
Esta es la cabecera de un MOD:
|
|
|
|
|
|
|
Nombre de la canción en formato ASCII, rellenándose los bytes no usados con ceros. |
|
|
|
Nombre del Sample en ASCII, rellenándose con ceros los bytes no usados. |
|
|
|
Tamaño del Sample en Words. Multiplicando el número por 2 se obtiene el tamaño. Cuando vale 0 o 1, está vacío. |
|
|
|
Finetune. Nibble (4 bits) con signo (-8 a +7 en decimal). Cada incremento cambia la nota un octavo de semitono. |
|
|
|
Valor del volumen de cada Sample (0 a 64 en decimal). |
|
|
|
Offset de repetición. Cuando se quiere que un Sample se repita varias veces. Indica a partir de qué posición, en palabras, comenzará el Loop. Si vale 0 es nulo. |
|
|
|
Longitud de repetición. Si es mayor que 1 indica repetición, y debe repetirse desde la posición indicada en el campo anterior (Campo 6), un nº de palabras igual a este valor. |
|
|
|
Número de patrones. (1 a 128). Indica el número de patrones distintos que existen en el MOD. |
|
|
|
Valor constante = 127. |
|
|
|
Tabla de patrones. Aquí se guardan los patrones que se van a tocar en cada posición (0 a 63). Rellenando con ceros el espacio sobrante. |
|
|
|
Letras M.K. En honor al que descubrió la forma de pasar de 15 samples a 31 sin casi ningún cambio en los MOD. |
|
|
|
Información de los patrones. |
Ahora es necesario dar un concepto más entendible de lo que es un archivo MOD.
Éstos se dividen en patrones (el mismo concepto que el usado
en el Editor, donde cada uno contenía 20 órdenes como máximo),
que son como tablas en las que podemos almacenar hasta 64 notas musicales
distintas para cada uno de los cuatro canales. Así, un patrón
se puede ver como la siguiente tabla:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cada Canal lleva un número de sample que se interpretará por él, así como el valor de la nota para ese sample. Se puede pensar en un sample como en un archivo .VOC que se va interpretando a mayor o menor frecuencia (velocidad), tono...
Como la SB sólo es capaz de producir un sonido a la vez por su única salida (y queremos 4), es necesario hacer una media aritmética entre los sonidos y crear un sonido que sea la combinación de todos ellos con sus características para cada una de los 64 celdas de cada patrón.
Ahora vamos a ver en mayor profundidad cómo son estos patrones:
Como vemos, gracias a estos grupos de bits, designamos el sample, su periodo y sus efectos asociados.
Los efectos son cambios sobre el sonido normal del sample. Por ejemplo: Trémolo, vibrato, glissando, slide, saltos, subidas/bajadas de volumen, etc., los cuales tienden a añadir brillantez (y bastante complejidad al programarlos) y profesionalidad a las melodías que vamos a ser capaz de interpretar. Estos efectos son funciones de dominio público, pero no nos equivoquemos, no quiero decir que sean implementados por códigos creados por otros autores y donados como "dominio público", sino que me refiero al hecho de cómo se implementan. Un ejemplo tonto para aclararlo:
Cuando se descubrió la forma de sumar mediante un ordenador, el autor difundió la idea de cómo hacerlo, es decir, el método, pero no el código, que dependía de la máquina. Pues igualmente, el que creó el efecto "vibrato" en la música clásica, difundió la idea de qué era y cómo realizarlo. Por esto, sólo hay una forma de crear un "vibrato": particionar la onda de sonido en varios valores y solapar las zonas contiguas. Con lo que el efecto es: de dominio público.
El código que lo implementa y está incluido en el fichero "CARGAMOD.ASM", es:
Vibrato: mov dh,dl
and dl,0Fh
shr dh,4
shl dh,2
add [di+VibPos],dh
mov dh,[di+VibPos]
mov bl,dh
shr bl,2
and bx,1Fh
mov al,[SinTable+bx]
mul dl
rol ax,1
xchg al,ah
and ah,1
test dh,dh
jns VibUp
neg ax
Inserto
Los códigos fuente que se encargan de este tipo de ficheros están realizados en Ensamblador puro. Ahora es necesario aclarar el porqué en el cambio de la filosofía de programación utilizada hasta este momento (Ensamblador Inmerso y C puro).
Casi toda la parte que se encarga de ejecutar las órdenes están en C o en Ensamblador Inmerso. Originalmente las rutinas para interpretar los archivos MOD fueron creadas en C, pero eran demasiado lentas y el resultado era decepcionante para el esfuerzo invertido en comprender este tipo de archivo tan complejo y extremadamente lioso.
Posteriormente fue traducido al Ensamblador Inmerso, en busca de la velocidad imprescindible, pero aunque los resultados fueron los esperados, apareció un nuevo problema: algunas instrucciones de Ensamblador no estaban implementadas, tales como la creación de una pila y la utilización de diversos operandos y direccionamientos de memoria, con lo que hubo que suplirlos con técnicas que no eran las más adecuadas, pero que constituían la única solución.
Esto dio como resultado un código que verdaderamente interpretaba de forma correcta los MOD, pero con el inconveniente insalvable de que al conjuntarlo con el resto del código del Proyecto, no funcionaba de ninguna manera. Esto era debido a las características del extremadamente rígido modelo de compilación "Large".
Concretando, para hacer que el lector de MOD funcionase conjuntamente con el resto del código, debía de compilarse todo en el modo Compact, pero de esta forma algunos procedimientos no funcionaban por su necesidad de acceder a punteros lejanos forzosamente, y a que la conversión directa del C (far *) no funciona correctamente en esta versión del compilador (tal y como se refleja en los FAQ "Frecuently Asqued Questions" de Borland).
Tras esto, la única esperanza fue hacer el último esfuerzo antes de tirar el código a la basura y olvidarse de esta parte tan importante, por lo que finalmente traduje todo a Ensamblador puro y lo compilé con el Turbo Assembler 4.0, creando los OBJ pertinentes y "linkándolos" con el resto del código desde el compilador Borland C++ 3.1.
Esto dio como resultado tres módulos distintos: Sb.ASM, CargaMod.ASM y TocaMOD.ASM, que por su aislamiento con respecto al resto del código, tienen que incluir rutinas ya implementadas en C y Ensamblador Inmerso como las de reserva de memoria mediante la función 48h, el acceso a DMA, la inicialización y detección de la SB, etc.
Pero Borland volvió a hacer de las suyas. Ahora indicaba que
se había excedido su capacidad de compilación debido a: Group
"Dgroup" execeds 64kb.
|
|