0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2005 Joe Pham <djpham@bitpim.org> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: midifile.py 3608 2006-10-06 02:51:56Z djpham $ 0009 0010 import common 0011 0012 0013 module_debug=False 0014 0015 class MIDIEvent(object): 0016 META_EVENT=0 0017 SYSEX_EVENT=1 0018 SYSEX1_EVENT=2 0019 MIDI_EVENT=3 0020 LAST_MIDI_EVENT=4 0021 type_str=('Meta', 'SYSEX', 'SYSEX cont', 'MIDI', 'Last MIDI') 0022 0023 def __init__(self, file, offset, last_cmd=None): 0024 self.__f=file 0025 self.__start=self.__ofs=offset 0026 self.__time_delta=self.__get_var_len() 0027 b=self.__get_int() 0028 if b==0xff: 0029 # meta event 0030 self.__get_meta_event() 0031 elif b==0xf0 or b==0xf7: 0032 # sysex event 0033 self.__get_sysex_event(b) 0034 else: 0035 # MIDI Channel event 0036 self.__get_midi_event(b, last_cmd) 0037 self.__total_len=self.__ofs-self.__start 0038 0039 def __get_int(self): 0040 i=int(self.__f.GetByte(self.__ofs)) 0041 self.__ofs+=1 0042 return i 0043 0044 def __get_bytes(self, len): 0045 data=self.__f.GetBytes(self.__ofs, len) 0046 self.__ofs+=len 0047 return data 0048 0049 def __get_var_len(self): 0050 t=0 0051 b=self.__get_int() 0052 while (b&0x80): 0053 t=(t<<7)|(b&0x7f) 0054 b=self.__get_int() 0055 return (t<<7)|(b&0x7f) 0056 0057 def __get_meta_event(self): 0058 self.__type=self.META_EVENT 0059 self.__cmd=self.__get_int() 0060 self.__len=self.__get_var_len() 0061 if self.__len: 0062 self.__param1=self.__get_bytes(self.__len) 0063 else: 0064 self.__param1=None 0065 self.__param2=None 0066 0067 def __get_sysex_event(self, cmd): 0068 if cmd==0xf0: 0069 self.__type=self.SYSEX_EVENT 0070 else: 0071 self.__type=self.SYSEX1_EVENT 0072 self.__cmd=cmd 0073 self.__len=self.__get_var_len() 0074 if self.__len: 0075 self.__param1=self.__get_bytes(self.__len) 0076 else: 0077 self.__param1=None 0078 self.__param2=None 0079 0080 def __get_midi_event(self, cmd, last_cmd): 0081 if cmd&0x80: 0082 # not a running command 0083 i=cmd 0084 self.__type=self.MIDI_EVENT 0085 self.__param1=self.__get_int() 0086 else: 0087 i=last_cmd 0088 self.__type=self.LAST_MIDI_EVENT 0089 self.__param1=cmd 0090 self.__cmd=(i&0xf0)>>4 0091 self.__midi_channel=i&0x0f 0092 if self.__cmd==0x0c or self.__cmd==0x0d: 0093 self.__len=1 0094 self.__param2=None 0095 else: 0096 self.__len=2 0097 self.__param2=self.__get_int() 0098 0099 def __get_type(self): 0100 return self.__type 0101 type=property(fget=__get_type) 0102 0103 def __get_time_delta(self): 0104 return self.__time_delta 0105 time_delta=property(fget=__get_time_delta) 0106 0107 def __get_total_len(self): 0108 return self.__total_len 0109 total_len=property(fget=__get_total_len) 0110 0111 def __get_cmd(self): 0112 return self.__cmd 0113 cmd=property(fget=__get_cmd) 0114 0115 def __get_midi_channel(self): 0116 return self.__midi_channel 0117 midi_channel=property(fget=__get_midi_channel) 0118 0119 def __get_param_len(self): 0120 return self.__len 0121 param_len=property(fget=__get_param_len) 0122 0123 def __get_params(self): 0124 return self.__param1, self.__param2 0125 params=property(fget=__get_params) 0126 0127 def __str__(self): 0128 if self.type==self.MIDI_EVENT or \ 0129 self.type==self.LAST_MIDI_EVENT: 0130 return '0x%04x: %s cmd: 0x%x, Channel: %d, Len: %d'%\ 0131 (self.time_delta, self.type_str[self.type], 0132 self.cmd, self.midi_channel, self.param_len) 0133 else: 0134 return '0x%04x: %s cmd: 0x%x, Len: %d'%\ 0135 (self.time_delta, self.type_str[self.type], 0136 self.cmd, self.param_len) 0137 0138 class MIDITrack(object): 0139 def __init__(self, file, offset): 0140 self.__f=file 0141 self.__ofs=offset 0142 if module_debug: 0143 print 'New Track @ ofs:', offset 0144 if self.__f.GetBytes(self.__ofs, 4)!='MTrk': 0145 raise TypeError, 'not an MIDI track' 0146 self.__len=self.__f.GetMSBUint32(self.__ofs+4) 0147 ofs=self.__ofs+8 0148 ofs_end=ofs+self.__len 0149 last_cmd=None 0150 self.__time_delta=0 0151 self.__mpqn=None 0152 while ofs<ofs_end: 0153 e=MIDIEvent(file, ofs, last_cmd) 0154 if module_debug: 0155 print e 0156 ofs+=e.total_len 0157 self.__time_delta+=e.time_delta 0158 if e.type==e.META_EVENT: 0159 if e.cmd==0x51: 0160 # set tempo 0161 p1, p2=e.params 0162 self.__mpqn=(ord(p1[0])<<16)|(ord(p1[1])<<8)|ord(p1[2]) 0163 if e.type==e.MIDI_EVENT or e.type==e.LAST_MIDI_EVENT: 0164 last_cmd=(e.cmd<<4)|e.midi_channel 0165 else: 0166 last_cmd=e.cmd 0167 self.__total_len=ofs-self.__ofs 0168 if module_debug: 0169 print 'self.__ofs', self.__ofs+8, 'self.__len:', self.__len, 'ofs: ', ofs 0170 print 'time delta:', self.__time_delta, 'MPQN: ', self.__mpqn 0171 0172 def __get_time_delta(self): 0173 return self.__time_delta 0174 time_delta=property(fget=__get_time_delta) 0175 def __get_total_len(self): 0176 return self.__total_len 0177 total_len=property(fget=__get_total_len) 0178 def __get_mpqn(self): 0179 return self.__mpqn 0180 mpqn=property(fget=__get_mpqn) 0181 0182 class MIDIFile(object): 0183 def __init__(self, file_wraper): 0184 try: 0185 self.__valid=False 0186 self.__file=file_wraper 0187 if self.__file.GetBytes(0, 4)!='MThd' or \ 0188 self.__file.GetMSBUint32(4)!=6: 0189 # not a valid MIDI header 0190 return 0191 self.__valid=True 0192 self.__type=self.__file.GetMSBUint16(8) 0193 self.__num_tracks=self.__file.GetMSBUint16(10) 0194 self.__time_division=self.__file.GetMSBUint16(12) 0195 self.__tracks=[] 0196 self.__mpqn=2000000 0197 file_ofs=14 0198 time_delta=0 0199 for i in range(self.__num_tracks): 0200 trk=MIDITrack(self.__file, file_ofs) 0201 self.__tracks.append(trk) 0202 file_ofs+=trk.total_len 0203 time_delta=max(time_delta, trk.time_delta) 0204 if trk.mpqn is not None: 0205 self.__mpqn=trk.mpqn 0206 self.__duration=(self.__mpqn*time_delta/self.__time_division)/1000000.0 0207 if module_debug: 0208 print 'type:', self.__type 0209 print 'time division:', self.__time_division 0210 print 'num of tracks:', self.__num_tracks 0211 print 'MPQN:', self.__mpqn 0212 print 'longest time delta: ', time_delta 0213 print 'duration:', self.__duration 0214 except: 0215 self.__valid=False 0216 0217 def __get_valid(self): 0218 return self.__valid 0219 valid=property(fget=__get_valid) 0220 0221 def __get_type(self): 0222 return self.__type 0223 type=property(fget=__get_type) 0224 0225 def __get_num_tracks(self): 0226 return self.__num_tracks 0227 num_tracks=property(fget=__get_num_tracks) 0228 0229 def __get_duration(self): 0230 return self.__duration 0231 duration=property(fget=__get_duration) 0232 0233 0234 0235 0236
Generated by PyXR 0.9.4