3 # midi2ly.py -- LilyPond midi import script
5 # source file of the GNU LilyPond music typesetter
7 # convert MIDI to LilyPond source
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
32 # do fuddling: we must load the midi module from the right directory.
33 datadir
= '@local_lilypond_datadir@'
34 if os
.environ
.has_key ('LILYPONDPREFIX'):
35 datadir
= os
.environ
['LILYPONDPREFIX']
37 datadir
= '@local_lilypond_datadir@'
39 sys
.path
.append (os
.path
.join (datadir
, 'python'))
40 sys
.path
.append (os
.path
.join (datadir
, 'python/out'))
44 ################################################################
45 ################ CONSTANTS
50 scale_steps
= [0,2,4,5,7,9,11]
58 start_quant_clocks
= 0
60 duration_quant_clocks
= 0
62 allowed_tuplet_clocks
= []
64 explicit_durations_p
= 0
69 ################################################################
71 localedir
= '@localedir@'
74 gettext
.bindtextdomain ('lilypond', localedir
)
75 gettext
.textdomain ('lilypond')
81 program_name
= 'midi2ly'
82 program_version
= '@TOPLEVEL_VERSION@'
84 errorport
= sys
.stderr
87 # temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
88 # original_dir = os.getcwd ()
92 help_summary
= _ ("Convert MIDI to LilyPond source")
94 option_definitions
= [
95 ('', 'a', 'absolute-pitches', _ ("print absolute pitches")),
96 (_ ("DUR"), 'd', 'duration-quant', _ ("quantise note durations on DUR")),
97 ('', 'e', 'explicit-durations', _ ("print explicit durations")),
98 ('', 'h', 'help', _ ("this help")),
99 (_ ("ALT[:MINOR]"), 'k', 'key', _ ("set key: ALT=+sharps|-flats; MINOR=1")),
100 (_ ("FILE"), 'o', 'output', _ ("write ouput to FILE")),
101 (_ ("DUR"), 's', 'start-quant', _ ("quantise note starts on DUR")),
102 (_ ("DUR*NUM/DEN"), 't', 'allow-tuplet', _ ("allow tuplet durations DUR*NUM/DEN")),
103 ('', 'V', 'verbose', _ ("verbose")),
104 ('', 'v', 'version', _ ("print version number")),
105 ('', 'w', 'warranty', _ ("show warranty and copyright")),
106 ('', 'x', 'text-lyrics', _ ("treat every text as a lyric")),
109 ################################################################
110 # lilylib.py -- options and stuff
112 # source file of the GNU LilyPond music typesetter
118 gettext
.bindtextdomain ('lilypond', localedir
)
119 gettext
.textdomain ('lilypond')
125 if program_version
== '@' + 'TOPLEVEL_VERSION' + '@':
126 program_version
= '1.5.17'
129 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
133 sys
.stdout
.write ('\n')
134 sys
.stdout
.write (_ ('Copyright (c) %s by' % ' 2001--2002'))
135 sys
.stdout
.write ('\n')
136 sys
.stdout
.write (' Han-Wen Nienhuys')
137 sys
.stdout
.write (' Jan Nieuwenhuizen')
138 sys
.stdout
.write ('\n')
139 sys
.stdout
.write (_ (r
'''
140 Distributed under terms of the GNU General Public License. It comes with
142 sys
.stdout
.write ('\n')
145 errorport
.write (s
+ '\n')
148 progress (_ ("warning: ") + s
)
153 '''Report the error S. Exit by raising an exception. Please
154 do not abuse by trying to catch this error. If you do not want
155 a stack trace, write to the output directly.
163 progress (_ ("error: ") + s
)
164 raise _ ("Exiting ... ")
166 def getopt_args (opts
):
167 '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
182 def option_help_str (o
):
183 '''Transform one option description (4-tuple ) into neatly formatted string'''
201 return ' ' + sh
+ sep
+ long + arg
204 def options_help_str (opts
):
205 '''Convert a list of options into a neatly formatted string'''
211 s
= option_help_str (o
)
212 strs
.append ((s
, o
[3]))
218 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
222 ls
= [(_ ("Usage: %s [OPTION]... FILE") % program_name
),
228 (options_help_str (option_definitions
)),
230 (_ ("Report bugs to %s") % 'bug-lilypond@gnu.org'),
232 map (sys
.stdout
.write
, ls
)
236 Create a temporary directory, and return its name.
239 if not keep_temp_dir_p
:
240 temp_dir
= tempfile
.mktemp (program_name
)
242 os
.mkdir (temp_dir
, 0777)
249 def system (cmd
, ignore_error
= 0):
250 """Run CMD. If IGNORE_ERROR is set, don't complain when CMD returns non zero.
258 progress (_ ("Invoking `%s\'") % cmd
)
261 name
= re
.match ('[ \t]*([^ \t]*)', cmd
).group (1)
262 msg
= name
+ ': ' + _ ("command exited with value %d") % st
264 warning (msg
+ ' ' + _ ("(ignored)") + ' ')
272 if not keep_temp_dir_p
:
274 progress (_ ("Cleaning %s...") % temp_dir
)
275 shutil
.rmtree (temp_dir
)
278 def strip_extension (f
, ext
):
279 (p
, e
) = os
.path
.splitext (f
)
284 ################################################################
286 ################################################################
292 allowed_durs
= (1, 2, 4, 8, 16, 32, 64, 128)
293 def __init__ (self
, clocks
):
296 self
.clocks
= duration_quant_clocks
297 (self
.dur
, self
.num
, self
.den
) = self
.dur_num_den (clocks
)
299 def dur_num_den (self
, clocks
):
300 for i
in range (len (allowed_tuplet_clocks
)):
301 if clocks
== allowed_tuplet_clocks
[i
]:
302 return allowed_tuplets
[i
]
304 dur
= 0; num
= 1; den
= 1;
305 g
= gcd (clocks
, clocks_per_1
)
307 (dur
, num
) = (clocks_per_1
/ g
, clocks
/ g
)
308 if not dur
in self
.allowed_durs
:
309 dur
= 4; num
= clocks
; den
= clocks_per_4
310 return (dur
, num
, den
)
316 elif self
.num
== 3 and self
.dur
!= 1:
317 s
= '%d.' % (self
.dur
/ 2)
319 s
= '%d*%d' % (self
.dur
, self
.num
)
321 s
= '%d*%d/%d' % (self
.dur
, self
.num
, self
.den
)
323 global reference_note
324 reference_note
.duration
= self
328 def compare (self
, other
):
329 return self
.clocks
- other
.clocks
338 names
= (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
339 alterations
= (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
340 alteration_names
= ('eses', 'es', '', 'is' , 'isis')
342 def __init__ (self
, clocks
, pitch
, velocity
):
344 self
.velocity
= velocity
347 self
.duration
= Duration (clocks
)
348 (self
.octave
, self
.notename
, self
.alteration
) = self
.o_n_a ()
352 # minor scale: la-la (= + 5) '''
354 n
= self
.names
[(self
.pitch
) % 12]
355 a
= self
.alterations
[(self
.pitch
) % 12]
358 a
= - self
.alterations
[(self
.pitch
) % 12]
361 # By tradition, all scales now consist of a sequence
362 # of 7 notes each with a distinct name, from amongst
363 # a b c d e f g. But, minor scales have a wide
364 # second interval at the top - the 'leading note' is
365 # sharped. (Why? it just works that way! Anything
366 # else doesn't sound as good and isn't as flexible at
367 # saying things. In medieval times, scales only had 6
368 # notes to avoid this problem - the hexachords.)
370 # So, the d minor scale is d e f g a b-flat c-sharp d
371 # - using d-flat for the leading note would skip the
372 # name c and duplicate the name d. Why isn't c-sharp
373 # put in the key signature? Tradition. (It's also
374 # supposedly based on the Pythagorean theory of the
375 # cycle of fifths, but that really only applies to
376 # major scales...) Anyway, g minor is g a b-flat c d
377 # e-flat f-sharp g, and all the other flat minor keys
378 # end up with a natural leading note. And there you
381 # John Sankey <bf250@freenet.carleton.ca>
383 # Let's also do a-minor: a b c d e f gis a
387 o
= self
.pitch
/ 12 - 4
391 if key
.sharps
== 0 and key
.flats
== 0 \
392 and n
== 5 and a
== -1:
395 elif key
.flats
== 1 and n
== 1 and a
== -1:
398 elif key
.flats
== 2 and n
== 4 and a
== -1:
401 elif key
.sharps
== 5 and n
== 4 and a
== 0:
404 elif key
.sharps
== 6 and n
== 1 and a
== 0:
407 elif key
.sharps
== 7 and n
== 5 and a
== 0:
411 if key
.flats
>= 6 and n
== 6 and a
== 0:
412 n
= 0; a
= -1; o
= o
+ 1
414 if key
.flats
>= 7 and n
== 2 and a
== 0:
418 if key
.sharps
>= 3 and n
== 3 and a
== 0:
421 if key
.sharps
>= 4 and n
== 0 and a
== 0:
422 n
= 6; a
= 1; o
= o
- 1
427 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
428 s
= s
+ self
.alteration_names
[self
.alteration
+ 2]
432 delta
= self
.pitch
- reference_note
.pitch
433 commas
= sign (delta
) * (abs (delta
) / 12)
435 * (self
.notename
- reference_note
.notename
) + 7) \
437 or ((self
.notename
== reference_note
.notename
) \
438 and (abs (delta
) > 4) and (abs (delta
) < 12)):
439 commas
= commas
+ sign (delta
)
444 s
= s
+ "," * -commas
446 if explicit_durations_p \
447 or Duration
.compare (self
.duration
, reference_note
.duration
):
448 s
= s
+ self
.duration
.dump ()
450 global reference_note
451 reference_note
= self
458 def __init__ (self
, num
, den
):
463 def bar_clocks (self
):
464 return clocks_per_1
* self
.num
/ self
.den
469 return '\n ' + '\\time %d/%d ' % (self
.num
, self
.den
) + '\n '
472 def __init__ (self
, seconds_per_1
):
474 self
.seconds_per_1
= seconds_per_1
477 return '\n ' + '\\tempo 4 = %d ' % (4 * 60 / self
.seconds_per_1
) + '\n '
480 clefs
= ('"bass_8"', 'bass', 'violin', '"violin^8"')
481 def __init__ (self
, type):
485 return '\n \\clef %s\n ' % self
.clefs
[self
.type]
488 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
489 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
491 def __init__ (self
, sharps
, flats
, minor
):
502 if self
.sharps
and self
.flats
:
503 s
= '\\keysignature %s ' % 'TODO'
507 k
= (ord ('cfbeadg'[self
.flats
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
509 k
= (ord ('cgdaebf'[self
.sharps
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
512 name
= chr ((k
+ 2) % 7 + ord ('a'))
514 name
= chr ((k
+ 2) % 7 + ord ('a'))
516 # fis cis gis dis ais eis bis
517 sharps
= (2, 4, 6, 1, 3, 5, 7)
518 # bes es as des ges ces fes
519 flats
= (6, 4, 2, 7, 5, 3, 1)
522 if flats
[k
] <= self
.flats
:
525 if sharps
[k
] <= self
.sharps
:
529 name
= name
+ Note
.alteration_names
[a
+ 2]
537 return '\n\n ' + s
+ '\n '
545 'SEQUENCE_TRACK_NAME',
551 def __init__ (self
, type, text
):
557 # urg, we should be sure that we're in a lyrics staff
558 if self
.type == midi
.LYRIC
:
559 s
= '"%s"' % self
.text
560 d
= Duration (self
.clocks
)
561 if explicit_durations_p \
562 or Duration
.compare (d
,
563 reference_note
.duration
):
564 s
= s
+ Duration (self
.clocks
).dump ()
567 s
= '\n % [' + self
.text_types
[self
.type] + '] ' + self
.text
+ '\n '
571 def split_track (track
):
578 if data
[0] > 0x7f and data
[0] < 0xf0:
580 e
= (e
[0], tuple ([data
[0] & 0xf0] + data
[1:]))
590 for v
in chs
.values ():
591 events
= events_on_channel (v
)
592 thread
= unthread_notes (events
)
594 threads
.append (thread
)
598 def quantise_clocks (clocks
, quant
):
599 q
= int (clocks
/ quant
) * quant
601 for tquant
in allowed_tuplet_clocks
:
602 if int (clocks
/ tquant
) * tquant
== clocks
:
604 if 2 * (clocks
- q
) > quant
:
608 def end_note (pitches
, notes
, t
, e
):
610 (lt
, vel
) = pitches
[e
]
620 if duration_quant_clocks
:
621 d
= quantise_clocks (d
, duration_quant_clocks
)
623 d
= duration_quant_clocks
626 (lt
, Note (d
, e
, vel
)))
631 def events_on_channel (channel
):
641 if start_quant_clocks
:
642 t
= quantise_clocks (t
, start_quant_clocks
)
645 if e
[1][0] == midi
.NOTE_OFF \
646 or (e
[1][0] == midi
.NOTE_ON
and e
[1][2] == 0):
647 end_note (pitches
, notes
, t
, e
[1][1])
649 elif e
[1][0] == midi
.NOTE_ON
:
650 if not pitches
.has_key (e
[1][1]):
651 pitches
[e
[1][1]] = (t
, e
[1][2])
653 # all include ALL_NOTES_OFF
654 elif e
[1][0] >= midi
.ALL_SOUND_OFF \
655 and e
[1][0] <= midi
.POLY_MODE_ON
:
656 for i
in pitches
.keys ():
657 end_note (pitches
, notes
, t
, i
)
659 elif e
[1][0] == midi
.META_EVENT
:
660 if e
[1][1] == midi
.END_OF_TRACK
:
661 for i
in pitches
.keys ():
662 end_note (pitches
, notes
, t
, i
)
665 elif e
[1][1] == midi
.SET_TEMPO
:
666 (u0
, u1
, u2
) = map (ord, e
[1][2])
667 us_per_4
= u2
+ 256 * (u1
+ 256 * u0
)
668 seconds_per_1
= us_per_4
* 4 / 1e6
669 events
.append ((t
, Tempo (seconds_per_1
)))
670 elif e
[1][1] == midi
.TIME_SIGNATURE
:
671 (num
, dur
, clocks4
, count32
) = map (ord, e
[1][2])
673 events
.append ((t
, Time (num
, den
)))
674 elif e
[1][1] == midi
.KEY_SIGNATURE
:
675 (alterations
, minor
) = map (ord, e
[1][2])
678 if alterations
< 127:
681 flats
= 256 - alterations
683 k
= Key (sharps
, flats
, minor
)
684 events
.append ((t
, k
))
686 # ugh, must set key while parsing
687 # because Note init uses key
688 # Better do Note.calc () at dump time?
692 elif e
[1][1] == midi
.LYRIC \
693 or (text_lyrics_p
and e
[1][1] == midi
.TEXT_EVENT
):
695 last_lyric
.clocks
= t
- last_time
696 events
.append ((last_time
, last_lyric
))
698 last_lyric
= Text (midi
.LYRIC
, e
[1][2])
700 elif e
[1][1] >= midi
.SEQUENCE_NUMBER \
701 and e
[1][1] <= midi
.CUE_POINT
:
702 events
.append ((t
, Text (e
[1][1], e
[1][2])))
705 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
709 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
713 # last_lyric.clocks = t - last_time
715 last_lyric
.clocks
= clocks_per_4
716 events
.append ((last_time
, last_lyric
))
721 if i
< len (events
) and notes
[0][0] >= events
[i
][0]:
724 events
.insert (i
, notes
[0])
728 def unthread_notes (channel
):
737 if e
[1].__class
__ == Note \
738 and ((t
== start_busy_t \
739 and e
[1].clocks
+ t
== end_busy_t
) \
743 end_busy_t
= t
+ e
[1].clocks
744 elif e
[1].__class
__ == Time \
745 or e
[1].__class
__ == Key \
746 or e
[1].__class
__ == Text \
747 or e
[1].__class
__ == Tempo
:
751 threads
.append (thread
)
766 def dump_skip (skip
, clocks
):
767 return skip
+ Duration (clocks
).dump () + ' '
776 if i
.__class
__ == Note
:
781 s
= s
+ dump (notes
[0])
782 elif len (notes
) > 1:
783 global reference_note
785 s
= s
+ notes
[0].dump ()
793 def dump_bar_line (last_bar_t
, t
, bar_count
):
795 bar_t
= time
.bar_clocks ()
796 if t
- last_bar_t
>= bar_t
:
797 bar_count
= bar_count
+ (t
- last_bar_t
) / bar_t
799 if t
- last_bar_t
== bar_t
:
800 s
= '|\n %% %d\n ' % bar_count
803 # urg, this will barf at meter changes
804 last_bar_t
= last_bar_t
+ (t
- last_bar_t
) / bar_t
* bar_t
806 return (s
, last_bar_t
, bar_count
)
809 def dump_channel (thread
, skip
):
810 global key
, reference_note
, time
814 # urg LilyPond doesn't start at c4, but
815 # remembers from previous tracks!
816 # reference_note = Note (clocks_per_4, 4*12, 0)
817 reference_note
= Note (0, 4*12, 0)
823 if last_e
and last_e
[0] == e
[0]:
827 chs
.append ((last_e
[0], ch
))
834 chs
.append ((last_e
[0], ch
))
844 i
= string
.rfind (lines
[-1], '\n') + 1
845 if len (lines
[-1][i
:]) > LINE_BELL
:
849 lines
[-1] = lines
[-1] + dump_skip (skip
, t
-last_t
)
851 errorport
.write ('BUG: time skew')
853 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
855 lines
[-1] = lines
[-1] + s
857 lines
[-1] = lines
[-1] + dump_chord (ch
[1])
861 if i
.clocks
> clocks
:
866 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
868 lines
[-1] = lines
[-1] + s
870 return string
.join (lines
, '\n ') + '\n'
873 return 'track%c' % (i
+ ord ('A'))
875 def channel_name (i
):
876 return 'channel%c' % (i
+ ord ('A'))
878 def dump_track (channels
, n
):
880 track
= track_name (n
)
881 clef
= guess_clef (channels
)
883 for i
in range (len (channels
)):
884 channel
= channel_name (i
)
885 item
= thread_first_item (channels
[i
])
887 if item
and item
.__class
__ == Note
:
889 s
= s
+ '%s = \\notes' % (track
+ channel
)
891 s
= s
+ '\\relative c '
892 elif item
and item
.__class
__ == Text
:
894 s
= s
+ '%s = \\lyrics ' % (track
+ channel
)
897 # must be in \notes mode for parsing \skip
898 s
= s
+ '%s = \\notes ' % (track
+ channel
)
900 s
= s
+ ' ' + dump_channel (channels
[i
][0], skip
)
903 s
= s
+ '%s = <\n' % track
906 s
= s
+ clef
.dump () + '\n'
908 for i
in range (len (channels
)):
909 channel
= channel_name (i
)
910 item
= thread_first_item (channels
[i
])
911 if item
and item
.__class
__ == Text
:
912 s
= s
+ ' \\context Lyrics = %s \\%s\n' % (channel
,
915 s
= s
+ ' \\context Voice = %s \\%s\n' % (channel
,
920 def thread_first_item (thread
):
923 if event
[1].__class
__ == Note \
924 or (event
[1].__class
__ == Text \
925 and event
[1].type == midi
.LYRIC
):
929 def track_first_item (track
):
931 return thread_first_item (thread
)
933 def guess_clef (track
):
939 if event
[1].__class
__ == Note
:
941 p
= p
+ event
[1].pitch
942 if i
and p
/ i
<= 3*12:
944 elif i
and p
/ i
<= 5*12:
946 elif i
and p
/ i
>= 7*12:
952 def convert_midi (f
, o
):
953 global clocks_per_1
, clocks_per_4
, key
955 str = open (f
).read ()
956 midi_dump
= midi
.parse (str)
958 clocks_per_1
= midi_dump
[0][1]
959 clocks_per_4
= clocks_per_1
/ 4
961 global start_quant
, start_quant_clocks
963 start_quant_clocks
= clocks_per_1
/ start_quant
965 global duration_quant
, duration_quant_clocks
967 duration_quant_clocks
= clocks_per_1
/ duration_quant
969 global allowed_tuplet_clocks
970 allowed_tuplet_clocks
= []
971 for (dur
, num
, den
) in allowed_tuplets
:
972 allowed_tuplet_clocks
.append (clocks_per_1
* num
/ (dur
* den
))
975 for t
in midi_dump
[1]:
977 tracks
.append (split_track (t
))
979 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, f
)
983 for i
in range (len (tracks
)):
984 s
= s
+ dump_track (tracks
[i
], i
)
986 s
= s
+ '\n\\score {\n <\n'
987 for i
in range (len (tracks
)):
988 track
= track_name (i
)
989 item
= track_first_item (tracks
[i
])
990 if item
and item
.__class
__ == Note
:
991 s
= s
+ ' \\context Staff=%s \\%s\n' % (track
, track
)
992 elif item
and item
.__class
__ == Text
:
993 s
= s
+ ' \\context Lyrics=%s \\%s\n' % (track
, track
)
996 progress (_ ("%s output to `%s'...") % ('LY', o
))
1007 (sh
, long) = getopt_args (option_definitions
)
1009 (options
, files
) = getopt
.getopt(sys
.argv
[1:], sh
, long)
1010 except getopt
.error
, s
:
1011 errorport
.write ('\n')
1012 errorport
.write (_ ("error: ") + _ ("getopt says: `%s\'" % s
))
1013 errorport
.write ('\n')
1014 errorport
.write ('\n')
1024 elif o
== '--help' or o
== '-h':
1026 errorport
.write ('\n')
1027 errorport
.write (_ ("Example:"))
1028 errorport
.write (r
'''
1029 midi2ly --key=-2:1 --duration-quant=32 \
1030 --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
1033 elif o
== '--output' or o
== '-o':
1035 elif o
== '--verbose' or o
== '-V':
1037 elif o
== '--version' or o
== '-v':
1040 elif o
== '--warranty' or o
== '-w':
1041 status
= system ('lilypond -w', ignore_error
= 1)
1047 elif o
== '--absolute-pitches' or o
== '-a':
1050 elif o
== '--duration-quant' or o
== '-d':
1051 global duration_quant
1052 duration_quant
= string
.atoi (a
)
1053 elif o
== '--explicit-durations' or o
== '-e':
1054 global explicit_durations_p
1055 explicit_durations_p
= 1
1056 elif o
== '--key' or o
== '-k':
1057 (alterations
, minor
) = map (string
.atoi
, string
.split (a
+ ':0', ':'))[0:2]
1060 if alterations
>= 0:
1061 sharps
= alterations
1063 flats
= - alterations
1065 key
= Key (sharps
, flats
, minor
)
1066 elif o
== '--start-quant' or o
== '-s':
1067 start_quant
= string
.atoi (a
)
1068 elif o
== '--allow-tuplet' or o
== '-t':
1069 a
= string
.replace (a
, '/', '*')
1070 tuplet
= map (string
.atoi
, string
.split (a
, '*'))
1071 allowed_tuplets
.append (tuplet
)
1072 # lots of midi files use plain text for lyric events
1073 elif o
== '--text-lyrics' or o
== '-x':
1077 if not files
or files
[0] == '-':
1079 # FIXME: read from stdin when files[0] = '-'
1081 errorport
.write (program_name
+ ":" + _ ("error: ") + _ ("no files specified on command line.") + '\n')
1088 g
= strip_extension (g
, '.midi')
1089 g
= strip_extension (g
, '.mid')
1090 g
= strip_extension (g
, '.MID')
1091 (outdir
, outbase
) = ('','')
1095 outbase
= os
.path
.basename (g
)
1096 o
= os
.path
.join (outdir
, outbase
+ '-midi.ly')
1097 elif output_name
[-1] == os
.sep
:
1098 outdir
= output_name
1099 outbase
= os
.path
.basename (g
)
1100 os
.path
.join (outdir
, outbase
+ '-gen.ly')
1103 (outdir
, outbase
) = os
.path
.split (o
)
1105 if outdir
!= '.' and outdir
!= '':
1107 os
.mkdir (outdir
, 0777)