::|CONTENTS
- What's the most efficient module type?
- Basic tips and tools
- Space-saving tips: Samples & Instruments
- Space-saving tips: Pattern Structure
- Space-saving tips: Pattern Data, Notes & Effects
- Space-saving tips: Other Data
- See also
So, you're trying to tackle the
tinymod/modXk formats, eh? It may seem like they're pretty limiting, but there are a lot of cool and interesting ways to optimize your module and cram a lot of information into a small amount of space - and plenty of best practices to ensure you're not wasting space along the way.
The smaller the format, the more critical it will be to optimize your pattern data, which is a major focus of this article.
Mod04k,
mod08k and
mod12k will be especially reliant on careful tracking to get much of a full song written. This is simply due to the size of pattern data being generally a few bytes per item, and that's proportionally more of, say, 4KB, than it is of 64KB.
For the larger tinymods like
mod32k,
mod48k and
mod64k, the most relevant factor in your filesize will wind up being the samples you use. You can still save space by optimizing your pattern data, of course, but it may be easier to manipulate the samples you're using to cut your module's size down.
What's the most efficient module type?
Generally it's
.it, which handles empty space the best of the bunch.
.xm is viable too if you prefer, but .xm is best used if the song data is quite dense; otherwise your song may take up more space than if it were .it.
The least viable option is .mod. Tread carefully; there's no fancy compression here. It's a unique sound though, so if you want to try it, go ahead!
Basic tips and tools
* In OpenMPT's Edit menu, you will find options for "Cleanup" and "Automatic Sample Trimmer". Both of these tools can save you space and are best run near the end of your work (choose cleanup options carefully).
* If you're using .it,
Munch.py is a tool that will optimize your module data even further; results may vary, but you can expect to save 0.5-1.5KB overall in the smallest formats, and potentially way more in the larger ones thanks to its sample compression*. Run your module through here at the very end!
(*Note that IT sample compression is enabled by default in OpenMPT v1.31 or later, which means Munch will not make as big a difference, though it will still help in other ways.)
* Similarly, if you're using .xm,
BoobieSqueezer (yes, really) is a tool that will optimize your module data even further while remaining completely playable and format-accurate.
* In OpenMPT, using "Compatibility Export" from the File menu may save a small amount of space; it's shaving away a few newer features to be better compatible with the original DOS trackers.
Space-saving tips: Samples & Instruments
Sample Size
Samples take up a lot of space. Use small, mono, 8-bit sample data. Ensure there is no extra data after the loop; it is useless space. Likewise for any silence at the start of your sample. Note that Munch.py (for .it) applies valuable space-saving compression here*, which may turn out to be a huge amount in the larger mod*k variants. But that doesn't mean you shouldn't be efficient, and you may need to do some trimming to make space without editing your patterns.
*As mentioned above, OpenMPT v1.31+ has IT lossless sample compression turned on by default, which saves a lot of space in your module without you having to do a thing! It does mean that Munch.py will ultimately do less for your module though.
If using mono 8-bit samples, their
uncompressed size should equate to the number of bytes they are consuming (e.g. 7000 length sample = 7000 bytes). Compression via Munch or OpenMPT will reduce this, though. Complete silence in the middle of samples also gets compressed nicely!
Downsampling
You may be able to comfortably
downsample your sounds, though they will sound flatter and flatter (losing high frequency content) each time. This reduces the sample rate of the sound, lowering its quality by reducing the number of data points per second in the audio.
In OpenMPT, use
"Resample" on the sample screen--the sixth icon from the left in the big row of buttons, also mapped to Ctrl+R by default. This can also be done in Audacity by changing the "project rate" in the lower left corner.
Try it and see how small you can get them before the resulting sound becomes unacceptable to you. The default "downsample" option halves the size, but you can also input your own value to downsize in whatever proportion you desire. Compare with the "Freq. (Hz)" value listed on the lower left of the sample screen. If the value is 22000, choosing "21000" for your resample value will make it only slightly smaller, for example, whereas 11000 would halve it.
Downsampling in OpenMPT also offers different interpolation options. Generally, "r8brain (High Quality)" preserves the sample best, but you may get more interesting results with other options like "None", particularly if dealing with simpler waveforms.
Sample Trimming
Particularly if your sample loops a few periods of a waveform, consider trimming this down by either selecting a new loop point or by adjusting the position of the loop. If hunting for new loop points, the arrow buttons next to the Loop Start and Loop End values will try to seek to the nearest closely matching value to pair with the other value. You may also be able to simply subtract the same amount from both values to "nudge" the loop around while keeping it the same size.
OpenMPT does offer a "Crossfade" tool in the Samples tab - it may be also helpful when searching for a viable loop point to shore up a slight clicking sound or unwanted overtones. A couple of sliders are available to tweak its result.
Instruments
Instruments in .it take up
almost 400 bytes apiece, so avoid using Instrument mode unless you plan to take advantage of the features like New Note Actions (NNAs), random volume/panning sliders, sample maps and/or the various envelopes. However they
can be used very effectively with those features to achieve sounds otherwise not possible in the tinymod restrictions. If you want to learn more about designing complex .it Instruments and utilizing their more intricate features, check out the
Using .it Instruments article!
If you are just using
ADSR volume envelopes, they are probably best represented with
Dx/
Cx/
Bx/
Ax (vol column) or
Dxy (effect column) commands - the volume column ones will be most efficient, as detailed below.
You may be able to get away with this in the larger mod*k variants but be aware of the space they consume.
Also, for maximum efficiency, make sure the order of your samples and instruments matches! Occasionally Munch.py has issues if they are out of order, too. You can reorder them in OpenMPT's "Tree" view sidebar.
Space-saving tips: Pattern Structure
The "Unmute Trick"
It is possible to save pattern space in .it by using the
Mxx (Channel Volume) command to
mute and unmute channels of your choice.
For example, if you wrote a pattern that you wanted to copy and add something to, you could have the Channel Volume at 0 for the first pass, then set it to a new nonzero value with
Mxx for the second pass - all contained within one pattern.
Depending on the construction of your song this can be an extremely useful, invaluable trick to fitting more material into less space.
M40 is the maximum (which is 64 in hex).
A channel can start out muted by
setting its volume to 0 in the General tab.
A common use of the Unmute Trick is to have an additional pattern,
one row in length, containing only the necessary
Mxx commands. Try using a Speed Change command of speed 1 in this tiny pattern alongside another Speed Change on the final row of the previous pattern that's 1 less than your overall speed. That way, they add up and it's as smooth as possible!
Another useful variant is to use the
Nxy Channel Volume Slide command to
gradually fade in (or out) the material in a new channel.
Speed
Using
slower speed settings (i.e. high number of ticks per row) will help you save space overall because there will be less empty space inbetween your notes. Consider using the
slowest speed you can while still being able to contain the notes you want, so as to have few (or no) blank rows. Blank rows cost you 1 byte each in .it and substantially more in .xm because each row on each channel costs space.
The speed can be briefly raised for a flourish of fast notes and then lowered again (with
Axx (.it)/
Fxx (.xm)).
Also, the
SDx (Note Delay) command can be used for intermittent
offsetting from the slow-speed grid, so give this a try if there's just a few notes keeping you away from slow speed settings working for you.
Pattern Delays
Blank rows can be trimmed by using the
Pattern Delay command,
SEx (.it) or
EEx (.xm) in the row before the blank rows would happen. This extends the length of the row by holding in place
x times, where
x is the length of one row.
For example,
SE1 at speed 6 would cause that row to be held for 12 ticks instead.
If there's nothing in between a bunch of rows, consider using this command and moving everything else up to fill the space!
Another way to do this is by changing the speed - for instance, setting the speed to 16 in the middle of music that's speed 4 in order to hold for four rows' worth of time, then switching back to 4. Doing it this way may consume marginally more space in comparison to the Pattern Delay, though (since it will take at minimum two effect commands).
Notably, there is a strange interaction between Pattern Delays and the Note Delay command (
SDx (.it)/
EDx (.xm)); if used on a row containing a Pattern Delay, the delayed note will
repeat with each iteration of the Pattern Delay. At times this may be unwanted, but it can also potentially be used to your advantage (e.g. with Instrument NNAs in .it).
Pattern Loops
If there are smaller repeating chunks of pattern data (for example, you wrote a 16-row pattern where the stuff in 1-4, 5-8, and 9-12 is identical), you can save space by using the
Pattern Loop command (
SBx in .it,
E6x in .xm).
Placing an
SB0 command at row 1 and an
SB2 command at row 4 will cause 1-4 to loop twice before proceeding. This is potentially a huge space-saver. In this example your pattern's length would be halved, from 16 to 8 rows.
They are also another way to potentially take advantage of the "unmute trick" in .it for adding new sounds on a second loop through something. Try placing an
Mxx command alongside the second
SBx, or even after it for additional unique loop passthroughs!
Pattern loops can also be
nested! Try experimenting with loops inside of loops. Place the new loops on a different channel (the loop end command will always search for the most recent
SBx in its own channel).
Notably for .it, the end of a Pattern Loop may behave strangely if placed on a row that contains a Pattern Delay
SEx command. (This works fine in .xm however.) The loop may be ignored entirely if the value for
SEx is greater than the value for the adjacent
SBx. In other situations, the loop count may differ from what's written or simply loop infinitely. The rules for this appear complex, with low-value
SEx (1 or 2) being more likely to cause infinite loops, especially if both effect values are odd or even.
Merging Patterns, De-Duping Patterns
You can
merge/combine patterns that are only used once (or only ever used adjacently), as they may take up a few bytes more of space when separated. A new pattern will take up, at minimum,
14 bytes (one blank row).
Also, pattern separators
+++ in .it take up a byte apiece (as well as any other addition to the pattern order), making them technically trimmable chaff.
However, splitting patterns out can also be beneficial to
"de-duplicate" data in your module: if there are chunks of pattern data utilized in multiple places within your song, it will be beneficial to split the patterns so that the reused chunk is only stored in one place. For example: You have 16 rows of data - let's call each group of 4 rows A B C D. The "A" chunk also appears in a later pattern like A E F G; that pattern starts the same way, but the rest is different. If you split both patterns so that the "A" part is on its own, you can save space by using that pattern in both places. Since pattern data takes up so much space, this will almost certainly be worth the byte cost of a new pattern.
Space-saving tips: Pattern Data, Notes & Effects
"Continue" Commands
Do not use "continue" commands (e.g.
H00,
D00, etc) in .it. It is more efficient to write
H44 H44 H44 than
H44 H00 H00. Continue commands take up an additional byte.
Munch.py should change this for you automatically, but it is good practice to get used to it.
There can be niche use cases for "continue" commands - for example, a pattern containing only continues can be made to sound different if played after some other pattern assigning a different value.
T00, the Set Tempo continue command, could be either a speed-up or a slow-down depending on whether it's preceded by
T01 (-1 bpm) or
T11 (+1 bpm). If doing this, you may not be able to use Munch (or at least you will have to modify the code yourself to make it not remove continues).
Repeated Commands Are Compressed
This one is a very important concept! Reuse of previous commands in .it saves space if they are used in succession--for
all columns.
For example,
v56 v56 v56 v56 will take up less space than
v56 v12 v56 v12. But also,
C-4 C-4 C-4 C-4 will take up less space than
C-4 C-5 C-4 C-5.
This means that it's best to stick with constant volumes where possible, and if you are alternating between volumes or effects, separating them into their own channels is a better use of space.
Other effects will also be compressed if used without something different separating them, as detailed in the next tip, which elaborates upon this idea. How much will depend on a few details!
Volume Column vs. Effect Column
In general, for .it,
volume column commands take up 3 bytes of space apiece, while
effect column commands take up 4 bytes of space. Thus it is likely more efficient to put volume slide commands in the volume column (
Dx/
Cx/
Bx/
Ax).
For .xm, instead it's 1 byte and 2 bytes, respectively!
As mentioned above, compression of consecutive effects occurs in .it format - even if there are blank rows inbetween their uses.
For compression in the volume column, the first repeated effect is reduced from 3 bytes to 2, and all subsequent repeated effects get reduced to 1 byte!
For the effects column, reuse of the same type of effect (e.g. two H commands) consumes 1 fewer byte, and likewise for reuse of the same effect value (e.g. A4), regardless of the effect itself.
This means that with subsequent uses of the same effect, for example
E02 E02 E02, all effects after the first consume 2 bytes instead of 4. Different values for E would change this to 3 bytes, as would different effects all using value 02.
Note that if using
Hx for vibrato in the volume colum, it must be "initialized" with an
Hxy effect command prior, to set the desired speed.
(An interesting niche .it case: If every successive row in a channel is identical, i.e. same note, instrument, volume and effect, the additional effects [or notes or volumes] take up no space whatsoever!)
Note Cuts
For .it users, Note Cuts
^^ may take up a little more space than using a
v00 command, although this may depend somewhat on what effects you might be using in the volume column, and may be undesirable if using a New Note Action in Instrument mode other than the default Cut. If you need to do some trimming though, try swapping Note Cuts out for
v00 commands.
A Note Cut (or Off or Fade) in .it will cost you
3 bytes generally, but has the potential to be compressed down to 2 if no new data is placed in the instrument, volume, or effect columns.
Meanwhile, it just costs 1 byte in .xm, the same as the cost of a volume column command, so either one works.
Instrument Column
In .it,
instrument column values don't take up space either, so trimming them is not typically important. In fact, it should be discouraged if using Munch.py and Instrument mode both. This is because one of the thing Munch cuts space through is the Sample Map within the Instrument settings - all notes that the script deems "unused" get assigned a garbage value pointing to a nonexistent sample. How does it check for something "unused"? It looks for each value next to the note in the instrument column. This will probably cause some notes to make no sound at all.
This has a notable interaction if you're using
Gx in the volume column and removing the value in the instrument column to keep its volume consistent (this is just a quirky .it behavior - if the previous note was at a non-default volume, the new portamento note will play at default volume unless you do this). If that new note doesn't appear anywhere else in your module, Munch will still interpret this as an "unused" note and it won't make sound in your munched module.
One of the only reasons you could bother to trim instrument column values in .it is if you're repeatedly alternating between notes and Note Cuts. The Note Cuts specifically will be compressed by 1 byte each if there's no additional data outside of the note column inbetween them.
In .xm, instrument column values do actually take up space. You can trim all but the first value that sets the instrument and shave a byte of space each.
Auto-Vibrato
Rather than using vibrato commands, the
"Auto-Vibrato" setting on the sample screen can be used to automatically apply vibrato at a chosen rate, depth and "sweep" (which sets the length of time until vibrato starts happening).
This can be set for each sample individually, without even needing Instrument mode!
In .it format, rate can be 0-63 with 63 being the fastest; depth can be 0-31 with 31 being the widest; and sweep can be 0-255 with 255 making it occur immediately.
For .xm it's slightly different: rate is still 0-63 with 63 being the fastest; depth can only be 0-15 with 15 being the widest; and sweep can be 0-255 but instead the value is exactly the number of ticks until vibrato triggers, so 0 is immediately.
Space-saving tips: Other Data
Extra Channels
Extra channels do not actually take up space in .it - unless they're full, of course. A 24-channel .it file will be the same size as 1-channel if blank. (For .xm though this is definitely not true! It does not handle blank space efficiently!)
Text Fields
The song Name field in the General tab takes up no space, so feel free to indicate preferred interpolation settings here.
The Artist field, however,
does take up additional space, so Munch.py will trim it away.
The name fields for samples and instruments do not take up additional space.
However, the name fields for Channels (set in the General tab) do take up space, so avoid using those.
See also
The tinymod formats:
*
mod04k (format)
*
mod08k (format)
*
mod12k (format)
*
mod16k (format)
*
mod24k (format)
*
mod32k (format)
*
mod48k (format)
*
mod64k (format)
Related formats:
*
S3XMODIT (*.s3m, *.xm, *.mod, *.it)
*
Amigamod (*.mod)
*
ModPlugTracker Module (*.mptm)
Related articles:
*
IT Module Optimisation (some more technical stuff than what's written here)