An XM file only says what to play; the sound depends on how a program steps through it.
The explorer's replayer is a port of the one in 8bitbubsy's FastTracker 2 clone
(ft2_replayer.c), which reproduces FastTracker 2 2.08 and 2.09, bugs included,
with the clone's mixer reduced to linear interpolation. This page describes what it does;
the commands themselves are on the effects
page.
Ticks, rows and tempo
Playback runs in ticks. A row lasts speed ticks (the header's default tempo, 6, changed by Fxx with xx below 32). A tick lasts 2.5 / BPM seconds (the header's default BPM, 125, changed by Fxx with xx of 32 or more), so at 125 BPM a tick is 20 ms and a row at speed 6 is 120 ms. The replayer makes each tick a whole number of output frames, floor(2.5 × rate / BPM), which is what FT2 and OpenMPT do (OpenMPT's classic tempo mode); at 44,100 Hz and 118 BPM a tick is 934 frames, though it is 934.3 exactly. A new tempo takes effect on the tick that sets it.
On each tick the replayer first does the work of that tick for every channel, from channel 1 to the last, then the voices are updated and the tick's frames are mixed. On the first tick of a row the cells are read (volume column, then effect column); on the other ticks the continuing effects run. Envelopes, fadeout and auto-vibrato run on every tick.
Order and rows
- After the last tick of a row the next row is chosen. Normally it is the row after, and when the pattern ends, row 0 of the next position of the order list.
- A Dxx (pattern break) goes to the next position at row xx (decimal; above 63 means row 0). A Bxx (position jump) goes to position xx at row 0, or at the row of a Dxx on the same row. A position past the end starts at position 0.
- E60 and E6x (pattern loop) go back to the remembered row. FT2 keeps the loop's row when the pattern changes, so the next pattern starts at that row unless a Bxx or Dxx reset it (see the extended commands). A row number beyond the next pattern's length becomes 0.
- EEx holds the row for x more passes. The cells are read once; on the held passes only the continuing ("other tick") effects run.
- When the last position has played, the song goes on at the restart position (the header gives it). The explorer measures and plays the song up to that point once.
- F00 sets the speed to 0, and FT2 then stands still; the explorer stops there.
- A song that never reaches the end (a Bxx back to a row already played, or an endless pattern loop) is cut when it jumps back to a row it has already played, or after an hour.
Reading a row
For each channel the cell is handled in this order. The volume column and the effect column both run in the same tick; the rules are those of the clone.
- The volume column byte is stored for the later ticks.
- If the previous row had a vibrato or arpeggio that this row does not continue, the period goes back to the note's period.
- An instrument number from 1 to 128 selects the channel's instrument (an invalid one is ignored).
- A note delay (ED1 to EDF) stops here: nothing else is done on the first tick.
- Unless the effect is E90: with a tone portamento (3xx, 5xy or a volume column Fx), the note sets the target period and is not played, and an instrument number resets the volume and panning and the envelopes (not the sound). With K00 the note is released. Otherwise, with no note, an instrument number resets the volume, panning and envelopes; the sound goes on.
- With a note (1 to 96) the sample is chosen from the instrument's map; the sample's default volume and panning are loaded; the period is set from the note, the relative note and the finetune (E5x replaces it); the voice is started (at the offset a 9xx gives); the vibrato and tremolo positions, the retrigger counter and the key-off flag are reset; the volume and panning envelopes restart, the fadeout is reset to 32768 and the auto-vibrato starts.
- A note of 97 releases the note.
- An instrument number with a note also loads the sample's default volume and panning; without one, the volume and panning of the sound playing are reset to the new instrument's.
- The volume column and effect column then run their "first tick" part.
The format description's table of events (from the xmp documents) agrees with this, with the two cases it calls "Switch" and "Play":
| Event | No instrument | Same instrument as before | Another valid instrument | Invalid instrument |
|---|---|---|---|---|
| New note | Switch: a new note at the current volume | Play: a new note at the sample's volume | Play | Cut (but if there was no instrument yet, Switch) |
| New instrument, no note | (none) | Old volume: the default volume is set, the sound goes on | Old volume | Old volume |
| Tone portamento | Continue | Old volume | Old volume | Old volume |
Volume and panning
The channel volume (0 to 64) is multiplied by the global volume (0 to 64), the fadeout (32768 down to 0) and the volume envelope (0 to 64) to give a value from 0 to 1. The panning (0 to 255) is the channel's, moved by the panning envelope: the envelope's distance from 32 is scaled by how far the panning is from the middle. The left side gets the square root of (256 − pan) / 256 and the right side the square root of pan / 256.
When a voice's volume changes it is ramped to avoid clicks: over the length of a tick for a slide or an envelope step, and over 5 ms (rate / 200 frames) when a note starts, the volume is set, or a note is cut. A note that replaces another one fades the old one out over 5 ms while the new one fades in from zero. At the start of every tick the previous ramp is taken as finished.
Envelopes
Each channel keeps a tick counter and a value for each envelope. The counter restarts (at 65535, so that it becomes 0 on the first step) when a note or an instrument number starts, and Lxx sets it. Each tick:
- The counter goes up by one.
- If it equals the tick of the current point, the value becomes that point's (value × 256); the next point is chosen; if the loop is on and the point is the loop end the envelope goes back to the loop start (unless it is the sustain point and the note has been released); a sustain point holds the envelope there while the note is held; the slope to the next point is ((next − this) × 256) / (ticks between), in whole steps; if the ticks between two points are 0 the slope is 0.
- Otherwise the value moves by the slope. A value that leaves 0 to 64 is cut to the end of the range.
A key off moves the counter back to the tick of the current point, so a released envelope carries on from the sustain point. A key off on an instrument with its volume envelope off sets the volume to 0 at once. An envelope that reaches its last point stays there. A panning envelope uses the same steps but works on the panning, and (an FT2 bug) the key off adjustment above is made when the panning envelope is off.
Fadeout
After a key off the fadeout volume (32768 when the note started) is reduced by the instrument's fadeout value each tick, down to 0, and the channel's volume follows it. It is part of the volume formula above, so it only matters while the volume envelope is on: with it off, a key off silences the note at once.
Auto-vibrato
Each tick, when the instrument's depth is not 0, a position runs on by the rate (0 to 255, wrapping) and the period is changed by (wave × amplitude) / 16384, with the wave from −64 to 64 (a sine table, a square of ±64, or two ramps) and the amplitude the depth × 256 once the sweep has finished. During the sweep the amplitude grows by depth × 256 / sweep each tick until its top byte exceeds the depth, and stops growing at a key off. The result is added to the period for that tick only; a result above 31,999 plays at 0 Hz.
Vibrato and tremolo
The vibrato position goes up by the speed each tick (wrapping at 256). The wave value comes from the position divided by 4, within 0 to 31: the sine is a table of 32 values (0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, then back down); the ramp is the index times 8 (inverted for the second half); the square is 255. The value is multiplied by the depth and divided by 32 and added to the period in the first half of the cycle (positions 0 to 127) and subtracted in the second. Tremolo is the same on the volume, divided by 64. E4x and E7x choose the waveform.
Periods, frequencies and sample steps
The pitch of a channel is a period. For a note it comes from a table indexed by the note (plus the relative note) and the finetune (frequency tables). Portamentos, vibrato and arpeggio change the period. The voice's step through the sample (in sample points per output frame) is the frequency divided by the output rate. In the linear table the frequency is 8363 × 2(4608 − period) / 768 Hz; in the Amiga table 8363 × 1712 / period. A period of 0 plays at 0 Hz.
Mixing
Each voice reads its sample at its step, interpolating between two neighbouring points. At the end of a forward loop the next point is the loop's first; the sample after the end of a ping-pong loop is the loop's last point, and the position reflects. A sample without a loop stops at its end, and its last point is what the interpolation sees after it. A stereo sample is played as the mean of its two channels. The channels are added, scaled by 12/32 (FastTracker 2's amplification of 12) and clipped to ±1. Sample start positions beyond the sample's end silence the voice.
The explorer renders at 44,100 Hz, or 32,000 or 22,050 Hz for a very long song, and cuts a song that would not fit in memory; the title of the Song section says so.
Where players differ
FastTracker 2's behaviour is not specified anywhere but its own code, and other players decide differently in the edge cases:
- Global volume (Gxx) reaches the channels to the right of it on the same row at once, and the ones to the left of it only when their volume is next recalculated. OpenMPT applies it to all channels at once.
- Empty patterns have 64 rows in FT2 and the clone; the explorer, OpenMPT and libxmp use the row count in the header.
- Loops outside a sample (loop start plus length past the end) are turned off by the clone and cut at the end of the sample by OpenMPT.
- Stereo samples are mixed down in the clone; OpenMPT and libxmp play them in stereo.
- Notes started mid-tick: OpenMPT starts a note a few frames later than the clone does (its ramp and tick boundaries differ); this shows as a shift of about three frames when two renders are compared.
- Volume ramping and interpolation differ in detail: FT2 and the clone ramp over 5 ms and a tick as above, and the clone offers linear, cubic and sinc interpolation, where this replayer has linear only.
- OpenMPT only: the Zxx macros and plug-ins, the pitch envelope, NNA and duplicate checks, the other extension fields, instrument-level resampling modes. The explorer plays none of them.
How it was checked
The replayer's output was compared with libopenmpt 0.8.9's, built for the purpose (Specs/xm in
the research folder, run as openmpt123 --render with linear interpolation). Each
file was rendered by both for the first 30 seconds at 44,100 Hz; the comparison finds the shift of at most 8 frames
at which the two agree best, scales one by a single gain, and reports the signal-to-error ratio.
| Result over 97 real XM files (30 seconds each) | |
|---|---|
| Median signal-to-error ratio | 27 dB (correlation 0.999) |
| At 20 dB or better | 66 files |
| Below 10 dB | 15 files (correlation 0.85 to 0.95) |
| Best | 50 dB |
| Fitted gain | 0.92 to 1.0: the explorer's level matches libopenmpt's |
| Playing time | the explorer's and libopenmpt's agree to within 0.1 s on the files compared (306.0 s against 306.03 s for one 5-minute song) |
The files that agree worst are chip-style songs that use instruments with auto-vibrato, very short looped waves and tiny finetune offsets, where a difference of a fraction of a period unit builds up as drift over a long song and the error ratio falls although the two sound alike. The cause was not found; the clone's code was followed, and in one of them (KB_ML2) the match improved when the auto-vibrato was switched off in the comparison, so OpenMPT's auto-vibrato and the clone's may differ in some detail. libxmp's own player, rendered the same way, agrees with the explorer less well than libopenmpt does on the files tried. The libxmp and OpenMPT test modules for single FT2 behaviours were also rendered and differ from libopenmpt's output in places where OpenMPT does not copy FT2 (the ones listed above, such as the global volume order), and where a test module's signal is a steady level, so that a ratio of errors says little.
A comparison with FastTracker 2 itself was not possible: no copy was run.