Improve handling of nested overrides using list syntax.
[lilypond/mpolesky.git] / scripts / abc2ly.py
blobc58c6ef9e753b1e85b977d88da30baa52b493242
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 parser_state.parsing_tuplet:
1049 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
1050 if not parser_state.parsing_tuplet:
1051 voices_append ("}")
1052 if slur_begin:
1053 voices_append ('-(' * slur_begin )
1054 if slur_end:
1055 voices_append ('-)' *slur_end )
1057 if global_options.beams and \
1058 str[0] in '^=_ABCDEFGabcdefg' and \
1059 not parser_state.parsing_beam and \
1060 not parser_state.parsing_tuplet:
1061 parser_state.parsing_beam = 1
1062 voices_append_back( '[' )
1064 return str
1066 def junk_space (str,state):
1067 while str and str[0] in '\t\n\r ':
1068 str = str[1:]
1069 close_beam_state(state)
1071 return str
1074 def try_parse_guitar_chord (str, state):
1075 if str[:1] =='"':
1076 str = str[1:]
1077 gc = ''
1078 if str[0] == '_' or (str[0] == '^'):
1079 position = str[0]
1080 str = str[1:]
1081 else:
1082 position = '^'
1083 while str and str[0] != '"':
1084 gc = gc + str[0]
1085 str = str[1:]
1087 if str:
1088 str = str[1:]
1089 gc = re.sub('#', '\\#', gc) # escape '#'s
1090 state.next_articulation = ("%c\"%s\"" % (position, gc)) \
1091 + state.next_articulation
1092 return str
1094 def try_parse_escape (str):
1095 if not str or str [0] != '\\':
1096 return str
1098 str = str[1:]
1099 if str[:1] =='K':
1100 key_table = compute_key ()
1101 return str
1104 # |] thin-thick double bar line
1105 # || thin-thin double bar line
1106 # [| thick-thin double bar line
1107 # :| left repeat
1108 # |: right repeat
1109 # :: left-right repeat
1110 # |1 volta 1
1111 # |2 volta 2
1112 old_bar_dict = {
1113 '|]' : '|.',
1114 '||' : '||',
1115 '[|' : '||',
1116 ':|' : ':|',
1117 '|:' : '|:',
1118 '::' : ':|:',
1119 '|1' : '|',
1120 '|2' : '|',
1121 ':|2' : ':|',
1122 '|' : '|'
1124 bar_dict = {
1125 '|]' : '\\bar "|."',
1126 '||' : '\\bar "||"',
1127 '[|' : '\\bar "||"',
1128 ':|' : '}',
1129 '|:' : '\\repeat volta 2 {',
1130 '::' : '} \\repeat volta 2 {',
1131 '|1' : '} \\alternative{{',
1132 '|2' : '} {',
1133 ':|2' : '} {',
1134 '|' : '\\bar "|"'
1138 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
1139 alternative_opener = ['|1', '|2', ':|2']
1140 repeat_ender = ['::', ':|']
1141 repeat_opener = ['::', '|:']
1142 in_repeat = [''] * 8
1143 doing_alternative = [''] * 8
1144 using_old = ''
1146 def try_parse_bar (str,state):
1147 global in_repeat, doing_alternative, using_old
1148 do_curly = ''
1149 bs = None
1150 if current_voice_idx < 0:
1151 select_voice ('default', '')
1152 # first try the longer one
1153 for trylen in [3,2,1]:
1154 if str[:trylen] and bar_dict.has_key (str[:trylen]):
1155 s = str[:trylen]
1156 if using_old:
1157 bs = "\\bar \"%s\"" % old_bar_dict[s]
1158 else:
1159 bs = "%s" % bar_dict[s]
1160 str = str[trylen:]
1161 if s in alternative_opener:
1162 if not in_repeat[current_voice_idx]:
1163 using_old = 't'
1164 bs = "\\bar \"%s\"" % old_bar_dict[s]
1165 else:
1166 doing_alternative[current_voice_idx] = 't'
1168 if s in repeat_ender:
1169 if not in_repeat[current_voice_idx]:
1170 sys.stderr.write("Warning: inserting repeat to beginning of notes.\n")
1171 repeat_prepend()
1172 in_repeat[current_voice_idx] = ''
1173 else:
1174 if doing_alternative[current_voice_idx]:
1175 do_curly = 't'
1176 if using_old:
1177 bs = "\\bar \"%s\"" % old_bar_dict[s]
1178 else:
1179 bs = bar_dict[s]
1180 doing_alternative[current_voice_idx] = ''
1181 in_repeat[current_voice_idx] = ''
1182 if s in repeat_opener:
1183 in_repeat[current_voice_idx] = 't'
1184 if using_old:
1185 bs = "\\bar \"%s\"" % old_bar_dict[s]
1186 else:
1187 bs = bar_dict[s]
1188 break
1189 if str[:1] == '|':
1190 state.next_bar = '|\n'
1191 str = str[1:]
1192 clear_bar_acc(state)
1193 close_beam_state(state)
1195 if bs <> None or state.next_bar != '':
1196 if state.parsing_tuplet:
1197 state.parsing_tuplet =0
1198 voices_append ('} ')
1200 if bs <> None:
1201 clear_bar_acc(state)
1202 close_beam_state(state)
1203 voices_append (bs)
1204 if do_curly != '':
1205 voices_append("} ")
1206 do_curly = ''
1207 return str
1209 def try_parse_tie (str):
1210 if str[:1] =='-':
1211 str = str[1:]
1212 voices_append (' ~ ')
1213 return str
1215 def bracket_escape (str, state):
1216 m = re.match ( '^([^\]]*)] *(.*)$', str)
1217 if m:
1218 cmd = m.group (1)
1219 str = m.group (2)
1220 try_parse_header_line (cmd, state)
1221 return str
1223 def try_parse_chord_delims (str, state):
1224 if str[:1] =='[':
1225 str = str[1:]
1226 if re.match('[A-Z]:', str): # bracket escape
1227 return bracket_escape(str, state)
1228 if state.next_bar:
1229 voices_append(state.next_bar)
1230 state.next_bar = ''
1231 voices_append ('<<')
1233 if str[:1] == '+':
1234 str = str[1:]
1235 if state.plus_chord:
1236 voices_append ('>>')
1237 state.plus_chord = 0
1238 else:
1239 if state.next_bar:
1240 voices_append(state.next_bar)
1241 state.next_bar = ''
1242 voices_append ('<<')
1243 state.plus_chord = 1
1245 ch = ''
1246 if str[:1] ==']':
1247 str = str[1:]
1248 ch = '>>'
1250 end = 0
1251 while str[:1] ==')':
1252 end = end + 1
1253 str = str[1:]
1256 voices_append ("\\spanrequest \\stop \"slur\"" * end)
1257 voices_append (ch)
1258 return str
1260 def try_parse_grace_delims (str, state):
1261 if str[:1] =='{':
1262 if state.next_bar:
1263 voices_append(state.next_bar)
1264 state.next_bar = ''
1265 str = str[1:]
1266 voices_append ('\\grace { ')
1268 if str[:1] =='}':
1269 str = str[1:]
1270 voices_append ('}')
1272 return str
1274 def try_parse_comment (str):
1275 global nobarlines
1276 if (str[0] == '%'):
1277 if str[0:5] == '%MIDI':
1278 #the nobarlines option is necessary for an abc to lilypond translator for
1279 #exactly the same reason abc2midi needs it: abc requires the user to enter
1280 #the note that will be printed, and MIDI and lilypond expect entry of the
1281 #pitch that will be played.
1283 #In standard 19th century musical notation, the algorithm for translating
1284 #between printed note and pitch involves using the barlines to determine
1285 #the scope of the accidentals.
1287 #Since ABC is frequently used for music in styles that do not use this
1288 #convention, such as most music written before 1700, or ethnic music in
1289 #non-western scales, it is necessary to be able to tell a translator that
1290 #the barlines should not affect its interpretation of the pitch.
1291 if 'nobarlines' in str:
1292 nobarlines = 1
1293 elif str[0:3] == '%LY':
1294 p = str.find ('voices')
1295 if (p > -1):
1296 voices_append(str[p+7:])
1297 voices_append("\n")
1298 p = str.find ('slyrics')
1299 if (p > -1):
1300 slyrics_append(str[p+8:])
1302 #write other kinds of appending if we ever need them.
1303 return str
1305 lineno = 0
1306 happy_count = 100
1307 def parse_file (fn):
1308 f = open (fn)
1309 ls = f.readlines ()
1310 ls = map (lambda x: re.sub ("\r$", '', x), ls)
1312 select_voice('default', '')
1313 global lineno
1314 lineno = 0
1315 sys.stderr.write ("Line ... ")
1316 sys.stderr.flush ()
1317 __main__.state = state_list[current_voice_idx]
1319 for ln in ls:
1320 lineno = lineno + 1
1322 if not (lineno % happy_count):
1323 sys.stderr.write ('[%d]'% lineno)
1324 sys.stderr.flush ()
1325 m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice
1326 if m:
1327 if m.group(2):
1328 try_parse_comment(m.group(2))
1329 voices_append ('%% %s\n' % m.group(2))
1330 ln = m.group (1)
1332 orig_ln = ln
1334 ln = try_parse_header_line (ln, state)
1336 # Try nibbling characters off until the line doesn't change.
1337 prev_ln = ''
1338 while ln != prev_ln:
1339 prev_ln = ln
1340 ln = try_parse_chord_delims (ln, state)
1341 ln = try_parse_rest (ln, state)
1342 ln = try_parse_articulation (ln,state)
1343 ln = try_parse_note (ln, state)
1344 ln = try_parse_bar (ln, state)
1345 ln = try_parse_tie (ln)
1346 ln = try_parse_escape (ln)
1347 ln = try_parse_guitar_chord (ln, state)
1348 ln = try_parse_tuplet_begin (ln, state)
1349 ln = try_parse_group_end (ln, state)
1350 ln = try_parse_grace_delims (ln, state)
1351 ln = junk_space (ln, state)
1353 if ln:
1354 error ("%s: %d: Huh? Don't understand\n" % (fn, lineno))
1355 left = orig_ln[0:-len (ln)]
1356 sys.stderr.write (left + '\n')
1357 sys.stderr.write (' ' * len (left) + ln + '\n')
1360 def identify():
1361 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1363 authors = """
1364 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1365 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1368 def print_version ():
1369 print r"""abc2ly (GNU lilypond) %s""" % version
1371 def get_option_parser ():
1372 p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
1373 description=_ ('''abc2ly converts ABC music files (see
1374 %s) to LilyPond input.
1375 ''') % 'http://abcnotation.com/abc2mtex/abc.txt',
1376 add_help_option=False)
1378 p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
1379 p.add_option("--version",
1380 action="version",
1381 help=_ ("show version number and exit"))
1383 p.add_option("-h", "--help",
1384 action="help",
1385 help=_ ("show this help and exit"))
1386 p.add_option ('-o', '--output', metavar='FILE',
1387 help=_ ("write output to FILE"),
1388 action='store')
1389 p.add_option ('-s', '--strict', help=_ ("be strict about success"),
1390 action='store_true')
1391 p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"), action="store_true")
1392 p.add_option_group ('',
1393 description=(
1394 _ ('Report bugs via %s')
1395 % 'http://post.gmane.org/post.php'
1396 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
1397 return p
1400 option_parser = get_option_parser ()
1401 (global_options, files) = option_parser.parse_args ()
1404 identify ()
1406 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1407 for f in files:
1408 if f == '-':
1409 f = ''
1411 sys.stderr.write ('Parsing `%s\'...\n' % f)
1412 parse_file (f)
1414 if not global_options.output:
1415 global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1416 sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
1417 outf = open (global_options.output, 'w')
1419 # don't substitute @VERSION@. We want this to reflect
1420 # the last version that was verified to work.
1421 outf.write ('\\version "2.7.40"\n')
1423 # dump_global (outf)
1424 dump_header (outf, header)
1425 dump_slyrics (outf)
1426 dump_voices (outf)
1427 dump_score (outf)
1428 dump_lyrics (outf)
1429 sys.stderr.write ('\n')