midi

package module
v0.0.0-...-83511ac Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 18, 2018 License: Apache-2.0 Imports: 11 Imported by: 0

README

midi

The MIDI package is a high level MIDI library to consume and generate MIDI files.

GoDoc Go Report Card Coverage Status Build Status

Alternative

Depending on your needs, you might want to check out the excellent gomidi library by metakeule. His library is lower level but more complete and offers support for live MIDI messaging which this library isn't trying to do.

Documentation

Overview

Package midi is a midi format parser that parses the content of a midi stream and asyncronously send the data to a provided channel or collect the data into an accessible struct,

Index

Constants

View Source
const (
	MetricalTF timeFormat = iota + 1
	TimeCodeTF
)
View Source
const (
	SingleTrack uint16 = iota
	Syncronous
	Asyncronous
)
View Source
const (
	// Dur8th is the duration of a 1/8th note in beats
	Dur8th = 1.0 / 2.0
	// Dur8thT is the duration of a 1/8th triplet note in beats
	Dur8thT = 1.0 / 3.0
	// Dur16th is the duration of a 1/16th note in beats
	Dur16th = Dur8th / 2
	// Dur16thT is the duration of a 1/16th note in beats
	Dur16thT = Dur8thT / 2
)

Variables

View Source
var (

	// ErrFmtNotSupported is a generic error reporting an unknown format.
	ErrFmtNotSupported = errors.New("format not supported")
	// ErrUnexpectedData is a generic error reporting that the parser encountered unexpected data.
	ErrUnexpectedData = errors.New("unexpected data content")
)
View Source
var CCNames = map[uint8]string{}/* 128 elements not displayed */

CCNames is a map of Control Changes

View Source
var CCVals = map[string]int{
	"Bank Select":                             0,
	"Modulation Wheel or Lever":               1,
	"Breath Controller":                       2,
	"Undefined":                               3,
	"Foot Controller":                         4,
	"Portamento Time":                         5,
	"Data Entry MSB":                          6,
	"Channel Volume":                          7,
	"Balance":                                 8,
	"Pan":                                     10,
	"Expression Controller":                   11,
	"Effect Control 1":                        12,
	"Effect Control 2":                        13,
	"General Purpose Controller 1":            16,
	"General Purpose Controller 2":            17,
	"General Purpose Controller 3":            18,
	"General Purpose Controller 4":            19,
	"Control 0 Bank Select":                   32,
	"Control 1 Modulation Wheel or Lever":     33,
	"Control 2 Breath Controller":             34,
	"Control 4 Foot Controller":               36,
	"Control 5 Portamento Time":               37,
	"Control 6 Data Entry":                    38,
	"Control 7 Channel Volume":                39,
	"Control 8 Balance":                       40,
	"Control 10 Pan":                          42,
	"Control 11 Expression Controller":        43,
	"Control 12 Effect control 1":             44,
	"Control 13 Effect control 2":             45,
	"Control 16 General Purpose Controller 1": 48,
	"Control 17 General Purpose Controller 2": 49,
	"Control 18 General Purpose Controller 3": 50,
	"Control 19 General Purpose Controller 4": 51,
	"Sustain":                                 64,
	"Portamento":                              65,
	"Sostenuto":                               66,
	"Soft Pedal":                              66,
	"Legato Footswitch":                       68,
	"Hold 2":                                  69,
	"Sound Variation":                         70,
	"Timbre/Harmonic Intens.":                 71,
	"Release Time":                            72,
	"Attack Time":                             73,
	"Brightness":                              74,
	"Decay Time":                              75,
	"Vibrato Rate":                            76,
	"Vibrato Depth":                           77,
	"Vibrato Delay":                           78,
	"General Purpose Controller 5":            80,
	"General Purpose Controller 6":            81,
	"General Purpose Controller 7":            82,
	"General Purpose Controller 8":            83,
	"Portamento Control":                      84,
	"High Resolution Velocity Prefix":         88,
	"Effects 1 Depth":                         91,
	"Effects 2 Depth":                         92,
	"Effects 3 Depth":                         93,
	"Effects 4 Depth":                         94,
	"Effects 5 Depth":                         95,
	"Data Increment":                          96,
	"Data Decrement":                          97,
	"All Sound Off":                           120,
	"Reset All Controllers":                   121,
	"Local Control":                           122,
	"All Notes Off":                           123,
	"Omni Mode Off":                           124,
	"Omni Mode On":                            125,
	"Mono Mode On":                            126,
	"Poly Mode On":                            127,
}
View Source
var EventByteMap = map[string]byte{
	"NoteOff":           0x8,
	"NoteOn":            0x9,
	"AfterTouch":        0xA,
	"ControlChange":     0xB,
	"ProgramChange":     0xC,
	"ChannelAfterTouch": 0xD,
	"PitchWheelChange":  0xE,
	"Meta":              0xF,
}

