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