madmom.utils.midi

This module contains MIDI functionality.

Almost all code is taken from Giles Hall’s python-midi package: https://github.com/vishnubob/python-midi

It combines the complete package in a single file, to make it easier to distribute. Most notable changes are MIDITrack and MIDIFile classes which handle all data i/o and provide a interface which allows to read/display all notes as simple numpy arrays. Also, the EventRegistry is handled differently.

The last merged commit is 3053fefe.

Since then the following commits have been added functionality-wise:

  • 0964c0b (prevent multiple tick conversions)
  • c43bf37 (add pitch and value properties to AfterTouchEvent)
  • 40111c6 (add 0x08 MetaEvent: ProgramNameEvent)
  • 43de818 (handle unknown MIDI meta events gracefully)

Additionally, the module has been updated to work with Python3.

The MIT License (MIT) Copyright (c) 2013 Giles F. Hall

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

madmom.utils.midi.byte2int(byte)[source]

Convert a byte-character to an integer.

madmom.utils.midi.read_variable_length(data)[source]

Read a variable length variable from the given data.

Parameters:

data : bytearray

Data of variable length.

Returns:

length : int

Length in bytes.

madmom.utils.midi.write_variable_length(value)[source]

Write a variable length variable.

Parameters:

value : bytearray

Value to be encoded as a variable of variable length.

Returns:

bytearray

Variable with variable length.

class madmom.utils.midi.EventRegistry[source]

Class for registering Events.

Event classes should be registered manually by calling EventRegistry.register_event(EventClass) after the class definition.

Normal events are registered in the events dictionary and use the event’s status_msg as a key; meta events are registered in the meta_events dictionary and use their meta_command as key.

classmethod register_event(event)[source]

Registers an event in the registry.

Parameters:

event : Event instance

Event to be registered.

class madmom.utils.midi.Event(**kwargs)[source]

Generic MIDI Event.

class madmom.utils.midi.ChannelEvent(**kwargs)[source]

Event with a channel number.

class madmom.utils.midi.NoteEvent(**kwargs)[source]

NoteEvent is a special subclass of Event that is not meant to be used as a concrete class. It defines the generalities of NoteOn and NoteOff events.

pitch

Pitch of the note event.

velocity

Velocity of the note event.

class madmom.utils.midi.NoteOnEvent(**kwargs)[source]

Note On Event.

class madmom.utils.midi.NoteOffEvent(**kwargs)[source]

Note Off Event.

class madmom.utils.midi.AfterTouchEvent(**kwargs)[source]

After Touch Event.

pitch

Pitch of the after touch event.

value

Value of the after touch event.

class madmom.utils.midi.ControlChangeEvent(**kwargs)[source]

Control Change Event.

control

Control ID.

value

Value of the controller.

class madmom.utils.midi.ProgramChangeEvent(**kwargs)[source]

Program Change Event.

value

Value of the Program Change Event.

class madmom.utils.midi.ChannelAfterTouchEvent(**kwargs)[source]

Channel After Touch Event.

value

Value of the Channel After Touch Event.

class madmom.utils.midi.PitchWheelEvent(**kwargs)[source]

Pitch Wheel Event.

pitch

Pitch of the Pitch Wheel Event.

class madmom.utils.midi.SysExEvent(**kwargs)[source]

System Exclusive Event.

class madmom.utils.midi.MetaEvent(**kwargs)[source]

MetaEvent is a special subclass of Event that is not meant to be used as a concrete class. It defines a subset of Events known as the Meta events.

class madmom.utils.midi.MetaEventWithText(**kwargs)[source]

Meta Event With Text.

class madmom.utils.midi.SequenceNumberMetaEvent(**kwargs)[source]

Sequence Number Meta Event.

class madmom.utils.midi.TextMetaEvent(**kwargs)[source]

Text Meta Event.

class madmom.utils.midi.CopyrightMetaEvent(**kwargs)[source]

Copyright Meta Event.

class madmom.utils.midi.TrackNameEvent(**kwargs)[source]

Track Name Event.

class madmom.utils.midi.InstrumentNameEvent(**kwargs)[source]

Instrument Name Event.

class madmom.utils.midi.LyricsEvent(**kwargs)[source]

Lyrics Event.

class madmom.utils.midi.MarkerEvent(**kwargs)[source]

Marker Event.

class madmom.utils.midi.CuePointEvent(**kwargs)[source]

Cue Point Event.

class madmom.utils.midi.ProgramNameEvent(**kwargs)[source]

Program Name Event.

class madmom.utils.midi.UnknownMetaEvent(**kwargs)[source]

Unknown Meta Event.

Parameters:

meta_command : int

Value of the meta command.

class madmom.utils.midi.ChannelPrefixEvent(**kwargs)[source]

