#!/usr/bin/python # #!/usr/local/bin/python import re import sys from midi.MidiOutStream import MidiOutStream from midi.MidiInFile import MidiInFile from midi.MidiOutFile import MidiOutFile ############################################################# # This is the bare-bones minimum for writing midi. def writeMidi(): out_file = 'minimal_type0.mid' midi = MidiOutFile(out_file) # non optional midi framework midi.header() midi.start_of_track() # musical events midi.update_time(0) midi.note_on(channel=0, note=0x40) # update time after each note, using relative or absolute. midi.update_time(192) midi.note_off(channel=0, note=0x40) # non optional midi framework midi.update_time(0) midi.end_of_track() midi.eof() ############################################################# class NoteOnPrinter(MidiOutStream): "Prints all note_on events on channel 0" def __init__(self): print "Initializing NoteOnPrinter" self.noteOnArr = [] self.noteOffArr = [] self.windowSize = 5 self.noteState = {} self.eventsOn = [] # Constants, for indexing midi values in note tuple. self.CHANNEL = 0 self.NOTE = 1 self.VEL = 2 self.REL_TIME = 3 self.ABS_TIME = 4 ############################################################# def note_on(self, channel=0, note=0x40, velocity=0x40): print "ON : chan: " + str(channel) + " note: " + str(note) + " vel: " + str(velocity) + \ " relT: " + str(self.rel_time()) + " absT " + str(self.abs_time()) noteTuple = (channel, note, velocity, self.rel_time(), self.abs_time()) self.noteOnArr.append(noteTuple) self.checkEvent(self.abs_time()) self.noteState[note] = noteTuple ############################################################# def note_off(self, channel=0, note=0x40, velocity=0x40): print "OFF: chan: " + str(channel) + " note: " + str(note) + " vel: " + str(velocity) + \ " relT: " + str(self.rel_time()) + " absT " + str(self.abs_time()) noteTuple = (channel, note, velocity, self.rel_time(), self.abs_time()) self.noteOffArr.append(noteTuple) self.checkEvent(self.abs_time()) self.noteState[note] = None ############################################################# lastWindowStart = 0 def checkEvent(self, time): if time - self.lastWindowStart >= self.windowSize: #print "time %d: need to write state!" % (time) # Dump note state array. eventsAtTick = [] for note, tuple in self.noteState.items(): if tuple != None: eventsAtTick.append(tuple) curWindowStart = self.lastWindowStart while time - curWindowStart >= self.windowSize: self.eventsOn.append((curWindowStart, eventsAtTick)) curWindowStart += self.windowSize # Reset window self.lastWindowStart = curWindowStart ############################################################# midiFile = None def openMidi(self, filename): midi = MidiOutFile(filename) # non optional midi framework midi.header() midi.start_of_track() self.midiFile = midi ############################################################# def closeMidi(self): # non optional midi framework self.midiFile.update_time(0) self.midiFile.end_of_track() # not optional! self.midiFile.eof() ############################################################# # Read the events, turn them into a midi file. def readEvents(self, filename): self.openMidi(filename + ".mid") f = open(filename, "r") lines = f.readlines() p = re.compile('\(\d+ \d+\)') ep = re.compile('\((\d+) (\d+)\)') # Get the events in this line for l in lines: print "line: " + l # If the event is two nils, it means silence for this # sample. As it happens, this already worked, since # notesOn gets reset every sample, and if there are no # events everything will get turned off. If the event is # a "real" event, then extract note, velocities from each # event, and add them to the notes that are on during this # sample. events = p.findall(l) notesOn = {} for i in events: print "event: " + i noteVelM = ep.match(i) note = int(noteVelM.group(1)) vel = int(noteVelM.group(2)) print "note %d vel %d" % (note, vel) notesOn[note] = vel # Produce the noteOn/noteOff events based on notes on # during this sample. self.buildEvents(notesOn) f.close() self.closeMidi() ############################################################# notesOnLast = {} curTime = 0 def buildEvents(self, notesOn): print "--- buildEvents ---" # Turn notes from last sample off if len(self.notesOnLast) != 0: for note in self.notesOnLast.keys(): print " turning off note " + str(note) + " ...", if not notesOn.has_key(note): self.midiFile.update_time(self.curTime, 0) self.midiFile.note_off(channel=0, note=note) print "Off!" else: print " note still on this event, leaving on." # Turn notes from this sample on #self.midiFile.update_time(0) for (note, vel) in notesOn.items(): if not self.notesOnLast.has_key(note): print " turning ON note " + str(note) self.midiFile.update_time(self.curTime, 0) self.midiFile.note_on(channel=0, note=note, velocity=vel) # Update the current time, and toggle the notes on last time. # Note that if no notes were played this time, time will be # updated, so the silence will come through. self.notesOnLast = notesOn self.curTime += self.windowSize ############################################################# def showEvents(self, fname): print "Writing note events!" fp = open(fname, "w") for i in self.eventsOn: #print i if i[1] == []: fp.write("(nil nil)\n") else: fp.write("( ") for noteEvent in i[1]: note = noteEvent[self.NOTE] vel = noteEvent[self.VEL] fp.write("(%d %d) " % (note, vel)) fp.write(")\n") fp.close() ############################################################# def buildSample(self): curOn = 0 curOff = 0 startWinIndex = 0 curWinIndex = 0 numOnNotes = len(self.noteOnArr) numOffNotes = len(self.noteOffArr) done = False doneOn = False doneOff = False curTick = 0 while done == False: # Find notes on during this tick. while True and not doneOn: onNote = self.noteOnArr[curOn] onTime = onNote[self.ABS_TIME] print "Note %d went on at %d\n" % (onNote[self.NOTE], onTime) if onTime > curTick: break else: curOn += 1 if curOn == numOnNotes: doneOn = True # Find notes off during this tick. while True and not doneOff: offNote = self.noteOffArr[curOff] offTime = offNote[self.ABS_TIME] print "Note %d went on at %d\n" % (offNote[self.NOTE], offTime) if offTime > curTick: break else: curOff += 1 if curOff == numOffNotes: doneOff = True # See if I'm done with both on and off events. curTick += 1 if doneOff and doneOn: done = True print "Done!" curWinIndex += 1 if curWinIndex == self.windowSize: curWinIndex = 0 # Write note state ############################################################# # Main loop if __name__ == "__main__": # event_handler = NoteOnPrinter() # Encode midi into events if sys.argv[1] == "m-to-e": for i in sys.argv[2:]: event_handler = NoteOnPrinter() fname = str(i) + ".mid" print " - encoding events for " + fname midi_in = MidiInFile(event_handler, fname) midi_in.read() event_handler.showEvents(fname + ".events") # Encode events into midi if sys.argv[1] == "e-to-m": event_handler = NoteOnPrinter() eventsFile = sys.argv[2] + ".events" midiFile = sys.argv[2] + ".mid" print "Encoding events file " + eventsFile + " into midi file " + midiFile event_handler.readEvents(eventsFile) # Are the notes that don't occur during a window being captured # correctly?