changeset 38:9e8ed92b477c

Re-jig note translation to only happen when we are going to emit a note. This fixes "note_off with no note_on" cases (iWriteMusic likes to emit these for rests). This means some messages have untransposed notes but we draw the line because they only have to be transposed because of limitations in the organ so before that they are are untransposed (except for the bulk adjustment)
author Daniel O'Connor <darius@dons.net.au>
date Mon, 23 May 2016 22:35:44 +0930
parents c490fecec0ef
children 86622ba474e4
files musiccutter.py
diffstat 1 files changed, 34 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/musiccutter.py	Mon May 23 21:11:27 2016 +0930
+++ b/musiccutter.py	Mon May 23 22:35:44 2016 +0930
@@ -88,38 +88,44 @@
         title = os.path.basename(midifile)
         title, ext = os.path.splitext(title)
         for ev in midi:
-            evw = EVWrap(ev)
-            # Find a slot (plus adjust pitch, attempt transposition etc)
-            if hasattr(evw.ev, 'note'):
-                self.getslotfornote(evw, stats, ctime)
-                # Check if it was unplayable
-                if not evw.slot:
-                    continue
-            if evw.ev.type == 'text' and ctime == 0:
-                title = evw.ev.text
+            # Adjust pitch
+            if hasattr(ev, 'note'):
+                ev.note += self.noteoffset
+            ctime += ev.time
+            #print ctime, ev
+            #Tracer()()
 
-            ctime += evw.ev.time
-            #print ctime, evw.ev
-            if evw.ev.type == 'note_on' and evw.ev.velocity > 0:
-                if evw.ev.note in channels[evw.ev.channel]:
-                    print 'Duplicate note_on message %d (%s)' % (evw.ev.note, evw.notename)
+            if ev.type == 'text' and 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)
+                else:
+                    channels[ev.channel][ev.note] = ctime
+            elif ev.type == 'note_off' or (ev.type == 'note_on' and ev.velocity == 0):
+                if ev.note not in channels[ev.channel]:
+                    print 'note_off with no corresponding note_on at %.1f sec for channel %d note %d' % (ctime, ev.channel, ev.note)
+                    continue
                 else:
-                    channels[evw.ev.channel][evw.ev.note] = ctime
-            elif evw.ev.type == 'note_off' or (evw.ev.type == 'note_on' and evw.ev.velocity == 0):
-                if evw.ev.note not in channels[evw.ev.channel]:
-                    print 'note_off with no corresponding note_on for channel %d note %d' % (evw.ev.channel, evw.ev.note)
-                else:
-                    start = channels[evw.ev.channel][evw.ev.note]
+                    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, evw.ev.slot, start, notelen)
+                    #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[evw.ev.channel][evw.ev.note]
-            elif evw.ev.type == 'end_of_track':
+                del channels[ev.channel][orignote]
+            elif ev.type == 'end_of_track':
                 print 'EOT, not flushing, check for missed notes'
                 for chan in channels:
-                    for evw.ev in chan:
-                        print evw.ev
+                    for ev in chan:
+                        print ev
 
         print 'Playable count:', stats.playablecount
         print 'Unplayable count:', stats.unplayablecount
@@ -188,8 +194,6 @@
         evw.slot = None
         evw.notename = None
 
-        evw.ev.note += self.noteoffset
-
         # First off, is the note in our midi table?
         if evw.ev.note in self.midi2note:
             evw.notename = self.midi2note[evw.ev.note]
@@ -218,17 +222,18 @@
                 print 'Note at %.1f sec %d (%s) not playable' % (ctime, evw.ev.note, self.midi2note[evw.ev.note])
                 stats.unplayablecount += 1
         else:
-            print 'Note at %.1f sec %d not in MIDI table' % (ctime, evw.ev.note)
+            print 'Note at %.1f sec, %d not in MIDI table' % (ctime, evw.ev.note)
             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..
     @staticmethod
     def genmidi2note():
         '''Create forward & reverse tables for midi number to note name (assuming 69 = A4 = A440)'''
         names = ['C%d', 'C%d#', 'D%d', 'D%d#', 'E%d', 'F%d', 'F%d#', 'G%d', 'G%d#', 'A%d', 'A%d#', 'B%d']
         midi2note = {}
         note2midi = {}
-        for midi in range(21, 128):
+        for midi in range(12, 128):
             octave = midi / len(names) - 1
             index = midi % len(names)
             name = names[index] % (octave)