Add some PEP8 happiness
This commit is contained in:
parent
fc5cd7fdbd
commit
489c9c4494
@ -10,17 +10,22 @@
|
|||||||
# software is distributed.
|
# software is distributed.
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
import struct, sys, math
|
import struct
|
||||||
|
import sys
|
||||||
|
import math
|
||||||
|
|
||||||
# TICKSPERBEAT is the number of "ticks" (time measurement in the MIDI file) that
|
# TICKSPERBEAT is the number of "ticks" (time measurement in the MIDI
|
||||||
# corresponds to one beat. This number is somewhat arbitrary, but should be chosen
|
# file) that corresponds to one beat. This number is somewhat
|
||||||
# to provide adequate temporal resolution.
|
# arbitrary, but should be chosen to provide adequate temporal
|
||||||
|
# resolution.
|
||||||
|
|
||||||
TICKSPERBEAT = 128
|
TICKSPERBEAT = 128
|
||||||
|
|
||||||
controllerEventTypes = {
|
controllerEventTypes = {
|
||||||
'pan': 0x0a
|
'pan': 0x0a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class MIDIEvent:
|
class MIDIEvent:
|
||||||
'''
|
'''
|
||||||
The class to contain the MIDI Event (placed on MIDIEventList.
|
The class to contain the MIDI Event (placed on MIDIEventList.
|
||||||
@ -44,36 +49,40 @@ class MIDIEvent:
|
|||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
class GenericEvent():
|
class GenericEvent():
|
||||||
'''The event class from which specific events are derived
|
|
||||||
'''
|
'''
|
||||||
|
The event class from which specific events are derived
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, time):
|
def __init__(self, time):
|
||||||
self.time = time
|
self.time = time
|
||||||
self.type = 'Unknown'
|
self.type = 'Unknown'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
'''
|
'''
|
||||||
Equality operator for Generic Events and derived classes.
|
Equality operator for Generic Events and derived classes.
|
||||||
|
|
||||||
In the processing of the event list, we have need to remove duplicates. To do this
|
In the processing of the event list, we have need to remove
|
||||||
we rely on the fact that the classes are hashable, and must therefore have an
|
duplicates. To do this we rely on the fact that the classes
|
||||||
equality operator (__hash__() and __eq__() must both be defined).
|
are hashable, and must therefore have an equality operator
|
||||||
|
(__hash__() and __eq__() must both be defined).
|
||||||
|
|
||||||
This is the most embarrassing portion of the code, and anyone who knows about OO
|
This is the most embarrassing portion of the code, and anyone
|
||||||
programming would find this almost unbelievable. Here we have a base class that
|
who knows about OO programming would find this almost
|
||||||
knows specifics about derived classes, thus breaking the very spirit of
|
unbelievable. Here we have a base class that knows specifics
|
||||||
OO programming.
|
about derived classes, thus breaking the very spirit of OO
|
||||||
|
programming.
|
||||||
|
|
||||||
I suppose I should go back and restructure the code, perhaps removing the derived
|
I suppose I should go back and restructure the code, perhaps
|
||||||
classes altogether. At some point perhaps I will.
|
removing the derived classes altogether. At some point perhaps
|
||||||
|
I will.
|
||||||
'''
|
'''
|
||||||
if self.time != other.time or self.type != other.type:
|
if self.time != other.time or self.type != other.type:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# What follows is code that encodes the concept of equality for each derived
|
# What follows is code that encodes the concept of equality
|
||||||
# class. Believe it f you dare.
|
# for each derived class. Believe it f you dare.
|
||||||
|
|
||||||
if self.type == 'note':
|
if self.type == 'note':
|
||||||
if self.pitch != other.pitch or self.channel != other.channel:
|
if self.pitch != other.pitch or self.channel != other.channel:
|
||||||
@ -82,7 +91,8 @@ class GenericEvent():
|
|||||||
if self.tempo != other.tempo:
|
if self.tempo != other.tempo:
|
||||||
return False
|
return False
|
||||||
if self.type == 'programChange':
|
if self.type == 'programChange':
|
||||||
if self.programNumber != other.programNumber or self.channel != other.channel:
|
if self.programNumber != other.programNumber or \
|
||||||
|
self.channel != other.channel:
|
||||||
return False
|
return False
|
||||||
if self.type == 'trackName':
|
if self.type == 'trackName':
|
||||||
if self.trackName != other.trackName:
|
if self.trackName != other.trackName:
|
||||||
@ -92,6 +102,7 @@ class GenericEvent():
|
|||||||
self.parameter2 != other.parameter2 or \
|
self.parameter2 != other.parameter2 or \
|
||||||
self.channel != other.channel or \
|
self.channel != other.channel or \
|
||||||
self.eventType != other.eventType:
|
self.eventType != other.eventType:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.type == 'SysEx':
|
if self.type == 'SysEx':
|
||||||
@ -102,6 +113,7 @@ class GenericEvent():
|
|||||||
if self.code != other.code or \
|
if self.code != other.code or \
|
||||||
self.subcode != other.subcode or \
|
self.subcode != other.subcode or \
|
||||||
self.sysExChannel != other.sysExChannel:
|
self.sysExChannel != other.sysExChannel:
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
@ -110,10 +122,13 @@ class GenericEvent():
|
|||||||
'''
|
'''
|
||||||
Return a hash code for the object.
|
Return a hash code for the object.
|
||||||
|
|
||||||
This is needed for the removal of duplicate objects from the event list. The only
|
This is needed for the removal of duplicate objects from the
|
||||||
real requirement for the algorithm is that the hash of equal objects must be equal.
|
event list. The only real requirement for the algorithm is
|
||||||
There is probably great opportunity for improvements in the hashing function.
|
that the hash of equal objects must be equal. There is
|
||||||
|
probably great opportunity for improvements in the hashing
|
||||||
|
function.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Robert Jenkin's 32 bit hash.
|
# Robert Jenkin's 32 bit hash.
|
||||||
a = int(self.time)
|
a = int(self.time)
|
||||||
a = (a + 0x7ed55d16) + (a << 12)
|
a = (a + 0x7ed55d16) + (a << 12)
|
||||||
@ -122,8 +137,10 @@ class GenericEvent():
|
|||||||
a = (a + 0xd3a2646c) ^ (a << 9)
|
a = (a + 0xd3a2646c) ^ (a << 9)
|
||||||
a = (a + 0xfd7046c5) + (a << 3)
|
a = (a + 0xfd7046c5) + (a << 3)
|
||||||
a = (a ^ 0xb55a4f09) ^ (a >> 16)
|
a = (a ^ 0xb55a4f09) ^ (a >> 16)
|
||||||
|
|
||||||
return a
|
return a
|
||||||
|
|
||||||
|
|
||||||
class MIDITrack:
|
class MIDITrack:
|
||||||
'''A class that encapsulates a MIDI track
|
'''A class that encapsulates a MIDI track
|
||||||
'''
|
'''
|
||||||
@ -132,8 +149,8 @@ class MIDITrack:
|
|||||||
class note(GenericEvent):
|
class note(GenericEvent):
|
||||||
'''A class that encapsulates a note
|
'''A class that encapsulates a note
|
||||||
'''
|
'''
|
||||||
def __init__(self,channel, pitch,time,duration,volume):
|
|
||||||
|
|
||||||
|
def __init__(self, channel, pitch, time, duration, volume):
|
||||||
GenericEvent.__init__(self, time)
|
GenericEvent.__init__(self, time)
|
||||||
self.pitch = pitch
|
self.pitch = pitch
|
||||||
self.duration = duration
|
self.duration = duration
|
||||||
@ -154,12 +171,11 @@ class MIDITrack:
|
|||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class tempo(GenericEvent):
|
class tempo(GenericEvent):
|
||||||
'''A class that encapsulates a tempo meta-event
|
'''A class that encapsulates a tempo meta-event
|
||||||
'''
|
'''
|
||||||
def __init__(self,time,tempo):
|
|
||||||
|
|
||||||
|
def __init__(self, time, tempo):
|
||||||
GenericEvent.__init__(self, time)
|
GenericEvent.__init__(self, time)
|
||||||
self.type = 'tempo'
|
self.type = 'tempo'
|
||||||
self.tempo = int(60000000 / tempo)
|
self.tempo = int(60000000 / tempo)
|
||||||
@ -188,7 +204,9 @@ class MIDITrack:
|
|||||||
'''A class that encapsulates a Universal System Exclusive event.
|
'''A class that encapsulates a Universal System Exclusive event.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, time, realTime, sysExChannel, code, subcode, payload):
|
def __init__(self,
|
||||||
|
time, realTime, sysExChannel,
|
||||||
|
code, subcode, payload):
|
||||||
GenericEvent.__init__(self, time,)
|
GenericEvent.__init__(self, time,)
|
||||||
self.type = 'UniversalSysEx'
|
self.type = 'UniversalSysEx'
|
||||||
self.realTime = realTime
|
self.realTime = realTime
|
||||||
@ -201,7 +219,7 @@ class MIDITrack:
|
|||||||
'''A class that encapsulates a program change event.
|
'''A class that encapsulates a program change event.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, channel, time, eventType, parameter1,):
|
def __init__(self, channel, time, eventType, parameter1):
|
||||||
GenericEvent.__init__(self, time,)
|
GenericEvent.__init__(self, time,)
|
||||||
self.type = 'controllerEvent'
|
self.type = 'controllerEvent'
|
||||||
self.parameter1 = parameter1
|
self.parameter1 = parameter1
|
||||||
@ -217,7 +235,6 @@ class MIDITrack:
|
|||||||
self.type = 'trackName'
|
self.type = 'trackName'
|
||||||
self.trackName = trackName
|
self.trackName = trackName
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, removeDuplicates, deinterleave):
|
def __init__(self, removeDuplicates, deinterleave):
|
||||||
'''Initialize the MIDITrack object.
|
'''Initialize the MIDITrack object.
|
||||||
'''
|
'''
|
||||||
@ -231,17 +248,18 @@ class MIDITrack:
|
|||||||
self.deinterleave = deinterleave
|
self.deinterleave = deinterleave
|
||||||
|
|
||||||
def addNoteByNumber(self, channel, pitch, time, duration, volume):
|
def addNoteByNumber(self, channel, pitch, time, duration, volume):
|
||||||
'''Add a note by chromatic MIDI number
|
|
||||||
'''
|
'''
|
||||||
self.eventList.append(MIDITrack.note(channel, pitch,time,duration,volume))
|
Add a note by chromatic MIDI number
|
||||||
|
'''
|
||||||
|
self.eventList.append(MIDITrack.note(channel, pitch,
|
||||||
|
time, duration, volume))
|
||||||
|
|
||||||
def addControllerEvent(self, channel, time, eventType, paramerter1):
|
def addControllerEvent(self, channel, time, eventType, paramerter1):
|
||||||
'''
|
'''
|
||||||
Add a controller event.
|
Add a controller event.
|
||||||
'''
|
'''
|
||||||
|
self.eventList.append(
|
||||||
self.eventList.append(MIDITrack.ControllerEvent(channel,time,eventType, \
|
MIDITrack.ControllerEvent(channel, time, eventType, paramerter1))
|
||||||
paramerter1))
|
|
||||||
|
|
||||||
def addTempo(self, time, tempo):
|
def addTempo(self, time, tempo):
|
||||||
'''
|
'''
|
||||||
@ -255,13 +273,14 @@ class MIDITrack:
|
|||||||
'''
|
'''
|
||||||
self.eventList.append(MIDITrack.SysExEvent(time, manID, payload))
|
self.eventList.append(MIDITrack.SysExEvent(time, manID, payload))
|
||||||
|
|
||||||
def addUniversalSysEx(self,time,code, subcode, payload, sysExChannel=0x7F, \
|
def addUniversalSysEx(self, time, code, subcode,
|
||||||
realTime=False):
|
payload, sysExChannel=0x7F, realTime=False):
|
||||||
'''
|
'''
|
||||||
Add a Universal SysEx event.
|
Add a Universal SysEx event.
|
||||||
'''
|
'''
|
||||||
self.eventList.append(MIDITrack.UniversalSysExEvent(time, realTime, \
|
self.eventList.append(
|
||||||
sysExChannel, code, subcode, payload))
|
MIDITrack.UniversalSysExEvent(time, realTime, sysExChannel,
|
||||||
|
code, subcode, payload))
|
||||||
|
|
||||||
def addProgramChange(self, channel, time, program):
|
def addProgramChange(self, channel, time, program):
|
||||||
'''
|
'''
|
||||||
@ -273,21 +292,25 @@ class MIDITrack:
|
|||||||
'''
|
'''
|
||||||
Add a track name event.
|
Add a track name event.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
self.eventList.append(MIDITrack.trackName(time, trackName))
|
self.eventList.append(MIDITrack.trackName(time, trackName))
|
||||||
|
|
||||||
def changeNoteTuning(self, tunings, sysExChannel=0x7F, realTime=False, \
|
def changeNoteTuning(self, tunings, sysExChannel=0x7F,
|
||||||
tuningProgam=0):
|
realTime=False, tuningProgam=0):
|
||||||
'''Change the tuning of MIDI notes
|
'''Change the tuning of MIDI notes
|
||||||
'''
|
'''
|
||||||
payload = struct.pack('>B', tuningProgam)
|
payload = struct.pack('>B', tuningProgam)
|
||||||
payload = payload + struct.pack('>B', len(tunings))
|
payload = payload + struct.pack('>B', len(tunings))
|
||||||
|
|
||||||
for (noteNumber, frequency) in tunings:
|
for (noteNumber, frequency) in tunings:
|
||||||
payload = payload + struct.pack('>B', noteNumber)
|
payload = payload + struct.pack('>B', noteNumber)
|
||||||
MIDIFreqency = frequencyTransform(frequency)
|
MIDIFreqency = frequencyTransform(frequency)
|
||||||
|
|
||||||
for byte in MIDIFreqency:
|
for byte in MIDIFreqency:
|
||||||
payload = payload + struct.pack('>B', byte)
|
payload = payload + struct.pack('>B', byte)
|
||||||
|
|
||||||
self.eventList.append(MIDITrack.UniversalSysExEvent(0, realTime, sysExChannel,\
|
self.eventList.append(
|
||||||
|
MIDITrack.UniversalSysExEvent(0, realTime, sysExChannel,
|
||||||
8, 2, payload))
|
8, 2, payload))
|
||||||
|
|
||||||
def processEventList(self):
|
def processEventList(self):
|
||||||
@ -299,7 +322,6 @@ class MIDITrack:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
# Loop over all items in the eventList
|
# Loop over all items in the eventList
|
||||||
|
|
||||||
for thing in self.eventList:
|
for thing in self.eventList:
|
||||||
if thing.type == 'note':
|
if thing.type == 'note':
|
||||||
event = MIDIEvent()
|
event = MIDIEvent()
|
||||||
@ -380,9 +402,6 @@ class MIDITrack:
|
|||||||
print "Error in MIDITrack: Unknown event type"
|
print "Error in MIDITrack: Unknown event type"
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
# Assumptions in the code expect the list to be time-sorted.
|
|
||||||
# self.MIDIEventList.sort(lambda x, y: x.time - y.time)
|
|
||||||
|
|
||||||
self.MIDIEventList.sort(lambda x, y: int(1000 * (x.time - y.time)))
|
self.MIDIEventList.sort(lambda x, y: int(1000 * (x.time - y.time)))
|
||||||
|
|
||||||
if self.deinterleave:
|
if self.deinterleave:
|
||||||
@ -392,12 +411,14 @@ class MIDITrack:
|
|||||||
'''
|
'''
|
||||||
Remove duplicates from the eventList.
|
Remove duplicates from the eventList.
|
||||||
|
|
||||||
This function will remove duplicates from the eventList. This is necessary
|
This function will remove duplicates from the eventList. This
|
||||||
because we the MIDI event stream can become confused otherwise.
|
is necessary because we the MIDI event stream can become
|
||||||
|
confused otherwise.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# For this algorithm to work, the events in the eventList must be hashable
|
# For this algorithm to work, the events in the eventList must
|
||||||
# (that is, they must have a __hash__() and __eq__() function defined).
|
# be hashable (that is, they must have a __hash__() and
|
||||||
|
# __eq__() function defined).
|
||||||
|
|
||||||
tempDict = {}
|
tempDict = {}
|
||||||
for item in self.eventList:
|
for item in self.eventList:
|
||||||
@ -405,11 +426,13 @@ class MIDITrack:
|
|||||||
|
|
||||||
self.eventList = tempDict.keys()
|
self.eventList = tempDict.keys()
|
||||||
|
|
||||||
# Sort on type, them on time. Necessary because keys() has no requirement to return
|
# Sort on type, them on time. Necessary because keys() has no
|
||||||
# things in any order.
|
# requirement to return things in any order.
|
||||||
|
|
||||||
self.eventList.sort(lambda x, y: cmp(x.type, y.type))
|
self.eventList.sort(lambda x, y: cmp(x.type, y.type))
|
||||||
self.eventList.sort(lambda x, y: int( 1000 * (x.time - y.time))) #A bit of a hack.
|
|
||||||
|
# A bit of a hack.
|
||||||
|
self.eventList.sort(lambda x, y: int(1000 * (x.time - y.time)))
|
||||||
|
|
||||||
def closeTrack(self):
|
def closeTrack(self):
|
||||||
'''Called to close a track before writing
|
'''Called to close a track before writing
|
||||||
@ -421,14 +444,14 @@ class MIDITrack:
|
|||||||
Called by the parent MIDIFile object.
|
Called by the parent MIDIFile object.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if self.closed == True:
|
if self.closed:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
if self.remdep:
|
if self.remdep:
|
||||||
self.removeDuplicates()
|
self.removeDuplicates()
|
||||||
|
|
||||||
|
|
||||||
self.processEventList()
|
self.processEventList()
|
||||||
|
|
||||||
def writeMIDIStream(self):
|
def writeMIDIStream(self):
|
||||||
@ -437,16 +460,12 @@ class MIDITrack:
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
# Process the events in the eventList
|
# Process the events in the eventList
|
||||||
|
|
||||||
self.writeEventsToStream()
|
self.writeEventsToStream()
|
||||||
|
|
||||||
# Write MIDI close event.
|
# Write MIDI close event.
|
||||||
|
self.MIDIdata += struct.pack('BBBB', 0x00, 0xFF, 0x2F, 0x00)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('BBBB',0x00,0xFF, \
|
|
||||||
0x2F,0x00)
|
|
||||||
|
|
||||||
# Calculate the entire length of the data and write to the header
|
# Calculate the entire length of the data and write to the header
|
||||||
|
|
||||||
self.dataLength = struct.pack('>L', len(self.MIDIdata))
|
self.dataLength = struct.pack('>L', len(self.MIDIdata))
|
||||||
|
|
||||||
def writeEventsToStream(self):
|
def writeEventsToStream(self):
|
||||||
@ -458,93 +477,120 @@ class MIDITrack:
|
|||||||
if event.type == "NoteOn":
|
if event.type == "NoteOn":
|
||||||
code = 0x9 << 4 | event.channel
|
code = 0x9 << 4 | event.channel
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.pitch)
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.volume)
|
self.MIDIdata += struct.pack('>B', event.pitch)
|
||||||
|
self.MIDIdata += struct.pack('>B', event.volume)
|
||||||
elif event.type == "NoteOff":
|
elif event.type == "NoteOff":
|
||||||
code = 0x8 << 4 | event.channel
|
code = 0x8 << 4 | event.channel
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.pitch)
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.volume)
|
self.MIDIdata += struct.pack('>B', event.pitch)
|
||||||
|
self.MIDIdata += struct.pack('>B', event.volume)
|
||||||
elif event.type == "Tempo":
|
elif event.type == "Tempo":
|
||||||
code = 0xFF
|
code = 0xFF
|
||||||
subcode = 0x51
|
subcode = 0x51
|
||||||
fourbite = struct.pack('>L', event.tempo)
|
fourbite = struct.pack('>L', event.tempo)
|
||||||
threebite = fourbite[1:4] # Just discard the MSB
|
threebite = fourbite[1:4] # Just discard the MSB
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',subcode)
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', 0x03) # Data length: 3
|
self.MIDIdata += struct.pack('>B', subcode)
|
||||||
self.MIDIdata = self.MIDIdata + threebite
|
|
||||||
|
# Data length: 3
|
||||||
|
self.MIDIdata += struct.pack('>B', 0x03)
|
||||||
|
|
||||||
|
self.MIDIdata += threebite
|
||||||
elif event.type == 'ProgramChange':
|
elif event.type == 'ProgramChange':
|
||||||
code = 0xC << 4 | event.channel
|
code = 0xC << 4 | event.channel
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.programNumber)
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
|
self.MIDIdata += struct.pack('>B', event.programNumber)
|
||||||
elif event.type == 'TrackName':
|
elif event.type == 'TrackName':
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('B',0xFF) # Meta-event
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('B',0X03) # Event Type
|
# Meta-event
|
||||||
|
self.MIDIdata += struct.pack('B', 0xFF)
|
||||||
|
|
||||||
|
# Event Type
|
||||||
|
self.MIDIdata += struct.pack('B', 0X03)
|
||||||
|
|
||||||
dataLength = len(event.trackName)
|
dataLength = len(event.trackName)
|
||||||
dataLenghtVar = writeVarLength(dataLength)
|
dataLenghtVar = writeVarLength(dataLength)
|
||||||
|
|
||||||
for i in range(0, len(dataLenghtVar)):
|
for i in range(0, len(dataLenghtVar)):
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack("b",dataLenghtVar[i])
|
self.MIDIdata += struct.pack("b", dataLenghtVar[i])
|
||||||
self.MIDIdata = self.MIDIdata + event.trackName
|
|
||||||
|
self.MIDIdata += event.trackName
|
||||||
elif event.type == "ControllerEvent":
|
elif event.type == "ControllerEvent":
|
||||||
code = 0xB << 4 | event.channel
|
code = 0xB << 4 | event.channel
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',code)
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.eventType)
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.paramerter1)
|
self.MIDIdata += struct.pack('>B', event.eventType)
|
||||||
|
self.MIDIdata += struct.pack('>B', event.paramerter1)
|
||||||
elif event.type == "SysEx":
|
elif event.type == "SysEx":
|
||||||
code = 0xF0
|
code = 0xF0
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', code)
|
|
||||||
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
|
|
||||||
payloadLength = writeVarLength(len(event.payload) + 2)
|
payloadLength = writeVarLength(len(event.payload) + 2)
|
||||||
for lenByte in payloadLength:
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',lenByte)
|
|
||||||
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', event.manID)
|
for lenByte in payloadLength:
|
||||||
self.MIDIdata = self.MIDIdata + event.payload
|
self.MIDIdata += struct.pack('>B', lenByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',0xF7)
|
|
||||||
|
self.MIDIdata += struct.pack('>B', event.manID)
|
||||||
|
self.MIDIdata += event.payload
|
||||||
|
self.MIDIdata += struct.pack('>B', 0xF7)
|
||||||
elif event.type == "UniversalSysEx":
|
elif event.type == "UniversalSysEx":
|
||||||
code = 0xF0
|
code = 0xF0
|
||||||
varTime = writeVarLength(event.time)
|
varTime = writeVarLength(event.time)
|
||||||
|
|
||||||
for timeByte in varTime:
|
for timeByte in varTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',timeByte)
|
self.MIDIdata += struct.pack('>B', timeByte)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', code)
|
|
||||||
|
self.MIDIdata += struct.pack('>B', code)
|
||||||
|
|
||||||
# Do we need to add a length?
|
# Do we need to add a length?
|
||||||
payloadLength = writeVarLength(len(event.payload) + 5)
|
payloadLength = writeVarLength(len(event.payload) + 5)
|
||||||
|
|
||||||
for lenByte in payloadLength:
|
for lenByte in payloadLength:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',lenByte)
|
self.MIDIdata += struct.pack('>B', lenByte)
|
||||||
|
|
||||||
if event.realTime:
|
if event.realTime:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', 0x7F)
|
self.MIDIdata += struct.pack('>B', 0x7F)
|
||||||
else:
|
else:
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', 0x7E)
|
self.MIDIdata += struct.pack('>B', 0x7E)
|
||||||
|
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', event.sysExChannel)
|
self.MIDIdata += struct.pack('>B', event.sysExChannel)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', event.code)
|
self.MIDIdata += struct.pack('>B', event.code)
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B', event.subcode)
|
self.MIDIdata += struct.pack('>B', event.subcode)
|
||||||
self.MIDIdata = self.MIDIdata + event.payload
|
self.MIDIdata += event.payload
|
||||||
self.MIDIdata = self.MIDIdata + struct.pack('>B',0xF7)
|
self.MIDIdata += struct.pack('>B', 0xF7)
|
||||||
|
|
||||||
def deInterleaveNotes(self):
|
def deInterleaveNotes(self):
|
||||||
'''Correct Interleaved notes.
|
'''Correct Interleaved notes.
|
||||||
@ -559,16 +605,17 @@ class MIDITrack:
|
|||||||
stack = {}
|
stack = {}
|
||||||
|
|
||||||
for event in self.MIDIEventList:
|
for event in self.MIDIEventList:
|
||||||
|
|
||||||
if event.type == 'NoteOn':
|
if event.type == 'NoteOn':
|
||||||
if stack.has_key(str(event.pitch)+str(event.channel)):
|
if str(event.pitch) + str(event.channel) in stack:
|
||||||
stack[str(event.pitch)+str(event.channel)].append(event.time)
|
stack[str(event.pitch) + str(event.channel)] \
|
||||||
|
.append(event.time)
|
||||||
else:
|
else:
|
||||||
stack[str(event.pitch)+str(event.channel)] = [event.time]
|
stack[str(event.pitch)+str(event.channel)] = [event.time]
|
||||||
tempEventList.append(event)
|
tempEventList.append(event)
|
||||||
elif event.type == 'NoteOff':
|
elif event.type == 'NoteOff':
|
||||||
if len(stack[str(event.pitch)+str(event.channel)]) > 1:
|
if len(stack[str(event.pitch)+str(event.channel)]) > 1:
|
||||||
event.time = stack[str(event.pitch)+str(event.channel)].pop()
|
event.time = stack[str(event.pitch) + str(event.channel)] \
|
||||||
|
.pop()
|
||||||
tempEventList.append(event)
|
tempEventList.append(event)
|
||||||
else:
|
else:
|
||||||
stack[str(event.pitch)+str(event.channel)].pop()
|
stack[str(event.pitch)+str(event.channel)].pop()
|
||||||
@ -578,11 +625,12 @@ class MIDITrack:
|
|||||||
|
|
||||||
self.MIDIEventList = tempEventList
|
self.MIDIEventList = tempEventList
|
||||||
|
|
||||||
# A little trickery here. We want to make sure that NoteOff events appear
|
# A little trickery here. We want to make sure that NoteOff
|
||||||
# before NoteOn events, so we'll do two sorts -- on on type, one on time.
|
# events appear before NoteOn events, so we'll do two sorts --
|
||||||
# This may have to be revisited, as it makes assumptions about how
|
# on on type, one on time. This may have to be revisited, as
|
||||||
# the internal sort works, and is in essence creating a sort on a primary
|
# it makes assumptions about how the internal sort works, and
|
||||||
# and secondary key.
|
# is in essence creating a sort on a primary and secondary
|
||||||
|
# key.
|
||||||
|
|
||||||
self.MIDIEventList.sort(lambda x, y: cmp(x.type, y.type))
|
self.MIDIEventList.sort(lambda x, y: cmp(x.type, y.type))
|
||||||
self.MIDIEventList.sort(lambda x, y: int(1000 * (x.time - y.time)))
|
self.MIDIEventList.sort(lambda x, y: int(1000 * (x.time - y.time)))
|
||||||
@ -630,8 +678,10 @@ class MIDIHeader:
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
def __init__(self, numTracks):
|
def __init__(self, numTracks):
|
||||||
''' Initialize the data structures
|
|
||||||
'''
|
'''
|
||||||
|
Initialize the data structures
|
||||||
|
'''
|
||||||
|
|
||||||
self.headerString = struct.pack('cccc', 'M', 'T', 'h', 'd')
|
self.headerString = struct.pack('cccc', 'M', 'T', 'h', 'd')
|
||||||
self.headerSize = struct.pack('>L', 6)
|
self.headerSize = struct.pack('>L', 6)
|
||||||
# Format 1 = multi-track file
|
# Format 1 = multi-track file
|
||||||
@ -639,7 +689,6 @@ class MIDIHeader:
|
|||||||
self.numTracks = struct.pack('>H', numTracks)
|
self.numTracks = struct.pack('>H', numTracks)
|
||||||
self.ticksPerBeat = struct.pack('>H', TICKSPERBEAT)
|
self.ticksPerBeat = struct.pack('>H', TICKSPERBEAT)
|
||||||
|
|
||||||
|
|
||||||
def writeFile(self, fileHandle):
|
def writeFile(self, fileHandle):
|
||||||
fileHandle.write(self.headerString)
|
fileHandle.write(self.headerString)
|
||||||
fileHandle.write(self.headerSize)
|
fileHandle.write(self.headerSize)
|
||||||
@ -647,8 +696,10 @@ class MIDIHeader:
|
|||||||
fileHandle.write(self.numTracks)
|
fileHandle.write(self.numTracks)
|
||||||
fileHandle.write(self.ticksPerBeat)
|
fileHandle.write(self.ticksPerBeat)
|
||||||
|
|
||||||
|
|
||||||
class MIDIFile:
|
class MIDIFile:
|
||||||
'''Class that represents a full, well-formed MIDI pattern.
|
'''
|
||||||
|
Class that represents a full, well-formed MIDI pattern.
|
||||||
|
|
||||||
This is a container object that contains a header, one or more tracks,
|
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.
|
and the data associated with a proper and well-formed MIDI pattern.
|
||||||
@ -665,13 +716,15 @@ class MIDIFile:
|
|||||||
|
|
||||||
tracks: The number of tracks this object contains
|
tracks: The number of tracks this object contains
|
||||||
|
|
||||||
removeDuplicates: If true (the default), the software will remove duplicate
|
removeDuplicates: If true (the default), the software will
|
||||||
events which have been added. For example, two notes at the same channel,
|
remove duplicate events which have been added. For example,
|
||||||
time, pitch, and duration would be considered duplicate.
|
two notes at the same channel, time, pitch, and duration would
|
||||||
|
be considered duplicate.
|
||||||
|
|
||||||
deinterleave: If True (the default), overlapping notes (same pitch, same
|
deinterleave: If True (the default), overlapping notes (same
|
||||||
channel) will be modified so that they do not overlap. Otherwise the sequencing
|
pitch, same channel) will be modified so that they do not
|
||||||
software will need to figure out how to interpret NoteOff events upon playback.
|
overlap. Otherwise the sequencing software will need to figure
|
||||||
|
out how to interpret NoteOff events upon playback.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, numTracks, removeDuplicates=True, deinterleave=True):
|
def __init__(self, numTracks, removeDuplicates=True, deinterleave=True):
|
||||||
@ -687,9 +740,8 @@ class MIDIFile:
|
|||||||
for i in range(0, numTracks):
|
for i in range(0, numTracks):
|
||||||
self.tracks.append(MIDITrack(removeDuplicates, deinterleave))
|
self.tracks.append(MIDITrack(removeDuplicates, deinterleave))
|
||||||
|
|
||||||
|
# Public Functions. These (for the most part) wrap the MIDITrack
|
||||||
# Public Functions. These (for the most part) wrap the MIDITrack functions, where most
|
# functions, where most Processing takes place.
|
||||||
# Processing takes place.
|
|
||||||
|
|
||||||
def addNote(self, track, channel, pitch, time, duration, volume):
|
def addNote(self, track, channel, pitch, time, duration, volume):
|
||||||
"""
|
"""
|
||||||
@ -706,7 +758,8 @@ class MIDIFile:
|
|||||||
duration: the duration of the note (in beats) [Float].
|
duration: the duration of the note (in beats) [Float].
|
||||||
volume: the volume (velocity) of the note. [Integer, 0-127].
|
volume: the volume (velocity) of the note. [Integer, 0-127].
|
||||||
"""
|
"""
|
||||||
self.tracks[track].addNoteByNumber(channel, pitch, time, duration, volume)
|
self.tracks[track].addNoteByNumber(channel, pitch, time,
|
||||||
|
duration, volume)
|
||||||
|
|
||||||
def addTrackName(self, track, time, trackName):
|
def addTrackName(self, track, time, trackName):
|
||||||
"""
|
"""
|
||||||
@ -756,24 +809,28 @@ class MIDIFile:
|
|||||||
Add a MIDI controller event.
|
Add a MIDI controller event.
|
||||||
|
|
||||||
Use:
|
Use:
|
||||||
MyMIDI.addControllerEvent(track, channel, time, eventType, parameter1)
|
MyMIDI.addControllerEvent(track, channel, time,
|
||||||
|
eventType, parameter1)
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
track: The track to which the event is added. [Integer, 0-127].
|
track: The track to which the event is added. [Integer, 0-127].
|
||||||
channel: The channel the event is assigned to. [Integer, 0-15].
|
channel: The channel the event is assigned to. [Integer, 0-15].
|
||||||
time: The time at which the event is added, in beats. [Float].
|
time: The time at which the event is added, in beats. [Float].
|
||||||
eventType: the controller event type.
|
eventType: the controller event type.
|
||||||
parameter1: The event's parameter. The meaning of which varies by event type.
|
parameter1: The event's parameter. The meaning of which
|
||||||
|
varies by event type.
|
||||||
"""
|
"""
|
||||||
self.tracks[track].addControllerEvent(channel,time,eventType, paramerter1)
|
self.tracks[track].addControllerEvent(channel, time,
|
||||||
|
eventType, paramerter1)
|
||||||
|
|
||||||
def changeNoteTuning(self, track, tunings, sysExChannel=0x7F, \
|
def changeNoteTuning(self, track, tunings, sysExChannel=0x7F,
|
||||||
realTime=False, tuningProgam=0):
|
realTime=False, tuningProgam=0):
|
||||||
"""
|
"""
|
||||||
Change a note's tuning using SysEx change tuning program.
|
Change a note's tuning using SysEx change tuning program.
|
||||||
|
|
||||||
Use:
|
Use:
|
||||||
MyMIDI.changeNoteTuning(track,[tunings],realTime=False, tuningProgram=0)
|
MyMIDI.changeNoteTuning(track,[tunings],realTime=False,
|
||||||
|
tuningProgram=0)
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
track: The track to which the event is added. [Integer, 0-127].
|
track: The track to which the event is added. [Integer, 0-127].
|
||||||
@ -781,14 +838,16 @@ class MIDIFile:
|
|||||||
[[(Integer,Float]]
|
[[(Integer,Float]]
|
||||||
realTime: Boolean which sets the real-time flag. Defaults to false.
|
realTime: Boolean which sets the real-time flag. Defaults to false.
|
||||||
sysExChannel: do note use (see below).
|
sysExChannel: do note use (see below).
|
||||||
tuningProgram: Tuning program to assign. Defaults to zero. [Integer, 0-127]
|
tuningProgram: Tuning program to assign. Defaults to
|
||||||
|
zero. [Integer, 0-127]
|
||||||
|
|
||||||
In general the sysExChannel should not be changed (parameter will be depreciated).
|
In general the sysExChannel should not be changed (parameter
|
||||||
|
will be depreciated).
|
||||||
|
|
||||||
Also note that many software packages and hardware packages do not implement
|
Also note that many software packages and hardware packages do
|
||||||
this standard!
|
not implement this standard!
|
||||||
"""
|
"""
|
||||||
self.tracks[track].changeNoteTuning(tunings, sysExChannel, realTime,\
|
self.tracks[track].changeNoteTuning(tunings, sysExChannel, realTime,
|
||||||
tuningProgam)
|
tuningProgam)
|
||||||
|
|
||||||
def writeFile(self, fileHandle):
|
def writeFile(self, fileHandle):
|
||||||
@ -804,7 +863,8 @@ class MIDIFile:
|
|||||||
|
|
||||||
self.header.writeFile(fileHandle)
|
self.header.writeFile(fileHandle)
|
||||||
|
|
||||||
#Close the tracks and have them create the MIDI event data structures.
|
# Close the tracks and have them create the MIDI event data
|
||||||
|
# structures.
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
# Write the MIDI Events to file.
|
# Write the MIDI Events to file.
|
||||||
@ -831,7 +891,7 @@ class MIDIFile:
|
|||||||
"""
|
"""
|
||||||
self.tracks[track].addSysEx(time, manID, payload)
|
self.tracks[track].addSysEx(time, manID, payload)
|
||||||
|
|
||||||
def addUniversalSysEx(self,track, time,code, subcode, payload, \
|
def addUniversalSysEx(self, track, time, code, subcode, payload,
|
||||||
sysExChannel=0x7F, realTime=False):
|
sysExChannel=0x7F, realTime=False):
|
||||||
"""
|
"""
|
||||||
Add a Universal SysEx event.
|
Add a Universal SysEx event.
|
||||||
@ -849,23 +909,27 @@ class MIDIFile:
|
|||||||
sysExChannel: The SysEx channel.
|
sysExChannel: The SysEx channel.
|
||||||
realTime: Sets the real-time flag. Defaults to zero.
|
realTime: Sets the real-time flag. Defaults to zero.
|
||||||
|
|
||||||
Note: This is a low-level MIDI function, so care must be used in
|
Note: This is a low-level MIDI function, so care must be used
|
||||||
constructing the payload. It is recommended that higher-level helper
|
in constructing the payload. It is recommended that
|
||||||
functions be written to wrap this function and construct the payload if
|
higher-level helper functions be written to wrap this function
|
||||||
a developer finds him or herself using the function heavily. As an example
|
and construct the payload if a developer finds him or herself
|
||||||
of such a helper function, see the changeNoteTuning function, both here and
|
using the function heavily. As an example of such a helper
|
||||||
in MIDITrack.
|
function, see the changeNoteTuning function, both here and in
|
||||||
|
MIDITrack.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.tracks[track].addUniversalSysEx(time,code, subcode, payload, sysExChannel,\
|
self.tracks[track].addUniversalSysEx(time, code, subcode, payload,
|
||||||
realTime)
|
sysExChannel, realTime)
|
||||||
|
|
||||||
def shiftTracks(self, offset=0):
|
def shiftTracks(self, offset=0):
|
||||||
"""Shift tracks to be zero-origined, or origined at offset.
|
"""
|
||||||
|
Shift tracks to be zero-origined, or origined at offset.
|
||||||
|
|
||||||
Note that the shifting of the time in the tracks uses the MIDIEventList -- in other
|
Note that the shifting of the time in the tracks uses the
|
||||||
words it is assumed to be called in the stage where the MIDIEventList has been
|
MIDIEventList -- in other words it is assumed to be called in
|
||||||
created. This function, however, it meant to operate on the eventList itself.
|
the stage where the MIDIEventList has been created. This
|
||||||
|
function, however, it meant to operate on the eventList
|
||||||
|
itself.
|
||||||
"""
|
"""
|
||||||
origin = 1000000 # A little silly, but we'll assume big enough
|
origin = 1000000 # A little silly, but we'll assume big enough
|
||||||
|
|
||||||
@ -877,13 +941,10 @@ class MIDIFile:
|
|||||||
|
|
||||||
for track in self.tracks:
|
for track in self.tracks:
|
||||||
tempEventList = []
|
tempEventList = []
|
||||||
#runningTime = 0
|
|
||||||
|
|
||||||
for event in track.eventList:
|
for event in track.eventList:
|
||||||
adjustedTime = event.time - origin
|
adjustedTime = event.time - origin
|
||||||
#event.time = adjustedTime - runningTime + offset
|
|
||||||
event.time = adjustedTime + offset
|
event.time = adjustedTime + offset
|
||||||
#runningTime = adjustedTime
|
|
||||||
tempEventList.append(event)
|
tempEventList.append(event)
|
||||||
|
|
||||||
track.eventList = tempEventList
|
track.eventList = tempEventList
|
||||||
@ -891,19 +952,23 @@ class MIDIFile:
|
|||||||
# End Public Functions ########################
|
# End Public Functions ########################
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
'''Close the MIDIFile for further writing.
|
'''
|
||||||
|
Close the MIDIFile for further writing.
|
||||||
|
|
||||||
To close the File for events, we must close the tracks, adjust the time to be
|
To close the File for events, we must close the tracks, adjust
|
||||||
zero-origined, and have the tracks write to their MIDI Stream data structure.
|
the time to be zero-origined, and have the tracks write to
|
||||||
|
their MIDI Stream data structure.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
if self.closed == True:
|
if self.closed:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i in range(0, self.numTracks):
|
for i in range(0, self.numTracks):
|
||||||
self.tracks[i].closeTrack()
|
self.tracks[i].closeTrack()
|
||||||
# We want things like program changes to come before notes when they are at the
|
|
||||||
# same time, so we sort the MIDI events by their ordinality
|
# We want things like program changes to come before notes
|
||||||
|
# when they are at the same time, so we sort the MIDI
|
||||||
|
# events by their ordinality
|
||||||
self.tracks[i].MIDIEventList.sort()
|
self.tracks[i].MIDIEventList.sort()
|
||||||
|
|
||||||
origin = self.findOrigin()
|
origin = self.findOrigin()
|
||||||
@ -914,31 +979,36 @@ class MIDIFile:
|
|||||||
|
|
||||||
self.closed = True
|
self.closed = True
|
||||||
|
|
||||||
|
|
||||||
def findOrigin(self):
|
def findOrigin(self):
|
||||||
'''Find the earliest time in the file's tracks.append.
|
'''
|
||||||
|
Find the earliest time in the file's tracks.append.
|
||||||
'''
|
'''
|
||||||
origin = 1000000 # A little silly, but we'll assume big enough
|
origin = 1000000 # A little silly, but we'll assume big enough
|
||||||
|
|
||||||
# Note: This code assumes that the MIDIEventList has been sorted, so this should be insured
|
# Note: This code assumes that the MIDIEventList has been
|
||||||
# before it is called. It is probably a poor design to do this.
|
# sorted, so this should be insured before it is
|
||||||
# TODO: -- Consider making this less efficient but more robust by not assuming the list to be sorted.
|
# called. It is probably a poor design to do this.
|
||||||
|
# TODO: Consider making this less efficient but more robust
|
||||||
|
# by not assuming the list to be sorted.
|
||||||
|
|
||||||
for track in self.tracks:
|
for track in self.tracks:
|
||||||
if len(track.MIDIEventList) > 0:
|
if len(track.MIDIEventList) > 0:
|
||||||
if track.MIDIEventList[0].time < origin:
|
if track.MIDIEventList[0].time < origin:
|
||||||
origin = track.MIDIEventList[0].time
|
origin = track.MIDIEventList[0].time
|
||||||
|
|
||||||
|
|
||||||
return origin
|
return origin
|
||||||
|
|
||||||
def writeVarLength(i):
|
|
||||||
'''Accept an input, and write a MIDI-compatible variable length stream
|
|
||||||
|
|
||||||
The MIDI format is a little strange, and makes use of so-called variable
|
def writeVarLength(i):
|
||||||
length quantities. These quantities are a stream of bytes. If the most
|
'''
|
||||||
significant bit is 1, then more bytes follow. If it is zero, then the
|
Accept an input, and write a MIDI-compatible variable length
|
||||||
byte in question is the last in the stream
|
stream
|
||||||
|
|
||||||
|
The MIDI format is a little strange, and makes use of so-called
|
||||||
|
variable length quantities. These quantities are a stream of
|
||||||
|
bytes. If the most significant bit is 1, then more bytes
|
||||||
|
follow. If it is zero, then the byte in question is the last in
|
||||||
|
the stream
|
||||||
'''
|
'''
|
||||||
input = int(i)
|
input = int(i)
|
||||||
output = [0, 0, 0, 0]
|
output = [0, 0, 0, 0]
|
||||||
@ -948,6 +1018,7 @@ def writeVarLength(i):
|
|||||||
output[count] = result
|
output[count] = result
|
||||||
count = count + 1
|
count = count + 1
|
||||||
input = input >> 7
|
input = input >> 7
|
||||||
|
|
||||||
while input > 0:
|
while input > 0:
|
||||||
result = input & 0x7F
|
result = input & 0x7F
|
||||||
result = result | 0x80
|
result = result | 0x80
|
||||||
@ -959,20 +1030,26 @@ def writeVarLength(i):
|
|||||||
reversed[1] = output[2]
|
reversed[1] = output[2]
|
||||||
reversed[2] = output[1]
|
reversed[2] = output[1]
|
||||||
reversed[3] = output[0]
|
reversed[3] = output[0]
|
||||||
|
|
||||||
return reversed[4-count:4]
|
return reversed[4-count:4]
|
||||||
|
|
||||||
|
|
||||||
def frequencyTransform(freq):
|
def frequencyTransform(freq):
|
||||||
'''Returns a three-byte transform of a frequencyTransform
|
|
||||||
'''
|
'''
|
||||||
|
Returns a three-byte transform of a frequencyTransform
|
||||||
|
'''
|
||||||
|
|
||||||
resolution = 16384
|
resolution = 16384
|
||||||
freq = float(freq)
|
freq = float(freq)
|
||||||
dollars = 69 + 12 * math.log(freq/(float(440)), 2)
|
dollars = 69 + 12 * math.log(freq/(float(440)), 2)
|
||||||
firstByte = int(dollars)
|
firstByte = int(dollars)
|
||||||
lowerFreq = 440 * pow(2.0, ((float(firstByte) - 69.0)/12.0))
|
lowerFreq = 440 * pow(2.0, ((float(firstByte) - 69.0)/12.0))
|
||||||
|
|
||||||
if freq != lowerFreq:
|
if freq != lowerFreq:
|
||||||
centDif = 1200 * math.log((freq/lowerFreq), 2)
|
centDif = 1200 * math.log((freq/lowerFreq), 2)
|
||||||
else:
|
else:
|
||||||
centDif = 0
|
centDif = 0
|
||||||
|
|
||||||
cents = round(centDif/100 * resolution) # round?
|
cents = round(centDif/100 * resolution) # round?
|
||||||
secondByte = min([int(cents) >> 7, 0x7F])
|
secondByte = min([int(cents) >> 7, 0x7F])
|
||||||
thirdByte = cents - (secondByte << 7)
|
thirdByte = cents - (secondByte << 7)
|
||||||
@ -983,11 +1060,17 @@ def frequencyTransform(freq):
|
|||||||
thirdByte = int(thirdByte)
|
thirdByte = int(thirdByte)
|
||||||
return [firstByte, secondByte, thirdByte]
|
return [firstByte, secondByte, thirdByte]
|
||||||
|
|
||||||
|
|
||||||
def returnFrequency(freqBytes):
|
def returnFrequency(freqBytes):
|
||||||
'''The reverse of frequencyTransform. Given a byte stream, return a frequency.
|
|
||||||
'''
|
'''
|
||||||
|
The reverse of frequencyTransform. Given a byte stream, return a
|
||||||
|
frequency.
|
||||||
|
'''
|
||||||
|
|
||||||
resolution = 16384.0
|
resolution = 16384.0
|
||||||
baseFrequency = 440 * pow(2.0, (float(freqBytes[0]-69.0)/12.0))
|
baseFrequency = 440 * pow(2.0, (float(freqBytes[0]-69.0)/12.0))
|
||||||
frac = (float((int(freqBytes[1]) << 7) + int(freqBytes[2])) * 100.0) / resolution
|
frac = (float((int(freqBytes[1]) << 7) +
|
||||||
|
int(freqBytes[2])) * 100.0) / resolution
|
||||||
frequency = baseFrequency * pow(2.0, frac/1200.0)
|
frequency = baseFrequency * pow(2.0, frac/1200.0)
|
||||||
|
|
||||||
return frequency
|
return frequency
|
||||||
|
Loading…
Reference in New Issue
Block a user