A small XM (Fasttracker II Extended Module) player library. Main features:
Small and hackable: many features can be disabled at compile-time, or are optimized out by the compiler if not used. It is easy to bundle libxm in your game, demo, intro, etc.
Fairly portable. Minimal dependencies (just libm). No memory allocations. Big-endian compatible (tested on s390x).
Reasonable accuracy compared to Fasttracker 2. Deviations from FT2 playback, that aren't obviously bugs in FT2, are also libxm bugs. If you have a module that plays incorrectly, please test it in FT2/FT2clone and open an issue!
Can load most XM/MOD files, however playback accuracy of non-XM is best-effort.
Timing functions for synchronising against specific instruments, samples or channels.
Samples can be loaded and altered at run-time, making it possible to use libxm with softsynths or other real-time signal processors.
Written in C23 and released under the WTFPL license, version 2.
Build the library:
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -Bbuild -Ssrc make -C build
Build a specific example:
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -Bbuild-FOO -Sexamples/FOO make -C build-FOO
To build a shared library and link dynamically, use
cmake -DBUILD_SHARED_LIBS=ON
.To see a list of build options, use
cmake -L
orcmake-gui
.To use libxm in your program, put these lines in the
CMakeLists.txt
of your project, then#include <xm.h>
:add_subdirectory(/path/to/libxm/src libxm_build) target_link_libraries(my_stuff PRIVATE xm)
libxmtoau
can be compiled (with all playback features enabled) and crushed to about 3871 bytes (x86_64-linux-gnu).
cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DXM_VERBOSE=OFF -DXM_LIBXM_DELTA_SAMPLES=OFF -DXM_LINEAR_INTERPOLATION=OFF -DXM_RAMPING=OFF -DXM_STRINGS=OFF -DXM_TIMING_FUNCTIONS=OFF -DXM_MUTING_FUNCTIONS=OFF -DXM_SAMPLE_TYPE=float -DXM_SAMPLE_RATE=44100 -Bbuild-libxmize -Sexamples/libxmize
make -C build-libxmize libxmtoau
strip -R .eh_frame_hdr -R .eh_frame build-libxmize/libxmtoau
xzcrush build-libxmize/libxmtoau
If you are using libxm to play a single module (like in a demo/intro), disable features as suggested by libxmize --analyze
to save a few more bytes.
For example, libxmize --analyze mindrmr.xm
suggests -DXM_DISABLED_EFFECTS=0xFFFFD9FBFFDE68E1 -DXM_DISABLED_VOLUME_EFFECTS=0x0CC0 -DXM_DISABLED_FEATURES=0xFFFFFFFFFFF7EE04 -DXM_PANNING_TYPE=8
. We only want to play the module once and quit, so we can use -DXM_LOOPING_TYPE=1
. Compiling with these new flags, the resulting binary is crushed to 2705 bytes.
libxm.js is a very simple XM player/visualiser that runs in a browser (emscripten port).
xmgl
is a simple music visualiser that uses OpenGL, GLFW and JACK for very precise audio synchronisation. See a demo here: https://www.youtube.com/watch?v=SR-fSa7J698xmprocdemo
: a simple non-interactive demo that plays back a single module with procedurally generated samples. Somewhat optimized for size. (Dream Candy by Drozerix, public domain. Thank you Drozerix for the great music!)libxmize
converts a.xm
module to the libxm format. It is highly non-portable and is meant for static linking and sizecoding (loading code is much shorter and libxm format compresses better).libxmtoau
reads standard input (a file generated bylibxmize
) and writes a .AU file to standard output. Somewhat optimized for size, see size section above. You can test it with, for example,libxmize file.xm | libxmtoau | mpv -
.
Here are some interesting modules, most showcase unusual or advanced tracking techniques (and thus are a good indicator of a player's accuracy):
- Cerror - Expatarimental
- Lamb - Among the stars
- Raina - Cyberculosis
- Raina - Slumberjack
- Strobe - One for all
- Strobe - Paralysicical death
- E8y panning effect is supported, FT2 has no support for this command
- Glissando control (E3y) with Amiga frequencies is not yet supported
- Arpeggios after pitch slides with Amiga frequencies are subtly incorrect
- Amiga filter toggle (E0y) is not supported, and is unlikely to be
- Invert loop / funk repeat (EFy) is not supported, and is unlikely to be
- Period wraparound after a long slide down (with eg, 2xx) is not accurate
- Global volume effects (Gxx/Hxy) are subtly incorrect
- Tone portamento (3xx/Mx) does not "lock" its direction
- Arpeggio (0xy) does not reset vibrato offset (Vy) when Spd=1
- (MOD only) Sample offset (9xx) beyond sample loop end will cut the note
- Can be manually toggled with
FEATURE_ACCURATE_SAMPLE_OFFSET_EFFECT
- Can be manually toggled with
To report more, please open an issue.
Some test XM files are in the tests
directory.
A few tests can be automatically run (failing tests marked XXX are not regressions, but bugs/inaccuracies that have yet to be fixed):
cmake -Bbuild-tests -Stests
make -C build-tests all test
Other tests require manual checking, see the table below.
Test | Status | Tested against | Extras
--------------------------------+----------------+------------------------+------------------------------------------------
autovibrato-triggers.xm | MOSTLY | FT2clone 1.94 | Should sound identical. Use a spectrogram as it is very hard to hear subtle changes in pitch.
pattern-loop-quirk.xm | PASS | MilkyTracker | Should play the same notes at the same time.
pos_jump.xm | PASS | Milkytracker, OpenMPT | Only one beep should be heard.
ramping.xm | PASS | FT2clone 1.94 | If XM_RAMPING is ON, output should be mostly frame for frame identical.
waveform-control-autovibrato.xm | PASS | FT2clone 1.94 | Should sound identical. Patterns 0 and 1 should also sound identical. Use a spectrogram as it is very hard to hear subtle changes in pitch.
waveform-control-combo.xm | PASS | FT2clone 1.94 | Should sound identical.
waveform-control-tremolo.xm | PASS | FT2clone 1.94 | Should sound identical.
waveform-control-vibrato.xm | PASS | FT2clone 1.94 | Should sound identical.
Thanks to:
Thunder [email protected], for writing the
modfil10.txt
file;Matti "ccr" Hamalainen [email protected], for writing the
xm-form.txt
file;Mr.H of Triton and Guru and Alfred of Sahara Surfers, for writing the specification of XM 1.04 files;
All the MilkyTracker contributors, for the thorough documentation of effects;
Vladimir Kameñar, for writing
The Unofficial XM File Format Specification
;All the people that helped on
#milkytracker
IRC;All the libxm contributors.