changeset 42:3925ac56d99e

Allow flushing notes. Requires refactoring a bunch of crap though and makes some of the functions know a bit bunch..
author Daniel O'Connor <darius@dons.net.au>
date Tue, 24 May 2016 19:42:02 +0930
parents 21da8af1cdd2
children c69e53b8917c
files musiccutter.py
diffstat 1 files changed, 62 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- 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):