3 # midi2ly.py -- LilyPond midi import script
5 # source file of the GNU LilyPond music typesetter
7 # (c) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
8 # Jan Nieuwenhuizen <janneke@gnu.org>
13 * test on weird and unquantised midi input (lily-devel)
14 * update doc and manpage
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
35 ################################################################
40 scale_steps
= [0, 2, 4, 5, 7, 9, 11]
48 start_quant_clocks
= 0
50 duration_quant_clocks
= 0
51 allowed_tuplet_clocks
= []
54 ################################################################
57 program_name
= sys
.argv
[0]
58 program_version
= '@TOPLEVEL_VERSION@'
60 authors
= ('Jan Nieuwenhuizen <janneke@gnu.org>',
61 'Han-Wen Nienhuys <hanwen@xs4all.nl>')
63 errorport
= sys
.stderr
66 sys
.stdout
.write ('%s (GNU LilyPond) %s\n' % (program_name
, program_version
))
70 ly
.encoded_write (sys
.stdout
, '''
77 ''' % ( _ ('Copyright (c) %s by') % '2001--2009',
79 _ ('Distributed under terms of the GNU General Public License.'),
80 _ ('It comes with NO WARRANTY.')))
83 ly
.encoded_write (errorport
, s
+ '\n')
86 progress (_ ("warning: ") + s
)
89 progress (_ ("error: ") + s
)
90 raise Exception (_ ("Exiting... "))
92 def system (cmd
, ignore_error
= 0):
93 return ly
.system (cmd
, ignore_error
=ignore_error
)
95 def strip_extension (f
, ext
):
96 (p
, e
) = os
.path
.splitext (f
)
103 allowed_durs
= (1, 2, 4, 8, 16, 32, 64, 128)
104 def __init__ (self
, clocks
):
107 self
.clocks
= duration_quant_clocks
108 (self
.dur
, self
.num
, self
.den
) = self
.dur_num_den (clocks
)
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
]
115 dur
= 0; num
= 1; den
= 1;
116 g
= gcd (clocks
, clocks_per_1
)
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
)
127 elif self
.num
== 3 and self
.dur
!= 1:
128 s
= '%d.' % (self
.dur
/ 2)
130 s
= '%d*%d' % (self
.dur
, self
.num
)
132 s
= '%d*%d/%d' % (self
.dur
, self
.num
, self
.den
)
134 global reference_note
135 reference_note
.duration
= self
139 def compare (self
, other
):
140 return self
.clocks
- other
.clocks
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
):
154 self
.velocity
= velocity
157 self
.duration
= Duration (clocks
)
158 (self
.octave
, self
.notename
, self
.alteration
) = self
.o_n_a ()
162 # minor scale: la-la (= + 5) '''
164 n
= self
.names
[(self
.pitch
) % 12]
165 a
= self
.alterations
[(self
.pitch
) % 12]
167 if a
and global_options
.key
.flats
:
168 a
= - self
.alterations
[(self
.pitch
) % 12]
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.)
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
191 # John Sankey <bf250@freenet.carleton.ca>
193 # Let's also do a-minor: a b c d e f gis a
197 o
= self
.pitch
/ 12 - 4
199 key
= global_options
.key
202 if (key
.sharps
== 0 and key
.flats
== 0
203 and n
== 5 and a
== -1):
206 elif key
.flats
== 1 and n
== 1 and a
== -1:
209 elif key
.flats
== 2 and n
== 4 and a
== -1:
212 elif key
.sharps
== 5 and n
== 4 and a
== 0:
215 elif key
.sharps
== 6 and n
== 1 and a
== 0:
218 elif key
.sharps
== 7 and n
== 5 and a
== 0:
222 if key
.flats
>= 6 and n
== 6 and a
== 0:
223 n
= 0; a
= -1; o
= o
+ 1
225 if key
.flats
>= 7 and n
== 2 and a
== 0:
229 if key
.sharps
>= 3 and n
== 3 and a
== 0:
232 if key
.sharps
>= 4 and n
== 0 and a
== 0:
233 n
= 6; a
= 1; o
= o
- 1
238 s
= chr ((self
.notename
+ 2) % 7 + ord ('a'))
239 return 'Note(%s %s)' % (s
, self
.duration
.dump())
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
:
248 delta
= self
.pitch
- reference_note
.pitch
249 commas
= sign (delta
) * (abs (delta
) / 12)
251 * (self
.notename
- reference_note
.notename
) + 7) \
253 or ((self
.notename
== reference_note
.notename
) \
254 and (abs (delta
) > 4) and (abs (delta
) < 12)):
255 commas
= commas
+ sign (delta
)
260 s
= s
+ "," * -commas
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 ()
267 reference_note
= self
274 def __init__ (self
, num
, den
):
279 def bar_clocks (self
):
280 return clocks_per_1
* self
.num
/ self
.den
283 return 'Time(%d/%d)' % (self
.num
, self
.den
)
288 return '\n ' + '\\time %d/%d ' % (self
.num
, self
.den
) + '\n '
291 def __init__ (self
, seconds_per_1
):
293 self
.seconds_per_1
= seconds_per_1
296 return 'Tempo(%d)' % self
.bpm ()
299 return 4 * 60 / self
.seconds_per_1
302 return '\n ' + '\\tempo 4 = %d ' % (self
.bpm()) + '\n '
305 clefs
= ('"bass_8"', 'bass', 'violin', '"violin^8"')
306 def __init__ (self
, type):
310 return 'Clef(%s)' % self
.clefs
[self
.type]
313 return '\n \\clef %s\n ' % self
.clefs
[self
.type]
316 key_sharps
= ('c', 'g', 'd', 'a', 'e', 'b', 'fis')
317 key_flats
= ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges')
319 def __init__ (self
, sharps
, flats
, minor
):
326 global_options
.key
= self
329 if self
.sharps
and self
.flats
:
333 k
= (ord ('cfbeadg'[self
.flats
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
335 k
= (ord ('cgdaebf'[self
.sharps
% 7]) - ord ('a') - 2 -2 * self
.minor
+ 7) % 7
338 name
= chr ((k
+ 2) % 7 + ord ('a'))
340 name
= chr ((k
+ 2) % 7 + ord ('a'))
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)
348 if flats
[k
] <= self
.flats
:
351 if sharps
[k
] <= self
.sharps
:
355 name
= name
+ Note
.alteration_names
[a
+ 2]
363 return '\n\n ' + s
+ '\n '
371 'SEQUENCE_TRACK_NAME',
377 def __init__ (self
, type, text
):
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 ()
392 s
= '\n % [' + self
.text_types
[self
.type] + '] ' + self
.text
+ '\n '
396 return 'Text(%d=%s)' % (self
.type, self
.text
)
400 def split_track (track
):
407 if data
[0] > 0x7f and data
[0] < 0xf0:
409 e
= (e
[0], tuple ([data
[0] & 0xf0] + data
[1:]))
419 for v
in chs
.values ():
420 events
= events_on_channel (v
)
421 thread
= unthread_notes (events
)
423 threads
.append (thread
)
427 def quantise_clocks (clocks
, quant
):
428 q
= int (clocks
/ quant
) * quant
430 for tquant
in allowed_tuplet_clocks
:
431 if int (clocks
/ tquant
) * tquant
== clocks
:
433 if 2 * (clocks
- q
) > quant
:
437 def end_note (pitches
, notes
, t
, e
):
439 (lt
, vel
) = pitches
[e
]
449 if duration_quant_clocks
:
450 d
= quantise_clocks (d
, duration_quant_clocks
)
452 d
= duration_quant_clocks
455 (lt
, Note (d
, e
, vel
)))
460 def events_on_channel (channel
):
470 if start_quant_clocks
:
471 t
= quantise_clocks (t
, start_quant_clocks
)
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])
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])
482 # all include ALL_NOTES_OFF
483 elif e
[1][0] >= midi
.ALL_SOUND_OFF \
484 and e
[1][0] <= midi
.POLY_MODE_ON
:
486 end_note (pitches
, notes
, t
, i
)
488 elif e
[1][0] == midi
.META_EVENT
:
489 if e
[1][1] == midi
.END_OF_TRACK
:
491 end_note (pitches
, notes
, t
, i
)
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])
502 events
.append ((t
, Time (num
, den
)))
503 elif e
[1][1] == midi
.KEY_SIGNATURE
:
504 (alterations
, minor
) = map (ord, e
[1][2])
507 if alterations
< 127:
510 flats
= 256 - alterations
512 k
= Key (sharps
, flats
, minor
)
513 events
.append ((t
, k
))
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
520 elif e
[1][1] == midi
.LYRIC \
521 or (global_options
.text_lyrics
and e
[1][1] == midi
.TEXT_EVENT
):
523 last_lyric
.clocks
= t
- last_time
524 events
.append ((last_time
, last_lyric
))
526 last_lyric
= Text (midi
.LYRIC
, e
[1][2])
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])))
532 if global_options
.verbose
:
533 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
536 if global_options
.verbose
:
537 sys
.stderr
.write ("SKIP: %s\n" % `e`
)
541 # last_lyric.clocks = t - last_time
543 last_lyric
.clocks
= clocks_per_4
544 events
.append ((last_time
, last_lyric
))
549 if i
< len (events
) and notes
[0][0] >= events
[i
][0]:
552 events
.insert (i
, notes
[0])
556 def unthread_notes (channel
):
565 if e
[1].__class
__ == Note \
566 and ((t
== start_busy_t \
567 and e
[1].clocks
+ t
== end_busy_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
:
579 threads
.append (thread
)
594 def dump_skip (skip
, clocks
):
595 return skip
+ Duration (clocks
).dump () + ' '
604 if i
.__class
__ == Note
:
609 s
= s
+ dump (notes
[0])
610 elif len (notes
) > 1:
611 global reference_note
613 s
= s
+ notes
[0].dump (dump_dur
= 0)
616 s
= s
+ i
.dump (dump_dur
= 0 )
619 s
= s
+ notes
[0].duration
.dump() + ' '
623 def dump_bar_line (last_bar_t
, t
, bar_count
):
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
629 if t
- last_bar_t
== bar_t
:
630 s
= '|\n %% %d\n ' % bar_count
633 # urg, this will barf at meter changes
634 last_bar_t
= last_bar_t
+ (t
- last_bar_t
) / bar_t
* bar_t
636 return (s
, last_bar_t
, bar_count
)
639 def dump_channel (thread
, skip
):
640 global reference_note
, time
642 global_options
.key
= Key (0, 0, 0)
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)
653 if last_e
and last_e
[0] == e
[0]:
657 chs
.append ((last_e
[0], ch
))
664 chs
.append ((last_e
[0], ch
))
674 i
= lines
[-1].rfind ('\n') + 1
675 if len (lines
[-1][i
:]) > LINE_BELL
:
679 lines
[-1] = lines
[-1] + dump_skip (skip
, t
-last_t
)
681 errorport
.write ('BUG: time skew')
683 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
685 lines
[-1] = lines
[-1] + s
687 lines
[-1] = lines
[-1] + dump_chord (ch
[1])
691 if i
.clocks
> clocks
:
696 (s
, last_bar_t
, bar_count
) = dump_bar_line (last_bar_t
,
698 lines
[-1] = lines
[-1] + s
700 return '\n '.join (lines
) + '\n'
703 return 'track%c' % (i
+ ord ('A'))
705 def channel_name (i
):
706 return 'channel%c' % (i
+ ord ('A'))
708 def dump_track (channels
, n
):
710 track
= track_name (n
)
711 clef
= guess_clef (channels
)
713 for i
in range (len (channels
)):
714 channel
= channel_name (i
)
715 item
= thread_first_item (channels
[i
])
717 if item
and item
.__class
__ == Note
:
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
:
724 s
= s
+ '%s = \\lyricmode ' % (track
+ channel
)
727 s
= s
+ '%s = ' % (track
+ channel
)
729 s
= s
+ ' ' + dump_channel (channels
[i
][0], skip
)
732 s
= s
+ '%s = <<\n' % track
735 s
= s
+ clef
.dump () + '\n'
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
,
744 s
= s
+ ' \\context Voice = %s \\%s\n' % (channel
,
749 def thread_first_item (thread
):
752 if (event
[1].__class
__ == Note
753 or (event
[1].__class
__ == Text
754 and event
[1].type == midi
.LYRIC
)):
759 def track_first_item (track
):
761 first
= thread_first_item (thread
)
766 def guess_clef (track
):
772 if event
[1].__class
__ == Note
:
774 p
= p
+ event
[1].pitch
775 if i
and p
/ i
<= 3*12:
777 elif i
and p
/ i
<= 5*12:
779 elif i
and p
/ i
>= 7*12:
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
791 str = open (in_file
).read ()
792 midi_dump
= midi
.parse (str)
794 clocks_per_1
= midi_dump
[0][1]
795 clocks_per_4
= clocks_per_1
/ 4
797 if global_options
.start_quant
:
798 start_quant_clocks
= clocks_per_1
/ global_options
.start_quant
800 if global_options
.duration_quant
:
801 duration_quant_clocks
= clocks_per_1
/ global_options
.duration_quant
803 allowed_tuplet_clocks
= []
804 for (dur
, num
, den
) in global_options
.allowed_tuplets
:
805 allowed_tuplet_clocks
.append (clocks_per_1
/ den
)
808 for t
in midi_dump
[1]:
809 global_options
.key
= Key (0, 0, 0)
810 tracks
.append (split_track (t
))
812 tag
= '%% Lily was here -- automatically converted by %s from %s' % ( program_name
, in_file
)
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
)
820 s
= s
+ '\n\\score {\n <<\n'
824 track
= track_name (i
)
825 item
= track_first_item (t
)
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
)
835 progress (_ ("%s output to `%s'...") % ('LY', out_file
))
840 handle
= open (out_file
, 'w')
846 def get_option_parser ():
847 p
= ly
.get_option_parser (usage
=_ ("%s [OPTION]... FILE") % 'midi2ly',
848 description
=_ ("Convert %s to LilyPond input.\n") % 'MIDI',
849 add_help_option
=False)
851 p
.add_option ('-a', '--absolute-pitches',
853 help=_ ("print absolute pitches"))
854 p
.add_option ('-d', '--duration-quant',
856 help=_ ("quantise note durations on DUR"))
857 p
.add_option ('-e', '--explicit-durations',
859 help=_ ("print explicit durations"))
860 p
.add_option("-h", "--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]"),
866 p
.add_option ('-o', '--output', help=_ ("write output to FILE"),
869 p
.add_option ('-s', '--start-quant',help= _ ("quantise note starts on DUR"),
871 p
.add_option ('-t', '--allow-tuplet',
872 metavar
=_ ("DUR*NUM/DEN"),
874 dest
="allowed_tuplets",
875 help=_ ("allow tuplet durations DUR*NUM/DEN"),
877 p
.add_option ('-V', '--verbose', help=_ ("be verbose"),
880 p
.version
= "midi2ly (LilyPond) @TOPLEVEL_VERSION@"
881 p
.add_option("--version",
883 help=_ ("show version number and exit"))
884 p
.add_option ('-w', '--warranty', help=_ ("show warranty and copyright"),
887 p
.add_option ('-x', '--text-lyrics', help=_ ("treat every text as a lyric"),
890 p
.add_option_group (ly
.display_encode (_ ("Examples")),
892 $ midi2ly --key=-2:1 --duration-quant=32 --allow-tuplet=4*2/3 --allow-tuplet=2*4/3 foo.midi
894 p
.add_option_group ('',
896 _ ('Report bugs via %s')
897 % 'http://post.gmane.org/post.php'
898 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
904 opt_parser
= get_option_parser()
905 (options
, args
) = opt_parser
.parse_args ()
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.")))
913 if options
.duration_quant
:
914 options
.duration_quant
= int (options
.duration_quant
)
920 (alterations
, minor
) = map (int, (options
.key
+ ':0').split (':'))[0:2]
926 flats
= - alterations
928 options
.key
= Key (sharps
, flats
, minor
)
931 if options
.start_quant
:
932 options
.start_quant
= int (options
.start_quant
)
934 options
.allowed_tuplets
= [map (int, a
.replace ('/','*').split ('*'))
935 for a
in options
.allowed_tuplets
]
937 global global_options
938 global_options
= options
947 g
= strip_extension (g
, '.midi')
948 g
= strip_extension (g
, '.mid')
949 g
= strip_extension (g
, '.MID')
950 (outdir
, outbase
) = ('','')
952 if not global_options
.output
:
954 outbase
= os
.path
.basename (g
)
955 o
= os
.path
.join (outdir
, outbase
+ '-midi.ly')
956 elif global_options
.output
[-1] == os
.sep
:
957 outdir
= global_options
.output
958 outbase
= os
.path
.basename (g
)
959 os
.path
.join (outdir
, outbase
+ '-gen.ly')
961 o
= global_options
.output
962 (outdir
, outbase
) = os
.path
.split (o
)
964 if outdir
!= '.' and outdir
!= '':
966 os
.mkdir (outdir
, 0777)
971 if __name__
== '__main__':