# HG changeset patch # User Daniel O'Connor # Date 1464084722 -34200 # Node ID 3925ac56d99ea7da39721646bdf58abf11ad8ee8 # Parent 21da8af1cdd28bb37db808229e2866f51a8ef51e Allow flushing notes. Requires refactoring a bunch of crap though and makes some of the functions know a bit bunch.. diff -r 21da8af1cdd2 -r 3925ac56d99e musiccutter.py --- a/musiccutter.py Tue May 24 19:40:11 2016 +0930 +++ b/musiccutter.py Tue May 24 19:42:02 2016 +0930 @@ -82,26 +82,26 @@ self.note2slot, self.slot2note = Midi2PDF.loadnote2slot(cp.get('default', 'notes').split(), self.note2midi) def processMidi(self, midifile, outpat): - stats = Stats() - stats.playablecount = 0 - stats.unplayablecount = 0 - stats.transposeupcount = 0 - stats.transposedowncount = 0 + self.stats = Stats() + self.stats.playablecount = 0 + self.stats.unplayablecount = 0 + self.stats.transposeupcount = 0 + self.stats.transposedowncount = 0 midi = mido.MidiFile(midifile) - ctime = 0 - channels = [] - for i in range(16): - channels.append({}) + self.ctime = 0 + self.channels = [] + for i in range(16): # 16 is the maximum number of MIDI channels + self.channels.append({}) npages = int(math.ceil(((midi.length * self.timescale) + self.leadin) / self.pagewidth)) npdfs = int(math.ceil(float(npages) / self.pagesperpdf)) print 'npages %d, npdfs %d' % (npages, npdfs) - pdfs = [] + self.pdfs = [] for i in range(npdfs): pdf = reportlab.pdfgen.canvas.Canvas(file(outpat % (i + 1), 'w'), pagesize = (self.pdfwidth * mm, self.pageheight * mm)) - pdfs.append(pdf) + self.pdfs.append(pdf) title = os.path.basename(midifile) title, ext = os.path.splitext(title) @@ -109,52 +109,43 @@ # Adjust pitch if hasattr(ev, 'note'): ev.note += self.noteoffset - ctime += ev.time - #print ctime, ev + self.ctime += ev.time + print '%.03f: %s' % (self.ctime, str(ev)) #Tracer()() - if ev.type == 'text' and ctime == 0: + if ev.type == 'text' and self.ctime == 0: title = ev.text if ev.type == 'note_on' and ev.velocity > 0: - if ev.note in channels[ev.channel]: - print 'Duplicate note_on message at %.1f sec channel %d note %d' % (ctime, ev.channel, ev.note) + if ev.note in self.channels[ev.channel]: + print 'Duplicate note_on message at %.1f sec channel %d note %d' % (self.ctime, ev.channel, ev.note) else: - channels[ev.channel][ev.note] = ctime + # Store start time + self.channels[ev.channel][ev.note] = self.ctime + # note_on with velocity of 0 is a note_off elif ev.type == 'note_off' or (ev.type == 'note_on' and ev.velocity == 0): - if ev.note not in channels[ev.channel]: + if ev.note not in self.channels[ev.channel]: # These can be rests (iWriteMusic) - print 'note_off with no corresponding note_on at %.1f sec for channel %d note %d' % (ctime, ev.channel, ev.note) + print 'note_off with no corresponding note_on at %.1f sec for channel %d note %d' % (self.ctime, ev.channel, ev.note) continue else: - orignote = ev.note - start = channels[ev.channel][orignote] - evw = EVWrap(ev) - # Find a slot (plus adjust pitch, attempt transposition etc) - if hasattr(ev, 'note'): - self.getslotfornote(evw, stats, ctime) - # Check if it was unplayable - if not evw.slot: - continue - notelen = ctime - start - #print 'Note %s (%d) at %.2f length %.2f' % (evw.notename, ev.slot, start, notelen) - self.emitnote(pdfs, evw.slot, start, notelen * self.notescale) - - del channels[ev.channel][orignote] + orignote = self.emitnote(ev) + del self.channels[ev.channel][orignote] elif ev.type == 'end_of_track': - print 'EOT, not flushing, check for missed notes' - for chan in channels: + for chan in self.channels: for ev in chan: + print 'Flushed', ev + self.emitnote(ev) print ev - print 'Playable count:', stats.playablecount - print 'Unplayable count:', stats.unplayablecount + print 'Playable count:', self.stats.playablecount + print 'Unplayable count:', self.stats.unplayablecount if self.trytranspose: - print 'Transpose down:', stats.transposedowncount - print 'Transpose up:', stats.transposeupcount + print 'Transpose down:', self.stats.transposedowncount + print 'Transpose up:', self.stats.transposeupcount # Do per-page things for pindx in range(npages): - pdf = pdfs[pindx / self.pagesperpdf] # PDF for this page + pdf = self.pdfs[pindx / self.pagesperpdf] # PDF for this page # Offset into PDF where the page starts pageofs = self.pagewidth * (self.pagesperpdf - (pindx % self.pagesperpdf) - 1) # Add title and page number @@ -193,17 +184,17 @@ if self.notenames: Midi2PDF.textHelper(pdf, (self.pdfwidth - 10) * mm, (ofs + 0.5) * mm, ENGRAVE_COLOUR, False, self.fontname, self.fontsize, self.slot2note[slot]) pdf.restoreState() - for pdf in pdfs: + for pdf in self.pdfs: pdf.save() def noteisplayable(self, midi): - slot = None + #Tracer()() if midi in self.midi2note: notename = self.midi2note[midi] if notename in self.note2slot: - slot = self.note2slot[notename] + return True - return slot + return False # Check if the organ can play the note def transposenote(self, evw, amount): @@ -212,7 +203,7 @@ evw.slot = self.note2slot[evw.notename] # Work out which slot to use for the note, transpose if desired - def getslotfornote(self, evw, stats, ctime): + def getslotfornote(self, evw): evw.slot = None evw.notename = None @@ -220,32 +211,32 @@ if evw.ev.note in self.midi2note: evw.notename = self.midi2note[evw.ev.note] # Is it playable? - if self.noteisplayable(evw.ev.note) != None: + if self.noteisplayable(evw.ev.note): evw.slot = self.note2slot[evw.notename] # Nope, maybe we can transpose? elif self.trytranspose: # Go for -3 to +3 octaves (going down first) for i in [-12, -24, -36, 12, 24, 36]: - if self.noteisplayable(evw.ev.note + i) != None: + if self.noteisplayable(evw.ev.note + i): self.transposenote(evw, i) if i < 0: - stats.transposedowncount += 1 + self.stats.transposedowncount += 1 tmp = 'down' else: - stats.transposeupcount += 1 + self.stats.transposeupcount += 1 tmp = 'up' print 'Transposed note at %.1f sec %s (%d) %s %d octave(s) to %s (%d)' % ( - ctime, self.midi2note[evw.ev.note - i], evw.ev.note - i, tmp, + self.ctime, self.midi2note[evw.ev.note - i], evw.ev.note - i, tmp, abs(i / 12), evw.notename, evw.ev.note) break if evw.slot != None: - stats.playablecount += 1 + self.stats.playablecount += 1 else: - print 'Note at %.1f sec %d (%s) not playable' % (ctime, evw.ev.note, self.midi2note[evw.ev.note]) - stats.unplayablecount += 1 + print 'Note at %.1f sec %d (%s) not playable' % (self.ctime, evw.ev.note, self.midi2note[evw.ev.note]) + self.stats.unplayablecount += 1 else: - print 'Note at %.1f sec, %d not in MIDI table' % (ctime, evw.ev.note) - stats.unplayablecount += 1 + print 'Note at %.1f sec, %d not in MIDI table' % (self.ctime, evw.ev.note) + self.stats.unplayablecount += 1 # http://newt.phys.unsw.edu.au/jw/notes.html # But this seems dumb since the lowest MIDI note is 0 which would be C-1.. @@ -282,12 +273,23 @@ return note2slot, slot2note - def emitnote(self, pdfs, slot, start, notelen): + def emitnote(self, ev): + orignote = ev.note + start = self.channels[ev.channel][orignote] + evw = EVWrap(ev) + # Find a slot (plus adjust pitch, attempt transposition etc) + if hasattr(ev, 'note'): + self.getslotfornote(evw) + # Check if it was unplayable + if evw.slot == None: + return orignote + notelen = self.ctime - start + print 'Note %s (%d) at %.2f length %.2f' % (evw.notename, evw.slot, start, notelen) x = start * self.timescale + self.leadin # Convert start time to distance pdfidx = int(x / self.pdfwidth) # Determine which pdf x = x % self.pdfwidth # and where on that pdf h = self.slotsize - y = self.pageheight - (self.heel + slot * self.pitch - self.slotsize / 2) - self.slotsize + y = self.pageheight - (self.heel + evw.slot * self.pitch - self.slotsize / 2) - self.slotsize w = notelen * self.timescale # Convert note length in time to distance #print 'pdf = %d x = %.3f y = %.3f w = %.3f h = %.3f' % (pdfidx, x, y, w, h) @@ -299,9 +301,10 @@ assert w2 <= self.pdfwidth, 'note extends for more than a pdf' # Emit second half of note #print 'split note, pdf %d w2 = %.3f' % (pdfidx + 1, w2) - Midi2PDF._emitnote(pdfs[pdfidx + 1], self.pdfwidth - w2, y, w2, h) + Midi2PDF._emitnote(self.pdfs[pdfidx + 1], self.pdfwidth - w2, y, w2, h) - Midi2PDF._emitnote(pdfs[pdfidx], self.pdfwidth - x - w1, y, w1, h) + Midi2PDF._emitnote(self.pdfs[pdfidx], self.pdfwidth - x - w1, y, w1, h) + return orignote @staticmethod def _emitnote(pdf, x, y, w, h):