Add some PEP8 happiness

This commit is contained in:
Gergely Polonkai 2016-05-27 17:09:34 +02:00
parent fc5cd7fdbd
commit 489c9c4494
1 changed files with 503 additions and 420 deletions

View File

@ -10,17 +10,22 @@
# 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
# corresponds to one beat. This number is somewhat arbitrary, but should be chosen
# to provide adequate temporal resolution.
# TICKSPERBEAT is the number of "ticks" (time measurement in the MIDI
# file) that corresponds to one beat. This number is somewhat
# arbitrary, but should be chosen to provide adequate temporal
# resolution.
TICKSPERBEAT = 128
controllerEventTypes = {
'pan': 0x0a
}
class MIDIEvent:
'''
The class to contain the MIDI Event (placed on MIDIEventList.
@ -44,36 +49,40 @@ class MIDIEvent:
else:
return 0
class GenericEvent():
'''The event class from which specific events are derived
'''
The event class from which specific events are derived
'''
def __init__(self, time):
self.time = time
self.type = 'Unknown'
def __eq__(self, other):
'''
Equality operator for Generic Events and derived classes.
In the processing of the event list, we have need to remove duplicates. To do this
we rely on the fact that the classes are hashable, and must therefore have an
equality operator (__hash__() and __eq__() must both be defined).
In the processing of the event list, we have need to remove
duplicates. To do this we rely on the fact that the classes
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
programming would find this almost unbelievable. Here we have a base class that
knows specifics about derived classes, thus breaking the very spirit of
OO programming.
This is the most embarrassing portion of the code, and anyone
who knows about OO programming would find this almost
unbelievable. Here we have a base class that knows specifics
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
classes altogether. At some point perhaps I will.
I suppose I should go back and restructure the code, perhaps
removing the derived classes altogether. At some point perhaps
I will.
'''
if self.time != other.time or self.type != other.type:
return False
# What follows is code that encodes the concept of equality for each derived
# class. Believe it f you dare.
# What follows is code that encodes the concept of equality
# for each derived class. Believe it f you dare.
if self.type == 'note':
if self.pitch != other.pitch or self.channel != other.channel:
@ -82,7 +91,8 @@ class GenericEvent():
if self.tempo != other.tempo:
return False
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
if self.type == 'trackName':
if self.trackName != other.trackName:
@ -92,6 +102,7 @@ class GenericEvent():
self.parameter2 != other.parameter2 or \
self.channel != other.channel or \
self.eventType != other.eventType:
return False
if self.type == 'SysEx':
@ -102,6 +113,7 @@ class GenericEvent():
if self.code != other.code or \
self.subcode != other.subcode or \
self.sysExChannel != other.sysExChannel:
return False
return True
@ -110,10 +122,13 @@ class GenericEvent():
'''
Return a hash code for the object.
This is needed for the removal of duplicate objects from the event list. The only
real requirement for the algorithm is that the hash of equal objects must be equal.
There is probably great opportunity for improvements in the hashing function.
This is needed for the removal of duplicate objects from the
event list. The only real requirement for the algorithm is
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.
a = int(self.time)
a = (a + 0x7ed55d16) + (a << 12)
@ -122,8 +137,10 @@ class GenericEvent():
a = (a + 0xd3a2646c) ^ (a << 9)
a = (a + 0xfd7046c5) + (a << 3)
a = (a ^ 0xb55a4f09) ^ (a >> 16)
return a
class MIDITrack:
'''A class that encapsulates a MIDI track
'''
@ -132,8 +149,8 @@ class MIDITrack:
class note(GenericEvent):
'''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)
self.pitch = pitch
self.duration = duration
@ -154,12 +171,11 @@ class MIDITrack:
else:
return False
class tempo(GenericEvent):
'''A class that encapsulates a tempo meta-event
'''
def __init__(self,time,tempo):
def __init__(self, time, tempo):
GenericEvent.__init__(self, time)
self.type = 'tempo'
self.tempo = int(60000000 / tempo)
@ -188,7 +204,9 @@ class MIDITrack:
'''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,)
self.type = 'UniversalSysEx'
self.realTime = realTime
@ -201,7 +219,7 @@ class MIDITrack:
'''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,)
self.type = 'controllerEvent'
self.parameter1 = parameter1
@ -217,7 +235,6 @@ class MIDITrack:
self.type = 'trackName'
self.trackName = trackName
def __init__(self, removeDuplicates, deinterleave):
'''Initialize the MIDITrack object.
'''
@ -231,17 +248,18 @@ class MIDITrack:
self.deinterleave = deinterleave
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):
'''
Add a controller event.
'''
self.eventList.append(MIDITrack.ControllerEvent(channel,time,eventType, \
paramerter1))
self.eventList.append(
MIDITrack.ControllerEvent(channel, time, eventType, paramerter1))
def addTempo(self, time, tempo):
'''
@ -255,13 +273,14 @@ class MIDITrack:
'''
self.eventList.append(MIDITrack.SysExEvent(time, manID, payload))
def addUniversalSysEx(self,time,code, subcode, payload, sysExChannel=0x7F, \
realTime=False):
def addUniversalSysEx(self, time, code, subcode,
payload, sysExChannel=0x7F, realTime=False):
'''
Add a Universal SysEx event.
'''
self.eventList.append(MIDITrack.UniversalSysExEvent(time, realTime, \
sysExChannel, code, subcode, payload))
self.eventList.append(
MIDITrack.UniversalSysExEvent(time, realTime, sysExChannel,
code, subcode, payload))
def addProgramChange(self, channel, time, program):
'''
@ -273,21 +292,25 @@ class MIDITrack:
'''
Add a track name event.
'''
self.eventList.append(MIDITrack.trackName(time, trackName))
def changeNoteTuning(self, tunings, sysExChannel=0x7F, realTime=False, \
tuningProgam=0):
def changeNoteTuning(self, tunings, sysExChannel=0x7F,
realTime=False, tuningProgam=0):
'''Change the tuning of MIDI notes
'''
payload = struct.pack('>B', tuningProgam)
payload = payload + struct.pack('>B', len(tunings))
for (noteNumber, frequency) in tunings:
payload = payload + struct.pack('>B', noteNumber)
MIDIFreqency = frequencyTransform(frequency)
for byte in MIDIFreqency:
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))
def processEventList(self):
@ -299,7 +322,6 @@ class MIDITrack:
'''
# Loop over all items in the eventList
for thing in self.eventList:
if thing.type == 'note':
event = MIDIEvent()
@ -380,9 +402,6 @@ class MIDITrack:
print "Error in MIDITrack: Unknown event type"
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)))
if self.deinterleave:
@ -392,12 +411,14 @@ class MIDITrack:
'''
Remove duplicates from the eventList.
This function will remove duplicates from the eventList. This is necessary
because we the MIDI event stream can become confused otherwise.
This function will remove duplicates from the eventList. This
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
# (that is, they must have a __hash__() and __eq__() function defined).
# For this algorithm to work, the events in the eventList must
# be hashable (that is, they must have a __hash__() and
# __eq__() function defined).
tempDict = {}
for item in self.eventList:
@ -405,11 +426,13 @@ class MIDITrack:
self.eventList = tempDict.keys()
# Sort on type, them on time. Necessary because keys() has no requirement to return
# things in any order.
# Sort on type, them on time. Necessary because keys() has no
# requirement to return things in any order.
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):
'''Called to close a track before writing
@ -421,14 +444,14 @@ class MIDITrack:
Called by the parent MIDIFile object.
'''
if self.closed == True:
if self.closed:
return
self.closed = True
if self.remdep:
self.removeDuplicates()
self.processEventList()
def writeMIDIStream(self):
@ -437,16 +460,12 @@ class MIDITrack:
'''
# Process the events in the eventList
self.writeEventsToStream()
# Write MIDI close event.
self.MIDIdata = self.MIDIdata + struct.pack('BBBB',0x00,0xFF, \
0x2F,0x00)
self.MIDIdata += struct.pack('BBBB', 0x00, 0xFF, 0x2F, 0x00)
# Calculate the entire length of the data and write to the header
self.dataLength = struct.pack('>L', len(self.MIDIdata))
def writeEventsToStream(self):
@ -458,93 +477,120 @@ class MIDITrack:
if event.type == "NoteOn":
code = 0x9 << 4 | event.channel
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',event.pitch)
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.volume)
self.MIDIdata += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
self.MIDIdata += struct.pack('>B', event.pitch)
self.MIDIdata += struct.pack('>B', event.volume)
elif event.type == "NoteOff":
code = 0x8 << 4 | event.channel
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',event.pitch)
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.volume)
self.MIDIdata += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
self.MIDIdata += struct.pack('>B', event.pitch)
self.MIDIdata += struct.pack('>B', event.volume)
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) # Data length: 3
self.MIDIdata = self.MIDIdata + threebite
self.MIDIdata += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
self.MIDIdata += struct.pack('>B', subcode)
# Data length: 3
self.MIDIdata += struct.pack('>B', 0x03)
self.MIDIdata += threebite
elif event.type == 'ProgramChange':
code = 0xC << 4 | event.channel
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',event.programNumber)
self.MIDIdata += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
self.MIDIdata += struct.pack('>B', event.programNumber)
elif event.type == 'TrackName':
varTime = writeVarLength(event.time)
for timeByte in varTime:
self.MIDIdata = 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
self.MIDIdata += struct.pack('>B', timeByte)
# Meta-event
self.MIDIdata += struct.pack('B', 0xFF)
# Event Type
self.MIDIdata += struct.pack('B', 0X03)
dataLength = len(event.trackName)
dataLenghtVar = writeVarLength(dataLength)
for i in range(0, len(dataLenghtVar)):
self.MIDIdata = self.MIDIdata + struct.pack("b",dataLenghtVar[i])
self.MIDIdata = self.MIDIdata + event.trackName
self.MIDIdata += struct.pack("b", dataLenghtVar[i])
self.MIDIdata += event.trackName
elif event.type == "ControllerEvent":
code = 0xB << 4 | event.channel
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',event.eventType)
self.MIDIdata = self.MIDIdata + struct.pack('>B',event.paramerter1)
self.MIDIdata += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
self.MIDIdata += struct.pack('>B', event.eventType)
self.MIDIdata += struct.pack('>B', event.paramerter1)
elif event.type == "SysEx":
code = 0xF0
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 += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
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)
self.MIDIdata = self.MIDIdata + event.payload
self.MIDIdata = self.MIDIdata + struct.pack('>B',0xF7)
for lenByte in payloadLength:
self.MIDIdata += struct.pack('>B', lenByte)
self.MIDIdata += struct.pack('>B', event.manID)
self.MIDIdata += event.payload
self.MIDIdata += struct.pack('>B', 0xF7)
elif event.type == "UniversalSysEx":
code = 0xF0
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 += struct.pack('>B', timeByte)
self.MIDIdata += struct.pack('>B', code)
# Do we need to add a length?
payloadLength = writeVarLength(len(event.payload) + 5)
for lenByte in payloadLength:
self.MIDIdata = self.MIDIdata + struct.pack('>B',lenByte)
self.MIDIdata += struct.pack('>B', lenByte)
if event.realTime:
self.MIDIdata = self.MIDIdata + struct.pack('>B', 0x7F)
self.MIDIdata += struct.pack('>B', 0x7F)
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 = self.MIDIdata + struct.pack('>B', event.code)
self.MIDIdata = self.MIDIdata + struct.pack('>B', event.subcode)
self.MIDIdata = self.MIDIdata + event.payload
self.MIDIdata = self.MIDIdata + struct.pack('>B',0xF7)
self.MIDIdata += struct.pack('>B', event.sysExChannel)
self.MIDIdata += struct.pack('>B', event.code)
self.MIDIdata += struct.pack('>B', event.subcode)
self.MIDIdata += event.payload
self.MIDIdata += struct.pack('>B', 0xF7)
def deInterleaveNotes(self):
'''Correct Interleaved notes.
@ -559,16 +605,17 @@ class MIDITrack:
stack = {}
for event in self.MIDIEventList:
if event.type == 'NoteOn':
if stack.has_key(str(event.pitch)+str(event.channel)):
stack[str(event.pitch)+str(event.channel)].append(event.time)
if str(event.pitch) + str(event.channel) in stack:
stack[str(event.pitch) + str(event.channel)] \
.append(event.time)
else:
stack[str(event.pitch)+str(event.channel)] = [event.time]
tempEventList.append(event)
elif event.type == 'NoteOff':
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)
else:
stack[str(event.pitch)+str(event.channel)].pop()
@ -578,11 +625,12 @@ class MIDITrack:
self.MIDIEventList = tempEventList
# A little trickery here. We want to make sure that NoteOff events appear
# before NoteOn events, so we'll do two sorts -- on on type, one on time.
# This may have to be revisited, as it makes assumptions about how
# the internal sort works, and is in essence creating a sort on a primary
# and secondary key.
# A little trickery here. We want to make sure that NoteOff
# events appear before NoteOn events, so we'll do two sorts --
# on on type, one on time. This may have to be revisited, as
# it makes assumptions about how the internal sort works, and
# 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: int(1000 * (x.time - y.time)))
@ -630,8 +678,10 @@ class MIDIHeader:
'''
def __init__(self, numTracks):
''' Initialize the data structures
'''
Initialize the data structures
'''
self.headerString = struct.pack('cccc', 'M', 'T', 'h', 'd')
self.headerSize = struct.pack('>L', 6)
# Format 1 = multi-track file
@ -639,7 +689,6 @@ class MIDIHeader:
self.numTracks = struct.pack('>H', numTracks)
self.ticksPerBeat = struct.pack('>H', TICKSPERBEAT)
def writeFile(self, fileHandle):
fileHandle.write(self.headerString)
fileHandle.write(self.headerSize)
@ -647,8 +696,10 @@ class MIDIHeader:
fileHandle.write(self.numTracks)
fileHandle.write(self.ticksPerBeat)
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,
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
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.
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.
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.
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.
'''
def __init__(self, numTracks, removeDuplicates=True, deinterleave=True):
@ -687,9 +740,8 @@ class MIDIFile:
for i in range(0, numTracks):
self.tracks.append(MIDITrack(removeDuplicates, deinterleave))
# Public Functions. These (for the most part) wrap the MIDITrack functions, where most
# Processing takes place.
# Public Functions. These (for the most part) wrap the MIDITrack
# functions, where most Processing takes place.
def addNote(self, track, channel, pitch, time, duration, volume):
"""
@ -706,7 +758,8 @@ class MIDIFile:
duration: the duration of the note (in beats) [Float].
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):
"""
@ -756,24 +809,28 @@ class MIDIFile:
Add a MIDI controller event.
Use:
MyMIDI.addControllerEvent(track, channel, time, eventType, parameter1)
MyMIDI.addControllerEvent(track, channel, time,
eventType, parameter1)
Arguments:
track: The track to which the event is added. [Integer, 0-127].
channel: The channel the event is assigned to. [Integer, 0-15].
time: The time at which the event is added, in beats. [Float].
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):
"""
Change a note's tuning using SysEx change tuning program.
Use:
MyMIDI.changeNoteTuning(track,[tunings],realTime=False, tuningProgram=0)
MyMIDI.changeNoteTuning(track,[tunings],realTime=False,
tuningProgram=0)
Arguments:
track: The track to which the event is added. [Integer, 0-127].
@ -781,14 +838,16 @@ class MIDIFile:
[[(Integer,Float]]
realTime: Boolean which sets the real-time flag. Defaults to false.
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
this standard!
Also note that many software packages and hardware packages do
not implement this standard!
"""
self.tracks[track].changeNoteTuning(tunings, sysExChannel, realTime,\
self.tracks[track].changeNoteTuning(tunings, sysExChannel, realTime,
tuningProgam)
def writeFile(self, fileHandle):
@ -804,7 +863,8 @@ class MIDIFile:
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()
# Write the MIDI Events to file.
@ -831,7 +891,7 @@ class MIDIFile:
"""
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):
"""
Add a Universal SysEx event.
@ -849,23 +909,27 @@ class MIDIFile:
sysExChannel: The SysEx channel.
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.
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.
"""
self.tracks[track].addUniversalSysEx(time,code, subcode, payload, sysExChannel,\
realTime)
self.tracks[track].addUniversalSysEx(time, code, subcode, payload,
sysExChannel, realTime)
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
words it is assumed to be called in the stage where the MIDIEventList has been
created. This function, however, it meant to operate on the eventList itself.
Note that the shifting of the time in the tracks uses the
MIDIEventList -- in other words it is assumed to be called in
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
@ -877,13 +941,10 @@ class MIDIFile:
for track in self.tracks:
tempEventList = []
#runningTime = 0
for event in track.eventList:
adjustedTime = event.time - origin
#event.time = adjustedTime - runningTime + offset
event.time = adjustedTime + offset
#runningTime = adjustedTime
tempEventList.append(event)
track.eventList = tempEventList
@ -891,19 +952,23 @@ class MIDIFile:
# End Public Functions ########################
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
zero-origined, and have the tracks write to their MIDI Stream data structure.
To close the File for events, we must close the tracks, adjust
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
for i in range(0, self.numTracks):
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()
origin = self.findOrigin()
@ -914,31 +979,36 @@ class MIDIFile:
self.closed = True
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
# Note: This code assumes that the MIDIEventList has been sorted, so this should be insured
# before it is 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.
# Note: This code assumes that the MIDIEventList has been
# sorted, so this should be insured before it is
# 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:
if len(track.MIDIEventList) > 0:
if track.MIDIEventList[0].time < origin:
origin = track.MIDIEventList[0].time
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
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
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 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)
output = [0, 0, 0, 0]
@ -948,6 +1018,7 @@ def writeVarLength(i):
output[count] = result
count = count + 1
input = input >> 7
while input > 0:
result = input & 0x7F
result = result | 0x80
@ -959,20 +1030,26 @@ def writeVarLength(i):
reversed[1] = output[2]
reversed[2] = output[1]
reversed[3] = output[0]
return reversed[4-count:4]
def frequencyTransform(freq):
'''Returns a three-byte transform of a frequencyTransform
'''
Returns a three-byte transform of a frequencyTransform
'''
resolution = 16384
freq = float(freq)
dollars = 69 + 12 * math.log(freq/(float(440)), 2)
firstByte = int(dollars)
lowerFreq = 440 * pow(2.0, ((float(firstByte) - 69.0)/12.0))
if freq != lowerFreq:
centDif = 1200 * math.log((freq/lowerFreq), 2)
else:
centDif = 0
cents = round(centDif/100 * resolution) # round?
secondByte = min([int(cents) >> 7, 0x7F])
thirdByte = cents - (secondByte << 7)
@ -983,11 +1060,17 @@ def frequencyTransform(freq):
thirdByte = int(thirdByte)
return [firstByte, secondByte, thirdByte]
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
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)
return frequency