EventByteMap takes an event name and returns its equivalent MIDI byte

View Source
var EventMap = map[byte]string{
	0x8: "NoteOff",
	0x9: "NoteOn",
	0xA: "AfterTouch",
	0xB: "ControlChange",
	0xC: "ProgramChange",
	0xD: "ChannelAfterTouch",
	0xE: "PitchWheelChange",
	0xF: "Meta",
}

EventMap takes a event byte and returns its matching event name. http://www.midi.org/techspecs/midimessages.php

View Source
var MetaByteMap = map[string]byte{
	"Sequence number":        0x0,
	"Text event":             0x01,
	"Copyright":              0x02,
	"Sequence/Track name":    0x03,
	"Instrument name":        0x04,
	"Lyric":                  0x05,
	"Marker":                 0x06,
	"Cue Point":              0x07,
	"MIDI Channel Prefix":    0x20,
	"End of Track":           0x2f,
	"Tempo":                  0x51,
	"Time Signature":         0x58,
	"Key Signature":          0x59,
	"Sequencer specific":     0x7F,
	"Timing Clock":           0x8F,
	"Start current sequence": 0xFA,
	"Continue stopped sequence where left off": 0xFB,
	"Stop sequence": 0xFC,
}

MetaByteMap maps metadata command names to their binary cmd code

View Source
var MetaCmdMap = map[byte]string{
	0x0:  "Sequence number",
	0x01: "Text event",
	0x02: "Copyright",
	0x03: "Sequence/Track name",
	0x04: "Instrument name",
	0x05: "Lyric",
	0x06: "Marker",
	0x07: "Cue Point",
	0x20: "MIDI Channel Prefix",
	0x2f: "End of Track",
	0x51: "Tempo",
	0x58: "Time Signature",
	0x59: "Key Signature",
	0x7F: "Sequencer specific",
	0x8F: "Timing Clock",
	0xFA: "Start current sequence",
	0xFB: "Continue stopped sequence where left off",
	0xFC: "Stop sequence",
}

MetaCmdMap maps metadata binary command to their names

