comparison musiccutter.py @ 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
comparison
equal deleted inserted replaced
37:c490fecec0ef 38:9e8ed92b477c
86 pdfs.append(pdf) 86 pdfs.append(pdf)
87 87
88 title = os.path.basename(midifile) 88 title = os.path.basename(midifile)
89 title, ext = os.path.splitext(title) 89 title, ext = os.path.splitext(title)
90 for ev in midi: 90 for ev in midi:
91 evw = EVWrap(ev) 91 # Adjust pitch
92 # Find a slot (plus adjust pitch, attempt transposition etc) 92 if hasattr(ev, 'note'):
93 if hasattr(evw.ev, 'note'): 93 ev.note += self.noteoffset
94 self.getslotfornote(evw, stats, ctime) 94 ctime += ev.time
95 # Check if it was unplayable 95 #print ctime, ev
96 if not evw.slot: 96 #Tracer()()
97
98 if ev.type == 'text' and ctime == 0:
99 title = ev.text
100 if ev.type == 'note_on' and ev.velocity > 0:
101 if ev.note in channels[ev.channel]:
102 print 'Duplicate note_on message at %.1f sec channel %d note %d' % (ctime, ev.channel, ev.note)
103 else:
104 channels[ev.channel][ev.note] = ctime
105 elif ev.type == 'note_off' or (ev.type == 'note_on' and ev.velocity == 0):
106 if ev.note not in channels[ev.channel]:
107 print 'note_off with no corresponding note_on at %.1f sec for channel %d note %d' % (ctime, ev.channel, ev.note)
97 continue 108 continue
98 if evw.ev.type == 'text' and ctime == 0:
99 title = evw.ev.text
100
101 ctime += evw.ev.time
102 #print ctime, evw.ev
103 if evw.ev.type == 'note_on' and evw.ev.velocity > 0:
104 if evw.ev.note in channels[evw.ev.channel]:
105 print 'Duplicate note_on message %d (%s)' % (evw.ev.note, evw.notename)
106 else: 109 else:
107 channels[evw.ev.channel][evw.ev.note] = ctime 110 orignote = ev.note
108 elif evw.ev.type == 'note_off' or (evw.ev.type == 'note_on' and evw.ev.velocity == 0): 111 start = channels[ev.channel][orignote]
109 if evw.ev.note not in channels[evw.ev.channel]: 112 evw = EVWrap(ev)
110 print 'note_off with no corresponding note_on for channel %d note %d' % (evw.ev.channel, evw.ev.note) 113 # Find a slot (plus adjust pitch, attempt transposition etc)
111 else: 114 if hasattr(ev, 'note'):
112 start = channels[evw.ev.channel][evw.ev.note] 115 self.getslotfornote(evw, stats, ctime)
116 # Check if it was unplayable
117 if not evw.slot:
118 continue
113 notelen = ctime - start 119 notelen = ctime - start
114 #print 'Note %s (%d) at %.2f length %.2f' % (evw.notename, evw.ev.slot, start, notelen) 120 #print 'Note %s (%d) at %.2f length %.2f' % (evw.notename, ev.slot, start, notelen)
115 self.emitnote(pdfs, evw.slot, start, notelen * self.notescale) 121 self.emitnote(pdfs, evw.slot, start, notelen * self.notescale)
116 122
117 del channels[evw.ev.channel][evw.ev.note] 123 del channels[ev.channel][orignote]
118 elif evw.ev.type == 'end_of_track': 124 elif ev.type == 'end_of_track':
119 print 'EOT, not flushing, check for missed notes' 125 print 'EOT, not flushing, check for missed notes'
120 for chan in channels: 126 for chan in channels:
121 for evw.ev in chan: 127 for ev in chan:
122 print evw.ev 128 print ev
123 129
124 print 'Playable count:', stats.playablecount 130 print 'Playable count:', stats.playablecount
125 print 'Unplayable count:', stats.unplayablecount 131 print 'Unplayable count:', stats.unplayablecount
126 if self.trytranspose: 132 if self.trytranspose:
127 print 'Transpose down:', stats.transposedowncount 133 print 'Transpose down:', stats.transposedowncount
185 evw.slot = self.note2slot[evw.notename] 191 evw.slot = self.note2slot[evw.notename]
186 192
187 def getslotfornote(self, evw, stats, ctime): 193 def getslotfornote(self, evw, stats, ctime):
188 evw.slot = None 194 evw.slot = None
189 evw.notename = None 195 evw.notename = None
190
191 evw.ev.note += self.noteoffset
192 196
193 # First off, is the note in our midi table? 197 # First off, is the note in our midi table?
194 if evw.ev.note in self.midi2note: 198 if evw.ev.note in self.midi2note:
195 evw.notename = self.midi2note[evw.ev.note] 199 evw.notename = self.midi2note[evw.ev.note]
196 # Is it playable? 200 # Is it playable?
216 stats.playablecount += 1 220 stats.playablecount += 1
217 else: 221 else:
218 print 'Note at %.1f sec %d (%s) not playable' % (ctime, evw.ev.note, self.midi2note[evw.ev.note]) 222 print 'Note at %.1f sec %d (%s) not playable' % (ctime, evw.ev.note, self.midi2note[evw.ev.note])
219 stats.unplayablecount += 1 223 stats.unplayablecount += 1
220 else: 224 else:
221 print 'Note at %.1f sec %d not in MIDI table' % (ctime, evw.ev.note) 225 print 'Note at %.1f sec, %d not in MIDI table' % (ctime, evw.ev.note)
222 stats.unplayablecount += 1 226 stats.unplayablecount += 1
223 227
224 # http://newt.phys.unsw.edu.au/jw/notes.html 228 # http://newt.phys.unsw.edu.au/jw/notes.html
229 # But this seems dumb since the lowest MIDI note is 0 which would be C-1..
225 @staticmethod 230 @staticmethod
226 def genmidi2note(): 231 def genmidi2note():
227 '''Create forward & reverse tables for midi number to note name (assuming 69 = A4 = A440)''' 232 '''Create forward & reverse tables for midi number to note name (assuming 69 = A4 = A440)'''
228 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'] 233 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']
229 midi2note = {} 234 midi2note = {}
230 note2midi = {} 235 note2midi = {}
231 for midi in range(21, 128): 236 for midi in range(12, 128):
232 octave = midi / len(names) - 1 237 octave = midi / len(names) - 1
233 index = midi % len(names) 238 index = midi % len(names)
234 name = names[index] % (octave) 239 name = names[index] % (octave)
235 midi2note[midi] = name 240 midi2note[midi] = name
236 note2midi[name] = midi 241 note2midi[name] = midi