module formats
2024-07-21

The XM Format

In 1993, the demogroup Triton entered FastTracker as another Swedish challenger in the Nordic tracker competition, joining NoiseTracker (Sweden), ProTracker (Norway) and ScreamTracker (Finland), among others. The original FastTracker was a competent tracker that stood out for having a graphical user interface that looked almost exactly like ProTracker did on the Amiga, at a time when most PC trackers were text-mode based, [FT1].

But it was FastTracker II from 1994 that permanently raised the level module music. The tracker supported the “extended module” (XM) format that Triton had developed, which included a number of new features that made much higher quality modules possible.

XM has enjoyed broad and lasting popularity and continues to be widely used and supported. The popular music player XMPlay started out, as the name suggests, exclusively as an XM player. MilkyTracker, one of the most popular trackers today, is being developed into a contemporary version of FastTracker II. Both MilkyTracker and OpenMPT provide excellent compatibility with XM modules. And thanks to the good module support of the original Unreal Engine, XM modules became part of the soundtrack of such high-profile games as Unreal and Deus Ex.

What’s New?

With FastTracker II and the XM format, Fredrik Huss Magnus Högdahl of Triton followed an unusually deliberate and disciplined approach to choosing features. FastTracker II can play both ProTracker MODs with various extensions and ScreamTracker STM and S3M modules, and therefore supports most of their capabilities. Beyond that, however, it focuses on creating a much richer and more flexible instrument model, rather than adding lots of new effect commands or making existing ones more complicated. Most of the few new effects in the XM format directly support some instrument functionality.

Here’s a list of some of the more important innovations relevant to the XM format and module playback.

  • a much-improved instrument model, with multiple samples per instrument, support for volume and panning envelopes, key-off events and instrument vibrato
  • more of everything: 32 fully usable channels, 128 instruments, bigger samples, etc.
  • “linear” pitch slides
  • variable-length patterns
  • a more capable volume column with support for various commands
  • excellent and very flexible panning support
  • provisions for improved playback quality, including interpolation and volume ramping
With regards to the implementation, the new instrument-related features are what requires the most attention and is the most extra work. There’s nothing inherently difficult about any of it, but it adds a significant amount of complexity to a player.

The linear slides are a big new feature that affect the operation of all effects related to pitch changes. Yet, they can be implemented in a way that hardly requires any additional work or code.

The new volume column effects complicate things by adding a number of possible interactions between regular effects and volume effects. In practice, this does not create any real problems, but for authentic, FastTracker II-like playback, it’s important to understand how the different effects play together.

Improvements in playback quality are, of course, primarily tracker features rather than characteristics of the format. They are important to understand when creating a player, however, for at least two reasons. First, they set the standard that a player could try to emulate or surpass. Second, the fact that FastTracker II offered a range of options that ensure reasonably good playback quality frees today’s trackers and players from the obligation to exactly emulate a particular way of generating the sound output for authenticity. For example, since users could choose between 8 and 16-bit mixing at sample rates between 8 kHz and 44 kHz in FT2, it hard to argue that using floating point mixing at the audio driver’s preferred playback rate would result in audio output that’s noticeably different from what an original tracker or player could have produced. At the same time, composers are targeting a less uniform and restrictive playback environment, which means that they do not have to worry as much about technical details, tricks and constraints.

Overview

The XM format is an evolution of the ProTracker MOD format with a number of additional features, including some adopted from ScreamTracker and S3M modules. It retains the ProTracker canon of effects and their labels, 0xy to Fxx, while extending the effects library somewhat. New effects are identified by letters beyond F in trackers, for example Gxx sets the global volume and X1x is where S3M’s extra fine portamento up got mapped.

The volume column, familiar from ScreamTracker, previously only held 65 different volume values. FastTracker II makes better use of the byte representing it by cramming a number of volume, panning and pitch related effects into its 8 bits. In the tracker, these effects are entered as a combination of a character identifying the effect and a hex number representing the effect parameter. Many of the effects are, however, represented by a symbol instead of a letter in the pattern editor. For example, R5 means “panning slide to the right by 5” and would be represented by ▸5, V4 would be vibrato of depth 4 and M0 performs tone portamento at the previously set rate. Volume values continue to be represented by 2-digit numbers, albeit by hex numbers in contrast to ScreamTracker’s decimal representation.

FastTracker II supported playback on a range of SoundBlaster-compatible cards, the Gravis Ultrasound (GUS), the internal PC speaker and (often home-made: [LPT1]) contraptions ([CST], [LPT2]) that attached to the computer’s parallel port. Playing samples on the PC speaker could sound surprisingly non-terrible if done well, and FastTracker actually did fine job at it. Using the speaker or parallel port for audio output was a thing in the early days of playing modules (and games) on aurally underprivileged PCs. The popular early-90s module player MODPlay, for example, came with instructions on how to build a device to get “mono output from a parallel port for around 1 pound.” By the time FastTracker II came out, such features were largely obsolete, as SoundBlaster-compatible cards would have been a standard component of any reasonable PC for years.

In contrast to ScreamTracker, FastTracker II supports a given set of playback features independently of the playback device used rather than tying them particular hardware. The XM format is free of any hardware-specific information, and it is left to the player to map any desired audio output to the available audio hardware. Presumably, a SoundBlaster 16 would have been the ideal playback device at the time, followed by the GUS and the SoundBlaster Pro.

The important innovation of the XM format is to replace the idea of just playing samples by that of playing instruments. Instruments allow for much finer control of how notes sound depending on what note is played and how it is played. FastTracker supports the key-off (or note-off) command familiar from the S3M format and makes it useful for playing sample-based instruments. It is possible, for example, to design an instrument that increases in volume at a given rate, then remains at a certain volume until the note is released, then fluctuates in volume and fades, all the while bouncing back and forth between left and right panning.

Instruments are composed of (and contain) possibly several samples that can be mapped to different notes on the scale. Different samples within the same instrument can have different lengths and can loop in various different ways. In addition to this, it is possible to design a set of fairly complex envelopes for an instrument, which control how volume and panning evolve over time as the note is played. These envelopes support loops of their own as well as sustain points, where note characteristics are held stable until a note is released. All these envelope-related features are processed at tick-resolution and are entirely independent of sample playback.

It’s quite obvious that the authors of FastTracker were obsessed with panning-related effects, and so the XM format ended up with a set of panning features for notes and instruments that is almost as rich as those related to volume and pitch.

File Format

Like other trackers, FastTracker II was distributed with a document [XMF] describing the file format as well as some basic information on playback. For someone who knows the MOD and S3M formats, it is sufficient to parse the original XM files and to get started on playing them. Other sources of interest include [MMW] and [CXM], although neither of them adds much to the information already available in [XMF] as far as the file format is concerned; they are more valuable for hints on accurate module playback. The document that is worth reading is [UXM], “The Unofficial XM File Format Specification” by Vladimir Kameñar. This manual is complete, detailed, up-to-date, explains things well and includes information on all sorts of extensions, such as compressed samples and stripped XM files. I recommend using this when writing an XM file parser. I will have little to add to it except a few rather unimportant comments.

XM Header

The layout of the first two fields followed by the $1A byte is indeed designed to let you get a quick peek into the file. $1A is the end-of-file marker used by DOS. So if I enter “TYPE WARRIO~1.XS” at the DOS prompt, I get “Extended Module: WarriorsOfTheDawn”. This will work at the Windows command prompt, too, but don’t try it on a UNIX-like system, it will likely create a mess of an output.

FastTracker II allowed for up to 32 channels. MilkyTracker has the same limit, but OpenMPT will let you use up to 127.

“Default tempo” refers to ticks per row, and “default BPM” is the value used to calculate the duration of the tick (2.5s/BPM, as usual). In non-XM documents, these values would typically be referred to as speed and tempo, respectively. The non-standard and confusing use of “tempo” here is due to the folks at Triton, [XMF].

Pattern Header

The [UXM] document gives good advice: Whenever you need to access a pattern that was not stored in the file, have an empty default-size pattern ready.

The variable length of patterns makes it more difficult to check if a row does actually exist in a pattern. FastTracker II has bugs where it reads garbage from memory when trying to access non-existing rows. In DOS, this led to unexpected sound output. Today, you may get segmentation faults for trying.

Some of the information provided on possible values in the pattern data and encoding are inaccurate and inconsistent in [XMF] and [UXM].

  • Notes: 0 means no note, values from 1 through 96 are the valid notes that can be played (12 semitones per octave times 8 octaves, labelled C0 to B7); 97 is the note-off code.
  • As usual, instrument numbers start at index 1 and 0 means no instrument in the cell.
  • The volume column byte can take on pretty much any value, as explained elsewhere in [XMF] and [UXM]. Values of 0 mean no volume effect.
  • Effect numbers range from $00 (0xx) to $21 (Xxy) in standard FastTracker XM files, but extensions exist that add further effects (see for example [MPT]).

Instrument Header

Instrument type: [XMF] assures us that it’s always zero, but I had the impression that FT2 does set this to non-zero values in some converted MOD and S3M files. Still, it does not seem to matter for playback.

The keymap assignment provides the mapping between the 96 possible pattern notes and the samples included in the instrument. Notice that samples are firmly assigned to an instrument. If you want to use the same sample in two instruments, it needs to be included twice.

Instruments that are not stored in the file are just empty instruments with everything relevant initialized to zero (except panning, which is $80, not that it matters) and zero-length samples. Such instruments can be triggered, they just don't play anything.

Sample Header

All length or position related values in the sample header are in bytes (sample length, loop start, loop length). This is important for 16-bit samples. Note that the effect parameter for the 9xx (Set Sample Offset) effect is in samples.

[UXM] indicates that a value of $AD in the reserved byte at offset 17 in the sample header signifies ADPCM compression. It’s worth pointing out that trackers appear to write all kinds of values into this byte, so if you encounter a non-zero number there, this does not necessarily mean that the sample is encoded in an unusual way.

Playing the Module

