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/>.
26 * test on weird and unquantised midi input (lily-devel)
27 * update doc and manpage
29 * simply insert clef changes whenever too many ledger lines
30 [to avoid tex capacity exceeded]
31 * do not ever quant skips
32 * better lyrics handling
33 * [see if it is feasible to] move ly-classes to library for use in
34 other converters, while leaving midi specific stuff here
48 ################################################################
53 scale_steps
= [0, 2, 4, 5, 7, 9, 11]
61 start_quant_clocks
= 0
63 duration_quant_clocks
= 0
64 allowed_tuplet_clocks
= []
67 ################################################################
70 program_name
= sys
.argv
[0]
71 program_version
= '@TOPLEVEL_VERSION@'
73 authors
= ('Jan Nieuwenhuizen <janneke@gnu.org>',
74 'Han-Wen Nienhuys <hanwen@xs4all.nl>')
76 errorport
= sys
.stderr
79 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
83 ly
.encoded_write (sys
.stdout
, '''
90 ''' % ( _ ('Copyright (c) %s by') % '1998--2011',
92 _ ('Distributed under terms of the GNU General Public License.'),
93 _ ('It comes with NO WARRANTY.')))
96 ly
.encoded_write (errorport
, s
+ '\n')
99 progress (_ ("warning: ") + s
)
102 progress (_ ("error: ") + s
)
103 raise Exception (_ ("Exiting... "))
107 progress ("debug: " + s
)
109 def system (cmd
, ignore_error
= 0):
110 return ly
.system (cmd
, ignore_error
=ignore_error
)
112 def strip_extension (f
, ext
):
113 (p
, e
) = os
.path
.splitext (f
)
120 allowed_durs
= (1, 2, 4, 8, 16, 32, 64, 128)
121 def __init__ (self
, clocks
):
124 self
.clocks
= duration_quant_clocks
125 (self
.dur
, self
.num
, self
.den
) = self
.dur_num_den (clocks
)
127 def dur_num_den (self
, clocks
):
128 for i
in range (len (allowed_tuplet_clocks
)):
129 if clocks
== allowed_tuplet_clocks
[i
]:
130 return global_options
.allowed_tuplets
[i
]
132 dur
= 0; num
= 1; den
= 1;
133 g
= gcd (clocks
, clocks_per_1
)
135 (dur
, num
) = (clocks_per_1
/ g
, clocks
/ g
)
136 if not dur
in self
.allowed_durs
:
137 dur
= 4; num
= clocks
; den
= clocks_per_4
138 return (dur
, num
, den
)
144 elif self
.num
== 3 and self
.dur
!= 1:
145 s
= '%d.' % (self
.dur
/ 2)
147 s
= '%d*%d' % (self
.dur
, self
.num
)
149 s
= '%d*%d/%d' % (self
.dur
, self
.num
, self
.den
)
151 global reference_note
152 reference_note
.duration
= self
156 def compare (self
, other
):
157 return self
.clocks
- other
.clocks
166 names
= (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6)
167 alterations
= (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0)
168 alteration_names
= ('eses', 'es', '', 'is' , 'isis')
169 def __init__ (self
, clocks
, pitch
, velocity
):
171 self
.velocity
= velocity
174 self
.duration
= Duration (clocks
)
175 (self
.octave
, self
.notename
, self
.alteration
) = self
.o_n_a ()
179 # minor scale: la-la (= + 5) '''
181 n
= self
.names
[(self
.pitch
) % 12]
182 a
= self
.alterations
[(self
.pitch
) % 12]
184 if a
and global_options
.key
.flats
:
185 a
= - self
.alterations
[(self
.pitch
) % 12]
188 # By tradition, all scales now consist of a sequence
189 # of 7 notes each with a distinct name, from amongst
190 # a b c d e f g. But, minor scales have a wide
191 # second interval at the top - the 'leading note' is
192 # sharped. (Why? it just works that way! Anything
193 # else doesn't sound as good and isn't as flexible at
194 # saying things. In medieval times, scales only had 6
195 # notes to avoid this problem - the hexachords.)
197 # So, the d minor scale is d e f g a b-flat c-sharp d
198 # - using d-flat for the leading note would skip the
199 # name c and duplicate the name d. Why isn't c-sharp
200 # put in the key signature? Tradition. (It's also
201 # supposedly based on the Pythagorean theory of the
202 # cycle of fifths, but that really only applies to
203 # major scales...) Anyway, g minor is g a b-flat c d
204 # e-flat f-sharp g, and all the other flat minor keys
205 # end up with a natural leading note. And there you
208 # John Sankey <bf250@freenet.carleton.ca>
210 # Let's also do a-minor: a b c d e f gis a
214 o
= self
.pitch
/ 12 - 4
216 key
= global_options
.key
219 if (key
.sharps
== 0 and key
.flats
== 0
220 and n
== 5 and a
== -1):
223 elif key
.flats
== 1 and n
== 1 and a
== -1:
226 elif key
.flats
== 2 and n
== 4 and a
== -1:
229 elif key
.sharps
== 5 and n
== 4 and a
== 0:
232 elif key
.sharps
== 6 and n
== 1 and a
== 0:
235 elif key
.sharps
== 7 and n
== 5 and a
== 0:
239 if key
.flats
>= 6 and n
== 6 and a
== 0:
240 n
= 0; a
= -1; o
= o
+ 1
242 if key
.flats
>= 7 and n
== 2 and a
== 0:
246 if key
.sharps
>= 3 and n
== 3 and a
== 0:
249 if key
.sharps
>= 4 and n
== 0 and a
== 0:
250 n
= 6; a
= 1; o
= o
- 1
255 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
256 return 'Note(%s %s)' % (s
, self
.duration
.dump ())
258 def dump (self
, dump_dur
=True):
259 global reference_note
260 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
261 s
= s
+ self
.alteration_names
[self
.alteration
+ 2]
262 if global_options
.absolute_pitches
:
265 delta
= self
.pitch
- reference_note
.pitch
266 commas
= sign (delta
) * (abs (delta
) / 12)
268 * (self
.notename
- reference_note
.notename
) + 7)
270 or ((self
.notename
== reference_note
.notename
)
271 and (abs (delta
) > 4) and (abs (delta
) < 12))):
272 commas
= commas
+ sign (delta
)
277 s
= s
+ "," * -commas
280 and self
.duration
.compare (reference_note
.duration
))
281 or global_options
.explicit_durations
):
282 s
= s
+ self
.duration
.dump ()
284 reference_note
= self
291 def __init__ (self
, num
, den
):
296 def bar_clocks (self
):
297 return clocks_per_1
* self
.num
/ self
.den
300 return 'Time(%d/%d)' % (self
.num
, self
.den
)
305 return '\n ' + '\\time %d/%d ' % (self
.num
, self
.den
) + '\n '
308 def __init__ (self
, seconds_per_1
):
310 self
.seconds_per_1
= seconds_per_1
313 return 'Tempo(%d)' % self
.bpm ()
316 return 4 * 60 / self
.seconds_per_1
319 return '\n ' + '\\tempo 4 = %d ' % (self
.bpm ()) + '\n '
322 clefs
= ('"bass_8"', 'bass', 'violin', '"violin^8"')
323 def __init__ (self
, type):
327 return 'Clef(%s)' % self
.clefs
[self
.type]
330 return '\n \\clef %s\n ' % self
.clefs
[self
.type]
333 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
334 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
336 def __init__ (self
, sharps
, flats
, minor
):
343 global_options
.key
= self
346 if self
.sharps
and self
.flats
:
350 k
= (ord ('cfbeadg'[self
.flats
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
352 k
= (ord ('cgdaebf'[self
.sharps
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
355 name
= chr ((k
+ 2) % 7 + ord ('a'))
357 name
= chr ((k
+ 2) % 7 + ord ('a'))
359 # fis cis gis dis ais eis bis
360 sharps
= (2, 4, 6, 1, 3, 5, 7)
361 # bes es as des ges ces fes
362 flats
= (6, 4, 2, 7, 5, 3, 1)
365 if flats
[k
] <= self
.flats
:
368 if sharps
[k
] <= self
.sharps
:
372 name
= name
+ Note
.alteration_names
[a
+ 2]
380 return '\n\n ' + s
+ '\n '
388 'SEQUENCE_TRACK_NAME',
394 def __init__ (self
, type, text
):
400 # urg, we should be sure that we're in a lyrics staff
401 if self
.type == midi
.LYRIC
:
402 s
= '"%s"' % self
.text
403 d
= Duration (self
.clocks
)
404 if (global_options
.explicit_durations
405 or d
.compare (reference_note
.duration
)):
406 s
= s
+ Duration (self
.clocks
).dump ()
409 s
= '\n % [' + self
.text_types
[self
.type] + '] ' + self
.text
+ '\n '
413 return 'Text(%d=%s)' % (self
.type, self
.text
)
417 def split_track (track
):
424 if data
[0] > 0x7f and data
[0] < 0xf0:
426 e
= (e
[0], tuple ([data
[0] & 0xf0] + data
[1:]))
436 for v
in chs
.values ():
437 events
= events_on_channel (v
)
438 t
= unthread_notes (events
)
443 def quantise_clocks (clocks
, quant
):
444 q
= int (clocks
/ quant
) * quant
446 for tquant
in allowed_tuplet_clocks
:
447 if int (clocks
/ tquant
) * tquant
== clocks
:
449 if 2 * (clocks
- q
) > quant
:
453 def end_note (pitches
, notes
, t
, e
):
455 (lt
, vel
) = pitches
[e
]
465 if duration_quant_clocks
:
466 d
= quantise_clocks (d
, duration_quant_clocks
)
468 d
= duration_quant_clocks
471 (lt
, Note (d
, e
, vel
)))
476 def events_on_channel (channel
):
486 if start_quant_clocks
:
487 t
= quantise_clocks (t
, start_quant_clocks
)
490 if (e
[1][0] == midi
.NOTE_OFF
491 or (e
[1][0] == midi
.NOTE_ON
and e
[1][2] == 0)):
492 debug ('%d: NOTE OFF: %s' % (t
, e
[1][1]))
494 debug (' ...treated as OFF')
495 end_note (pitches
, notes
, t
, e
[1][1])
497 elif e
[1][0] == midi
.NOTE_ON
:
498 if not pitches
.has_key (e
[1][1]):
499 debug ('%d: NOTE ON: %s' % (t
, e
[1][1]))
500 pitches
[e
[1][1]] = (t
, e
[1][2])
504 # all include ALL_NOTES_OFF
505 elif (e
[1][0] >= midi
.ALL_SOUND_OFF
506 and e
[1][0] <= midi
.POLY_MODE_ON
):
508 end_note (pitches
, notes
, t
, i
)
510 elif e
[1][0] == midi
.META_EVENT
:
511 if e
[1][1] == midi
.END_OF_TRACK
:
513 end_note (pitches
, notes
, t
, i
)
516 elif e
[1][1] == midi
.SET_TEMPO
:
517 (u0
, u1
, u2
) = map (ord, e
[1][2])
518 us_per_4
= u2
+ 256 * (u1
+ 256 * u0
)
519 seconds_per_1
= us_per_4
* 4 / 1e6
520 events
.append ((t
, Tempo (seconds_per_1
)))
521 elif e
[1][1] == midi
.TIME_SIGNATURE
:
522 (num
, dur
, clocks4
, count32
) = map (ord, e
[1][2])
524 events
.append ((t
, Time (num
, den
)))
525 elif e
[1][1] == midi
.KEY_SIGNATURE
:
526 (alterations
, minor
) = map (ord, e
[1][2])
529 if alterations
< 127:
532 flats
= 256 - alterations
534 k
= Key (sharps
, flats
, minor
)
535 events
.append ((t
, k
))
537 # ugh, must set key while parsing
538 # because Note init uses key
539 # Better do Note.calc () at dump time?
540 global_options
.key
= k
542 elif (e
[1][1] == midi
.LYRIC
543 or (global_options
.text_lyrics
and e
[1][1] == midi
.TEXT_EVENT
)):
545 last_lyric
.clocks
= t
- last_time
546 events
.append ((last_time
, last_lyric
))
548 last_lyric
= Text (midi
.LYRIC
, e
[1][2])
550 elif (e
[1][1] >= midi
.SEQUENCE_NUMBER
551 and e
[1][1] <= midi
.CUE_POINT
):
552 events
.append ((t
, Text (e
[1][1], e
[1][2])))
554 if global_options
.verbose
:
555 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
558 if global_options
.verbose
:
559 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
563 # last_lyric.clocks = t - last_time
565 last_lyric
.clocks
= clocks_per_4
566 events
.append ((last_time
, last_lyric
))
571 if i
< len (events
) and notes
[0][0] >= events
[i
][0]:
574 events
.insert (i
, notes
[0])
578 def unthread_notes (channel
):
587 if (e
[1].__class
__ == Note
588 and ((t
== start_busy_t
589 and e
[1].clocks
+ t
== end_busy_t
)
590 or t
>= end_busy_t
)):
593 end_busy_t
= t
+ e
[1].clocks
594 elif (e
[1].__class
__ == Time
595 or e
[1].__class
__ == Key
596 or e
[1].__class
__ == Text
597 or e
[1].__class
__ == Tempo
):
601 threads
.append (thread
)
616 def dump_skip (skip
, clocks
):
617 return skip
+ Duration (clocks
).dump () + ' '
626 if i
.__class
__ == Note
:
631 s
= s
+ dump (notes
[0])
632 elif len (notes
) > 1:
633 global reference_note
635 s
= s
+ notes
[0].dump (dump_dur
=False)
638 s
= s
+ i
.dump (dump_dur
=False)
641 s
= s
+ notes
[0].duration
.dump () + ' '
645 def dump_bar_line (last_bar_t
, t
, bar_count
):
647 bar_t
= time
.bar_clocks ()
648 if t
- last_bar_t
>= bar_t
:
649 bar_count
= bar_count
+ (t
- last_bar_t
) / bar_t
651 if t
- last_bar_t
== bar_t
:
652 s
= '|\n %% %d\n ' % bar_count
655 # urg, this will barf at meter changes
656 last_bar_t
= last_bar_t
+ (t
- last_bar_t
) / bar_t
* bar_t
658 return (s
, last_bar_t
, bar_count
)
661 def dump_voice (thread
, skip
):
662 global reference_note
, time
664 global_options
.key
= Key (0, 0, 0)
666 # urg LilyPond doesn't start at c4, but
667 # remembers from previous tracks!
668 # reference_note = Note (clocks_per_4, 4*12, 0)
669 if not reference_note
:
670 reference_note
= Note (0, 4*12, 0)
676 if last_e
and last_e
[0] == e
[0]:
680 chs
.append ((last_e
[0], ch
))
687 chs
.append ((last_e
[0], ch
))
697 i
= lines
[-1].rfind ('\n') + 1
698 if len (lines
[-1][i
:]) > LINE_BELL
:
703 if bar_max
and t
> time
.bar_clocks () * bar_max
:
704 d
= time
.bar_clocks () * bar_max
- last_t
705 lines
[-1] = lines
[-1] + dump_skip (skip
, d
)
707 errorport
.write ('BUG: time skew')
709 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
712 if bar_max
and bar_count
> bar_max
:
715 lines
[-1] = lines
[-1] + s
716 lines
[-1] = lines
[-1] + dump_chord (ch
[1])
720 if i
.clocks
> clocks
:
725 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
727 lines
[-1] = lines
[-1] + s
729 return '\n '.join (lines
) + '\n'
731 def number2ascii (i
):
736 s
= '%c' % (m
+ ord ('A')) + s
740 def get_track_name (i
):
741 return 'track' + number2ascii (i
)
743 def get_channel_name (i
):
744 return 'channel' + number2ascii (i
)
746 def get_voice_name (i
):
748 return 'voice' + number2ascii (i
)
751 def dump_track (track
, n
):
753 track_name
= get_track_name (n
)
754 clef
= guess_clef (track
)
757 for channel
in track
:
758 channel_name
= get_channel_name (c
)
761 for voice
in channel
:
762 voice_name
= get_voice_name (v
)
763 voice_id
= track_name
+ channel_name
+ voice_name
764 item
= voice_first_item (voice
)
766 if item
and item
.__class
__ == Note
:
768 s
+= '%(voice_id)s = ' % locals ()
769 if not global_options
.absolute_pitches
:
771 elif item
and item
.__class
__ == Text
:
773 s
+= '%(voice_id)s = \\lyricmode ' % locals ()
776 s
+= '%(voice_id)s = ' % locals ()
778 if len (channel
) > 1 and v
< 4:
779 s
+= '\\voice' + ['One', 'Two', 'Three', 'Four'][v
]
780 s
+= ' ' + dump_voice (voice
, skip
)
784 s
+= '%(track_name)s = <<\n' % locals ()
787 s
+= clef
.dump () + '\n'
790 for channel
in track
:
791 channel_name
= get_channel_name (c
)
794 for voice
in channel
:
795 voice_name
= get_voice_name (v
)
797 voice_id
= track_name
+ channel_name
+ voice_name
798 item
= voice_first_item (voice
)
800 if item
and item
.__class
__ == Text
:
802 s
+= ' \\context %(context)s = %(voice_name)s \\%(voice_id)s\n' % locals ()
806 def voice_first_item (voice
):
808 if (event
[1].__class
__ == Note
809 or (event
[1].__class
__ == Text
810 and event
[1].type == midi
.LYRIC
)):
814 def channel_first_item (channel
):
815 for voice
in channel
:
816 first
= voice_first_item (voice
)
821 def track_first_item (track
):
822 for channel
in track
:
823 first
= channel_first_item (channel
)
828 def guess_clef (track
):
834 if event
[1].__class
__ == Note
:
836 p
= p
+ event
[1].pitch
837 if i
and p
/ i
<= 3*12:
839 elif i
and p
/ i
<= 5*12:
841 elif i
and p
/ i
>= 7*12:
847 def convert_midi (in_file
, out_file
):
848 global clocks_per_1
, clocks_per_4
, key
849 global start_quant_clocks
850 global duration_quant_clocks
851 global allowed_tuplet_clocks
853 str = open (in_file
, 'rb').read ()
854 clocks_max
= bar_max
* clocks_per_1
* 2
855 midi_dump
= midi
.parse (str, clocks_max
)
857 clocks_per_1
= midi_dump
[0][1]
858 clocks_per_4
= clocks_per_1
/ 4
860 if global_options
.start_quant
:
861 start_quant_clocks
= clocks_per_1
/ global_options
.start_quant
863 if global_options
.duration_quant
:
864 duration_quant_clocks
= clocks_per_1
/ global_options
.duration_quant
866 allowed_tuplet_clocks
= []
867 for (dur
, num
, den
) in global_options
.allowed_tuplets
:
868 allowed_tuplet_clocks
.append (clocks_per_1
/ dur
* num
/ den
)
870 if global_options
.verbose
:
871 print 'allowed tuplet clocks:', allowed_tuplet_clocks
874 for t
in midi_dump
[1]:
875 global_options
.key
= Key (0, 0, 0)
876 tracks
.append (split_track (t
))
878 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, in_file
)
882 s
= tag
+ '\n\\version "2.7.38"\n\n'
883 for i
in range (len (tracks
)):
884 s
= s
+ dump_track (tracks
[i
], i
)
886 s
= s
+ '\n\\score {\n <<\n'
890 track_name
= get_track_name (i
)
891 item
= track_first_item (t
)
893 if item
and item
.__class
__ == Note
:
894 s
+= ' \\context Staff=%(track_name)s \\%(track_name)s\n' % locals ()
895 elif item
and item
.__class
__ == Text
:
896 s
+= ' \\context Lyrics=%(track_name)s \\%(track_name)s\n' % locals ()
901 progress (_ ("%s output to `%s'...") % ('LY', out_file
))
906 handle
= open (out_file
, 'w')
912 def get_option_parser ():
913 p
= ly
.get_option_parser (usage
=_ ("%s [OPTION]... FILE") % 'midi2ly',
914 description
=_ ("Convert %s to LilyPond input.\n") % 'MIDI',
915 add_help_option
=False)
917 p
.add_option ('-a', '--absolute-pitches',
919 help=_ ('print absolute pitches'))
920 p
.add_option ('-d', '--duration-quant',
922 help=_ ('quantise note durations on DUR'))
923 p
.add_option ('-e', '--explicit-durations',
925 help=_ ('print explicit durations'))
926 p
.add_option('-h', '--help',
928 help=_ ('show this help and exit'))
929 p
.add_option('-k', '--key', help=_ ('set key: ALT=+sharps|-flats; MINOR=1'),
930 metavar
=_ ('ALT[:MINOR]'),
932 p
.add_option ('-o', '--output', help=_ ('write output to FILE'),
935 p
.add_option ('-p', '--preview', help=_ ('preview of first 4 bars'),
937 p
.add_option ('-s', '--start-quant',help= _ ('quantise note starts on DUR'),
939 p
.add_option ('-t', '--allow-tuplet',
940 metavar
=_ ('DUR*NUM/DEN'),
942 dest
='allowed_tuplets',
943 help=_ ('allow tuplet durations DUR*NUM/DEN'),
945 p
.add_option ('-V', '--verbose', help=_ ('be verbose'),
948 p
.version
= 'midi2ly (LilyPond) @TOPLEVEL_VERSION@'
949 p
.add_option ('--version',
951 help=_ ('show version number and exit'))
952 p
.add_option ('-w', '--warranty', help=_ ('show warranty and copyright'),
955 p
.add_option ('-x', '--text-lyrics', help=_ ('treat every text as a lyric'),
958 p
.add_option_group (ly
.display_encode (_ ('Examples')),
960 $ midi2ly --key=-2:1 --duration-quant=32 --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
962 p
.add_option_group ('',
964 _ ('Report bugs via %s')
965 % 'http://post.gmane.org/post.php'
966 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
972 opt_parser
= get_option_parser ()
973 (options
, args
) = opt_parser
.parse_args ()
975 if not args
or args
[0] == '-':
976 opt_parser
.print_help ()
977 ly
.stderr_write ('\n%s: %s %s\n' % (program_name
, _ ('error: '),
978 _ ('no files specified on command line.')))
981 if options
.duration_quant
:
982 options
.duration_quant
= int (options
.duration_quant
)
988 (alterations
, minor
) = map (int, (options
.key
+ ':0').split (':'))[0:2]
994 flats
= - alterations
996 options
.key
= Key (sharps
, flats
, minor
)
998 if options
.start_quant
:
999 options
.start_quant
= int (options
.start_quant
)
1005 options
.allowed_tuplets
= [map (int, a
.replace ('/','*').split ('*'))
1006 for a
in options
.allowed_tuplets
]
1009 sys
.stderr
.write ('Allowed tuplets: %s\n' % `options
.allowed_tuplets`
)
1011 global global_options
1012 global_options
= options
1017 files
= do_options ()
1021 g
= strip_extension (g
, '.midi')
1022 g
= strip_extension (g
, '.mid')
1023 g
= strip_extension (g
, '.MID')
1024 (outdir
, outbase
) = ('','')
1026 if not global_options
.output
:
1028 outbase
= os
.path
.basename (g
)
1029 o
= os
.path
.join (outdir
, outbase
+ '-midi.ly')
1030 elif global_options
.output
[-1] == os
.sep
:
1031 outdir
= global_options
.output
1032 outbase
= os
.path
.basename (g
)
1033 os
.path
.join (outdir
, outbase
+ '-gen.ly')
1035 o
= global_options
.output
1036 (outdir
, outbase
) = os
.path
.split (o
)
1038 if outdir
!= '.' and outdir
!= '':
1040 os
.mkdir (outdir
, 0777)
1046 if __name__
== '__main__':