xmp es un player para línea de comandos que soporta un montón de módulos musicales y al mismo tiempo es una librería (está desarrollado sobre esa librería) que nos permite utilizarla para por ejemplo reproducir módulos en nuestros programas.
En este articulo simplemente vamos a integrar la librería xmp.h junto con SDL.h y ver como podemos trabajar con cientos de tipos diferentes de módulos e integrarlos en nuestros programas/demos/juegos/… con SDL2.
Información sobre xmp y libxmp (versión 4.4.1):
Website: xmp.sourceforge.net/
API documentación: xmp.sourceforge.net/libxmp.html
Ejemplos: xmp.sourceforge.net/examples/
Descargar libxmp: libxmp-4.4.1 aquí.
Instalar libxmp-dev en Debian/Ubuntu:
apt-get install libxmp-dev
Entre los módulos que soporta (más de 90) este player y su librería podemos destacar estos:
- Protracker
- OctaMED
- MilkyTracker
- Fast Tracker
- Fast Tracker II
- Impulse Tracker
- DIGI Booster
- Scream Tracker 2
- Scream Tracker 3
- Ice Tracker
- Oktalyzer
- Composer 669
- Digitrakker
- Farandole Composer
- Funktracker
- Imago Orpheus
- Liquid Tracker
- Multitracker
- Poly Tracker
- Real Tracker
- TakeTracker
- Ultra Tracker
- X-Tracker
- Game Music Creator (GMC)
- Galaxy Music System
- Digital Tracker
- Flextrax
- Graoumf Tracker
- Octalyser
- TCB Tracker
- Archimedes Tracker
- …
Al margen de que con SDL2 base se puede reproducir wav y que con otras librerías podemos reproducir ogg, mp3, … la idea es poder secuenciar módulos más que otra cosa. Eso nos permite tener acceso a los instrumentos e incluso mutear canales.
Yo prefiero usar xmp.h ahora mismo para poder incluir música molona que se genera en tiempo de ejecución. No obstante en futura entradas veremos que para un ogg o un mp3 no necesitamos añadir nada extra.
Lo primero es probar que la cosa funciona bien. Si hemos instalado la librería (tanto sdl base como libxmp-dev) vamos a ver uno de los ejemplos que ofrecen junto a la librería xmp.
Descargar el código de ejemplo con módulos de sonido y todos los directorios necesarios: playmod.tar.gz
El makefile que solemos usar ha sufrido en este caso una alteración (-lxmp) para poder compilar:
CC := gcc CFLAGS := -Wall LINKER_FLAGS = `sdl2-config --cflags --libs` -lxmp test: make clean make build build: $(CC) $(CFLAGS) -o bin/playmod playmod.c $(LINKER_FLAGS) run: ./bin/playmod clean: -rm bin/playmod
Y aquí va el código de ejemplo que viene con libxmp (que guardamos en un archivo llamado playmod.c:
#include <stdio.h> #include <stdlib.h> #include <SDL.h> #include <xmp.h> static int playing; static void fill_audio(void *udata, Uint8 *stream, int len) { if (xmp_play_buffer((xmp_context)udata, stream, len, 0) < 0) playing = 0; } static int sdl_init(xmp_context ctx) { SDL_AudioSpec a; if (SDL_Init(SDL_INIT_AUDIO) < 0) { fprintf(stderr, "sdl: can't initialize: %s\n", SDL_GetError()); return -1; } a.freq = 44100; a.format = AUDIO_S16; a.channels = 2; a.samples = 2048; a.callback = fill_audio; a.userdata = ctx; if (SDL_OpenAudio(&a, NULL) < 0) { fprintf(stderr, "%s\n", SDL_GetError()); return -1; } return 0; } static void sdl_deinit() { SDL_CloseAudio(); } int main(int argc, char **argv) { xmp_context ctx; struct xmp_module_info mi; struct xmp_frame_info fi; int i; ctx = xmp_create_context(); if (sdl_init(ctx) < 0) { fprintf(stderr, "%s: can't initialize sound\n", argv[0]); exit(1); } for (i = 1; i < argc; i++) { if (xmp_load_module(ctx, argv[i]) < 0) { fprintf(stderr, "%s: error loading %s\n", argv[0], argv[i]); continue; } if (xmp_start_player(ctx, 44100, 0) == 0) { /* Show module data */ xmp_get_module_info(ctx, &mi); printf("%s (%s)\n", mi.mod->name, mi.mod->type); /* Play module */ playing = 1; SDL_PauseAudio(0); while (playing) { SDL_Delay(10); xmp_get_frame_info(ctx, &fi); printf("%3d/%3d %3d/%3d\r", fi.pos, mi.mod->len, fi.row, fi.num_rows); fflush(stdout); } xmp_end_player(ctx); } xmp_release_module(ctx); printf("\n"); } xmp_free_context(ctx); sdl_deinit(); return 0; }
En el directorio res dentro de bin he dejado algunos modulos de ejemplo ( 56k.es/wp-content/uploads/2021/01/mods.zip ). No necesitas descargarlos si has bajado el tar.gz con el código de ejemplo.
Lo compilamos con make en el directorio en el que tenemos el makefile. (make build)
De modo que para probar si chuta así:
cd bin ./playmod res/lickit.mod
Veremos algo como esto:
Lo cierto es que el ejemplo sirve para saber que la cosa chuta pero no es lo óptimo. Lo ideal es usarlo en nuestro programa para elegir desde este cuando sonará un modulo y que modulo será.
Con paciencia poco a poco.
Información sobre los módulos importante.
Hace un tiempo que programé un player para módulos formato protracker (para AmigaOS 4.1).
Me tocó buscar mucha documentación sobre como funcionan realmente estos módulos. Este texto rescatado de textfiles.com me sirvió de mucha ayuda: www.textfiles.com/programming/AMIGA/protrack.ami.
Aunque tiene extensión ami es un texto plano que puedes abrir con cualquier editor.
También puede ser de utilidad este texto con conclusiones sacadas a través de ingeniería inversa: www.textfiles.com/programming/FORMATS/modulesg.txt
Lo interesante es que cuando cargas un archivo y lo atacas podrás ir posicionándote para sacar la información que necesitas para reproducirlo. En ese archivo se define por ejemplo donde se almacena el titulo de un modulo. el nombre de los samples, …:
Offset Bytes Description ------ ----- ----------- 0 20 Songname. Remember to put trailing null bytes at the end... 20 22 Samplename for sample 1. Pad with null bytes.
Es bueno conocer como son estos archivos y su estructura, no obstante como estamos tirando de libxmp no necesitaremos conocer tanto y vamos a poder realizar la mayoría de cosas sin siquiera saber mucho sobre los módulos. Nos dedicaremos a cargar música, pausar música, pararla, extraer información de los módulos y poco más.
Si andas en GNU+Linux aquí tienes un post que lo mismo te permite entender más a fondo el formato mod protracker: 56k.es/fanta/anatomia-binaria-de-un-mod-protracker-con-dd/
Salvo que queramos realizar un editor tracker en SDL lo normal es que usemos la librería para simplemente reproducir música.
Y que si queremos un tracker en SDL milkytracker es crema.
Saludos cordiales.