Drop of 0.87 release.
This commit is contained in:
229
documentation/ClassReference.txt
Normal file
229
documentation/ClassReference.txt
Normal file
@@ -0,0 +1,229 @@
|
||||
========================
|
||||
MIDIUtil Class Reference
|
||||
========================
|
||||
|
||||
--------------
|
||||
class MIDIFile
|
||||
--------------
|
||||
|
||||
A class that represents a full, well-formed MIDI pattern.
|
||||
|
||||
This is a container object that contains a header, one or more
|
||||
tracks, and the data associated with a proper and well-formed
|
||||
MIDI pattern.
|
||||
|
||||
Calling
|
||||
|
||||
MyMIDI = MidiFile(tracks, removeDuplicates=True, deinterleave=True)
|
||||
|
||||
normally
|
||||
|
||||
MyMIDI = MidiFile(tracks)
|
||||
|
||||
Arguments
|
||||
|
||||
o tracks: The number of tracks this object contains
|
||||
|
||||
o removeDuplicates: If true (the default), the software will
|
||||
remove duplicate events which have been added. For example,
|
||||
two notes at the same channel, time, pitch, and duration would
|
||||
be considered duplicate.
|
||||
|
||||
o deinterleave: If True (the default), overlapping notes
|
||||
(same pitch, same channel) will be modified so that they do
|
||||
not overlap. Otherwise the sequencing software will need to
|
||||
figure out how to interpret NoteOff events upon playback.
|
||||
|
||||
================
|
||||
Public Functions
|
||||
================
|
||||
|
||||
---------------------------------------------------
|
||||
addNote(track, channel, pitch,time,duration,volume)
|
||||
---------------------------------------------------
|
||||
|
||||
Add notes to the MIDIFile object
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addNotes(track,channel,pitch,time, duration, volume)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the note is added.
|
||||
o channel: the MIDI channel to assign to the note. [Integer, 0-15]
|
||||
o pitch: the MIDI pitch number [Integer, 0-127].
|
||||
o time: the time (in beats) at which the note sounds [Float].
|
||||
o duration: the duration of the note (in beats) [Float].
|
||||
o lume: the volume (velocity) of the note. [Integer, 0-127].
|
||||
|
||||
|
||||
----------------------------------
|
||||
addTrackName(track, time,trackName)
|
||||
----------------------------------
|
||||
|
||||
Add a track name to a MIDI track.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addTrackName(track,time,trackName)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the name is added. [Integer, 0-127].
|
||||
o time: The time at which the track name is added, in beats
|
||||
[Float].
|
||||
o trackName: The track name. [String].
|
||||
|
||||
---------------------------
|
||||
addTempo(track, time,tempo)
|
||||
---------------------------
|
||||
|
||||
Add a tempo event.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addTempo(track, time, tempo)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127]
|
||||
o time: The time at which the event is added, in beats. [Float]
|
||||
o tempo: The tempo, in Beats per Minute. [Integer]
|
||||
|
||||
|
||||
-----------------------------------------------
|
||||
addProgramChange(track, channel, time, program)
|
||||
-----------------------------------------------
|
||||
|
||||
Add a MIDI program change event.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addProgramChange(track,channel, time, program)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127]
|
||||
o channel: The channel the event is assigned to. [Integer, 0-15]
|
||||
o time: The time at which the event is added, in beats. [Float]
|
||||
o program: the program number. [Integer, 0-127]
|
||||
|
||||
|
||||
--------------------------------------------------------------
|
||||
addControllerEvent(track, channel,time,eventType, paramerter1)
|
||||
--------------------------------------------------------------
|
||||
|
||||
Add a MIDI controller event.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addControllerEvent(track, channel, time, eventType, \
|
||||
parameter1)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127]
|
||||
o channel: The channel the event is assigned to. [Integer, 0-15]
|
||||
o time: The time at which the event is added, in beats. [Float]
|
||||
o eventType: the controller event type.
|
||||
o parameter1: The event's parameter. The meaning of which varies
|
||||
by event type.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
changeNoteTuning(track, tunings, sysExChannel=0x7F, realTime=False, \
|
||||
tuningProgam=0)
|
||||
---------------------------------------------------------------------
|
||||
|
||||
Change a note's tuning using sysEx change tuning program.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.changeNoteTuning(track,[tunings],realTime=False, \
|
||||
tuningProgram=0)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127].
|
||||
o tunings: A list of tuples in the form (pitchNumber,
|
||||
frequency). [[(Integer,Float]]
|
||||
o realTime: Boolean which sets the real-time flag. Defaults to false.
|
||||
o sysExChannel: do note use (see below).
|
||||
o tuningProgram: Tuning program to assign. Defaults to
|
||||
zero. [Integer, 0-127]
|
||||
|
||||
In general the sysExChannel should not be changed (parameter will
|
||||
be depreciated).
|
||||
|
||||
Also note that many software packages and hardware packages do not
|
||||
implement this standard!
|
||||
|
||||
|
||||
---------------------
|
||||
writeFile(fileHandle)
|
||||
---------------------
|
||||
|
||||
Write the MIDI File.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.writeFile(filehandle)
|
||||
|
||||
Arguments
|
||||
|
||||
o filehandle: a file handle that has been opened for binary
|
||||
writing.
|
||||
|
||||
|
||||
-------------------------------------
|
||||
addSysEx(track, time, manID, payload)
|
||||
-------------------------------------
|
||||
|
||||
Add a SysEx event
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addSysEx(track,time,ID,payload)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127].
|
||||
o time: The time at which the event is added, in beats. [Float].
|
||||
o ID: The SysEx ID number
|
||||
o payload: the event payload.
|
||||
|
||||
Note: This is a low-level MIDI function, so care must be used in
|
||||
constructing the payload. It is recommended that higher-level helper
|
||||
functions be written to wrap this function and construct the payload
|
||||
if a developer finds him or herself using the function heavily.
|
||||
|
||||
|
||||
---------------------------------------------------------
|
||||
addUniversalSysEx(track, time,code, subcode, payload, \
|
||||
sysExChannel=0x7F, realTime=False)}f
|
||||
---------------------------------------------------------
|
||||
|
||||
Add a Universal SysEx event.
|
||||
|
||||
Use
|
||||
|
||||
MyMIDI.addUniversalSysEx(track, time, code, subcode, payload, \
|
||||
sysExChannel=0x7f, realTime=False)
|
||||
|
||||
Arguments
|
||||
|
||||
o track: The track to which the event is added. [Integer, 0-127].
|
||||
o time: The time at which the event is added, in beats. [Float].
|
||||
o code: The event code. [Integer]
|
||||
o subcode The event sub-code [Integer]
|
||||
o payload: The event payload. [Binary string]
|
||||
o sysExChannel: The SysEx channel.
|
||||
o realTime: Sets the real-time flag. Defaults to zero.
|
||||
|
||||
Note: This is a low-level MIDI function, so care must be used in
|
||||
constructing the payload. It is recommended that higher-level helper
|
||||
functions be written to wrap this function and construct the payload
|
||||
if a developer finds him or herself using the function heavily. As an
|
||||
example of such a helper function, see the changeNoteTuning function,
|
||||
both here and in MIDITrack.
|
||||
|
169
documentation/Extending.txt
Normal file
169
documentation/Extending.txt
Normal file
@@ -0,0 +1,169 @@
|
||||
=====================
|
||||
Extending the Library
|
||||
=====================
|
||||
|
||||
The choice of MIDI event types included in the library is somewhat
|
||||
idiosyncratic; I included the events I needed for another software
|
||||
project I was wrote. You may find that you need additional events in
|
||||
your work. For this reason I am including some instructions on extending
|
||||
the library. The process isn't too hard (provided you have a working
|
||||
knowledge of Python and the MIDI standard), so the task shouldn't present
|
||||
a competent coder too much difficulty. Alternately (if, for example,
|
||||
you *don't* have a working knowledge of MIDI and don't desire to gain it),
|
||||
you can submit new feature requests to me, and I will include them into
|
||||
the development branch of the code, subject to the constraints of time.
|
||||
|
||||
To illustrate the process I show below how the MIDI tempo event is
|
||||
incorporated into the code. This is a relatively simple event, so while
|
||||
it may not illustrate some of the subtleties of MIDI programing, it
|
||||
provides a good, illustrative case.
|
||||
|
||||
|
||||
-----------------------
|
||||
Create a New Event Type
|
||||
-----------------------
|
||||
|
||||
The first order of business is to create a new subclass of the GnericEvent
|
||||
object of the MIDIFile module. This subclass initializes any specific
|
||||
instance data that is needed for the MIDI event to be written. In
|
||||
the case of the tempo event, it is the actual tempo (which is defined
|
||||
in the MIDI standard to be 60000000 divided by the tempo in beats per
|
||||
minute). This class should also call the superclass' initializer with
|
||||
the event time and set the event type (a unique string used internally by
|
||||
the software) in the __init__() function. In the case of the tempo event:
|
||||
|
||||
class tempo(GenericEvent):
|
||||
def __init__(self,time,tempo):
|
||||
GenericEvent.__init__(self,time)
|
||||
self.type = 'tempo'
|
||||
self.tempo = int(60000000 / tempo)
|
||||
|
||||
Next (and this is an embarrassing break of OO programming) the __eq__()
|
||||
function of the GenericEvent class should be modified so that equality
|
||||
of these types of events can be calculated. In calculating equivalence
|
||||
time is always checked, so two tempo events are considered the same if
|
||||
the have the same tempo value. Thus the following snippet of code from
|
||||
GenericEvent's _eq__() function accomplishes this goal:
|
||||
|
||||
|
||||
if self.type == 'tempo':
|
||||
if self.tempo != other.tempo:
|
||||
return False
|
||||
|
||||
|
||||
If events are equivalent, the code should return False. If they are not
|
||||
equivalent no return should be called.
|
||||
|
||||
---------------------------
|
||||
Create an Accessor Function
|
||||
---------------------------
|
||||
|
||||
|
||||
Next, an accessor function should be added to MIDITrack to create an
|
||||
event of this type. Continuing the example of the tempo event:
|
||||
|
||||
|
||||
def addTempo(self,time,tempo):
|
||||
self.eventList.append(MIDITrack.tempo(time,tempo))
|
||||
|
||||
|
||||
The public accessor function is via the MIDIFile object, and must include
|
||||
the track number to which the event is written:
|
||||
|
||||
|
||||
def addTempo(self,track,time,tempo):
|
||||
self.tracks[track].addTempo(time,tempo)
|
||||
|
||||
|
||||
This is the function you will use in your code to create an event of
|
||||
the desired type.
|
||||
|
||||
|
||||
-----------------------
|
||||
Modify processEventList
|
||||
-----------------------
|
||||
|
||||
Next, the logic pertaining to the new event type should be added to
|
||||
processEventList function of the MIDITrack class. In general this code
|
||||
will create a MIDIEvent object and set its type, time, ordinality, and
|
||||
any specific information that is needed for the event type. This object
|
||||
is then added to the MIDIEventList.
|
||||
|
||||
The ordinality (self.ord) is a number that tells the software how to
|
||||
sequence MIDI events that occur at the same time. The higher the number,
|
||||
the later in the sequence the event will be written in comparison to
|
||||
other, simultaneous events.
|
||||
|
||||
The relevant section for the tempo event is:
|
||||
|
||||
|
||||
elif thing.type == 'tempo':
|
||||
event = MIDIEvent()
|
||||
event.type = "Tempo"
|
||||
event.time = thing.time * TICKSPERBEAT
|
||||
event.tempo = thing.tempo
|
||||
event.ord = 3
|
||||
self.MIDIEventList.append(event)
|
||||
|
||||
|
||||
Thus if other events occur at the same time, type which have an ordinality
|
||||
of 1 or 2 will be written to the stream first.
|
||||
|
||||
Time needs to be converted from beats (which the accessor function uses)
|
||||
and MIDI time by multiplying by the constant TICKSPERBEAT. The value
|
||||
of thing.type is the unique string you defined above, and event.type
|
||||
is another unique things (they can--and probably should--be the same,
|
||||
although the coding here is a little sloppy and changes case of the
|
||||
string).
|
||||
|
||||
|
||||
----------------------------------------
|
||||
Write the Event Data to the MIDI Stream
|
||||
----------------------------------------
|
||||
|
||||
|
||||
The last step is to modify the MIDIFile writeEventsToStream function;
|
||||
here is where some understanding of the MIDI standard is necessary. The
|
||||
following code shows the creation of a MIDI tempo event:
|
||||
|
||||
|
||||
elif event.type == "Tempo":
|
||||
code = 0xFF
|
||||
subcode = 0x51
|
||||
fourbite = struct.pack('>L', event.tempo)
|
||||
threebite = fourbite[1:4] # Just discard the MSB
|
||||
varTime = writeVarLength(event.time)
|
||||
for timeByte in varTime:
|
||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',subcode)
|
||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', 0x03)
|
||||
self.MIDIdata = self.MIDIdata + threebite
|
||||
|
||||
|
||||
The event.type string ("Tempo") was the one chosen in the processEventList
|
||||
logic.
|
||||
|
||||
The code and subcode are binary values that come from the MIDI
|
||||
specification.
|
||||
|
||||
Next the data is packed into a three byte structure (or a four byte
|
||||
structure, discarding the most significant byte). Again, the MIDI
|
||||
specification determines the number of bytes used in the data payload.
|
||||
|
||||
The event time should be converted to MIDI variable-length data with the
|
||||
writeVarLength() function before writing to the stream (as shown above).
|
||||
The MIDI standard utilizes a slightly bizarre variable length data
|
||||
record. In it, only seven bits of a word are used to store data; the
|
||||
eighth bit signifies if more bytes encoding the value follow. The total
|
||||
length may be 1 to 3 bytes, depending upon the size of the value encoded.
|
||||
The writeVarLength() function takes care of this conversion for you.
|
||||
|
||||
Now the data is written to the binary object self.MIDIdata, which is
|
||||
the actual MIDI-encoded data stream. As per the MIDI standard, first we
|
||||
write our variable-length time value. Next we add the event type code and
|
||||
subcode. Then we write the length of the data payload, which in the case
|
||||
of the tempo event is three bytes. Lastly, we write the actual payload,
|
||||
which has been packed into the variable threebite.
|
||||
|
||||
Clear as mud!
|
Reference in New Issue
Block a user