PyXR

c:\projects\bitpim\src \ midifile.py



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