Scream Tracker 3 module: C++98/STL 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.

Usage

Runtime library

All parsing code for C++98/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.s3m", std::ifstream::binary);
    
    #include <sstream>
    
    std::istringstream is(str);
    
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
    
  2. We need to wrap our input stream into Kaitai stream:
    #include "kaitai/kaitaistream.h"
    
    kaitai::kstream ks(&is);
    
  3. And finally, we can invoke the parsing:
    s3m_t data(&ks);
    

After that, one can get various attributes from the structure by invoking getter methods like:

data.num_orders() // => Number of orders in a song

C++98/STL source code to parse Scream Tracker 3 module

s3m.h

#ifndef S3M_H_
#define S3M_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <vector>

#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif

/**
 * 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.
 * \sa http://hackipedia.org/browse.cgi/File%20formats/Music%20tracker/S3M%2c%20ScreamTracker%203/Scream%20Tracker%203.20%20by%20Future%20Crew.txt Source
 */

class s3m_t : public kaitai::kstruct {

public:
    class channel_pan_t;
    class pattern_cell_t;
    class pattern_cells_t;
    class channel_t;
    class swapped_u3_t;
    class pattern_t;
    class pattern_ptr_t;
    class instrument_ptr_t;
    class instrument_t;

    s3m_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, s3m_t* p__root = 0);

private:
    void _read();
    void _clean_up();

