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