Scream Tracker 3 module is a tracker music file format that, as all tracker music, bundles both sound samples and instructions on which notes to play. It originates from a Scream Tracker 3 music editor (1994) by Future Crew, derived from original Scream Tracker 2 (.stm) module format.
Instrument descriptions in S3M format allow to use either digital samples or setup and control AdLib (OPL2) synth.
Music is organized in so called patterns
. "Pattern" is a generally
a 64-row long table, which instructs which notes to play on which
time measure. "Patterns" are played one-by-one in a sequence
determined by orders
, which is essentially an array of pattern IDs
This page hosts a formal specification of Scream Tracker 3 module using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
digraph {
rankdir=LR;
node [shape=plaintext];
subgraph cluster__s3m {
label="S3m";
graph[style=dotted];
s3m__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="song_name_pos">0</TD><TD PORT="song_name_size">28</TD><TD></TD><TD PORT="song_name_type">song_name</TD></TR>
<TR><TD PORT="magic1_pos">28</TD><TD PORT="magic1_size">1</TD><TD></TD><TD PORT="magic1_type">magic1</TD></TR>
<TR><TD PORT="file_type_pos">29</TD><TD PORT="file_type_size">1</TD><TD>u1</TD><TD PORT="file_type_type">file_type</TD></TR>
<TR><TD PORT="reserved1_pos">30</TD><TD PORT="reserved1_size">2</TD><TD></TD><TD PORT="reserved1_type">reserved1</TD></TR>
<TR><TD PORT="num_orders_pos">32</TD><TD PORT="num_orders_size">2</TD><TD>u2le</TD><TD PORT="num_orders_type">num_orders</TD></TR>
<TR><TD PORT="num_instruments_pos">34</TD><TD PORT="num_instruments_size">2</TD><TD>u2le</TD><TD PORT="num_instruments_type">num_instruments</TD></TR>
<TR><TD PORT="num_patterns_pos">36</TD><TD PORT="num_patterns_size">2</TD><TD>u2le</TD><TD PORT="num_patterns_type">num_patterns</TD></TR>
<TR><TD PORT="flags_pos">38</TD><TD PORT="flags_size">2</TD><TD>u2le</TD><TD PORT="flags_type">flags</TD></TR>
<TR><TD PORT="version_pos">40</TD><TD PORT="version_size">2</TD><TD>u2le</TD><TD PORT="version_type">version</TD></TR>
<TR><TD PORT="samples_format_pos">42</TD><TD PORT="samples_format_size">2</TD><TD>u2le</TD><TD PORT="samples_format_type">samples_format</TD></TR>
<TR><TD PORT="magic2_pos">44</TD><TD PORT="magic2_size">4</TD><TD></TD><TD PORT="magic2_type">magic2</TD></TR>
<TR><TD PORT="global_volume_pos">48</TD><TD PORT="global_volume_size">1</TD><TD>u1</TD><TD PORT="global_volume_type">global_volume</TD></TR>
<TR><TD PORT="initial_speed_pos">49</TD><TD PORT="initial_speed_size">1</TD><TD>u1</TD><TD PORT="initial_speed_type">initial_speed</TD></TR>
<TR><TD PORT="initial_tempo_pos">50</TD><TD PORT="initial_tempo_size">1</TD><TD>u1</TD><TD PORT="initial_tempo_type">initial_tempo</TD></TR>
<TR><TD PORT="is_stereo_pos">51</TD><TD PORT="is_stereo_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="is_stereo_type">is_stereo</TD></TR>
<TR><TD PORT="master_volume_pos">51:1</TD><TD PORT="master_volume_size">7b</TD><TD>b7</TD><TD PORT="master_volume_type">master_volume</TD></TR>
<TR><TD PORT="ultra_click_removal_pos">52</TD><TD PORT="ultra_click_removal_size">1</TD><TD>u1</TD><TD PORT="ultra_click_removal_type">ultra_click_removal</TD></TR>
<TR><TD PORT="has_custom_pan_pos">53</TD><TD PORT="has_custom_pan_size">1</TD><TD>u1</TD><TD PORT="has_custom_pan_type">has_custom_pan</TD></TR>
<TR><TD PORT="reserved2_pos">54</TD><TD PORT="reserved2_size">8</TD><TD></TD><TD PORT="reserved2_type">reserved2</TD></TR>
<TR><TD PORT="ofs_special_pos">62</TD><TD PORT="ofs_special_size">2</TD><TD>u2le</TD><TD PORT="ofs_special_type">ofs_special</TD></TR>
<TR><TD PORT="channels_pos">64</TD><TD PORT="channels_size">1</TD><TD>Channel</TD><TD PORT="channels_type">channels</TD></TR>
<TR><TD COLSPAN="4" PORT="channels__repeat">repeat 32 times</TD></TR>
<TR><TD PORT="orders_pos">96</TD><TD PORT="orders_size">num_orders</TD><TD></TD><TD PORT="orders_type">orders</TD></TR>
<TR><TD PORT="instruments_pos">...</TD><TD PORT="instruments_size">2</TD><TD>InstrumentPtr</TD><TD PORT="instruments_type">instruments</TD></TR>
<TR><TD COLSPAN="4" PORT="instruments__repeat">repeat num_instruments times</TD></TR>
<TR><TD PORT="patterns_pos">...</TD><TD PORT="patterns_size">2</TD><TD>PatternPtr</TD><TD PORT="patterns_type">patterns</TD></TR>
<TR><TD COLSPAN="4" PORT="patterns__repeat">repeat num_patterns times</TD></TR>
<TR><TD PORT="channel_pans_pos">...</TD><TD PORT="channel_pans_size">1</TD><TD>ChannelPan</TD><TD PORT="channel_pans_type">channel_pans</TD></TR>
<TR><TD COLSPAN="4" PORT="channel_pans__repeat">repeat 32 times</TD></TR>
</TABLE>>];
subgraph cluster__channel_pan {
label="S3m::ChannelPan";
graph[style=dotted];
channel_pan__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="reserved1_pos">0</TD><TD PORT="reserved1_size">2b</TD><TD>b2</TD><TD PORT="reserved1_type">reserved1</TD></TR>
<TR><TD PORT="has_custom_pan_pos">0:2</TD><TD PORT="has_custom_pan_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="has_custom_pan_type">has_custom_pan</TD></TR>
<TR><TD PORT="reserved2_pos">0:3</TD><TD PORT="reserved2_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="reserved2_type">reserved2</TD></TR>
<TR><TD PORT="pan_pos">0:4</TD><TD PORT="pan_size">4b</TD><TD>b4</TD><TD PORT="pan_type">pan</TD></TR>
</TABLE>>];
}
subgraph cluster__pattern_cell {
label="S3m::PatternCell";
graph[style=dotted];
pattern_cell__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="has_fx_pos">0</TD><TD PORT="has_fx_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="has_fx_type">has_fx</TD></TR>
<TR><TD PORT="has_volume_pos">0:1</TD><TD PORT="has_volume_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="has_volume_type">has_volume</TD></TR>
<TR><TD PORT="has_note_and_instrument_pos">0:2</TD><TD PORT="has_note_and_instrument_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="has_note_and_instrument_type">has_note_and_instrument</TD></TR>
<TR><TD PORT="channel_num_pos">0:3</TD><TD PORT="channel_num_size">5b</TD><TD>b5</TD><TD PORT="channel_num_type">channel_num</TD></TR>
<TR><TD PORT="note_pos">1</TD><TD PORT="note_size">1</TD><TD>u1</TD><TD PORT="note_type">note</TD></TR>
<TR><TD PORT="instrument_pos">2</TD><TD PORT="instrument_size">1</TD><TD>u1</TD><TD PORT="instrument_type">instrument</TD></TR>
<TR><TD PORT="volume_pos">3</TD><TD PORT="volume_size">1</TD><TD>u1</TD><TD PORT="volume_type">volume</TD></TR>
<TR><TD PORT="fx_type_pos">4</TD><TD PORT="fx_type_size">1</TD><TD>u1</TD><TD PORT="fx_type_type">fx_type</TD></TR>
<TR><TD PORT="fx_value_pos">5</TD><TD PORT="fx_value_size">1</TD><TD>u1</TD><TD PORT="fx_value_type">fx_value</TD></TR>
</TABLE>>];
}
subgraph cluster__pattern_cells {
label="S3m::PatternCells";
graph[style=dotted];
pattern_cells__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="cells_pos">0</TD><TD PORT="cells_size">6</TD><TD>PatternCell</TD><TD PORT="cells_type">cells</TD></TR>
<TR><TD COLSPAN="4" PORT="cells__repeat">repeat to end of stream</TD></TR>
</TABLE>>];
}
subgraph cluster__channel {
label="S3m::Channel";
graph[style=dotted];
channel__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="is_disabled_pos">0</TD><TD PORT="is_disabled_size">1b</TD><TD>BitsType1(BigBitEndian)</TD><TD PORT="is_disabled_type">is_disabled</TD></TR>
<TR><TD PORT="ch_type_pos">0:1</TD><TD PORT="ch_type_size">7b</TD><TD>b7</TD><TD PORT="ch_type_type">ch_type</TD></TR>
</TABLE>>];
}
subgraph cluster__swapped_u3 {
label="S3m::SwappedU3";
graph[style=dotted];
swapped_u3__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="hi_pos">0</TD><TD PORT="hi_size">1</TD><TD>u1</TD><TD PORT="hi_type">hi</TD></TR>
<TR><TD PORT="lo_pos">1</TD><TD PORT="lo_size">2</TD><TD>u2le</TD><TD PORT="lo_type">lo</TD></TR>
</TABLE>>];
swapped_u3__inst__value [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">id</TD><TD BGCOLOR="#E0FFE0">value</TD></TR>
<TR><TD>value</TD><TD>(lo | (hi << 16))</TD></TR>
</TABLE>>];
}
subgraph cluster__pattern {
label="S3m::Pattern";
graph[style=dotted];
pattern__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="size_pos">0</TD><TD PORT="size_size">2</TD><TD>u2le</TD><TD PORT="size_type">size</TD></TR>
<TR><TD PORT="body_pos">2</TD><TD PORT="body_size">(size - 2)</TD><TD>PatternCells</TD><TD PORT="body_type">body</TD></TR>
</TABLE>>];
}
subgraph cluster__pattern_ptr {
label="S3m::PatternPtr";
graph[style=dotted];
pattern_ptr__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="paraptr_pos">0</TD><TD PORT="paraptr_size">2</TD><TD>u2le</TD><TD PORT="paraptr_type">paraptr</TD></TR>
</TABLE>>];
pattern_ptr__inst__body [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="body_pos">(paraptr * 16)</TD><TD PORT="body_size">...</TD><TD>Pattern</TD><TD PORT="body_type">body</TD></TR>
</TABLE>>];
}
subgraph cluster__instrument_ptr {
label="S3m::InstrumentPtr";
graph[style=dotted];
instrument_ptr__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="paraptr_pos">0</TD><TD PORT="paraptr_size">2</TD><TD>u2le</TD><TD PORT="paraptr_type">paraptr</TD></TR>
</TABLE>>];
instrument_ptr__inst__body [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="body_pos">(paraptr * 16)</TD><TD PORT="body_size">...</TD><TD>Instrument</TD><TD PORT="body_type">body</TD></TR>
</TABLE>>];
}
subgraph cluster__instrument {
label="S3m::Instrument";
graph[style=dotted];
instrument__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="type_pos">0</TD><TD PORT="type_size">1</TD><TD>u1→InstTypes</TD><TD PORT="type_type">type</TD></TR>
<TR><TD PORT="filename_pos">1</TD><TD PORT="filename_size">12</TD><TD></TD><TD PORT="filename_type">filename</TD></TR>
<TR><TD PORT="body_pos">13</TD><TD PORT="body_size">...</TD><TD>switch (type)</TD><TD PORT="body_type">body</TD></TR>
<TR><TD PORT="tuning_hz_pos">...</TD><TD PORT="tuning_hz_size">4</TD><TD>u4le</TD><TD PORT="tuning_hz_type">tuning_hz</TD></TR>
<TR><TD PORT="reserved2_pos">...</TD><TD PORT="reserved2_size">12</TD><TD></TD><TD PORT="reserved2_type">reserved2</TD></TR>
<TR><TD PORT="sample_name_pos">...</TD><TD PORT="sample_name_size">28</TD><TD></TD><TD PORT="sample_name_type">sample_name</TD></TR>
<TR><TD PORT="magic_pos">...</TD><TD PORT="magic_size">4</TD><TD></TD><TD PORT="magic_type">magic</TD></TR>
</TABLE>>];
instrument__seq_body_switch [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#F0F2E4">case</TD><TD BGCOLOR="#F0F2E4">type</TD></TR>
<TR><TD>:inst_types_sample</TD><TD PORT="case0">Sampled</TD></TR>
<TR><TD>_</TD><TD PORT="case1">Adlib</TD></TR>
</TABLE>>];
subgraph cluster__sampled {
label="S3m::Instrument::Sampled";
graph[style=dotted];
sampled__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="paraptr_sample_pos">0</TD><TD PORT="paraptr_sample_size">3</TD><TD>SwappedU3</TD><TD PORT="paraptr_sample_type">paraptr_sample</TD></TR>
<TR><TD PORT="len_sample_pos">3</TD><TD PORT="len_sample_size">4</TD><TD>u4le</TD><TD PORT="len_sample_type">len_sample</TD></TR>
<TR><TD PORT="loop_begin_pos">7</TD><TD PORT="loop_begin_size">4</TD><TD>u4le</TD><TD PORT="loop_begin_type">loop_begin</TD></TR>
<TR><TD PORT="loop_end_pos">11</TD><TD PORT="loop_end_size">4</TD><TD>u4le</TD><TD PORT="loop_end_type">loop_end</TD></TR>
<TR><TD PORT="default_volume_pos">15</TD><TD PORT="default_volume_size">1</TD><TD>u1</TD><TD PORT="default_volume_type">default_volume</TD></TR>
<TR><TD PORT="reserved1_pos">16</TD><TD PORT="reserved1_size">1</TD><TD>u1</TD><TD PORT="reserved1_type">reserved1</TD></TR>
<TR><TD PORT="is_packed_pos">17</TD><TD PORT="is_packed_size">1</TD><TD>u1</TD><TD PORT="is_packed_type">is_packed</TD></TR>
<TR><TD PORT="flags_pos">18</TD><TD PORT="flags_size">1</TD><TD>u1</TD><TD PORT="flags_type">flags</TD></TR>
</TABLE>>];
sampled__inst__sample [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="sample_pos">(paraptr_sample.value * 16)</TD><TD PORT="sample_size">len_sample</TD><TD></TD><TD PORT="sample_type">sample</TD></TR>
</TABLE>>];
}
subgraph cluster__adlib {
label="S3m::Instrument::Adlib";
graph[style=dotted];
adlib__seq [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD BGCOLOR="#E0FFE0">pos</TD><TD BGCOLOR="#E0FFE0">size</TD><TD BGCOLOR="#E0FFE0">type</TD><TD BGCOLOR="#E0FFE0">id</TD></TR>
<TR><TD PORT="reserved1_pos">0</TD><TD PORT="reserved1_size">3</TD><TD></TD><TD PORT="reserved1_type">reserved1</TD></TR>
<TR><TD PORT="_unnamed1_pos">3</TD><TD PORT="_unnamed1_size">16</TD><TD></TD><TD PORT="_unnamed1_type">_unnamed1</TD></TR>
</TABLE>>];
}
}
}
s3m__seq:channels_type -> channel__seq [style=bold];
s3m__seq:num_orders_type -> s3m__seq:orders_size [color="#404040"];
s3m__seq:instruments_type -> instrument_ptr__seq [style=bold];
s3m__seq:num_instruments_type -> s3m__seq:instruments__repeat [color="#404040"];
s3m__seq:patterns_type -> pattern_ptr__seq [style=bold];
s3m__seq:num_patterns_type -> s3m__seq:patterns__repeat [color="#404040"];
s3m__seq:channel_pans_type -> channel_pan__seq [style=bold];
pattern_cells__seq:cells_type -> pattern_cell__seq [style=bold];
swapped_u3__seq:lo_type -> swapped_u3__inst__value [color="#404040"];
swapped_u3__seq:hi_type -> swapped_u3__inst__value [color="#404040"];
pattern__seq:size_type -> pattern__seq:body_size [color="#404040"];
pattern__seq:body_type -> pattern_cells__seq [style=bold];
pattern_ptr__seq:paraptr_type -> pattern_ptr__inst__body:body_pos [color="#404040"];
pattern_ptr__inst__body:body_type -> pattern__seq [style=bold];
instrument_ptr__seq:paraptr_type -> instrument_ptr__inst__body:body_pos [color="#404040"];
instrument_ptr__inst__body:body_type -> instrument__seq [style=bold];
instrument__seq:body_type -> instrument__seq_body_switch [style=bold];
instrument__seq_body_switch:case0 -> sampled__seq [style=bold];
instrument__seq_body_switch:case1 -> adlib__seq [style=bold];
instrument__seq:type_type -> instrument__seq:body_type [color="#404040"];
sampled__seq:paraptr_sample_type -> swapped_u3__seq [style=bold];
swapped_u3__inst__value:value_type -> sampled__inst__sample:sample_pos [color="#404040"];
sampled__seq:len_sample_type -> sampled__inst__sample:sample_size [color="#404040"];
}