3 # midi2ly.py -- LilyPond midi import script
5 # source file of the GNU LilyPond music typesetter
7 # (c) 1998--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
8 # Jan Nieuwenhuizen <janneke@gnu.org>
13 * test on weird and unquantised midi input (lily-devel)
15 * update doc and manpage
17 * simply insert clef changes whenever too many ledger lines
18 [to avoid tex capacity exceeded]
19 * do not ever quant skips
20 * better lyrics handling
21 * [see if it is feasible to] move ly-classes to library for use in
22 other converters, while leaving midi specific stuff here
31 ################################################################
32 # Users of python modules should include this snippet.
34 # This soon to be removed for: import lilypond.lilylib as ly
35 libdir
= '@local_lilypond_libdir@'
36 if not os
.path
.isdir (libdir
):
37 libdir
= '@lilypond_libdir@'
38 sys
.path
.insert (0, os
.path
.join (libdir
, 'python'))
41 ################################################################
46 ################################################################
47 ################ CONSTANTS
52 scale_steps
= [0,2,4,5,7,9,11]
60 start_quant_clocks
= 0
62 duration_quant_clocks
= 0
64 allowed_tuplet_clocks
= []
66 explicit_durations_p
= 0
71 ################################################################
73 localedir
= '@localedir@'
76 gettext
.bindtextdomain ('lilypond', localedir
)
77 gettext
.textdomain ('lilypond')
83 program_name
= 'midi2ly'
84 program_version
= '@TOPLEVEL_VERSION@'
86 errorport
= sys
.stderr
89 # temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
90 # original_dir = os.getcwd ()
94 help_summary
= _ ("Convert MIDI to LilyPond source")
96 option_definitions
= [
97 ('', 'a', 'absolute-pitches', _ ("print absolute pitches")),
98 (_ ("DUR"), 'd', 'duration-quant', _ ("quantise note durations on DUR")),
99 ('', 'e', 'explicit-durations', _ ("print explicit durations")),
100 ('', 'h', 'help', _ ("this help")),
101 (_ ("ALT[:MINOR]"), 'k', 'key', _ ("set key: ALT=+sharps|-flats; MINOR=1")),
102 (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
103 (_ ("DUR"), 's', 'start-quant', _ ("quantise note starts on DUR")),
104 (_ ("DUR*NUM/DEN"), 't', 'allow-tuplet', _ ("allow tuplet durations DUR*NUM/DEN")),
105 ('', 'V', 'verbose', _ ("verbose")),
106 ('', 'v', 'version', _ ("print version number")),
107 ('', 'w', 'warranty', _ ("show warranty and copyright")),
108 ('', 'x', 'text-lyrics', _ ("treat every text as a lyric")),
111 ################################################################
112 # lilylib.py -- options and stuff
114 # source file of the GNU LilyPond music typesetter
120 gettext
.bindtextdomain ('lilypond', localedir
)
121 gettext
.textdomain ('lilypond')
127 if program_version
== '@' + 'TOPLEVEL_VERSION' + '@':
128 program_version
= '1.5.17'
131 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
135 sys
.stdout
.write ('\n')
136 sys
.stdout
.write (_ ('Copyright (c) %s by' % ' 2001--2003'))
137 sys
.stdout
.write ('\n')
138 sys
.stdout
.write (' Han-Wen Nienhuys')
139 sys
.stdout
.write (' Jan Nieuwenhuizen')
140 sys
.stdout
.write ('\n')
141 sys
.stdout
.write (_ (r
'''
142 Distributed under terms of the GNU General Public License. It comes with
144 sys
.stdout
.write ('\n')
147 errorport
.write (s
+ '\n')
150 progress (_ ("warning: ") + s
)
155 '''Report the error S. Exit by raising an exception. Please
156 do not abuse by trying to catch this error. If you do not want
157 a stack trace, write to the output directly.
165 progress (_ ("error: ") + s
)
166 raise _ ("Exiting ... ")
168 def getopt_args (opts
):
169 '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
184 def option_help_str (o
):
185 '''Transform one option description (4-tuple ) into neatly formatted string'''
203 return ' ' + sh
+ sep
+ long + arg
206 def options_help_str (opts
):
207 '''Convert a list of options into a neatly formatted string'''
213 s
= option_help_str (o
)
214 strs
.append ((s
, o
[3]))
220 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
224 ls
= [(_ ("Usage: %s [OPTION]... FILE") % program_name
),
230 (options_help_str (option_definitions
)),
232 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
234 map (sys
.stdout
.write
, ls
)
238 Create a temporary directory, and return its name.
241 if not keep_temp_dir_p
:
242 temp_dir
= tempfile
.mktemp (program_name
)
244 os
.mkdir (temp_dir
, 0777)
251 def system (cmd
, ignore_error
= 0):
252 """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
260 progress (_ ("Invoking `%s\'") % cmd
)
263 name
= re
.match ('[ \t]*([^ \t]*)', cmd
).group (1)
264 msg
= name
+ ': ' + _ ("command exited with value %d") % st
266 warning (msg
+ ' ' + _ ("(ignored)") + ' ')
274 if not keep_temp_dir_p
:
276 progress (_ ("Cleaning %s...") % temp_dir
)
277 shutil
.rmtree (temp_dir
)
280 def strip_extension (f
, ext
):
281 (p
, e
) = os
.path
.splitext (f
)
286 ################################################################
288 ################################################################
294 allowed_durs
= (1, 2, 4, 8, 16, 32, 64, 128)
295 def __init__ (self
, clocks
):
298 self
.clocks
= duration_quant_clocks
299 (self
.dur
, self
.num
, self
.den
) = self
.dur_num_den (clocks
)
301 def dur_num_den (self
, clocks
):
302 for i
in range (len (allowed_tuplet_clocks
)):
303 if clocks
== allowed_tuplet_clocks
[i
]:
304 return allowed_tuplets
[i
]
306 dur
= 0; num
= 1; den
= 1;
307 g
= gcd (clocks
, clocks_per_1
)
309 (dur
, num
) = (clocks_per_1
/ g
, clocks
/ g
)
310 if not dur
in self
.allowed_durs
:
311 dur
= 4; num
= clocks
; den
= clocks_per_4
312 return (dur
, num
, den
)
318 elif self
.num
== 3 and self
.dur
!= 1:
319 s
= '%d.' % (self
.dur
/ 2)
321 s
= '%d*%d' % (self
.dur
, self
.num
)
323 s
= '%d*%d/%d' % (self
.dur
, self
.num
, self
.den
)
325 global reference_note
326 reference_note
.duration
= self
330 def compare (self
, other
):
331 return self
.clocks
- other
.clocks
340 names
= (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
341 alterations
= (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
342 alteration_names
= ('eses', 'es', '', 'is' , 'isis')
344 def __init__ (self
, clocks
, pitch
, velocity
):
346 self
.velocity
= velocity
349 self
.duration
= Duration (clocks
)
350 (self
.octave
, self
.notename
, self
.alteration
) = self
.o_n_a ()
354 # minor scale: la-la (= + 5) '''
356 n
= self
.names
[(self
.pitch
) % 12]
357 a
= self
.alterations
[(self
.pitch
) % 12]
360 a
= - self
.alterations
[(self
.pitch
) % 12]
363 # By tradition, all scales now consist of a sequence
364 # of 7 notes each with a distinct name, from amongst
365 # a b c d e f g. But, minor scales have a wide
366 # second interval at the top - the 'leading note' is
367 # sharped. (Why? it just works that way! Anything
368 # else doesn't sound as good and isn't as flexible at
369 # saying things. In medieval times, scales only had 6
370 # notes to avoid this problem - the hexachords.)
372 # So, the d minor scale is d e f g a b-flat c-sharp d
373 # - using d-flat for the leading note would skip the
374 # name c and duplicate the name d. Why isn't c-sharp
375 # put in the key signature? Tradition. (It's also
376 # supposedly based on the Pythagorean theory of the
377 # cycle of fifths, but that really only applies to
378 # major scales...) Anyway, g minor is g a b-flat c d
379 # e-flat f-sharp g, and all the other flat minor keys
380 # end up with a natural leading note. And there you
383 # John Sankey <bf250@freenet.carleton.ca>
385 # Let's also do a-minor: a b c d e f gis a
389 o
= self
.pitch
/ 12 - 4
393 if key
.sharps
== 0 and key
.flats
== 0 \
394 and n
== 5 and a
== -1:
397 elif key
.flats
== 1 and n
== 1 and a
== -1:
400 elif key
.flats
== 2 and n
== 4 and a
== -1:
403 elif key
.sharps
== 5 and n
== 4 and a
== 0:
406 elif key
.sharps
== 6 and n
== 1 and a
== 0:
409 elif key
.sharps
== 7 and n
== 5 and a
== 0:
413 if key
.flats
>= 6 and n
== 6 and a
== 0:
414 n
= 0; a
= -1; o
= o
+ 1
416 if key
.flats
>= 7 and n
== 2 and a
== 0:
420 if key
.sharps
>= 3 and n
== 3 and a
== 0:
423 if key
.sharps
>= 4 and n
== 0 and a
== 0:
424 n
= 6; a
= 1; o
= o
- 1
428 def dump (self
, dump_dur
= 1):
429 global reference_note
430 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
431 s
= s
+ self
.alteration_names
[self
.alteration
+ 2]
435 delta
= self
.pitch
- reference_note
.pitch
436 commas
= sign (delta
) * (abs (delta
) / 12)
438 * (self
.notename
- reference_note
.notename
) + 7) \
440 or ((self
.notename
== reference_note
.notename
) \
441 and (abs (delta
) > 4) and (abs (delta
) < 12)):
442 commas
= commas
+ sign (delta
)
447 s
= s
+ "," * -commas
449 ## FIXME: compile fix --jcn
450 if dump_dur
and (explicit_durations_p \
451 or Duration
.compare (self
.duration
,
452 reference_note
.duration
)):
453 s
= s
+ self
.duration
.dump ()
455 reference_note
= self
462 def __init__ (self
, num
, den
):
467 def bar_clocks (self
):
468 return clocks_per_1
* self
.num
/ self
.den
473 return '\n ' + '\\time %d/%d ' % (self
.num
, self
.den
) + '\n '
476 def __init__ (self
, seconds_per_1
):
478 self
.seconds_per_1
= seconds_per_1
481 return '\n ' + '\\tempo 4 = %d ' % (4 * 60 / self
.seconds_per_1
) + '\n '
484 clefs
= ('"bass_8"', 'bass', 'violin', '"violin^8"')
485 def __init__ (self
, type):
489 return '\n \\clef %s\n ' % self
.clefs
[self
.type]
492 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
493 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
495 def __init__ (self
, sharps
, flats
, minor
):
506 if self
.sharps
and self
.flats
:
507 s
= '\\keysignature %s ' % 'TODO'
511 k
= (ord ('cfbeadg'[self
.flats
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
513 k
= (ord ('cgdaebf'[self
.sharps
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
516 name
= chr ((k
+ 2) % 7 + ord ('a'))
518 name
= chr ((k
+ 2) % 7 + ord ('a'))
520 # fis cis gis dis ais eis bis
521 sharps
= (2, 4, 6, 1, 3, 5, 7)
522 # bes es as des ges ces fes
523 flats
= (6, 4, 2, 7, 5, 3, 1)
526 if flats
[k
] <= self
.flats
:
529 if sharps
[k
] <= self
.sharps
:
533 name
= name
+ Note
.alteration_names
[a
+ 2]
541 return '\n\n ' + s
+ '\n '
549 'SEQUENCE_TRACK_NAME',
555 def __init__ (self
, type, text
):
561 # urg, we should be sure that we're in a lyrics staff
562 if self
.type == midi
.LYRIC
:
563 s
= '"%s"' % self
.text
564 d
= Duration (self
.clocks
)
565 if explicit_durations_p \
566 or Duration
.compare (d
,
567 reference_note
.duration
):
568 s
= s
+ Duration (self
.clocks
).dump ()
571 s
= '\n % [' + self
.text_types
[self
.type] + '] ' + self
.text
+ '\n '
575 def split_track (track
):
582 if data
[0] > 0x7f and data
[0] < 0xf0:
584 e
= (e
[0], tuple ([data
[0] & 0xf0] + data
[1:]))
594 for v
in chs
.values ():
595 events
= events_on_channel (v
)
596 thread
= unthread_notes (events
)
598 threads
.append (thread
)
602 def quantise_clocks (clocks
, quant
):
603 q
= int (clocks
/ quant
) * quant
605 for tquant
in allowed_tuplet_clocks
:
606 if int (clocks
/ tquant
) * tquant
== clocks
:
608 if 2 * (clocks
- q
) > quant
:
612 def end_note (pitches
, notes
, t
, e
):
614 (lt
, vel
) = pitches
[e
]
624 if duration_quant_clocks
:
625 d
= quantise_clocks (d
, duration_quant_clocks
)
627 d
= duration_quant_clocks
630 (lt
, Note (d
, e
, vel
)))
635 def events_on_channel (channel
):
645 if start_quant_clocks
:
646 t
= quantise_clocks (t
, start_quant_clocks
)
649 if e
[1][0] == midi
.NOTE_OFF \
650 or (e
[1][0] == midi
.NOTE_ON
and e
[1][2] == 0):
651 end_note (pitches
, notes
, t
, e
[1][1])
653 elif e
[1][0] == midi
.NOTE_ON
:
654 if not pitches
.has_key (e
[1][1]):
655 pitches
[e
[1][1]] = (t
, e
[1][2])
657 # all include ALL_NOTES_OFF
658 elif e
[1][0] >= midi
.ALL_SOUND_OFF \
659 and e
[1][0] <= midi
.POLY_MODE_ON
:
660 for i
in pitches
.keys ():
661 end_note (pitches
, notes
, t
, i
)
663 elif e
[1][0] == midi
.META_EVENT
:
664 if e
[1][1] == midi
.END_OF_TRACK
:
665 for i
in pitches
.keys ():
666 end_note (pitches
, notes
, t
, i
)
669 elif e
[1][1] == midi
.SET_TEMPO
:
670 (u0
, u1
, u2
) = map (ord, e
[1][2])
671 us_per_4
= u2
+ 256 * (u1
+ 256 * u0
)
672 seconds_per_1
= us_per_4
* 4 / 1e6
673 events
.append ((t
, Tempo (seconds_per_1
)))
674 elif e
[1][1] == midi
.TIME_SIGNATURE
:
675 (num
, dur
, clocks4
, count32
) = map (ord, e
[1][2])
677 events
.append ((t
, Time (num
, den
)))
678 elif e
[1][1] == midi
.KEY_SIGNATURE
:
679 (alterations
, minor
) = map (ord, e
[1][2])
682 if alterations
< 127:
685 flats
= 256 - alterations
687 k
= Key (sharps
, flats
, minor
)
688 events
.append ((t
, k
))
690 # ugh, must set key while parsing
691 # because Note init uses key
692 # Better do Note.calc () at dump time?
696 elif e
[1][1] == midi
.LYRIC \
697 or (text_lyrics_p
and e
[1][1] == midi
.TEXT_EVENT
):
699 last_lyric
.clocks
= t
- last_time
700 events
.append ((last_time
, last_lyric
))
702 last_lyric
= Text (midi
.LYRIC
, e
[1][2])
704 elif e
[1][1] >= midi
.SEQUENCE_NUMBER \
705 and e
[1][1] <= midi
.CUE_POINT
:
706 events
.append ((t
, Text (e
[1][1], e
[1][2])))
709 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
713 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
717 # last_lyric.clocks = t - last_time
719 last_lyric
.clocks
= clocks_per_4
720 events
.append ((last_time
, last_lyric
))
725 if i
< len (events
) and notes
[0][0] >= events
[i
][0]:
728 events
.insert (i
, notes
[0])
732 def unthread_notes (channel
):
741 if e
[1].__class
__ == Note \
742 and ((t
== start_busy_t \
743 and e
[1].clocks
+ t
== end_busy_t
) \
747 end_busy_t
= t
+ e
[1].clocks
748 elif e
[1].__class
__ == Time \
749 or e
[1].__class
__ == Key \
750 or e
[1].__class
__ == Text \
751 or e
[1].__class
__ == Tempo
:
755 threads
.append (thread
)
770 def dump_skip (skip
, clocks
):
771 return skip
+ Duration (clocks
).dump () + ' '
780 if i
.__class
__ == Note
:
785 s
= s
+ dump (notes
[0])
786 elif len (notes
) > 1:
787 global reference_note
789 s
= s
+ notes
[0].dump (dump_dur
= 0)
792 s
= s
+ i
.dump (dump_dur
= 0 )
795 s
= s
+ notes
[0].duration
.dump()
799 def dump_bar_line (last_bar_t
, t
, bar_count
):
801 bar_t
= time
.bar_clocks ()
802 if t
- last_bar_t
>= bar_t
:
803 bar_count
= bar_count
+ (t
- last_bar_t
) / bar_t
805 if t
- last_bar_t
== bar_t
:
806 s
= '|\n %% %d\n ' % bar_count
809 # urg, this will barf at meter changes
810 last_bar_t
= last_bar_t
+ (t
- last_bar_t
) / bar_t
* bar_t
812 return (s
, last_bar_t
, bar_count
)
815 def dump_channel (thread
, skip
):
816 global key
, reference_note
, time
820 # urg LilyPond doesn't start at c4, but
821 # remembers from previous tracks!
822 # reference_note = Note (clocks_per_4, 4*12, 0)
823 reference_note
= Note (0, 4*12, 0)
829 if last_e
and last_e
[0] == e
[0]:
833 chs
.append ((last_e
[0], ch
))
840 chs
.append ((last_e
[0], ch
))
850 i
= string
.rfind (lines
[-1], '\n') + 1
851 if len (lines
[-1][i
:]) > LINE_BELL
:
855 lines
[-1] = lines
[-1] + dump_skip (skip
, t
-last_t
)
857 errorport
.write ('BUG: time skew')
859 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
861 lines
[-1] = lines
[-1] + s
863 lines
[-1] = lines
[-1] + dump_chord (ch
[1])
867 if i
.clocks
> clocks
:
872 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
874 lines
[-1] = lines
[-1] + s
876 return string
.join (lines
, '\n ') + '\n'
879 return 'track%c' % (i
+ ord ('A'))
881 def channel_name (i
):
882 return 'channel%c' % (i
+ ord ('A'))
884 def dump_track (channels
, n
):
886 track
= track_name (n
)
887 clef
= guess_clef (channels
)
889 for i
in range (len (channels
)):
890 channel
= channel_name (i
)
891 item
= thread_first_item (channels
[i
])
893 if item
and item
.__class
__ == Note
:
895 s
= s
+ '%s = \\notes' % (track
+ channel
)
897 s
= s
+ '\\relative c '
898 elif item
and item
.__class
__ == Text
:
900 s
= s
+ '%s = \\lyrics ' % (track
+ channel
)
903 # must be in \notes mode for parsing \skip
904 s
= s
+ '%s = \\notes ' % (track
+ channel
)
906 s
= s
+ ' ' + dump_channel (channels
[i
][0], skip
)
909 s
= s
+ '%s = <\n' % track
912 s
= s
+ clef
.dump () + '\n'
914 for i
in range (len (channels
)):
915 channel
= channel_name (i
)
916 item
= thread_first_item (channels
[i
])
917 if item
and item
.__class
__ == Text
:
918 s
= s
+ ' \\context Lyrics = %s \\%s\n' % (channel
,
921 s
= s
+ ' \\context Voice = %s \\%s\n' % (channel
,
926 def thread_first_item (thread
):
929 if event
[1].__class
__ == Note \
930 or (event
[1].__class
__ == Text \
931 and event
[1].type == midi
.LYRIC
):
935 def track_first_item (track
):
937 return thread_first_item (thread
)
939 def guess_clef (track
):
945 if event
[1].__class
__ == Note
:
947 p
= p
+ event
[1].pitch
948 if i
and p
/ i
<= 3*12:
950 elif i
and p
/ i
<= 5*12:
952 elif i
and p
/ i
>= 7*12:
958 def convert_midi (f
, o
):
959 global clocks_per_1
, clocks_per_4
, key
961 str = open (f
).read ()
962 midi_dump
= midi
.parse (str)
964 clocks_per_1
= midi_dump
[0][1]
965 clocks_per_4
= clocks_per_1
/ 4
967 global start_quant
, start_quant_clocks
969 start_quant_clocks
= clocks_per_1
/ start_quant
971 global duration_quant
, duration_quant_clocks
973 duration_quant_clocks
= clocks_per_1
/ duration_quant
975 global allowed_tuplet_clocks
976 allowed_tuplet_clocks
= []
977 for (dur
, num
, den
) in allowed_tuplets
:
978 allowed_tuplet_clocks
.append (clocks_per_1
* num
/ (dur
* den
))
981 for t
in midi_dump
[1]:
983 tracks
.append (split_track (t
))
985 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, f
)
989 for i
in range (len (tracks
)):
990 s
= s
+ dump_track (tracks
[i
], i
)
992 s
= s
+ '\n\\score {\n <\n'
993 for i
in range (len (tracks
)):
994 track
= track_name (i
)
995 item
= track_first_item (tracks
[i
])
996 if item
and item
.__class
__ == Note
:
997 s
= s
+ ' \\context Staff=%s \\%s\n' % (track
, track
)
998 elif item
and item
.__class
__ == Text
:
999 s
= s
+ ' \\context Lyrics=%s \\%s\n' % (track
, track
)
1002 progress (_ ("%s output to `%s'...") % ('LY', o
))
1013 (sh
, long) = getopt_args (option_definitions
)
1015 (options
, files
) = getopt
.getopt(sys
.argv
[1:], sh
, long)
1016 except getopt
.error
, s
:
1017 errorport
.write ('\n')
1018 errorport
.write (_ ("error: ") + _ ("getopt says: `%s\'" % s
))
1019 errorport
.write ('\n')
1020 errorport
.write ('\n')
1030 elif o
== '--help' or o
== '-h':
1032 errorport
.write ('\n')
1033 errorport
.write (_ ("Example:"))
1034 errorport
.write (r
'''
1035 midi2ly --key=-2:1 --duration-quant=32 \
1036 --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
1039 elif o
== '--output' or o
== '-o':
1041 elif o
== '--verbose' or o
== '-V':
1043 elif o
== '--version' or o
== '-v':
1046 elif o
== '--warranty' or o
== '-w':
1047 status
= system ('lilypond -w', ignore_error
= 1)
1053 elif o
== '--absolute-pitches' or o
== '-a':
1055 elif o
== '--duration-quant' or o
== '-d':
1056 duration_quant
= string
.atoi (a
)
1057 elif o
== '--explicit-durations' or o
== '-e':
1058 explicit_durations_p
= 1
1059 elif o
== '--key' or o
== '-k':
1060 (alterations
, minor
) = map (string
.atoi
, string
.split (a
+ ':0', ':'))[0:2]
1063 if alterations
>= 0:
1064 sharps
= alterations
1066 flats
= - alterations
1067 key
= Key (sharps
, flats
, minor
)
1068 elif o
== '--start-quant' or o
== '-s':
1069 start_quant
= string
.atoi (a
)
1070 elif o
== '--allow-tuplet' or o
== '-t':
1071 a
= string
.replace (a
, '/', '*')
1072 tuplet
= map (string
.atoi
, string
.split (a
, '*'))
1073 allowed_tuplets
.append (tuplet
)
1074 # lots of midi files use plain text for lyric events
1075 elif o
== '--text-lyrics' or o
== '-x':
1079 if not files
or files
[0] == '-':
1081 # FIXME: read from stdin when files[0] = '-'
1083 errorport
.write (program_name
+ ":" + _ ("error: ") + _ ("no files specified on command line.") + '\n')
1090 g
= strip_extension (g
, '.midi')
1091 g
= strip_extension (g
, '.mid')
1092 g
= strip_extension (g
, '.MID')
1093 (outdir
, outbase
) = ('','')
1097 outbase
= os
.path
.basename (g
)
1098 o
= os
.path
.join (outdir
, outbase
+ '-midi.ly')
1099 elif output_name
[-1] == os
.sep
:
1100 outdir
= output_name
1101 outbase
= os
.path
.basename (g
)
1102 os
.path
.join (outdir
, outbase
+ '-gen.ly')
1105 (outdir
, outbase
) = os
.path
.split (o
)
1107 if outdir
!= '.' and outdir
!= '':
1109 os
.mkdir (outdir
, 0777)