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.
// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.
import (
"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
"bytes"
"io"
)
/**
* 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 way it's possible to reuse certain patterns more than once
* for repetitive musical phrases.
* @see <a href="http://hackipedia.org/browse.cgi/File%20formats/Music%20tracker/S3M%2c%20ScreamTracker%203/Scream%20Tracker%203.20%20by%20Future%20Crew.txt">Source</a>
*/
type S3m struct {
SongName []byte
Magic1 []byte
FileType uint8
Reserved1 []byte
NumOrders uint16
NumInstruments uint16
NumPatterns uint16
Flags uint16
Version uint16
SamplesFormat uint16
Magic2 []byte
GlobalVolume uint8
InitialSpeed uint8
InitialTempo uint8
IsStereo bool
MasterVolume uint64
UltraClickRemoval uint8
HasCustomPan uint8
Reserved2 []byte
OfsSpecial uint16
Channels []*S3m_Channel
Orders []byte
Instruments []*S3m_InstrumentPtr
Patterns []*S3m_PatternPtr
ChannelPans []*S3m_ChannelPan
_io *kaitai.Stream
_root *S3m
_parent kaitai.Struct
}
func NewS3m() *S3m {
return &S3m{
}
}
func (this S3m) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m) Read(io *kaitai.Stream, parent kaitai.Struct, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp1, err := this._io.ReadBytes(int(28))
if err != nil {
return err
}
tmp1 = kaitai.BytesTerminate(tmp1, 0, false)
this.SongName = tmp1
tmp2, err := this._io.ReadBytes(int(1))
if err != nil {
return err
}
tmp2 = tmp2
this.Magic1 = tmp2
if !(bytes.Equal(this.Magic1, []uint8{26})) {
return kaitai.NewValidationNotEqualError([]uint8{26}, this.Magic1, this._io, "/seq/1")
}
tmp3, err := this._io.ReadU1()
if err != nil {
return err
}
this.FileType = tmp3
tmp4, err := this._io.ReadBytes(int(2))
if err != nil {
return err
}
tmp4 = tmp4
this.Reserved1 = tmp4
tmp5, err := this._io.ReadU2le()
if err != nil {
return err
}
this.NumOrders = uint16(tmp5)
tmp6, err := this._io.ReadU2le()
if err != nil {
return err
}
this.NumInstruments = uint16(tmp6)
tmp7, err := this._io.ReadU2le()
if err != nil {
return err
}
this.NumPatterns = uint16(tmp7)
tmp8, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Flags = uint16(tmp8)
tmp9, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Version = uint16(tmp9)
tmp10, err := this._io.ReadU2le()
if err != nil {
return err
}
this.SamplesFormat = uint16(tmp10)
tmp11, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp11 = tmp11
this.Magic2 = tmp11
if !(bytes.Equal(this.Magic2, []uint8{83, 67, 82, 77})) {
return kaitai.NewValidationNotEqualError([]uint8{83, 67, 82, 77}, this.Magic2, this._io, "/seq/10")
}
tmp12, err := this._io.ReadU1()
if err != nil {
return err
}
this.GlobalVolume = tmp12
tmp13, err := this._io.ReadU1()
if err != nil {
return err
}
this.InitialSpeed = tmp13
tmp14, err := this._io.ReadU1()
if err != nil {
return err
}
this.InitialTempo = tmp14
tmp15, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.IsStereo = tmp15 != 0
tmp16, err := this._io.ReadBitsIntBe(7)
if err != nil {
return err
}
this.MasterVolume = tmp16
this._io.AlignToByte()
tmp17, err := this._io.ReadU1()
if err != nil {
return err
}
this.UltraClickRemoval = tmp17
tmp18, err := this._io.ReadU1()
if err != nil {
return err
}
this.HasCustomPan = tmp18
tmp19, err := this._io.ReadBytes(int(8))
if err != nil {
return err
}
tmp19 = tmp19
this.Reserved2 = tmp19
tmp20, err := this._io.ReadU2le()
if err != nil {
return err
}
this.OfsSpecial = uint16(tmp20)
for i := 0; i < int(32); i++ {
_ = i
tmp21 := NewS3m_Channel()
err = tmp21.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Channels = append(this.Channels, tmp21)
}
tmp22, err := this._io.ReadBytes(int(this.NumOrders))
if err != nil {
return err
}
tmp22 = tmp22
this.Orders = tmp22
for i := 0; i < int(this.NumInstruments); i++ {
_ = i
tmp23 := NewS3m_InstrumentPtr()
err = tmp23.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Instruments = append(this.Instruments, tmp23)
}
for i := 0; i < int(this.NumPatterns); i++ {
_ = i
tmp24 := NewS3m_PatternPtr()
err = tmp24.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Patterns = append(this.Patterns, tmp24)
}
if (this.HasCustomPan == 252) {
for i := 0; i < int(32); i++ {
_ = i
tmp25 := NewS3m_ChannelPan()
err = tmp25.Read(this._io, this, this._root)
if err != nil {
return err
}
this.ChannelPans = append(this.ChannelPans, tmp25)
}
}
return err
}
/**
* Number of orders in a song
*/
/**
* Number of instruments in a song
*/
/**
* Number of patterns in a song
*/
/**
* Scream Tracker version that was used to save this file
*/
/**
* 1 = signed samples, 2 = unsigned samples
*/
/**
* Offset of special data, not used by Scream Tracker directly.
*/
type S3m_Channel struct {
IsDisabled bool
ChType uint64
_io *kaitai.Stream
_root *S3m
_parent *S3m
}
func NewS3m_Channel() *S3m_Channel {
return &S3m_Channel{
}
}
func (this S3m_Channel) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_Channel) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp26, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.IsDisabled = tmp26 != 0
tmp27, err := this._io.ReadBitsIntBe(7)
if err != nil {
return err
}
this.ChType = tmp27
return err
}
/**
* Channel type (0..7 = left sample channels, 8..15 = right sample channels, 16..31 = AdLib synth channels)
*/
type S3m_ChannelPan struct {
Reserved1 uint64
HasCustomPan bool
Reserved2 bool
Pan uint64
_io *kaitai.Stream
_root *S3m
_parent *S3m
}
func NewS3m_ChannelPan() *S3m_ChannelPan {
return &S3m_ChannelPan{
}
}
func (this S3m_ChannelPan) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_ChannelPan) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp28, err := this._io.ReadBitsIntBe(2)
if err != nil {
return err
}
this.Reserved1 = tmp28
tmp29, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.HasCustomPan = tmp29 != 0
tmp30, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.Reserved2 = tmp30 != 0
tmp31, err := this._io.ReadBitsIntBe(4)
if err != nil {
return err
}
this.Pan = tmp31
return err
}
/**
* If true, then use a custom pan setting provided in the `pan`
* field. If false, the channel would use the default setting
* (0x7 for mono, 0x3 or 0xc for stereo).
*/
type S3m_Instrument_InstTypes int
const (
S3m_Instrument_InstTypes__Sample S3m_Instrument_InstTypes = 1
S3m_Instrument_InstTypes__Melodic S3m_Instrument_InstTypes = 2
S3m_Instrument_InstTypes__BassDrum S3m_Instrument_InstTypes = 3
S3m_Instrument_InstTypes__SnareDrum S3m_Instrument_InstTypes = 4
S3m_Instrument_InstTypes__Tom S3m_Instrument_InstTypes = 5
S3m_Instrument_InstTypes__Cymbal S3m_Instrument_InstTypes = 6
S3m_Instrument_InstTypes__Hihat S3m_Instrument_InstTypes = 7
)
var values_S3m_Instrument_InstTypes = map[S3m_Instrument_InstTypes]struct{}{1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {}}
func (v S3m_Instrument_InstTypes) isDefined() bool {
_, ok := values_S3m_Instrument_InstTypes[v]
return ok
}
type S3m_Instrument struct {
Type S3m_Instrument_InstTypes
Filename []byte
Body kaitai.Struct
TuningHz uint32
Reserved2 []byte
SampleName []byte
Magic []byte
_io *kaitai.Stream
_root *S3m
_parent *S3m_InstrumentPtr
}
func NewS3m_Instrument() *S3m_Instrument {
return &S3m_Instrument{
}
}
func (this S3m_Instrument) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_Instrument) Read(io *kaitai.Stream, parent *S3m_InstrumentPtr, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp32, err := this._io.ReadU1()
if err != nil {
return err
}
this.Type = S3m_Instrument_InstTypes(tmp32)
tmp33, err := this._io.ReadBytes(int(12))
if err != nil {
return err
}
tmp33 = kaitai.BytesTerminate(tmp33, 0, false)
this.Filename = tmp33
switch (this.Type) {
case S3m_Instrument_InstTypes__Sample:
tmp34 := NewS3m_Instrument_Sampled()
err = tmp34.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp34
default:
tmp35 := NewS3m_Instrument_Adlib()
err = tmp35.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Body = tmp35
}
tmp36, err := this._io.ReadU4le()
if err != nil {
return err
}
this.TuningHz = uint32(tmp36)
tmp37, err := this._io.ReadBytes(int(12))
if err != nil {
return err
}
tmp37 = tmp37
this.Reserved2 = tmp37
tmp38, err := this._io.ReadBytes(int(28))
if err != nil {
return err
}
tmp38 = kaitai.BytesTerminate(tmp38, 0, false)
this.SampleName = tmp38
tmp39, err := this._io.ReadBytes(int(4))
if err != nil {
return err
}
tmp39 = tmp39
this.Magic = tmp39
if !(bytes.Equal(this.Magic, []uint8{83, 67, 82, 83})) {
return kaitai.NewValidationNotEqualError([]uint8{83, 67, 82, 83}, this.Magic, this._io, "/types/instrument/seq/6")
}
return err
}
type S3m_Instrument_Adlib struct {
Reserved1 []byte
_unnamed1 []byte
_io *kaitai.Stream
_root *S3m
_parent *S3m_Instrument
}
func NewS3m_Instrument_Adlib() *S3m_Instrument_Adlib {
return &S3m_Instrument_Adlib{
}
}
func (this S3m_Instrument_Adlib) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_Instrument_Adlib) Read(io *kaitai.Stream, parent *S3m_Instrument, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp40, err := this._io.ReadBytes(int(3))
if err != nil {
return err
}
tmp40 = tmp40
this.Reserved1 = tmp40
if !(bytes.Equal(this.Reserved1, []uint8{0, 0, 0})) {
return kaitai.NewValidationNotEqualError([]uint8{0, 0, 0}, this.Reserved1, this._io, "/types/instrument/types/adlib/seq/0")
}
tmp41, err := this._io.ReadBytes(int(16))
if err != nil {
return err
}
tmp41 = tmp41
this._unnamed1 = tmp41
return err
}
type S3m_Instrument_Sampled struct {
ParaptrSample *S3m_SwappedU3
LenSample uint32
LoopBegin uint32
LoopEnd uint32
DefaultVolume uint8
Reserved1 uint8
IsPacked uint8
Flags uint8
_io *kaitai.Stream
_root *S3m
_parent *S3m_Instrument
_f_sample bool
sample []byte
}
func NewS3m_Instrument_Sampled() *S3m_Instrument_Sampled {
return &S3m_Instrument_Sampled{
}
}
func (this S3m_Instrument_Sampled) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_Instrument_Sampled) Read(io *kaitai.Stream, parent *S3m_Instrument, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp42 := NewS3m_SwappedU3()
err = tmp42.Read(this._io, this, this._root)
if err != nil {
return err
}
this.ParaptrSample = tmp42
tmp43, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LenSample = uint32(tmp43)
tmp44, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LoopBegin = uint32(tmp44)
tmp45, err := this._io.ReadU4le()
if err != nil {
return err
}
this.LoopEnd = uint32(tmp45)
tmp46, err := this._io.ReadU1()
if err != nil {
return err
}
this.DefaultVolume = tmp46
tmp47, err := this._io.ReadU1()
if err != nil {
return err
}
this.Reserved1 = tmp47
tmp48, err := this._io.ReadU1()
if err != nil {
return err
}
this.IsPacked = tmp48
tmp49, err := this._io.ReadU1()
if err != nil {
return err
}
this.Flags = tmp49
return err
}
func (this *S3m_Instrument_Sampled) Sample() (v []byte, err error) {
if (this._f_sample) {
return this.sample, nil
}
this._f_sample = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
tmp50, err := this.ParaptrSample.Value()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(tmp50 * 16), io.SeekStart)
if err != nil {
return nil, err
}
tmp51, err := this._io.ReadBytes(int(this.LenSample))
if err != nil {
return nil, err
}
tmp51 = tmp51
this.sample = tmp51
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.sample, nil
}
/**
* Default volume
*/
/**
* 0 = unpacked, 1 = DP30ADPCM packing
*/
type S3m_InstrumentPtr struct {
Paraptr uint16
_io *kaitai.Stream
_root *S3m
_parent *S3m
_f_body bool
body *S3m_Instrument
}
func NewS3m_InstrumentPtr() *S3m_InstrumentPtr {
return &S3m_InstrumentPtr{
}
}
func (this S3m_InstrumentPtr) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_InstrumentPtr) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp52, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Paraptr = uint16(tmp52)
return err
}
func (this *S3m_InstrumentPtr) Body() (v *S3m_Instrument, err error) {
if (this._f_body) {
return this.body, nil
}
this._f_body = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.Paraptr * 16), io.SeekStart)
if err != nil {
return nil, err
}
tmp53 := NewS3m_Instrument()
err = tmp53.Read(this._io, this, this._root)
if err != nil {
return nil, err
}
this.body = tmp53
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.body, nil
}
type S3m_Pattern struct {
Size uint16
Body *S3m_PatternCells
_io *kaitai.Stream
_root *S3m
_parent *S3m_PatternPtr
_raw_Body []byte
}
func NewS3m_Pattern() *S3m_Pattern {
return &S3m_Pattern{
}
}
func (this S3m_Pattern) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_Pattern) Read(io *kaitai.Stream, parent *S3m_PatternPtr, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp54, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Size = uint16(tmp54)
tmp55, err := this._io.ReadBytes(int(this.Size - 2))
if err != nil {
return err
}
tmp55 = tmp55
this._raw_Body = tmp55
_io__raw_Body := kaitai.NewStream(bytes.NewReader(this._raw_Body))
tmp56 := NewS3m_PatternCells()
err = tmp56.Read(_io__raw_Body, this, this._root)
if err != nil {
return err
}
this.Body = tmp56
return err
}
type S3m_PatternCell struct {
HasFx bool
HasVolume bool
HasNoteAndInstrument bool
ChannelNum uint64
Note uint8
Instrument uint8
Volume uint8
FxType uint8
FxValue uint8
_io *kaitai.Stream
_root *S3m
_parent *S3m_PatternCells
}
func NewS3m_PatternCell() *S3m_PatternCell {
return &S3m_PatternCell{
}
}
func (this S3m_PatternCell) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_PatternCell) Read(io *kaitai.Stream, parent *S3m_PatternCells, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp57, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.HasFx = tmp57 != 0
tmp58, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.HasVolume = tmp58 != 0
tmp59, err := this._io.ReadBitsIntBe(1)
if err != nil {
return err
}
this.HasNoteAndInstrument = tmp59 != 0
tmp60, err := this._io.ReadBitsIntBe(5)
if err != nil {
return err
}
this.ChannelNum = tmp60
this._io.AlignToByte()
if (this.HasNoteAndInstrument) {
tmp61, err := this._io.ReadU1()
if err != nil {
return err
}
this.Note = tmp61
}
if (this.HasNoteAndInstrument) {
tmp62, err := this._io.ReadU1()
if err != nil {
return err
}
this.Instrument = tmp62
}
if (this.HasVolume) {
tmp63, err := this._io.ReadU1()
if err != nil {
return err
}
this.Volume = tmp63
}
if (this.HasFx) {
tmp64, err := this._io.ReadU1()
if err != nil {
return err
}
this.FxType = tmp64
}
if (this.HasFx) {
tmp65, err := this._io.ReadU1()
if err != nil {
return err
}
this.FxValue = tmp65
}
return err
}
type S3m_PatternCells struct {
Cells []*S3m_PatternCell
_io *kaitai.Stream
_root *S3m
_parent *S3m_Pattern
}
func NewS3m_PatternCells() *S3m_PatternCells {
return &S3m_PatternCells{
}
}
func (this S3m_PatternCells) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_PatternCells) Read(io *kaitai.Stream, parent *S3m_Pattern, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
for i := 0;; i++ {
tmp66, err := this._io.EOF()
if err != nil {
return err
}
if tmp66 {
break
}
tmp67 := NewS3m_PatternCell()
err = tmp67.Read(this._io, this, this._root)
if err != nil {
return err
}
this.Cells = append(this.Cells, tmp67)
}
return err
}
type S3m_PatternPtr struct {
Paraptr uint16
_io *kaitai.Stream
_root *S3m
_parent *S3m
_f_body bool
body *S3m_Pattern
}
func NewS3m_PatternPtr() *S3m_PatternPtr {
return &S3m_PatternPtr{
}
}
func (this S3m_PatternPtr) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_PatternPtr) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp68, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Paraptr = uint16(tmp68)
return err
}
func (this *S3m_PatternPtr) Body() (v *S3m_Pattern, err error) {
if (this._f_body) {
return this.body, nil
}
this._f_body = true
_pos, err := this._io.Pos()
if err != nil {
return nil, err
}
_, err = this._io.Seek(int64(this.Paraptr * 16), io.SeekStart)
if err != nil {
return nil, err
}
tmp69 := NewS3m_Pattern()
err = tmp69.Read(this._io, this, this._root)
if err != nil {
return nil, err
}
this.body = tmp69
_, err = this._io.Seek(_pos, io.SeekStart)
if err != nil {
return nil, err
}
return this.body, nil
}
/**
* Custom 3-byte integer, stored in mixed endian manner.
*/
type S3m_SwappedU3 struct {
Hi uint8
Lo uint16
_io *kaitai.Stream
_root *S3m
_parent *S3m_Instrument_Sampled
_f_value bool
value int
}
func NewS3m_SwappedU3() *S3m_SwappedU3 {
return &S3m_SwappedU3{
}
}
func (this S3m_SwappedU3) IO_() *kaitai.Stream {
return this._io
}
func (this *S3m_SwappedU3) Read(io *kaitai.Stream, parent *S3m_Instrument_Sampled, root *S3m) (err error) {
this._io = io
this._parent = parent
this._root = root
tmp70, err := this._io.ReadU1()
if err != nil {
return err
}
this.Hi = tmp70
tmp71, err := this._io.ReadU2le()
if err != nil {
return err
}
this.Lo = uint16(tmp71)
return err
}
func (this *S3m_SwappedU3) Value() (v int, err error) {
if (this._f_value) {
return this.value, nil
}
this._f_value = true
this.value = int(this.Lo | this.Hi << 16)
return this.value, nil
}