View Source
var (
	Notes = []string{"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}
)
View Source
var NotesToInt = map[string]int{
	"C":  0,
	"C#": 1,
	"DB": 1,
	"D":  2,
	"D#": 3,
	"EB": 3,
	"E":  4,
	"F":  5,
	"F#": 6,
	"GB": 6,
	"G":  7,
	"G#": 8,
	"AB": 8,
	"A":  9,
	"A#": 10,
	"BB": 10,
	"B":  11,
}

NotesToInt is a map of note name to zero indexed position.

Functions

func DecodeVarint

func DecodeVarint(buf []byte) (x uint32, n int)

DecodeVarint reads a varint-encoded integer from the slice. It returns the integer and the number of bytes consumed, or zero if there is not enough.

func EncodeVarint

func EncodeVarint(x uint32) []byte

EncodeVarint returns the varint encoding of x.

func FreqToNote

func FreqToNote(freq float64) int

FreqToNote reports the associated midi node for a given frequency.

func KeyFreq

func KeyFreq(n string, octave int) float64

KeyFreq returns the frequency for the given key/octave combo https://en.wikipedia.org/wiki/MIDI_Tuning_Standard#Frequency_values

func KeyInt

func KeyInt(n string, octave int) int

KeyInt converts an A-G note notation to a midi note number value.

func NoteOctave

func NoteOctave(note int) int

NoteOctave returns the octave of the MIDI note

func NoteToFreq

func NoteToFreq(note int) float64

NoteToFreq returns the frequency of the passed midi note.

func NoteToName

func NoteToName(note int) string

NoteToName converts a midi note value into its English name

func Uint24

func Uint24(n uint32) []byte

Uint24 converts a uint32 into a uint24 (big endian)

Types

type ControlChangeEvent

type ControlChangeEvent struct {
	Channel int
	ID      int
	Value   int
	Name    string
}

type Decoder

type Decoder struct {
	Debug bool

	Ch chan *Track
	/*
	   Format describes the tracks format

	   0	-	single-track
	   Format 0 file has a header chunk followed by one track chunk. It
	   is the most interchangable representation of data. It is very useful
	   for a simple single-track player in a program which needs to make
	   synthesizers make sounds, but which is primarily concerened with
	   something else such as mixers or sound effect boxes. It is very
	   desirable to be able to produce such a format, even if your program
	   is track-based, in order to work with these simple programs. On the
	   other hand, perhaps someone will write a format conversion from
	   format 1 to format 0 which might be so easy to use in some setting
	   that it would save you the trouble of putting it into your program.


	   Synchronous multiple tracks means that the tracks will all be vertically synchronous, or in other words,
	    they all start at the same time, and so can represent different parts in one song.
	    1	-	multiple tracks, synchronous
	    Asynchronous multiple tracks do not necessarily start at the same time, and can be completely asynchronous.
	    2	-	multiple tracks, asynchronous
	*/
	Format uint16

	// NumTracks represents the number of tracks in the midi file
	NumTracks uint16

	TicksPerQuarterNote uint16

	TimeFormat timeFormat
	Tracks     []*Track
	// contains filtered or unexported fields
}

Decoder Format documented there: http://www.music.mcgill.ca/~ich/classes/mumt306/midiformat.pdf

  <Header Chunk> = <chunk type><length><format><ntrks><division>

				Division, specifies the meaning of the delta-times.
				It has two formats, one for metrical time, and one for time-code-based
				time:
				 +---+-----------------------------------------+
				 | 0 | ticks per quarter-note                  |
				 ==============================================|
				 | 1 | negative SMPTE format  | ticks per frame|
				 +---+-----------------------+-----------------+
				 |15 |14                    8 |7             0 |
				If bit 15 of <division> is zero, the bits 14 thru 0 represent the number
				of delta time "ticks" which make up a quarter-note. For instance, if
				division is 96, then a time interval of an eighth-note between two
				events in the file would be 48.
		    If bit 15 of <division> is a one, delta times in a file correspond
		    to subdivisions of a second, in a way consistent with SMPTE and MIDI
		    Time Code. Bits 14 thru 8 contain one of the four values -24, -25, -29,
		    or -30, corresponding to the four standard SMPTE and MIDI Time Code
		    formats (-29 corresponds to 30 drop frome), and represents the
		    number of frames per second. These negative numbers are stored in
		    two's compliment form. The second byte (stored positive) is the
		    resolution within a frame: typical values may be 4 (MIDI Time Code
		    resolution), 8, 10, 80 (bit resolution), or 100. This stream allows
		    exact specifications of time-code-based tracks, but also allows
		    milisecond-based tracks by specifying 25|frames/sec and a
		    resolution of 40 units per frame. If the events in a file are stored
		    with a bit resolution of thirty-framel time code, the division word
		    would be E250 hex.

func NewDecoder

func NewDecoder(r io.Reader) *Decoder

func NewParser

func NewParser(r io.Reader, ch chan *Track) *Decoder

func (*Decoder) CurrentTrack

func (d *Decoder) CurrentTrack() *Track

CurrentTrack returns the current track

func (*Decoder) Decode

func (d *Decoder) Decode() error

Decode decodes the MIDI file into a structure available from the decoder.

func (*Decoder) IDnSize

func (d *Decoder) IDnSize() ([4]byte, uint32, error)

IDnSize returns the next ID + block size

func (*Decoder) Parse

func (d *Decoder) Parse() error

Parse is a deprecated API, Decode() should be used instead.

func (*Decoder) Read

func (d *Decoder) Read(dst interface{}) error

Read reads n bytes from the parser's reader and stores them into the provided dst, which must be a pointer to a fixed-size value.

func (*Decoder) ReadByte

func (d *Decoder) ReadByte() (byte, error)

ReadByte returns one byte from the decoder

func (*Decoder) Uint24

func (d *Decoder) Uint24() (uint32, error)

Uint24 reads 3 bytes and convert them into a uint32

func (*Decoder) Uint7

func (d *Decoder) Uint7() (uint8, error)

Uint7 reads a byte and converts the first 7 bits into an uint8

func (*Decoder) VarLen

func (d *Decoder) VarLen() (val uint32, readBytes uint32, err error)

VarLen returns the variable length value at the exact parser location.

func (*Decoder) VarLenTxt

func (d *Decoder) VarLenTxt() (string, uint32, error)

VarLenTxt Returns a variable length text string as well as the amount of bytes read

type Encoder

type Encoder struct {

	/*
	   Format describes the tracks format

	   0	-	single-track
	   Format 0 file has a header chunk followed by one track chunk. It
	   is the most interchangable representation of data. It is very useful
	   for a simple single-track player in a program which needs to make
	   synthesizers make sounds, but which is primarily concerned with
	   something else such as mixers or sound effect boxes. It is very
	   desirable to be able to produce such a format, even if your program
	   is track-based, in order to work with these simple programs. On the
	   other hand, perhaps someone will write a format conversion from
	   format 1 to format 0 which might be so easy to use in some setting
	   that it would save you the trouble of putting it into your program.


	   Synchronous multiple tracks means that the tracks will all be vertically synchronous, or in other words,
	    they all start at the same time, and so can represent different parts in one song.
	    1	-	multiple tracks, synchronous
	    Asynchronous multiple tracks do not necessarily start at the same time, and can be completely asynchronous.
	    2	-	multiple tracks, asynchronous
	*/
	Format uint16

	// NumTracks represents the number of tracks in the midi file
	NumTracks uint16

	// resolution for delta timing
	TicksPerQuarterNote uint16

	TimeFormat timeFormat
	Tracks     []*Track
	// contains filtered or unexported fields
}

func NewEncoder

func NewEncoder(w io.WriteSeeker, format uint16, ppqn uint16) *Encoder

NewEncoder returns an encoder with the specified format

func (*Encoder) NewTrack

func (e *Encoder) NewTrack() *Track

NewTrack adds and return a new track (not thread safe)

func (*Encoder) Write

func (e *Encoder) Write() error

Write writes the binary representation to the writer

type Event

type Event struct {
	TimeDelta uint32
	// AbsTicks is the absolute number of ticks since the start of the file.
	AbsTicks     uint64
	MsgType      uint8
	MsgChan      uint8
	Note         uint8
	Velocity     uint8
	Pressure     uint8
	Controller   uint8
	NewValue     uint8
	NewProgram   uint8
	Channel      uint8
	AbsPitchBend uint16
	RelPitchBend int16
	// Meta
	Cmd            uint8
	SeqNum         uint16
	Text           string
	Copyright      string
	SeqTrackName   string
	InstrumentName string
	Lyric          string
	Marker         string
	CuePoint       string
	MsPerQuartNote uint32
	Bpm            uint32
	TimeSignature  *TimeSignature
	// A positive value for the key specifies the number of sharps and a negative value specifies the number of flats.
	Key int32 //-7 to +7
	// A value of 0 for the scale specifies a major key and a value of 1 specifies a minor key.
	Scale uint32 // 0 or 1
	//
	SmpteOffset *SmpteOffset
}

Event <event> = <MIDI event> | <sysex event> | <meta-event> <MIDI event> is any MIDI channel message. Running status is used: status bytes of MIDI channel messages may be omitted if the preceding event is a MIDI channel message with the same status. The first event in each MTrk chunk must specifyy status. Delta-time is not considered an event itself: it is an integral part of the syntax for an MTrk event. Notice that running status occurs across delta-times. See http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI4.shtml

func Aftertouch

func Aftertouch(channel, key, vel int) *Event

Aftertouch returns a pointer to a new aftertouch event

func ChannelAfterTouch

func ChannelAfterTouch(channel, vel int) *Event

ChannelAfterTouch is a global aftertouch with a value from 0 to 127

func ControlChange

func ControlChange(channel, controller, newVal int) *Event

ControlChange sets a new value for a given controller The controller number is between 0-119. The new controller value is between 0-127.

func CopyrightEvent

func CopyrightEvent(txt string) *Event

CopyrightEvent returns a copyright event with the passed string in it.

func EndOfTrack

func EndOfTrack() *Event

EndOfTrack indicates the end of the midi track. Note that this event is automatically added when encoding a normal track.

func Meta

func Meta(channel int) *Event

TODO

func NoteOff

func NoteOff(channel, key int) *Event

NoteOff returns a pointer to a new event of type NoteOff (without the delta timing data)

func NoteOn

func NoteOn(channel, key, vel int) *Event

NoteOn returns a pointer to a new event of type NoteOn (without the delta timing data)

func PitchWheelChange

func PitchWheelChange(channel, int, val int) *Event

PitchWheelChange is sent to indicate a change in the pitch bender. The possible value goes between 0 and 16383 where 8192 is the center.

func ProgramChange

func ProgramChange(channel, controller, newVal int) *Event

ProgramChange sets a new value the same way as ControlChange but implements Mode control and special message by using reserved controller numbers 120-127.

func TempoEvent

func TempoEvent(bpmF float64) *Event

TempoEvent returns a new tempo event of the passed value.

func TrackName

func TrackName(name string) *Event

func (*Event) Copy

func (e *Event) Copy() *Event

Copy returns an exact copy of the event

func (*Event) Encode

func (e *Event) Encode() ([]byte, error)

Encode converts an Event into a slice of bytes ready to be written to a file.

func (*Event) Position

func (e *Event) Position(ppq uint16) Position

Position returns the start position of the event in index zero.

func (*Event) Size

func (e *Event) Size() uint32

Size represents the byte size to encode the event

func (*Event) String

func (e *Event) String() string

String implements the stringer interface

type Note

type Note struct {
	Channel  int
	Key      int
	Velocity int
}

type Position

type Position struct {
	// Bar is the bar/measure of position (index 0)
	Bar uint64
	// Beat is the beat position within the bar, index 0, the max value depends
	// of the time signature
	Beat uint32
	// Div is the division position within the beat
	Div   uint32
	Ticks uint32
}

Position indicates where we are at in the project.

func TickPosition

func TickPosition(tick uint64, ppq uint16) Position

TickPosition returns the position of the passed tick

func (Position) ToTicks

func (p Position) ToTicks(ppq uint16) uint64

ToTicks converts a position to an absolute tick number.

type SmpteOffset

type SmpteOffset struct {
	Hour  uint8
	Min   uint8
	Sec   uint8
	Fr    uint8
	SubFr uint8
}

type TimeSignature

type TimeSignature struct {
	Numerator                   uint8
	Denominator                 uint8
	ClocksPerTick               uint8
	ThirtySecondNotesPerQuarter uint8
}

TimeSignature FF 58 04 nn dd cc bb Time Signature The time signature is expressed as four numbers. nn and dd represent the numerator and denominator of the time signature as it would be notated. The denominator is a negative power of two: 2 represents a quarter-note, 3 represents an eighth-note, etc. The cc parameter expresses the number of MIDI clocks in a metronome click. The bb parameter expresses the number of notated 32nd-notes in a MIDI quarter-note (24 MIDI clocks). This was added because there are already multiple programs which allow a user to specify that what MIDI thinks of as a quarter-note (24 clocks) is to be notated as, or related to in terms of, something else.

func (*TimeSignature) Denum

func (ts *TimeSignature) Denum() int

Denum returns the notation denominator (which is not how it's stored in MIDI)

func (*TimeSignature) String

func (ts *TimeSignature) String() string

type Track

type Track struct {
	Size   uint32
	Events []*Event
	// contains filtered or unexported fields
}

Track <Track Chunk> = <chunk type><length><MTrk event> <MTrk event> = <delta-time><event>

func (*Track) Add

func (t *Track) Add(beatDelta float64, e *Event)

Add schedules the passed event after x beats (relative to the previous event)

func (*Track) AddAfterDelta

func (t *Track) AddAfterDelta(ticks uint32, e *Event)

AddAfterDelta schedules the passed event after x beats (relative to the previous event)

func (*Track) ChunkData

func (t *Track) ChunkData(endTrack bool) ([]byte, error)

ChunkData converts the track and its events into a binary byte slice (chunk header included). If endTrack is set to true, the end track metadata will be added if not already present.

func (*Track) Name

func (t *Track) Name() string

Name returns the name of the track if provided

func (*Track) SetName

func (t *Track) SetName(name string) *Track

SetName sets the name on the track so it can be encoded when the track is serialized.

func (*Track) Tempo

func (t *Track) Tempo() int

Tempo returns the tempo of the track if set, 0 otherwise

Directories

Path Synopsis
cmd
gen

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL