Standard MIDI file: Go parsing library

Standard MIDI file, typically known just as "MID", is a standard way to serialize series of MIDI events, which is a protocol used in many music synthesizers to transfer music data: notes being played, effects being applied, etc.

Internally, file consists of a header and series of tracks, every track listing MIDI events with certain header designating time these events are happening.

NOTE: Rarely, MIDI files employ certain stateful compression scheme to avoid storing certain elements of further elements, instead reusing them from events which happened earlier in the stream. Kaitai Struct (as of v0.9) is currently unable to parse these, but files employing this mechanism are relatively rare.

File extension

["mid", "midi", "smf"]

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of Standard MIDI file 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 Standard MIDI file

standard_midi_file.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"
)


/**
 * Standard MIDI file, typically known just as "MID", is a standard way
 * to serialize series of MIDI events, which is a protocol used in many
 * music synthesizers to transfer music data: notes being played,
 * effects being applied, etc.
 * 
 * Internally, file consists of a header and series of tracks, every
 * track listing MIDI events with certain header designating time these
 * events are happening.
 * 
 * NOTE: Rarely, MIDI files employ certain stateful compression scheme
 * to avoid storing certain elements of further elements, instead
 * reusing them from events which happened earlier in the
 * stream. Kaitai Struct (as of v0.9) is currently unable to parse
 * these, but files employing this mechanism are relatively rare.
 */
type StandardMidiFile struct {
	Hdr *StandardMidiFile_Header
	Tracks []*StandardMidiFile_Track
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent kaitai.Struct
}
func NewStandardMidiFile() *StandardMidiFile {
	return &StandardMidiFile{
	}
}

