SimpleMIDIFile
part of wslib
a MIDI file implementation capable of reading and writing all types of
MIDI data (except sysex messages).
first an example:
(
m = SimpleMIDIFile( "~/Desktop/midifiletest.mid" ); // create empty file
m.init1( 3, 120, "4/4" ); // init for type 1 (multitrack); 3 tracks, 120bpm, 4/4 measures
m.timeMode = \seconds; // change from default to something useful
((0,(1/8)..5)).do({ |starttime| // add random notes
m.addNote( 36 + 50.rand, 32 + 96.rand, starttime, [0.1,0.05].choose, 127,
track: 1 )
});
((0,(1/4)..5)).do({ |starttime| // add random notes to next track
m.addNote( 36 + 50.rand, 64 + 64.rand, starttime, [0.1,0.025].choose, 127,
channel: 1, // note: = midi channel 2
track: 2 )
});
)
m.midiEvents.dopostln; // all midi events
m.metaEvents.dopostln; // notice the incorrect 'endOfTrack' events for track 1 & 2;
m.adjustEndOfTrack;
m.metaEvents.dopostln; // try again
m.tempoMap; // get the tempo map ( [[ startTime, bpm ], [ etc.. ]] )
m.timeSignatures; // get the time signatures ( ( [[ startTime, a/b ], [ etc.. ]] )
m.p.play; // convert to Pattern and play
m.write; // now play the file in Quicktime, or open with another app
m.plot; // uses ScaledUserView
SimpleMIDIFile also supports Event Patterns (see Pattern and Pbind helpfiles). You can use the p method to convert a SimpleMIDIFile into a playable pattern (a Ppar to be exact).
// download a Bach midi file:
"curl http://www.bachcentral.com/BachCentral/ORGAN/toccata1.mid -o ~/Desktop/toccata1.mid".unixCmd;
// read it
m = SimpleMIDIFile.read( "~/Desktop/toccata1.mid" );
// play it (cmd-. to stop)
m.p.play; // takes a few seconds to start because this midifile starts with a rest
// play it with another instrument
(
SynthDef( "organ", { |freq = 440, sustain = 1, amp = 0.1|
var sig;
sig = LFPar.ar( freq * [1,2,3,5], 0, amp/[2,4,5,7] );
Out.ar( 0, Env([0,1,1,0], [0.025,sustain,0.025]).kr(2) * sig.dup )
}).add;
);
x = m.p( \organ ).play;
x.stop;
Via the fromPattern method patterns can be turned into midi files as well:
(
p = Pbind(
\dur, Prand( [0.25,0.5,1], inf ),
\freq, Pwhite( 440, 880 ),
\db, Pwhite( -40,-10 ),
\legato, Pwhite( 0.25, 4 ) );
m = SimpleMIDIFile( "~/Desktop/testPat.mid" );
m.init1( 2, 120, "4/4" );
m.fromPattern( p );
)
m.plot;
m.p.play; // note numbers are not rounded
p.play; // compare
m.write; // when writing to file note numbers are rounded (MIDI file format doesn't allow floats)
Instance variables:
all variables stated here have getters and setters. However in some cases the setters
are actually separate methods which may change or convert other variables as well.
pathName
file path of the midi file. defaults to "~/scwork/midi.mid"
midiEvents
an array containing all MIDI events as arrays. Format:
[ trackNumber, absTime, type, channel, val1, val2 ]
trackNumber
the number of the track in which this event sits. For format type 0 files this is always 0,
for type 1 files track 0 is reserved for meta events, so midiEvent tracks start counting at 1
absTime
the absolute start-time of the event. If timeMode == \ticks the absTime is in ticks (tempo dependant),
if timeMode == \seconds it is in seconds (not depending on tempo). In the original midi file format all
times are relative to the previous event on a track, but for convenience SimpleMIDIFile converts them
to absolute times when reading in.
type
usually a Symbol which can be:
\noteOff
\noteOn
\polyTouch
\cc
\program
\afterTouch
\pitchBend
channel
midi channel on which the event plays. Starts at 0 (= midi channel 1)
val1, val2
values for the event. These are usually 0-127. The contents of these vary per event type:
type val1 val2
noteOn note number velocity
noteOff note number velocity
polyTouch note number amount
cc control number amount
program program number (no val2)
afterTouch amount (no val2)
pitchBend depends on pitchBendMode
metaEvents
an array containing all META events as arrays. Format:
[ trackNumber, absTime, type, [values] / value / string ]
The absTime represents the abslute time at which the event takes place. In the Standard MIDI file
format time values are always relative to the previous event. For convenience reasons the
SimpleMIDIFile class converts these to absolute times when reading a file.
The format of last item in the array above depends on the type of the event:
types of which the last item is a string
\text
\copyright
\trackName
\instName
\lyrics
\marker
\cuePoint
types of which the last item is a value
\sequenceNumber
\tempo -> tempo in beats per minute
types of which the last item is an array of values
\timeSignature
\keySignature
\sequencerSpecific
\midiChannelPrefix
types of which the last item something else
\smpteOffset -> a SMPTE
\endOfTrack -> nil
there can be unknown types of meta events. They have status byte number instead of
a symbol in the 'type' slot of the array
tempo
a global tempo indicator in bpm. If there's one or more tempo events in the metaEvents,
tempo will always indicate the tempo of the first one found. The tempo mapping functionality
uses the tempo events instead of this value. Setting the tempo with tempo_ doesn't add or change
any tempo event ( a MIDI file can but doesn't need to contain a tempo event ). To get the tempo
again from the metaEvents if their there use getTempo.
timeMode
can be 'ticks' or 'seconds'. Defaults to 'ticks'. This affects all time information
contained in the midiEvents and metaEvents. Whenever the timeMode is switched
all time data will be converted.
ticks
ticks are the standard MIDI file format for time data. The duration of one tick
depends on the division and tempo at that specific point.
seconds
seconds are absolute seconds, not influenced by tempo or division
division
number of 'ticks' per quarter note. This should usually be 1024. Change it only when
you experience problems with specific sequencer software (there seem to be some which
use different divisions)
format
can be 0, 1 or 2 (single, multi or pattern oriented)
0
format 0 files typically have all data in the first track (0)
1
format 1 files typically have al global metaData ins the first track (0)
and all midi data in the following tracks (1..). A trackName metaEvent
in track 0 is usually interpreted as the song name
2
format 2 is not very common. Works the same as format 1, but is meant for
separate patterns in one file, contained in sequences (tracks)
Empty SimpleMIDIFiles need to be inited as format 0 or 1 first, using the init0 and init1 methods
tracks
number of tracks used. Can be fetched from midiEvents using the adjustTracks method
pitchBendMode
can be 'int8', 'int16' or 'float'. The default is 'int8'. When the pitchBendMode is changed all existing
values are converted.
int8
pitchbend stored as array of 2 int8 values (0-127), as stored in the midifile
int16
pitchbend stored as single int16 value (0-16383)
float
pitchbend stored as floating point values (-1.0 - 1.0)
Methods
creation
*new ( pathname )
initiation
init0 ( inTempo, timeSignature )
init1 ( inTracks, inTempo, timeSignature ) // minimum tracks: 2; first is metaData
read / write
*read ( pathname )
read
write ( newFileName )
pattern support
p ( inst, amp )
fromPattern ( pattern, maxEvents = 200, maxAmp = 0.2, startTime = 0 )
generatePatternSeqs // returns sequenceable [note, dur] data in an array
correction
adjustTracks // adjust number of tracks according to midi data
adjustEndOfTrack ( track, wait )
correctTempoEvents ( removeDuplicates, removeDoubles )
sortMIDIEvents // sort events on time and track
sortMetaEvents
getTempo
conversion / processing
convertNoteOns ( noteOffVelo )
convertNoteOffs
shiftTime ( deltaTime )
selection
midiTrackEvents ( trackNumber )
midiChannelEvents ( channel, track )
midiTrackTypeEvents ( track, type, channel )
midiTypeEvents ( args )
noteEvents ( channel, track )
noteOnEvents ( channel, track )
realNoteOnEvents ( channel, track )
noteOffEvents ( channel, track )
firstNoteOnTrack ( track )
pitchBendEvents ( channel, track )
afterTouchEvents ( channel, track )
controllerEvents ( cc, channel, track )
ccEvents ( cc, channel, track )
modulationEvents ( channel, track )
breathEvents ( channel, track )
volumeEvents ( channel, track )
panEvents ( channel, track )
expressionEvents ( channel, track )
damperEvents ( channel, track )
metaTrackEvents ( trackNumber )
timeSignatureEvents
keySignatureEvents
smpteOffsetEvents
tempoEvents
endOfTrack ( track )
converted selection to different array formats or Classes
noteSustainEvents( channel, track )
// noteSustainEvents returns events in this form:
// [track, absTime, \noteOn, channel, note, velo, dur, upVelo]
midiDeltaEvents ( track, type, channel )
midiDeltaCCEvents ( track, cc, channel )
timeSignatures
keySignatures
trackNames
instNames
midiTracks
metaTracks
asDicts ( )
asNoteDicts ( channel, track )
envFromType ( track, type, channel )
envFromCC ( track, cc, channel )
extraction of single values
trackName ( track )
instName ( track )
length
smpteOffset
timing methods (converted selection, extraction)
tempi
tempoMap
tempoEnv
beatAtTime ( time )
timeAtBeat ( beat )
tempoAtTime ( time )
timeSignatureAsArray
measureAtBeat ( beat, measureFormat )
measureAtTime ( time, measureFormat )
beatAtMeasure ( measure, measureFormat )
timeAtMeasure ( measure, measureFormat )
addition of events
addMIDIEvent ( event, sort )
addAllMIDIEvents ( events, sort )
addMIDIEventToTrack ( event, track, sort )
addAllMIDIEventsToTrack ( events, track, sort )
addMIDITypeEvent ( type, channel, args, absTime, track, sort )
addAllMIDITypeEvents ( type, channel, args, absTime, track, sort )
addNote ( noteNumber, velo, startTime, dur, upVelo, channel, track, sort )
addCC ( cc, val, startTime, channel, track )
addMetaEvent ( event, sort )
addTrack ( name )
addTimeSignature ( div, denom, time, sort, removeOld )
addTimeSignatureString ( string, time, sort, removeOld )
addTempo ( tempo, time, sort, removeOld )
setTrackName ( name, track )
setInstName ( name, track )
setTempo ( newTempo )
removal
removeMetaEvents ( type, time, track )
removeTimeSignature ( time, doIt )
removeTempo ( time, doIt )
analysis
analyzeTypes
analyzeMetaTypes
analyzeChannels ( type )
analyzeChannel ( channel, track )
analyzeUsedChannels ( track )
analyzeUsedTracks
analyzeUsedEvents
analyzeTracks ( type )
analyzeCC
analyzeCCTracks
usedChannels ( track )
usedTracks ( channel )
countMIDIEvents ( type, track, channel )
info