def readMidi(filename): """ Read existing midi file, parse and return events, textevents & lyrics """ global offset, midifile, beatDivision, istart, iend, firstNote, ignorePC try: inpath = file(filename, "rb") except: error("Unable to open MIDI file %s for reading" % filename) midifile=inpath.read() inpath.close() # Create our storage: # A dic with the channels 0-15 as keys for the midi note events # 2 lists for lyrics and text events. These have tuples for (time, text) events={} for c in range(0,16): events[c]=[] textEvs=[] lyricEvs=[] # Ensure this is valid header hd=midifile[0:4] if hd != 'MThd': error("Expecting 'MThd', %s not a standard midi file" % filename) offset = 4 a = m32i() if a != 6: error("Expecting a 32 bit value of 6 in header") format=m16i() if format not in (0,1): error("MIDI file format %s not recognized" % format) ntracks=m16i() beatDivision=m16i() if beatDivision != gbl.BperQ: warning("MIDI file '%s' tick/beat of %s differs from MMA's " "%s. Will try to compensate" % (filename, beatDivision, gbl.BperQ)) # Adjust start/end to the file's tick istart *= beatDivision iend *= beatDivision midievents={} firstNote = 0xffffff for tr in range(ntracks): tm=0 hdr = midifile[offset:offset+4] offset+=4 if hdr != 'MTrk': error("Malformed MIDI file in track header") trlen = m32i() # track length, not used? lastevent = None """ Parse the midi file. We have to parse off each event, even though many will just be thrown away. You can't just skip around in a midi file :) In the future we might decide to include meta stuff, etc. Or, we may not :) For now, we keep: - note on - note off - key pressure - control change - program change - channel pressure - pitch blend - text event - lyric event """ while 1: tm += mvarlen() # adjust total offset by delta ev=m1i() if ev < 0x80: if not lastevent: error("Illegal running status in %s at %s" % (midifile, offset)) offset -= 1 ev=lastevent sValue = ev>>4 # Shift MSBs to get a 4 bit value channel = ev & 0x0f if sValue == 0x8: # note off event note=m1i() vel=m1i() if octAdjust and channel != 10: note += octAdjust while note < 0: note += 12 while note >127: note -= 12 events[channel].append([tm, ev & 0xf0, chr(note)+chr(vel)]) elif sValue == 0x9: # note on event if tm < firstNote: firstNote = tm note=m1i() vel=m1i() if octAdjust and channel != 10: note += octAdjust while note < 0: note += 12 while note >127: note -= 12 if volAdjust != 100: vel = int( (vel*volAdjust)/100) if vel<0: vel=1 if vel>127: vel=127 events[ev & 0xf].append([tm, ev & 0xf0, chr(note)+chr(vel)]) elif sValue == 0xa: # key pressure events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xb: # control change events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xc: # program change if ignorePC: # default is to ignore these offset += 1 else: # set with option IgnorePC=1 events[ev & 0xf].append([tm, ev & 0xf0, chars(1)]) elif sValue == 0xd: # channel pressure events[ev & 0xf].append([tm, ev & 0xf0, chars(1)]) elif sValue == 0xe: # pitch blend events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xf: # system, mostly ignored if ev == 0xff: # meta events a=m1i() if a == 0x00: # sequence number l=mvarlen() offset += l elif a == 0x01: # text (could be lyrics) textEvs.append((tm, chars(mvarlen()))) elif a == 0x02: # copyright l=mvarlen() offset += l elif a == 0x03: # seq/track name l=mvarlen() offset += l elif a == 0x04: # instrument name l=mvarlen() offset += l elif a == 0x05: # lyric lyricEvs.append((tm, chars(mvarlen()))) elif a == 0x06: # marker l=mvarlen() offset += l elif a == 0x07: # cue point l=mvarlen() offset += l elif a == 0x21: # midi port l=mvarlen() offset += l elif a == 0x2f: # end of track l=mvarlen() offset += l break elif a == 0x51: #tempo l=mvarlen() offset += l elif a == 0x54: # SMPTE offset l=mvarlen() offset += l elif a == 0x58: # time sig l=mvarlen() offset += l elif a == 0x59: # key sig l=mvarlen() offset += l else: # probably 0x7f, proprietary event l=mvarlen() offset += l elif ev == 0xf0: # system exclusive l=mvarlen() offset += l elif ev == 0xf2: # song position pointer, 2 bytes offset += 2 elif ev == 0xf3: # song select, 1 byte offset += 1 else: # all others are single byte commands pass if ev >= 0x80 and ev <= 0xef: lastevent = ev return (events, textEvs, lyricEvs)
print print "No data created. Did you remember to set a groove/sequence?" if fileExist: print "Existing file '%s' has not been modified." % outfile sys.exit(1) lyric.leftovers() if fileExist: print "Overwriting existing", else: print "Creating new", print "midi file (%s bars, %.2f min): '%s'" % (gbl.barNum, gbl.totTime, outfile) try: out = file(outfile, 'wb') except: error("Can't open file '%s' for writing" % outfile) MMA.midi.writeTracks(out) out.close() if gbl.playFile: import MMA.player MMA.player.playMidi(outfile) if gbl.debug: print "Completed processing file '%s'." % outfile
def readMidi(filename): """ Read existing midi file, parse and return events, textevents & lyrics """ global offset, midifile, firstNote, ignorePC try: inpath = file(filename, "rb") except: error("MidiInc: Unable to open MIDI file %s for reading" % filename) midifile = inpath.read() inpath.close() # Create our storage: # A dic with the channels 0-15 as keys for the midi note events # 2 lists for lyrics and text events. These have tuples for (time, text) events = {} for c in range(0, 16): events[c] = [] textEvs = [] lyricEvs = [] # Ensure this is valid header hd = midifile[0:4] if hd != 'MThd': error("MidiInc: Expecting 'MThd', %s not a standard midi file" % filename) offset = 4 a = m32i() if a != 6: error("MidiInc: Expecting a 32 bit value of 6 in header") format = m16i() if format not in (0, 1): error("MidiInc: MIDI file format %s not recognized" % format) ntracks = m16i() beatDivision = m16i() if beatDivision != gbl.BperQ: warning("MIDI file '%s' tick/beat of %s differs from MMA's " "%s. Will try to compensate" % (filename, beatDivision, gbl.BperQ)) beatad = gbl.BperQ / float(beatDivision) else: beatad = None midievents = {} firstNote = 0xffffff for tr in range(ntracks): tm = 0 hdr = midifile[offset:offset + 4] offset += 4 if hdr != 'MTrk': error("MidiInc: Malformed MIDI file in track header") trlen = m32i() # track length, not used? lastevent = None """ Parse the midi file. We have to parse off each event, even though many will just be thrown away. You can't just skip around in a midi file :) In the future we might decide to include meta stuff, etc. Or, we may not :) For now, we keep: - note on - note off - key pressure - control change - program change - channel pressure - pitch blend - text event - lyric event """ while 1: tm += mvarlen() # adjust total offset by delta ev = m1i() if ev < 0x80: if not lastevent: error("MidiInc: Illegal running status in %s at %s" % (midifile, offset)) offset -= 1 ev = lastevent sValue = ev >> 4 # Shift MSBs to get a 4 bit value channel = ev & 0x0f if sValue == 0x8: # note off event note = m1i() vel = m1i() if octAdjust and channel != 9: # drums are 9 when 0..15 (not 10!) note += octAdjust while note < 0: note += 12 while note > 127: note -= 12 events[channel].append([tm, ev & 0xf0, chr(note) + chr(vel)]) elif sValue == 0x9: # note on event if tm < firstNote: firstNote = tm note = m1i() vel = m1i() if octAdjust and channel != 9: note += octAdjust while note < 0: note += 12 while note > 127: note -= 12 if volAdjust != 100: vel = int((vel * volAdjust) / 100) if vel < 0: vel = 1 if vel > 127: vel = 127 events[ev & 0xf].append([tm, ev & 0xf0, chr(note) + chr(vel)]) elif sValue == 0xa: # key pressure events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xb: # control change events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xc: # program change if ignorePC: # default is to ignore these offset += 1 else: # set with option IgnorePC=1 events[ev & 0xf].append([tm, ev & 0xf0, chars(1)]) elif sValue == 0xd: # channel pressure events[ev & 0xf].append([tm, ev & 0xf0, chars(1)]) elif sValue == 0xe: # pitch blend events[ev & 0xf].append([tm, ev & 0xf0, chars(2)]) elif sValue == 0xf: # system, mostly ignored if ev == 0xff: # meta events a = m1i() if a == 0x00: # sequence number l = mvarlen() offset += l elif a == 0x01: # text (could be lyrics) textEvs.append([tm, chars(mvarlen())]) elif a == 0x02: # copyright l = mvarlen() offset += l elif a == 0x03: # seq/track name l = mvarlen() offset += l elif a == 0x04: # instrument name l = mvarlen() offset += l elif a == 0x05: # lyric lyricEvs.append([tm, chars(mvarlen())]) elif a == 0x06: # marker l = mvarlen() offset += l elif a == 0x07: # cue point l = mvarlen() offset += l elif a == 0x21: # midi port l = mvarlen() offset += l elif a == 0x2f: # end of track l = mvarlen() offset += l break elif a == 0x51: #tempo l = mvarlen() offset += l elif a == 0x54: # SMPTE offset l = mvarlen() offset += l elif a == 0x58: # time sig l = mvarlen() offset += l elif a == 0x59: # key sig l = mvarlen() offset += l else: # probably 0x7f, proprietary event l = mvarlen() offset += l elif ev == 0xf0: # system exclusive l = mvarlen() offset += l elif ev == 0xf2: # song position pointer, 2 bytes offset += 2 elif ev == 0xf3: # song select, 1 byte offset += 1 else: # all others are single byte commands pass if ev >= 0x80 and ev <= 0xef: lastevent = ev # Modify the lists of events to compensate for timing if beatad: if verbose: print "Adjusting all deltas by %s." % beatad for ch in events: for e in events[ch]: e[0] = int(e[0] * beatad) for e in textEvs: e[0] = int(e[0] * beatad) for e in lyricEvs: e[0] = int(e[0] * beatad) return (events, textEvs, lyricEvs)
print print "No data created. Did you remember to set a groove/sequence?" if fileExist: print "Existing file '%s' has not been modified." % outfile sys.exit(1) lyric.leftovers() if fileExist: print "Overwriting existing", else: print "Creating new", print "midi file (%s bars, %.2f min): '%s'" % (gbl.barNum, gbl.totTime, outfile) try: out = file(outfile, 'wb') except IOError: error("Can't open file '%s' for writing" % outfile) MMA.midi.writeTracks(out) out.close() if gbl.playFile: import MMA.player MMA.player.playMidi(outfile) if gbl.debug: print "Completed processing file '%s'." % outfile