Scream Tracker 3 module: Go parsing library

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.

File extension

s3m

KS implementation details

License: CC0-1.0

References

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.

Go source code to parse Scream Tracker 3 module

s3m.go

// 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 interface{}
}
func NewS3m() *S3m {
	return &S3m{
	}
}

func (this *S3m) Read(io *kaitai.Stream, parent interface{}, 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_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) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp26, err := this._io.ReadBitsIntBe(2)
	if err != nil {
		return err
	}
	this.Reserved1 = tmp26
	tmp27, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.HasCustomPan = tmp27 != 0
	tmp28, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.Reserved2 = tmp28 != 0
	tmp29, err := this._io.ReadBitsIntBe(4)
	if err != nil {
		return err
	}
	this.Pan = tmp29
	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_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) Read(io *kaitai.Stream, parent *S3m_PatternCells, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp30, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.HasFx = tmp30 != 0
	tmp31, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.HasVolume = tmp31 != 0
	tmp32, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.HasNoteAndInstrument = tmp32 != 0
	tmp33, err := this._io.ReadBitsIntBe(5)
	if err != nil {
		return err
	}
	this.ChannelNum = tmp33
	this._io.AlignToByte()
	if (this.HasNoteAndInstrument) {
		tmp34, err := this._io.ReadU1()
		if err != nil {
			return err
		}
		this.Note = tmp34
	}
	if (this.HasNoteAndInstrument) {
		tmp35, err := this._io.ReadU1()
		if err != nil {
			return err
		}
		this.Instrument = tmp35
	}
	if (this.HasVolume) {
		tmp36, err := this._io.ReadU1()
		if err != nil {
			return err
		}
		this.Volume = tmp36
	}
	if (this.HasFx) {
		tmp37, err := this._io.ReadU1()
		if err != nil {
			return err
		}
		this.FxType = tmp37
	}
	if (this.HasFx) {
		tmp38, err := this._io.ReadU1()
		if err != nil {
			return err
		}
		this.FxValue = tmp38
	}
	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) Read(io *kaitai.Stream, parent *S3m_Pattern, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 1;; i++ {
		tmp39, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp39 {
			break
		}
		tmp40 := NewS3m_PatternCell()
		err = tmp40.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Cells = append(this.Cells, tmp40)
	}
	return err
}
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) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp41, err := this._io.ReadBitsIntBe(1)
	if err != nil {
		return err
	}
	this.IsDisabled = tmp41 != 0
	tmp42, err := this._io.ReadBitsIntBe(7)
	if err != nil {
		return err
	}
	this.ChType = tmp42
	return err
}

/**
 * Channel type (0..7 = left sample channels, 8..15 = right sample channels, 16..31 = AdLib synth channels)
 */

