Docs: authors.itexi: Add contributor.
[lilypond/mpolesky.git] / scripts / abc2ly.py
blob8adc6e4ac5890885d41f2f193b2c06b060fe075e
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
4 # once upon a rainy monday afternoon.
6 # This file is part of LilyPond, the GNU music typesetter.
8 # LilyPond is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # LilyPond is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
22 # ...
24 # (not finished.)
25 # ABC standard v1.6: http://abcnotation.com/
27 # Enhancements (Roy R. Rankin)
29 # Header section moved to top of lilypond file
30 # handle treble, treble-8, alto, and bass clef
31 # Handle voices (V: headers) with clef and part names, multiple voices
32 # Handle w: lyrics with multiple verses
33 # Handle key mode names for minor, major, phrygian, ionian, locrian, aeolian,
34 # mixolydian, lydian, dorian
35 # Handle part names from V: header
36 # Tuplets handling fixed up
37 # Lines starting with |: not discarded as header lines
38 # Multiple T: and C: header entries handled
39 # Accidental maintained until next bar check
40 # Silent rests supported
41 # articulations fermata, upbow, downbow, ltoe, accent, tenuto supported
42 # Chord strings([-^]"string") can contain a '#'
43 # Header fields enclosed by [] in notes string processed
44 # W: words output after tune as abc2ps does it (they failed before)
46 # Enhancements (Laura Conrad)
48 # Barring now preserved between ABC and lilypond
49 # the default placement for text in abc is above the staff.
50 # %%LY now supported.
51 # \breve and \longa supported.
52 # M:none doesn't crash lily.
53 # lilypond '--' supported.
55 # Enhancements (Guy Gascoigne-Piggford)
57 # Add support for maintaining ABC's notion of beaming, this is selectable
58 # from the command line with a -b or --beam option.
59 # Fixd a problem where on cygwin empty lines weren't being correctly identifed
60 # and so were complaining, but still generating the correct output.
62 # Limitations
64 # Multiple tunes in single file not supported
65 # Blank T: header lines should write score and open a new score
66 # Not all header fields supported
67 # ABC line breaks are ignored
68 # Block comments generate error and are ignored
69 # Postscript commands are ignored
70 # lyrics not resynchronized by line breaks (lyrics must fully match notes)
71 # %%LY slyrics can't be directly before a w: line.
72 # ???
76 #TODO:
78 # * coding style
79 # * lilylib
80 # * GNU style messages: warning:FILE:LINE:
81 # * l10n
83 # Convert to new chord styles.
85 # UNDEF -> None
89 import __main__
90 import getopt
91 import sys
92 import re
93 import os
95 program_name = sys.argv[0]
98 """
99 @relocate-preamble@
102 import lilylib as ly
103 global _;_=ly._
105 version = '@TOPLEVEL_VERSION@'
106 if version == '@' + 'TOPLEVEL_VERSION' + '@':
107 version = '(unknown version)' # uGUHGUHGHGUGH
109 UNDEF = 255
110 state = UNDEF
111 voice_idx_dict = {}
112 header = {}
113 header['footnotes'] = ''
114 lyrics = []
115 slyrics = []
116 voices = []
117 state_list = []
118 repeat_state = [0] * 8
119 current_voice_idx = -1
120 current_lyric_idx = -1
121 lyric_idx = -1
122 part_names = 0
123 default_len = 8
124 length_specified = 0
125 nobarlines = 0
126 global_key = [0] * 7 # UGH
127 names = ["One", "Two", "Three"]
128 DIGITS='0123456789'
129 HSPACE=' \t'
130 midi_specs = ''
133 def error (msg):
134 sys.stderr.write (msg)
135 if global_options.strict:
136 sys.exit (1)
139 def alphabet (i):
140 return chr (i + ord('A'))
142 def check_clef(s):
143 # the number gives the base_octave
144 clefs = [("treble", "treble", 0),
145 ("treble1", "french", 0),
146 ("bass3", "varbaritone", 0),
147 ("bass", "bass", 0),
148 ("alto4", "tenor", 0),
149 ("alto2", "mezzosoprano", 0),
150 ("alto1", "soprano", 0),
151 ("alto", "alto", 0),
152 ("perc", "percussion", 0)]
153 modifier = [("-8va", "_8", -1),
154 ("-8", "_8", -1),
155 ("\+8", "^8", +1),
156 ("8", "_8", -1)]
158 if not s:
159 return ''
160 clef = None;
161 octave = 0;
162 for c in clefs:
163 m = re.match('^'+c[0], s)
164 if m:
165 (clef, octave) = (c[1], c[2])
166 s = s[m.end():]
167 break;
168 if not clef:
169 return s
171 mod = "";
172 for md in modifier:
173 m = re.match('^'+md[0], s)
174 if m:
175 mod = md[1];
176 octave += md[2];
177 s = s[m.end():]
178 break;
180 state.base_octave = octave
181 voices_append ("\\clef \""+clef+mod+"\"\n")
182 return s
184 def select_voice (name, rol):
185 if not voice_idx_dict.has_key (name):
186 state_list.append(Parser_state())
187 voices.append ('')
188 slyrics.append ([])
189 voice_idx_dict[name] = len (voices) -1
190 __main__.current_voice_idx = voice_idx_dict[name]
191 __main__.state = state_list[current_voice_idx]
192 while rol != '':
193 m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork
194 if m:
195 keyword = m.group(1)
196 rol = m.group (2)
197 a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol)
198 if a:
199 value = a.group (1)
200 rol = a.group ( 2)
201 if keyword == 'clef':
202 check_clef(value)
203 elif keyword == "name":
204 value = re.sub ('\\\\','\\\\\\\\', value)
205 ## < 2.2
206 voices_append ("\\set Staff.instrument = %s\n" % value )
208 __main__.part_names = 1
209 elif keyword == "sname" or keyword == "snm":
210 voices_append ("\\set Staff.instr = %s\n" % value )
211 else:
212 break
214 def dump_header (outf,hdr):
215 outf.write ('\\header {\n')
216 ks = hdr.keys ()
217 ks.sort ()
218 for k in ks:
219 hdr[k] = re.sub('"', '\\"', hdr[k])
220 outf.write ('\t%s = "%s"\n'% (k,hdr[k]))
221 outf.write ('}')
223 def dump_lyrics (outf):
224 if (len(lyrics)):
225 outf.write("\n\\score\n{\n \\lyrics\n <<\n")
226 for i in range (len (lyrics)):
227 outf.write ( lyrics [i])
228 outf.write ("\n")
229 outf.write(" >>\n \\layout{}\n}\n")
231 def dump_default_bar (outf):
233 Nowadays abc2ly outputs explicits barlines (?)
235 ## < 2.2
236 outf.write ("\n\\set Score.defaultBarType = \"empty\"\n")
239 def dump_slyrics (outf):
240 ks = voice_idx_dict.keys()
241 ks.sort ()
242 for k in ks:
243 if re.match('[1-9]', k):
244 m = alphabet (int (k))
245 else:
246 m = k
247 for i in range (len(slyrics[voice_idx_dict[k]])):
248 l= alphabet(i)
249 outf.write ("\nwords%sV%s = \\lyricmode {" % (m, l))
250 outf.write ("\n" + slyrics [voice_idx_dict[k]][i])
251 outf.write ("\n}")
253 def dump_voices (outf):
254 global doing_alternative, in_repeat
255 ks = voice_idx_dict.keys()
256 ks.sort ()
257 for k in ks:
258 if re.match ('[1-9]', k):
259 m = alphabet (int (k))
260 else:
261 m = k
262 outf.write ("\nvoice%s = {" % m)
263 dump_default_bar(outf)
264 if repeat_state[voice_idx_dict[k]]:
265 outf.write("\n\\repeat volta 2 {")
266 outf.write ("\n" + voices [voice_idx_dict[k]])
267 if not using_old:
268 if doing_alternative[voice_idx_dict[k]]:
269 outf.write("}")
270 if in_repeat[voice_idx_dict[k]]:
271 outf.write("}")
272 outf.write ("\n}")
274 def try_parse_q(a):
275 #assume that Q takes the form "Q:'opt. description' 1/4=120"
276 #There are other possibilities, but they are deprecated
277 r = re.compile ('^(.*) *([0-9]+) */ *([0-9]+) *=* *([0-9]+)\s*')
278 m = r.match (a)
279 if m:
280 descr = m.group(1) # possibly empty
281 numerator = int(m.group (2))
282 denominator = int(m.group (3))
283 tempo = m.group (4)
284 dur = duration_to_lilypond_duration ((numerator,denominator), 1, 0)
285 voices_append ("\\tempo " + descr + " " + dur + "=" + tempo + "\n")
286 else:
287 sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a)
289 def dump_score (outf):
290 outf.write (r"""
292 \score{
294 """)
296 ks = voice_idx_dict.keys ();
297 ks.sort ()
298 for k in ks:
299 if re.match('[1-9]', k):
300 m = alphabet (int (k))
301 else:
302 m = k
303 if k == 'default' and len (voice_idx_dict) > 1:
304 break
305 outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k )
306 if k != 'default':
307 outf.write ("\t \\voicedefault\n")
308 outf.write ("\t \\voice%s " % m)
309 outf.write ("\n\t}\n")
311 l = ord( 'A' )
312 for lyrics in slyrics [voice_idx_dict[k]]:
313 outf.write ("\n\t\\addlyrics {\n")
314 if re.match('[1-9]',k):
315 m = alphabet (int (k))
316 else:
317 m = k
319 outf.write ( " \\words%sV%s } " % ( m, chr (l)) )
320 l += 1
322 outf.write ("\n >>")
323 outf.write ("\n\t\\layout {\n")
324 outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs)
328 def set_default_length (s):
329 global length_specified
330 m = re.search ('1/([0-9]+)', s)
331 if m:
332 __main__.default_len = int ( m.group (1))
333 length_specified = 1
335 def set_default_len_from_time_sig (s):
336 m = re.search ('([0-9]+)/([0-9]+)', s)
337 if m:
338 n = int (m.group (1))
339 d = int (m.group (2))
340 if (n * 1.0 )/(d * 1.0) < 0.75:
341 __main__.default_len = 16
342 else:
343 __main__.default_len = 8
345 def gulp_file(f):
346 try:
347 i = open(f)
348 i.seek (0, 2)
349 n = i.tell ()
350 i.seek (0,0)
351 except:
352 sys.stderr.write ("cannot open file: `%s'\n" % f)
353 return ''
354 s = i.read (n)
355 if len (s) <= 0:
356 sys.stderr.write ("gulped empty file: `%s'\n" % f)
357 i.close ()
358 return s
361 # pitch manipulation. Tuples are (name, alteration).
362 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
363 # pitch in semitones.
364 def semitone_pitch (tup):
365 p =0
367 t = tup[0]
368 p = p + 12 * (t / 7)
369 t = t % 7
371 if t > 2:
372 p = p- 1
374 p = p + t* 2 + tup[1]
375 return p
377 def fifth_above_pitch (tup):
378 (n, a) = (tup[0] + 4, tup[1])
380 difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
381 a = a + difference
383 return (n,a)
385 def sharp_keys ():
386 p = (0,0)
387 l = []
388 k = 0
389 while 1:
390 l.append (p)
391 (t,a) = fifth_above_pitch (p)
392 if semitone_pitch((t,a)) % 12 == 0:
393 break
395 p = (t % 7, a)
396 return l
398 def flat_keys ():
399 p = (0,0)
400 l = []
401 k = 0
402 while 1:
403 l.append (p)
404 (t,a) = quart_above_pitch (p)
405 if semitone_pitch((t,a)) % 12 == 0:
406 break
408 p = (t % 7, a)
409 return l
411 def quart_above_pitch (tup):
412 (n, a) = (tup[0] + 3, tup[1])
414 difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
415 a = a + difference
417 return (n,a)
419 key_lookup = { # abc to lilypond key mode names
420 'm' : 'minor',
421 'min' : 'minor',
422 'maj' : 'major',
423 'major' : 'major',
424 'phr' : 'phrygian',
425 'ion' : 'ionian',
426 'loc' : 'locrian',
427 'aeo' : 'aeolian',
428 'mix' : 'mixolydian',
429 'mixolydian' : 'mixolydian',
430 'lyd' : 'lydian',
431 'dor' : 'dorian',
432 'dorian' : 'dorian'
435 def lily_key (k):
436 orig = "" + k
437 # UGR
438 k = k.lower ()
439 key = k[0]
440 #UGH
441 k = k[1:]
442 if k and k[0] == '#':
443 key = key + 'is'
444 k = k[1:]
445 elif k and k[0] == 'b':
446 key = key + 'es'
447 k = k[1:]
448 if not k:
449 return '%s \\major' % key
451 type = k[0:3]
452 if not key_lookup.has_key (type):
453 #ugh, use lilylib, say WARNING:FILE:LINE:
454 sys.stderr.write ("abc2ly:warning:")
455 sys.stderr.write ("ignoring unknown key: `%s'" % orig)
456 sys.stderr.write ('\n')
457 return 0
458 return ("%s \\%s" % ( key, key_lookup[type]))
460 def shift_key (note, acc, shift):
461 s = semitone_pitch((note, acc))
462 s = (s + shift + 12) % 12
463 if s <= 4:
464 n = s / 2
465 a = s % 2
466 else:
467 n = (s + 1) / 2
468 a = (s + 1) % 2
469 if a:
470 n = n + 1
471 a = -1
472 return (n,a)
474 key_shift = { # semitone shifts for key mode names
475 'm' : 3,
476 'min' : 3,
477 'minor' : 3,
478 'maj' : 0,
479 'major' : 0,
480 'phr' : -4,
481 'phrygian' : -4,
482 'ion' : 0,
483 'ionian' : 0,
484 'loc' : 1,
485 'locrian' : 1,
486 'aeo' : 3,
487 'aeolian' : 3,
488 'mix' : 5,
489 'mixolydian' : 5,
490 'lyd' : -5,
491 'lydian' : -5,
492 'dor' : -2,
493 'dorian' : -2
495 def compute_key (k):
496 k = k.lower ()
497 intkey = (ord (k[0]) - ord('a') + 5) % 7
498 intkeyacc =0
499 k = k[1:]
501 if k and k[0] == 'b':
502 intkeyacc = -1
503 k = k[1:]
504 elif k and k[0] == '#':
505 intkeyacc = 1
506 k = k[1:]
507 k = k[0:3]
508 if k and key_shift.has_key(k):
509 (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k])
510 keytup = (intkey, intkeyacc)
512 sharp_key_seq = sharp_keys ()
513 flat_key_seq = flat_keys ()
515 accseq = None
516 accsign = 0
517 if keytup in sharp_key_seq:
518 accsign = 1
519 key_count = sharp_key_seq.index (keytup)
520 accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1))
522 elif keytup in flat_key_seq:
523 accsign = -1
524 key_count = flat_key_seq.index (keytup)
525 accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
526 else:
527 error ("Huh?")
528 raise Exception ("Huh")
530 key_table = [0] * 7
531 for a in accseq:
532 key_table[a] = key_table[a] + accsign
534 return key_table
536 tup_lookup = {
537 '2' : '3/2',
538 '3' : '2/3',
539 '4' : '4/3',
540 '5' : '4/5',
541 '6' : '4/6',
542 '7' : '6/7',
543 '9' : '8/9',
547 def try_parse_tuplet_begin (str, state):
548 if re.match ('\([2-9]', str):
549 dig = str[1]
550 str = str[2:]
551 prev_tuplet_state = state.parsing_tuplet
552 state.parsing_tuplet = int (dig[0])
553 if prev_tuplet_state:
554 voices_append ("}")
555 voices_append ("\\times %s {" % tup_lookup[dig])
556 return str
558 def try_parse_group_end (str, state):
559 if str and str[0] in HSPACE:
560 str = str[1:]
561 close_beam_state(state)
562 return str
564 def header_append (key, a):
565 s = ''
566 if header.has_key (key):
567 s = header[key] + "\n"
568 header [key] = s + a
570 def wordwrap(a, v):
571 linelen = len (v) - v.rfind ('\n')
572 if linelen + len (a) > 80:
573 v = v + '\n'
574 return v + a + ' '
576 def stuff_append (stuff, idx, a):
577 if not stuff:
578 stuff.append (a)
579 else:
580 stuff [idx] = wordwrap(a, stuff[idx])
582 # ignore wordwrap since we are adding to the previous word
583 def stuff_append_back(stuff, idx, a):
584 if not stuff:
585 stuff.append (a)
586 else:
587 point = len(stuff[idx])-1
588 while stuff[idx][point] is ' ':
589 point = point - 1
590 point = point +1
591 stuff[idx] = stuff[idx][:point] + a + stuff[idx][point:]
593 def voices_append(a):
594 if current_voice_idx < 0:
595 select_voice ('default', '')
596 stuff_append (voices, current_voice_idx, a)
598 # word wrap really makes it hard to bind beams to the end of notes since it
599 # pushes out whitespace on every call. The _back functions do an append
600 # prior to the last space, effectively tagging whatever they are given
601 # onto the last note
602 def voices_append_back(a):
603 if current_voice_idx < 0:
604 select_voice ('default', '')
605 stuff_append_back(voices, current_voice_idx, a)
607 def repeat_prepend():
608 global repeat_state
609 if current_voice_idx < 0:
610 select_voice ('default', '')
611 if not using_old:
612 repeat_state[current_voice_idx] = 't'
615 def lyrics_append(a):
616 a = re.sub ('#', '\\#', a) # latex does not like naked #'s
617 a = re.sub ('"', '\\"', a) # latex does not like naked "'s
618 a = '\t{ "' + a + '" }\n'
619 stuff_append (lyrics, current_lyric_idx, a)
621 # break lyrics to words and put "'s around words containing numbers and '"'s
622 def fix_lyric(str):
623 ret = ''
624 while str != '':
625 m = re.match('[ \t]*([^ \t]*)[ \t]*(.*$)', str)
626 if m:
627 word = m.group(1)
628 str = m.group(2)
629 word = re.sub('"', '\\"', word) # escape "
630 if re.match('.*[0-9"\(]', word):
631 word = re.sub('_', ' ', word) # _ causes probs inside ""
632 ret = ret + '\"' + word + '\" '
633 else:
634 ret = ret + word + ' '
635 else:
636 return (ret)
637 return (ret)
639 def slyrics_append(a):
640 a = re.sub ( '_', ' _ ', a) # _ to ' _ '
641 a = re.sub ( '([^-])-([^-])', '\\1- \\2', a) # split words with "-" unless was originally "--"
642 a = re.sub ( '\\\\- ', '-', a) # unless \-
643 a = re.sub ( '~', '_', a) # ~ to space('_')
644 a = re.sub ( '\*', '_ ', a) # * to to space
645 a = re.sub ( '#', '\\#', a) # latex does not like naked #'s
646 if re.match('.*[0-9"\(]', a): # put numbers and " and ( into quoted string
647 a = fix_lyric(a)
648 a = re.sub ( '$', ' ', a) # insure space between lines
649 __main__.lyric_idx = lyric_idx + 1
650 if len(slyrics[current_voice_idx]) <= lyric_idx:
651 slyrics[current_voice_idx].append(a)
652 else:
653 v = slyrics[current_voice_idx][lyric_idx]
654 slyrics[current_voice_idx][lyric_idx] = wordwrap(a, slyrics[current_voice_idx][lyric_idx])
657 def try_parse_header_line (ln, state):
658 global length_specified
659 m = re.match ('^([A-Za-z]): *(.*)$', ln)
661 if m:
662 g =m.group (1)
663 a = m.group (2)
664 if g == 'T': #title
665 a = re.sub('[ \t]*$','', a) #strip trailing blanks
666 if header.has_key('title'):
667 if a:
668 if len(header['title']):
669 # the non-ascii character
670 # in the string below is a
671 # punctuation dash. (TeX ---)
672 header['title'] = header['title'] + ' — ' + a
673 else:
674 header['subtitle'] = a
675 else:
676 header['title'] = a
677 if g == 'M': # Meter
678 if a == 'C':
679 if not state.common_time:
680 state.common_time = 1
681 voices_append (" \\override Staff.TimeSignature #'style = #'C\n")
682 a = '4/4'
683 if a == 'C|':
684 if not state.common_time:
685 state.common_time = 1
686 voices_append ("\\override Staff.TimeSignature #'style = #'C\n")
687 a = '2/2'
688 if not length_specified:
689 set_default_len_from_time_sig (a)
690 else:
691 length_specified = 0
692 if not a == 'none':
693 voices_append ('\\time %s' % a)
694 state.next_bar = ''
695 if g == 'K': # KEY
696 a = check_clef(a)
697 if a:
698 m = re.match ('^([^ \t]*) *([^ ]*)( *)(.*)$', a) # seperate clef info
699 if m:
700 # there may or may not be a space
701 # between the key letter and the mode
702 # convert the mode to lower-case before comparing
703 mode = m.group(2)[0:3].lower();
704 if key_lookup.has_key(mode):
705 # use the full mode, not only the first three letters
706 key_info = m.group(1) + m.group(2).lower()
707 clef_info = a[m.start(4):]
708 else:
709 key_info = m.group(1)
710 clef_info = a[m.start(2):]
711 __main__.global_key = compute_key (key_info)
712 k = lily_key (key_info)
713 if k:
714 voices_append ('\\key %s' % k)
715 check_clef(clef_info)
716 else:
717 __main__.global_key = compute_key (a)
718 k = lily_key (a)
719 if k:
720 voices_append ('\\key %s \\major' % k)
721 if g == 'N': # Notes
722 header ['footnotes'] = header['footnotes'] + '\\\\\\\\' + a
723 if g == 'O': # Origin
724 header ['origin'] = a
725 if g == 'X': # Reference Number
726 header ['crossRefNumber'] = a
727 if g == 'A': # Area
728 header ['area'] = a
729 if g == 'H': # History
730 header_append ('history', a)
731 if g == 'B': # Book
732 header ['book'] = a
733 if g == 'C': # Composer
734 if header.has_key('composer'):
735 if a:
736 header['composer'] = header['composer'] + '\\\\\\\\' + a
737 else:
738 header['composer'] = a
739 if g == 'S':
740 header ['subtitle'] = a
741 if g == 'L': # Default note length
742 set_default_length (ln)
743 if g == 'V': # Voice
744 voice = re.sub (' .*$', '', a)
745 rest = re.sub ('^[^ \t]* *', '', a)
746 if state.next_bar:
747 voices_append(state.next_bar)
748 state.next_bar = ''
749 select_voice (voice, rest)
750 if g == 'W': # Words
751 lyrics_append(a)
752 if g == 'w': # vocals
753 slyrics_append (a)
754 if g == 'Q': #tempo
755 try_parse_q (a)
756 return ''
757 return ln
759 # we use in this order specified accidental, active accidental for bar,
760 # active accidental for key
761 def pitch_to_lilypond_name (name, acc, bar_acc, key):
762 s = ''
763 if acc == UNDEF:
764 if not nobarlines:
765 acc = bar_acc
766 if acc == UNDEF:
767 acc = key
768 if acc == -1:
769 s = 'es'
770 elif acc == 1:
771 s = 'is'
773 if name > 4:
774 name = name -7
775 return(chr (name + ord('c')) + s)
778 def octave_to_lilypond_quotes (o):
779 o = o + 2
780 s =''
781 if o < 0:
782 o = -o
783 s=','
784 else:
785 s ='\''
787 return s * o
789 def parse_num (str):
790 durstr = ''
791 while str and str[0] in DIGITS:
792 durstr = durstr + str[0]
793 str = str[1:]
795 n = None
796 if durstr:
797 n = int (durstr)
798 return (str,n)
801 def duration_to_lilypond_duration (multiply_tup, defaultlen, dots):
802 base = 1
803 # (num / den) / defaultlen < 1/base
804 while base * multiply_tup[0] < multiply_tup[1]:
805 base = base * 2
806 if base == 1:
807 if (multiply_tup[0] / multiply_tup[1]) == 2:
808 base = '\\breve'
809 if (multiply_tup[0] / multiply_tup[1]) == 3:
810 base = '\\breve'
811 dots = 1
812 if (multiply_tup[0] / multiply_tup[1]) == 4:
813 base = '\\longa'
814 return '%s%s' % ( base, '.'* dots)
816 class Parser_state:
817 def __init__ (self):
818 self.in_acc = {}
819 self.next_articulation = ''
820 self.next_bar = ''
821 self.next_dots = 0
822 self.next_den = 1
823 self.parsing_tuplet = 0
824 self.plus_chord = 0
825 self.base_octave = 0
826 self.common_time = 0
827 self.parsing_beam = 0
831 # return (str, num,den,dots)
832 def parse_duration (str, parser_state):
833 num = 0
834 den = parser_state.next_den
835 parser_state.next_den = 1
837 (str, num) = parse_num (str)
838 if not num:
839 num = 1
840 if len(str):
841 if str[0] == '/':
842 if len(str[0]):
843 while str[:1] == '/':
844 str= str[1:]
845 d = 2
846 if str[0] in DIGITS:
847 (str, d) =parse_num (str)
849 den = den * d
851 den = den * default_len
853 current_dots = parser_state.next_dots
854 parser_state.next_dots = 0
855 if re.match ('[ \t]*[<>]', str):
856 while str[0] in HSPACE:
857 str = str[1:]
858 while str[0] == '>':
859 str = str [1:]
860 current_dots = current_dots + 1
861 parser_state.next_den = parser_state.next_den * 2
863 while str[0] == '<':
864 str = str [1:]
865 den = den * 2
866 parser_state.next_dots = parser_state.next_dots + 1
870 try_dots = [3, 2, 1]
871 for d in try_dots:
872 f = 1 << d
873 multiplier = (2*f-1)
874 if num % multiplier == 0 and den % f == 0:
875 num = num / multiplier
876 den = den / f
877 current_dots = current_dots + d
879 return (str, num,den,current_dots)
882 def try_parse_rest (str, parser_state):
883 if not str or str[0] <> 'z' and str[0] <> 'x':
884 return str
886 __main__.lyric_idx = -1
888 if parser_state.next_bar:
889 voices_append(parser_state.next_bar)
890 parser_state.next_bar = ''
892 if str[0] == 'z':
893 rest = 'r'
894 else:
895 rest = 's'
896 str = str[1:]
898 (str, num,den,d) = parse_duration (str, parser_state)
899 voices_append ('%s%s' % (rest, duration_to_lilypond_duration ((num,den), default_len, d)))
900 if parser_state.next_articulation:
901 voices_append (parser_state.next_articulation)
902 parser_state.next_articulation = ''
904 return str
906 artic_tbl = {
907 '.' : '-.',
908 'T' : '^\\trill',
909 'H' : '^\\fermata',
910 'u' : '^\\upbow',
911 'K' : '^\\ltoe',
912 'k' : '^\\accent',
913 'M' : '^\\tenuto',
914 '~' : '^"~" ',
915 'J' : '', # ignore slide
916 'R' : '', # ignore roll
917 'S' : '^\\segno',
918 'O' : '^\\coda',
919 'v' : '^\\downbow'
922 def try_parse_articulation (str, state):
923 while str and artic_tbl.has_key(str[:1]):
924 state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
925 if not artic_tbl[str[:1]]:
926 sys.stderr.write("Warning: ignoring `%s'\n" % str[:1] )
928 str = str[1:]
932 # s7m2 input doesnt care about spaces
933 if re.match('[ \t]*\(', str):
934 str = str.lstrip ()
936 slur_begin =0
937 while str[:1] =='(' and str[1] not in DIGITS:
938 slur_begin = slur_begin + 1
939 state.next_articulation = state.next_articulation + '('
940 str = str[1:]
942 return str
945 # remember accidental for rest of bar
947 def set_bar_acc(note, octave, acc, state):
948 if acc == UNDEF:
949 return
950 n_oct = note + octave * 7
951 state.in_acc[n_oct] = acc
953 # get accidental set in this bar or UNDEF if not set
954 def get_bar_acc(note, octave, state):
955 n_oct = note + octave * 7
956 if state.in_acc.has_key(n_oct):
957 return(state.in_acc[n_oct])
958 else:
959 return(UNDEF)
961 def clear_bar_acc(state):
962 state.in_acc = {}
965 # if we are parsing a beam, close it off
966 def close_beam_state(state):
967 if state.parsing_beam and global_options.beams:
968 state.parsing_beam = 0
969 voices_append_back( ']' )
972 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP !
973 def try_parse_note (str, parser_state):
974 mud = ''
976 slur_begin =0
977 if not str:
978 return str
980 articulation =''
981 acc = UNDEF
982 if str[0] in '^=_':
983 c = str[0]
984 str = str[1:]
985 if c == '^':
986 acc = 1
987 if c == '=':
988 acc = 0
989 if c == '_':
990 acc = -1
992 octave = parser_state.base_octave
993 if str[0] in "ABCDEFG":
994 str = str[0].lower () + str[1:]
995 octave = octave - 1
998 notename = 0
999 if str[0] in "abcdefg":
1000 notename = (ord(str[0]) - ord('a') + 5)%7
1001 str = str[1:]
1002 else:
1003 return str # failed; not a note!
1006 __main__.lyric_idx = -1
1008 if parser_state.next_bar:
1009 voices_append(parser_state.next_bar)
1010 parser_state.next_bar = ''
1012 while str[0] == ',':
1013 octave = octave - 1
1014 str = str[1:]
1015 while str[0] == '\'':
1016 octave = octave + 1
1017 str = str[1:]
1019 (str, num,den,current_dots) = parse_duration (str, parser_state)
1021 if re.match('[ \t]*\)', str):
1022 str = str.lstrip ()
1024 slur_end =0
1025 while str[:1] ==')':
1026 slur_end = slur_end + 1
1027 str = str[1:]
1030 bar_acc = get_bar_acc(notename, octave, parser_state)
1031 pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename])
1032 oct = octave_to_lilypond_quotes (octave)
1033 if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
1034 mod='!'
1035 else:
1036 mod = ''
1037 voices_append ("%s%s%s%s" %
1038 (pit, oct, mod,
1039 duration_to_lilypond_duration ((num,den), default_len, current_dots)))
1041 set_bar_acc(notename, octave, acc, parser_state)
1042 if parser_state.next_articulation:
1043 articulation = articulation + parser_state.next_articulation
1044 parser_state.next_articulation = ''
1046 voices_append (articulation)
1048 if slur_begin:
1049 voices_append ('-(' * slur_begin )
1050 if slur_end:
1051 voices_append ('-)' *slur_end )
1053 if parser_state.parsing_tuplet:
1054 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
1055 if not parser_state.parsing_tuplet:
1056 voices_append ("}")
1058 if global_options.beams and \
1059 str[0] in '^=_ABCDEFGabcdefg' and \
1060 not parser_state.parsing_beam and \
1061 not parser_state.parsing_tuplet:
1062 parser_state.parsing_beam = 1
1063 voices_append_back( '[' )
1065 return str
1067 def junk_space (str,state):
1068 while str and str[0] in '\t\n\r ':
1069 str = str[1:]
1070 close_beam_state(state)
1072 return str
1075 def try_parse_guitar_chord (str, state):
1076 if str[:1] =='"':
1077 str = str[1:]
1078 gc = ''
1079 if str[0] == '_' or (str[0] == '^'):
1080 position = str[0]
1081 str = str[1:]
1082 else:
1083 position = '^'
1084 while str and str[0] != '"':
1085 gc = gc + str[0]
1086 str = str[1:]
1088 if str:
1089 str = str[1:]
1090 gc = re.sub('#', '\\#', gc) # escape '#'s
1091 state.next_articulation = ("%c\"%s\"" % (position, gc)) \
1092 + state.next_articulation
1093 return str
1095 def try_parse_escape (str):
1096 if not str or str [0] != '\\':
1097 return str
1099 str = str[1:]
1100 if str[:1] =='K':
1101 key_table = compute_key ()
1102 return str
1105 # |] thin-thick double bar line
1106 # || thin-thin double bar line
1107 # [| thick-thin double bar line
1108 # :| left repeat
1109 # |: right repeat
1110 # :: left-right repeat
1111 # |1 volta 1
1112 # |2 volta 2
1113 old_bar_dict = {
1114 '|]' : '|.',
1115 '||' : '||',
1116 '[|' : '||',
1117 ':|' : ':|',
1118 '|:' : '|:',
1119 '::' : ':|:',
1120 '|1' : '|',
1121 '|2' : '|',
1122 ':|2' : ':|',
1123 '|' : '|'
1125 bar_dict = {
1126 '|]' : '\\bar "|."',
1127 '||' : '\\bar "||"',
1128 '[|' : '\\bar "||"',
1129 ':|' : '}',
1130 '|:' : '\\repeat volta 2 {',
1131 '::' : '} \\repeat volta 2 {',
1132 '|1' : '} \\alternative{{',
1133 '|2' : '} {',
1134 ':|2' : '} {',
1135 '|' : '\\bar "|"'
1139 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
1140 alternative_opener = ['|1', '|2', ':|2']
1141 repeat_ender = ['::', ':|']
1142 repeat_opener = ['::', '|:']
1143 in_repeat = [''] * 8
1144 doing_alternative = [''] * 8
1145 using_old = ''
1147 def try_parse_bar (str,state):
1148 global in_repeat, doing_alternative, using_old
1149 do_curly = ''
1150 bs = None
1151 if current_voice_idx < 0:
1152 select_voice ('default', '')
1153 # first try the longer one
1154 for trylen in [3,2,1]:
1155 if str[:trylen] and bar_dict.has_key (str[:trylen]):
1156 s = str[:trylen]
1157 if using_old:
1158 bs = "\\bar \"%s\"" % old_bar_dict[s]
1159 else:
1160 bs = "%s" % bar_dict[s]
1161 str = str[trylen:]
1162 if s in alternative_opener:
1163 if not in_repeat[current_voice_idx]:
1164 using_old = 't'
1165 bs = "\\bar \"%s\"" % old_bar_dict[s]
1166 else:
1167 doing_alternative[current_voice_idx] = 't'
1169 if s in repeat_ender:
1170 if not in_repeat[current_voice_idx]:
1171 sys.stderr.write("Warning: inserting repeat to beginning of notes.\n")
1172 repeat_prepend()
1173 in_repeat[current_voice_idx] = ''
1174 else:
1175 if doing_alternative[current_voice_idx]:
1176 do_curly = 't'
1177 if using_old:
1178 bs = "\\bar \"%s\"" % old_bar_dict[s]
1179 else:
1180 bs = bar_dict[s]
1181 doing_alternative[current_voice_idx] = ''
1182 in_repeat[current_voice_idx] = ''
1183 if s in repeat_opener:
1184 in_repeat[current_voice_idx] = 't'
1185 if using_old:
1186 bs = "\\bar \"%s\"" % old_bar_dict[s]
1187 else:
1188 bs = bar_dict[s]
1189 break
1190 if str[:1] == '|':
1191 state.next_bar = '|\n'
1192 str = str[1:]
1193 clear_bar_acc(state)
1194 close_beam_state(state)
1196 if bs <> None or state.next_bar != '':
1197 if state.parsing_tuplet:
1198 state.parsing_tuplet =0
1199 voices_append ('} ')
1201 if bs <> None:
1202 clear_bar_acc(state)
1203 close_beam_state(state)
1204 voices_append (bs)
1205 if do_curly != '':
1206 voices_append("} ")
1207 do_curly = ''
1208 return str
1210 def try_parse_tie (str):
1211 if str[:1] =='-':
1212 str = str[1:]
1213 voices_append (' ~ ')
1214 return str
1216 def bracket_escape (str, state):
1217 m = re.match ( '^([^\]]*)] *(.*)$', str)
1218 if m:
1219 cmd = m.group (1)
1220 str = m.group (2)
1221 try_parse_header_line (cmd, state)
1222 return str
1224 def try_parse_chord_delims (str, state):
1225 if str[:1] =='[':
1226 str = str[1:]
1227 if re.match('[A-Z]:', str): # bracket escape
1228 return bracket_escape(str, state)
1229 if state.next_bar:
1230 voices_append(state.next_bar)
1231 state.next_bar = ''
1232 voices_append ('<<')
1234 if str[:1] == '+':
1235 str = str[1:]
1236 if state.plus_chord:
1237 voices_append ('>>')
1238 state.plus_chord = 0
1239 else:
1240 if state.next_bar:
1241 voices_append(state.next_bar)
1242 state.next_bar = ''
1243 voices_append ('<<')
1244 state.plus_chord = 1
1246 ch = ''
1247 if str[:1] ==']':
1248 str = str[1:]
1249 ch = '>>'
1251 end = 0
1252 while str[:1] ==')':
1253 end = end + 1
1254 str = str[1:]
1257 voices_append ("\\spanrequest \\stop \"slur\"" * end)
1258 voices_append (ch)
1259 return str
1261 def try_parse_grace_delims (str, state):
1262 if str[:1] =='{':
1263 if state.next_bar:
1264 voices_append(state.next_bar)
1265 state.next_bar = ''
1266 str = str[1:]
1267 voices_append ('\\grace { ')
1269 if str[:1] =='}':
1270 str = str[1:]
1271 voices_append ('}')
1273 return str
1275 def try_parse_comment (str):
1276 global nobarlines
1277 if (str[0] == '%'):
1278 if str[0:5] == '%MIDI':
1279 #the nobarlines option is necessary for an abc to lilypond translator for
1280 #exactly the same reason abc2midi needs it: abc requires the user to enter
1281 #the note that will be printed, and MIDI and lilypond expect entry of the
1282 #pitch that will be played.
1284 #In standard 19th century musical notation, the algorithm for translating
1285 #between printed note and pitch involves using the barlines to determine
1286 #the scope of the accidentals.
1288 #Since ABC is frequently used for music in styles that do not use this
1289 #convention, such as most music written before 1700, or ethnic music in
1290 #non-western scales, it is necessary to be able to tell a translator that
1291 #the barlines should not affect its interpretation of the pitch.
1292 if 'nobarlines' in str:
1293 nobarlines = 1
1294 elif str[0:3] == '%LY':
1295 p = str.find ('voices')
1296 if (p > -1):
1297 voices_append(str[p+7:])
1298 voices_append("\n")
1299 p = str.find ('slyrics')
1300 if (p > -1):
1301 slyrics_append(str[p+8:])
1303 #write other kinds of appending if we ever need them.
1304 return str
1306 lineno = 0
1307 happy_count = 100
1308 def parse_file (fn):
1309 f = open (fn)
1310 ls = f.readlines ()
1311 ls = map (lambda x: re.sub ("\r$", '', x), ls)
1313 select_voice('default', '')
1314 global lineno
1315 lineno = 0
1316 sys.stderr.write ("Line ... ")
1317 sys.stderr.flush ()
1318 __main__.state = state_list[current_voice_idx]
1320 for ln in ls:
1321 lineno = lineno + 1
1323 if not (lineno % happy_count):
1324 sys.stderr.write ('[%d]'% lineno)
1325 sys.stderr.flush ()
1326 m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice
1327 if m:
1328 if m.group(2):
1329 try_parse_comment(m.group(2))
1330 voices_append ('%% %s\n' % m.group(2))
1331 ln = m.group (1)
1333 orig_ln = ln
1335 ln = try_parse_header_line (ln, state)
1337 # Try nibbling characters off until the line doesn't change.
1338 prev_ln = ''
1339 while ln != prev_ln:
1340 prev_ln = ln
1341 ln = try_parse_chord_delims (ln, state)
1342 ln = try_parse_rest (ln, state)
1343 ln = try_parse_articulation (ln,state)
1344 ln = try_parse_note (ln, state)
1345 ln = try_parse_bar (ln, state)
1346 ln = try_parse_tie (ln)
1347 ln = try_parse_escape (ln)
1348 ln = try_parse_guitar_chord (ln, state)
1349 ln = try_parse_tuplet_begin (ln, state)
1350 ln = try_parse_group_end (ln, state)
1351 ln = try_parse_grace_delims (ln, state)
1352 ln = junk_space (ln, state)
1354 if ln:
1355 error ("%s: %d: Huh? Don't understand\n" % (fn, lineno))
1356 left = orig_ln[0:-len (ln)]
1357 sys.stderr.write (left + '\n')
1358 sys.stderr.write (' ' * len (left) + ln + '\n')
1361 def identify():
1362 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1364 authors = """
1365 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1366 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1369 def print_version ():
1370 print r"""abc2ly (GNU lilypond) %s""" % version
1372 def get_option_parser ():
1373 p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
1374 description=_ ('''abc2ly converts ABC music files (see
1375 %s) to LilyPond input.
1376 ''') % 'http://abcnotation.com/abc2mtex/abc.txt',
1377 add_help_option=False)
1379 p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
1380 p.add_option("--version",
1381 action="version",
1382 help=_ ("show version number and exit"))
1384 p.add_option("-h", "--help",
1385 action="help",
1386 help=_ ("show this help and exit"))
1387 p.add_option ('-o', '--output', metavar='FILE',
1388 help=_ ("write output to FILE"),
1389 action='store')
1390 p.add_option ('-s', '--strict', help=_ ("be strict about success"),
1391 action='store_true')
1392 p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"), action="store_true")
1393 p.add_option_group ('',
1394 description=(
1395 _ ('Report bugs via %s')
1396 % 'http://post.gmane.org/post.php'
1397 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
1398 return p
1401 option_parser = get_option_parser ()
1402 (global_options, files) = option_parser.parse_args ()
1405 identify ()
1407 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1408 for f in files:
1409 if f == '-':
1410 f = ''
1412 sys.stderr.write ('Parsing `%s\'...\n' % f)
1413 parse_file (f)
1415 if not global_options.output:
1416 global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1417 sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
1418 outf = open (global_options.output, 'w')
1420 # don't substitute @VERSION@. We want this to reflect
1421 # the last version that was verified to work.
1422 outf.write ('\\version "2.7.40"\n')
1424 # dump_global (outf)
1425 dump_header (outf, header)
1426 dump_slyrics (outf)
1427 dump_voices (outf)
1428 dump_score (outf)
1429 dump_lyrics (outf)
1430 sys.stderr.write ('\n')