Channel Prefix Event.

class madmom.utils.midi.PortEvent(**kwargs)[source]

Port Event.

class madmom.utils.midi.TrackLoopEvent(**kwargs)[source]

Track Loop Event.

class madmom.utils.midi.EndOfTrackEvent(**kwargs)[source]

End Of Track Event.

class madmom.utils.midi.SetTempoEvent(**kwargs)[source]

Set Tempo Event.

microseconds_per_quarter_note

Microseconds per quarter note.

class madmom.utils.midi.SmpteOffsetEvent(**kwargs)[source]

SMPTE Offset Event.

class madmom.utils.midi.TimeSignatureEvent(**kwargs)[source]

Time Signature Event.

numerator

Numerator of the time signature.

denominator

Denominator of the time signature.

metronome

Metronome.

thirty_seconds

Thirty-seconds of the time signature.

class madmom.utils.midi.KeySignatureEvent(**kwargs)[source]

Key Signature Event.

alternatives

Alternatives of the key signature.

minor

Major / minor.

class madmom.utils.midi.SequencerSpecificEvent(**kwargs)[source]

Sequencer Specific Event.

class madmom.utils.midi.MIDITrack(events=None)[source]

MIDI Track.

Parameters:

events : list

MIDI events.

Notes

The events must be sorted. Consider using from_notes() method.

Examples

Create a MIDI track from a list of events. Please note that the events must be sorted.

>>> e1 = NoteOnEvent(tick=100, pitch=50, velocity=60)
>>> e2 = NoteOffEvent(tick=300, pitch=50)
>>> e3 = NoteOnEvent(tick=200, pitch=62, velocity=90)
>>> e4 = NoteOffEvent(tick=600, pitch=62)
>>> t = MIDITrack(sorted([e1, e2, e3, e4]))
>>> t  
<madmom.utils.midi.MIDITrack object at 0x...>
>>> t.events  
[<madmom.utils.midi.NoteOnEvent object at 0x...>,
 <madmom.utils.midi.NoteOnEvent object at 0x...>,
 <madmom.utils.midi.NoteOffEvent object at 0x...>,
 <madmom.utils.midi.NoteOffEvent object at 0x...>]

It can also be created from an array containing the notes. The from_notes method also takes care of creating tempo and time signature events.

>>> notes = np.array([[0.1, 50, 0.3, 60], [0.2, 62, 0.4, 90]])
>>> t = MIDITrack.from_notes(notes)
>>> t  
<madmom.utils.midi.MIDITrack object at 0x...>
>>> t.events  
[<madmom.utils.midi.SetTempoEvent object at 0x...>,
 <madmom.utils.midi.TimeSignatureEvent object at 0...>,
 <madmom.utils.midi.NoteOnEvent object at 0x...>,
 <madmom.utils.midi.NoteOnEvent object at 0x...>,
 <madmom.utils.midi.NoteOffEvent object at 0x...>,
 <madmom.utils.midi.NoteOffEvent object at 0x...>]
data_stream

MIDI data stream representation of the track.

classmethod from_stream(midi_stream)[source]

Create a MIDI track by reading the data from a stream.

Parameters:

midi_stream : open file handle

MIDI file stream (e.g. open MIDI file handle)

Returns:

MIDITrack instance

MIDITrack instance

classmethod from_notes(notes, tempo=120, time_signature=(4, 4), resolution=480)[source]

Create a MIDI track from the given notes.

Parameters:

notes : numpy array

Array with the notes, one per row. The columns must be: (onset time, pitch, duration, velocity, [channel]).

tempo : float, optional

Tempo of the MIDI track, given in beats per minute (bpm).

time_signature : tuple, optional

Time signature of the track, e.g. (4, 4) for 4/4.

resolution : int

Resolution (i.e. ticks per quarter note) of the MIDI track.

Returns:

MIDITrack instance

MIDITrack instance

Notes

All events including the generated tempo and time signature events is included in the returned track (i.e. as defined in MIDI format 0).

class madmom.utils.midi.MIDIFile(tracks=None, resolution=480, file_format=0)[source]

MIDI File.

Parameters:

tracks : list

List of MIDITrack instances.

resolution : int, optional

Resolution (i.e. microseconds per quarter note).

file_format : int, optional

Format of the MIDI file.

Notes

Writing a MIDI file assumes a tempo of 120 beats per minute (bpm) and a 4/4 time signature and writes all events into a single track (i.e. MIDI format 0).

Examples

Create a MIDI file from an array with notes. The format of the note array is: ‘onset time’, ‘pitch’, ‘duration’, ‘velocity’, ‘channel’. The last column can be omitted, assuming channel 0.