This is where it gets complicated. The XM format was originally introduced with a playback tool and FastTracker II, both supplied by Triton. It was also supported by ImpulseTracker, ModPlug, and other trackers, which played XM modules in their own engines. XMPlay was a popular player for the format. So by end of the 90s, XM files would have been created and used in a potentially fairly heterogeneous environment and thus hopefully made with general compatibility in mind.

Today, the landscape may be somewhat different. Possibly the best tool for composing XM modules today is MilkyTracker, which has pledged to recreate the FastTracker II engine as faithfully as possible, including all its oddities. XMPlay is said to be highly compatible with FT2 as well, and in the light of all this, OpenMPT advises to turn on its many FT2 compatibility switches when creating XM modules, see [XMC]. It seems that today, the only right way to play an XM module is the FastTracker way, just as in 1994. If XM files are played back on “lesser players,” as the MilkyTracker manual [MTM] puts it, they might not play correctly.

Now, FastTracker II has a reputation for being an extremely quirky and buggy piece of software that is tricky to emulate accurately. Looking at the absurdly long list of compatibility switches that OpenMPT provides ([XMC]) is truly scary. However, based on my experience with FastTracker II, this reputation is not entirely deserved.

The majority of the odd behaviours observed in FT2 are really only strange and unexpected relative to prior expectations based on people's experience with other tracker engines, such as ProTracker or ImpulseTracker. Most of them pertain to unusual corner cases, for which there is no specified “correct” behaviour beyond the examples set by alternative trackers, and arise as entirely predictable behaviour from perfectly reasonable design choices. There are some bugs, but I’d consider only two of them somewhat severe, and one of them is so high-profile and well understood that anyone making XM modules should be aware of it (it is even mentioned on Wikipedia, [WFT]).

In what follows, I will describe a module player engine that plays XM files the FastTracker way and handles many of this tracker’s idiosyncrasies automatically. In some places, I will be fairly specific about details. I want to emphasize that what I describe here is not necessarily what actually happens in FastTracker. It is a model that can account for the tracker’s characteristics, to the extent that I am aware of them. It is based on experiments with FT 2.08 and 2.09, not on disassembling the tracker or reading someone else’s sources.

The first document to look at in order to understand the tracker features is its manual [XMM], followed by the file format specification [XMF], which contains some technical details, for example on sample rate calculation. In addition to that, MilkyTracker provides some helpful information on the actual behaviour of some effect commands in its manual, [MTM]. [MPT] has a full list of effects with a brief description, including many non-FastTracker extensions. However, the effect descriptions tend to reflect more the standard set by ImpulseTracker than the operation of FT2. This older file format document [CXM] gives a number of helpful pointers, but some of the information provided is inaccurate. [MMW] mostly contains generic placeholder-level information on how effects work, with the exception of two important commands, for which it gives some rather helpful details. One excellent source for information on unusual tracker features is, as usual, the OpenMPT compatibility documentation [XMC].

In what follows, I will occasionally hide some less important comments like this:
NotesYes, this is how it works.

Click on them, if you are interested.

Periods and Frequencies

One problem with MOD pitch slides is that the distance between notes depends on the period p. In ProTracker, sliding from C3 (period p=214) to C2 (p=428) requires covering a distance of 214 period units, and this takes 5 ticks with a 22B command. Sliding down another octave to C1 (p=856) takes 10 ticks when using the same command. Doubling the parameter value, thus using the effect 256 instead, solves the problem in principle, but the slide will still sound like it slows down. The first tick will cover more than 3 semitones, the last one less than two.

This problem gets even worse with module formats that allow for samples at different rates. As, by convention, the period length p is firmly tied to the actual sample playback rate, playing a note of C4 in ScreamTracker involves in very different period values depending on whether the C4Spd of the sample was 8 kHz or 44 kHz, thus requiring very different effect parameters to achieve the same slide.

Wouldn’t it be great if we could tell the tracker to slide, for example, by \(\frac{1}{4}\) semitone per tick, and it would just work for any note and any instrument? The “linear slides,” which FastTracker II offers as a new feature, accomplish just that. There is now a module-level setting offering the choice between the traditional “Amiga” way of doing things and a new mode, in which the parameters of pitch-bending effects are specified in fractions of semitones rather than period units.

Suppose an effect command with parameter \(n\) should change the pitch of a note by \(\frac{n}{16}\) of a semitone. The most obvious way of accomplishing this to continue using the same period measure as with Amiga slides, but to adjust periods p proportionally by the correct factor of \(2^{\frac{n}{12\cdot 16}}\), rather than simply adding \(n\) to p as for Amiga slides. This approach has the disadvantage that every pitch-changing effect has to come in two versions, an Amiga one that makes additive changes to the period, and a linear one making multiplicative adjustments. ImpulseTracker does just this by tabulating the proportional changes associated with linear slides as fixed-point values.

FastTracker II uses a different method. Instead of keeping track of periods as in Amiga mode, it measures pitch in log-periods when using linear slides. This means that p is calculated differently when a note is initially triggered, and that the mapping from these log-periods to sample rates is more complicated than just dividing a constant by p. It does, however, have the advantage that none of the effects have to be changed at all. Adding a pitch change \(n\) to the log-period p will change the frequency \(f\) of the note by \(\frac{n}{16}\) of a semitone if it is calculated as \(f=F\cdot 2^{-\frac{p}{12\cdot 16}}\) for some positive constant \(F\).

Here’s how things actually work in FastTracker II.

The XM format supports the same range of playable notes (C0 to B7) and employs the same default mapping from notes to sample playback rates (C4 corresponds to 8,363 Hz) as ScreamTracker and S3M modules. In patterns, the notes are mapped to the numbers 1 through 96, with C4 being represented by 49. When calculating a period value, notes are adjusted in two ways. Individual samples can be transposed to a different semitone by adding the (signed) value from the “relative note number” field of the relevant sample header to it. The resulting effective note index effNote must be between 0 and 119 for the note to be triggerable; but a period value could in principle be calculated even for notes outside that range. Then, the note is also adjusted by a finetune value, which is a signed integer value with a unit of \(\frac{1}{128}\) semitone. It is normally supplied by the sample header, but can be overridden by the E5x command.

In linear slide mode, the log period is calculated as p=16*4*(10*12-(effNote-1))-finetune/2. It is defined in units of \(\frac{1}{16\cdot 4}=\frac{1}{64}\) semitones (half as fine as the finetune values), which means the parameters of regular slide commands are in \(\frac{1}{16}\) semitones and extra fine slides in \(\frac{1}{64}\) semitones (remember from S3M: the internal representation of periods got scaled by 4 compared to MOD, so most pitch commands have a resolution of 4 p-units). The effective note effNote gets adjusted by one to make the formula zero-based (with this change, C0 corresponds to 0 and C4 to 48). Adding 10*12 to the negative effective note shifts everything by 10 octaves and ensures that the log-period is nonnegative for the legitimate note indices in the range of 0 to 119. The frequency associated with a log-period p is then \(\varphi=8363\cdot 2^{(6-\frac{p}{12\cdot 64})}\). It’s easy to verify that C4 will play at a rate of 8,363 Hz, as it should. These formulas can be found in [XMF], [CXM] and [UXM]. FastTracker almost certainly tabulates the calculation of \(\varphi\) somehow; see the comments below for some remarks on this.

When Amiga slides are used, the mapping from notes to periods and from periods to frequencies is equivalent to how things work with S3M modules, i.e. C4 is mapped to a period of \(428\cdot 4\). The theoretical period value, not accounting for any ProTracker period irregularities, is \(p=428\cdot 4\cdot2^{-\frac{1}{12}\left(\text{effNote}-49+\frac{\text{finetune}}{128}\right)}\). For more authentic playback, [XMF] provides us with a \(12\cdot 8\)-element period table, which contains an octave’s worth of period values at ProTracker’s finetune resolution of \(\frac{1}{8}\) semitone. It is set up to work with the effNote as defined above, as the period of a C is found at index 8. It can be used like this: function AmigaPeriod4(effNote,finetune): // I’ll add some “safety” octaves soc, to ensure AmigaFinetune is // not negative even if something goes seriously wrong; effNote // could be as low as -95 for a valid XM file and who-knows-what // when reading a broken one; finetune could be -128; // this hack is not a solution, it’s a warning; check bounds! soc=10 AmigaFinetune=(effNote*128+fineTune+soc*12*128)>>4 tableIndex=AmigaFinetune%(12*8) octave=floor(AmigaFinetune/(12*8))-soc return periodTable[tableIndex]*2*2^(octave-4)

If you want to interpolate between the (more coarse) Amiga finetune values, as [XMF] (possibly incorrectly) suggests that FastTracker may do, you can use p=AmigaPeriod4(effNote,finetune)*(1-weight)+AmigaPeriod4(effNote,finetune+16)*weight for weight=(finetune%16)/16. Alternatively, I recommend simply using p=AmigaPeriod4(effNote,finetune+8), which rounds the finer XM finetune values to the Amiga ones. No matter how you calculated the period value, the appropriate sample rate is \(\varphi=\frac{1712⋅8363\text{ Hz}}{p}\).

Notes The formulas for period calculation provided in [XMF] and reproduced in [CXM] and [UXM] are somewhat problematic. Can you spot 4 errors before I point them out?
  1. First, there’s some inconsistency regarding whether PatternNote has C0 at index 0 or 1. It’s claimed it’s at 0, in the actual pattern it’s 1, the linear formula works for 0 and the Amiga table works for 1. The error was found by [CXM], too.
  2. The Amiga interpolation formula does not actually interpolate anything – both table indices are the same. [CXM] points that out.
  3. The table index will be out of bounds for negative finetune values, as observed by [CXM]. Also, as finetune can be a full semitone, add it first, DIV later.
  4. Even if we fix all these problems, the Amiga period is still off by a factor of 2 (for C4, Note=49, we should get Period=428*4=1712; but we get 856).
Anyway, the formulas convey the right ideas.

