]> git.donarmstrong.com Git - lilypond.git/blob - scripts/midi2ly.py
The IR guide
[lilypond.git] / scripts / midi2ly.py
1 #!@TARGET_PYTHON@
2 #
3 # msdi2ly.py -- LilyPond midi import script
4
5 # source file of the GNU LilyPond music typesetter
6 #
7 # (c) 1998--2007  Han-Wen Nienhuys <hanwen@xs4all.nl>
8 #                 Jan Nieuwenhuizen <janneke@gnu.org>
9
10
11 '''
12 TODO:
13     * test on weird and unquantised midi input (lily-devel)
14     * update doc and manpage
15
16     * simply insert clef changes whenever too many ledger lines
17         [to avoid tex capacity exceeded]
18     * do not ever quant skips
19     * better lyrics handling
20     * [see if it is feasible to] move ly-classes to library for use in
21         other converters, while leaving midi specific stuff here
22 '''
23
24 import os
25 import string
26 import sys
27
28 """
29 @relocate-preamble@
30 """
31
32 import midi
33 import lilylib as ly
34 global _;_=ly._
35
36 ################################################################
37 ## CONSTANTS
38
39
40 output_name = ''
41 LINE_BELL = 60
42 scale_steps = [0,2,4,5,7,9,11]
43 global_options = None
44
45
46 clocks_per_1 = 1536
47 clocks_per_4 = 0
48
49 time = 0
50 reference_note = 0
51 start_quant_clocks = 0
52
53 duration_quant_clocks = 0
54 allowed_tuplet_clocks = []
55
56
57 ################################################################
58
59
60 program_name = sys.argv[0]
61 program_version = '@TOPLEVEL_VERSION@'
62
63 errorport = sys.stderr
64
65 def identify ():
66     sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
67
68 def warranty ():
69     identify ()
70     ly.encoded_write (sys.stdout, '''
71 Copyright (c) %s by
72
73  Han-Wen Nienhuys
74  Jan Nieuwenhuizen
75
76 %s
77 %s
78 '''  ( '2001--2006',
79    _('Distributed under terms of the GNU General Public License.'),
80    _('It comes with NO WARRANTY.')))
81
82
83 def progress (s):
84     ly.encoded_write (errorport, s + '\n')
85
86 def warning (s):
87     progress (_ ("warning: ") + s)
88         
89 def error (s):
90     progress (_ ("error: ") + s)
91     raise _ ("Exiting... ")
92
93 def system (cmd, ignore_error = 0):
94     return ly.system (cmd, ignore_error=ignore_error)
95
96 def strip_extension (f, ext):
97     (p, e) = os.path.splitext (f)
98     if e == ext:
99         e = ''
100     return p + e
101
102
103
104 class Duration:
105     allowed_durs = (1, 2, 4, 8, 16, 32, 64, 128)
106     def __init__ (self, clocks):
107         self.clocks = clocks
108         if clocks <= 0:
109             self.clocks = duration_quant_clocks
110         (self.dur, self.num, self.den) = self.dur_num_den (clocks)
111         
112     def dur_num_den (self, clocks):
113         for i in range (len (allowed_tuplet_clocks)):
114             if clocks == allowed_tuplet_clocks[i]:
115                 return global_options.allowed_tuplets[i]
116
117         dur = 0; num = 1; den = 1;
118         g = gcd (clocks, clocks_per_1)
119         if g:
120             (dur, num) = (clocks_per_1 / g, clocks / g)
121         if not dur in self.allowed_durs:
122             dur = 4; num = clocks; den = clocks_per_4
123         return (dur, num, den)
124
125     def dump (self):
126         if self.den == 1:
127             if self.num == 1:
128                 s = '%d' % self.dur
129             elif self.num == 3 and self.dur != 1:
130                 s = '%d.' % (self.dur / 2)
131             else:
132                 s = '%d*%d' % (self.dur, self.num)
133         else:
134             s = '%d*%d/%d' % (self.dur, self.num, self.den)
135             
136         global reference_note
137         reference_note.duration = self
138
139         return s
140
141     def compare (self, other):
142         return self.clocks - other.clocks
143
144 def sign (x):
145     if x >= 0:
146         return 1
147     else:
148         return -1
149
150 class Note:
151     names = (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
152     alterations = (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
153     alteration_names = ('eses', 'es', '', 'is' , 'isis')
154     def __init__ (self, clocks, pitch, velocity):
155         self.pitch = pitch
156         self.velocity = velocity
157         # hmm
158         self.clocks = clocks
159         self.duration = Duration (clocks)
160         (self.octave, self.notename, self.alteration) = self.o_n_a ()
161
162     def o_n_a (self):
163         # major scale: do-do
164         # minor scale: la-la  (= + 5) '''
165
166         n = self.names[(self.pitch) % 12]
167         a = self.alterations[(self.pitch) % 12]
168
169         if a and global_options.key.flats:
170             a = - self.alterations[(self.pitch) % 12]
171             n = (n - a) % 7
172
173         #  By tradition, all scales now consist of a sequence
174         #  of 7 notes each with a distinct name, from amongst
175         #  a b c d e f g.  But, minor scales have a wide
176         #  second interval at the top - the 'leading note' is
177         #  sharped. (Why? it just works that way! Anything
178         #  else doesn't sound as good and isn't as flexible at
179         #  saying things. In medieval times, scales only had 6
180         #  notes to avoid this problem - the hexachords.)
181
182         #  So, the d minor scale is d e f g a b-flat c-sharp d
183         #  - using d-flat for the leading note would skip the
184         #  name c and duplicate the name d.  Why isn't c-sharp
185         #  put in the key signature? Tradition. (It's also
186         #  supposedly based on the Pythagorean theory of the
187         #  cycle of fifths, but that really only applies to
188         #  major scales...)  Anyway, g minor is g a b-flat c d
189         #  e-flat f-sharp g, and all the other flat minor keys
190         #  end up with a natural leading note. And there you
191         #  have it.
192
193         #  John Sankey <bf250@freenet.carleton.ca>
194         #
195         #  Let's also do a-minor: a b c d e f gis a
196         #
197         #  --jcn
198
199         o = self.pitch / 12 - 4
200
201         key = global_options.key
202         if key.minor:
203             # as -> gis
204             if (key.sharps == 0 and key.flats == 0
205                 and n == 5 and a == -1):
206                 n = 4; a = 1
207             # des -> cis
208             elif key.flats == 1 and n == 1 and a == -1:
209                 n = 0; a = 1
210             # ges -> fis
211             elif key.flats == 2 and n == 4 and a == -1:
212                 n = 3; a = 1
213             # g -> fisis
214             elif key.sharps == 5 and n == 4 and a == 0:
215                 n = 3; a = 2
216             # d -> cisis
217             elif key.sharps == 6 and n == 1 and a == 0:
218                 n = 0; a = 2
219             # a -> gisis
220             elif key.sharps == 7 and n == 5 and a == 0:
221                 n = 4; a = 2
222
223         # b -> ces
224         if key.flats >= 6 and n == 6 and a == 0:
225             n = 0; a = -1; o = o + 1
226         # e -> fes
227         if key.flats >= 7 and n == 2 and a == 0:
228             n = 3; a = -1
229
230         # f -> eis
231         if key.sharps >= 3 and n == 3 and a == 0:
232             n = 2; a = 1
233         # c -> bis
234         if key.sharps >= 4 and n == 0 and a == 0:
235             n = 6; a = 1; o = o - 1
236
237         return (o, n, a)
238         
239     def __repr__ (self):
240         s = chr ((self.notename + 2)  % 7 + ord ('a'))
241         return 'Note(%s %s)' % (s, self.duration.dump())
242
243     def dump (self, dump_dur = 1):
244         global reference_note
245         s = chr ((self.notename + 2)  % 7 + ord ('a'))
246         s = s + self.alteration_names[self.alteration + 2]
247         if global_options.absolute_pitches:
248             commas = self.octave
249         else:
250             delta = self.pitch - reference_note.pitch
251             commas = sign (delta) * (abs (delta) / 12)
252             if ((sign (delta) \
253               * (self.notename - reference_note.notename) + 7) \
254               % 7 >= 4) \
255               or ((self.notename == reference_note.notename) \
256                 and (abs (delta) > 4) and (abs (delta) < 12)):
257                 commas = commas + sign (delta)
258             
259         if commas > 0:
260             s = s + "'" * commas
261         elif commas < 0:
262             s = s + "," * -commas
263
264         ## FIXME: compile fix --jcn
265         if dump_dur and (global_options.explicit_durations \
266          or self.duration.compare (reference_note.duration)):
267             s = s + self.duration.dump ()
268
269         reference_note = self
270         
271         # TODO: move space
272         return s + ' '
273
274
275 class Time:
276     def __init__ (self, num, den):
277         self.clocks = 0
278         self.num = num
279         self.den = den
280
281     def bar_clocks (self):
282         return clocks_per_1 * self.num / self.den
283
284     def __repr__ (self):
285         return 'Time(%d/%d)' % (self.num, self.den)
286     
287     def dump (self):
288         global time
289         time = self
290         return '\n  ' + '\\time %d/%d ' % (self.num, self.den) + '\n  '
291
292 class Tempo:
293     def __init__ (self, seconds_per_1):
294         self.clocks = 0
295         self.seconds_per_1 = seconds_per_1
296
297     def __repr__ (self):
298         return 'Tempo(%d)' % self.bpm ()
299     
300     def bpm (self):
301         return 4 * 60 / self.seconds_per_1
302     
303     def dump (self):
304         return '\n  ' + '\\tempo 4 = %d ' % (self.bpm()) + '\n  '
305
306 class Clef:
307     clefs = ('"bass_8"', 'bass', 'violin', '"violin^8"')
308     def __init__ (self, type):
309         self.type = type
310
311     def __repr__ (self):
312         return 'Clef(%s)' % self.clefs[self.type]
313     
314     def dump (self):
315         return '\n  \\clef %s\n  ' % self.clefs[self.type]
316
317 class Key:
318     key_sharps = ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
319     key_flats = ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
320
321     def __init__ (self, sharps, flats, minor):
322         self.clocks = 0
323         self.flats = flats
324         self.sharps = sharps
325         self.minor = minor
326
327     def dump (self):
328         global_options.key = self
329
330         s = ''
331         if self.sharps and self.flats:
332             pass
333         else:
334             if self.flats:
335                 k = (ord ('cfbeadg'[self.flats % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7
336             else:
337                 k = (ord ('cgdaebf'[self.sharps % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7
338  
339             if not self.minor:
340                 name = chr ((k + 2) % 7 + ord ('a'))
341             else:
342                 name = chr ((k + 2) % 7 + ord ('a'))
343
344             # fis cis gis dis ais eis bis
345             sharps = (2, 4, 6, 1, 3, 5, 7)
346             # bes es as des ges ces fes
347             flats = (6, 4, 2, 7, 5, 3, 1)
348             a = 0
349             if self.flats:
350                 if flats[k] <= self.flats:
351                     a = -1
352             else:
353                 if sharps[k] <= self.sharps:
354                     a = 1
355
356             if a:
357                 name = name + Note.alteration_names[a + 2]
358
359             s = '\\key ' + name
360             if self.minor:
361                 s = s + ' \\minor'
362             else:
363                 s = s + ' \\major'
364
365         return '\n\n  ' + s + '\n  '
366
367
368 class Text:
369     text_types = (
370         'SEQUENCE_NUMBER',
371         'TEXT_EVENT',
372         'COPYRIGHT_NOTICE',
373         'SEQUENCE_TRACK_NAME',
374         'INSTRUMENT_NAME',
375         'LYRIC',
376         'MARKER',
377         'CUE_POINT',)
378     
379     def __init__ (self, type, text):
380         self.clocks = 0
381         self.type = type
382         self.text = text
383
384     def dump (self):
385         # urg, we should be sure that we're in a lyrics staff
386         if self.type == midi.LYRIC:
387             s = '"%s"' % self.text
388             d = Duration (self.clocks)
389             if global_options.explicit_durations \
390              or d.compare (reference_note.duration):
391                 s = s + Duration (self.clocks).dump ()
392             s = s + ' '
393         else:
394             s = '\n  % [' + self.text_types[self.type] + '] ' + self.text + '\n  '
395         return s
396
397     def __repr__ (self):
398         return 'Text(%d=%s)' % (self.type, self.text)
399
400
401
402 def split_track (track):
403     chs = {}
404     for i in range(16):
405         chs[i] = []
406         
407     for e in track:
408         data = list (e[1])
409         if data[0] > 0x7f and data[0] < 0xf0:
410             c = data[0] & 0x0f
411             e = (e[0], tuple ([data[0] & 0xf0] + data[1:]))
412             chs[c].append (e)
413         else:
414             chs[0].append (e)
415
416     for i in range (16):
417         if chs[i] == []:
418             del chs[i]
419
420     threads = []
421     for v in chs.values ():
422         events = events_on_channel (v)
423         thread = unthread_notes (events)
424         if len (thread):
425             threads.append (thread)
426     return threads
427
428
429 def quantise_clocks (clocks, quant):
430     q = int (clocks / quant) * quant
431     if q != clocks:
432         for tquant in allowed_tuplet_clocks:
433             if int (clocks / tquant) * tquant == clocks:
434                 return clocks
435         if 2 * (clocks - q) > quant:
436             q = q + quant
437     return q
438
439 def end_note (pitches, notes, t, e):
440     try:
441         (lt, vel) = pitches[e]
442         del pitches[e]
443
444         i = len (notes) - 1 
445         while i > 0:
446             if notes[i][0] > lt:
447                 i = i -1
448             else:
449                 break
450         d = t - lt
451         if duration_quant_clocks:
452             d = quantise_clocks (d, duration_quant_clocks)
453             if not d:
454                 d = duration_quant_clocks
455
456         notes.insert (i + 1,
457               (lt, Note (d, e, vel)))
458
459     except KeyError:
460         pass
461
462 def events_on_channel (channel):
463     pitches = {}
464
465     notes = []
466     events = []
467     last_lyric = 0
468     last_time = 0
469     for e in channel:
470         t = e[0]
471
472         if start_quant_clocks:
473             t = quantise_clocks (t, start_quant_clocks)
474
475
476         if e[1][0] == midi.NOTE_OFF \
477          or (e[1][0] == midi.NOTE_ON and e[1][2] == 0):
478             end_note (pitches, notes, t, e[1][1])
479             
480         elif e[1][0] == midi.NOTE_ON:
481             if not pitches.has_key (e[1][1]):
482                 pitches[e[1][1]] = (t, e[1][2])
483                 
484         # all include ALL_NOTES_OFF
485         elif e[1][0] >= midi.ALL_SOUND_OFF \
486           and e[1][0] <= midi.POLY_MODE_ON:
487             for i in pitches.keys ():
488                 end_note (pitches, notes, t, i)
489                 
490         elif e[1][0] == midi.META_EVENT:
491             if e[1][1] == midi.END_OF_TRACK:
492                 for i in pitches.keys ():
493                     end_note (pitches, notes, t, i)
494                 break
495
496             elif e[1][1] == midi.SET_TEMPO:
497                 (u0, u1, u2) = map (ord, e[1][2])
498                 us_per_4 = u2 + 256 * (u1 + 256 * u0)
499                 seconds_per_1 = us_per_4 * 4 / 1e6
500                 events.append ((t, Tempo (seconds_per_1)))
501             elif e[1][1] == midi.TIME_SIGNATURE:
502                 (num, dur, clocks4, count32) = map (ord, e[1][2])
503                 den = 2 ** dur 
504                 events.append ((t, Time (num, den)))
505             elif e[1][1] == midi.KEY_SIGNATURE:
506                 (alterations, minor) = map (ord, e[1][2])
507                 sharps = 0
508                 flats = 0
509                 if alterations < 127:
510                     sharps = alterations
511                 else:
512                     flats = 256 - alterations
513
514                 k = Key (sharps, flats, minor)
515                 events.append ((t, k))
516
517                 # ugh, must set key while parsing
518                 # because Note init uses key
519                 # Better do Note.calc () at dump time?
520                 global_options.key = k
521
522             elif e[1][1] == midi.LYRIC \
523               or (global_options.text_lyrics and e[1][1] == midi.TEXT_EVENT):
524                 if last_lyric:
525                     last_lyric.clocks = t - last_time
526                     events.append ((last_time, last_lyric))
527                 last_time = t
528                 last_lyric = Text (midi.LYRIC, e[1][2])
529
530             elif e[1][1] >= midi.SEQUENCE_NUMBER \
531               and e[1][1] <= midi.CUE_POINT:
532                 events.append ((t, Text (e[1][1], e[1][2])))
533             else:
534                 if global_options.verbose:
535                     sys.stderr.write ("SKIP: %s\n" % `e`)
536                 pass
537         else:
538             if global_options.verbose:
539                 sys.stderr.write ("SKIP: %s\n" % `e`)
540             pass
541
542     if last_lyric:
543         # last_lyric.clocks = t - last_time
544         # hmm
545         last_lyric.clocks = clocks_per_4
546         events.append ((last_time, last_lyric))
547         last_lyric = 0
548         
549     i = 0
550     while len (notes):
551         if i < len (events) and notes[0][0] >= events[i][0]:
552             i = i + 1
553         else:
554             events.insert (i, notes[0])
555             del notes[0]
556     return events
557
558 def unthread_notes (channel):
559     threads = []
560     while channel:
561         thread = []
562         end_busy_t = 0
563         start_busy_t = 0
564         todo = []
565         for e in channel:
566             t = e[0]
567             if e[1].__class__ == Note \
568              and ((t == start_busy_t \
569                 and e[1].clocks + t == end_busy_t) \
570               or t >= end_busy_t):
571                 thread.append (e)
572                 start_busy_t = t
573                 end_busy_t = t + e[1].clocks
574             elif e[1].__class__ == Time \
575               or e[1].__class__ == Key \
576               or e[1].__class__ == Text \
577               or e[1].__class__ == Tempo:
578                 thread.append (e)
579             else:
580                 todo.append (e)
581         threads.append (thread)
582         channel = todo
583
584     return threads
585
586 def gcd (a,b):
587     if b == 0:
588         return a
589     c = a
590     while c: 
591         c = a % b
592         a = b
593         b = c
594     return a
595     
596 def dump_skip (skip, clocks):
597     return skip + Duration (clocks).dump () + ' '
598
599 def dump (d):
600     return d.dump ()
601
602 def dump_chord (ch):
603     s = ''
604     notes = []
605     for i in ch:
606         if i.__class__ == Note:
607             notes.append (i)
608         else:
609             s = s + i.dump ()
610     if len (notes) == 1:
611         s = s + dump (notes[0])
612     elif len (notes) > 1:
613         global reference_note
614         s = s + '<'
615         s = s + notes[0].dump (dump_dur = 0)
616         r = reference_note
617         for i in notes[1:]:
618             s = s + i.dump (dump_dur = 0 )
619         s = s + '>'
620
621         s = s + notes[0].duration.dump() + ' '
622         reference_note = r
623     return s
624
625 def dump_bar_line (last_bar_t, t, bar_count):
626     s = ''
627     bar_t = time.bar_clocks ()
628     if t - last_bar_t >= bar_t:
629         bar_count = bar_count + (t - last_bar_t) / bar_t
630         
631         if t - last_bar_t == bar_t:
632             s = '|\n  %% %d\n  ' % bar_count
633             last_bar_t = t
634         else:
635             # urg, this will barf at meter changes
636             last_bar_t = last_bar_t + (t - last_bar_t) / bar_t * bar_t
637             
638     return (s, last_bar_t, bar_count)
639
640             
641 def dump_channel (thread, skip):
642     global reference_note, time
643
644     global_options.key = Key (0, 0, 0)
645     time = Time (4, 4)
646     # urg LilyPond doesn't start at c4, but
647     # remembers from previous tracks!
648     # reference_note = Note (clocks_per_4, 4*12, 0)
649     reference_note = Note (0, 4*12, 0)
650     last_e = None
651     chs = []
652     ch = []
653
654     for e in thread:
655         if last_e and last_e[0] == e[0]:
656             ch.append (e[1])
657         else:
658             if ch:
659                 chs.append ((last_e[0], ch))
660                 
661             ch = [e[1]]
662             
663         last_e = e
664
665     if ch:
666         chs.append ((last_e[0], ch))
667     t = 0
668     last_t = 0
669     last_bar_t = 0
670     bar_count = 1
671     
672     lines = ['']
673     for ch in chs: 
674         t = ch[0]
675
676         i = string.rfind (lines[-1], '\n') + 1
677         if len (lines[-1][i:]) > LINE_BELL:
678             lines.append ('')
679             
680         if t - last_t > 0:
681             lines[-1] = lines[-1] + dump_skip (skip, t-last_t)
682         elif t - last_t < 0:
683             errorport.write ('BUG: time skew')
684
685         (s, last_bar_t, bar_count) = dump_bar_line (last_bar_t,
686                               t, bar_count)
687         lines[-1] = lines[-1] + s
688         
689         lines[-1] = lines[-1] + dump_chord (ch[1])
690
691         clocks = 0
692         for i in ch[1]:
693             if i.clocks > clocks:
694                 clocks = i.clocks
695                 
696         last_t = t + clocks
697         
698         (s, last_bar_t, bar_count) = dump_bar_line (last_bar_t,
699                               last_t, bar_count)
700         lines[-1] = lines[-1] + s
701
702     return string.join (lines, '\n  ') + '\n'
703
704 def track_name (i):
705     return 'track%c' % (i + ord ('A'))
706
707 def channel_name (i):
708     return 'channel%c' % (i + ord ('A'))
709
710 def dump_track (channels, n):
711     s = '\n'
712     track = track_name (n)
713     clef = guess_clef (channels)
714
715     for i in range (len (channels)):
716         channel = channel_name (i)
717         item = thread_first_item (channels[i])
718
719         if item and item.__class__ == Note:
720             skip = 's'
721             s = s + '%s = ' % (track + channel)
722             if not global_options.absolute_pitches:
723                 s = s + '\\relative c '
724         elif item and item.__class__ == Text:
725             skip = '" "'
726             s = s + '%s = \\lyricmode ' % (track + channel)
727         else:
728             skip = '\\skip '
729             s = s + '%s =  ' % (track + channel)
730         s = s + '{\n'
731         s = s + '  ' + dump_channel (channels[i][0], skip)
732         s = s + '}\n\n'
733
734     s = s + '%s = <<\n' % track
735
736     if clef.type != 2:
737         s = s + clef.dump () + '\n'
738
739     for i in range (len (channels)):
740         channel = channel_name (i)
741         item = thread_first_item (channels[i])
742         if item and item.__class__ == Text:
743             s = s + '  \\context Lyrics = %s \\%s\n' % (channel,
744                                   track + channel)
745         else:
746             s = s + '  \\context Voice = %s \\%s\n' % (channel,
747                                  track + channel)
748     s = s + '>>\n\n'
749     return s
750
751 def thread_first_item (thread):
752     for chord in thread:
753         for event in chord:
754             if (event[1].__class__ == Note 
755               or (event[1].__class__ == Text 
756                 and event[1].type == midi.LYRIC)):
757                 
758               return event[1]
759     return None
760
761 def track_first_item (track):
762     for thread in track:
763         first = thread_first_item (thread)
764         if first:
765             return first
766     return None
767
768 def guess_clef (track):
769     i = 0
770     p = 0
771     for thread in track:
772         for chord in thread:
773             for event in chord:
774                 if event[1].__class__ == Note:
775                     i = i + 1
776                     p = p + event[1].pitch
777     if i and p / i <= 3*12:
778         return Clef (0)
779     elif i and p / i <= 5*12:
780         return Clef (1)
781     elif i and p / i >= 7*12:
782         return Clef (3)
783     else:
784         return Clef (2)
785     
786
787 def convert_midi (in_file, out_file):
788     global clocks_per_1, clocks_per_4, key
789     global start_quant_clocks
790     global  duration_quant_clocks
791     global allowed_tuplet_clocks
792
793     str = open (in_file).read ()
794     midi_dump = midi.parse (str)
795     
796     clocks_per_1 = midi_dump[0][1]
797     clocks_per_4 = clocks_per_1 / 4
798     
799     if global_options.start_quant:
800         start_quant_clocks = clocks_per_1 / global_options.start_quant
801
802     if global_options.duration_quant:
803         duration_quant_clocks = clocks_per_1 / global_options.duration_quant
804
805     allowed_tuplet_clocks = []
806     for (dur, num, den) in global_options.allowed_tuplets:
807         allowed_tuplet_clocks.append (clocks_per_1 / den)
808
809     tracks = []
810     for t in midi_dump[1]:
811         global_options.key = Key (0, 0, 0)
812         tracks.append (split_track (t))
813
814     tag = '%% Lily was here -- automatically converted by %s from %s' % ( program_name, in_file)
815
816     
817     s = ''
818     s = tag + '\n\\version "2.7.18"\n\n'
819     for i in range (len (tracks)):
820         s = s + dump_track (tracks[i], i)
821
822     s = s + '\n\\score {\n  <<\n'
823     
824     i = 0
825     for t in tracks:
826         track = track_name (i)
827         item = track_first_item (t)
828         
829         if item and item.__class__ == Note:
830             s = s + '    \\context Staff=%s \\%s\n' % (track, track)
831         elif item and item.__class__ == Text:
832             s = s + '    \\context Lyrics=%s \\%s\n' % (track, track)
833
834         i += 1
835     s = s + '  >>\n}\n'
836
837     progress (_ ("%s output to `%s'...") % ('LY', out_file))
838
839     if out_file == '-':
840         handle = sys.stdout
841     else:
842         handle = open (out_file, 'w')
843
844     handle.write (s)
845     handle.close ()
846
847
848 def get_option_parser ():
849     p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'midi2ly',
850                  description=_ ("Convert %s to LilyPond input.") % 'MIDI',
851                  add_help_option=False)
852
853     p.add_option ('-a', '--absolute-pitches',
854            action='store_true',
855            help=_ ("print absolute pitches"))
856     p.add_option ('-d', '--duration-quant',
857            metavar= _("DUR"),
858            help=_ ("quantise note durations on DUR"))
859     p.add_option ('-e', '--explicit-durations',
860            action='store_true',
861            help=_ ("print explicit durations"))
862     p.add_option("-h", "--help",
863                  action="help",
864                  help=_ ("show this help and exit"))
865     p.add_option('-k', '--key', help=_ ("set key: ALT=+sharps|-flats; MINOR=1"),
866           metavar=_ ("ALT[:MINOR]"),
867           default='0'),
868     p.add_option ('-o', '--output', help=_ ("write output to FILE"),
869            metavar=_("FILE"),
870            action='store')
871     p.add_option ('-s', '--start-quant',help= _ ("quantise note starts on DUR"),
872            metavar=_ ("DUR"))
873     p.add_option ('-t', '--allow-tuplet',
874            metavar=_ ("DUR*NUM/DEN"),
875            action = "append",
876            dest="allowed_tuplets",
877            help=_ ("allow tuplet durations DUR*NUM/DEN"),
878            default=[])
879     p.add_option ('-V', '--verbose', help=_ ("be verbose"),
880            action='store_true'
881            ),
882     p.version = "midi2ly (LilyPond) @TOPLEVEL_VERSION@"
883     p.add_option("--version",
884                  action="version",
885                  help=_ ("show version number and exit"))
886     p.add_option ('-w', '--warranty', help=_ ("show warranty and copyright"),
887            action='store_true',
888            ),
889     p.add_option ('-x', '--text-lyrics', help=_ ("treat every text as a lyric"),
890            action='store_true')
891
892     p.add_option_group (ly.display_encode (_ ("Examples")),
893               description = r'''
894   midi2ly --key=-2:1 --duration-quant=32 \
895     --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
896 ''')
897     p.add_option_group (ly.display_encode (_ ('Bugs')),
898                         description=(_ ('Report bugs via')
899                                      + ''' http://post.gmane.org/post.php'''
900                                      '''?group=gmane.comp.gnu.lilypond.bugs\n'''))
901     return p
902
903
904
905 def do_options ():
906     opt_parser = get_option_parser()
907     (options, args) = opt_parser.parse_args ()
908
909     if not args or args[0] == '-':
910         opt_parser.print_help ()
911         ly.stderr_write ('\n%s: %s %s\n' % (program_name, _ ("error: "),
912                          _ ("no files specified on command line.")))
913         sys.exit (2)
914
915     if options.duration_quant:
916         options.duration_quant = int (options.duration_quant)
917
918     if options.warranty:
919         warranty ()
920         sys.exit (0)
921     if 1:
922         (alterations, minor) = map (int, string.split (options.key + ':0', ':'))[0:2]
923         sharps = 0
924         flats = 0
925         if alterations >= 0:
926             sharps = alterations
927         else:
928             flats = - alterations
929
930         options.key = Key (sharps, flats, minor)
931
932         
933     if options.start_quant:
934         options.start_quant = int (options.start_quant)
935         
936     options.allowed_tuplets = [map (int, a.replace ('/','*').split ('*'))
937                 for a in options.allowed_tuplets]
938     
939     global global_options
940     global_options = options
941
942     return args
943
944 def main():
945     files = do_options()
946
947     for f in files:
948         g = f
949         g = strip_extension (g, '.midi')
950         g = strip_extension (g, '.mid')
951         g = strip_extension (g, '.MID')
952         (outdir, outbase) = ('','')
953
954         if not output_name:
955             outdir = '.'
956             outbase = os.path.basename (g)
957             o = os.path.join (outdir, outbase + '-midi.ly')
958         elif output_name[-1] == os.sep:
959             outdir = output_name
960             outbase = os.path.basename (g)
961             os.path.join (outdir, outbase + '-gen.ly')
962         else:
963             o = output_name
964             (outdir, outbase) = os.path.split (o)
965
966         if outdir != '.' and outdir != '':
967             try:
968                 os.mkdir (outdir, 0777)
969             except OSError:
970                 pass
971
972         convert_midi (f, o)
973 if __name__ == '__main__':
974     main()