>>> notes = np.array([[0, 50, 1, 60], [0.5, 62, 0.5, 90]])
>>> m = MIDIFile.from_notes(notes)
>>> m  
<madmom.utils.midi.MIDIFile object at 0x...>

The notes can be accessed as a numpy array in various formats (default is seconds):

>>> m.notes()
array([[  0. ,  50. ,   1. ,  60. ,   0. ],
       [  0.5,  62. ,   0.5,  90. ,   0. ]])
>>> m.notes(unit='ticks')
array([[   0.,   50.,  960.,   60.,    0.],
       [ 480.,   62.,  480.,   90.,    0.]])
>>> m.notes(unit='beats')
array([[  0.,  50.,   2.,  60.,   0.],
       [  1.,  62.,   1.,  90.,   0.]])
>>> m = MIDIFile.from_notes(notes, tempo=60)
>>> m.notes(unit='ticks')
array([[   0.,   50.,  480.,   60.,    0.],
       [ 240.,   62.,  240.,   90.,    0.]])
>>> m.notes(unit='beats')
array([[  0. ,  50. ,   1. ,  60. ,   0. ],
       [  0.5,  62. ,   0.5,  90. ,   0. ]])
>>> m = MIDIFile.from_notes(notes, tempo=60, time_signature=(2, 2))
>>> m.notes(unit='ticks')
array([[   0.,   50.,  960.,   60.,    0.],
       [ 480.,   62.,  480.,   90.,    0.]])
>>> m.notes(unit='beats')
array([[  0. ,  50. ,   1. ,  60. ,   0. ],
       [  0.5,  62. ,   0.5,  90. ,   0. ]])
>>> m = MIDIFile.from_notes(notes, tempo=240, time_signature=(3, 8))
>>> m.notes(unit='ticks')
array([[   0.,   50.,  960.,   60.,    0.],
       [ 480.,   62.,  480.,   90.,    0.]])
>>> m.notes(unit='beats')
array([[  0.,  50.,   4.,  60.,   0.],
       [  2.,  62.,   2.,  90.,   0.]])
ticks_per_quarter_note

Number of ticks per quarter note.

tempi(suppress_warnings=False)[source]

Tempi of the MIDI file.

Returns:

tempi : numpy array

Array with tempi (tick, seconds per tick, cumulative time).

time_signatures(suppress_warnings=False)[source]

Time signatures of the MIDI file.

Returns:

time_signatures : numpy array

Array with time signatures (tick, numerator, denominator).

notes(unit='s')[source]

Notes of the MIDI file.

Parameters:

unit : {‘s’, ‘seconds’, ‘b’, ‘beats’, ‘t’, ‘ticks’}

Time unit for notes, seconds (‘s’) beats (‘b’) or ticks (‘t’)

Returns:

notes : numpy array

Array with notes (onset time, pitch, duration, velocity, channel).

data_stream

MIDI data stream representation of the MIDI file.

write(midi_file)[source]

Write a MIDI file.

Parameters:

midi_file : str

The MIDI file name.

classmethod from_file(midi_file)[source]

Create a MIDI file instance from a .mid file.

Parameters:

midi_file : str

Name of the .mid file to load.

Returns:

MIDIFile instance

MIDIFile instance

classmethod from_notes(notes, tempo=120, time_signature=(4, 4), resolution=480)[source]

Create a MIDIFile from the given notes.

Parameters:

notes : numpy array

Array with the notes, one per row. The columns must be: (onset time, pitch, duration, velocity, [channel]).

tempo : float, optional

Tempo of the MIDI track, given in beats per minute (bpm).

time_signature : tuple, optional

Time signature of the track, e.g. (4, 4) for 4/4.

resolution : int

Resolution (i.e. ticks per quarter note) of the MIDI track.

Returns:

MIDIFile instance

MIDIFile instance with all notes collected in one track.

Notes

All note events (including the generated tempo and time signature events) are written into a single track (i.e. MIDI file format 0).

static add_arguments(parser, length=None, velocity=None, channel=None)[source]

Add MIDI related arguments to an existing parser object.

Parameters:

parser : argparse parser instance

Existing argparse parser object.

length : float, optional

Default length of the notes [seconds].

velocity : int, optional

Default velocity of the notes.

channel : int, optional

Default channel of the notes.

Returns:

argparse argument group

MIDI argument parser group object.

madmom.utils.midi.process_notes(data, output=None)[source]

This is a simple processing function. It either loads the notes from a MIDI file and or writes the notes to a file.

The behaviour depends on the presence of the output argument, if ‘None’ is given, the notes are read, otherwise the notes are written to file.

Parameters:

data : str or numpy array

MIDI file to be loaded (if output is ‘None’) / notes to be written.

output : str, optional

Output file name. If set, the notes given by data are written.

Returns:

notes : numpy array

Notes read/written.