func (this StandardMidiFile) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile) Read(io *kaitai.Stream, parent kaitai.Struct, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp1 := NewStandardMidiFile_Header()
	err = tmp1.Read(this._io, this, this._root)
	if err != nil {
		return err
	}
	this.Hdr = tmp1
	for i := 0; i < int(this.Hdr.NumTracks); i++ {
		_ = i
		tmp2 := NewStandardMidiFile_Track()
		err = tmp2.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Tracks = append(this.Tracks, tmp2)
	}
	return err
}
type StandardMidiFile_ChannelPressureEvent struct {
	Pressure uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_ChannelPressureEvent() *StandardMidiFile_ChannelPressureEvent {
	return &StandardMidiFile_ChannelPressureEvent{
	}
}

func (this StandardMidiFile_ChannelPressureEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_ChannelPressureEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp3, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Pressure = tmp3
	return err
}
type StandardMidiFile_ControllerEvent struct {
	Controller uint8
	Value uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_ControllerEvent() *StandardMidiFile_ControllerEvent {
	return &StandardMidiFile_ControllerEvent{
	}
}

func (this StandardMidiFile_ControllerEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_ControllerEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp4, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Controller = tmp4
	tmp5, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Value = tmp5
	return err
}
type StandardMidiFile_Header struct {
	Magic []byte
	LenHeader uint32
	Format uint16
	NumTracks uint16
	Division int16
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile
}
func NewStandardMidiFile_Header() *StandardMidiFile_Header {
	return &StandardMidiFile_Header{
	}
}

func (this StandardMidiFile_Header) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_Header) Read(io *kaitai.Stream, parent *StandardMidiFile, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp6, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp6 = tmp6
	this.Magic = tmp6
	if !(bytes.Equal(this.Magic, []uint8{77, 84, 104, 100})) {
		return kaitai.NewValidationNotEqualError([]uint8{77, 84, 104, 100}, this.Magic, this._io, "/types/header/seq/0")
	}
	tmp7, err := this._io.ReadU4be()
	if err != nil {
		return err
	}
	this.LenHeader = uint32(tmp7)
	tmp8, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.Format = uint16(tmp8)
	tmp9, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.NumTracks = uint16(tmp9)
	tmp10, err := this._io.ReadS2be()
	if err != nil {
		return err
	}
	this.Division = int16(tmp10)
	return err
}

type StandardMidiFile_MetaEventBody_MetaTypeEnum int
const (
	StandardMidiFile_MetaEventBody_MetaTypeEnum__SequenceNumber StandardMidiFile_MetaEventBody_MetaTypeEnum = 0
	StandardMidiFile_MetaEventBody_MetaTypeEnum__TextEvent StandardMidiFile_MetaEventBody_MetaTypeEnum = 1
	StandardMidiFile_MetaEventBody_MetaTypeEnum__Copyright StandardMidiFile_MetaEventBody_MetaTypeEnum = 2
	StandardMidiFile_MetaEventBody_MetaTypeEnum__SequenceTrackName StandardMidiFile_MetaEventBody_MetaTypeEnum = 3
	StandardMidiFile_MetaEventBody_MetaTypeEnum__InstrumentName StandardMidiFile_MetaEventBody_MetaTypeEnum = 4
	StandardMidiFile_MetaEventBody_MetaTypeEnum__LyricText StandardMidiFile_MetaEventBody_MetaTypeEnum = 5
	StandardMidiFile_MetaEventBody_MetaTypeEnum__MarkerText StandardMidiFile_MetaEventBody_MetaTypeEnum = 6
	StandardMidiFile_MetaEventBody_MetaTypeEnum__CuePoint StandardMidiFile_MetaEventBody_MetaTypeEnum = 7
	StandardMidiFile_MetaEventBody_MetaTypeEnum__MidiChannelPrefixAssignment StandardMidiFile_MetaEventBody_MetaTypeEnum = 32
	StandardMidiFile_MetaEventBody_MetaTypeEnum__EndOfTrack StandardMidiFile_MetaEventBody_MetaTypeEnum = 47
	StandardMidiFile_MetaEventBody_MetaTypeEnum__Tempo StandardMidiFile_MetaEventBody_MetaTypeEnum = 81
	StandardMidiFile_MetaEventBody_MetaTypeEnum__SmpteOffset StandardMidiFile_MetaEventBody_MetaTypeEnum = 84
	StandardMidiFile_MetaEventBody_MetaTypeEnum__TimeSignature StandardMidiFile_MetaEventBody_MetaTypeEnum = 88
	StandardMidiFile_MetaEventBody_MetaTypeEnum__KeySignature StandardMidiFile_MetaEventBody_MetaTypeEnum = 89
	StandardMidiFile_MetaEventBody_MetaTypeEnum__SequencerSpecificEvent StandardMidiFile_MetaEventBody_MetaTypeEnum = 127
)
var values_StandardMidiFile_MetaEventBody_MetaTypeEnum = map[StandardMidiFile_MetaEventBody_MetaTypeEnum]struct{}{0: {}, 1: {}, 2: {}, 3: {}, 4: {}, 5: {}, 6: {}, 7: {}, 32: {}, 47: {}, 81: {}, 84: {}, 88: {}, 89: {}, 127: {}}
func (v StandardMidiFile_MetaEventBody_MetaTypeEnum) isDefined() bool {
	_, ok := values_StandardMidiFile_MetaEventBody_MetaTypeEnum[v]
	return ok
}
type StandardMidiFile_MetaEventBody struct {
	MetaType StandardMidiFile_MetaEventBody_MetaTypeEnum
	Len *VlqBase128Be
	Body []byte
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_MetaEventBody() *StandardMidiFile_MetaEventBody {
	return &StandardMidiFile_MetaEventBody{
	}
}

func (this StandardMidiFile_MetaEventBody) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_MetaEventBody) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp11, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.MetaType = StandardMidiFile_MetaEventBody_MetaTypeEnum(tmp11)
	tmp12 := NewVlqBase128Be()
	err = tmp12.Read(this._io, nil, nil)
	if err != nil {
		return err
	}
	this.Len = tmp12
	tmp13, err := this.Len.Value()
	if err != nil {
		return err
	}
	tmp14, err := this._io.ReadBytes(int(tmp13))
	if err != nil {
		return err
	}
	tmp14 = tmp14
	this.Body = tmp14
	return err
}
type StandardMidiFile_NoteOffEvent struct {
	Note uint8
	Velocity uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_NoteOffEvent() *StandardMidiFile_NoteOffEvent {
	return &StandardMidiFile_NoteOffEvent{
	}
}

func (this StandardMidiFile_NoteOffEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_NoteOffEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp15, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Note = tmp15
	tmp16, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Velocity = tmp16
	return err
}
type StandardMidiFile_NoteOnEvent struct {
	Note uint8
	Velocity uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_NoteOnEvent() *StandardMidiFile_NoteOnEvent {
	return &StandardMidiFile_NoteOnEvent{
	}
}

func (this StandardMidiFile_NoteOnEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_NoteOnEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp17, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Note = tmp17
	tmp18, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Velocity = tmp18
	return err
}
type StandardMidiFile_PitchBendEvent struct {
	B1 uint8
	B2 uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
	_f_adjBendValue bool
	adjBendValue int
	_f_bendValue bool
	bendValue int
}
func NewStandardMidiFile_PitchBendEvent() *StandardMidiFile_PitchBendEvent {
	return &StandardMidiFile_PitchBendEvent{
	}
}

func (this StandardMidiFile_PitchBendEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_PitchBendEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp19, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.B1 = tmp19
	tmp20, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.B2 = tmp20
	return err
}
func (this *StandardMidiFile_PitchBendEvent) AdjBendValue() (v int, err error) {
	if (this._f_adjBendValue) {
		return this.adjBendValue, nil
	}
	this._f_adjBendValue = true
	tmp21, err := this.BendValue()
	if err != nil {
		return 0, err
	}
	this.adjBendValue = int(tmp21 - 16384)
	return this.adjBendValue, nil
}
func (this *StandardMidiFile_PitchBendEvent) BendValue() (v int, err error) {
	if (this._f_bendValue) {
		return this.bendValue, nil
	}
	this._f_bendValue = true
	this.bendValue = int((this.B2 << 7 + this.B1) - 16384)
	return this.bendValue, nil
}
type StandardMidiFile_PolyphonicPressureEvent struct {
	Note uint8
	Pressure uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_PolyphonicPressureEvent() *StandardMidiFile_PolyphonicPressureEvent {
	return &StandardMidiFile_PolyphonicPressureEvent{
	}
}

func (this StandardMidiFile_PolyphonicPressureEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_PolyphonicPressureEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp22, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Note = tmp22
	tmp23, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Pressure = tmp23
	return err
}
type StandardMidiFile_ProgramChangeEvent struct {
	Program uint8
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_ProgramChangeEvent() *StandardMidiFile_ProgramChangeEvent {
	return &StandardMidiFile_ProgramChangeEvent{
	}
}

func (this StandardMidiFile_ProgramChangeEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_ProgramChangeEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp24, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Program = tmp24
	return err
}
type StandardMidiFile_SysexEventBody struct {
	Len *VlqBase128Be
	Data []byte
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvent
}
func NewStandardMidiFile_SysexEventBody() *StandardMidiFile_SysexEventBody {
	return &StandardMidiFile_SysexEventBody{
	}
}

func (this StandardMidiFile_SysexEventBody) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_SysexEventBody) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvent, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp25 := NewVlqBase128Be()
	err = tmp25.Read(this._io, nil, nil)
	if err != nil {
		return err
	}
	this.Len = tmp25
	tmp26, err := this.Len.Value()
	if err != nil {
		return err
	}
	tmp27, err := this._io.ReadBytes(int(tmp26))
	if err != nil {
		return err
	}
	tmp27 = tmp27
	this.Data = tmp27
	return err
}
type StandardMidiFile_Track struct {
	Magic []byte
	LenEvents uint32
	Events *StandardMidiFile_TrackEvents
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile
	_raw_Events []byte
}
func NewStandardMidiFile_Track() *StandardMidiFile_Track {
	return &StandardMidiFile_Track{
	}
}

func (this StandardMidiFile_Track) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_Track) Read(io *kaitai.Stream, parent *StandardMidiFile, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp28, err := this._io.ReadBytes(int(4))
	if err != nil {
		return err
	}
	tmp28 = tmp28
	this.Magic = tmp28
	if !(bytes.Equal(this.Magic, []uint8{77, 84, 114, 107})) {
		return kaitai.NewValidationNotEqualError([]uint8{77, 84, 114, 107}, this.Magic, this._io, "/types/track/seq/0")
	}
	tmp29, err := this._io.ReadU4be()
	if err != nil {
		return err
	}
	this.LenEvents = uint32(tmp29)
	tmp30, err := this._io.ReadBytes(int(this.LenEvents))
	if err != nil {
		return err
	}
	tmp30 = tmp30
	this._raw_Events = tmp30
	_io__raw_Events := kaitai.NewStream(bytes.NewReader(this._raw_Events))
	tmp31 := NewStandardMidiFile_TrackEvents()
	err = tmp31.Read(_io__raw_Events, this, this._root)
	if err != nil {
		return err
	}
	this.Events = tmp31
	return err
}
type StandardMidiFile_TrackEvent struct {
	VTime *VlqBase128Be
	EventHeader uint8
	MetaEventBody *StandardMidiFile_MetaEventBody
	SysexBody *StandardMidiFile_SysexEventBody
	EventBody kaitai.Struct
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_TrackEvents
	_f_channel bool
	channel int
	_f_eventType bool
	eventType int
}
func NewStandardMidiFile_TrackEvent() *StandardMidiFile_TrackEvent {
	return &StandardMidiFile_TrackEvent{
	}
}

func (this StandardMidiFile_TrackEvent) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_TrackEvent) Read(io *kaitai.Stream, parent *StandardMidiFile_TrackEvents, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp32 := NewVlqBase128Be()
	err = tmp32.Read(this._io, nil, nil)
	if err != nil {
		return err
	}
	this.VTime = tmp32
	tmp33, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.EventHeader = tmp33
	if (this.EventHeader == 255) {
		tmp34 := NewStandardMidiFile_MetaEventBody()
		err = tmp34.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.MetaEventBody = tmp34
	}
	if (this.EventHeader == 240) {
		tmp35 := NewStandardMidiFile_SysexEventBody()
		err = tmp35.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.SysexBody = tmp35
	}
	tmp36, err := this.EventType()
	if err != nil {
		return err
	}
	switch (tmp36) {
	case 128:
		tmp37 := NewStandardMidiFile_NoteOffEvent()
		err = tmp37.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp37
	case 144:
		tmp38 := NewStandardMidiFile_NoteOnEvent()
		err = tmp38.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp38
	case 160:
		tmp39 := NewStandardMidiFile_PolyphonicPressureEvent()
		err = tmp39.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp39
	case 176:
		tmp40 := NewStandardMidiFile_ControllerEvent()
		err = tmp40.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp40
	case 192:
		tmp41 := NewStandardMidiFile_ProgramChangeEvent()
		err = tmp41.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp41
	case 208:
		tmp42 := NewStandardMidiFile_ChannelPressureEvent()
		err = tmp42.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp42
	case 224:
		tmp43 := NewStandardMidiFile_PitchBendEvent()
		err = tmp43.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.EventBody = tmp43
	}
	return err
}
func (this *StandardMidiFile_TrackEvent) Channel() (v int, err error) {
	if (this._f_channel) {
		return this.channel, nil
	}
	this._f_channel = true
	tmp44, err := this.EventType()
	if err != nil {
		return 0, err
	}
	if (tmp44 != 240) {
		this.channel = int(this.EventHeader & 15)
	}
	return this.channel, nil
}
func (this *StandardMidiFile_TrackEvent) EventType() (v int, err error) {
	if (this._f_eventType) {
		return this.eventType, nil
	}
	this._f_eventType = true
	this.eventType = int(this.EventHeader & 240)
	return this.eventType, nil
}
type StandardMidiFile_TrackEvents struct {
	Event []*StandardMidiFile_TrackEvent
	_io *kaitai.Stream
	_root *StandardMidiFile
	_parent *StandardMidiFile_Track
}
func NewStandardMidiFile_TrackEvents() *StandardMidiFile_TrackEvents {
	return &StandardMidiFile_TrackEvents{
	}
}

func (this StandardMidiFile_TrackEvents) IO_() *kaitai.Stream {
	return this._io
}

func (this *StandardMidiFile_TrackEvents) Read(io *kaitai.Stream, parent *StandardMidiFile_Track, root *StandardMidiFile) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 0;; i++ {
		tmp45, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp45 {
			break
		}
		tmp46 := NewStandardMidiFile_TrackEvent()
		err = tmp46.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Event = append(this.Event, tmp46)
	}
	return err
}