1*f81fb7c4SAndroid Build Coastguard Worker""" 2*f81fb7c4SAndroid Build Coastguard Worker File: 3*f81fb7c4SAndroid Build Coastguard Worker midifile.py 4*f81fb7c4SAndroid Build Coastguard Worker 5*f81fb7c4SAndroid Build Coastguard Worker Contents and purpose: 6*f81fb7c4SAndroid Build Coastguard Worker Utilities used throughout JetCreator 7*f81fb7c4SAndroid Build Coastguard Worker 8*f81fb7c4SAndroid Build Coastguard Worker Copyright (c) 2008 Android Open Source Project 9*f81fb7c4SAndroid Build Coastguard Worker 10*f81fb7c4SAndroid Build Coastguard Worker Licensed under the Apache License, Version 2.0 (the "License"); 11*f81fb7c4SAndroid Build Coastguard Worker you may not use this file except in compliance with the License. 12*f81fb7c4SAndroid Build Coastguard Worker You may obtain a copy of the License at 13*f81fb7c4SAndroid Build Coastguard Worker 14*f81fb7c4SAndroid Build Coastguard Worker http://www.apache.org/licenses/LICENSE-2.0 15*f81fb7c4SAndroid Build Coastguard Worker 16*f81fb7c4SAndroid Build Coastguard Worker Unless required by applicable law or agreed to in writing, software 17*f81fb7c4SAndroid Build Coastguard Worker distributed under the License is distributed on an "AS IS" BASIS, 18*f81fb7c4SAndroid Build Coastguard Worker WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 19*f81fb7c4SAndroid Build Coastguard Worker See the License for the specific language governing permissions and 20*f81fb7c4SAndroid Build Coastguard Worker limitations under the License. 21*f81fb7c4SAndroid Build Coastguard Worker""" 22*f81fb7c4SAndroid Build Coastguard Worker 23*f81fb7c4SAndroid Build Coastguard Workerimport logging 24*f81fb7c4SAndroid Build Coastguard Workerimport struct 25*f81fb7c4SAndroid Build Coastguard Workerimport copy 26*f81fb7c4SAndroid Build Coastguard Workerimport array 27*f81fb7c4SAndroid Build Coastguard Worker 28*f81fb7c4SAndroid Build Coastguard Worker# JET events 29*f81fb7c4SAndroid Build Coastguard WorkerJET_EVENT_MARKER = 102 30*f81fb7c4SAndroid Build Coastguard WorkerJET_MARKER_LOOP_END = 0 31*f81fb7c4SAndroid Build Coastguard WorkerJET_EVENT_TRIGGER_CLIP = 103 32*f81fb7c4SAndroid Build Coastguard Worker 33*f81fb7c4SAndroid Build Coastguard Worker# header definitions 34*f81fb7c4SAndroid Build Coastguard WorkerSMF_HEADER_FMT = '>4slHHH' 35*f81fb7c4SAndroid Build Coastguard WorkerSMF_RIFF_TAG = 'MThd' 36*f81fb7c4SAndroid Build Coastguard Worker 37*f81fb7c4SAndroid Build Coastguard WorkerSMF_TRACK_HEADER_FMT = '>4sl' 38*f81fb7c4SAndroid Build Coastguard WorkerSMF_TRACK_RIFF_TAG = 'MTrk' 39*f81fb7c4SAndroid Build Coastguard Worker 40*f81fb7c4SAndroid Build Coastguard Worker# defaults 41*f81fb7c4SAndroid Build Coastguard WorkerDEFAULT_PPQN = 120 42*f81fb7c4SAndroid Build Coastguard WorkerDEFAULT_BEATS_PER_MEASURE = 4 43*f81fb7c4SAndroid Build Coastguard WorkerDEFAULT_TIME_FORMAT = '%03d:%02d:%03d' 44*f81fb7c4SAndroid Build Coastguard Worker 45*f81fb7c4SAndroid Build Coastguard Worker# force note-offs to end of list 46*f81fb7c4SAndroid Build Coastguard WorkerMAX_SEQ_NUM = 0x7fffffff 47*f81fb7c4SAndroid Build Coastguard Worker 48*f81fb7c4SAndroid Build Coastguard Worker# MIDI messages 49*f81fb7c4SAndroid Build Coastguard WorkerNOTE_OFF = 0x80 50*f81fb7c4SAndroid Build Coastguard WorkerNOTE_ON = 0x90 51*f81fb7c4SAndroid Build Coastguard WorkerPOLY_KEY_PRESSURE = 0xa0 52*f81fb7c4SAndroid Build Coastguard WorkerCONTROL_CHANGE = 0xb0 53*f81fb7c4SAndroid Build Coastguard WorkerPROGRAM_CHANGE = 0xc0 54*f81fb7c4SAndroid Build Coastguard WorkerCHANNEL_PRESSURE = 0xd0 55*f81fb7c4SAndroid Build Coastguard WorkerPITCH_BEND = 0xe0 56*f81fb7c4SAndroid Build Coastguard Worker 57*f81fb7c4SAndroid Build Coastguard Worker# System common messages 58*f81fb7c4SAndroid Build Coastguard WorkerSYSEX = 0xf0 59*f81fb7c4SAndroid Build Coastguard WorkerMIDI_TIME_CODE = 0xf1 60*f81fb7c4SAndroid Build Coastguard WorkerSONG_POSITION_POINTER = 0xf2 61*f81fb7c4SAndroid Build Coastguard WorkerSONG_SELECT = 0xf3 62*f81fb7c4SAndroid Build Coastguard WorkerRESERVED_F4 = 0xf4 63*f81fb7c4SAndroid Build Coastguard WorkerRESERVED_F5 = 0xf5 64*f81fb7c4SAndroid Build Coastguard WorkerTUNE_REQUEST = 0xf6 65*f81fb7c4SAndroid Build Coastguard WorkerEND_SYSEX = 0xf7 66*f81fb7c4SAndroid Build Coastguard Worker 67*f81fb7c4SAndroid Build Coastguard Worker# System real-time messages 68*f81fb7c4SAndroid Build Coastguard WorkerTIMING_CLOCK = 0xf8 69*f81fb7c4SAndroid Build Coastguard WorkerRESERVED_F9 = 0xf9 70*f81fb7c4SAndroid Build Coastguard WorkerSTART = 0xfa 71*f81fb7c4SAndroid Build Coastguard WorkerCONTINUE = 0xfb 72*f81fb7c4SAndroid Build Coastguard WorkerSTOP = 0xfc 73*f81fb7c4SAndroid Build Coastguard WorkerRESERVED_FD = 0xfd 74*f81fb7c4SAndroid Build Coastguard WorkerACTIVE_SENSING = 0xfe 75*f81fb7c4SAndroid Build Coastguard WorkerSYSTEM_RESET = 0xff 76*f81fb7c4SAndroid Build Coastguard Worker 77*f81fb7c4SAndroid Build Coastguard WorkerONE_BYTE_MESSAGES = ( 78*f81fb7c4SAndroid Build Coastguard Worker TUNE_REQUEST, 79*f81fb7c4SAndroid Build Coastguard Worker TIMING_CLOCK, 80*f81fb7c4SAndroid Build Coastguard Worker RESERVED_F9, 81*f81fb7c4SAndroid Build Coastguard Worker START, 82*f81fb7c4SAndroid Build Coastguard Worker CONTINUE, 83*f81fb7c4SAndroid Build Coastguard Worker STOP, 84*f81fb7c4SAndroid Build Coastguard Worker RESERVED_FD, 85*f81fb7c4SAndroid Build Coastguard Worker ACTIVE_SENSING, 86*f81fb7c4SAndroid Build Coastguard Worker SYSTEM_RESET) 87*f81fb7c4SAndroid Build Coastguard Worker 88*f81fb7c4SAndroid Build Coastguard WorkerTHREE_BYTE_MESSAGES = ( 89*f81fb7c4SAndroid Build Coastguard Worker NOTE_OFF, 90*f81fb7c4SAndroid Build Coastguard Worker NOTE_ON, 91*f81fb7c4SAndroid Build Coastguard Worker POLY_KEY_PRESSURE, 92*f81fb7c4SAndroid Build Coastguard Worker CONTROL_CHANGE, 93*f81fb7c4SAndroid Build Coastguard Worker PITCH_BEND) 94*f81fb7c4SAndroid Build Coastguard Worker 95*f81fb7c4SAndroid Build Coastguard WorkerMIDI_MESSAGES = ( 96*f81fb7c4SAndroid Build Coastguard Worker NOTE_OFF, 97*f81fb7c4SAndroid Build Coastguard Worker NOTE_ON, 98*f81fb7c4SAndroid Build Coastguard Worker POLY_KEY_PRESSURE, 99*f81fb7c4SAndroid Build Coastguard Worker CONTROL_CHANGE, 100*f81fb7c4SAndroid Build Coastguard Worker CHANNEL_PRESSURE, 101*f81fb7c4SAndroid Build Coastguard Worker PITCH_BEND, 102*f81fb7c4SAndroid Build Coastguard Worker SYSEX) 103*f81fb7c4SAndroid Build Coastguard Worker 104*f81fb7c4SAndroid Build Coastguard Worker# Meta-events 105*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT = 0xff 106*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_SEQUENCE_NUMBER = 0x00 107*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_TEXT_EVENT = 0x01 108*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_COPYRIGHT_NOTICE = 0x02 109*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_SEQUENCE_TRACK_NAME = 0x03 110*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_INSTRUMENT_NAME = 0x04 111*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_LYRIC = 0x05 112*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_MARKER = 0x06 113*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_CUE_POINT = 0x07 114*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_MIDI_CHANNEL_PREFIX = 0x20 115*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_END_OF_TRACK = 0x2f 116*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_SET_TEMPO = 0x51 117*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_SMPTE_OFFSET = 0x54 118*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_TIME_SIGNATURE = 0x58 119*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_KEY_SIGNATURE = 0x59 120*f81fb7c4SAndroid Build Coastguard WorkerMETA_EVENT_SEQUENCER_SPECIFIC = 0x7f 121*f81fb7c4SAndroid Build Coastguard Worker 122*f81fb7c4SAndroid Build Coastguard Worker# recurring error messages 123*f81fb7c4SAndroid Build Coastguard WorkerMSG_NOT_SMF_FILE = 'Not an SMF file - aborting parse!' 124*f81fb7c4SAndroid Build Coastguard WorkerMSG_INVALID_TRACK_HEADER = 'Track header is invalid' 125*f81fb7c4SAndroid Build Coastguard WorkerMSG_TYPE_MISMATCH = 'msg_type does not match event type' 126*f81fb7c4SAndroid Build Coastguard Worker 127*f81fb7c4SAndroid Build Coastguard WorkerLARGE_TICK_WARNING = 1000 128*f81fb7c4SAndroid Build Coastguard Worker 129*f81fb7c4SAndroid Build Coastguard Worker# default control values 130*f81fb7c4SAndroid Build Coastguard WorkerCTRL_BANK_SELECT_MSB = 0 131*f81fb7c4SAndroid Build Coastguard WorkerCTRL_MOD_WHEEL = 1 132*f81fb7c4SAndroid Build Coastguard WorkerCTRL_RPN_DATA_MSB = 6 133*f81fb7c4SAndroid Build Coastguard WorkerCTRL_VOLUME = 7 134*f81fb7c4SAndroid Build Coastguard WorkerCTRL_PAN = 10 135*f81fb7c4SAndroid Build Coastguard WorkerCTRL_EXPRESSION = 11 136*f81fb7c4SAndroid Build Coastguard WorkerCTRL_BANK_SELECT_LSB = 32 137*f81fb7c4SAndroid Build Coastguard WorkerCTRL_RPN_DATA_LSB = 38 138*f81fb7c4SAndroid Build Coastguard WorkerCTRL_SUSTAIN = 64 139*f81fb7c4SAndroid Build Coastguard WorkerCTRL_RPN_LSB = 100 140*f81fb7c4SAndroid Build Coastguard WorkerCTRL_RPN_MSB = 101 141*f81fb7c4SAndroid Build Coastguard WorkerCTRL_RESET_CONTROLLERS = 121 142*f81fb7c4SAndroid Build Coastguard Worker 143*f81fb7c4SAndroid Build Coastguard WorkerRPN_PITCH_BEND_SENSITIVITY = 0 144*f81fb7c4SAndroid Build Coastguard WorkerRPN_FINE_TUNING = 1 145*f81fb7c4SAndroid Build Coastguard WorkerRPN_COARSE_TUNING = 2 146*f81fb7c4SAndroid Build Coastguard Worker 147*f81fb7c4SAndroid Build Coastguard WorkerMONITOR_CONTROLLERS = ( 148*f81fb7c4SAndroid Build Coastguard Worker CTRL_BANK_SELECT_MSB, 149*f81fb7c4SAndroid Build Coastguard Worker CTRL_MOD_WHEEL, 150*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_DATA_MSB, 151*f81fb7c4SAndroid Build Coastguard Worker CTRL_VOLUME, 152*f81fb7c4SAndroid Build Coastguard Worker CTRL_PAN, 153*f81fb7c4SAndroid Build Coastguard Worker CTRL_EXPRESSION, 154*f81fb7c4SAndroid Build Coastguard Worker CTRL_BANK_SELECT_LSB, 155*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_DATA_LSB, 156*f81fb7c4SAndroid Build Coastguard Worker CTRL_SUSTAIN, 157*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_LSB, 158*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_MSB) 159*f81fb7c4SAndroid Build Coastguard Worker 160*f81fb7c4SAndroid Build Coastguard WorkerMONITOR_RPNS = ( 161*f81fb7c4SAndroid Build Coastguard Worker RPN_PITCH_BEND_SENSITIVITY, 162*f81fb7c4SAndroid Build Coastguard Worker RPN_FINE_TUNING, 163*f81fb7c4SAndroid Build Coastguard Worker RPN_COARSE_TUNING) 164*f81fb7c4SAndroid Build Coastguard Worker 165*f81fb7c4SAndroid Build Coastguard WorkerRPN_PITCH_BEND_SENSITIVITY = 0 166*f81fb7c4SAndroid Build Coastguard WorkerRPN_FINE_TUNING = 1 167*f81fb7c4SAndroid Build Coastguard WorkerRPN_COARSE_TUNING = 2 168*f81fb7c4SAndroid Build Coastguard Worker 169*f81fb7c4SAndroid Build Coastguard WorkerDEFAULT_CONTROLLER_VALUES = { 170*f81fb7c4SAndroid Build Coastguard Worker CTRL_BANK_SELECT_MSB : 121, 171*f81fb7c4SAndroid Build Coastguard Worker CTRL_MOD_WHEEL : 0, 172*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_DATA_MSB : 0, 173*f81fb7c4SAndroid Build Coastguard Worker CTRL_VOLUME : 100, 174*f81fb7c4SAndroid Build Coastguard Worker CTRL_PAN : 64, 175*f81fb7c4SAndroid Build Coastguard Worker CTRL_EXPRESSION : 127, 176*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_DATA_LSB : 0, 177*f81fb7c4SAndroid Build Coastguard Worker CTRL_BANK_SELECT_LSB : 0, 178*f81fb7c4SAndroid Build Coastguard Worker CTRL_SUSTAIN : 0, 179*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_LSB : 0x7f, 180*f81fb7c4SAndroid Build Coastguard Worker CTRL_RPN_MSB : 0x7f} 181*f81fb7c4SAndroid Build Coastguard Worker 182*f81fb7c4SAndroid Build Coastguard WorkerDEFAULT_RPN_VALUES = { 183*f81fb7c4SAndroid Build Coastguard Worker RPN_PITCH_BEND_SENSITIVITY : 0x100, 184*f81fb7c4SAndroid Build Coastguard Worker RPN_FINE_TUNING : 0, 185*f81fb7c4SAndroid Build Coastguard Worker RPN_COARSE_TUNING : 1} 186*f81fb7c4SAndroid Build Coastguard Worker 187*f81fb7c4SAndroid Build Coastguard Worker# initialize logger 188*f81fb7c4SAndroid Build Coastguard Workermidi_file_logger = logging.getLogger('MIDI_file') 189*f81fb7c4SAndroid Build Coastguard Workermidi_file_logger.setLevel(logging.NOTSET) 190*f81fb7c4SAndroid Build Coastguard Worker 191*f81fb7c4SAndroid Build Coastguard Worker 192*f81fb7c4SAndroid Build Coastguard Workerclass trackGrid(object): 193*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, track, channel, name, empty): 194*f81fb7c4SAndroid Build Coastguard Worker self.track = track 195*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 196*f81fb7c4SAndroid Build Coastguard Worker self.name = name 197*f81fb7c4SAndroid Build Coastguard Worker self.empty = empty 198*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 199*f81fb7c4SAndroid Build Coastguard Worker return "['%s', '%s', '%s']" % (self.track, self.channel, self.name) 200*f81fb7c4SAndroid Build Coastguard Worker 201*f81fb7c4SAndroid Build Coastguard Worker 202*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 203*f81fb7c4SAndroid Build Coastguard Worker# MIDIFileException 204*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 205*f81fb7c4SAndroid Build Coastguard Workerclass MIDIFileException (Exception): 206*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, stream, msg): 207*f81fb7c4SAndroid Build Coastguard Worker stream.error_loc = stream.tell() 208*f81fb7c4SAndroid Build Coastguard Worker self.stream = stream 209*f81fb7c4SAndroid Build Coastguard Worker self.msg = msg 210*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 211*f81fb7c4SAndroid Build Coastguard Worker return '[%d]: %s' % (self.stream.error_loc, self.msg) 212*f81fb7c4SAndroid Build Coastguard Worker 213*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 214*f81fb7c4SAndroid Build Coastguard Worker# TimeBase 215*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 216*f81fb7c4SAndroid Build Coastguard Workerclass TimeBase (object): 217*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ppqn=DEFAULT_PPQN, beats_per_measure=DEFAULT_BEATS_PER_MEASURE): 218*f81fb7c4SAndroid Build Coastguard Worker self.ppqn = ppqn 219*f81fb7c4SAndroid Build Coastguard Worker self.beats_per_measure = beats_per_measure 220*f81fb7c4SAndroid Build Coastguard Worker 221*f81fb7c4SAndroid Build Coastguard Worker def ConvertToTicks (self, measures, beats, ticks): 222*f81fb7c4SAndroid Build Coastguard Worker total_beats = beats + (measures * self.beats_per_measure) 223*f81fb7c4SAndroid Build Coastguard Worker total_ticks = ticks + (total_beats * self.ppqn) 224*f81fb7c4SAndroid Build Coastguard Worker return total_ticks 225*f81fb7c4SAndroid Build Coastguard Worker 226*f81fb7c4SAndroid Build Coastguard Worker def ConvertTicksToMBT (self, ticks): 227*f81fb7c4SAndroid Build Coastguard Worker beats = ticks / self.ppqn 228*f81fb7c4SAndroid Build Coastguard Worker ticks -= beats * self.ppqn 229*f81fb7c4SAndroid Build Coastguard Worker measures = beats / self.beats_per_measure 230*f81fb7c4SAndroid Build Coastguard Worker beats -= measures * self.beats_per_measure 231*f81fb7c4SAndroid Build Coastguard Worker return (measures, beats, ticks) 232*f81fb7c4SAndroid Build Coastguard Worker 233*f81fb7c4SAndroid Build Coastguard Worker def ConvertTicksToStr (self, ticks, format=DEFAULT_TIME_FORMAT): 234*f81fb7c4SAndroid Build Coastguard Worker measures, beats, ticks = self.ConvertTicksToMBT(ticks) 235*f81fb7c4SAndroid Build Coastguard Worker return format % (measures, beats, ticks) 236*f81fb7c4SAndroid Build Coastguard Worker 237*f81fb7c4SAndroid Build Coastguard Worker def ConvertStrTimeToTuple(self, s): 238*f81fb7c4SAndroid Build Coastguard Worker try: 239*f81fb7c4SAndroid Build Coastguard Worker measures, beats, ticks = s.split(':',3) 240*f81fb7c4SAndroid Build Coastguard Worker return (int(measures), int(beats), int(ticks)) 241*f81fb7c4SAndroid Build Coastguard Worker except: 242*f81fb7c4SAndroid Build Coastguard Worker return (0,0,0) 243*f81fb7c4SAndroid Build Coastguard Worker 244*f81fb7c4SAndroid Build Coastguard Worker def ConvertStrTimeToTicks(self, s): 245*f81fb7c4SAndroid Build Coastguard Worker measures, beats, ticks = self.ConvertStrTimeToTuple(s) 246*f81fb7c4SAndroid Build Coastguard Worker return self.ConvertToTicks(measures, beats, ticks) 247*f81fb7c4SAndroid Build Coastguard Worker 248*f81fb7c4SAndroid Build Coastguard Worker def MbtDifference(self, mbt1, mbt2): 249*f81fb7c4SAndroid Build Coastguard Worker t1 = self.ConvertToTicks(mbt1[0], mbt1[1], mbt1[2]) 250*f81fb7c4SAndroid Build Coastguard Worker t2 = self.ConvertToTicks(mbt2[0], mbt2[1], mbt2[2]) 251*f81fb7c4SAndroid Build Coastguard Worker return abs(t1-t2) 252*f81fb7c4SAndroid Build Coastguard Worker 253*f81fb7c4SAndroid Build Coastguard Worker 254*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 255*f81fb7c4SAndroid Build Coastguard Worker# Helper functions 256*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 257*f81fb7c4SAndroid Build Coastguard Workerdef ReadByte (stream): 258*f81fb7c4SAndroid Build Coastguard Worker try: 259*f81fb7c4SAndroid Build Coastguard Worker return ord(stream.read(1)) 260*f81fb7c4SAndroid Build Coastguard Worker except TypeError: 261*f81fb7c4SAndroid Build Coastguard Worker stream.error_loc = stream.tell() 262*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, 'Unexpected EOF') 263*f81fb7c4SAndroid Build Coastguard Worker 264*f81fb7c4SAndroid Build Coastguard Workerdef ReadBytes (stream, length): 265*f81fb7c4SAndroid Build Coastguard Worker bytes = [] 266*f81fb7c4SAndroid Build Coastguard Worker for i in range(length): 267*f81fb7c4SAndroid Build Coastguard Worker bytes.append(ReadByte(stream)) 268*f81fb7c4SAndroid Build Coastguard Worker return bytes 269*f81fb7c4SAndroid Build Coastguard Worker 270*f81fb7c4SAndroid Build Coastguard Workerdef ReadVarLenQty (stream): 271*f81fb7c4SAndroid Build Coastguard Worker value = 0 272*f81fb7c4SAndroid Build Coastguard Worker while 1: 273*f81fb7c4SAndroid Build Coastguard Worker byte = ReadByte(stream) 274*f81fb7c4SAndroid Build Coastguard Worker value = (value << 7) + (byte & 0x7f) 275*f81fb7c4SAndroid Build Coastguard Worker if byte & 0x80 == 0: 276*f81fb7c4SAndroid Build Coastguard Worker return value 277*f81fb7c4SAndroid Build Coastguard Worker 278*f81fb7c4SAndroid Build Coastguard Workerdef WriteByte (stream, value): 279*f81fb7c4SAndroid Build Coastguard Worker stream.write(chr(value)) 280*f81fb7c4SAndroid Build Coastguard Worker 281*f81fb7c4SAndroid Build Coastguard Workerdef WriteBytes (stream, bytes): 282*f81fb7c4SAndroid Build Coastguard Worker for byte in bytes: 283*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, byte) 284*f81fb7c4SAndroid Build Coastguard Worker 285*f81fb7c4SAndroid Build Coastguard Workerdef WriteVarLenQty (stream, value): 286*f81fb7c4SAndroid Build Coastguard Worker bytes = [value & 0x7f] 287*f81fb7c4SAndroid Build Coastguard Worker value = value >> 7 288*f81fb7c4SAndroid Build Coastguard Worker while value > 0: 289*f81fb7c4SAndroid Build Coastguard Worker bytes.append((value & 0x7f) | 0x80) 290*f81fb7c4SAndroid Build Coastguard Worker value = value >> 7 291*f81fb7c4SAndroid Build Coastguard Worker bytes.reverse() 292*f81fb7c4SAndroid Build Coastguard Worker WriteBytes(stream, bytes) 293*f81fb7c4SAndroid Build Coastguard Worker 294*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 295*f81fb7c4SAndroid Build Coastguard Worker# EventFilter 296*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 297*f81fb7c4SAndroid Build Coastguard Workerclass EventFilter (object): 298*f81fb7c4SAndroid Build Coastguard Worker pass 299*f81fb7c4SAndroid Build Coastguard Worker 300*f81fb7c4SAndroid Build Coastguard Workerclass EventTypeFilter (object): 301*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, events, exclude=True): 302*f81fb7c4SAndroid Build Coastguard Worker self.events = events 303*f81fb7c4SAndroid Build Coastguard Worker self.exclude = exclude 304*f81fb7c4SAndroid Build Coastguard Worker def Check (self, event): 305*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type in self.events: 306*f81fb7c4SAndroid Build Coastguard Worker return not self.exclude 307*f81fb7c4SAndroid Build Coastguard Worker return self.exclude 308*f81fb7c4SAndroid Build Coastguard Worker 309*f81fb7c4SAndroid Build Coastguard Workerclass NoteFilter (EventFilter): 310*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, notes, exclude=True): 311*f81fb7c4SAndroid Build Coastguard Worker self.notes = notes 312*f81fb7c4SAndroid Build Coastguard Worker self.exclude = exclude 313*f81fb7c4SAndroid Build Coastguard Worker def Check (self, event): 314*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type in (NOTE_ON, NOTE_OFF): 315*f81fb7c4SAndroid Build Coastguard Worker if event.note in self.notes: 316*f81fb7c4SAndroid Build Coastguard Worker return not self.exclude 317*f81fb7c4SAndroid Build Coastguard Worker return self.exclude 318*f81fb7c4SAndroid Build Coastguard Worker 319*f81fb7c4SAndroid Build Coastguard Workerclass ChannelFilter (EventFilter): 320*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, channel, exclude=True): 321*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 322*f81fb7c4SAndroid Build Coastguard Worker self.exclude = exclude 323*f81fb7c4SAndroid Build Coastguard Worker def Check (self, event): 324*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type in (NOTE_ON, NOTE_OFF, POLY_KEY_PRESSURE, CONTROL_CHANGE, CHANNEL_PRESSURE, PITCH_BEND): 325*f81fb7c4SAndroid Build Coastguard Worker if event.channel in self.channel: 326*f81fb7c4SAndroid Build Coastguard Worker return not self.exclude 327*f81fb7c4SAndroid Build Coastguard Worker return self.exclude 328*f81fb7c4SAndroid Build Coastguard Worker 329*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 330*f81fb7c4SAndroid Build Coastguard Worker# MIDIEvent 331*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 332*f81fb7c4SAndroid Build Coastguard Workerclass MIDIEvent (object): 333*f81fb7c4SAndroid Build Coastguard Worker """Factory for creating MIDI events from a stream.""" 334*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 335*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 336*f81fb7c4SAndroid Build Coastguard Worker if msg_type == SYSEX: 337*f81fb7c4SAndroid Build Coastguard Worker return SysExEvent.ReadFromStream(stream, seq, ticks, msg_type) 338*f81fb7c4SAndroid Build Coastguard Worker elif msg_type == END_SYSEX: 339*f81fb7c4SAndroid Build Coastguard Worker return SysExContEvent.ReadFromStream(stream, seq, ticks, msg_type) 340*f81fb7c4SAndroid Build Coastguard Worker elif msg_type == META_EVENT: 341*f81fb7c4SAndroid Build Coastguard Worker return MetaEvent.ReadFromStream(stream, seq, ticks, msg_type) 342*f81fb7c4SAndroid Build Coastguard Worker else: 343*f81fb7c4SAndroid Build Coastguard Worker high_nibble = msg_type & 0xf0 344*f81fb7c4SAndroid Build Coastguard Worker if high_nibble == NOTE_OFF: 345*f81fb7c4SAndroid Build Coastguard Worker return NoteOffEvent.ReadFromStream(stream, seq, ticks, msg_type) 346*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == NOTE_ON: 347*f81fb7c4SAndroid Build Coastguard Worker return NoteOnEvent.ReadFromStream(stream, seq, ticks, msg_type) 348*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == POLY_KEY_PRESSURE: 349*f81fb7c4SAndroid Build Coastguard Worker return PolyKeyPressureEvent.ReadFromStream(stream, seq, ticks, msg_type) 350*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == CONTROL_CHANGE: 351*f81fb7c4SAndroid Build Coastguard Worker return ControlChangeEvent.ReadFromStream(stream, seq, ticks, msg_type) 352*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == PROGRAM_CHANGE: 353*f81fb7c4SAndroid Build Coastguard Worker return ProgramChangeEvent.ReadFromStream(stream, seq, ticks, msg_type) 354*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == CHANNEL_PRESSURE: 355*f81fb7c4SAndroid Build Coastguard Worker return ChannelPressureEvent.ReadFromStream(stream, seq, ticks, msg_type) 356*f81fb7c4SAndroid Build Coastguard Worker elif high_nibble == PITCH_BEND: 357*f81fb7c4SAndroid Build Coastguard Worker return PitchBendEvent.ReadFromStream(stream, seq, ticks, msg_type) 358*f81fb7c4SAndroid Build Coastguard Worker else: 359*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Ignoring unexpected message type 0x%02x' % msg_type) 360*f81fb7c4SAndroid Build Coastguard Worker def WriteTicks (self, stream, track): 361*f81fb7c4SAndroid Build Coastguard Worker WriteVarLenQty(stream, self.ticks - track.ticks) 362*f81fb7c4SAndroid Build Coastguard Worker track.ticks = self.ticks 363*f81fb7c4SAndroid Build Coastguard Worker def WriteRunningStatus (self, stream, track, filters, msg, data1, data2=None): 364*f81fb7c4SAndroid Build Coastguard Worker if not self.CheckFilters(filters): 365*f81fb7c4SAndroid Build Coastguard Worker return 366*f81fb7c4SAndroid Build Coastguard Worker self.WriteTicks(stream, track) 367*f81fb7c4SAndroid Build Coastguard Worker status = msg + self.channel 368*f81fb7c4SAndroid Build Coastguard Worker if track.running_status != status: 369*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, status) 370*f81fb7c4SAndroid Build Coastguard Worker track.running_status = status 371*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, data1) 372*f81fb7c4SAndroid Build Coastguard Worker if data2 is not None: 373*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, data2) 374*f81fb7c4SAndroid Build Coastguard Worker def CheckFilters (self, filters): 375*f81fb7c4SAndroid Build Coastguard Worker if filters is None or not len(filters): 376*f81fb7c4SAndroid Build Coastguard Worker return True 377*f81fb7c4SAndroid Build Coastguard Worker 378*f81fb7c4SAndroid Build Coastguard Worker # never filter meta-events 379*f81fb7c4SAndroid Build Coastguard Worker if (self.msg_type == META_EVENT) and (self.meta_type == META_EVENT_END_OF_TRACK): 380*f81fb7c4SAndroid Build Coastguard Worker return True 381*f81fb7c4SAndroid Build Coastguard Worker 382*f81fb7c4SAndroid Build Coastguard Worker # check all filters 383*f81fb7c4SAndroid Build Coastguard Worker for f in filters: 384*f81fb7c4SAndroid Build Coastguard Worker if not f.Check(self): 385*f81fb7c4SAndroid Build Coastguard Worker return False 386*f81fb7c4SAndroid Build Coastguard Worker return True 387*f81fb7c4SAndroid Build Coastguard Worker 388*f81fb7c4SAndroid Build Coastguard Worker def TimeEventStr (self, timebase): 389*f81fb7c4SAndroid Build Coastguard Worker return '[%s]: %s' % (timebase.ConvertTicksToStr(self.ticks), self.__str__()) 390*f81fb7c4SAndroid Build Coastguard Worker 391*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 392*f81fb7c4SAndroid Build Coastguard Worker# NoteOffEvent 393*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 394*f81fb7c4SAndroid Build Coastguard Workerclass NoteOffEvent (MIDIEvent): 395*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, note, velocity): 396*f81fb7c4SAndroid Build Coastguard Worker self.name = 'NoteOff' 397*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = NOTE_OFF 398*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 399*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 400*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 401*f81fb7c4SAndroid Build Coastguard Worker self.note = note 402*f81fb7c4SAndroid Build Coastguard Worker self.velocity = velocity 403*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 404*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 405*f81fb7c4SAndroid Build Coastguard Worker ticks = ticks 406*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 407*f81fb7c4SAndroid Build Coastguard Worker note = ReadByte(stream) 408*f81fb7c4SAndroid Build Coastguard Worker velocity = ReadByte(stream) 409*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != NOTE_OFF: 410*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 411*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 412*f81fb7c4SAndroid Build Coastguard Worker return NoteOffEvent(ticks, seq, channel, note, velocity) 413*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 414*f81fb7c4SAndroid Build Coastguard Worker # special case for note-off using zero velocity 415*f81fb7c4SAndroid Build Coastguard Worker if self.velocity > 0: 416*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, NOTE_ON, self.note, self.velocity) 417*f81fb7c4SAndroid Build Coastguard Worker if track.running_status == (NOTE_OFF + self.channel): 418*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, NOTE_ON, self.note, self.velocity) 419*f81fb7c4SAndroid Build Coastguard Worker else: 420*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, NOTE_ON, self.note, 0) 421*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 422*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d n=%d v=%d' % (self.name, self.channel, self.note, self.velocity) 423*f81fb7c4SAndroid Build Coastguard Worker 424*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 425*f81fb7c4SAndroid Build Coastguard Worker# NoteOnEvent 426*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 427*f81fb7c4SAndroid Build Coastguard Workerclass NoteOnEvent (MIDIEvent): 428*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, note, velocity, note_length, note_off_velocity): 429*f81fb7c4SAndroid Build Coastguard Worker self.name = 'NoteOn' 430*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = NOTE_ON 431*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 432*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 433*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 434*f81fb7c4SAndroid Build Coastguard Worker self.note = note 435*f81fb7c4SAndroid Build Coastguard Worker self.velocity = velocity 436*f81fb7c4SAndroid Build Coastguard Worker self.note_length = note_length 437*f81fb7c4SAndroid Build Coastguard Worker self.note_off_velocity = note_off_velocity 438*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 439*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 440*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 441*f81fb7c4SAndroid Build Coastguard Worker note = ReadByte(stream) 442*f81fb7c4SAndroid Build Coastguard Worker velocity = ReadByte(stream) 443*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != NOTE_ON: 444*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 445*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 446*f81fb7c4SAndroid Build Coastguard Worker if velocity == 0: 447*f81fb7c4SAndroid Build Coastguard Worker return NoteOffEvent(ticks, seq, channel, note, velocity) 448*f81fb7c4SAndroid Build Coastguard Worker return NoteOnEvent(ticks, seq, channel, note, velocity, None, None) 449*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 450*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, NOTE_ON, self.note, self.velocity) 451*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 452*f81fb7c4SAndroid Build Coastguard Worker if self.note_length is not None: 453*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d n=%d v=%d l=%d' % (self.name, self.channel, self.note, self.velocity, self.note_length) 454*f81fb7c4SAndroid Build Coastguard Worker else: 455*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d n=%d v=%d' % (self.name, self.channel, self.note, self.velocity) 456*f81fb7c4SAndroid Build Coastguard Worker 457*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 458*f81fb7c4SAndroid Build Coastguard Worker# PolyKeyPressureEvent 459*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 460*f81fb7c4SAndroid Build Coastguard Workerclass PolyKeyPressureEvent (MIDIEvent): 461*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, note, value): 462*f81fb7c4SAndroid Build Coastguard Worker self.name = 'PolyKeyPressure' 463*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = POLY_KEY_PRESSURE 464*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 465*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 466*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 467*f81fb7c4SAndroid Build Coastguard Worker self.note = note 468*f81fb7c4SAndroid Build Coastguard Worker self.value = value 469*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 470*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 471*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 472*f81fb7c4SAndroid Build Coastguard Worker note = ReadByte(stream) 473*f81fb7c4SAndroid Build Coastguard Worker value = ReadByte(stream) 474*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != POLY_KEY_PRESSURE: 475*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 476*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 477*f81fb7c4SAndroid Build Coastguard Worker return PolyKeyPressureEvent(ticks, seq, channel, note, value) 478*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 479*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, POLY_KEY_PRESSURE, self.note, self.value) 480*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 481*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d n=%d v=%d' % (self.name, self.channel, self.note, self.value) 482*f81fb7c4SAndroid Build Coastguard Worker 483*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 484*f81fb7c4SAndroid Build Coastguard Worker# ControlChangeEvent 485*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 486*f81fb7c4SAndroid Build Coastguard Workerclass ControlChangeEvent (MIDIEvent): 487*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, controller, value): 488*f81fb7c4SAndroid Build Coastguard Worker self.name = 'ControlChange' 489*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = CONTROL_CHANGE 490*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 491*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 492*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 493*f81fb7c4SAndroid Build Coastguard Worker self.controller = controller 494*f81fb7c4SAndroid Build Coastguard Worker self.value = value 495*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 496*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 497*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 498*f81fb7c4SAndroid Build Coastguard Worker controller = ReadByte(stream) 499*f81fb7c4SAndroid Build Coastguard Worker value = ReadByte(stream) 500*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != CONTROL_CHANGE: 501*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 502*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 503*f81fb7c4SAndroid Build Coastguard Worker if controller >= 120: 504*f81fb7c4SAndroid Build Coastguard Worker return ChannelModeEvent(ticks, seq, channel, controller, value) 505*f81fb7c4SAndroid Build Coastguard Worker return ControlChangeEvent(ticks, seq, channel, controller, value) 506*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 507*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, CONTROL_CHANGE, self.controller, self.value) 508*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 509*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d c=%d v=%d' % (self.name, self.channel, self.controller, self.value) 510*f81fb7c4SAndroid Build Coastguard Worker 511*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 512*f81fb7c4SAndroid Build Coastguard Worker# ChannelModeEvent 513*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 514*f81fb7c4SAndroid Build Coastguard Workerclass ChannelModeEvent (MIDIEvent): 515*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, controller, value): 516*f81fb7c4SAndroid Build Coastguard Worker self.name = 'ChannelMode' 517*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = CONTROL_CHANGE 518*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 519*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 520*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 521*f81fb7c4SAndroid Build Coastguard Worker self.controller = controller 522*f81fb7c4SAndroid Build Coastguard Worker self.value = value 523*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 524*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 525*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 526*f81fb7c4SAndroid Build Coastguard Worker controller = ReadByte(stream) 527*f81fb7c4SAndroid Build Coastguard Worker value = ReadByte(stream) 528*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != CONTROL_CHANGE: 529*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 530*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 531*f81fb7c4SAndroid Build Coastguard Worker if controller < 120: 532*f81fb7c4SAndroid Build Coastguard Worker return ControlChangeEvent(ticks, seq, channel, controller, value) 533*f81fb7c4SAndroid Build Coastguard Worker return ChannelModeEvent(ticks, seq, channel, value) 534*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 535*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, CONTROL_CHANGE, self.controller, self.value) 536*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 537*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d c=%d v=%d' % (self.name, self.channel, self.controller, self.value) 538*f81fb7c4SAndroid Build Coastguard Worker 539*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 540*f81fb7c4SAndroid Build Coastguard Worker# ProgramChangeEvent 541*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 542*f81fb7c4SAndroid Build Coastguard Workerclass ProgramChangeEvent (MIDIEvent): 543*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, program): 544*f81fb7c4SAndroid Build Coastguard Worker self.name = 'ProgramChange' 545*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = PROGRAM_CHANGE 546*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 547*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 548*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 549*f81fb7c4SAndroid Build Coastguard Worker self.program = program 550*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 551*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 552*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 553*f81fb7c4SAndroid Build Coastguard Worker program = ReadByte(stream) 554*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != PROGRAM_CHANGE: 555*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-1,1) 556*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 557*f81fb7c4SAndroid Build Coastguard Worker return ProgramChangeEvent(ticks, seq, channel, program) 558*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 559*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, PROGRAM_CHANGE, self.program) 560*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 561*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d p=%d' % (self.name, self.channel, self.program) 562*f81fb7c4SAndroid Build Coastguard Worker 563*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 564*f81fb7c4SAndroid Build Coastguard Worker# ChannelPressureEvent 565*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 566*f81fb7c4SAndroid Build Coastguard Workerclass ChannelPressureEvent (MIDIEvent): 567*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, value): 568*f81fb7c4SAndroid Build Coastguard Worker self.name = 'ChannelPressure' 569*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = CHANNEL_PRESSURE 570*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 571*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 572*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 573*f81fb7c4SAndroid Build Coastguard Worker self.value = value 574*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 575*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 576*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 577*f81fb7c4SAndroid Build Coastguard Worker value = ReadByte(stream) 578*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != CHANNEL_PRESSURE: 579*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-1,1) 580*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 581*f81fb7c4SAndroid Build Coastguard Worker return ChannelPressureEvent(ticks, seq, channel, value) 582*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 583*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, CHANNEL_PRESSURE, self.value) 584*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 585*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d v=%d' % (self.name, self.channel, self.value) 586*f81fb7c4SAndroid Build Coastguard Worker 587*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 588*f81fb7c4SAndroid Build Coastguard Worker# PitchBendEvent 589*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 590*f81fb7c4SAndroid Build Coastguard Workerclass PitchBendEvent (MIDIEvent): 591*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, channel, value): 592*f81fb7c4SAndroid Build Coastguard Worker self.name = 'PitchBend' 593*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = PITCH_BEND 594*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 595*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 596*f81fb7c4SAndroid Build Coastguard Worker self.channel = channel 597*f81fb7c4SAndroid Build Coastguard Worker self.value = value 598*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 599*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 600*f81fb7c4SAndroid Build Coastguard Worker channel = msg_type & 0x0f 601*f81fb7c4SAndroid Build Coastguard Worker value = (ReadByte(stream) << 7) + ReadByte(stream) - 0x2000 602*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0xf0 != PITCH_BEND: 603*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-2,1) 604*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 605*f81fb7c4SAndroid Build Coastguard Worker return PitchBendEvent(ticks, seq, channel, value) 606*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 607*f81fb7c4SAndroid Build Coastguard Worker value = self.value + 0x2000 608*f81fb7c4SAndroid Build Coastguard Worker if value < 0: 609*f81fb7c4SAndroid Build Coastguard Worker value = 0 610*f81fb7c4SAndroid Build Coastguard Worker if value > 0x3fff: 611*f81fb7c4SAndroid Build Coastguard Worker value = 0x3fff 612*f81fb7c4SAndroid Build Coastguard Worker self.WriteRunningStatus(stream, track, filters, PITCH_BEND, value >> 7, value & 0x7f) 613*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 614*f81fb7c4SAndroid Build Coastguard Worker return '%s: ch=%d v=%d' % (self.name, self.channel, self.value) 615*f81fb7c4SAndroid Build Coastguard Worker 616*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 617*f81fb7c4SAndroid Build Coastguard Worker# SysExEvent 618*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 619*f81fb7c4SAndroid Build Coastguard Workerclass SysExEvent (MIDIEvent): 620*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, msg): 621*f81fb7c4SAndroid Build Coastguard Worker self.name = 'SysEx' 622*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = SYSEX 623*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 624*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 625*f81fb7c4SAndroid Build Coastguard Worker self.length = len(msg) 626*f81fb7c4SAndroid Build Coastguard Worker self.msg = msg 627*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 628*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 629*f81fb7c4SAndroid Build Coastguard Worker pos = stream.tell() 630*f81fb7c4SAndroid Build Coastguard Worker length = ReadVarLenQty(stream) 631*f81fb7c4SAndroid Build Coastguard Worker msg = ReadBytes(stream, length) 632*f81fb7c4SAndroid Build Coastguard Worker if msg_type != SYSEX: 633*f81fb7c4SAndroid Build Coastguard Worker stream.seek(pos,0) 634*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 635*f81fb7c4SAndroid Build Coastguard Worker return SysExEvent(ticks, seq, msg) 636*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 637*f81fb7c4SAndroid Build Coastguard Worker if not self.CheckFilters(filters): 638*f81fb7c4SAndroid Build Coastguard Worker return 639*f81fb7c4SAndroid Build Coastguard Worker self.WriteTicks(stream, track) 640*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, SYSEX) 641*f81fb7c4SAndroid Build Coastguard Worker WriteVarLenQty(stream, self.length) 642*f81fb7c4SAndroid Build Coastguard Worker WriteBytes(stream, self.msg) 643*f81fb7c4SAndroid Build Coastguard Worker track.running_status = None 644*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 645*f81fb7c4SAndroid Build Coastguard Worker fmt_str = '%s: f0' + ' %02x'*self.length 646*f81fb7c4SAndroid Build Coastguard Worker return fmt_str % ((self.name,) + tuple(self.msg)) 647*f81fb7c4SAndroid Build Coastguard Worker 648*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 649*f81fb7c4SAndroid Build Coastguard Worker# SysExContEvent 650*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 651*f81fb7c4SAndroid Build Coastguard Workerclass SysExContEvent (MIDIEvent): 652*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, msg): 653*f81fb7c4SAndroid Build Coastguard Worker self.name = 'SysEx+' 654*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = END_SYSEX 655*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 656*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 657*f81fb7c4SAndroid Build Coastguard Worker self.length = len(msg) 658*f81fb7c4SAndroid Build Coastguard Worker self.msg = msg 659*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 660*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 661*f81fb7c4SAndroid Build Coastguard Worker pos = stream.tell() 662*f81fb7c4SAndroid Build Coastguard Worker length = ReadVarLenQty(stream) 663*f81fb7c4SAndroid Build Coastguard Worker msg = ReadBytes(stream, length) 664*f81fb7c4SAndroid Build Coastguard Worker if msg_type != END_SYSEX: 665*f81fb7c4SAndroid Build Coastguard Worker stream.seek(pos,0) 666*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 667*f81fb7c4SAndroid Build Coastguard Worker return SysExContEvent(ticks, seq, msg) 668*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 669*f81fb7c4SAndroid Build Coastguard Worker if not self.CheckFilters(filters): 670*f81fb7c4SAndroid Build Coastguard Worker return 671*f81fb7c4SAndroid Build Coastguard Worker self.WriteTicks(stream, track) 672*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, END_SYSEX) 673*f81fb7c4SAndroid Build Coastguard Worker WriteVarLenQty(stream, self.length) 674*f81fb7c4SAndroid Build Coastguard Worker WriteBytes(stream, self.msg) 675*f81fb7c4SAndroid Build Coastguard Worker track.running_status = None 676*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 677*f81fb7c4SAndroid Build Coastguard Worker fmt_str = '%s:' + ' %02x'*self.length 678*f81fb7c4SAndroid Build Coastguard Worker return fmt_str % ((self.name,) + tuple(self.msg)) 679*f81fb7c4SAndroid Build Coastguard Worker 680*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 681*f81fb7c4SAndroid Build Coastguard Worker# MetaEvent 682*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 683*f81fb7c4SAndroid Build Coastguard Workerclass MetaEvent (MIDIEvent): 684*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, ticks, seq, meta_type, msg): 685*f81fb7c4SAndroid Build Coastguard Worker self.name = 'MetaEvent' 686*f81fb7c4SAndroid Build Coastguard Worker self.msg_type = META_EVENT 687*f81fb7c4SAndroid Build Coastguard Worker self.ticks = ticks 688*f81fb7c4SAndroid Build Coastguard Worker self.seq = seq 689*f81fb7c4SAndroid Build Coastguard Worker self.meta_type = meta_type 690*f81fb7c4SAndroid Build Coastguard Worker self.length = len(msg) 691*f81fb7c4SAndroid Build Coastguard Worker self.msg = msg 692*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 693*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (stream, seq, ticks, msg_type): 694*f81fb7c4SAndroid Build Coastguard Worker pos = stream.tell() 695*f81fb7c4SAndroid Build Coastguard Worker meta_type = ReadByte(stream) 696*f81fb7c4SAndroid Build Coastguard Worker length = ReadVarLenQty(stream) 697*f81fb7c4SAndroid Build Coastguard Worker msg = ReadBytes(stream, length) 698*f81fb7c4SAndroid Build Coastguard Worker if msg_type != META_EVENT: 699*f81fb7c4SAndroid Build Coastguard Worker stream.seek(pos,0) 700*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_TYPE_MISMATCH) 701*f81fb7c4SAndroid Build Coastguard Worker obj = MetaEvent(ticks, seq, meta_type, msg) 702*f81fb7c4SAndroid Build Coastguard Worker return obj 703*f81fb7c4SAndroid Build Coastguard Worker def WriteToStream (self, stream, track, filters=None): 704*f81fb7c4SAndroid Build Coastguard Worker if not self.CheckFilters(filters): 705*f81fb7c4SAndroid Build Coastguard Worker return 706*f81fb7c4SAndroid Build Coastguard Worker self.WriteTicks(stream, track) 707*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, META_EVENT) 708*f81fb7c4SAndroid Build Coastguard Worker WriteByte(stream, self.meta_type) 709*f81fb7c4SAndroid Build Coastguard Worker WriteVarLenQty(stream, self.length) 710*f81fb7c4SAndroid Build Coastguard Worker WriteBytes(stream, self.msg) 711*f81fb7c4SAndroid Build Coastguard Worker track.running_status = None 712*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 713*f81fb7c4SAndroid Build Coastguard Worker fmt_str = '%s: %02x' + ' %02x'*self.length 714*f81fb7c4SAndroid Build Coastguard Worker return fmt_str % ((self.name, self.meta_type) + tuple(self.msg)) 715*f81fb7c4SAndroid Build Coastguard Worker 716*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 717*f81fb7c4SAndroid Build Coastguard Worker# MIDIControllers 718*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 719*f81fb7c4SAndroid Build Coastguard Workerclass MIDIControllers (object): 720*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self): 721*f81fb7c4SAndroid Build Coastguard Worker self.controllers = [] 722*f81fb7c4SAndroid Build Coastguard Worker self.rpns = [] 723*f81fb7c4SAndroid Build Coastguard Worker for channel in range(16): 724*f81fb7c4SAndroid Build Coastguard Worker self.controllers.append({}) 725*f81fb7c4SAndroid Build Coastguard Worker self.controllers[channel] = copy.deepcopy(DEFAULT_CONTROLLER_VALUES) 726*f81fb7c4SAndroid Build Coastguard Worker self.rpns.append({}) 727*f81fb7c4SAndroid Build Coastguard Worker self.rpns[channel] = copy.deepcopy(DEFAULT_RPN_VALUES) 728*f81fb7c4SAndroid Build Coastguard Worker self.pitchbend = [0] * 16 729*f81fb7c4SAndroid Build Coastguard Worker self.program = [-1] * 16 730*f81fb7c4SAndroid Build Coastguard Worker self.pressure = [0] * 16 731*f81fb7c4SAndroid Build Coastguard Worker 732*f81fb7c4SAndroid Build Coastguard Worker def __str__ (self): 733*f81fb7c4SAndroid Build Coastguard Worker output = [] 734*f81fb7c4SAndroid Build Coastguard Worker for channel in range(16): 735*f81fb7c4SAndroid Build Coastguard Worker output.append('channel=%d' % channel) 736*f81fb7c4SAndroid Build Coastguard Worker output.append(' program=%d' % self.program[channel]) 737*f81fb7c4SAndroid Build Coastguard Worker output.append(' pressure=%d' % self.pressure[channel]) 738*f81fb7c4SAndroid Build Coastguard Worker 739*f81fb7c4SAndroid Build Coastguard Worker output.append(' controllers') 740*f81fb7c4SAndroid Build Coastguard Worker for controller in self.controllers[channel].keys(): 741*f81fb7c4SAndroid Build Coastguard Worker output.append(' %03d: %03d' % (controller, self.controllers[channel][controller])) 742*f81fb7c4SAndroid Build Coastguard Worker 743*f81fb7c4SAndroid Build Coastguard Worker output.append(' rpns') 744*f81fb7c4SAndroid Build Coastguard Worker for rpn in self.rpns[channel].keys(): 745*f81fb7c4SAndroid Build Coastguard Worker output.append(' %05d: %05d>' % (controller, self.rpns[channel][rpn])) 746*f81fb7c4SAndroid Build Coastguard Worker return '\n'.join(output) 747*f81fb7c4SAndroid Build Coastguard Worker 748*f81fb7c4SAndroid Build Coastguard Worker 749*f81fb7c4SAndroid Build Coastguard Worker def Event (self, event): 750*f81fb7c4SAndroid Build Coastguard Worker """Process an event and save any changes in controller values""" 751*f81fb7c4SAndroid Build Coastguard Worker # process control changes 752*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == CONTROL_CHANGE: 753*f81fb7c4SAndroid Build Coastguard Worker self.ControlChange(event) 754*f81fb7c4SAndroid Build Coastguard Worker elif event.msg_type == CHANNEL_PRESSURE: 755*f81fb7c4SAndroid Build Coastguard Worker self.PressureChange(event) 756*f81fb7c4SAndroid Build Coastguard Worker elif event.msg_type == PROGRAM_CHANGE: 757*f81fb7c4SAndroid Build Coastguard Worker self.ProgramChange(event) 758*f81fb7c4SAndroid Build Coastguard Worker elif event.msg_type == PITCH_BEND: 759*f81fb7c4SAndroid Build Coastguard Worker self.PitchBendChange(event) 760*f81fb7c4SAndroid Build Coastguard Worker 761*f81fb7c4SAndroid Build Coastguard Worker def PitchBendChange (self, event): 762*f81fb7c4SAndroid Build Coastguard Worker """Monitor pitch bend change.""" 763*f81fb7c4SAndroid Build Coastguard Worker self.pitchbend[event.channel] = event.value 764*f81fb7c4SAndroid Build Coastguard Worker 765*f81fb7c4SAndroid Build Coastguard Worker def ProgramChange (self, event): 766*f81fb7c4SAndroid Build Coastguard Worker """Monitor program change.""" 767*f81fb7c4SAndroid Build Coastguard Worker self.program[event.channel] = event.program 768*f81fb7c4SAndroid Build Coastguard Worker 769*f81fb7c4SAndroid Build Coastguard Worker def ControlChange (self, event): 770*f81fb7c4SAndroid Build Coastguard Worker """Monitor control change.""" 771*f81fb7c4SAndroid Build Coastguard Worker controller = event.controller 772*f81fb7c4SAndroid Build Coastguard Worker if controller in MONITOR_CONTROLLERS: 773*f81fb7c4SAndroid Build Coastguard Worker channel = event.channel 774*f81fb7c4SAndroid Build Coastguard Worker self.controllers[channel][controller] = event.value 775*f81fb7c4SAndroid Build Coastguard Worker if (controller == CTRL_RPN_DATA_MSB) or (controller == CTRL_RPN_DATA_LSB): 776*f81fb7c4SAndroid Build Coastguard Worker rpn = (self.controllers[channel][CTRL_RPN_MSB] << 7) + self.controllers[channel][CTRL_RPN_LSB] 777*f81fb7c4SAndroid Build Coastguard Worker if rpn in MONITOR_RPNS: 778*f81fb7c4SAndroid Build Coastguard Worker value = (self.controllers[channel][CTRL_RPN_DATA_MSB] << 7) + self.controllers[channel][CTRL_RPN_DATA_LSB] 779*f81fb7c4SAndroid Build Coastguard Worker self.rpns[channel][rpn] = value 780*f81fb7c4SAndroid Build Coastguard Worker 781*f81fb7c4SAndroid Build Coastguard Worker # reset controllers 782*f81fb7c4SAndroid Build Coastguard Worker elif event.controller == CTRL_RESET_CONTROLLERS: 783*f81fb7c4SAndroid Build Coastguard Worker self.ResetControllers[event.channel] 784*f81fb7c4SAndroid Build Coastguard Worker 785*f81fb7c4SAndroid Build Coastguard Worker def PressureChange (self, event): 786*f81fb7c4SAndroid Build Coastguard Worker """Monitor pressure change.""" 787*f81fb7c4SAndroid Build Coastguard Worker self.pressure[event.channel] = event.value 788*f81fb7c4SAndroid Build Coastguard Worker 789*f81fb7c4SAndroid Build Coastguard Worker def ResetControllers (self, channel): 790*f81fb7c4SAndroid Build Coastguard Worker """Reset controllers to default.""" 791*f81fb7c4SAndroid Build Coastguard Worker self.controllers[channel] = DEFAULT_CONTROLLER_VALUES 792*f81fb7c4SAndroid Build Coastguard Worker self.rpns[channel] = DEFAULT_RPN_VALUES 793*f81fb7c4SAndroid Build Coastguard Worker self.pressure[channel] = 0 794*f81fb7c4SAndroid Build Coastguard Worker 795*f81fb7c4SAndroid Build Coastguard Worker def GenerateEventList (self, ticks, ref_values=None): 796*f81fb7c4SAndroid Build Coastguard Worker """Generate an event list based on controller differences.""" 797*f81fb7c4SAndroid Build Coastguard Worker events = EventList() 798*f81fb7c4SAndroid Build Coastguard Worker 799*f81fb7c4SAndroid Build Coastguard Worker # if no reference values, based on default values 800*f81fb7c4SAndroid Build Coastguard Worker if ref_values is None: 801*f81fb7c4SAndroid Build Coastguard Worker ref_values = MIDIControllers() 802*f81fb7c4SAndroid Build Coastguard Worker 803*f81fb7c4SAndroid Build Coastguard Worker # iterate through 16 MIDI channels 804*f81fb7c4SAndroid Build Coastguard Worker for channel in range(16): 805*f81fb7c4SAndroid Build Coastguard Worker 806*f81fb7c4SAndroid Build Coastguard Worker # generate RPN changes 807*f81fb7c4SAndroid Build Coastguard Worker for rpn in self.rpns[channel].keys(): 808*f81fb7c4SAndroid Build Coastguard Worker value = self.rpns[channel][rpn] 809*f81fb7c4SAndroid Build Coastguard Worker if value != ref_values.rpns[channel][rpn]: 810*f81fb7c4SAndroid Build Coastguard Worker events.append(ControlChangeEvent(ticks, -1, channel, CTRL_RPN_MSB, rpn >> 7)) 811*f81fb7c4SAndroid Build Coastguard Worker events.append(ControlChangeEvent(ticks, -1, channel, CTRL_RPN_LSB, rpn & 0x7f)) 812*f81fb7c4SAndroid Build Coastguard Worker events.append(ControlChangeEvent(ticks, -1, channel, CTRL_RPN_DATA_MSB, value >> 7)) 813*f81fb7c4SAndroid Build Coastguard Worker events.append(ControlChangeEvent(ticks, -1, channel, CTRL_RPN_DATA_LSB, value & 0x7f)) 814*f81fb7c4SAndroid Build Coastguard Worker 815*f81fb7c4SAndroid Build Coastguard Worker # generate controller changes 816*f81fb7c4SAndroid Build Coastguard Worker for controller in self.controllers[channel].keys(): 817*f81fb7c4SAndroid Build Coastguard Worker if self.controllers[channel][controller] != ref_values.controllers[channel][controller]: 818*f81fb7c4SAndroid Build Coastguard Worker events.append(ControlChangeEvent(ticks, -1, channel, controller, self.controllers[channel][controller])) 819*f81fb7c4SAndroid Build Coastguard Worker 820*f81fb7c4SAndroid Build Coastguard Worker # generate pressure changes 821*f81fb7c4SAndroid Build Coastguard Worker if self.pressure[channel] != ref_values.pressure[channel]: 822*f81fb7c4SAndroid Build Coastguard Worker events.append(ChannelPressureEvent(ticks, -1, channel, self.pressure[channel])) 823*f81fb7c4SAndroid Build Coastguard Worker 824*f81fb7c4SAndroid Build Coastguard Worker # generate program changes 825*f81fb7c4SAndroid Build Coastguard Worker if self.program[channel] != ref_values.program[channel]: 826*f81fb7c4SAndroid Build Coastguard Worker if self.program[channel] in range(128): 827*f81fb7c4SAndroid Build Coastguard Worker events.append(ProgramChangeEvent(ticks, -1, channel, self.program[channel])) 828*f81fb7c4SAndroid Build Coastguard Worker 829*f81fb7c4SAndroid Build Coastguard Worker # generate pitch bend changes 830*f81fb7c4SAndroid Build Coastguard Worker if self.pitchbend[channel] != ref_values.pitchbend[channel]: 831*f81fb7c4SAndroid Build Coastguard Worker if self.pitchbend[channel] in range(-8192,8191): 832*f81fb7c4SAndroid Build Coastguard Worker events.append(PitchBendEvent(ticks, -1, channel, self.pitchbend[channel])) 833*f81fb7c4SAndroid Build Coastguard Worker 834*f81fb7c4SAndroid Build Coastguard Worker return events 835*f81fb7c4SAndroid Build Coastguard Worker 836*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 837*f81fb7c4SAndroid Build Coastguard Worker# EventList 838*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 839*f81fb7c4SAndroid Build Coastguard Workerclass EventList (list): 840*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self): 841*f81fb7c4SAndroid Build Coastguard Worker list.__init__(self) 842*f81fb7c4SAndroid Build Coastguard Worker 843*f81fb7c4SAndroid Build Coastguard Worker def FixNoteLengths (self): 844*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Fix note lengths') 845*f81fb7c4SAndroid Build Coastguard Worker 846*f81fb7c4SAndroid Build Coastguard Worker # search for note-on's in event list 847*f81fb7c4SAndroid Build Coastguard Worker for index in range(len(self)): 848*f81fb7c4SAndroid Build Coastguard Worker event = self[index] 849*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == NOTE_ON: 850*f81fb7c4SAndroid Build Coastguard Worker note_off_ticks = event.ticks + event.note_length 851*f81fb7c4SAndroid Build Coastguard Worker 852*f81fb7c4SAndroid Build Coastguard Worker # check for note-on occuring before end of current note 853*f81fb7c4SAndroid Build Coastguard Worker for i in range(index + 1, len(self)): 854*f81fb7c4SAndroid Build Coastguard Worker event_to_check = self[i] 855*f81fb7c4SAndroid Build Coastguard Worker if event_to_check.ticks >= note_off_ticks: 856*f81fb7c4SAndroid Build Coastguard Worker break 857*f81fb7c4SAndroid Build Coastguard Worker 858*f81fb7c4SAndroid Build Coastguard Worker # adjust note length 859*f81fb7c4SAndroid Build Coastguard Worker if (event_to_check.msg_type == NOTE_ON) and (event_to_check.note == event.note): 860*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Adjusting note length @ %d' % event.ticks) 861*f81fb7c4SAndroid Build Coastguard Worker event.note_length = event_to_check.ticks - event.ticks 862*f81fb7c4SAndroid Build Coastguard Worker break 863*f81fb7c4SAndroid Build Coastguard Worker 864*f81fb7c4SAndroid Build Coastguard Worker def ChaseControllers (self, end_seq, start_seq = 0, values = None): 865*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('ChaseControllers from %d to %d' % (start_seq, end_seq)) 866*f81fb7c4SAndroid Build Coastguard Worker 867*f81fb7c4SAndroid Build Coastguard Worker # initialize controller values 868*f81fb7c4SAndroid Build Coastguard Worker if values is None: 869*f81fb7c4SAndroid Build Coastguard Worker values = MIDIControllers() 870*f81fb7c4SAndroid Build Coastguard Worker 871*f81fb7c4SAndroid Build Coastguard Worker # chase controllers in track 872*f81fb7c4SAndroid Build Coastguard Worker for i in range(start_seq, min(end_seq, len(self))): 873*f81fb7c4SAndroid Build Coastguard Worker values.Event(self[i]) 874*f81fb7c4SAndroid Build Coastguard Worker 875*f81fb7c4SAndroid Build Coastguard Worker # return new values 876*f81fb7c4SAndroid Build Coastguard Worker return values 877*f81fb7c4SAndroid Build Coastguard Worker 878*f81fb7c4SAndroid Build Coastguard Worker def SelectEvents (self, start, end): 879*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('SelectEvents: %d to %d' % (start, end)) 880*f81fb7c4SAndroid Build Coastguard Worker selected = EventList() 881*f81fb7c4SAndroid Build Coastguard Worker for event in self: 882*f81fb7c4SAndroid Build Coastguard Worker if event.ticks >= start: 883*f81fb7c4SAndroid Build Coastguard Worker if event.ticks >= end: 884*f81fb7c4SAndroid Build Coastguard Worker break 885*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('SelectEvent: %s' % event.__str__()) 886*f81fb7c4SAndroid Build Coastguard Worker selected.append(event) 887*f81fb7c4SAndroid Build Coastguard Worker return selected 888*f81fb7c4SAndroid Build Coastguard Worker 889*f81fb7c4SAndroid Build Coastguard Worker def MergeEvents (self, events): 890*f81fb7c4SAndroid Build Coastguard Worker # copy events and sort them by ticks/sequence# 891*f81fb7c4SAndroid Build Coastguard Worker self.extend(events) 892*f81fb7c4SAndroid Build Coastguard Worker self.SortEvents() 893*f81fb7c4SAndroid Build Coastguard Worker 894*f81fb7c4SAndroid Build Coastguard Worker def InsertEvents (self, events, seq): 895*f81fb7c4SAndroid Build Coastguard Worker self[seq:seq] = events 896*f81fb7c4SAndroid Build Coastguard Worker self.RenumberSeq() 897*f81fb7c4SAndroid Build Coastguard Worker 898*f81fb7c4SAndroid Build Coastguard Worker def DeleteEvents (self, start_index, end_index, move_meta_events=None): 899*f81fb7c4SAndroid Build Coastguard Worker # default parameters 900*f81fb7c4SAndroid Build Coastguard Worker if start_index is None: 901*f81fb7c4SAndroid Build Coastguard Worker start_index = 0 902*f81fb7c4SAndroid Build Coastguard Worker if end_index is None: 903*f81fb7c4SAndroid Build Coastguard Worker end_index = len(self) 904*f81fb7c4SAndroid Build Coastguard Worker 905*f81fb7c4SAndroid Build Coastguard Worker #print("\n") 906*f81fb7c4SAndroid Build Coastguard Worker #for evt in self[start_index:end_index]: 907*f81fb7c4SAndroid Build Coastguard Worker # print("%d %s" % (evt.ticks, evt)) 908*f81fb7c4SAndroid Build Coastguard Worker 909*f81fb7c4SAndroid Build Coastguard Worker # delete events 910*f81fb7c4SAndroid Build Coastguard Worker delete_count = 0 911*f81fb7c4SAndroid Build Coastguard Worker move_count = 0 912*f81fb7c4SAndroid Build Coastguard Worker for event in self[start_index:end_index]: 913*f81fb7c4SAndroid Build Coastguard Worker #Bth; Added this so we always get clip end events; clips that ended on last measure wouldn't end on repeat 914*f81fb7c4SAndroid Build Coastguard Worker if (event.msg_type == CONTROL_CHANGE) and \ 915*f81fb7c4SAndroid Build Coastguard Worker (event.controller == JET_EVENT_TRIGGER_CLIP) and \ 916*f81fb7c4SAndroid Build Coastguard Worker ((event.value & 0x40) != 0x40): 917*f81fb7c4SAndroid Build Coastguard Worker pass 918*f81fb7c4SAndroid Build Coastguard Worker else: 919*f81fb7c4SAndroid Build Coastguard Worker if (move_meta_events is None) or (event.msg_type != META_EVENT): 920*f81fb7c4SAndroid Build Coastguard Worker self.remove(event) 921*f81fb7c4SAndroid Build Coastguard Worker delete_count += 1 922*f81fb7c4SAndroid Build Coastguard Worker 923*f81fb7c4SAndroid Build Coastguard Worker # move meta-events 924*f81fb7c4SAndroid Build Coastguard Worker else: 925*f81fb7c4SAndroid Build Coastguard Worker event.ticks = move_meta_events 926*f81fb7c4SAndroid Build Coastguard Worker move_count += 1 927*f81fb7c4SAndroid Build Coastguard Worker 928*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('DeleteEvents: deleted %d events in range(%s:%s)' % (delete_count, start_index, end_index)) 929*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('DeleteEvents: moved %d events in range(%s:%s)' % (move_count, start_index, end_index)) 930*f81fb7c4SAndroid Build Coastguard Worker 931*f81fb7c4SAndroid Build Coastguard Worker 932*f81fb7c4SAndroid Build Coastguard Worker def SeekEvent (self, pos): 933*f81fb7c4SAndroid Build Coastguard Worker for i in range(len(self)): 934*f81fb7c4SAndroid Build Coastguard Worker if self[i].ticks >= pos: 935*f81fb7c4SAndroid Build Coastguard Worker return i 936*f81fb7c4SAndroid Build Coastguard Worker return None 937*f81fb7c4SAndroid Build Coastguard Worker 938*f81fb7c4SAndroid Build Coastguard Worker def RenumberSeq (self): 939*f81fb7c4SAndroid Build Coastguard Worker seq = 0 940*f81fb7c4SAndroid Build Coastguard Worker for event in self: 941*f81fb7c4SAndroid Build Coastguard Worker event.seq = seq 942*f81fb7c4SAndroid Build Coastguard Worker seq += 1 943*f81fb7c4SAndroid Build Coastguard Worker 944*f81fb7c4SAndroid Build Coastguard Worker def SortEvents (self): 945*f81fb7c4SAndroid Build Coastguard Worker self.sort(self.EventSorter) 946*f81fb7c4SAndroid Build Coastguard Worker self.RenumberSeq() 947*f81fb7c4SAndroid Build Coastguard Worker 948*f81fb7c4SAndroid Build Coastguard Worker @staticmethod 949*f81fb7c4SAndroid Build Coastguard Worker def EventSorter (x, y): 950*f81fb7c4SAndroid Build Coastguard Worker if x.ticks == y.ticks: 951*f81fb7c4SAndroid Build Coastguard Worker return cmp(x.seq, y.seq) 952*f81fb7c4SAndroid Build Coastguard Worker else: 953*f81fb7c4SAndroid Build Coastguard Worker return cmp(x.ticks, y.ticks) 954*f81fb7c4SAndroid Build Coastguard Worker 955*f81fb7c4SAndroid Build Coastguard Worker def DumpEvents (self, output, timebase): 956*f81fb7c4SAndroid Build Coastguard Worker if output is not None: 957*f81fb7c4SAndroid Build Coastguard Worker for event in self: 958*f81fb7c4SAndroid Build Coastguard Worker output.write('%s\n' % event.TimeEventStr(timebase)) 959*f81fb7c4SAndroid Build Coastguard Worker else: 960*f81fb7c4SAndroid Build Coastguard Worker for event in self: 961*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug(event.TimeEventStr(timebase)) 962*f81fb7c4SAndroid Build Coastguard Worker 963*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 964*f81fb7c4SAndroid Build Coastguard Worker# MIDITrack 965*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 966*f81fb7c4SAndroid Build Coastguard Workerclass MIDITrack (object): 967*f81fb7c4SAndroid Build Coastguard Worker """The MIDITrack class implements methods for reading, parsing, 968*f81fb7c4SAndroid Build Coastguard Worker modifying, and writing tracks in Standard MIDI Files (SMF). 969*f81fb7c4SAndroid Build Coastguard Worker 970*f81fb7c4SAndroid Build Coastguard Worker """ 971*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self): 972*f81fb7c4SAndroid Build Coastguard Worker self.length = 0 973*f81fb7c4SAndroid Build Coastguard Worker self.events = EventList() 974*f81fb7c4SAndroid Build Coastguard Worker self.end_of_track = None 975*f81fb7c4SAndroid Build Coastguard Worker self.channel = None 976*f81fb7c4SAndroid Build Coastguard Worker self.name = None 977*f81fb7c4SAndroid Build Coastguard Worker 978*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (self, stream, offset, file_size): 979*f81fb7c4SAndroid Build Coastguard Worker self.stream = stream 980*f81fb7c4SAndroid Build Coastguard Worker ticks = 0 981*f81fb7c4SAndroid Build Coastguard Worker seq = 0 982*f81fb7c4SAndroid Build Coastguard Worker running_status = None 983*f81fb7c4SAndroid Build Coastguard Worker tick_warning_level = stream.timebase.ppqn * LARGE_TICK_WARNING 984*f81fb7c4SAndroid Build Coastguard Worker 985*f81fb7c4SAndroid Build Coastguard Worker # read the track header - verify it's an SMF track 986*f81fb7c4SAndroid Build Coastguard Worker stream.seek(offset) 987*f81fb7c4SAndroid Build Coastguard Worker bytes = stream.read(struct.calcsize(SMF_TRACK_HEADER_FMT)) 988*f81fb7c4SAndroid Build Coastguard Worker riff_tag, track_len = struct.unpack(SMF_TRACK_HEADER_FMT, bytes) 989*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('SMF track header\n Tag: %s\n TrackLen: %d' % (riff_tag, track_len)) 990*f81fb7c4SAndroid Build Coastguard Worker if (riff_tag != SMF_TRACK_RIFF_TAG): 991*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(stream, MSG_INVALID_TRACK_HEADER) 992*f81fb7c4SAndroid Build Coastguard Worker self.start = stream.tell() 993*f81fb7c4SAndroid Build Coastguard Worker 994*f81fb7c4SAndroid Build Coastguard Worker # check for valid track length 995*f81fb7c4SAndroid Build Coastguard Worker if (self.start + track_len) > file_size: 996*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Ignoring illegal track length - %d exceeds length of file' % track_len) 997*f81fb7c4SAndroid Build Coastguard Worker track_len = None 998*f81fb7c4SAndroid Build Coastguard Worker 999*f81fb7c4SAndroid Build Coastguard Worker # read the entire track 1000*f81fb7c4SAndroid Build Coastguard Worker note_on_list = [] 1001*f81fb7c4SAndroid Build Coastguard Worker while 1: 1002*f81fb7c4SAndroid Build Coastguard Worker 1003*f81fb7c4SAndroid Build Coastguard Worker # save current position 1004*f81fb7c4SAndroid Build Coastguard Worker pos = stream.tell() 1005*f81fb7c4SAndroid Build Coastguard Worker 1006*f81fb7c4SAndroid Build Coastguard Worker # check for end of track 1007*f81fb7c4SAndroid Build Coastguard Worker if track_len is not None: 1008*f81fb7c4SAndroid Build Coastguard Worker if (pos - self.start) >= track_len: 1009*f81fb7c4SAndroid Build Coastguard Worker break 1010*f81fb7c4SAndroid Build Coastguard Worker 1011*f81fb7c4SAndroid Build Coastguard Worker # are we past end of track? 1012*f81fb7c4SAndroid Build Coastguard Worker if self.end_of_track: 1013*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Ignoring data encountered beyond end-of-track meta-event') 1014*f81fb7c4SAndroid Build Coastguard Worker break; 1015*f81fb7c4SAndroid Build Coastguard Worker 1016*f81fb7c4SAndroid Build Coastguard Worker # read delta timestamp 1017*f81fb7c4SAndroid Build Coastguard Worker delta = ReadVarLenQty(stream) 1018*f81fb7c4SAndroid Build Coastguard Worker if ticks > tick_warning_level: 1019*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Tick value is excessive - possibly corrupt data?') 1020*f81fb7c4SAndroid Build Coastguard Worker ticks += delta 1021*f81fb7c4SAndroid Build Coastguard Worker 1022*f81fb7c4SAndroid Build Coastguard Worker # get the event type and process it 1023*f81fb7c4SAndroid Build Coastguard Worker msg_type = ReadByte(stream) 1024*f81fb7c4SAndroid Build Coastguard Worker 1025*f81fb7c4SAndroid Build Coastguard Worker # if data byte, check for running status 1026*f81fb7c4SAndroid Build Coastguard Worker if msg_type & 0x80 == 0: 1027*f81fb7c4SAndroid Build Coastguard Worker 1028*f81fb7c4SAndroid Build Coastguard Worker # use running status 1029*f81fb7c4SAndroid Build Coastguard Worker msg_type = running_status 1030*f81fb7c4SAndroid Build Coastguard Worker 1031*f81fb7c4SAndroid Build Coastguard Worker # back up so event can process data 1032*f81fb7c4SAndroid Build Coastguard Worker stream.seek(-1,1) 1033*f81fb7c4SAndroid Build Coastguard Worker 1034*f81fb7c4SAndroid Build Coastguard Worker # if no running status, we have a problem 1035*f81fb7c4SAndroid Build Coastguard Worker if not running_status: 1036*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Ignoring data byte received with no running status') 1037*f81fb7c4SAndroid Build Coastguard Worker 1038*f81fb7c4SAndroid Build Coastguard Worker # create event type from stream 1039*f81fb7c4SAndroid Build Coastguard Worker event = MIDIEvent.ReadFromStream(stream, seq, ticks, msg_type) 1040*f81fb7c4SAndroid Build Coastguard Worker 1041*f81fb7c4SAndroid Build Coastguard Worker if self.channel == None: 1042*f81fb7c4SAndroid Build Coastguard Worker try: 1043*f81fb7c4SAndroid Build Coastguard Worker self.channel = event.channel 1044*f81fb7c4SAndroid Build Coastguard Worker except AttributeError: 1045*f81fb7c4SAndroid Build Coastguard Worker pass 1046*f81fb7c4SAndroid Build Coastguard Worker 1047*f81fb7c4SAndroid Build Coastguard Worker # track note-ons 1048*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == NOTE_ON: 1049*f81fb7c4SAndroid Build Coastguard Worker 1050*f81fb7c4SAndroid Build Coastguard Worker """ 1051*f81fb7c4SAndroid Build Coastguard Worker Experimental code to clean up overlapping notes 1052*f81fb7c4SAndroid Build Coastguard Worker Clean up now occurs during write process 1053*f81fb7c4SAndroid Build Coastguard Worker 1054*f81fb7c4SAndroid Build Coastguard Worker for note_on in note_on_list: 1055*f81fb7c4SAndroid Build Coastguard Worker if (event.channel == note_on.channel) and (event.note == note_on.note): 1056*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Duplicate note-on\'s encountered without intervening note-off') 1057*f81fb7c4SAndroid Build Coastguard Worker stream.Warning(' [%s]: %s' % (stream.timebase.ConvertTicksToStr(event.ticks), event.__str__())) 1058*f81fb7c4SAndroid Build Coastguard Worker note_on.note_length = event.ticks - note_on.ticks - 1 1059*f81fb7c4SAndroid Build Coastguard Worker if note_on.note_length <= 0: 1060*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('Eliminating duplicate note-on') 1061*f81fb7c4SAndroid Build Coastguard Worker event.ticks = note_on.ticks 1062*f81fb7c4SAndroid Build Coastguard Worker self.events.remove(note_on) 1063*f81fb7c4SAndroid Build Coastguard Worker """ 1064*f81fb7c4SAndroid Build Coastguard Worker 1065*f81fb7c4SAndroid Build Coastguard Worker note_on_list.append(event) 1066*f81fb7c4SAndroid Build Coastguard Worker 1067*f81fb7c4SAndroid Build Coastguard Worker # process note-offs 1068*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == NOTE_OFF: 1069*f81fb7c4SAndroid Build Coastguard Worker for note_on in note_on_list[:]: 1070*f81fb7c4SAndroid Build Coastguard Worker if (event.channel == note_on.channel) and (event.note == note_on.note): 1071*f81fb7c4SAndroid Build Coastguard Worker note_on.note_length = event.ticks - note_on.ticks 1072*f81fb7c4SAndroid Build Coastguard Worker note_on.note_off_velocity = event.velocity 1073*f81fb7c4SAndroid Build Coastguard Worker note_on_list.remove(note_on) 1074*f81fb7c4SAndroid Build Coastguard Worker break 1075*f81fb7c4SAndroid Build Coastguard Worker #else: 1076*f81fb7c4SAndroid Build Coastguard Worker # stream.Warning('Note-off encountered without corresponding note-on') 1077*f81fb7c4SAndroid Build Coastguard Worker # stream.Warning(' [%s]: %s' % (stream.timebase.ConvertTicksToStr(event.ticks), event.__str__())) 1078*f81fb7c4SAndroid Build Coastguard Worker 1079*f81fb7c4SAndroid Build Coastguard Worker # check for end of track 1080*f81fb7c4SAndroid Build Coastguard Worker elif event.msg_type == META_EVENT and event.meta_type == META_EVENT_END_OF_TRACK: 1081*f81fb7c4SAndroid Build Coastguard Worker self.end_of_track = event.ticks 1082*f81fb7c4SAndroid Build Coastguard Worker 1083*f81fb7c4SAndroid Build Coastguard Worker # BTH; get track name 1084*f81fb7c4SAndroid Build Coastguard Worker elif event.msg_type == META_EVENT and event.meta_type == META_EVENT_SEQUENCE_TRACK_NAME: 1085*f81fb7c4SAndroid Build Coastguard Worker self.name = array.array('B', event.msg).tostring() 1086*f81fb7c4SAndroid Build Coastguard Worker 1087*f81fb7c4SAndroid Build Coastguard Worker # append event to event list 1088*f81fb7c4SAndroid Build Coastguard Worker else: 1089*f81fb7c4SAndroid Build Coastguard Worker self.events.append(event) 1090*f81fb7c4SAndroid Build Coastguard Worker seq += 1 1091*f81fb7c4SAndroid Build Coastguard Worker 1092*f81fb7c4SAndroid Build Coastguard Worker # save position for port-mortem 1093*f81fb7c4SAndroid Build Coastguard Worker stream.last_good_event = pos 1094*f81fb7c4SAndroid Build Coastguard Worker 1095*f81fb7c4SAndroid Build Coastguard Worker # update running statusc_str( 1096*f81fb7c4SAndroid Build Coastguard Worker if msg_type < 0xf0: 1097*f81fb7c4SAndroid Build Coastguard Worker running_status = msg_type 1098*f81fb7c4SAndroid Build Coastguard Worker elif (msg_type < 0xf8) or (msg_type == 0xff): 1099*f81fb7c4SAndroid Build Coastguard Worker running_status = None 1100*f81fb7c4SAndroid Build Coastguard Worker 1101*f81fb7c4SAndroid Build Coastguard Worker # check for stuck notes 1102*f81fb7c4SAndroid Build Coastguard Worker #if len(note_on_list): 1103*f81fb7c4SAndroid Build Coastguard Worker # stream.Warning('Note-ons encountered without corresponding note-offs') 1104*f81fb7c4SAndroid Build Coastguard Worker 1105*f81fb7c4SAndroid Build Coastguard Worker # check for missing end-of-track meta-event 1106*f81fb7c4SAndroid Build Coastguard Worker if self.end_of_track is None: 1107*f81fb7c4SAndroid Build Coastguard Worker self.last_tick = self.events[-1].ticks 1108*f81fb7c4SAndroid Build Coastguard Worker stream.Warning('End of track encountered with no end-of-track meta-event') 1109*f81fb7c4SAndroid Build Coastguard Worker 1110*f81fb7c4SAndroid Build Coastguard Worker # if track length was bad, correct it 1111*f81fb7c4SAndroid Build Coastguard Worker if track_len is None: 1112*f81fb7c4SAndroid Build Coastguard Worker track_len = stream.tell() - offset - 8 1113*f81fb7c4SAndroid Build Coastguard Worker 1114*f81fb7c4SAndroid Build Coastguard Worker return track_len 1115*f81fb7c4SAndroid Build Coastguard Worker 1116*f81fb7c4SAndroid Build Coastguard Worker def Write (self, stream, filters=None): 1117*f81fb7c4SAndroid Build Coastguard Worker # save current file position so we can write header 1118*f81fb7c4SAndroid Build Coastguard Worker header_loc = stream.tell() 1119*f81fb7c4SAndroid Build Coastguard Worker stream.seek(header_loc + struct.calcsize(SMF_TRACK_HEADER_FMT)) 1120*f81fb7c4SAndroid Build Coastguard Worker 1121*f81fb7c4SAndroid Build Coastguard Worker # save a copy of the event list so we can restore it 1122*f81fb7c4SAndroid Build Coastguard Worker save_events = copy.copy(self.events) 1123*f81fb7c4SAndroid Build Coastguard Worker 1124*f81fb7c4SAndroid Build Coastguard Worker # create note-off events 1125*f81fb7c4SAndroid Build Coastguard Worker index = 0 1126*f81fb7c4SAndroid Build Coastguard Worker while 1: 1127*f81fb7c4SAndroid Build Coastguard Worker if index >= len(self.events): 1128*f81fb7c4SAndroid Build Coastguard Worker break 1129*f81fb7c4SAndroid Build Coastguard Worker 1130*f81fb7c4SAndroid Build Coastguard Worker # if note-on event, create a note-off event 1131*f81fb7c4SAndroid Build Coastguard Worker event = self.events[index] 1132*f81fb7c4SAndroid Build Coastguard Worker index += 1 1133*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == NOTE_ON: 1134*f81fb7c4SAndroid Build Coastguard Worker note_off = NoteOffEvent(event.ticks + event.note_length, index, event.channel, event.note, event.note_off_velocity) 1135*f81fb7c4SAndroid Build Coastguard Worker 1136*f81fb7c4SAndroid Build Coastguard Worker # insert note-off in list 1137*f81fb7c4SAndroid Build Coastguard Worker for i in range(index, len(self.events)): 1138*f81fb7c4SAndroid Build Coastguard Worker if self.events[i].ticks >= note_off.ticks: 1139*f81fb7c4SAndroid Build Coastguard Worker self.events.insert(i, note_off) 1140*f81fb7c4SAndroid Build Coastguard Worker break 1141*f81fb7c4SAndroid Build Coastguard Worker else: 1142*f81fb7c4SAndroid Build Coastguard Worker self.events.append(note_off) 1143*f81fb7c4SAndroid Build Coastguard Worker 1144*f81fb7c4SAndroid Build Coastguard Worker # renumber list 1145*f81fb7c4SAndroid Build Coastguard Worker self.events.RenumberSeq() 1146*f81fb7c4SAndroid Build Coastguard Worker 1147*f81fb7c4SAndroid Build Coastguard Worker # write the events 1148*f81fb7c4SAndroid Build Coastguard Worker self.running_status = None 1149*f81fb7c4SAndroid Build Coastguard Worker self.ticks = 0 1150*f81fb7c4SAndroid Build Coastguard Worker for event in self.events: 1151*f81fb7c4SAndroid Build Coastguard Worker 1152*f81fb7c4SAndroid Build Coastguard Worker # write event 1153*f81fb7c4SAndroid Build Coastguard Worker event.WriteToStream(stream, self, filters) 1154*f81fb7c4SAndroid Build Coastguard Worker 1155*f81fb7c4SAndroid Build Coastguard Worker # restore original list (without note-off events) 1156*f81fb7c4SAndroid Build Coastguard Worker self.events = save_events 1157*f81fb7c4SAndroid Build Coastguard Worker 1158*f81fb7c4SAndroid Build Coastguard Worker # write the end-of-track meta-event 1159*f81fb7c4SAndroid Build Coastguard Worker MetaEvent(self.end_of_track, 0, META_EVENT_END_OF_TRACK,[]).WriteToStream(stream, self, None) 1160*f81fb7c4SAndroid Build Coastguard Worker 1161*f81fb7c4SAndroid Build Coastguard Worker # write track header 1162*f81fb7c4SAndroid Build Coastguard Worker end_of_track = stream.tell() 1163*f81fb7c4SAndroid Build Coastguard Worker track_len = end_of_track - header_loc - struct.calcsize(SMF_TRACK_HEADER_FMT) 1164*f81fb7c4SAndroid Build Coastguard Worker stream.seek(header_loc) 1165*f81fb7c4SAndroid Build Coastguard Worker bytes = struct.pack(SMF_TRACK_HEADER_FMT, SMF_TRACK_RIFF_TAG, track_len) 1166*f81fb7c4SAndroid Build Coastguard Worker stream.write(bytes) 1167*f81fb7c4SAndroid Build Coastguard Worker stream.seek(end_of_track) 1168*f81fb7c4SAndroid Build Coastguard Worker 1169*f81fb7c4SAndroid Build Coastguard Worker def Trim (self, start, end, slide=True, chase_controllers=True, delete_meta_events=False, quantize=0): 1170*f81fb7c4SAndroid Build Coastguard Worker controllers = None 1171*f81fb7c4SAndroid Build Coastguard Worker 1172*f81fb7c4SAndroid Build Coastguard Worker if quantize: 1173*f81fb7c4SAndroid Build Coastguard Worker # quantize events just before start 1174*f81fb7c4SAndroid Build Coastguard Worker for event in self.events.SelectEvents(start - quantize, start): 1175*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: Moving event %s to %d' % (event.__str__(), start)) 1176*f81fb7c4SAndroid Build Coastguard Worker event.ticks = start 1177*f81fb7c4SAndroid Build Coastguard Worker 1178*f81fb7c4SAndroid Build Coastguard Worker # quantize events just before end 1179*f81fb7c4SAndroid Build Coastguard Worker for event in self.events.SelectEvents(end - quantize, end): 1180*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: Moving event %s to %d' % (event.__str__(), end)) 1181*f81fb7c4SAndroid Build Coastguard Worker event.ticks = end 1182*f81fb7c4SAndroid Build Coastguard Worker 1183*f81fb7c4SAndroid Build Coastguard Worker # trim start 1184*f81fb7c4SAndroid Build Coastguard Worker if start: 1185*f81fb7c4SAndroid Build Coastguard Worker 1186*f81fb7c4SAndroid Build Coastguard Worker # find first event inside trim 1187*f81fb7c4SAndroid Build Coastguard Worker start_event = self.events.SeekEvent(start) 1188*f81fb7c4SAndroid Build Coastguard Worker if start_event is not None: 1189*f81fb7c4SAndroid Build Coastguard Worker 1190*f81fb7c4SAndroid Build Coastguard Worker # chase controllers to cut point 1191*f81fb7c4SAndroid Build Coastguard Worker if chase_controllers: 1192*f81fb7c4SAndroid Build Coastguard Worker controllers = self.events.ChaseControllers(self.events[start_event].seq) 1193*f81fb7c4SAndroid Build Coastguard Worker controller_events = controllers.GenerateEventList(0) 1194*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: insert new controller events at %d:' % start) 1195*f81fb7c4SAndroid Build Coastguard Worker controller_events.DumpEvents(None, self.stream.timebase) 1196*f81fb7c4SAndroid Build Coastguard Worker self.events.InsertEvents(controller_events, start_event) 1197*f81fb7c4SAndroid Build Coastguard Worker 1198*f81fb7c4SAndroid Build Coastguard Worker # delete events 1199*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: deleting events up to event %d' % start_event) 1200*f81fb7c4SAndroid Build Coastguard Worker if delete_meta_events: 1201*f81fb7c4SAndroid Build Coastguard Worker self.events.DeleteEvents(None, start_event, None) 1202*f81fb7c4SAndroid Build Coastguard Worker else: 1203*f81fb7c4SAndroid Build Coastguard Worker self.events.DeleteEvents(None, start_event, start) 1204*f81fb7c4SAndroid Build Coastguard Worker 1205*f81fb7c4SAndroid Build Coastguard Worker # delete everything except metadata 1206*f81fb7c4SAndroid Build Coastguard Worker else: 1207*f81fb7c4SAndroid Build Coastguard Worker self.events.DeleteEvents(None, None, start) 1208*f81fb7c4SAndroid Build Coastguard Worker 1209*f81fb7c4SAndroid Build Coastguard Worker # trim end 1210*f81fb7c4SAndroid Build Coastguard Worker end_event = self.events.SeekEvent(end) 1211*f81fb7c4SAndroid Build Coastguard Worker if end_event is not None: 1212*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: trimming section starting at event %d' % end_event) 1213*f81fb7c4SAndroid Build Coastguard Worker self.events.DeleteEvents(end_event, None) 1214*f81fb7c4SAndroid Build Coastguard Worker 1215*f81fb7c4SAndroid Build Coastguard Worker # trim any notes that extend past the end 1216*f81fb7c4SAndroid Build Coastguard Worker for event in self.events: 1217*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type == NOTE_ON: 1218*f81fb7c4SAndroid Build Coastguard Worker if (event.ticks + event.note_length) > end: 1219*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: trimming note that extends past end %s' % event.TimeEventStr(self.stream.timebase)) 1220*f81fb7c4SAndroid Build Coastguard Worker event.note_length = end - event.ticks 1221*f81fb7c4SAndroid Build Coastguard Worker if event.note_length <= 0: 1222*f81fb7c4SAndroid Build Coastguard Worker raise 'Error in note length - note should have been deleted' 1223*f81fb7c4SAndroid Build Coastguard Worker 1224*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: initial end-of-track: %d' % self.end_of_track) 1225*f81fb7c4SAndroid Build Coastguard Worker self.end_of_track = min(self.end_of_track, end) 1226*f81fb7c4SAndroid Build Coastguard Worker 1227*f81fb7c4SAndroid Build Coastguard Worker # slide events to start of track to fill hole 1228*f81fb7c4SAndroid Build Coastguard Worker if slide and start: 1229*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: sliding events: %d' % start) 1230*f81fb7c4SAndroid Build Coastguard Worker for event in self.events: 1231*f81fb7c4SAndroid Build Coastguard Worker if event.ticks > start: 1232*f81fb7c4SAndroid Build Coastguard Worker event.ticks -= start 1233*f81fb7c4SAndroid Build Coastguard Worker else: 1234*f81fb7c4SAndroid Build Coastguard Worker event.ticks = 0 1235*f81fb7c4SAndroid Build Coastguard Worker self.end_of_track = max(0, self.end_of_track - start) 1236*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trim: new end-of-track: %d' % self.end_of_track) 1237*f81fb7c4SAndroid Build Coastguard Worker 1238*f81fb7c4SAndroid Build Coastguard Worker self.events.RenumberSeq() 1239*f81fb7c4SAndroid Build Coastguard Worker self.events.FixNoteLengths() 1240*f81fb7c4SAndroid Build Coastguard Worker 1241*f81fb7c4SAndroid Build Coastguard Worker def DumpEvents (self, output): 1242*f81fb7c4SAndroid Build Coastguard Worker self.events.DumpEvents(output, self.stream.timebase) 1243*f81fb7c4SAndroid Build Coastguard Worker if output is not None: 1244*f81fb7c4SAndroid Build Coastguard Worker output.write('[%s]: end-of-track\n' % self.stream.timebase.ConvertTicksToStr(self.end_of_track)) 1245*f81fb7c4SAndroid Build Coastguard Worker else: 1246*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('[%s]: end-of-track' % self.stream.timebase.ConvertTicksToStr(self.end_of_track)) 1247*f81fb7c4SAndroid Build Coastguard Worker 1248*f81fb7c4SAndroid Build Coastguard Worker 1249*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 1250*f81fb7c4SAndroid Build Coastguard Worker# MIDIFile 1251*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 1252*f81fb7c4SAndroid Build Coastguard Workerclass MIDIFile (file): 1253*f81fb7c4SAndroid Build Coastguard Worker """The MIDIFile class implements methods for reading, parsing, 1254*f81fb7c4SAndroid Build Coastguard Worker modifying, and writing Standard MIDI Files (SMF). 1255*f81fb7c4SAndroid Build Coastguard Worker 1256*f81fb7c4SAndroid Build Coastguard Worker """ 1257*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self, name, mode): 1258*f81fb7c4SAndroid Build Coastguard Worker file.__init__(self, name, mode) 1259*f81fb7c4SAndroid Build Coastguard Worker self.timebase = TimeBase() 1260*f81fb7c4SAndroid Build Coastguard Worker 1261*f81fb7c4SAndroid Build Coastguard Worker def ReadFromStream (self, start_offset=0, file_size=None): 1262*f81fb7c4SAndroid Build Coastguard Worker """Parse the MIDI file creating a list of properties, tracks, 1263*f81fb7c4SAndroid Build Coastguard Worker and events based on the contents of the file. 1264*f81fb7c4SAndroid Build Coastguard Worker 1265*f81fb7c4SAndroid Build Coastguard Worker """ 1266*f81fb7c4SAndroid Build Coastguard Worker 1267*f81fb7c4SAndroid Build Coastguard Worker # determine file size - without using os.stat 1268*f81fb7c4SAndroid Build Coastguard Worker if file_size == None: 1269*f81fb7c4SAndroid Build Coastguard Worker self.start_offset = start_offset 1270*f81fb7c4SAndroid Build Coastguard Worker self.seek(0,2) 1271*f81fb7c4SAndroid Build Coastguard Worker file_size = self.tell() - self.start_offset 1272*f81fb7c4SAndroid Build Coastguard Worker self.seek(start_offset,0) 1273*f81fb7c4SAndroid Build Coastguard Worker else: 1274*f81fb7c4SAndroid Build Coastguard Worker file_size = file_size 1275*f81fb7c4SAndroid Build Coastguard Worker 1276*f81fb7c4SAndroid Build Coastguard Worker # for error recovery 1277*f81fb7c4SAndroid Build Coastguard Worker self.last_good_event = None 1278*f81fb7c4SAndroid Build Coastguard Worker self.error_loc = None 1279*f81fb7c4SAndroid Build Coastguard Worker 1280*f81fb7c4SAndroid Build Coastguard Worker # read the file header - verify it's an SMF file 1281*f81fb7c4SAndroid Build Coastguard Worker bytes = self.read(struct.calcsize(SMF_HEADER_FMT)) 1282*f81fb7c4SAndroid Build Coastguard Worker riff_tag, self.hdr_len, self.format, self.num_tracks, self.timebase.ppqn = struct.unpack(SMF_HEADER_FMT, bytes) 1283*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('SMF header\n Tag: %s\n HeaderLen: %d\n Format: %d\n NumTracks: %d\n PPQN: %d\n' % \ 1284*f81fb7c4SAndroid Build Coastguard Worker (riff_tag, self.hdr_len, self.format, self.num_tracks, self.timebase.ppqn)) 1285*f81fb7c4SAndroid Build Coastguard Worker 1286*f81fb7c4SAndroid Build Coastguard Worker # sanity check on header 1287*f81fb7c4SAndroid Build Coastguard Worker if (riff_tag != SMF_RIFF_TAG) or (self.format not in range(2)): 1288*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(self, MSG_NOT_SMF_FILE) 1289*f81fb7c4SAndroid Build Coastguard Worker 1290*f81fb7c4SAndroid Build Coastguard Worker # check for odd header size 1291*f81fb7c4SAndroid Build Coastguard Worker if self.hdr_len + 8 != struct.calcsize(SMF_HEADER_FMT): 1292*f81fb7c4SAndroid Build Coastguard Worker self.Warning('SMF file has unusual header size: %d bytes' % self.hdr_len) 1293*f81fb7c4SAndroid Build Coastguard Worker 1294*f81fb7c4SAndroid Build Coastguard Worker # read each of the tracks 1295*f81fb7c4SAndroid Build Coastguard Worker offset = start_offset + self.hdr_len + 8 1296*f81fb7c4SAndroid Build Coastguard Worker self.tracks = [] 1297*f81fb7c4SAndroid Build Coastguard Worker self.end_of_file = 0 1298*f81fb7c4SAndroid Build Coastguard Worker for i in range(self.num_tracks): 1299*f81fb7c4SAndroid Build Coastguard Worker #print("Track: %d" % i) 1300*f81fb7c4SAndroid Build Coastguard Worker 1301*f81fb7c4SAndroid Build Coastguard Worker # parse the track 1302*f81fb7c4SAndroid Build Coastguard Worker track = MIDITrack() 1303*f81fb7c4SAndroid Build Coastguard Worker length = track.ReadFromStream(self, offset, file_size) 1304*f81fb7c4SAndroid Build Coastguard Worker track.trackNum = i 1305*f81fb7c4SAndroid Build Coastguard Worker 1306*f81fb7c4SAndroid Build Coastguard Worker self.tracks.append(track) 1307*f81fb7c4SAndroid Build Coastguard Worker 1308*f81fb7c4SAndroid Build Coastguard Worker # calculate offset to next track 1309*f81fb7c4SAndroid Build Coastguard Worker offset += length + 8 1310*f81fb7c4SAndroid Build Coastguard Worker 1311*f81fb7c4SAndroid Build Coastguard Worker # determine time of last event 1312*f81fb7c4SAndroid Build Coastguard Worker self.end_of_file = max(self.end_of_file, track.end_of_track) 1313*f81fb7c4SAndroid Build Coastguard Worker 1314*f81fb7c4SAndroid Build Coastguard Worker # if start_offset is zero, the final offset should match the file length 1315*f81fb7c4SAndroid Build Coastguard Worker if (offset - start_offset) != file_size: 1316*f81fb7c4SAndroid Build Coastguard Worker self.Warning('SMF file size is incorrect - should be %d, was %d' % (file_size, offset)) 1317*f81fb7c4SAndroid Build Coastguard Worker 1318*f81fb7c4SAndroid Build Coastguard Worker def Save (self, offset=0, filters=None): 1319*f81fb7c4SAndroid Build Coastguard Worker """Save this file back to disk with modifications.""" 1320*f81fb7c4SAndroid Build Coastguard Worker if (not 'w' in self.mode) and (not '+' in self.mode): 1321*f81fb7c4SAndroid Build Coastguard Worker raise MIDIFileException(self, 'Cannot write to file in read-only mode') 1322*f81fb7c4SAndroid Build Coastguard Worker self.Write(self, offset, filters) 1323*f81fb7c4SAndroid Build Coastguard Worker 1324*f81fb7c4SAndroid Build Coastguard Worker def SaveAs (self, filename, offset=0, filters=None): 1325*f81fb7c4SAndroid Build Coastguard Worker """Save MIDI data to new file.""" 1326*f81fb7c4SAndroid Build Coastguard Worker output_file = MIDIFile(filename, 'wb') 1327*f81fb7c4SAndroid Build Coastguard Worker self.Write(output_file, offset, filters) 1328*f81fb7c4SAndroid Build Coastguard Worker output_file.close() 1329*f81fb7c4SAndroid Build Coastguard Worker 1330*f81fb7c4SAndroid Build Coastguard Worker def Write (self, output_file, offset=0, filters=None): 1331*f81fb7c4SAndroid Build Coastguard Worker """This function does the actual work of writing the file.""" 1332*f81fb7c4SAndroid Build Coastguard Worker # write the file header 1333*f81fb7c4SAndroid Build Coastguard Worker output_file.seek(offset) 1334*f81fb7c4SAndroid Build Coastguard Worker bytes = struct.pack(SMF_HEADER_FMT, SMF_RIFF_TAG, struct.calcsize(SMF_HEADER_FMT) - 8, self.format, self.num_tracks, self.timebase.ppqn) 1335*f81fb7c4SAndroid Build Coastguard Worker output_file.write(bytes) 1336*f81fb7c4SAndroid Build Coastguard Worker 1337*f81fb7c4SAndroid Build Coastguard Worker # write out the tracks 1338*f81fb7c4SAndroid Build Coastguard Worker for track in self.tracks: 1339*f81fb7c4SAndroid Build Coastguard Worker track.Write(output_file, filters) 1340*f81fb7c4SAndroid Build Coastguard Worker 1341*f81fb7c4SAndroid Build Coastguard Worker # flush the data to disk 1342*f81fb7c4SAndroid Build Coastguard Worker output_file.flush() 1343*f81fb7c4SAndroid Build Coastguard Worker 1344*f81fb7c4SAndroid Build Coastguard Worker def ConvertToType0 (self): 1345*f81fb7c4SAndroid Build Coastguard Worker """Convert a file to type 0.""" 1346*f81fb7c4SAndroid Build Coastguard Worker if self.format == 0: 1347*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.warning('File is already type 0 - ignoring request to convert') 1348*f81fb7c4SAndroid Build Coastguard Worker return 1349*f81fb7c4SAndroid Build Coastguard Worker 1350*f81fb7c4SAndroid Build Coastguard Worker # convert to type 0 1351*f81fb7c4SAndroid Build Coastguard Worker for track in self.tracks[1:]: 1352*f81fb7c4SAndroid Build Coastguard Worker self.tracks[0].MergeEvents(track.events) 1353*f81fb7c4SAndroid Build Coastguard Worker self.tracks = self.tracks[:1] 1354*f81fb7c4SAndroid Build Coastguard Worker self.num_tracks = 1 1355*f81fb7c4SAndroid Build Coastguard Worker self.format = 0 1356*f81fb7c4SAndroid Build Coastguard Worker 1357*f81fb7c4SAndroid Build Coastguard Worker def DeleteEmptyTracks (self): 1358*f81fb7c4SAndroid Build Coastguard Worker """Delete any tracks that do not contain MIDI messages""" 1359*f81fb7c4SAndroid Build Coastguard Worker track_num = 0 1360*f81fb7c4SAndroid Build Coastguard Worker for track in self.tracks[:]: 1361*f81fb7c4SAndroid Build Coastguard Worker for event in self.tracks.events: 1362*f81fb7c4SAndroid Build Coastguard Worker if event.msg_type in MIDI_MESSAGES: 1363*f81fb7c4SAndroid Build Coastguard Worker break; 1364*f81fb7c4SAndroid Build Coastguard Worker else: 1365*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Deleting track %d' % track_num) 1366*f81fb7c4SAndroid Build Coastguard Worker self.tracks.remove(track) 1367*f81fb7c4SAndroid Build Coastguard Worker track_num += 1 1368*f81fb7c4SAndroid Build Coastguard Worker 1369*f81fb7c4SAndroid Build Coastguard Worker def ConvertToTicks (self, measures, beats, ticks): 1370*f81fb7c4SAndroid Build Coastguard Worker return self.timebase.ConvertToTicks(measures, beats, ticks) 1371*f81fb7c4SAndroid Build Coastguard Worker 1372*f81fb7c4SAndroid Build Coastguard Worker def Trim (self, start, end, quantize=0, chase_controllers=True): 1373*f81fb7c4SAndroid Build Coastguard Worker track_num = 0 1374*f81fb7c4SAndroid Build Coastguard Worker for track in self.tracks: 1375*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('Trimming track %d' % track_num) 1376*f81fb7c4SAndroid Build Coastguard Worker track.Trim(start, end, quantize=quantize, chase_controllers=chase_controllers) 1377*f81fb7c4SAndroid Build Coastguard Worker track_num += 1 1378*f81fb7c4SAndroid Build Coastguard Worker 1379*f81fb7c4SAndroid Build Coastguard Worker def DumpTracks (self, output=None): 1380*f81fb7c4SAndroid Build Coastguard Worker track_num = 0 1381*f81fb7c4SAndroid Build Coastguard Worker for track in self.tracks: 1382*f81fb7c4SAndroid Build Coastguard Worker if output is None: 1383*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.debug('*** Track %d ***' % track_num) 1384*f81fb7c4SAndroid Build Coastguard Worker else: 1385*f81fb7c4SAndroid Build Coastguard Worker output.write('*** Track %d ***' % track_num) 1386*f81fb7c4SAndroid Build Coastguard Worker track.DumpEvents(output) 1387*f81fb7c4SAndroid Build Coastguard Worker track_num += 1 1388*f81fb7c4SAndroid Build Coastguard Worker 1389*f81fb7c4SAndroid Build Coastguard Worker def Warning (self, msg): 1390*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.warning('[%d]: %s' % (self.tell(), msg)) 1391*f81fb7c4SAndroid Build Coastguard Worker 1392*f81fb7c4SAndroid Build Coastguard Worker def Error (self, msg): 1393*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('[%d]: %s' % (self.tell(), msg)) 1394*f81fb7c4SAndroid Build Coastguard Worker 1395*f81fb7c4SAndroid Build Coastguard Worker def DumpError (self): 1396*f81fb7c4SAndroid Build Coastguard Worker if self.last_good_event: 1397*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('Dumping from last good event:') 1398*f81fb7c4SAndroid Build Coastguard Worker pos = self.last_good_event - 16 1399*f81fb7c4SAndroid Build Coastguard Worker length = self.error_loc - pos + 16 1400*f81fb7c4SAndroid Build Coastguard Worker elif self.error_loc: 1401*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('Dumping from 16 bytes prior to error:') 1402*f81fb7c4SAndroid Build Coastguard Worker pos = self.error_loc 1403*f81fb7c4SAndroid Build Coastguard Worker length = 32 1404*f81fb7c4SAndroid Build Coastguard Worker else: 1405*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('No dump information available') 1406*f81fb7c4SAndroid Build Coastguard Worker return 1407*f81fb7c4SAndroid Build Coastguard Worker 1408*f81fb7c4SAndroid Build Coastguard Worker self.seek(pos, 0) 1409*f81fb7c4SAndroid Build Coastguard Worker for i in range(length): 1410*f81fb7c4SAndroid Build Coastguard Worker if i % 16 == 0: 1411*f81fb7c4SAndroid Build Coastguard Worker if i: 1412*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error(' '.join(debug_out)) 1413*f81fb7c4SAndroid Build Coastguard Worker debug_out = ['%08x:' % (pos + i)] 1414*f81fb7c4SAndroid Build Coastguard Worker byte = self.read(1) 1415*f81fb7c4SAndroid Build Coastguard Worker if len(byte) == 0: 1416*f81fb7c4SAndroid Build Coastguard Worker break; 1417*f81fb7c4SAndroid Build Coastguard Worker debug_out.append('%02x' % ord(byte)) 1418*f81fb7c4SAndroid Build Coastguard Worker if i % 16 > 0: 1419*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error(' '.join(debug_out)) 1420*f81fb7c4SAndroid Build Coastguard Worker 1421*f81fb7c4SAndroid Build Coastguard Workerdef GetMidiInfo(midiFile): 1422*f81fb7c4SAndroid Build Coastguard Worker """Bth; Get MIDI info""" 1423*f81fb7c4SAndroid Build Coastguard Worker 1424*f81fb7c4SAndroid Build Coastguard Worker class midiData(object): 1425*f81fb7c4SAndroid Build Coastguard Worker def __init__ (self): 1426*f81fb7c4SAndroid Build Coastguard Worker self.err = 1 1427*f81fb7c4SAndroid Build Coastguard Worker self.endMbt = "0:0:0" 1428*f81fb7c4SAndroid Build Coastguard Worker self.totalTicks = 0 1429*f81fb7c4SAndroid Build Coastguard Worker self.maxTracks = 0 1430*f81fb7c4SAndroid Build Coastguard Worker self.maxMeasures = 0 1431*f81fb7c4SAndroid Build Coastguard Worker self.maxBeats = 0 1432*f81fb7c4SAndroid Build Coastguard Worker self.maxTicks = 0 1433*f81fb7c4SAndroid Build Coastguard Worker self.totalTicks = 0 1434*f81fb7c4SAndroid Build Coastguard Worker self.timebase = None 1435*f81fb7c4SAndroid Build Coastguard Worker self.ppqn = 0 1436*f81fb7c4SAndroid Build Coastguard Worker self.beats_per_measure = 0 1437*f81fb7c4SAndroid Build Coastguard Worker self.trackList = [] 1438*f81fb7c4SAndroid Build Coastguard Worker 1439*f81fb7c4SAndroid Build Coastguard Worker md = midiData() 1440*f81fb7c4SAndroid Build Coastguard Worker 1441*f81fb7c4SAndroid Build Coastguard Worker try: 1442*f81fb7c4SAndroid Build Coastguard Worker m = MIDIFile(midiFile, 'rb') 1443*f81fb7c4SAndroid Build Coastguard Worker m.ReadFromStream() 1444*f81fb7c4SAndroid Build Coastguard Worker 1445*f81fb7c4SAndroid Build Coastguard Worker for track in m.tracks: 1446*f81fb7c4SAndroid Build Coastguard Worker if track.channel is not None: 1447*f81fb7c4SAndroid Build Coastguard Worker empty = False 1448*f81fb7c4SAndroid Build Coastguard Worker trk = track.channel + 1 1449*f81fb7c4SAndroid Build Coastguard Worker else: 1450*f81fb7c4SAndroid Build Coastguard Worker empty = True 1451*f81fb7c4SAndroid Build Coastguard Worker trk = '' 1452*f81fb7c4SAndroid Build Coastguard Worker md.trackList.append(trackGrid(track.trackNum, trk, track.name, empty)) 1453*f81fb7c4SAndroid Build Coastguard Worker 1454*f81fb7c4SAndroid Build Coastguard Worker md.endMbt = m.timebase.ConvertTicksToMBT(m.end_of_file) 1455*f81fb7c4SAndroid Build Coastguard Worker md.endMbtStr = "%d:%d:%d" % (md.endMbt[0], md.endMbt[1], md.endMbt[2]) 1456*f81fb7c4SAndroid Build Coastguard Worker md.maxMeasures = md.endMbt[0] 1457*f81fb7c4SAndroid Build Coastguard Worker md.maxBeats = 4 1458*f81fb7c4SAndroid Build Coastguard Worker md.maxTicks = m.timebase.ppqn 1459*f81fb7c4SAndroid Build Coastguard Worker md.maxTracks = m.num_tracks 1460*f81fb7c4SAndroid Build Coastguard Worker md.totalTicks = m.end_of_file 1461*f81fb7c4SAndroid Build Coastguard Worker md.timebase = m.timebase 1462*f81fb7c4SAndroid Build Coastguard Worker md.ppqn = m.timebase.ppqn 1463*f81fb7c4SAndroid Build Coastguard Worker md.beats_per_measure = m.timebase.beats_per_measure 1464*f81fb7c4SAndroid Build Coastguard Worker 1465*f81fb7c4SAndroid Build Coastguard Worker #add above if more added 1466*f81fb7c4SAndroid Build Coastguard Worker md.err = 0 1467*f81fb7c4SAndroid Build Coastguard Worker 1468*f81fb7c4SAndroid Build Coastguard Worker m.close() 1469*f81fb7c4SAndroid Build Coastguard Worker except: 1470*f81fb7c4SAndroid Build Coastguard Worker raise 1471*f81fb7c4SAndroid Build Coastguard Worker pass 1472*f81fb7c4SAndroid Build Coastguard Worker 1473*f81fb7c4SAndroid Build Coastguard Worker return md 1474*f81fb7c4SAndroid Build Coastguard Worker 1475*f81fb7c4SAndroid Build Coastguard Worker 1476*f81fb7c4SAndroid Build Coastguard Worker 1477*f81fb7c4SAndroid Build Coastguard Worker 1478*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 1479*f81fb7c4SAndroid Build Coastguard Worker# main 1480*f81fb7c4SAndroid Build Coastguard Worker#--------------------------------------------------------------- 1481*f81fb7c4SAndroid Build Coastguard Workerif __name__ == '__main__': 1482*f81fb7c4SAndroid Build Coastguard Worker sys = __import__('sys') 1483*f81fb7c4SAndroid Build Coastguard Worker os = __import__('os') 1484*f81fb7c4SAndroid Build Coastguard Worker 1485*f81fb7c4SAndroid Build Coastguard Worker # initialize root logger 1486*f81fb7c4SAndroid Build Coastguard Worker root_logger = logging.getLogger('') 1487*f81fb7c4SAndroid Build Coastguard Worker root_logger.setLevel(logging.NOTSET) 1488*f81fb7c4SAndroid Build Coastguard Worker 1489*f81fb7c4SAndroid Build Coastguard Worker # initialize console handler 1490*f81fb7c4SAndroid Build Coastguard Worker console_handler = logging.StreamHandler() 1491*f81fb7c4SAndroid Build Coastguard Worker console_handler.setFormatter(logging.Formatter('%(message)s')) 1492*f81fb7c4SAndroid Build Coastguard Worker console_handler.setLevel(logging.DEBUG) 1493*f81fb7c4SAndroid Build Coastguard Worker root_logger.addHandler(console_handler) 1494*f81fb7c4SAndroid Build Coastguard Worker 1495*f81fb7c4SAndroid Build Coastguard Worker files = [] 1496*f81fb7c4SAndroid Build Coastguard Worker dirs = [] 1497*f81fb7c4SAndroid Build Coastguard Worker last_arg = None 1498*f81fb7c4SAndroid Build Coastguard Worker sysex_filter = False 1499*f81fb7c4SAndroid Build Coastguard Worker drum_filter = False 1500*f81fb7c4SAndroid Build Coastguard Worker convert = False 1501*f81fb7c4SAndroid Build Coastguard Worker 1502*f81fb7c4SAndroid Build Coastguard Worker # process args 1503*f81fb7c4SAndroid Build Coastguard Worker for arg in sys.argv[1:]: 1504*f81fb7c4SAndroid Build Coastguard Worker 1505*f81fb7c4SAndroid Build Coastguard Worker # previous argument implies this argument 1506*f81fb7c4SAndroid Build Coastguard Worker if last_arg is not None: 1507*f81fb7c4SAndroid Build Coastguard Worker if last_arg == '-DIR': 1508*f81fb7c4SAndroid Build Coastguard Worker dirs.append(arg) 1509*f81fb7c4SAndroid Build Coastguard Worker last_arg = None 1510*f81fb7c4SAndroid Build Coastguard Worker 1511*f81fb7c4SAndroid Build Coastguard Worker # check for switch 1512*f81fb7c4SAndroid Build Coastguard Worker elif arg[0] == '-': 1513*f81fb7c4SAndroid Build Coastguard Worker if arg == '-DIR': 1514*f81fb7c4SAndroid Build Coastguard Worker last_arg = arg 1515*f81fb7c4SAndroid Build Coastguard Worker elif arg == '-SYSEX': 1516*f81fb7c4SAndroid Build Coastguard Worker sysex_filter = True 1517*f81fb7c4SAndroid Build Coastguard Worker elif arg == '-DRUMS': 1518*f81fb7c4SAndroid Build Coastguard Worker drum_filter = True 1519*f81fb7c4SAndroid Build Coastguard Worker elif arg == '-CONVERT': 1520*f81fb7c4SAndroid Build Coastguard Worker convert = True 1521*f81fb7c4SAndroid Build Coastguard Worker else: 1522*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('Bad option %s' % arg) 1523*f81fb7c4SAndroid Build Coastguard Worker 1524*f81fb7c4SAndroid Build Coastguard Worker # must be a filename 1525*f81fb7c4SAndroid Build Coastguard Worker else: 1526*f81fb7c4SAndroid Build Coastguard Worker files.append(arg) 1527*f81fb7c4SAndroid Build Coastguard Worker 1528*f81fb7c4SAndroid Build Coastguard Worker # setup filters 1529*f81fb7c4SAndroid Build Coastguard Worker filters = [] 1530*f81fb7c4SAndroid Build Coastguard Worker if sysex_filter: 1531*f81fb7c4SAndroid Build Coastguard Worker filters.append(EventTypeFilter((SYSEX,))) 1532*f81fb7c4SAndroid Build Coastguard Worker if drum_filter: 1533*f81fb7c4SAndroid Build Coastguard Worker filters.append(ChannelFilter((9,),False)) 1534*f81fb7c4SAndroid Build Coastguard Worker 1535*f81fb7c4SAndroid Build Coastguard Worker 1536*f81fb7c4SAndroid Build Coastguard Worker # process dirs 1537*f81fb7c4SAndroid Build Coastguard Worker for d in dirs: 1538*f81fb7c4SAndroid Build Coastguard Worker for root, dir_list, file_list in os.walk(d): 1539*f81fb7c4SAndroid Build Coastguard Worker for f in file_list: 1540*f81fb7c4SAndroid Build Coastguard Worker if f.endswith('.mid'): 1541*f81fb7c4SAndroid Build Coastguard Worker files.append(os.path.join(root, f)) 1542*f81fb7c4SAndroid Build Coastguard Worker 1543*f81fb7c4SAndroid Build Coastguard Worker # process files 1544*f81fb7c4SAndroid Build Coastguard Worker bad_files = [] 1545*f81fb7c4SAndroid Build Coastguard Worker for f in files: 1546*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.info('Processing file %s' % f) 1547*f81fb7c4SAndroid Build Coastguard Worker midiFile = MIDIFile(f, 'rb') 1548*f81fb7c4SAndroid Build Coastguard Worker try: 1549*f81fb7c4SAndroid Build Coastguard Worker midiFile.ReadFromStream() 1550*f81fb7c4SAndroid Build Coastguard Worker 1551*f81fb7c4SAndroid Build Coastguard Worker #midiFile.DumpTracks() 1552*f81fb7c4SAndroid Build Coastguard Worker #print('[%s]: end-of-track\n' % midiFile.timebase.ConvertTicksToStr(midiFile.end_of_file)) 1553*f81fb7c4SAndroid Build Coastguard Worker 1554*f81fb7c4SAndroid Build Coastguard Worker # convert to type 0 1555*f81fb7c4SAndroid Build Coastguard Worker if convert and (midiFile.format == 1): 1556*f81fb7c4SAndroid Build Coastguard Worker midiFile.Convert(0) 1557*f81fb7c4SAndroid Build Coastguard Worker converted = True 1558*f81fb7c4SAndroid Build Coastguard Worker else: 1559*f81fb7c4SAndroid Build Coastguard Worker converted = False 1560*f81fb7c4SAndroid Build Coastguard Worker 1561*f81fb7c4SAndroid Build Coastguard Worker # write processed file 1562*f81fb7c4SAndroid Build Coastguard Worker if converted or len(filters): 1563*f81fb7c4SAndroid Build Coastguard Worker midiFile.SaveAs(f[:-4] + '-mod.mid', filters) 1564*f81fb7c4SAndroid Build Coastguard Worker 1565*f81fb7c4SAndroid Build Coastguard Worker except MIDIFileException, X: 1566*f81fb7c4SAndroid Build Coastguard Worker bad_files.append(f) 1567*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error('Error in file %s' % f) 1568*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.error(X) 1569*f81fb7c4SAndroid Build Coastguard Worker midiFile.DumpError() 1570*f81fb7c4SAndroid Build Coastguard Worker midiFile.close() 1571*f81fb7c4SAndroid Build Coastguard Worker 1572*f81fb7c4SAndroid Build Coastguard Worker # dump problem files 1573*f81fb7c4SAndroid Build Coastguard Worker if len(bad_files): 1574*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.info('The following file(s) had errors:') 1575*f81fb7c4SAndroid Build Coastguard Worker for f in bad_files: 1576*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.info(f) 1577*f81fb7c4SAndroid Build Coastguard Worker else: 1578*f81fb7c4SAndroid Build Coastguard Worker midi_file_logger.info('All files read successfully') 1579*f81fb7c4SAndroid Build Coastguard Worker 1580