[UXM] adds some incorrect statements to the formula which are best ignored (the distance between two octaves is \(12\cdot 16=192\) regular fine units, not 255; a slide of 1C0 at speed 2 will thus change the pitch by one octave; at speed 1, the slide will do nothing).

Since [UXM] brings up tabulation in the context of the linear frequency formula with the exponential function in it, I’ll make some comments, too. It may make sense to tabulate this formula, but it does not have to be done by octave, as [UXM] claims and ModPlug allegedly does.

If I tabulate by octave, I will be using something like f=table[p%(12*64)]<<floor(p/(12*64)). This has several disadvantages. First, I have to use this division by \(12\cdot 64\), which is not ideal (albeit not a big deal) if I care about clock cycles for some reason. Second, I split my set of 7,680 values (10 octaves×12 semitones×64 finetune values) only into 10 parts, leaving me with a large (and therefore possibly cache-unfriendly) table. Why not split it more effectively? If I need \(10\cdot 12\approx 128\) semitones and 64 finetune values, I can do something akin to f=F*semitones[p>>6]*finetune[p&63]? I’m using two tables of sizes 128 and 64, respectively, for an overall size of 192, and there’s no need to do divisions. Moreover, having tables of relative note frequencies and finetune adjustments likely comes in handy for other purposes (arpeggio uses semitone steps, for example). If table size is an issue, the memory footprint can be reduced further by using more tables of similar sizes. There is no reason why this would not work for fixed point numbers.

[XMC] points out a few FastTracker quirks related to period calculation. First, finetune changes are allegedly only audible at a resolution of 8. For linear frequencies, my impression was that the adjustment was pretty smooth. For Amiga slides, I seemed to hear even cruder steps, maybe 16 finetune values apart. I’m not sure about either, and I did not attempt to measure with any tool other than my ears. I do have the suspicion, though, that for Amiga periods, the interpolation between the ProTracker finetune values of \(\frac{1}{8}\) semitone is either not implemented or not working as it should.

The other issue listed in [XMC] is that of period overflows. The period variable is 16 bit wide, and if period values get too high, they wrap around. Sliding up by 16,384 standard units (or 4 times as many extra fine units) will take you full circle and back to the original pitch. Sliding towards higher pitches will not let you wrap around, presumably because FastTracker is concerned about underflows towards zero and the resulting issues when calculating frequencies. At least in Amiga mode, the period variable seems to be consistently interpreted as unsigned.

Playing Notes

FastTracker II adds panning as a new note characteristic. On Amiga trackers, the module channels were unchangeably assigned to either the left or right stereo channel, and in ScreamTracker, panning became a variable channel property. Now it joins volume and pitch as a part of the note state.

Both volume and period have a temporary component that is set by effects that create a short-lived deviation from the note's permanent state, such as tremolo and arpeggio. In FT2, the period has effectively two temporary variables, one that is shared between all volume column and regular effects and one exclusively for the instrument’s auto vibrato. This allows auto vibrato to operate independently and on top of other pitch-changing effects. Thanks to vibrato being available in the volume column now, it can be active concurrently with either regular vibrato 4xy or arpeggio 0xy. Since they set the same temporary period variable, only one of them can affect the pitch at a time, and this is always the regular effect, as it is processed after the volume effect. Neither the temporary period nor volume variables are automatically reset to zero at the beginning of a new tick. Effects setting temporary values are supposed to somehow clean up after themselves, but many of them don’t. Temporary variables are reset, however, when any changes to the corresponding permanent variables are made. FT2 does not support any effects that temporarily change panning, but OpenMPT makes one available as an XM extension, Yxy panbrello, see [MPT].
Note An alternative explanation for the fact that some supposedly temporary effects persist longer than they should would be that the temporary values do get reset, but relevant variables derived from them are not recalculated, such as the sample rate resulting from the period or the left and right stereo volumes as a function of the note’s volume and panning. If this were the problem, one might expect a change to the note's panning should get temporary volume effects unstuck, which is not the case.

As in other trackers, samples should be thought of as being played continuously once triggered until they are done or deliberately stopped, while the playback characteristics such as playback rate, volume or assignment to stereo channels can change periodically on ticks. Sample playback is pretty straightforward. Samples play through until they reach the end, then they stop. If they have a valid loop, they play forever. The XM format supports forward and ping-pong loops. A loop of length 0 does nothing in FT2, otherwise all loops work as expected. The tracker does not allow entering invalid loops with start or end points beyond the length of the sample. FT uses volume ramping when switching out samples. The samples are phased in or out by changing the volume in a linear fashion from 0 to 100% over a time of 6 ms (more on this below). FT supports linear interpolation between samples as a quality setting.

The XM instrument model makes it possible for the volume, period and panning of the note to change automatically over time without the explicit use of effects. The most impactful new instrument feature are probably envelopes.

There are two types of envelopes, a volume and a panning envelope. Their use is optional, and they are only turned on if the corresponding flag in the instrument header is set. As seen in the picture below, each envelope defines a piecewise linear function. The horizontal axis is the envelope frame (essentially ticks) and the function value lies in the range from 0 to 64. The shape of the envelope is determined by the up to 12 points provided in the instrument header. The first point always has an x-coordinate of zero, and points must be stored in ascending of order of frames. Each envelope has its own frame counter, which is initialized to zero when the note is triggered. With every tick, it is incremented by one. The current value of the envelope is the y-value of the polygon connecting the envelope points for the current frame. It is obtained by linear interpolation between points. Once the note has passed through the whole envelope, it remains at the last point and holds the envelope value constant.

There are two additional envelope features that can be turned on by setting their flags in the instrument header: A sustain point and a loop. The sustain point is the index of an envelope point. Once envelope processing reaches the sustain point, it stays there unless or until the note has been released by issuing a key-off command in the note column or using the Kxx effect. Once the note is released, envelope processing continues normally. The loop is defined by two point indices, the loop start and the loop end point. When reaching the loop end point, the frame immediately jumps to the loop start point. The loop-end value is not actually seen as an envelope value. If the loop start is greater than the loop end, it is possible to jump forward. If start and end point of the loop coincide, processing just continues, the frame number does not get stuck at that point.

If the sustain point and the loop end coincide and are both active, an interesting interaction occurs. The loop jump is performed if the note has not been released yet. Once the note has been released, the loop end point is ignored. It’s not clear if this is intentional, but this makes it possible to create a sustain loop that can be released instead of just having a sustain point.

FastTracker II allows you to enter envelope points with frame-coordinates of up to 324, the limiting factor being the width of the envelop editing window. Other trackers support longer envelopes.
NoteThe information on maximum envelope length in [CXM] seems inaccurate.

Another way in which the note changes automatically is fadeout. The fadeout volume v_fade is initialized to its maximum value (presumably 65,535 or 65,536) and remains unchanged until the note is released. From then on, it is reduced by twice the fadeout rate (the variable in the volume header referred to as “volume fadeout”) on every tick until it reaches zero. The interface allows choosing fadeout rates between 0 and $FFF for a minimum fadeout-time duration of 8 ticks.
Note [CXM] as well as the annotated version of [XMF] state that the initial volume of 65,536 is reduced by the fadeout rate every tick. This is not what I measured. For a fadeout rate of $100, it took $80 ticks for the volume to go from max to zero. My suspicion is that the original documentation [XMF] is incorrect with regards to the volume formula, and that the fadeout volume is in fact initialized to 32,768.

The final addition to the instrument model is auto vibrato. This feature works very similar to usual vibrato, with a few little tweaks. First, it’s much finer. The vibrato depth is 8 times finer than regular effect vibrato, i.e. auto-vibrato depth of $8 sounds like regular vibrato at a depth of $1. Speed is 4 times finer in principle. But because auto vibrato is executed on very tick unlike effect vibrato, which is executed on non-zero ticks, the speed-related behaviour of the two vibrato versions is less comparable. A speed of 0 does not turn vibrato off, it just creates a constant period offset. As expected, auto vibrato sounds different depending on whether linear slides or Amiga slides are active. The vibrato sweep variable found in the instrument header indicates over how many ticks vibrato is phased in after triggering. The effective vibrato depth is increased linearly during the sweep period. Auto vibrato supports 4 different waveforms, selected by the field “vibrato type” in the instrument header, 3 of which are flipped compared to the versions available to regular vibrato: sine (type 0, starts at 0, moves towards higher pitch initially), square (type 1, starts at high pitch), ramp down (type, 2, starts at zero, pitch decreases, same as in regular vibrato), ramp up (type 3, starts at 0, pitch increases). Vibrato waveform position and sweep always retrigger when an instrument is set.

All of these instrument features, the envelopes, the fadeout and the vibrato, are processed on every tick and thus more smoothly than many of the regular effects, which only operate on non-zero ticks.

As there are now multiple volume and panning variables contributing to the sample characteristics, we need to know how they are combined. The formulas are provided in [XMF] and reproduced in [CXM] and [UXM]. Volume is pretty straightforward, the various components are simply combined multiplicatively:

v_note=(v_fade/65536)*(v_eff/64)*(v_env/64)*(v_global/64),

where v_eff=min{64,max{0,v+v_temp}} is the effective volume clamped to the allowable range. The amplitude is scaled by the global volume v_global. In contrast to ScreamTracker, changes to the global volume immediately affect all channels, i.e. channel-level volumes are recalculated on every tick.
Note The variable Scale in the formula as shown in [XMF] likely refers to a combination of parameters that can be chosen in the tracker setting but are not saved in the module file, such as master volume, amplification and possibly a factor that depends on whether 8-bit or 16-bit mixing is used.

The panning formula ([XMF]) is more complicated and shows how much thought has gone into making panning work well and in an interesting way:

pan_note=pan+(pan_env-32)*(128-abs(pan-128))/32

If the envelopes are deactivated, v_env should be set to 64 and pan_env to 32.

Panning

