3 # midi2ly.py -- LilyPond midi import script
5 # This file is part of LilyPond, the GNU music typesetter.
7 # Copyright (C) 1998--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 # Jan Nieuwenhuizen <janneke@gnu.org>
10 # LilyPond is free software: you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation, either version 3 of the License, or
13 # (at your option) any later version.
15 # LilyPond is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
39 ################################################################
44 scale_steps
= [0, 2, 4, 5, 7, 9, 11]
52 start_quant_clocks
= 0
54 duration_quant_clocks
= 0
55 allowed_tuplet_clocks
= []
58 ################################################################
61 program_name
= sys
.argv
[0]
62 program_version
= '@TOPLEVEL_VERSION@'
64 authors
= ('Jan Nieuwenhuizen <janneke@gnu.org>',
65 'Han-Wen Nienhuys <hanwen@xs4all.nl>')
67 errorport
= sys
.stderr
70 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
74 ly
.encoded_write (sys
.stdout
, '''
81 ''' % ( _ ('Copyright (c) %s by') % '1998--2011',
83 _ ('Distributed under terms of the GNU General Public License.'),
84 _ ('It comes with NO WARRANTY.')))
87 ly
.encoded_write (errorport
, s
+ '\n')
90 progress (_ ("warning: ") + s
)
93 progress (_ ("error: ") + s
)
94 raise Exception (_ ("Exiting... "))
97 if global_options
.debug
:
98 progress ("debug: " + s
)
100 def system (cmd
, ignore_error
= 0):
101 return ly
.system (cmd
, ignore_error
=ignore_error
)
103 def strip_extension (f
, ext
):
104 (p
, e
) = os
.path
.splitext (f
)
111 allowed_durs
= (1, 2, 4, 8, 16, 32, 64, 128)
112 def __init__ (self
, clocks
):
115 self
.clocks
= duration_quant_clocks
116 (self
.dur
, self
.num
, self
.den
) = self
.dur_num_den (clocks
)
118 def dur_num_den (self
, clocks
):
119 for i
in range (len (allowed_tuplet_clocks
)):
120 if clocks
== allowed_tuplet_clocks
[i
]:
121 return global_options
.allowed_tuplets
[i
]
123 dur
= 0; num
= 1; den
= 1;
124 g
= gcd (clocks
, clocks_per_1
)
126 (dur
, num
) = (clocks_per_1
/ g
, clocks
/ g
)
127 if not dur
in self
.allowed_durs
:
128 dur
= 4; num
= clocks
; den
= clocks_per_4
129 return (dur
, num
, den
)
135 elif self
.num
== 3 and self
.dur
!= 1:
136 s
= '%d.' % (self
.dur
/ 2)
138 s
= '%d*%d' % (self
.dur
, self
.num
)
140 s
= '%d*%d/%d' % (self
.dur
, self
.num
, self
.den
)
142 global reference_note
143 if reference_note
: # debugging
144 reference_note
.duration
= self
148 def compare (self
, other
):
149 return self
.clocks
- other
.clocks
158 names
= (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
159 alterations
= (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
160 alteration_names
= ('eses', 'es', '', 'is' , 'isis')
161 def __init__ (self
, clocks
, pitch
, velocity
):
163 self
.velocity
= velocity
166 self
.duration
= Duration (clocks
)
167 (self
.octave
, self
.notename
, self
.alteration
) = self
.o_n_a ()
171 # minor scale: la-la (= + 5) '''
173 n
= self
.names
[(self
.pitch
) % 12]
174 a
= self
.alterations
[(self
.pitch
) % 12]
176 key
= global_options
.key
181 a
= - self
.alterations
[(self
.pitch
) % 12]
184 # By tradition, all scales now consist of a sequence
185 # of 7 notes each with a distinct name, from amongst
186 # a b c d e f g. But, minor scales have a wide
187 # second interval at the top - the 'leading note' is
188 # sharped. (Why? it just works that way! Anything
189 # else doesn't sound as good and isn't as flexible at
190 # saying things. In medieval times, scales only had 6
191 # notes to avoid this problem - the hexachords.)
193 # So, the d minor scale is d e f g a b-flat c-sharp d
194 # - using d-flat for the leading note would skip the
195 # name c and duplicate the name d. Why isn't c-sharp
196 # put in the key signature? Tradition. (It's also
197 # supposedly based on the Pythagorean theory of the
198 # cycle of fifths, but that really only applies to
199 # major scales...) Anyway, g minor is g a b-flat c d
200 # e-flat f-sharp g, and all the other flat minor keys
201 # end up with a natural leading note. And there you
204 # John Sankey <bf250@freenet.carleton.ca>
206 # Let's also do a-minor: a b c d e f gis a
210 o
= self
.pitch
/ 12 - 4
214 if (key
.sharps
== 0 and key
.flats
== 0
215 and n
== 5 and a
== -1):
218 elif key
.flats
== 1 and n
== 1 and a
== -1:
221 elif key
.flats
== 2 and n
== 4 and a
== -1:
224 elif key
.sharps
== 5 and n
== 4 and a
== 0:
227 elif key
.sharps
== 6 and n
== 1 and a
== 0:
230 elif key
.sharps
== 7 and n
== 5 and a
== 0:
234 if key
.flats
>= 6 and n
== 6 and a
== 0:
235 n
= 0; a
= -1; o
= o
+ 1
237 if key
.flats
>= 7 and n
== 2 and a
== 0:
241 if key
.sharps
>= 3 and n
== 3 and a
== 0:
244 if key
.sharps
>= 4 and n
== 0 and a
== 0:
245 n
= 6; a
= 1; o
= o
- 1
250 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
251 return 'Note(%s %s)' % (s
, self
.duration
.dump ())
253 def dump (self
, dump_dur
=True):
254 global reference_note
255 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
256 s
= s
+ self
.alteration_names
[self
.alteration
+ 2]
257 if global_options
.absolute_pitches
:
260 delta
= self
.pitch
- reference_note
.pitch
261 commas
= sign (delta
) * (abs (delta
) / 12)
263 * (self
.notename
- reference_note
.notename
) + 7)
265 or ((self
.notename
== reference_note
.notename
)
266 and (abs (delta
) > 4) and (abs (delta
) < 12))):
267 commas
= commas
+ sign (delta
)
272 s
= s
+ "," * -commas
275 and self
.duration
.compare (reference_note
.duration
))
276 or global_options
.explicit_durations
):
277 s
= s
+ self
.duration
.dump ()
279 reference_note
= self
286 def __init__ (self
, num
, den
):
291 def bar_clocks (self
):
292 return clocks_per_1
* self
.num
/ self
.den
295 return 'Time(%d/%d)' % (self
.num
, self
.den
)
300 return '\n ' + '\\time %d/%d ' % (self
.num
, self
.den
) + '\n '
303 def __init__ (self
, seconds_per_1
):
305 self
.seconds_per_1
= seconds_per_1
308 return 'Tempo(%d)' % self
.bpm ()
311 return 4 * 60 / self
.seconds_per_1
314 return '\n ' + '\\tempo 4 = %d ' % (self
.bpm ()) + '\n '
317 clefs
= ('"bass_8"', 'bass', 'violin', '"violin^8"')
318 def __init__ (self
, type):
322 return 'Clef(%s)' % self
.clefs
[self
.type]
325 return '\n \\clef %s\n ' % self
.clefs
[self
.type]
328 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
329 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
331 def __init__ (self
, sharps
, flats
, minor
):
338 global_options
.key
= self
341 if self
.sharps
and self
.flats
:
345 k
= (ord ('cfbeadg'[self
.flats
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
347 k
= (ord ('cgdaebf'[self
.sharps
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
350 name
= chr ((k
+ 2) % 7 + ord ('a'))
352 name
= chr ((k
+ 2) % 7 + ord ('a'))
354 # fis cis gis dis ais eis bis
355 sharps
= (2, 4, 6, 1, 3, 5, 7)
356 # bes es as des ges ces fes
357 flats
= (6, 4, 2, 7, 5, 3, 1)
360 if flats
[k
] <= self
.flats
:
363 if sharps
[k
] <= self
.sharps
:
367 name
= name
+ Note
.alteration_names
[a
+ 2]
375 return '\n\n ' + s
+ '\n '
383 'SEQUENCE_TRACK_NAME',
389 def __init__ (self
, type, text
):
395 # urg, we should be sure that we're in a lyrics staff
397 if self
.type == midi
.LYRIC
:
398 s
= '"%s"' % self
.text
399 d
= Duration (self
.clocks
)
400 if (global_options
.explicit_durations
401 or d
.compare (reference_note
.duration
)):
402 s
= s
+ Duration (self
.clocks
).dump ()
404 elif (self
.text
.strip ()
405 and self
.type == midi
.SEQUENCE_TRACK_NAME
406 and not self
.text
== 'control track'):
407 text
= self
.text
.replace ('(MIDI)', '').strip ()
409 s
= '\n \\set Staff.instrumentName = "%(text)s"\n ' % locals ()
410 elif self
.text
.strip ():
411 s
= '\n % [' + self
.text_types
[self
.type] + '] ' + self
.text
+ '\n '
415 return 'Text(%d=%s)' % (self
.type, self
.text
)
417 def get_voice (channel
, events
):
418 debug ('channel: ' + str (channel
) + '\n')
419 music
= parse_events (events
)
420 return unthread_notes (music
)
423 def __init__ (self
, number
):
426 def add (self
, event
):
427 self
.events
.append (event
)
429 def get_voice (self
):
430 return get_voice (self
.number
, self
.events
)
437 def _add (self
, event
):
438 if isinstance (event
, Text
) and event
.type == midi
.SEQUENCE_TRACK_NAME
:
439 self
.name
= event
.text
440 self
.events
.append (event
)
441 def add (self
, event
, channel
=None):
445 # self.channels[channel] = self.channels.get (channel, Channel (channel)).add (event)
446 self
.channels
[channel
] = self
.channels
.get (channel
, Channel (channel
))
447 self
.channels
[channel
].add (event
)
448 def get_voice (self
):
449 return get_voice (None, self
.events
)
450 def get_voices (self
):
451 return ([self
.get_voice ()]
452 + [self
.channels
[k
].get_voice ()
453 for k
in sorted (self
.channels
.keys ())])
455 def parse_track (events
):
459 if data
[0] > 0x7f and data
[0] < 0xf0:
460 channel
= data
[0] & 0x0f
461 e
= (e
[0], tuple ([data
[0] & 0xf0] + data
[1:]))
462 track
.add (e
, channel
)
467 def quantise_clocks (clocks
, quant
):
468 q
= int (clocks
/ quant
) * quant
470 for tquant
in allowed_tuplet_clocks
:
471 if int (clocks
/ tquant
) * tquant
== clocks
:
473 if 2 * (clocks
- q
) > quant
:
477 def end_note (pitches
, notes
, t
, e
):
479 (lt
, vel
) = pitches
[e
]
489 if duration_quant_clocks
:
490 d
= quantise_clocks (d
, duration_quant_clocks
)
492 d
= duration_quant_clocks
495 (lt
, Note (d
, e
, vel
)))
500 def parse_events (channel
):
510 if start_quant_clocks
:
511 t
= quantise_clocks (t
, start_quant_clocks
)
513 if (e
[1][0] == midi
.NOTE_OFF
514 or (e
[1][0] == midi
.NOTE_ON
and e
[1][2] == 0)):
515 debug ('%d: NOTE OFF: %s' % (t
, e
[1][1]))
517 debug (' ...treated as OFF')
518 end_note (pitches
, notes
, t
, e
[1][1])
520 elif e
[1][0] == midi
.NOTE_ON
:
521 if not pitches
.has_key (e
[1][1]):
522 debug ('%d: NOTE ON: %s' % (t
, e
[1][1]))
523 pitches
[e
[1][1]] = (t
, e
[1][2])
527 # all include ALL_NOTES_OFF
528 elif (e
[1][0] >= midi
.ALL_SOUND_OFF
529 and e
[1][0] <= midi
.POLY_MODE_ON
):
531 end_note (pitches
, notes
, t
, i
)
533 elif e
[1][0] == midi
.META_EVENT
:
534 if e
[1][1] == midi
.END_OF_TRACK
:
536 end_note (pitches
, notes
, t
, i
)
539 elif e
[1][1] == midi
.SET_TEMPO
:
540 (u0
, u1
, u2
) = map (ord, e
[1][2])
541 us_per_4
= u2
+ 256 * (u1
+ 256 * u0
)
542 seconds_per_1
= us_per_4
* 4 / 1e6
543 events
.append ((t
, Tempo (seconds_per_1
)))
544 elif e
[1][1] == midi
.TIME_SIGNATURE
:
545 (num
, dur
, clocks4
, count32
) = map (ord, e
[1][2])
547 events
.append ((t
, Time (num
, den
)))
548 elif e
[1][1] == midi
.KEY_SIGNATURE
:
549 (alterations
, minor
) = map (ord, e
[1][2])
552 if alterations
< 127:
555 flats
= 256 - alterations
557 k
= Key (sharps
, flats
, minor
)
558 if not t
and global_options
.key
:
559 # At t == 0, a set --key overrides us
560 k
= global_options
.key
561 events
.append ((t
, k
))
563 # ugh, must set key while parsing
564 # because Note init uses key
565 # Better do Note.calc () at dump time?
566 global_options
.key
= k
568 elif (e
[1][1] == midi
.LYRIC
569 or (global_options
.text_lyrics
and e
[1][1] == midi
.TEXT_EVENT
)):
571 last_lyric
.clocks
= t
- last_time
572 events
.append ((last_time
, last_lyric
))
574 last_lyric
= Text (midi
.LYRIC
, e
[1][2])
576 elif (e
[1][1] >= midi
.SEQUENCE_NUMBER
577 and e
[1][1] <= midi
.CUE_POINT
):
578 events
.append ((t
, Text (e
[1][1], e
[1][2])))
580 if global_options
.verbose
:
581 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
584 if global_options
.verbose
:
585 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
589 # last_lyric.clocks = t - last_time
591 last_lyric
.clocks
= clocks_per_4
592 events
.append ((last_time
, last_lyric
))
597 if i
< len (events
) and notes
[0][0] >= events
[i
][0]:
600 events
.insert (i
, notes
[0])
604 def unthread_notes (channel
):
613 if (e
[1].__class
__ == Note
614 and ((t
== start_busy_t
615 and e
[1].clocks
+ t
== end_busy_t
)
616 or t
>= end_busy_t
)):
619 end_busy_t
= t
+ e
[1].clocks
620 elif (e
[1].__class
__ == Time
621 or e
[1].__class
__ == Key
622 or e
[1].__class
__ == Text
623 or e
[1].__class
__ == Tempo
):
627 threads
.append (thread
)
642 def dump_skip (skip
, clocks
):
643 return skip
+ Duration (clocks
).dump () + ' '
652 if i
.__class
__ == Note
:
657 s
= s
+ dump (notes
[0])
658 elif len (notes
) > 1:
659 global reference_note
661 s
= s
+ notes
[0].dump (dump_dur
=False)
664 s
= s
+ i
.dump (dump_dur
=False)
667 s
= s
+ notes
[0].duration
.dump () + ' '
671 def dump_bar_line (last_bar_t
, t
, bar_count
):
673 bar_t
= time
.bar_clocks ()
674 if t
- last_bar_t
>= bar_t
:
675 bar_count
= bar_count
+ (t
- last_bar_t
) / bar_t
677 if t
- last_bar_t
== bar_t
:
678 s
= '\n | %% %(bar_count)d\n ' % locals ()
681 # urg, this will barf at meter changes
682 last_bar_t
= last_bar_t
+ (t
- last_bar_t
) / bar_t
* bar_t
684 return (s
, last_bar_t
, bar_count
)
687 def dump_voice (thread
, skip
):
688 global reference_note
, time
689 ref
= Note (0, 4*12, 0)
690 if not reference_note
:
693 ref
.duration
= reference_note
.duration
700 if last_e
and last_e
[0] == e
[0]:
704 chs
.append ((last_e
[0], ch
))
711 chs
.append ((last_e
[0], ch
))
721 i
= lines
[-1].rfind ('\n') + 1
722 if len (lines
[-1][i
:]) > LINE_BELL
:
727 if bar_max
and t
> time
.bar_clocks () * bar_max
:
728 d
= time
.bar_clocks () * bar_max
- last_t
729 lines
[-1] = lines
[-1] + dump_skip (skip
, d
)
731 errorport
.write ('BUG: time skew')
733 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
736 if bar_max
and bar_count
> bar_max
:
739 lines
[-1] = lines
[-1] + s
740 lines
[-1] = lines
[-1] + dump_chord (ch
[1])
744 if i
.clocks
> clocks
:
749 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
751 lines
[-1] = lines
[-1] + s
753 return '\n '.join (lines
) + '\n'
755 def number2ascii (i
):
760 s
= '%c' % (m
+ ord ('A')) + s
764 def get_track_name (i
):
765 return 'track' + number2ascii (i
)
767 def get_channel_name (i
):
768 return 'channel' + number2ascii (i
)
770 def get_voice_name (i
, zero_too_p
=False):
772 return 'voice' + number2ascii (i
)
775 def lst_append (lst
, x
):
779 def get_voice_layout (average_pitch
):
781 for i
in range (len (average_pitch
)):
782 d
[average_pitch
[i
]] = lst_append (d
.get (average_pitch
[i
], []), i
)
783 s
= list (reversed (sorted (average_pitch
)))
784 non_empty
= len (filter (lambda x
: x
, s
))
785 names
= ['One', 'Two']
787 names
= ['One', 'Three', 'Four', 'Two']
788 layout
= map (lambda x
: '', range (len (average_pitch
)))
789 for i
, n
in zip (s
, names
):
798 def dump_track (track
, n
):
800 track_name
= get_track_name (n
)
802 average_pitch
= track_average_pitch (track
)
803 voices
= len (filter (lambda x
: x
, average_pitch
[1:]))
804 clef
= get_best_clef (average_pitch
[0])
808 for channel
in track
:
810 channel_name
= get_channel_name (c
)
812 for voice
in channel
:
813 voice_name
= get_voice_name (v
)
814 voice_id
= track_name
+ channel_name
+ voice_name
815 item
= voice_first_item (voice
)
817 if item
and item
.__class
__ == Note
:
819 if global_options
.skip
:
821 s
+= '%(voice_id)s = ' % locals ()
822 if not global_options
.absolute_pitches
:
824 elif item
and item
.__class
__ == Text
:
826 s
+= '%(voice_id)s = \\lyricmode ' % locals ()
829 s
+= '%(voice_id)s = ' % locals ()
831 if not n
and not vv
and global_options
.key
:
832 s
+= global_options
.key
.dump ()
833 if average_pitch
[vv
+1] and voices
> 1:
834 s
+= ' \\voice' + get_voice_layout (average_pitch
[1:])[vv
] + '\n'
835 s
+= ' ' + dump_voice (voice
, skip
)
840 s
+= '%(track_name)s = <<\n' % locals ()
843 s
+= clef
.dump () + '\n'
847 for channel
in track
:
849 channel_name
= get_channel_name (c
)
851 for voice
in channel
:
852 voice_context_name
= get_voice_name (vv
, zero_too_p
=True)
853 voice_name
= get_voice_name (v
)
856 voice_id
= track_name
+ channel_name
+ voice_name
857 item
= voice_first_item (voice
)
859 if item
and item
.__class
__ == Text
:
861 s
+= ' \\context %(context)s = %(voice_context_name)s \\%(voice_id)s\n' % locals ()
865 def voice_first_item (voice
):
867 if (event
[1].__class
__ == Note
868 or (event
[1].__class
__ == Text
869 and event
[1].type == midi
.LYRIC
)):
873 def channel_first_item (channel
):
874 for voice
in channel
:
875 first
= voice_first_item (voice
)
880 def track_first_item (track
):
881 for channel
in track
:
882 first
= channel_first_item (channel
)
887 def track_average_pitch (track
):
891 for channel
in track
:
892 for voice
in channel
:
896 if event
[1].__class
__ == Note
:
899 p
[v
] += event
[1].pitch
908 def get_best_clef (average_pitch
):
910 if average_pitch
<= 3*12:
912 elif average_pitch
<= 5*12:
914 elif average_pitch
>= 7*12:
919 def __init__ (self
, track
):
920 self
.voices
= track
.get_voices ()
922 return dump_track (self
.voices
, i
)
924 def convert_midi (in_file
, out_file
):
925 global clocks_per_1
, clocks_per_4
, key
926 global start_quant_clocks
927 global duration_quant_clocks
928 global allowed_tuplet_clocks
931 str = open (in_file
, 'rb').read ()
932 clocks_max
= bar_max
* clocks_per_1
* 2
933 midi_dump
= midi
.parse (str, clocks_max
)
935 clocks_per_1
= midi_dump
[0][1]
936 clocks_per_4
= clocks_per_1
/ 4
939 if global_options
.start_quant
:
940 start_quant_clocks
= clocks_per_1
/ global_options
.start_quant
942 if global_options
.duration_quant
:
943 duration_quant_clocks
= clocks_per_1
/ global_options
.duration_quant
945 allowed_tuplet_clocks
= []
946 for (dur
, num
, den
) in global_options
.allowed_tuplets
:
947 allowed_tuplet_clocks
.append (clocks_per_1
/ dur
* num
/ den
)
949 if global_options
.verbose
:
950 print 'allowed tuplet clocks:', allowed_tuplet_clocks
952 staves
= [Staff (parse_track (t
)) for t
in midi_dump
[1]]
954 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, in_file
)
966 \remove "Note_heads_engraver"
967 \consists "Completion_heads_engraver"
968 \remove "Rest_engraver"
969 \consists "Completion_rest_engraver"
974 for i
in global_options
.include_header
:
975 s
+= '\n%% included from %(i)s\n' % locals ()
976 s
+= open (i
).read ()
981 for i
, t
in enumerate (staves
):
984 s
+= '\n\\score {\n <<\n'
987 for i
, staff
in enumerate (staves
):
988 track_name
= get_track_name (i
)
989 item
= track_first_item (staff
.voices
)
990 staff_name
= track_name
992 if not i
and not item
and len (staves
) > 1:
994 staff_name
= get_track_name (1)
996 elif (item
and item
.__class
__ == Note
):
998 elif item
and item
.__class
__ == Text
:
1001 s
+= ' \\context %(context)s=%(staff_name)s \\%(track_name)s\n' % locals ()
1009 progress (_ ("%s output to `%s'...") % ('LY', out_file
))
1014 handle
= open (out_file
, 'w')
1020 def get_option_parser ():
1021 p
= ly
.get_option_parser (usage
=_ ("%s [OPTION]... FILE") % 'midi2ly',
1022 description
=_ ("Convert %s to LilyPond input.\n") % 'MIDI',
1023 add_help_option
=False)
1025 p
.add_option ('-a', '--absolute-pitches',
1026 action
='store_true',
1027 help=_ ('print absolute pitches'))
1028 p
.add_option ('-d', '--duration-quant',
1030 help=_ ('quantise note durations on DUR'))
1031 p
.add_option ('-D', '--debug',
1032 action
='store_true',
1033 help=_ ('debug printing'))
1034 p
.add_option ('-e', '--explicit-durations',
1035 action
='store_true',
1036 help=_ ('print explicit durations'))
1037 p
.add_option('-h', '--help',
1039 help=_ ('show this help and exit'))
1040 p
.add_option('-i', '--include-header',
1041 help=_ ('prepend FILE to output'),
1045 p
.add_option('-k', '--key', help=_ ('set key: ALT=+sharps|-flats; MINOR=1'),
1046 metavar
=_ ('ALT[:MINOR]'),
1048 p
.add_option ('-o', '--output', help=_ ('write output to FILE'),
1051 p
.add_option ('-p', '--preview', help=_ ('preview of first 4 bars'),
1052 action
='store_true')
1053 p
.add_option ('-s', '--start-quant',help= _ ('quantise note starts on DUR'),
1055 p
.add_option ('-S', '--skip',
1056 action
= "store_true",
1057 help =_ ("use s instead of r for rests"))
1058 p
.add_option ('-t', '--allow-tuplet',
1059 metavar
=_ ('DUR*NUM/DEN'),
1061 dest
='allowed_tuplets',
1062 help=_ ('allow tuplet durations DUR*NUM/DEN'),
1064 p
.add_option ('-V', '--verbose', help=_ ('be verbose'),
1067 p
.version
= 'midi2ly (LilyPond) @TOPLEVEL_VERSION@'
1068 p
.add_option ('--version',
1070 help=_ ('show version number and exit'))
1071 p
.add_option ('-w', '--warranty', help=_ ('show warranty and copyright'),
1072 action
='store_true',
1074 p
.add_option ('-x', '--text-lyrics', help=_ ('treat every text as a lyric'),
1075 action
='store_true')
1077 p
.add_option_group (ly
.display_encode (_ ('Examples')),
1079 $ midi2ly --key=-2:1 --duration-quant=32 --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
1081 p
.add_option_group ('',
1083 _ ('Report bugs via %s')
1084 % 'http://post.gmane.org/post.php'
1085 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
1091 opt_parser
= get_option_parser ()
1092 (options
, args
) = opt_parser
.parse_args ()
1094 if options
.warranty
:
1098 if not args
or args
[0] == '-':
1099 opt_parser
.print_help ()
1100 ly
.stderr_write ('\n%s: %s %s\n' % (program_name
, _ ('error: '),
1101 _ ('no files specified on command line.')))
1104 if options
.duration_quant
:
1105 options
.duration_quant
= int (options
.duration_quant
)
1108 (alterations
, minor
) = map (int, (options
.key
+ ':0').split (':'))[0:2]
1111 if alterations
>= 0:
1112 sharps
= alterations
1114 flats
= - alterations
1115 options
.key
= Key (sharps
, flats
, minor
)
1117 if options
.start_quant
:
1118 options
.start_quant
= int (options
.start_quant
)
1124 options
.allowed_tuplets
= [map (int, a
.replace ('/','*').split ('*'))
1125 for a
in options
.allowed_tuplets
]
1128 sys
.stderr
.write ('Allowed tuplets: %s\n' % `options
.allowed_tuplets`
)
1130 global global_options
1131 global_options
= options
1136 files
= do_options ()
1138 exts
= ['.midi', '.mid', '.MID']
1142 g
= strip_extension (g
, e
)
1143 if not os
.path
.exists (f
):
1146 if os
.path
.exists (n
):
1150 if not global_options
.output
:
1152 outbase
= os
.path
.basename (g
)
1153 o
= outbase
+ '-midi.ly'
1154 elif (global_options
.output
[-1] == os
.sep
1155 or os
.path
.isdir (global_options
.output
)):
1156 outdir
= global_options
.output
1157 outbase
= os
.path
.basename (g
)
1158 o
= os
.path
.join (outdir
, outbase
+ '-midi.ly')
1160 o
= global_options
.output
1161 (outdir
, outbase
) = os
.path
.split (o
)
1163 if outdir
and outdir
!= '.' and not os
.path
.exists (outdir
):
1164 os
.mkdir (outdir
, 0777)
1168 if __name__
== '__main__':