Mercurial > ~darius > hgwebdir.cgi > musiccutter
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 |