FastTracker II supports the GUS for audio output, which uses constant-power panning with 16 possible panning positions. When using other stereo-capable hardware (such as a SB16 or some circuit boards plugged into both parallel ports), it employs the following formula for software panning, see [PAN]:
\(L_\text{FT}(P)=\sqrt{1-P}\), \(R_\text{FT}(P)=\sqrt{P}\), where \(P=\frac{\text{pan}}{256}\) for \(\text{pan}\in\{0,1,...255\}\).
Two observations: This is, quite obviously, a constant-power panning law: \(L_\text{FT}(P)^2+R_\text{FT}(P)^2=1\). It’s not too different from the constant-power law described in the post on the S3M format, the only difference is really that the angle is parameterized slightly differently. See the graphs below for a comparison. Second, because \(P\) cannot actually reach 1, the left channel is never fully off. Both of these properties align with my measurements.

panning position \(P\) volume \(L_\text{FT}(P)\) \(R_\text{FT}(P)\) \(L_\text{CP}(P)\) \(R_\text{CP}(P)\) CP panning position \(P_\text{CP}\) FT2 panning position PFT

These graphs compare The FastTracker panning law \(\{L_\text{FT},R_\text{FT}\}\) to the constant-power panning law introduced in the post on the S3M format, \(\{L_\text{CP},R_\text{CP}\}\) with \(L_\text{CP}(P)=\frac{1}{\sqrt{2}}\left(\cos(\alpha(P))+\sin(\alpha(P))\right)\), \(R_\text{CP}(P)=L_\text{CP}(1-P)\), \(\alpha(P)=\frac{\pi}{2}\left(P-\frac{1}{2}\right)\).
The first graph plots how volumes are scaled, the second one shows how the two panning laws parametrize the panning position differently by plotting the FT2 panning position \(P_\text{FT}\) that yields the same result as a position \(P_\text{CP}\) under the other constant-power law, \(L_\text{FT}(P_\text{FT})=L_\text{CP}(P_\text{CP})\).

Volume Ramping

FastTracker 2 supports smooth transitions between instruments and volume levels. This reduces certain artefacts associated with rapid volume changes, such as clicking noises. In module files, most changes happen discretely at given intervals, when ticks are processed. The general idea here is to spread out some of these changes over a certain time period. This feature is largely implemented at a fairly low level in the context of sample playback, as it involves making adjustments that happen between ticks and need to be processed at a much higher rate.

On every tick, various parameters change that affect the left and right stereo volume levels used in a channel. This includes several factors contributing to volume (note volume, temporary adjustments from effects, the volume envelope and global volume) and panning (note and envelope panning). Volume ramping is about smoothing out these volume changes. This is done in the form of a linear transition from the old to the new left and right volume values over a given time.

FastTracker's approach to volume ramping is fairly flexible. There appear to be three distinct scenarios that are handled differently.

  1. If a new note has been triggered (or retriggered), the old note (with its prior volume, panning and period) is phased out (volumes moving towards zero) while the new note (new sample, period, volume, etc.) is ramped up (starting at zero left and right volume). The transition time is about 6 ms.
  2. If there is an effect in the current cell that suggests an abrupt volume adjustment (a regular or volume column "set volume" effect, Cxx or xx, but not a global volume change Gxx), any volume changes are smoothed out over 6 ms.
  3. In all other cases, the volume change is smoothed out over the duration of the whole tick.
Note that the first scenario requires each channel to play two entirely different notes at the same time during the transition period. Triggering a note thus involves backing up the previous sample state (period or sample playback rate, reference to sample description and data, sample playback position, left and right effective volume) before setting things up for the new note.

Global Control Flow

The global control flow of XM modules in FastTracker is important puzzle piece for understanding the unusual characteristics of some commands. I’ll start by showing some code that updates tick, row and songPos before or after processing a tick. Then I will discuss how this relates to observable tracker behaviour. This possible structure was inferred from experiments with the tracker.

processAllChannels() firstTick=false tick=tick+1 if (tick>=ticksPerRow) tick=0 if (repeatRow>0) repeatRow=repeatRow-1 else firstTick=true row=row+1 if (row>=currentPatternRows()) patternBreak=true if (rowJump || patternBreak) row=rowJumpTarget // rowJumpTarget=0 [BUG: E6x] if (patternBreak) songPos=newSongPos if (songPos>=songLength) songPos=restartPos newSongPos=songPos+1 rowJumpTarget=0 // if (row>=currentPatternRows()) row=0 [BUG: play garbage] rowJump=false patternBreak=false

This code includes a number of flags that are used here and elsewhere in the tracker. firstTick indicates to the routines processing cells and effects whether we’re on the first tick of the row. rowJump signals that the row should be changed to rowJumpTarget; it is set by the E6x loop effect. Setting patternBreak initiates a jump to newSongPos; it is set by the Bxx and Dxy effects, which also set newSongPos and rowJumpTarget.

In addition to the obvious tick, row, and SongPos variables and the two jump targets rowJumpTarget and newSongPos that were already mentioned, another integer variable here is repeatRow, which is set by the EEx pattern delay effect to indicate how often the row should be repeated.

Where’s all this coming from?

There are two groups of effects that behave very differently when used with the EEx pattern delay effect. Suppose the module runs at 6 ticks per row and I trigger a pattern delay of 2 by using the EE2 effect, thus playing the current row for 18 ticks. Any effects that only need to differentiate between the first tick and non-first ticks (e.g. slides, fine slides, tone portamento or set volume) will operate as if on a single, extra-long row with 1 first tick and 17 non-first ticks. They just look at the flag firstTick and know what to do. Effects that need to know exactly what tick number they are on (for example delay note, note cut or arpeggio) will behave as if the current row is executed 3 times. From these observations, I infer that there’s something equivalent to the firstTick flag and that ticks are not continuously counted up during row repeats.

Another piece of evidence informing the code structure is the infamous E6x bug, see [MPT], [XMC], [CXM], [MTM] or any other source seriously discussing XM playback. When using the loop command E6x (x>0) to perform a jump in FT2, the next pattern will start on the row of the jump target, unless an effect Bxx or Dxx is used before the end of the pattern is reached. It is therefore frequently recommended to include a D00 command on the last row of a pattern using E6x.
NoteSome sources suggest that it’s marking the loop target with E60 that will trigger the bug. This is not the case. Only an E6x jump will cause the unwanted behaviour.
So what’s going on? Clearly, E6x, Bxx and Dxx are using the same variable to mark a jump target. E6x sets it to its own target row, Bxx sets it to 0 and Dxx to xx. We also know that it is automatically reset after a pattern break, because the bug only occurs once after using E6x. So presumably E6x sets rowJumpTarget and announces that it should be used after the current row without doing a pattern break, by setting the rowJump flag. This bug could easily be fixed by resetting rowJumpTarget to zero after any use (line 13 in the code above) instead of doing so just after a pattern break (line 18).

Note [CXM] alleges further bugs in the E6x implementation that supposedly arise if two E6x effects are on the same row, including random behaviour. I don’t agree. With multiple E6x effects on a row, the behaviour becomes much more complex, but in my experiments I always got the expected outcomes. Here’s one possible explanation for the reported observations. With multiple E6x commands, runtime is often proportional to the product of the individual loop lengths, as looping will only stop once all loop counters are zero at the same time. Letting the scenario play out takes a long time. It you stop execution prematurely, then next pattern run will look very different, as loop counters are not reset by the tracker upon pattern execution stop or start.

Combining a pattern delay EEx with any sort of jump will conduct the jump after the first time the row is executed. However, because firstTick is not set when arriving at the target row, the new notes and effects are not initialized and the row with the jump keeps on playing for the remaining time of the delay. After that is done, the row is incremented again, so the content of the rowJumpTarget row is never played in this case.

There is one more bug in the code above. After setting a non-zero row on line 12 and possibly switching the pattern as well, it’s necessary to check if the new row is valid for the pattern. This is not done. FastTracker will happily play whatever data can be found in the memory location of the invalid row for one full row (or longer if an EEx effect is in play) before moving on to row 0 of the following pattern. This bug can be triggered with Dxx, with Bxx+Dxx and, with a little trickery, also with E6x. Fix it by uncommenting line 19.

Processing a Cell and Triggering

Channels are processed in ascending order (“left to right”). Here we look at an individual channel.

Again, start with some code. This shows what might happen in a channel on every tick.

volumeEffectActive=true if (firstTick) [note,inst,volEff,eff,effParam]=readFromPatternCell(pat,row,channel) if (inst>0) newInst=inst triggerNote=(note>0) triggerInst=(inst>0) if (EDx with x>0) if (note>0) effEDxNote=note else effEDxNote=channelNote.note effEDxSetInstVolPan=triggerInst volumeEffectActive=false triggerNote=false triggerInst=false elseif ((Mx or 3xx or 5xx) && (note>0) && (note<97)) period=calculatePeriod(note,channelNote.inst,noFinetuneOverride) if (period valid) slideTargetPeriod=period slideUp=(slideTargetPeriod<channelNote.period) triggerNote=false elseif (K00) triggerNote=false processEffParameterMemory() // Mx after 3xx; exceptions: Sx, 4xy if (triggerNote) if (E5x) finetuneOverride=finetuneOverrideTable[effParam&0xF] else finetuneOverride=noFinetuneOverride if (9xx) sampleOfs=eff9xxMemory*256 else sampleOfs=0 channelNote.trigger(note,newInst,finetuneOverride,sampleOfs) if (triggerInst) channelNote.triggerInst() if (volumeEffectActive) processVolumeEffect() processEffect()

Some effects require at least some component of early processing around the time notes are triggered. This includes tone portamento, which prevents triggering, sample offset and finetune overrides, which modify the new note, and note delays, which change overall timing. FastTracker does much of this in a very particular way, which can be inferred from, and matters because of, mostly how the EDx note delay effect works and interacts with other effects.

For a non-zero effect parameter x, note delay on FT2 works by cancelling a number of firstTick actions, including triggering notes and instruments, processing target periods for tone portamento and even volume column effects. It then performs many of these actions itself on tick x, but often in a somewhat simplified and incomplete manner. This gives rise to a number of oddities, where some things just work differently when an EDx effect is active, and it explains why delayed notes trigger repeatedly if a row is extended using an EEx effect.

