Die Weltkarte basiert auf NASAs Blue Marble Bildern.
Das Foto vom Arts Gebäude stammt aus dem Archiv der University of Saskatchewan.
zurück |
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.
Here’s a list of some of the more important innovations relevant to the XM format and module playback.
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.
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.
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].
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].
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.
[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.
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: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}\).
[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
[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.
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?
Anyway, the formulas convey the right ideas. 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.
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.
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.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.
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)
,
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.
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.
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.
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 tick
s are not continuously counted up during row repeats.
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.
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.
firstTick
, the pattern information gets loaded. firstTick
rather than tick==0
, because combining EEx with a row jump will execute the jump before the first repeat without reloading effect information.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.
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.
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
:
newInst
)If instead of a note, there is a key-off code in the channel, the triggering routine does the following:
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:
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
.
effect | name | source | vol. eff. | description | D | T | M |
0xy | Arpeggio | MOD | on every tick, cycle between note, note+x semitones, note+y semitones | - | T | - | |
1xx | Portamento Up | MOD | decrease the period by 4⋅xx on every tick except the first | - | - | M | |
2xx | Portamento Down | MOD | increase the period by 4⋅xx on every tick except the first | - | - | M | |
3xx | Tone Portamento | MOD | Mx | set 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 slide | D | T | M |
4xy | Vibrato | MOD | Sx, Vy | perform vibrato with speed x and depth y using the waveform set by effect E4x on every tick except the first | D | T | N |
5xy | Volume Slide with Tone Portamento | MOD | equivalent to Axy with 300 | - | - | - | |
6xy | Volume Slide with Vibrato | MOD | equivalent to Axy with 400 | - | - | - | |
7xy | Tremolo | MOD | perform tremolo with speed x and depth y using the waveform set by effect E7x on every tick except the first | D | T | N | |
8xx | Set Panning | MOD (ext) | Px | set panning value for the channel to xx (0=left, $FF=right) | - | - | - |
9xx | Set Sample Offset | MOD | set the starting offset in samples of the current sample to 256⋅xx | - | T | M | |
Axy | Volume Slide | MOD | +x, -y | increase the volume by x or decrease it by y on every tick except the first | - | T | M |
Bxx | Position Jump | MOD | after processing this row, set the song position to xx and continue with row 0 of the corresponding pattern | D | T | - | |
Cxx | Set Volume | MOD | xx | set the current volume to min{xx,64} | - | - | - |
Dxy | Pattern Break | MOD | after processing this row, continue with row 10⋅x+y of the next song position; note that xy is decimal | D | T | - | |
Fxx | Set Speed | MOD | set ticks per row (for xx<$20) or tempo ("BPM"; for xx>=$20) to the indicated parameter value; xx=0 is invalid | - | T | - | |
Gxx | Set Global Volume | S3M: Vxx | set the global volume to min{xx,64} | - | - | - | |
Hxy | Global Volume Slide | analogous to Axy, but for global volume | - | - | M | ||
Kxx | Key Off | release the current note after xx ticks | D | T | - | ||
Lxx | Set Envelope Position | set the frames of the envelopes to xx for the currently playing note | D | T | - | ||
Pxy | Panning Slide | Rx, Ly | analogous to Axy, but for panning | D | - | M | |
Rxy | Multi Retrig Note | S3M: Qxy | retrigger the current instrument every y ticks, using x to select a tabulated volume change | D | T | N | |
Txy | Tremor | S3M: Ixy | turn the volume off and on repeatedly for the duration of the effect; on-time is x+1 ticks, off-time is y+1 ticks | D | T | M | |
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. | |||||||
effect | name | source | vol. eff. | description | D | T | M |
E0x | (Set Filter) | MOD | not implemented | - | - | - | |
E1x | Fine Portamento Up | MOD | decrease the period by 4⋅x on the first tick | - | - | M | |
E2x | Fine Portamento Down | MOD | increase the period by 4⋅x on the first tick | - | - | M | |
E3x | Glissando Control | MOD | enable (x=1) or disable (x=0) glissando for effect 3xx | - | T | - | |
E4x | Set Vibrato Waveform | MOD | choose the waveform to use with effect 4xy | D | - | - | |
E5x | Set Finetune Value | MOD | set the finetune value for the current sample | D | - | - | |
E6x | Loop Pattern | MOD | set the current row as the loop target (x=0) or jump back to the loop target x times from the current row (x>0) | D | T | - | |
E7x | Set Tremolo Waveform | MOD | choose the waveform to use with effect 7xy | D | T | - | |
E8x | (Unused) | unused | D | - | - | ||
E9x | Retrigger | MOD | retrigger the current instrument every x ticks starting at tick x if x>0; for x=0 only trigger on the first tick | D | T | - | |
EAx | Fine Volume Slide Up | MOD | Ux | increase the volume by x on the first tick | D | - | M |
EBx | Fine Volume Slide Down | MOD | Dx | decrease the volume by x on the first tick | D | - | M |
ECx | Note Cut | MOD | set the volume to zero after x ticks | - | T | - | |
EDx | Note Delay | MOD | delay triggering the note by x ticks | - | T | - | |
EEx | Pattern Delay | MOD | increase the number of ticks in this row from T to (x+1)⋅T | - | T | - | |
EFx | (Funk It!) | MOD | not implemented | - | - | - | |
X1x | Extra Fine Portamento Up | S3M: ExE | extra finely decrease the period by x on the first tick | - | - | M | |
X2x | Extra Fine Portamento Down | S3M: EEx | extra 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. | name | encoding | effect | description | D | T | M |
xx | Set Volume | $10+xx | Cxx | set volume to xx | - | - | - |
-x | Volume Slide Down | $60+x | A0x | decrease volume by x on every tick except the first | - | - | - |
+x | Volume Slide Up | $70+x | Ax0 | increase volume by x on every tick except the first | - | - | - |
Dx | Fine Volume Slide Down | $80+x | EBx | decrease the volume by x on the first tick | - | - | - |
Ux | Fine Volume Slide Up | $90+x | EAx | increase the volume by x on the first tick | - | - | - |
Sx | Set Vibrato Speed | $A0+x | set vibrato speed memory to x | D | T | S | |
Vx | Vibrato | $B0+x | 40x | perform vibrato of depth x at a previously set speed | D | T | S |
Px | Set Panning Position | $C0+x | 8xx | set the panning position to x⋅$10 | D | - | - |
Lx | Panning Slide Left | $D0+x | P0x | decrease the panning position by x on every tick except the first | - | - | - |
Rx | Panning Slide Right | $E0+x | Px0 | increase the panning position by x on every tick except the first | - | - | - |
Mx | Tone Portamento | $F0+x | 3xx | set 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 slide | D | T | S |
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.
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:
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).
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.
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.
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.Notice how nice and smooth everything is thanks to volume ramping.
Jumping with Bxx also circumvents the E6x bug.
How this effect operates under the hood and how it interacts with other commands has been explained in some detail above.
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]).
I’m not sure if the following oddity is best classified as a part of the effect specification or a tracker-specific bug...
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.
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.
x | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
vol chg | M | -1 | -2 | -4 | -8 | -16 | *2/3 | /2 |
x | 8 | 9 | $A | $B | $C | $D | $E | $F |
vol chg | 0 | 1 | 2 | 4 | 8 | 16 | *3/2 | *2 |
Effect memory works separately for the two nibbles of the effect parameter.
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.
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.
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.
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.
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.
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.
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.
x>=T
, in which case it will never execute). This can be verified by combining the effect with a volume column volume slide.
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 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.
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.
(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.
(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.
previous | index |