/**
 * 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) Read(io *kaitai.Stream, parent *S3m_Instrument_Sampled, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp43, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Hi = tmp43
	tmp44, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Lo = uint16(tmp44)
	return err
}
func (this *S3m_SwappedU3) Value() (v int, err error) {
	if (this._f_value) {
		return this.value, nil
	}
	this.value = int((this.Lo | (this.Hi << 16)))
	this._f_value = true
	return this.value, 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) Read(io *kaitai.Stream, parent *S3m_PatternPtr, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp45, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Size = uint16(tmp45)
	tmp46, err := this._io.ReadBytes(int((this.Size - 2)))
	if err != nil {
		return err
	}
	tmp46 = tmp46
	this._raw_Body = tmp46
	_io__raw_Body := kaitai.NewStream(bytes.NewReader(this._raw_Body))
	tmp47 := NewS3m_PatternCells()
	err = tmp47.Read(_io__raw_Body, this, this._root)
	if err != nil {
		return err
	}
	this.Body = tmp47
	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) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp48, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Paraptr = uint16(tmp48)
	return err
}
func (this *S3m_PatternPtr) Body() (v *S3m_Pattern, err error) {
	if (this._f_body) {
		return this.body, nil
	}
	_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
	}
	tmp49 := NewS3m_Pattern()
	err = tmp49.Read(this._io, this, this._root)
	if err != nil {
		return nil, err
	}
	this.body = tmp49
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_body = true
	this._f_body = true
	return this.body, nil
}
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) Read(io *kaitai.Stream, parent *S3m, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp50, err := this._io.ReadU2le()
	if err != nil {
		return err
	}
	this.Paraptr = uint16(tmp50)
	return err
}
func (this *S3m_InstrumentPtr) Body() (v *S3m_Instrument, err error) {
	if (this._f_body) {
		return this.body, nil
	}
	_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
	}
	tmp51 := NewS3m_Instrument()
	err = tmp51.Read(this._io, this, this._root)
	if err != nil {
		return nil, err
	}
	this.body = tmp51
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_body = true
	this._f_body = true
	return this.body, nil
}

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
)
type S3m_Instrument struct {
	Type S3m_Instrument_InstTypes
	Filename []byte
	Body interface{}
	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) Read(io *kaitai.Stream, parent *S3m_InstrumentPtr, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp52, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Type = S3m_Instrument_InstTypes(tmp52)
	tmp53, err := this._io.ReadBytes(int(12))
	if err != nil {
		return err
	}
	tmp53 = kaitai.BytesTerminate(tmp53, 0, false)
	this.Filename = tmp53
	switch (this.Type) {
	case S3m_Instrument_InstTypes__Sample:
		tmp54 := NewS3m_Instrument_Sampled()
		err = tmp54.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp54
	default:
		tmp55 := NewS3m_Instrument_Adlib()
		err = tmp55.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp55
	}
	tmp56, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.TuningHz = uint32(tmp56)
	tmp57, err := this._io.ReadBytes(int(12))
	if err != nil {
		return err
	}
	tmp57 = tmp57
	this.Reserved2 = tmp57
	tmp58, err := this._io.ReadBytes(int(28))
	if err != nil {
		return err
	}
	tmp58 = kaitai.BytesTerminate(tmp58, 0, false)
	this.SampleName = tmp58
	tmp59, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp59 = tmp59
	this.Magic = tmp59
	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_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) Read(io *kaitai.Stream, parent *S3m_Instrument, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp60 := NewS3m_SwappedU3()
	err = tmp60.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.ParaptrSample = tmp60
	tmp61, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.LenSample = uint32(tmp61)
	tmp62, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.LoopBegin = uint32(tmp62)
	tmp63, err := this._io.ReadU4le()
	if err != nil {
		return err
	}
	this.LoopEnd = uint32(tmp63)
	tmp64, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.DefaultVolume = tmp64
	tmp65, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Reserved1 = tmp65
	tmp66, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.IsPacked = tmp66
	tmp67, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Flags = tmp67
	return err
}
func (this *S3m_Instrument_Sampled) Sample() (v []byte, err error) {
	if (this._f_sample) {
		return this.sample, nil
	}
	_pos, err := this._io.Pos()
	if err != nil {
		return nil, err
	}
	tmp68, err := this.ParaptrSample.Value()
	if err != nil {
		return nil, err
	}
	_, err = this._io.Seek(int64((tmp68 * 16)), io.SeekStart)
	if err != nil {
		return nil, err
	}
	tmp69, err := this._io.ReadBytes(int(this.LenSample))
	if err != nil {
		return nil, err
	}
	tmp69 = tmp69
	this.sample = tmp69
	_, err = this._io.Seek(_pos, io.SeekStart)
	if err != nil {
		return nil, err
	}
	this._f_sample = true
	this._f_sample = true
	return this.sample, nil
}

/**
 * Default volume
 */

/**
 * 0 = unpacked, 1 = DP30ADPCM packing
 */
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) Read(io *kaitai.Stream, parent *S3m_Instrument, root *S3m) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp70, err := this._io.ReadBytes(int(3))
	if err != nil {
		return err
	}
	tmp70 = tmp70
	this.Reserved1 = tmp70
	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")
	}
	tmp71, err := this._io.ReadBytes(int(16))
	if err != nil {
		return err
	}
	tmp71 = tmp71
	this._unnamed1 = tmp71
	return err
}