On the first tick of the row, as flagged by firstTick, the pattern information gets loaded.
NoteWe know that this initial cell processing happens on firstTick rather than tick==0, because combining EEx with a row jump will execute the jump before the first repeat without reloading effect information.
Then it is checked whether any special treatment is applied. An EDx for x>0 effect prevents anything from triggering and cancels firstTick volume effects; tone portamento (3xx or Mx) will stop the note from triggering, while allowing instrument triggers as normal; and finally, the key-off effect with a delay of zero ticks, K00, is special in that it also prevents the current note from triggering. K00 still performs the normal Kxx actions later when regular effects are processed. We can infer that these three effects are tested for in this order and that testing stops after having found one (hence the if ... elseif .. elseif structure used above) from the observation that EDx will stop the firstTick processing of Mx, but K00 will not.

Effect Memory

Effect parameters are copied to effect memory unconditionally on firstTick, presumably before the note is triggered (the 9xx effect memory is already used here). This is of course pretty simple, but there are a few things to note here.
  • The two versions of tone portamento, 3xx and Mx, share the same memory. The 3xx parameter is processed first, so if both 3xx and Mx appear in the same cell, the Mx parameter is the one that’s used.
  • Vibrato is a little different. While the vibrato depth parameter of Vx is committed to effect memory early like most other effects, Sx and 4xy are handled later. Sx copies x to vibrato speed memory when the volume effect is executed on firstTick. I know this, because EDx effectively turns Sx off. 4xy commits its parameters to memory even later, when regular effects are processed on firstTick. This can be inferred from the observation that when Sx and 4xy are in the same cell, the 4xy speed parameter is used. Presumably this happens because the vibrato memory works differently from most effects, as the two nibbles of the effect parameter are checked for zero independently.
  • Tremolo 7xy and multi retrigger Rxy may be committed to memory as part of effect execution too, but it can’t be checked and does not matter, as there are no relevant effect interactions.

Overrides

The override effects 9xx (sample start position) and E5x (finetune) only work if they appear with a note, and if this note is triggered regularly. If they are combined with tone portamento, they are ineffective. They are really only used when calculating the initial state of a note. If the note is later retriggered, the sample starts at offset 0 and the instrument’s default finetune value is used. If the 9xx sample offset exceeds the sample length for a non-looping sample or the loop end for a looping one (no matter what type of loop), the sample will be considered finished and thus not play at all. Note that the parameter of the 9xx effect is in samples, not bytes.

Notes and Instruments in a Cell

Whenever a non-zero instrument index appears in the channel, the instrument is remembered (the variable is labelled newInst on line 5) and used for all future note triggers until a new instrument appears. This always happens, no matter if the instrument is actually triggered or not.

The usual expectation is that there’s either a note with an instrument in a cell, or neither. But of course they can appear independently and be triggered independently. As it turns out, they pretty much split the responsibilities for setting up a note between them.

The note is triggered first. The note always uses the last instrument found in the channel (newInst); if there has been none (newInst==0), there’s nothing to do. When a note is triggered for an instrument, FT first determines the right sample to play for the note using the 96-byte keymap-sample assignment map found in the instrument header (if not stored in the file, it should probably be initialized to zero). Then, it is checked if the effective note for this instrument and sample (pattern note plus relative note number) is in the allowable range of 0 to 119. If not, nothing is triggered, any existing channel note keeps on playing. Otherwise, the note is triggered by setting the following properties of the channelNote:

  • relevant information on the previous note is backed up for ->volume ramping; the old and new note are set up for phase out and phase in
  • the instrument number is recorded (set to newInst)
  • the triggered pattern note is recorded (for use with retrigger)
  • the period is calculated
  • the sample position is set to the start or the value given by the 9xx effect
  • the flag indicating continued vibrato is reset, see 4xy effect below
  • the temporary period variable is reset
Notably, triggering a note does not change anything related to volume, panning, auto-vibrato or envelopes. If a note is triggered this way without an instrument in the same cell, it will play at the old note’s current volume and panning, will continue any ongoing vibrato sweep if applicable and will play the new instrument’s vibrato but without resetting the vibrato position. Since neither the old note's release state nor the fade volume are updated, the new note may continue to fade or be entirely silent from the start if the fade volume was at zero already. And because envelope states are not reinitialized, outcomes are very inconsistent if the instrument changes and either instrument uses envelopes.

If instead of a note, there is a key-off code in the channel, the triggering routine does the following:

  • the note-released flag is set for the playing note; this will cause the fade volume to start dropping if the fadeout-rate is positive and may affect envelope processing
  • if the volume envelope is turned off for the note’s instrument, the current volume is also set to zero
The second action ensures compatibility with S3M behaviour (ScreamTracker does not support envelopes and turns off PCM notes after key-off). If the volume is restored by an effect in the same or a later cell, one can hear the note fade.

Triggering an instrument does the other half of the work. Any defaults applied to the channel’s playing note in this process are always the ones pertaining to the instrument the note was triggered with (some sources claim it’s the new instrument’s values, but this is incorrect). For example if a note is triggered with instrument 5, and while it’s playing instrument 7 appears in the channel without a note or with a tone portamento effect, the current note’s volume and panning will be reset to the defaults of instrument 5. Here is what happens if an instrument is triggered:

  • the current volume and panning are set to the note’s instrument's defaults
  • only if there is no key-off code in the current cell, the note is marked as not released and the fade volume is re-initialized to its maximum
  • the envelopes are initialized
  • the auto-vibrato sweep state is initialized and the auto-vibrato position is reset
  • the retrigger counter for effect Rxy is reset to zero
  • the table indices for vibrato and tremolo are reset, if required by the waveform
  • the temporary volume value is reset
Notes At this point, we have covered and explained 17 out of the 35 XM compatibility switches found in OpenMPT, [XMC]:
  1. Use smooth Fasttracker 2 volume ramping
  2. Reset note-off on portamento if there is an instrument number
  3. Do not play portamento-ed note if no previous note is playing
  4. FT2-style Kxx handling
  5. Offset past sample end stops the note
  6. Portamento with instrument number applies volume settings of new sample, but not the new sample itself
  7. Next pattern starts on the same row as the last E60 command
  8. Keep processing faded channels for later portamento pickup
  9. Reload sample settings even if a note-off is placed next to an instrument number
  10. Portamento with note delay next to it is ignored
  11. Ignore out-of-range transposed notes
  12. Bxx or Dxx on the same row as E6x terminates the loop
  13. Portamento target is not reset with new notes
  14. Sustain point at end of envelope loop stops the loop after release
  15. Use FT2's broken period handling
  16. Fade instrument on note-off when there is no volume envelope; instrument numbers reset note-off status.
  17. Delayed instrument-less notes should not recall volume and panning
The majority of the remaining ones deal with individual effects.

Effects

The following tables list the effects supported by FastTracker II. Several extensions due to ImpulseTracker or ModPlug can be found in [MPT]. The table contains, from left to right, the effect command signature, its name, the mod format it originated from (either MOD or S3M, in the latter case the S3M signature is given, too), any related volume effects and a brief description. As usual, the last three columns indicate if there is more detailed information below (D), if there is tracker-specific behaviour or bugs to report (T) and if the effect has memory (M: the memory is used if the full parameter byte is zero, N: the two nibbles of the parameter byte are tested for zero individually).

Different effects don't share their memory except when noted in the description. This includes the case of related effects that slide in different directions (1xx and 2xx have separate memory, and the same applies to E1x/E2x, EAx/EBx and X1x/X2x).

Unless explicitly stated otherwise, all effects use the firstTick flag to differentiate between the first and later ticks of the row and thus treat a row delayed by the EEx effect as one longer row.

I will not explain the basic functionality of these effects if it is unchanged from MOD or S3M. Differences between the regular effect and its volume column version, as well as any interactions beyond shared memory will be addressed when introducing the volume column effects later. In what follows, the variable T will stand for ticks per row, so that 0<=tick<T.

