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