public:
    ~s3m_t();

    class channel_pan_t : public kaitai::kstruct {

    public:

        channel_pan_t(kaitai::kstream* p__io, s3m_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~channel_pan_t();

    private:
        uint64_t m_reserved1;
        bool m_has_custom_pan;
        bool m_reserved2;
        uint64_t m_pan;
        s3m_t* m__root;
        s3m_t* m__parent;

    public:
        uint64_t reserved1() const { return m_reserved1; }

        /**
         * 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).
         */
        bool has_custom_pan() const { return m_has_custom_pan; }
        bool reserved2() const { return m_reserved2; }
        uint64_t pan() const { return m_pan; }
        s3m_t* _root() const { return m__root; }
        s3m_t* _parent() const { return m__parent; }
    };

    class pattern_cell_t : public kaitai::kstruct {

    public:

        pattern_cell_t(kaitai::kstream* p__io, s3m_t::pattern_cells_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~pattern_cell_t();

    private:
        bool m_has_fx;
        bool m_has_volume;
        bool m_has_note_and_instrument;
        uint64_t m_channel_num;
        uint8_t m_note;
        bool n_note;

    public:
        bool _is_null_note() { note(); return n_note; };

    private:
        uint8_t m_instrument;
        bool n_instrument;

    public:
        bool _is_null_instrument() { instrument(); return n_instrument; };

    private:
        uint8_t m_volume;
        bool n_volume;

    public:
        bool _is_null_volume() { volume(); return n_volume; };

    private:
        uint8_t m_fx_type;
        bool n_fx_type;

    public:
        bool _is_null_fx_type() { fx_type(); return n_fx_type; };

    private:
        uint8_t m_fx_value;
        bool n_fx_value;

    public:
        bool _is_null_fx_value() { fx_value(); return n_fx_value; };

    private:
        s3m_t* m__root;
        s3m_t::pattern_cells_t* m__parent;

    public:
        bool has_fx() const { return m_has_fx; }
        bool has_volume() const { return m_has_volume; }
        bool has_note_and_instrument() const { return m_has_note_and_instrument; }
        uint64_t channel_num() const { return m_channel_num; }
        uint8_t note() const { return m_note; }
        uint8_t instrument() const { return m_instrument; }
        uint8_t volume() const { return m_volume; }
        uint8_t fx_type() const { return m_fx_type; }
        uint8_t fx_value() const { return m_fx_value; }
        s3m_t* _root() const { return m__root; }
        s3m_t::pattern_cells_t* _parent() const { return m__parent; }
    };

    class pattern_cells_t : public kaitai::kstruct {

    public:

        pattern_cells_t(kaitai::kstream* p__io, s3m_t::pattern_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~pattern_cells_t();

    private:
        std::vector<pattern_cell_t*>* m_cells;
        s3m_t* m__root;
        s3m_t::pattern_t* m__parent;

    public:
        std::vector<pattern_cell_t*>* cells() const { return m_cells; }
        s3m_t* _root() const { return m__root; }
        s3m_t::pattern_t* _parent() const { return m__parent; }
    };

    class channel_t : public kaitai::kstruct {

    public:

        channel_t(kaitai::kstream* p__io, s3m_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~channel_t();

    private:
        bool m_is_disabled;
        uint64_t m_ch_type;
        s3m_t* m__root;
        s3m_t* m__parent;

    public:
        bool is_disabled() const { return m_is_disabled; }

        /**
         * Channel type (0..7 = left sample channels, 8..15 = right sample channels, 16..31 = AdLib synth channels)
         */
        uint64_t ch_type() const { return m_ch_type; }
        s3m_t* _root() const { return m__root; }
        s3m_t* _parent() const { return m__parent; }
    };

    /**
     * Custom 3-byte integer, stored in mixed endian manner.
     */

    class swapped_u3_t : public kaitai::kstruct {

    public:

        swapped_u3_t(kaitai::kstream* p__io, s3m_t::instrument_t::sampled_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~swapped_u3_t();

    private:
        bool f_value;
        int32_t m_value;

    public:
        int32_t value();

    private:
        uint8_t m_hi;
        uint16_t m_lo;
        s3m_t* m__root;
        s3m_t::instrument_t::sampled_t* m__parent;

    public:
        uint8_t hi() const { return m_hi; }
        uint16_t lo() const { return m_lo; }
        s3m_t* _root() const { return m__root; }
        s3m_t::instrument_t::sampled_t* _parent() const { return m__parent; }
    };

    class pattern_t : public kaitai::kstruct {

    public:

        pattern_t(kaitai::kstream* p__io, s3m_t::pattern_ptr_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~pattern_t();

    private:
        uint16_t m_size;
        pattern_cells_t* m_body;
        s3m_t* m__root;
        s3m_t::pattern_ptr_t* m__parent;
        std::string m__raw_body;
        kaitai::kstream* m__io__raw_body;

    public:
        uint16_t size() const { return m_size; }
        pattern_cells_t* body() const { return m_body; }
        s3m_t* _root() const { return m__root; }
        s3m_t::pattern_ptr_t* _parent() const { return m__parent; }
        std::string _raw_body() const { return m__raw_body; }
        kaitai::kstream* _io__raw_body() const { return m__io__raw_body; }
    };

    class pattern_ptr_t : public kaitai::kstruct {

    public:

        pattern_ptr_t(kaitai::kstream* p__io, s3m_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~pattern_ptr_t();

    private:
        bool f_body;
        pattern_t* m_body;

    public:
        pattern_t* body();

    private:
        uint16_t m_paraptr;
        s3m_t* m__root;
        s3m_t* m__parent;

    public:
        uint16_t paraptr() const { return m_paraptr; }
        s3m_t* _root() const { return m__root; }
        s3m_t* _parent() const { return m__parent; }
    };

    class instrument_ptr_t : public kaitai::kstruct {

    public:

        instrument_ptr_t(kaitai::kstream* p__io, s3m_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~instrument_ptr_t();

    private:
        bool f_body;
        instrument_t* m_body;

    public:
        instrument_t* body();

    private:
        uint16_t m_paraptr;
        s3m_t* m__root;
        s3m_t* m__parent;

    public:
        uint16_t paraptr() const { return m_paraptr; }
        s3m_t* _root() const { return m__root; }
        s3m_t* _parent() const { return m__parent; }
    };

    class instrument_t : public kaitai::kstruct {

    public:
        class sampled_t;
        class adlib_t;

        enum inst_types_t {
            INST_TYPES_SAMPLE = 1,
            INST_TYPES_MELODIC = 2,
            INST_TYPES_BASS_DRUM = 3,
            INST_TYPES_SNARE_DRUM = 4,
            INST_TYPES_TOM = 5,
            INST_TYPES_CYMBAL = 6,
            INST_TYPES_HIHAT = 7
        };

        instrument_t(kaitai::kstream* p__io, s3m_t::instrument_ptr_t* p__parent = 0, s3m_t* p__root = 0);

    private:
        void _read();
        void _clean_up();

    public:
        ~instrument_t();

        class sampled_t : public kaitai::kstruct {

        public:

            sampled_t(kaitai::kstream* p__io, s3m_t::instrument_t* p__parent = 0, s3m_t* p__root = 0);

        private:
            void _read();
            void _clean_up();

        public:
            ~sampled_t();

        private:
            bool f_sample;
            std::string m_sample;

        public:
            std::string sample();

        private:
            swapped_u3_t* m_paraptr_sample;
            uint32_t m_len_sample;
            uint32_t m_loop_begin;
            uint32_t m_loop_end;
            uint8_t m_default_volume;
            uint8_t m_reserved1;
            uint8_t m_is_packed;
            uint8_t m_flags;
            s3m_t* m__root;
            s3m_t::instrument_t* m__parent;

        public:
            swapped_u3_t* paraptr_sample() const { return m_paraptr_sample; }
            uint32_t len_sample() const { return m_len_sample; }
            uint32_t loop_begin() const { return m_loop_begin; }
            uint32_t loop_end() const { return m_loop_end; }

            /**
             * Default volume
             */
            uint8_t default_volume() const { return m_default_volume; }
            uint8_t reserved1() const { return m_reserved1; }

            /**
             * 0 = unpacked, 1 = DP30ADPCM packing
             */
            uint8_t is_packed() const { return m_is_packed; }
            uint8_t flags() const { return m_flags; }
            s3m_t* _root() const { return m__root; }
            s3m_t::instrument_t* _parent() const { return m__parent; }
        };

        class adlib_t : public kaitai::kstruct {

        public:

            adlib_t(kaitai::kstream* p__io, s3m_t::instrument_t* p__parent = 0, s3m_t* p__root = 0);

        private:
            void _read();
            void _clean_up();

        public:
            ~adlib_t();

        private:
            std::string m_reserved1;
            std::string m__unnamed1;
            s3m_t* m__root;
            s3m_t::instrument_t* m__parent;

        public:
            std::string reserved1() const { return m_reserved1; }
            std::string _unnamed1() const { return m__unnamed1; }
            s3m_t* _root() const { return m__root; }
            s3m_t::instrument_t* _parent() const { return m__parent; }
        };

    private:
        inst_types_t m_type;
        std::string m_filename;
        kaitai::kstruct* m_body;
        uint32_t m_tuning_hz;
        std::string m_reserved2;
        std::string m_sample_name;
        std::string m_magic;
        s3m_t* m__root;
        s3m_t::instrument_ptr_t* m__parent;

    public:
        inst_types_t type() const { return m_type; }
        std::string filename() const { return m_filename; }
        kaitai::kstruct* body() const { return m_body; }
        uint32_t tuning_hz() const { return m_tuning_hz; }
        std::string reserved2() const { return m_reserved2; }
        std::string sample_name() const { return m_sample_name; }
        std::string magic() const { return m_magic; }
        s3m_t* _root() const { return m__root; }
        s3m_t::instrument_ptr_t* _parent() const { return m__parent; }
    };

private:
    std::string m_song_name;
    std::string m_magic1;
    uint8_t m_file_type;
    std::string m_reserved1;
    uint16_t m_num_orders;
    uint16_t m_num_instruments;
    uint16_t m_num_patterns;
    uint16_t m_flags;
    uint16_t m_version;
    uint16_t m_samples_format;
    std::string m_magic2;
    uint8_t m_global_volume;
    uint8_t m_initial_speed;
    uint8_t m_initial_tempo;
    bool m_is_stereo;
    uint64_t m_master_volume;
    uint8_t m_ultra_click_removal;
    uint8_t m_has_custom_pan;
    std::string m_reserved2;
    uint16_t m_ofs_special;
    std::vector<channel_t*>* m_channels;
    std::string m_orders;
    std::vector<instrument_ptr_t*>* m_instruments;
    std::vector<pattern_ptr_t*>* m_patterns;
    std::vector<channel_pan_t*>* m_channel_pans;
    bool n_channel_pans;

public:
    bool _is_null_channel_pans() { channel_pans(); return n_channel_pans; };

private:
    s3m_t* m__root;
    kaitai::kstruct* m__parent;

public:
    std::string song_name() const { return m_song_name; }
    std::string magic1() const { return m_magic1; }
    uint8_t file_type() const { return m_file_type; }
    std::string reserved1() const { return m_reserved1; }

    /**
     * Number of orders in a song
     */
    uint16_t num_orders() const { return m_num_orders; }

    /**
     * Number of instruments in a song
     */
    uint16_t num_instruments() const { return m_num_instruments; }

    /**
     * Number of patterns in a song
     */
    uint16_t num_patterns() const { return m_num_patterns; }
    uint16_t flags() const { return m_flags; }

    /**
     * Scream Tracker version that was used to save this file
     */
    uint16_t version() const { return m_version; }

    /**
     * 1 = signed samples, 2 = unsigned samples
     */
    uint16_t samples_format() const { return m_samples_format; }
    std::string magic2() const { return m_magic2; }
    uint8_t global_volume() const { return m_global_volume; }
    uint8_t initial_speed() const { return m_initial_speed; }
    uint8_t initial_tempo() const { return m_initial_tempo; }
    bool is_stereo() const { return m_is_stereo; }
    uint64_t master_volume() const { return m_master_volume; }
    uint8_t ultra_click_removal() const { return m_ultra_click_removal; }
    uint8_t has_custom_pan() const { return m_has_custom_pan; }
    std::string reserved2() const { return m_reserved2; }

    /**
     * Offset of special data, not used by Scream Tracker directly.
     */
    uint16_t ofs_special() const { return m_ofs_special; }
    std::vector<channel_t*>* channels() const { return m_channels; }
    std::string orders() const { return m_orders; }
    std::vector<instrument_ptr_t*>* instruments() const { return m_instruments; }
    std::vector<pattern_ptr_t*>* patterns() const { return m_patterns; }
    std::vector<channel_pan_t*>* channel_pans() const { return m_channel_pans; }
    s3m_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
};

#endif  // S3M_H_

s3m.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "s3m.h"
#include "kaitai/exceptions.h"

s3m_t::s3m_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_channels = 0;
    m_instruments = 0;
    m_patterns = 0;
    m_channel_pans = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::_read() {
    m_song_name = kaitai::kstream::bytes_terminate(m__io->read_bytes(28), 0, false);
    m_magic1 = m__io->read_bytes(1);
    if (!(magic1() == std::string("\x1A", 1))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x1A", 1), magic1(), _io(), std::string("/seq/1"));
    }
    m_file_type = m__io->read_u1();
    m_reserved1 = m__io->read_bytes(2);
    m_num_orders = m__io->read_u2le();
    m_num_instruments = m__io->read_u2le();
    m_num_patterns = m__io->read_u2le();
    m_flags = m__io->read_u2le();
    m_version = m__io->read_u2le();
    m_samples_format = m__io->read_u2le();
    m_magic2 = m__io->read_bytes(4);
    if (!(magic2() == std::string("\x53\x43\x52\x4D", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x53\x43\x52\x4D", 4), magic2(), _io(), std::string("/seq/10"));
    }
    m_global_volume = m__io->read_u1();
    m_initial_speed = m__io->read_u1();
    m_initial_tempo = m__io->read_u1();
    m_is_stereo = m__io->read_bits_int_be(1);
    m_master_volume = m__io->read_bits_int_be(7);
    m__io->align_to_byte();
    m_ultra_click_removal = m__io->read_u1();
    m_has_custom_pan = m__io->read_u1();
    m_reserved2 = m__io->read_bytes(8);
    m_ofs_special = m__io->read_u2le();
    int l_channels = 32;
    m_channels = new std::vector<channel_t*>();
    m_channels->reserve(l_channels);
    for (int i = 0; i < l_channels; i++) {
        m_channels->push_back(new channel_t(m__io, this, m__root));
    }
    m_orders = m__io->read_bytes(num_orders());
    int l_instruments = num_instruments();
    m_instruments = new std::vector<instrument_ptr_t*>();
    m_instruments->reserve(l_instruments);
    for (int i = 0; i < l_instruments; i++) {
        m_instruments->push_back(new instrument_ptr_t(m__io, this, m__root));
    }
    int l_patterns = num_patterns();
    m_patterns = new std::vector<pattern_ptr_t*>();
    m_patterns->reserve(l_patterns);
    for (int i = 0; i < l_patterns; i++) {
        m_patterns->push_back(new pattern_ptr_t(m__io, this, m__root));
    }
    n_channel_pans = true;
    if (has_custom_pan() == 252) {
        n_channel_pans = false;
        int l_channel_pans = 32;
        m_channel_pans = new std::vector<channel_pan_t*>();
        m_channel_pans->reserve(l_channel_pans);
        for (int i = 0; i < l_channel_pans; i++) {
            m_channel_pans->push_back(new channel_pan_t(m__io, this, m__root));
        }
    }
}

s3m_t::~s3m_t() {
    _clean_up();
}

void s3m_t::_clean_up() {
    if (m_channels) {
        for (std::vector<channel_t*>::iterator it = m_channels->begin(); it != m_channels->end(); ++it) {
            delete *it;
        }
        delete m_channels; m_channels = 0;
    }
    if (m_instruments) {
        for (std::vector<instrument_ptr_t*>::iterator it = m_instruments->begin(); it != m_instruments->end(); ++it) {
            delete *it;
        }
        delete m_instruments; m_instruments = 0;
    }
    if (m_patterns) {
        for (std::vector<pattern_ptr_t*>::iterator it = m_patterns->begin(); it != m_patterns->end(); ++it) {
            delete *it;
        }
        delete m_patterns; m_patterns = 0;
    }
    if (!n_channel_pans) {
        if (m_channel_pans) {
            for (std::vector<channel_pan_t*>::iterator it = m_channel_pans->begin(); it != m_channel_pans->end(); ++it) {
                delete *it;
            }
            delete m_channel_pans; m_channel_pans = 0;
        }
    }
}

s3m_t::channel_pan_t::channel_pan_t(kaitai::kstream* p__io, s3m_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::channel_pan_t::_read() {
    m_reserved1 = m__io->read_bits_int_be(2);
    m_has_custom_pan = m__io->read_bits_int_be(1);
    m_reserved2 = m__io->read_bits_int_be(1);
    m_pan = m__io->read_bits_int_be(4);
}

s3m_t::channel_pan_t::~channel_pan_t() {
    _clean_up();
}

void s3m_t::channel_pan_t::_clean_up() {
}

s3m_t::pattern_cell_t::pattern_cell_t(kaitai::kstream* p__io, s3m_t::pattern_cells_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::pattern_cell_t::_read() {
    m_has_fx = m__io->read_bits_int_be(1);
    m_has_volume = m__io->read_bits_int_be(1);
    m_has_note_and_instrument = m__io->read_bits_int_be(1);
    m_channel_num = m__io->read_bits_int_be(5);
    m__io->align_to_byte();
    n_note = true;
    if (has_note_and_instrument()) {
        n_note = false;
        m_note = m__io->read_u1();
    }
    n_instrument = true;
    if (has_note_and_instrument()) {
        n_instrument = false;
        m_instrument = m__io->read_u1();
    }
    n_volume = true;
    if (has_volume()) {
        n_volume = false;
        m_volume = m__io->read_u1();
    }
    n_fx_type = true;
    if (has_fx()) {
        n_fx_type = false;
        m_fx_type = m__io->read_u1();
    }
    n_fx_value = true;
    if (has_fx()) {
        n_fx_value = false;
        m_fx_value = m__io->read_u1();
    }
}

s3m_t::pattern_cell_t::~pattern_cell_t() {
    _clean_up();
}

void s3m_t::pattern_cell_t::_clean_up() {
    if (!n_note) {
    }
    if (!n_instrument) {
    }
    if (!n_volume) {
    }
    if (!n_fx_type) {
    }
    if (!n_fx_value) {
    }
}

s3m_t::pattern_cells_t::pattern_cells_t(kaitai::kstream* p__io, s3m_t::pattern_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_cells = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::pattern_cells_t::_read() {
    m_cells = new std::vector<pattern_cell_t*>();
    {
        int i = 0;
        while (!m__io->is_eof()) {
            m_cells->push_back(new pattern_cell_t(m__io, this, m__root));
            i++;
        }
    }
}

s3m_t::pattern_cells_t::~pattern_cells_t() {
    _clean_up();
}

void s3m_t::pattern_cells_t::_clean_up() {
    if (m_cells) {
        for (std::vector<pattern_cell_t*>::iterator it = m_cells->begin(); it != m_cells->end(); ++it) {
            delete *it;
        }
        delete m_cells; m_cells = 0;
    }
}

s3m_t::channel_t::channel_t(kaitai::kstream* p__io, s3m_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::channel_t::_read() {
    m_is_disabled = m__io->read_bits_int_be(1);
    m_ch_type = m__io->read_bits_int_be(7);
}

s3m_t::channel_t::~channel_t() {
    _clean_up();
}

void s3m_t::channel_t::_clean_up() {
}

s3m_t::swapped_u3_t::swapped_u3_t(kaitai::kstream* p__io, s3m_t::instrument_t::sampled_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_value = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::swapped_u3_t::_read() {
    m_hi = m__io->read_u1();
    m_lo = m__io->read_u2le();
}

s3m_t::swapped_u3_t::~swapped_u3_t() {
    _clean_up();
}

void s3m_t::swapped_u3_t::_clean_up() {
}

int32_t s3m_t::swapped_u3_t::value() {
    if (f_value)
        return m_value;
    m_value = (lo() | (hi() << 16));
    f_value = true;
    return m_value;
}

s3m_t::pattern_t::pattern_t(kaitai::kstream* p__io, s3m_t::pattern_ptr_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_body = 0;
    m__io__raw_body = 0;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::pattern_t::_read() {
    m_size = m__io->read_u2le();
    m__raw_body = m__io->read_bytes((size() - 2));
    m__io__raw_body = new kaitai::kstream(m__raw_body);
    m_body = new pattern_cells_t(m__io__raw_body, this, m__root);
}

s3m_t::pattern_t::~pattern_t() {
    _clean_up();
}

void s3m_t::pattern_t::_clean_up() {
    if (m__io__raw_body) {
        delete m__io__raw_body; m__io__raw_body = 0;
    }
    if (m_body) {
        delete m_body; m_body = 0;
    }
}

s3m_t::pattern_ptr_t::pattern_ptr_t(kaitai::kstream* p__io, s3m_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_body = 0;
    f_body = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::pattern_ptr_t::_read() {
    m_paraptr = m__io->read_u2le();
}

s3m_t::pattern_ptr_t::~pattern_ptr_t() {
    _clean_up();
}

void s3m_t::pattern_ptr_t::_clean_up() {
    if (f_body) {
        if (m_body) {
            delete m_body; m_body = 0;
        }
    }
}

s3m_t::pattern_t* s3m_t::pattern_ptr_t::body() {
    if (f_body)
        return m_body;
    std::streampos _pos = m__io->pos();
    m__io->seek((paraptr() * 16));
    m_body = new pattern_t(m__io, this, m__root);
    m__io->seek(_pos);
    f_body = true;
    return m_body;
}

s3m_t::instrument_ptr_t::instrument_ptr_t(kaitai::kstream* p__io, s3m_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_body = 0;
    f_body = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::instrument_ptr_t::_read() {
    m_paraptr = m__io->read_u2le();
}

s3m_t::instrument_ptr_t::~instrument_ptr_t() {
    _clean_up();
}

void s3m_t::instrument_ptr_t::_clean_up() {
    if (f_body) {
        if (m_body) {
            delete m_body; m_body = 0;
        }
    }
}

s3m_t::instrument_t* s3m_t::instrument_ptr_t::body() {
    if (f_body)
        return m_body;
    std::streampos _pos = m__io->pos();
    m__io->seek((paraptr() * 16));
    m_body = new instrument_t(m__io, this, m__root);
    m__io->seek(_pos);
    f_body = true;
    return m_body;
}

s3m_t::instrument_t::instrument_t(kaitai::kstream* p__io, s3m_t::instrument_ptr_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::instrument_t::_read() {
    m_type = static_cast<s3m_t::instrument_t::inst_types_t>(m__io->read_u1());
    m_filename = kaitai::kstream::bytes_terminate(m__io->read_bytes(12), 0, false);
    switch (type()) {
    case s3m_t::instrument_t::INST_TYPES_SAMPLE: {
        m_body = new sampled_t(m__io, this, m__root);
        break;
    }
    default: {
        m_body = new adlib_t(m__io, this, m__root);
        break;
    }
    }
    m_tuning_hz = m__io->read_u4le();
    m_reserved2 = m__io->read_bytes(12);
    m_sample_name = kaitai::kstream::bytes_terminate(m__io->read_bytes(28), 0, false);
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\x53\x43\x52\x53", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x53\x43\x52\x53", 4), magic(), _io(), std::string("/types/instrument/seq/6"));
    }
}

s3m_t::instrument_t::~instrument_t() {
    _clean_up();
}

void s3m_t::instrument_t::_clean_up() {
    if (m_body) {
        delete m_body; m_body = 0;
    }
}

s3m_t::instrument_t::sampled_t::sampled_t(kaitai::kstream* p__io, s3m_t::instrument_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_paraptr_sample = 0;
    f_sample = false;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::instrument_t::sampled_t::_read() {
    m_paraptr_sample = new swapped_u3_t(m__io, this, m__root);
    m_len_sample = m__io->read_u4le();
    m_loop_begin = m__io->read_u4le();
    m_loop_end = m__io->read_u4le();
    m_default_volume = m__io->read_u1();
    m_reserved1 = m__io->read_u1();
    m_is_packed = m__io->read_u1();
    m_flags = m__io->read_u1();
}

s3m_t::instrument_t::sampled_t::~sampled_t() {
    _clean_up();
}

void s3m_t::instrument_t::sampled_t::_clean_up() {
    if (m_paraptr_sample) {
        delete m_paraptr_sample; m_paraptr_sample = 0;
    }
    if (f_sample) {
    }
}

std::string s3m_t::instrument_t::sampled_t::sample() {
    if (f_sample)
        return m_sample;
    std::streampos _pos = m__io->pos();
    m__io->seek((paraptr_sample()->value() * 16));
    m_sample = m__io->read_bytes(len_sample());
    m__io->seek(_pos);
    f_sample = true;
    return m_sample;
}

s3m_t::instrument_t::adlib_t::adlib_t(kaitai::kstream* p__io, s3m_t::instrument_t* p__parent, s3m_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;

    try {
        _read();
    } catch(...) {
        _clean_up();
        throw;
    }
}

void s3m_t::instrument_t::adlib_t::_read() {
    m_reserved1 = m__io->read_bytes(3);
    if (!(reserved1() == std::string("\x00\x00\x00", 3))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x00\x00\x00", 3), reserved1(), _io(), std::string("/types/instrument/types/adlib/seq/0"));
    }
    m__unnamed1 = m__io->read_bytes(16);
}

s3m_t::instrument_t::adlib_t::~adlib_t() {
    _clean_up();
}

void s3m_t::instrument_t::adlib_t::_clean_up() {
}