effectnamesourcevol. eff.descriptionDTM
0xyArpeggio MODon every tick, cycle between note, note+x semitones, note+y semitones-T-
1xxPortamento UpMODdecrease the period by 4⋅xx on every tick except the first--M
2xxPortamento DownMODincrease the period by 4⋅xx on every tick except the first--M
3xxTone PortamentoMODMxset portamento target if note is provided and slide towards the target period by 4⋅xx on every tick except the first; glissando (effect E3x) affects the type of the slideDTM
4xyVibratoMODSx, Vyperform vibrato with speed x and depth y using the waveform set by effect E4x on every tick except the firstDTN
5xyVolume Slide with Tone PortamentoMODequivalent to Axy with 300---
6xyVolume Slide with VibratoMODequivalent to Axy with 400---
7xyTremoloMODperform tremolo with speed x and depth y using the waveform set by effect E7x on every tick except the firstDTN
8xxSet PanningMOD (ext)Pxset panning value for the channel to xx (0=left, $FF=right)---
9xxSet Sample OffsetMODset the starting offset in samples of the current sample to 256⋅xx-TM
AxyVolume SlideMOD+x, -yincrease the volume by x or decrease it by y on every tick except the first-TM
BxxPosition JumpMODafter processing this row, set the song position to xx and continue with row 0 of the corresponding patternDT-
CxxSet VolumeMODxxset the current volume to min{xx,64}---
DxyPattern BreakMODafter processing this row, continue with row 10⋅x+y of the next song position; note that xy is decimalDT-
FxxSet SpeedMODset ticks per row (for xx<$20) or tempo ("BPM"; for xx>=$20) to the indicated parameter value; xx=0 is invalid-T-
GxxSet Global VolumeS3M: Vxxset the global volume to min{xx,64}---
HxyGlobal Volume Slideanalogous to Axy, but for global volume--M
KxxKey Offrelease the current note after xx ticksDT-
LxxSet Envelope Positionset the frames of the envelopes to xx for the currently playing noteDT-
PxyPanning SlideRx, Lyanalogous to Axy, but for panningD-M
RxyMulti Retrig NoteS3M: Qxyretrigger the current instrument every y ticks, using x to select a tabulated volume changeDTN
TxyTremorS3M: Ixyturn the volume off and on repeatedly for the duration of the effect; on-time is x+1 ticks, off-time is y+1 ticksDTM
The XM format introduces two new effects with 4-bit parameters and adds them as sub-effects of the X-effect. ModPlug has added a number of additional mini-effects to the X-bank, many of them backported from the IT format (see [MPT]). Here, we will only discuss original FastTracker effects.
effectnamesourcevol. eff.descriptionDTM
E0x(Set Filter)MODnot implemented---
E1xFine Portamento UpMODdecrease the period by 4⋅x on the first tick--M
E2xFine Portamento DownMODincrease the period by 4⋅x on the first tick--M
E3xGlissando ControlMODenable (x=1) or disable (x=0) glissando for effect 3xx-T-
E4xSet Vibrato WaveformMODchoose the waveform to use with effect 4xyD--
E5xSet Finetune ValueMODset the finetune value for the current sampleD--
E6xLoop PatternMODset the current row as the loop target (x=0) or jump back to the loop target x times from the current row (x>0)DT-
E7xSet Tremolo WaveformMODchoose the waveform to use with effect 7xyDT-
E8x(Unused)unusedD--
E9xRetriggerMODretrigger the current instrument every x ticks starting at tick x if x>0; for x=0 only trigger on the first tickDT-
EAxFine Volume Slide UpMODUxincrease the volume by x on the first tickD-M
EBxFine Volume Slide DownMODDxdecrease the volume by x on the first tickD-M
ECxNote CutMODset the volume to zero after x ticks-T-
EDxNote DelayMODdelay triggering the note by x ticks-T-
EExPattern DelayMODincrease the number of ticks in this row from T to (x+1)⋅T-T-
EFx(Funk It!)MODnot implemented---
X1xExtra Fine Portamento UpS3M: ExEextra finely decrease the period by x on the first tick--M
X2xExtra Fine Portamento DownS3M: EExextra finely increase the period by x on the first tick--M
FastTracker supports a number of rather capable volume column effects. They are executed before the regular effects. It appears that at least some of them are implemented separately, as there are some minor differences in behaviour. With the exception of three volume column effect that share their memory with their regular effect counterpart, which is indicated by an S in the last column of the following table, none of them have their own effect memory and effect parameters of 0 have no special meaning. In the case of the volume slide effects, a parameter of 0 means that no change is made to the permanent note volume v; however, the temporary volume component v_temp is still reset to 0, which helps clean up after the tremolo and tremor effects. For the most part, volume effects and regular effects combine exactly as one wold expect. For example, -3 together with A50 will increase the volume by 2 on every tick except the first. Where interactions are more complicated, they are explained below.
vol. eff.nameencodingeffectdescriptionDTM
xxSet Volume$10+xxCxxset volume to xx---
-xVolume Slide Down$60+xA0xdecrease volume by x on every tick except the first---
+xVolume Slide Up$70+xAx0increase volume by x on every tick except the first---
DxFine Volume Slide Down$80+xEBxdecrease the volume by x on the first tick---
UxFine Volume Slide Up$90+xEAxincrease the volume by x on the first tick---
SxSet Vibrato Speed$A0+xset vibrato speed memory to xDTS
VxVibrato$B0+x40xperform vibrato of depth x at a previously set speedDTS
PxSet Panning Position$C0+x8xxset the panning position to x⋅$10D--
LxPanning Slide Left$D0+xP0xdecrease the panning position by x on every tick except the first---
RxPanning Slide Right$E0+xPx0increase the panning position by x on every tick except the first---
MxTone Portamento$F0+x3xxset portamento target if note is provided and slide towards the target period by 4⋅(x⋅$10) on every tick except the first; glissando (effect E3x) affects the type of the slideDTS

0xy – Arpeggio

Tracker-specific:
Such a straightforward effect, I have no idea why it’s sometimes implemented so weirdly. Arpeggio does not rely on firstTick and thus treats the repeats caused by EEx as separate rows. It uses the same temporary period variable p_temp as vibrato. As regular effects are executed after volume effects, arpeggio supersedes volume effect vibrato. If both x and y are zero, arpeggio is turned off; otherwise the effect will set p_temp on every tick for its own purposes, even when the original note is played. This means that volume column vibrato will not be noticed at all if arpeggio is on.

Arpeggio operates relative to the current note period, not the last triggered period; it therefore cooperates as expected with portamento.

Now it gets weird. Naively, we would expect the effect to produce for example the pitch sequence [-xy-xy-x] on a row if there are 8 ticks. FastTracker II does something significantly more sophisticated. Define sequences (0) to (2) as (0)=[-yx],(1)=[-] and (2)=[-x], and let i=T%3. For ticks per row T in the range from 1 to 15, FastTracker will play sequence (i) followed by as many instances of (3) as needed to fill the row. In other words, if the row length in ticks is not divisible by 3, it will play the appropriate short sequence (1) or (2), followed by triples (3). If there are more than 15 ticks, FT2 simply plays [y] until the remaining row length is down to 15, followed by the sequence for a length-15 row, 5 times (0). Some examples for different row lengths:

 1: [-]
 2: [-x]
 3: [-yx]
 4: [--yx]
 8: [-x-yx-yx]
15: [-yx-yx-yx-yx-yx]
19: [yyyy-yx-yx-yx-yx-yx]
31: [yyyyyyyyyyyyyyyy-yx-yx-yx-yx-yx]
I also noted some odd behaviour if the base note pitch is very high. I this case, arpeggio plays the correct base note for the [-]-ticks, but tones based on a much lower base notes for the [x] and [y] ticks. This is the case even if x or y is 0. I don’t have more details for that.

Arpeggio does reset the temporary period adjustment to zero when done, so the pitch changes do not persist beyond the duration of the effect.

3xx – Tone Portamento

3xx shares its effect memory with the volume column effect Mx.
Tracker-specific:
The target period is calculated for the currently playing instrument (not some other newInst that may have been set in the same or an earlier row). It is only reset by other 3xx and Mx effects.

Glissando works as expected and is relative to a version of the previously triggered note (more specifically, it appears that the glissando cut-off frequencies are calculated based on the assumption that standard, unadjusted notes were triggered; finetune adjustments, whether defined in the instrument or by the E5x effect, do not affect the frequencies of the rounded notes played, just the timing when they are reached; quite possibly glissando just rounds to generic semitone-periods). Glissando-rounding is applied by setting the same temporary period variable p_temp used by all effects. This leads to the following interactions:

  • Volume column vibrato never works with 3xx, as 3xx will reset the vibrato's temporary period value on every relevant tick.
  • When using the volume column tone portamento Mx with regular effect vibrato 4xy, effect vibrato, being processed after Mx, overwrites the temporary period component set by Mx for glissando, thus neutralizing the effects of the glissando flag.
  • Auto vibrato works fine while using tone portamento, whether or not glissando is on.
Neither 3xx nor its volume column equivalent Mx reset the glissando temporary period values after the effect is over. If glissando is on, the note continues playing at a rounded period value until some other effect clears p_temp.

