Song format
This describes the format that songs are stored in fortISSimO at the binary level.
The format of files exported by teNOR is kind of irrelevant, and the format of hUGETracker’s .uge
files is documented elsewhere.
Last updated as of commit
7fb8329
. This is susceptible to having changed since then; see the changes since then, particularly filesteNOR/export.rs
andinclude/fortISSimO.inc
.
ℹ️ For forward compatibility’s sake, it is unwise to assume that components will always be in a certain order unless otherwise specified.
For example, currently, teNOR emits duty instruments immediately after the “row pool”, and right before the wave instruments; this should be considered an unstable implementation detail. However, all fields of the song header will remain in the specified order (until the next major release of fO, anyway).
Unless specified:
- there is no padding between any of the structures’ fields,
- all multi-byte values are stored in little-endian format (low byte first).
Header
- BYTE — How many ticks each row lasts for.
- BYTE — The maximum value
wOrderIdx
can take; inclusive. Also specifies the size of the pattern pointer arrays, below. - POINTER — To the array of duty instruments.
- POINTER — To the array of wave instruments.
- POINTER — To the array of noise instruments.
- POINTER — To the song’s routine.
- POINTER — To the array of waves.
- For each channel, from
1
to4
: its column of the order matrix:- As many as specified above:
- POINTER — To a pattern.
- As many as specified above:
Patterns
A pattern is simply a collection of rows; teNOR attempts to find overlap between the patterns to minimise how much space they take; thus, all patterns are coalesced into sort of a “pool of rows”.
There is no definitive end to the row pool—simply, only as many rows are emitted as are necessary.
Rows
Rows are constitued differently depending on whether they belong to a “main” pattern, or a subpattern.
Pattern rows
- BYTE — The effect’s parameter.
- BYTE — Split as follows:
- UPPER NIBBLE — The instrument ID, or 0 for “none”.
- LOWER NIBBLE — The effect ID.
- BYTE — The note’s ID, or 90 for “no note”.
Subpattern rows
- BYTE — The effect’s parameter.
- BYTE — Split as follows:
- UPPER NIBBLE — Bits 0–3 of the next row’s ID.
- LOWER NIBBLE — The effect ID.
- BYTE — Split as follows:
- BIT 7 — Bit 4 of the next row’s ID.
- BITS 0–6 — The offset from the base note, plus 36; or 90 for “no offset”.
In order to allow looping back on the same row as an effect, subpatterns rows all have a built-in jump target. Since there are 32 possible rows to jump to, 5 bits are needed—the unused 7th bit of the note byte is used to store that 5th bit.
Effects
Effect IDs are unchanged from hUGETracker. The effect parameter is, however, sometimes different.
Effect | ID (hex) | Stored parameter |
---|---|---|
Arpeggio | 0 | Unchanged. |
Porta up | 1 | Unchanged. |
Porta down | 2 | Unchanged. |
Tone porta | 3 | Unchanged. |
Vibrato | 4 | Unchanged. |
Set master vol | 5 | Unchanged. |
Call routine | 6 | Unchanged. |
Note delay | 7 | Unchanged. |
Set panning | 8 | Unchanged. |
Change timbre | 9 | Unchanged. |
Vol slide | A | Unchanged. |
Pos jump | B | The pattern ID is stored in wOrderIdx format. |
Set vol | C | Nibbles are swapped from hUGETracker. |
Pattern break | D | The row ID is stored in wForceRow ’s format. |
Note cut | E | Unchanged. |
Set tempo | F | Unchanged. |
Instruments
Instruments are grouped in “banks” by their type. Each bank is an array of up to 15 instruments, with no padding in-between.
Duty
- BYTE — Frequency sweep, in NR10 format.
- BYTE — Duty & length, in NR11/NR12 format.
- BYTE — Volume & envelope, in NR12/NR22 format.
- POINTER — Pointer to the subpattern, or 0 if not enabled.
- BYTE — Control bits:
- BIT 7 — Always set.
- BIT 6 — Whether the “length” is enabled.
Wave
- BYTE — Length, in NR31 format.
- BYTE — Volume, in NR32 format.
- POINTER — Pointer to the subpattern, or 0 if not enabled.
- BYTE — Control bits:
- BIT 7 — Always set.
- BIT 6 — Whether the “length” is enabled.
- BYTE — ID of the wave to load.
Noise
- BYTE — Volume, in NR42 format.
- POINTER — Pointer to the subpattern, or 0 if not enabled.
- BYTE — Control bits:
- BIT 7 — 0 if the LFSR should be in “long” (15-bit) mode, 1 if the LFSR should be in “short” (7-bit) mode.
- BIT 6 — Whether the “length” is enabled.
- BITS 0–5 — Length, in NR41 format.
Waves
Each wave is an array of 16 bytes, stored directly in wave RAM format.
There are up to 16 waves; wave IDs start at 0.