Wednesday, April 6, 2011

Mostly undocumented .it features: The resonant filter

This is actually documented, but not in ITTECH.TXT. There are two known locations; the SchismTracker wiki, and the MikIT source code.

Firstly, the MIDI data is listed after the orderlist and the pointerlists. I don't yet know how these are listed; apparently this region can also contain timestamps. I'll just use the defaults right now.

Z00-Z7F sets the channel's cutoff, while Z80-Z8F set the channel's resonance to (that - 0x80) * 8. Yes, these are just the defaults.

You might have noticed the IFC/IFR fields in the instrument header. If the top bit of one is set, the lower 7 bits indicate the cutoff/resonance to be applied to that channel.

The cutoff frequency is 110*(2^3/12)*(2^(cutoff/24)).
The resonance affects the "damping factor", which is 10^(-resonance/(24*128*20)).

EDIT: Looking at the source code snippets Jeff has released, the base note is C3, not A2! Fixed in the formula.

The formulae you've probably seen before if you've looked for this are:
a = 1.0/(1.0+d+e);
b = (d+2*e)*a;
c = -e*a;
Except they usually do /(1.0+d+e) instead of *a, as that's how Jeffrey Lim documents it.

d and e are calculated like so (as documented in the MikIT source comment):
d = 2*(damping factor)*(sampling rate)/(natural frequency) + 2*(damping factor)-1
e = (sampling rate/natural frequency)^2
"damping frequency" was explained before.
"natural frequency" is the cutoff frequency*2*pi.

We can factorise this down into:
f = samprate/(2*pi*resfrq);
d = 2.0*dmpfac*(f + 1.0) - 1.0;
e = f*f;
You can also premultiply a dmpfac table by 2, and premultiply a resfrq table by 2*pi.

Just be warned: I don't entirely know how to clamp the filter frequencies so they don't "run away".

You will most likely want to use floating-point mixing here. Fixed-point mixing requires at least 14 fraction bits from my experience with this (11 is too small -- a can get pretty small here).

When mixing, use the formula:
output[n] = input[m]*a + output[n-1]*b + output[n-2]*c;
You can write this in your own way. I personally use a couple of variables and feed the calculated values along the two.

Hopefully some time later I might do something on IT214/IT215 decompression and later maybe even compression.

Tuesday, April 5, 2011

Some more .s3m weirdness.

Here's some stuff you might find interesting about .s3m. Note, these are all off the top of my head, and could possibly be wrong.

  • Vxx (set global volume) only triggers on the second tick onwards - it does not trigger on the first tick. Having said that, it does not fire on speed 1.
  • Vxx does not affect the current playing volumes - it only affects volumes when they are changed, and it does this by multiplying the base output volume by the global volume.
  • The highest possible volume for any PCM channel is 63 - anything higher than that gets clamped to 63, including 64. On Adlib channels, a volume of 64 can be achieved, but only by having an Adlib instrument with a default value of 64.
  • When fast slides are off, D0F and DF0 slide down and up by 15 respectively, on every tick. Apparently this was due to Future Crew attempting to improve compatibility (versions < 0x1320 / ST 3.20 always use "fast slides", which do nonfine slides on every tick) and getting it wrong. ImpulseTracker carries this relic.
  • Kxy and Lxy ignore the first tick. This is due to a pointer lacking in the "first tick" pointer table in the code.
  • SBx uses a global set of variables to track the loopback information. That is, SB0 on channel 1 and SB2 on channel 2 will work fine.
  • There's some weird, weird stuff pertaining to SBx and Cxx. If I recall correctly, SBx can override at least some of Cxx, but I'm not sure on all the information.
  • Rxx oscillates around the last explicitly declared volume (includes an explicitly declared instrument). That is, if you do vol 32, then D01, and then suddenly use Rxx, it will oscillate around volume 32.
  • You can map multiple pattern channels to the same output channel in the channel settings. I'm not sure how useful this is (e.g. doing Gxx and Ixx at the same time won't really work), but it does cause some rather bizarre behaviour. This could be worth blogging about in its own post. Experiment for yourself if you want.

So, as you can probably tell, .s3m is a little weird. With that said, a lot of this makes sense (though some of it is just simply due to someone screwing something up).

Saturday, April 2, 2011

An .s3m quirk.

I thought I'd start another blog, this time focusing on technical aspects of various tracker module formats, most likely focusing on .mod, .s3m, .xm and .it.

So let's start with something 8bitbubsy pointed out.

.s3m has a rather useful quirk when it comes to actually making a player. Normally you don't encounter this, but if you ever play Purple Motion's "Satellite one." then you'll probably encounter a stuck note if you're not actually using ST3 itself (nickysn's "tralala" written in pascal is an exception to this rule). There are two quirks here.

What happens is that Kxx actually gets the xx from Hxx, so you basically end up with H81, K81.

Why? Only a few effects actually have their own special memory (G, H/U, O). All the others which use effect memory just use the last nonzero effect value used in that track.

According to this, of which I have contributed a lot to and nickysn has done a whole bunch more on (plus trixter made a note), D/E/F/I/J/K/L/Q/R are the ones which reuse the previous nonzero effect value.

From what I gather, XMPlay does not implement this quirk.

So what about the K81? Someone could be bothered actually documenting it:
Dxy, 1 <= x <= 0xE, 1 <= y <= 0xE: Scream Tracker treats it as a slide down by y, i.e. equivalent to D0y. Impulse Tracker does nothing.

Hang on, that page still has the TODO there... ok, fixed.

If you want to be truly irritating, you can try screwing around with this quirk, like I did on battleofthebits (module download). That module managed to get second out of 4 places, too, though that was probably because ToastyX's entry absolutely owned.

So yeah, uh, just about nothing gets this quirk right, just because people had to make assumptions.