It is possible in principle to slide towards the same note multiple times (slide to note, use 1xx/2xx, slide again. However, there are some quirks to this. When setting the target period, portamento decides on the direction of the slide (slideUp in the code above). When, given this direction, tone portamento reaches a period p beyond the target, p is simply set to the target. However, after arriving at the target, the slide direction is always reset for some reason (sideUp=false), so after the target has been reached once, it’s only possible to slide towards lower notes from then on (if a period above the target period is encountered, it’s set to the target period immediately).

Some other properties of this effect were already discussed before, for example that 3xx does not block instrument triggers (thus allowing to update volume, panning and envelope settings, as well as reviving released notes).

Invalid slide targets beyond the allowed range of effective notes seem to be ignored, although the check may be a little more generous than for notes (about 2 semitones at the top end, in my experiments).

4xy – Vibrato

The interpretation of the speed and depth parameters remains unchanged from MOD 4xy and S3M Hxy. The two nibbles of the effect parameter, x and y, are checked for zero separately and committed to effect memory accordingly. The effect shares its memory with Sx and Vx.
Tracker-specific:
Vibrato is executed on every tick except firstTick. There appears to be a flag as part of the current note state indicating if vibrato was executed on the previous row. This flag is set by 4xy and reset when a new note is successfully triggered (not if an out-of-range note is ignored or a key-off code appears in the channel). Only if this flag is set, the adjustment to the temporary period p_temp will be applied on firstTick, too. This allows for playing vibrato continuously without switching back to the base note for one tick at the beginning of each row.

The flag is tested by 4xy on firstRow, set by the effect on !firstRow and reset whenever there's no 4xy or 6xy effect in the cell. This means that the flag is not set on a row if T=1. The waveform values are applied to the period as expected, i.e. for example the sine wave will initially increase period length, thus lowering the pitch of the note. Effect vibrato resets the temporary period adjustment to zero when done.

7xy – Tremolo

Tremolo is modelled after the MOD version of the effect. The volume adjustment is given by v_temp=eff7_y*4*eff7_wave[eff7_wti], just like in ProTracker. This means the volume change can be as much as ±60 for y=$F.
Tracker-specific:
The waveform values add to the volume, so that for example sine wave tremolo initially increases volume.

FastTracker does not reset the temporary volume component to zero, so the volume change persists after the effect is over. This helps smooth out the volume blips that occur on first ticks when the effect is active for several rows, without using a more complicated mechanism as 4xy does. ScreamTracker had a similar "feature."

In an apparent attempt to surpass ScreamTracker in tremolo bugginess, FT2 seems to intentionally replicate ProTracker's tremolo quirks, thus making the “ramp down” wave a little more interesting. The picture below shows how this may look. When the waveform function makes a discrete jump, it also unwarrantedly flips the slope, and it tends to do these things more often than it should and at the wrong times if vibrato is playing. All this is sometimes characterized as a FastTracker bug, but really, FT2 just faithfully emulates ProTracker characteristics. ProTracker looked at the wrong bits when calculating this wave on the fly, see [TTF]. The formula for replicating the buggy behaviour was given in the post on ->the MOD format.
NoteSince the ProTacker sources were around and FastTracker originally started out as a ProTracker clone, its creators were likely aware of lots of ProTracker bugs and idiosyncrasies. Current trackers follow the FT2 example and emulate this bug. OpenMPT, for example, can replicate ProTracker and FastTracker tremolo behaviour.

These plots show the recorded audio output for tremolo with the ramp-down waveform in different scenarios. In all cases, a high-pitch tone plays for 16 rows at 9 ticks per row (for a total of 128 non-first ticks) at a volume of $20. Tremolo depth is set to 8, so that the effective volume varies from 0 to its maximum of $40.

Notice how nice and smooth everything is thanks to volume ramping.

9xx – Set Sample Offset

Tracker-specific:
How this effect works has already been ->discussed above. As a reminder: It only works with a note next to it, only for regular triggers (no retriggers or such) and is not remembered in any way by the current note.

Axy – Volume Slide

Tracker-specific:
If both x and y are non-zero, a volume slide up by x is performed. If both are zero, the last non-zero parameter is recalled from memory.

Bxx – Position Jump

This effect works largely as in ProTracker. It is commonly combined with Dxy to jump to a specific song position-row address. This works as expected if Dxy is in a higher-numbered channel than Bxx.

Jumping with Bxx also circumvents the E6x bug.

Tracker-specific:
If xx is greater or equal than the length of the song, the song will start over at the restart position. How this effect operates under the hood and how it interacts with other commands has been ->explained in some detail above.

Dxy – Pattern Break

It is frequently recommended to trigger this effect at the end of a pattern that uses the E6x effect to counter the E6x bug, see for example [MPT] and [MTM]. See Bxx for combining Bxx and Dxy.
Tracker-specific:
If the target row 10⋅x+y is greater than 63, the jump target will be row 0. This effect does not check if the target row is valid, which gives rise to a bug explained in the section on ->global control flow.
NoteIn the absence of flexible pattern length, the restriction to only jump target rows below 64 would have been sufficient to avoid this bug.

How this effect operates under the hood and how it interacts with other commands has been ->explained in some detail above.

Fxx – Set Speed

Tracker-specific:
F00 practically halts the module. Notes keep on playing, and new ticks are being generated (effects like vibrato keep on working), but there is no noticeable progress to the next row.

Kxx – Key Off

This command results in the same actions as issuing a key-off code 97 in the note column. The note is flagged as released, which will allow it to fade. If the current note’s instrument does not have a volume envelope, the note is also cut (volume set to zero). The difference compared to the key-off code is that Kxx can be applied at any tick xx, and that it can be combined with a new note in the same cell.
Tracker-specific:
This effect is executed based on the current tick, rather than firstTick, i.e. it is executed multiple times on rows delayed with EEx (you can check by combining a Kxx effect with a volume slide up volume column effect for an instrument without volume envelope). The argument xx is taken modulo $20 (xx&$1F or xx%$20). For xx=$53, for example, the effect will actually be applied on tick $13. If xx&$1F is greater or equal than the number of ticks per row, the effect is never executed.

Another idiosyncrasy of the effect was ->discussed above. K00 is special in that it cancels a note trigger (see [MTM]).

Lxx – Set Envelope Position

This effect simply sets the current envelope frames to xx (xx=0 thus restarts the envelope). This may be useful for jumping beyond a loop, for example.

I’m not sure if the following oddity is best classified as a part of the effect specification or a tracker-specific bug...

Tracker-specific (or not?):
Lxx always sets the volume envelope position if the volume envelope is active. The panning envelope position is also set to xx, but only if the sustain point of the volume envelope is enabled (see [MTM], [MPT]). This is the case even if the volume envelope is disabled. I suspect this is situation where FT looks at the wrong bit (the volume sustain point flag rather than the panning envelope flag).

Lxx performs the same checks and changes to the frame position that happen after a regular frame increment. Jumping to the loop end position will result in the frame being set to the loop start immediately.

Pxy – Panning Slide

This effect works analogous to volume slides in every respect. I just wanted to point out that units are as expected, so P40 will increase the panning position by 4. It may take quite a few ticks to slide from left (0) to right ($FF).

Rxy – Multi Retrig Note

FastTracker supports both the ProTracker and the ScreamTracker retrigger commands with their different behaviours, and this is the S3M one. The parameter x selects how the note volume gets adjusted, whereas y indicates the rate at which the note gets retriggered.

The effect uses a counter that is maintained across rows, which makes it possible to easily retrigger notes at intervals that do not align with or are even greater than ticks per row T.

The volume change table is almost the same as the one used by the S3M format, with the notable difference that x=0 recalls the last volume change parameter from memory.

x01234567
vol chgM-1-2-4-8-16*2/3/2
x89$A$B$C$D$E$F
vol chg0124816*3/2*2

Effect memory works separately for the two nibbles of the effect parameter.

Tracker-specific:
This effect employs a persistent counter. Details can be found in [MMW]. When the effect is active, it counts up and triggers when the counter is greater than or equal to the retrigger rate y. This ensures that the current rate is always an upper bound to the wait time between retriggers. The counter is initialized to 0 at playback start and is reset in two cases:
  • if either Rxy or E9x for x>0 (E90 won’t do) retrigger a note
  • if an instrument is triggered in the channel (it’s not enough for it to appear as stated in [MMW] – if I delay the instrument with EDx so that it never triggers, nothing happens to the counter)
Notes don’t reset the counter.

The effect retriggers the note that was previously triggered (channelNote.note) for the most recent instrument that appeared in the channel (newInst). This means that the instrument can change upon retrigger. In this case, the period will be correctly calculated for the previous note and the new instrument, even if the “relative note” or finetune values for the new instrument are different. The current period does not matter for retriggering.

Only the note gets triggered, not the instrument. The sample starts from the beginning, but volume, panning envelopes, vibrato and key-off status remain unchanged. If the volume column contains a volume value, it is reapplied before calculating the volume change selected by the parameter x (this may be part of the note-trigger routine, as this behaviour is similar to other effects triggering off-cycle). This is the only unexpected volume column interaction I found. Other volume column effects such as slides will continue operating between triggers, thus altering outcomes, but in an entirely regular way. I’m pretty sure I missed something, though, as both [MPT] and [MTM] characterize this effect as exceedingly buggy.

Since this effect has its own counter, there are no interesting interactions with EEx.

Txy – Tremor

This effect has its own counter and is executed on every tick, allowing it to operate consistently across rows without being affected by T or EEx delays. Effect memory is triggered only if both x and y are zero, meaning it’s possible to have either on or off periods of a single tick, but not both the same time.
Tracker-specific:
[MTM] points out that as effect memory is initialized to zero, it is possible to have both on and off periods of length one at the same time, but only until Txy is used with a non-zero parameter for the first time in a channel.

This effect does not reset the temporary volume variable when it's done. If the effect ends on silent, the note stays off until the note volume is changed by some other event. ScreamTracker had a similar bug.

E3x – Glissando Control

Tracker-specific:
Glissando is enabled for any nonzero value of x.

E4x/E7x - Set Vibrato/Tremolo Waveform

This command works as in ProTracker. Bits 0 and 1 of x select the waveform and bit 2 indicates whether the position is reset to zero when triggering a note.

For regular effect vibrato and tremolo, it’s reasonable to assume that the 4 possible waveforms are provided as fixed-point numbers with at least 6-bit resolution (7 bits including sign) in tables of length 64. Then, the waveforms look something like this:

0:sine:wave0[i]=sin(2*pi*i/64)
1:ramp down:wave1[i]=1-((i+32)%64)/32
2:square:wave2[i]=i<32?1:-1
3:wave3[i]=wave2[i]

Wave 3 is not mentioned in the documentation, but when selected, it’s just the square wave, exactly as in ProTracker.

Auto vibrato makes use of the same waveforms in principle, but speed is 4 times finer. Does this mean the we need 256-element tables? Maybe, but given that auto vibrato depth is low (a maximum amplitude of ±15 period units) I’d be surprised if it made a noticeable difference.

Tracker-specific:
Setting bit 2 (to prevent index reset on trigger) worked as expected for vibrato, and even note delays and retriggers reset the vibrato waveform index exactly when they should. When I tried setting bit 2 for tremolo, FastTracker 2.08 and 2.09 crashed upon triggering the next note in the channel.

E5x – Set Finetune Value

In contrast to MOD files, where the effect parameter could directly be interpreted as a signed finetune value in the proper range of -8 to 7, here x is an unsigned number that is scaled to the now wider range of finetune values in XM files. It works like this (see [MTM]): finetune=(x-8)⋅16

E6x – Loop Pattern

In FastTracker, jump targets and loop counters are channel-level variables rather than global ones. This makes it possible to nest loops by placing them in different channels. As this is also the standard way of handling loops for XM modules in the other major trackers, it’s probably how most people expect it to work.
Tracker-specific:
We have already discussed the infamous E6x bug and mentioned the possibility of triggering another bug (jumping beyond the end of a pattern) with this effect, and any interactions with the other control flow effects Bxx and Dxy should be clear as well. I will therefore only summarize the aspects of the effect's operation that we have not touched upon yet.

Unlike the Dxy effect, E60 and E6x handle rows above 63 as they should. The jump target is initialized to zero upon module start.

For the most part, FastTracker follows ProTracker behaviour with regards to loop handling, not the more robust ScreamTracker approach. It does not reset the jump target defined by E60 after a pattern break, making it possible to jump forward. Nor does it set a new jump target after completing a loop, which means that two E6x commands without an E60 between them will typically create an infinite loop.

E8x – Unused Effect Number

FastTracker does not implement this effect, but it is one of the original MOD panning effect extensions and implemented as such by modern trackers, [MPT], [MTM].

E9x – Retrigger

This effect retriggers the note once on tick 0 if x=0, otherwise it retriggers every x ticks except tick 0 (i.e. x, 2⋅x, 3⋅x, ...). If x is greater than or equal to the ticks per row T, the effect will not trigger at all. Unlike Rxy, E9x has no counter that would allow it to operate across rows in a meaningful way.

Really, this effect is the MOD retrigger, Rxy is the S3M retrigger.

Tracker-specific:
To be more specific:
E90 triggers only on firstTick, whereas E9x for x>0 triggers if ((tick%x==0) && !firstTick). This effect therefore uses both tick numbers and the firstTick mechanism to control timing, which leads to interesting outcomes when combined with EEx.

Suppose the module runs at 16 ticks per row. E95 will trigger on ticks 5, 10 and 15. If the row is doubled in length using an EE1 command in some other channel, the tick-0 trigger will not be suppressed the second time the row plays, as firstTick is not set (see the discussion on ->cell processing above). This means there will be a total of 7 triggers, at ticks 5, 10, 15, 16, 21, 26 and 31 out of the total 32 ticks of the repeated row.

Otherwise, things work just like with the Rxy effect: E9x will trigger the last note for the most recently seen instrument (newInst) without triggering the instrument itself.

As mentioned before, E9x for x>0 also resets the Rxy effect counter to 0 whenever it actually retriggers, E90 does not.

ECx – Note Cut

Tracker-specific:
As an effect that uses tick numbers to determine timing, it treats EEx repeats as separate rows and executes on each repetition (unless x>=T, in which case it will never execute). This can be verified by combining the effect with a volume column volume slide.

EDx – Note Delay

Tracker-specific:
ED0 seems to do (almost) nothing. EDx for x>0 is implemented more like a retrigger than a delay effect. As we saw in the section on ->cell processing, the effect suppresses triggering of notes and instruments as well as volume effect processing on the first tick. It then does some of these things itself when the time comes. Because the timing is controlled by tick numbers, the EDx command will repeat the same actions on every EEx-induced repeat.

Unless there's a key-off command in the cell, EDx always triggers a note on the tick when the effect activates. This is either the last note triggered in the channel (a proper note, not a note-off command; tone portamento slide targets don’t count either) or the note in the current cell. In the model code above, this note was called effEDxNote. Like the E9x and Rxy effects, EDx will trigger this note for newInst.

If instead of the note, there was a key-off command, no note is triggered (this means that the active note's instrument remains unchanged, even if a different instrument number was supplied). If the volume envelope of the current note's instrument is disabled, the note’s volume is reduced to zero.

Then the following actions are performed irrespective of whether a note was triggered or a note-off action was performed.

The envelope frames as well as auto vibrato sweep and index are reset, and so are vibrato and tremolo effect positions (if required by the chosen waveform) and the Rxy counter.

If an instrument number is provided in the same cell, default volume and panning of the current note's instrument are set (this is either the new instrument if the effect triggered a note, or the instrument the note had previously been triggered with if there's a key-off command in the cell). However, even in the absence of an instrument (and this includes the case where there was a key-off command), EDx will reset the key-off flag, set the fade volume to its maximum value, and reset the envelopes as well as the two vibrato states (table index and sweep).

The command also applies a set volume (xx) or set panning (Px) volume column command at this point (if one was supplied in the cell), but not the third remaining firstTick volume column command, Sx. Moreover, Px is not applied if there was a note-off command in the cell.

None of this affects the volume effect that only perform their action after firstTick in any way.

Short version Here's the executive summary:

if (effEDxNote==97) channelNote.release() else channelNote.trigger(effEDxNote,newInst,noFinetuneOverride,0) // the following is a regular instrument trigger EXCEPT that the argument // indicates whether volume and panning should be reset to instrument defaults; // this will undo most of the effects of channelNote.release() channelNote.triggerInst(effEDxSetInstVolPan) if (volume effect xx) channelNote.setVolume(xx) if ((volume effect Px) && (effEDxNote!=97)) channelNote.setPanning(x<<4)

I stated that ED0 does almost nothing. When combined with another effect that supposedly does nothing, S0, it restarts the module. This is either the weirdest FT2 bug or a secret control flow code.

EEx – Pattern Delay

Tracker-specific:
We’ve looked at this effect in detail in the ->control flow section and discussed every possible interaction of interest with any other effect. What more is there to say?

EEx sets the global variable repeatRow to x, even if x=0. If there are several EEx commands in a row, only the last one matters.

Sx – Set Vibrato Speed

This effect sets the speed parameter for later use with the Vx or 4xy effect. It does not actually perform vibrato. Combining Sx with 40y is equivalent to just using 4xy.
Tracker-specific:
S0 does nothing, except trigger odd behaviour when combined with ED0. The Sx effect is cancelled by an EDx effect (x>0). See the discussion of EDx for details. When Sx is combined with 4yz in the same cell, vibrato plays at a speed of y, not x, as discussed in the section on ->cell processing and triggering.

Vx – Vibrato

This effect performs vibrato of depth x with a speed selected by a previous 4xy or Sx command. It is equivalent to 40x. V0 uses a previously memorized depth as expected. Effect memory is shared with the 4xy effect.
Tracker-specific:
Combining Vx with 4yz in the same cell will play vibrato at speed 2⋅y and depth z. We can infer from this (1) that effect memory is processed first for Vx, then for 4xy as discussed in the section on ->cell processing, (2) that both effects independently operate and increment the vibrato table index, and (3) that both of them write to the same temporary period variable p_temp, so that the amplitude effect is not cumulative, as discussed in the section on ->playing notes.

Since other pitch changing effects reset the temporary period value used by vibrato, Vx will not work when combined with 1xx, 2xx, 3xx or 5xx in the same cell.

Vx does not use the 4xy flag indicating that vibrato happened on the previous row. The volume column vibrato does not change p_temp on firstTick.

Another difference compared to 4xy is that Vx does not reset the temporary period when done, so after the effect ends, the note continues to play at the frequency last set by vibrato.

This means that ongoing volume column vibrato will still play continuously, even though the frequency is not deliberately set on firstTick. Switching from Vx to 4xy between rows will play vibrato continuously. Switching from 4xy to Vx, however, there will be one tick when no vibrato effect is applied.

Px – Set Panning Position

Px sets the 8-bit panning position to (x<<4), i.e. P5 is equivalent to 850. [MTM] states that MilkyTracker will set the panning position to (x<<4)+x, so P5 would be the same as 855. This is different from what I measured in FT2.

Mx – Tone Portamento

Mx (x>0) sets the portamento speed parameter to (x<<4) and performs tone portamento just like 3xx. M3 is equivalent to 330. [MTM] states that MilkyTracker will set the speed to (x<<4)+x, so M3 would do the same as 333. This is different from what I measured in FT2.

Effect memory is shared with the 3xx effect. The speed parameter x used by the volume column effect is rather coarse, so it may be advisable to use M0, which slides at a previously set speed.

Tracker-specific:
As discussed in the section on ->cell processing, the effect’s target period update may get cancelled if an EDx command appears in the same cell. When Mx and 3yy are combined in the same cell, the slide is performed at a speed of 2⋅x. This suggests (1) that Mx effect memory is processed later than 3xx memory (see ->cell processing above) and (2) that both effects independently adjust the note period with a cumulative effect.

References:

Try out the original FastTracker in your browser.
->https://archive.org/details/demoscene_Fasttracker-Triton
This is the module format documentation included with FastTracker II:
->/b/Modules/docs/TECH.txt
I found an annotated version online:
->https://github.com/gheja/jsstuffs/blob/master/docs/FastTracker%202%20v2.04%20(.xm).txt
The MultimediaWiki has information about the file format and useful details about some effects.
->https://wiki.multimedia.cx/index.php/Fast_Tracker_2_Extended_Module
This document is older (2001), but very detailed. It explains the file format and playback.
->https://github.com/milkytracker/MilkyTracker/blob/c955afd0be5dd723f92483e78ca5e272c80ec380/resources/reference/xm-form.txt
The most complete XM file format specification I know of.
->https://www.celersms.com/doc/XM_file_format.pdf
The OpenMPT manual on XM effects.
->https://wiki.openmpt.org/Manual:_Effect_Reference#XM_Effect_Commands
The OpenMPT manual on XM-related compatibility settings - this gives helpful information on where to look for unusual behaviour and bugs.
->https://wiki.openmpt.org/Manual:_Compatible_Playback#XM_compatibility_settings
The MilkyTracker Manual. It explains the XM effects in a FastTracker-focused way.
->https://milkytracker.org/docs/MilkyTracker.html
The German Wikipedia Page on FastTracker. It even mentions some of the bugs!
->https://de.wikipedia.org/wiki/FastTracker
The original documentation that came with FastTracker II, converted to PDF by the friendly people at MilkyTracker.
->https://milkytracker.org/docs/FT2.pdf
The FastTracker II panning formula.
->https://modarchive.org/forums/index.php?topic=3517.0
The Wikipedia page about the "Convox Speech Thing," a simple and cheap analogue device that could be attached to a PC's parallel port to generate sound output.
->https://en.wikipedia.org/wiki/Covox_Speech_Thing
A homemade "speech thing" (website in Czech, but with lots of pictures).
->https://blog.frantovo.cz/c/307/DAC%20%28zvukov%C3%A1%20karta%29%20pro%20LPT%20port%20a.k.a.%20Covox
Apparently it's still possible to order "speech things."
->https://www.vogons.org/viewtopic.php?t=51483
This document is compares ProTracker, FastTracker and TakeTracker module playback. Among other things, it has some comments about the ramp-down tremolo bug in ProTracker and FastTracker:
->https://weaselaudiolib.sourceforge.net/TakeTracker-FastTracker-notes-and-format.html#e44_7xx_-_tremolosawtoothwaveformbugmod


<- previous^ index