8 scale_steps = [0,2,4,5,7,9,11]
13 program_name = 'midi2ly.py [experimental]'
15 def split_track (track):
22 if data[0] > 0x7f and data[0] < 0xf0:
24 e = (e[0], tuple ([data[0] & 0xf0] + data[1:]))
34 for v in chs.values ():
35 events = events_on_channel (v)
36 thread = unthread_notes (events)
38 threads.append (thread)
43 names = (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
44 accidentals = (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
45 acc_name = ('eses', 'es', 'BUG', 'is' , 'isis')
47 def __init__ (self, clocks, pitch, velocity):
48 self.velocity = velocity
52 def clocks_compare (a, b):
53 if a.clocks < b.clocks:
55 elif a.clocks > b.clocks:
62 # minor scale: la-la (= + 5) '''
64 n = self.names[(self.pitch) % 12]
65 a = self.accidentals[(self.pitch) % 12]
68 a = - self.accidentals[(self.pitch) % 12]
71 name = chr ((n + 2) % 7 + ord ('a'))
74 name = name + self.acc_name[a + 2]
76 # By tradition, all scales now consist of a sequence
77 # of 7 notes each with a distinct name, from amongst
78 # a b c d e f g. But, minor scales have a wide
79 # second interval at the top - the 'leading note' is
80 # sharped. (Why? it just works that way! Anything
81 # else doesn't sound as good and isn't as flexible at
82 # saying things. In medieval times, scales only had 6
83 # notes to avoid this problem - the hexachords.)
85 # So, the d minor scale is d e f g a b-flat c-sharp d
86 # - using d-flat for the leading note would skip the
87 # name c and duplicate the name d. Why isn't c-sharp
88 # put in the key signature? Tradition. (It's also
89 # supposedly based on the Pythagorean theory of the
90 # cycle of fifths, but that really only applies to
91 # major scales...) Anyway, g minor is g a b-flat c d
92 # e-flat f-sharp g, and all the other flat minor keys
93 # end up with a natural leading note. And there you
96 # John Sankey <bf250@freenet.carleton.ca>
98 # Let's also do a-minor: a b c d e f gis a
102 o = self.pitch / 12 - 4
105 if key.sharps == 0 and key.flats == 0 and name == 'as':
107 elif key.flats == 1 and name == 'des':
109 elif key.flats == 2 and name == 'ges':
111 elif key.sharps == 5 and name == 'g':
113 elif key.sharps == 6 and name == 'd':
115 elif key.sharps == 7 and name == 'a':
118 if key.flats >= 6 and name == 'b':
121 if key.flats >= 7 and name == 'e':
124 if key.sharps >= 3 and name == 'f':
126 if key.sharps >= 4 and name == 'c':
137 return s + dump_duration (self.clocks) + ' '
141 def __init__ (self, num, den):
147 return '\n ' + '\\time %d/%d ' % (self.num, self.den) + '\n '
150 def __init__ (self, seconds_per_1):
152 self.seconds_per_1 = seconds_per_1
155 # return '\n ' + '\\tempo 1 = %d ' % (60 / self.seconds_per_1) + '\n '
156 return '\n ' + '\\tempo 4 = %d ' % (4 * 60 / self.seconds_per_1) + '\n '
159 key_sharps = ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
160 key_flats = ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
162 def __init__ (self, sharps, flats, minor):
173 if self.sharps and self.flats:
174 s = '\\keysignature %s ' % 'TODO'
178 k = (ord ('cfbeadg'[self.flats % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7
180 k = (ord ('cgdaebf'[self.sharps % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7
183 name = chr ((k + 2) % 7 + ord ('a'))
185 name = chr ((k + 2) % 7 + ord ('a'))
187 # fis cis gis dis ais eis bis
188 sharps = (2, 4, 6, 1, 3, 5, 7)
189 # bes es as des ges ces fes
190 flats = (6, 4, 2, 7, 5, 3, 1)
193 if flats[k] <= self.flats:
196 if sharps[k] <= self.sharps:
200 name = name + Note.acc_name[a + 2]
208 return '\n\n ' + s + '\n '
211 # TODO: really handle lyrics
217 'SEQUENCE_TRACK_NAME',
223 def __init__ (self, type, text):
229 return '\n % [' + self.text_types[self.type] + '] ' + self.text + '\n '
231 def events_on_channel (channel):
239 if e[1][0] == midi.NOTE_ON:
240 if not pitches.has_key (e[1][1]):
241 pitches[e[1][1]] = (t, e[1][2])
242 elif e[1][0] == midi.NOTE_OFF:
244 (lt, vel) = pitches[e[1][1]]
254 (lt, Note (t-lt, e[1][1], vel)))
258 elif e[1][0] == midi.META_EVENT:
259 if e[1][1] == midi.SET_TEMPO:
260 (u0, u1, u2) = map (ord, e[1][2])
261 us_per_4 = u2 + 256 * (u1 + 256 * u0)
262 seconds_per_1 = us_per_4 * 4 / 1e6
263 events.append ((t, Tempo (seconds_per_1)))
264 elif e[1][1] == midi.TIME_SIGNATURE:
265 (num, dur, clocks4, count32) = map (ord, e[1][2])
267 events.append ((t, Time (num, den)))
268 elif e[1][1] == midi.KEY_SIGNATURE:
269 (accidentals, minor) = map (ord, e[1][2])
272 if accidentals < 127:
275 flats = 256 - accidentals
277 events.append ((t, Key (sharps, flats, minor)))
278 elif e[1][1] >= midi.SEQUENCE_NUMBER and e[1][1] <= midi.CUE_POINT:
279 events.append ((t, Text (e[1][1], e[1][2])))
281 sys.stderr.write ("SKIP: %s\n" % `e`)
284 sys.stderr.write ("SKIP: %s\n" % `e`)
289 if i < len (events) and notes[0][0] >= events[i][0]:
292 events.insert (i, notes[0])
296 def unthread_notes (channel):
305 if e[1].__class__ == Note and ((t == start_busy_t and e[1].clocks + t == end_busy_t) \
309 end_busy_t = t + e[1].clocks
310 elif e[1].__class__ == Time or e[1].__class__ == Key or e[1].__class__ == Text or e[1].__class__ == Tempo:
314 threads.append (thread)
329 def dump_skip (clocks):
330 return 's' + dump_duration (clocks) + ' '
332 def dump_duration (clocks):
333 g = gcd (clocks, clocks_per_1)
334 (d, n) = (clocks_per_1/ g, clocks / g)
337 elif n == 3 and d != 1:
350 if i.__class__ == Note:
355 s = s + dump (notes[0])
356 elif len (notes) > 1:
357 s = s + '<' + string.join (map (dump, notes)) + '>'
361 def dump_channel (thread):
370 if last_e and last_e[0] == e[0]:
374 chs.append ((last_e[0], ch))
381 chs.append ((last_e[0], ch))
387 i = string.rfind (lines[-1], '\n')
388 if len (lines[-1][i:]) > LINE_BELL:
393 lines[-1] = lines[-1] + dump_skip (t-last_t)
395 lines[-1] = lines[-1] + dump_chord (ch[1])
399 if i.clocks > clocks:
404 return string.join (lines, '\n ') + '\n'
407 return 'track%c' % (i + ord ('A'))
409 def channel_name (i):
410 return 'channel%c' % (i + ord ('A'))
412 def dump_track (channels, n):
414 track = track_name (n)
415 for i in range (len (channels)):
416 channel = channel_name (i)
417 s = s + '%s = \\notes {\n' % (track + channel)
418 s = s + ' ' + dump_channel (channels[i][0])
421 s = s + '%s = <\n' % track
423 for i in range (len (channels)):
424 channel = channel_name (i)
425 s = s + ' \\context Voice = %s \\%s\n' % (channel, track + channel)
430 def convert_midi (f):
433 str = open (f).read ()
434 midi_dump = midi.parse (str)
436 clocks_per_1 = midi_dump[0][1]
439 for t in midi_dump[1]:
440 tracks.append (split_track (t))
442 tag = '%% Lily was here -- automatically converted by %s from %s' % ( program_name, f)
446 for i in range (len (tracks)):
447 s = s + dump_track (tracks[i], i)
449 s = s + '\n\\score {\n <\n'
450 for i in range (len (tracks)):
451 track = track_name (i)
452 s = s + ' \\context Staff=%s \\%s\n' % (track, track)
458 for f in sys.argv[1:]: