Format and improve documentation of internal grob properties.
[lilypond.git] / scripts / abc2ly.py
blob2693817e92c0722d1ffe42a4a5cc5ee802826181
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
3 # once upon a rainy monday afternoon.
5 # ...
7 # (not finished.)
8 # ABC standard v1.6: http://www.walshaw.plus.com/abc/
10 # Enhancements (Roy R. Rankin)
12 # Header section moved to top of lilypond file
13 # handle treble, treble-8, alto, and bass clef
14 # Handle voices (V: headers) with clef and part names, multiple voices
15 # Handle w: lyrics with multiple verses
16 # Handle key mode names for minor, major, phrygian, ionian, locrian, aeolian,
17 # mixolydian, lydian, dorian
18 # Handle part names from V: header
19 # Tuplets handling fixed up
20 # Lines starting with |: not discarded as header lines
21 # Multiple T: and C: header entries handled
22 # Accidental maintained until next bar check
23 # Silent rests supported
24 # articulations fermata, upbow, downbow, ltoe, accent, tenuto supported
25 # Chord strings([-^]"string") can contain a '#'
26 # Header fields enclosed by [] in notes string processed
27 # W: words output after tune as abc2ps does it (they failed before)
29 # Enhancements (Laura Conrad)
31 # Barring now preserved between ABC and lilypond
32 # the default placement for text in abc is above the staff.
33 # %%LY now supported.
34 # \breve and \longa supported.
35 # M:none doesn't crash lily.
36 # lilypond '--' supported.
38 # Enhancements (Guy Gascoigne-Piggford)
40 # Add support for maintaining ABC's notion of beaming, this is selectable
41 # from the command line with a -b or --beam option.
42 # Fixd a problem where on cygwin empty lines weren't being correctly identifed
43 # and so were complaining, but still generating the correct output.
45 # Limitations
47 # Multiple tunes in single file not supported
48 # Blank T: header lines should write score and open a new score
49 # Not all header fields supported
50 # ABC line breaks are ignored
51 # Block comments generate error and are ignored
52 # Postscript commands are ignored
53 # lyrics not resynchronized by line breaks (lyrics must fully match notes)
54 # %%LY slyrics can't be directly before a w: line.
55 # ???
59 #TODO:
61 # * lilylib
62 # * GNU style messages: warning:FILE:LINE:
63 # * l10n
65 # Convert to new chord styles.
67 # UNDEF -> None
71 import __main__
72 import getopt
73 import sys
74 import re
75 import string
76 import os
78 program_name = sys.argv[0]
81 """
82 @relocate-preamble@
83 """
85 import lilylib as ly
86 global _;_=ly._
88 version = '@TOPLEVEL_VERSION@'
89 if version == '@' + 'TOPLEVEL_VERSION' + '@':
90 version = '(unknown version)' # uGUHGUHGHGUGH
92 UNDEF = 255
93 state = UNDEF
94 voice_idx_dict = {}
95 header = {}
96 header['footnotes'] = ''
97 lyrics = []
98 slyrics = []
99 voices = []
100 state_list = []
101 repeat_state = [0] * 8
102 current_voice_idx = -1
103 current_lyric_idx = -1
104 lyric_idx = -1
105 part_names = 0
106 default_len = 8
107 length_specified = 0
108 nobarlines = 0
109 global_key = [0] * 7 # UGH
110 names = ["One", "Two", "Three"]
111 DIGITS='0123456789'
112 HSPACE=' \t'
113 midi_specs = ''
116 def error (msg):
117 sys.stderr.write (msg)
118 if global_options.strict:
119 sys.exit (1)
122 def alphabet (i):
123 return chr (i + ord('A'))
125 def check_clef(s):
126 if not s:
127 return ''
128 if re.match('-8va', s) or re.match('treble8', s):
129 # treble8 is used by abctab2ps; -8va is used by barfly,
130 # and by my patch to abc2ps. If there's ever a standard
131 # about this we'll support that.
132 s = s[4:]
133 state.base_octave = -1
134 voices_append("\\clef \"G_8\"\n")
135 elif re.match('^treble', s):
136 s = s[6:]
137 if re.match ('^-8', s):
138 s = s[2:]
139 state.base_octave = -2
140 voices_append("\\clef \"G_8\"\n")
141 else:
142 state.base_octave = 0
143 voices_append("\\clef treble\n")
144 elif re.match('^alto', s):
145 s = s[4:]
146 state.base_octave = -1
147 voices_append ("\\clef alto\n" )
148 elif re.match('^bass',s ):
149 s = s[4:]
150 state.base_octave = -2
151 voices_append ("\\clef bass\n" )
152 return s
154 def select_voice (name, rol):
155 if not voice_idx_dict.has_key (name):
156 state_list.append(Parser_state())
157 voices.append ('')
158 slyrics.append ([])
159 voice_idx_dict[name] = len (voices) -1
160 __main__.current_voice_idx = voice_idx_dict[name]
161 __main__.state = state_list[current_voice_idx]
162 while rol != '':
163 m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork
164 if m:
165 keyword = m.group(1)
166 rol = m.group (2)
167 a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol)
168 if a:
169 value = a.group (1)
170 rol = a.group ( 2)
171 if keyword == 'clef':
172 check_clef(value)
173 elif keyword == "name":
174 value = re.sub ('\\\\','\\\\\\\\', value)
175 ## < 2.2
176 voices_append ("\\set Staff.instrument = %s\n" % value )
178 __main__.part_names = 1
179 elif keyword == "sname" or keyword == "snm":
180 voices_append ("\\set Staff.instr = %s\n" % value )
181 else:
182 break
184 def dump_header (outf,hdr):
185 outf.write ('\\header {\n')
186 ks = hdr.keys ()
187 ks.sort ()
188 for k in ks:
189 hdr[k] = re.sub('"', '\\"', hdr[k])
190 outf.write ('\t%s = "%s"\n'% (k,hdr[k]))
191 outf.write ('}')
193 def dump_lyrics (outf):
194 if (len(lyrics)):
195 outf.write("\n\\score\n{\n \\lyrics\n <<\n")
196 for i in range (len (lyrics)):
197 outf.write ( lyrics [i])
198 outf.write ("\n")
199 outf.write(" >>\n \\layout{}\n}\n")
201 def dump_default_bar (outf):
203 Nowadays abc2ly outputs explicits barlines (?)
205 ## < 2.2
206 outf.write ("\n\\set Score.defaultBarType = \"empty\"\n")
209 def dump_slyrics (outf):
210 ks = voice_idx_dict.keys()
211 ks.sort ()
212 for k in ks:
213 if re.match('[1-9]', k):
214 m = alphabet(string.atoi(k))
215 else:
216 m = k
217 for i in range (len(slyrics[voice_idx_dict[k]])):
218 l= alphabet(i)
219 outf.write ("\nwords%sV%s = \lyricmode {" % (m, l))
220 outf.write ("\n" + slyrics [voice_idx_dict[k]][i])
221 outf.write ("\n}")
223 def dump_voices (outf):
224 global doing_alternative, in_repeat
225 ks = voice_idx_dict.keys()
226 ks.sort ()
227 for k in ks:
228 if re.match ('[1-9]', k):
229 m = alphabet(string.atoi(k))
230 else:
231 m = k
232 outf.write ("\nvoice%s = {" % m)
233 dump_default_bar(outf)
234 if repeat_state[voice_idx_dict[k]]:
235 outf.write("\n\\repeat volta 2 {")
236 outf.write ("\n" + voices [voice_idx_dict[k]])
237 if not using_old:
238 if doing_alternative[voice_idx_dict[k]]:
239 outf.write("}")
240 if in_repeat[voice_idx_dict[k]]:
241 outf.write("}")
242 outf.write ("\n}")
244 def try_parse_q(a):
245 global midi_specs
246 #assume that Q takes the form "Q:1/4=120"
247 #There are other possibilities, but they are deprecated
248 if string.count(a, '/') == 1:
249 array=string.split(a,'/')
250 numerator=array[0]
251 if int(numerator) != 1:
252 sys.stderr.write("abc2ly: Warning, unable to translate a Q specification with a numerator of %s: %s\n" % (numerator, a))
253 array2=string.split(array[1],'=')
254 denominator=array2[0]
255 perminute=array2[1]
256 duration=str(string.atoi(denominator)/string.atoi(numerator))
257 midi_specs=string.join([" \n\t\t\context {\n\t\t \Score tempoWholesPerMinute = #(ly:make-moment ", perminute, " ", duration, ")\n\t\t }\n"])
258 else:
259 sys.stderr.write("abc2ly: Warning, unable to parse Q specification: %s\n" % a)
261 def dump_score (outf):
262 outf.write (r"""
264 \score{
266 """)
268 ks = voice_idx_dict.keys ();
269 ks.sort ()
270 for k in ks:
271 if re.match('[1-9]', k):
272 m = alphabet (string.atoi(k))
273 else:
274 m = k
275 if k == 'default' and len (voice_idx_dict) > 1:
276 break
277 outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k )
278 if k != 'default':
279 outf.write ("\t \\voicedefault\n")
280 outf.write ("\t \\voice%s " % m)
281 outf.write ("\n\t}\n")
283 l = ord( 'A' )
284 for lyrics in slyrics [voice_idx_dict[k]]:
285 outf.write ("\n\t\\addlyrics { \n")
286 if re.match('[1-9]',k):
287 m = alphabet (string.atoi(k))
288 else:
289 m = k
291 outf.write ( " \\words%sV%s } " % ( m, chr (l)) )
292 l += 1
294 outf.write ("\n >>")
295 outf.write ("\n\t\\layout {\n")
296 outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs)
300 def set_default_length (s):
301 global length_specified
302 m = re.search ('1/([0-9]+)', s)
303 if m:
304 __main__.default_len = string.atoi ( m.group (1))
305 length_specified = 1
307 def set_default_len_from_time_sig (s):
308 m = re.search ('([0-9]+)/([0-9]+)', s)
309 if m:
310 n = string.atoi (m.group (1))
311 d = string.atoi (m.group (2))
312 if (n * 1.0 )/(d * 1.0) < 0.75:
313 __main__.default_len = 16
314 else:
315 __main__.default_len = 8
317 def gulp_file(f):
318 try:
319 i = open(f)
320 i.seek (0, 2)
321 n = i.tell ()
322 i.seek (0,0)
323 except:
324 sys.stderr.write ("cannot open file: `%s'\n" % f)
325 return ''
326 s = i.read (n)
327 if len (s) <= 0:
328 sys.stderr.write ("gulped empty file: `%s'\n" % f)
329 i.close ()
330 return s
333 # pitch manipulation. Tuples are (name, alteration).
334 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
335 # pitch in semitones.
336 def semitone_pitch (tup):
337 p =0
339 t = tup[0]
340 p = p + 12 * (t / 7)
341 t = t % 7
343 if t > 2:
344 p = p- 1
346 p = p + t* 2 + tup[1]
347 return p
349 def fifth_above_pitch (tup):
350 (n, a) = (tup[0] + 4, tup[1])
352 difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
353 a = a + difference
355 return (n,a)
357 def sharp_keys ():
358 p = (0,0)
359 l = []
360 k = 0
361 while 1:
362 l.append (p)
363 (t,a) = fifth_above_pitch (p)
364 if semitone_pitch((t,a)) % 12 == 0:
365 break
367 p = (t % 7, a)
368 return l
370 def flat_keys ():
371 p = (0,0)
372 l = []
373 k = 0
374 while 1:
375 l.append (p)
376 (t,a) = quart_above_pitch (p)
377 if semitone_pitch((t,a)) % 12 == 0:
378 break
380 p = (t % 7, a)
381 return l
383 def quart_above_pitch (tup):
384 (n, a) = (tup[0] + 3, tup[1])
386 difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
387 a = a + difference
389 return (n,a)
391 key_lookup = { # abc to lilypond key mode names
392 'm' : 'minor',
393 'min' : 'minor',
394 'maj' : 'major',
395 'major' : 'major',
396 'phr' : 'phrygian',
397 'ion' : 'ionian',
398 'loc' : 'locrian',
399 'aeo' : 'aeolian',
400 'mix' : 'mixolydian',
401 'mixolydian' : 'mixolydian',
402 'lyd' : 'lydian',
403 'dor' : 'dorian',
404 'dorian' : 'dorian'
407 def lily_key (k):
408 orig = "" + k
409 # UGR
410 k = string.lower (k)
411 key = k[0]
412 #UGH
413 k = k[1:]
414 if k and k[0] == '#':
415 key = key + 'is'
416 k = k[1:]
417 elif k and k[0] == 'b':
418 key = key + 'es'
419 k = k[1:]
420 if not k:
421 return '%s \\major' % key
423 type = k[0:3]
424 if not key_lookup.has_key (type):
425 #ugh, use lilylib, say WARNING:FILE:LINE:
426 sys.stderr.write ("abc2ly:warning:")
427 sys.stderr.write ("ignoring unknown key: `%s'" % orig)
428 sys.stderr.write ('\n')
429 return 0
430 return ("%s \\%s" % ( key, key_lookup[type]))
432 def shift_key (note, acc, shift):
433 s = semitone_pitch((note, acc))
434 s = (s + shift + 12) % 12
435 if s <= 4:
436 n = s / 2
437 a = s % 2
438 else:
439 n = (s + 1) / 2
440 a = (s + 1) % 2
441 if a:
442 n = n + 1
443 a = -1
444 return (n,a)
446 key_shift = { # semitone shifts for key mode names
447 'm' : 3,
448 'min' : 3,
449 'minor' : 3,
450 'maj' : 0,
451 'major' : 0,
452 'phr' : -4,
453 'phrygian' : -4,
454 'ion' : 0,
455 'ionian' : 0,
456 'loc' : 1,
457 'locrian' : 1,
458 'aeo' : 3,
459 'aeolian' : 3,
460 'mix' : 5,
461 'mixolydian' : 5,
462 'lyd' : -5,
463 'lydian' : -5,
464 'dor' : -2,
465 'dorian' : -2
467 def compute_key (k):
468 k = string.lower (k)
469 intkey = (ord (k[0]) - ord('a') + 5) % 7
470 intkeyacc =0
471 k = k[1:]
473 if k and k[0] == 'b':
474 intkeyacc = -1
475 k = k[1:]
476 elif k and k[0] == '#':
477 intkeyacc = 1
478 k = k[1:]
479 k = k[0:3]
480 if k and key_shift.has_key(k):
481 (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k])
482 keytup = (intkey, intkeyacc)
484 sharp_key_seq = sharp_keys ()
485 flat_key_seq = flat_keys ()
487 accseq = None
488 accsign = 0
489 if keytup in sharp_key_seq:
490 accsign = 1
491 key_count = sharp_key_seq.index (keytup)
492 accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1))
494 elif keytup in flat_key_seq:
495 accsign = -1
496 key_count = flat_key_seq.index (keytup)
497 accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
498 else:
499 error ("Huh?")
500 raise "Huh"
502 key_table = [0] * 7
503 for a in accseq:
504 key_table[a] = key_table[a] + accsign
506 return key_table
508 tup_lookup = {
509 '2' : '3/2',
510 '3' : '2/3',
511 '4' : '4/3',
512 '5' : '4/5',
513 '6' : '4/6',
514 '7' : '6/7',
515 '9' : '8/9',
519 def try_parse_tuplet_begin (str, state):
520 if re.match ('\([2-9]', str):
521 dig = str[1]
522 str = str[2:]
523 prev_tuplet_state = state.parsing_tuplet
524 state.parsing_tuplet = string.atoi (dig[0])
525 if prev_tuplet_state:
526 voices_append ("}")
527 voices_append ("\\times %s {" % tup_lookup[dig])
528 return str
530 def try_parse_group_end (str, state):
531 if str and str[0] in HSPACE:
532 str = str[1:]
533 close_beam_state(state)
534 return str
536 def header_append (key, a):
537 s = ''
538 if header.has_key (key):
539 s = header[key] + "\n"
540 header [key] = s + a
542 def wordwrap(a, v):
543 linelen = len (v) - string.rfind(v, '\n')
544 if linelen + len (a) > 80:
545 v = v + '\n'
546 return v + a + ' '
548 def stuff_append (stuff, idx, a):
549 if not stuff:
550 stuff.append (a)
551 else:
552 stuff [idx] = wordwrap(a, stuff[idx])
554 # ignore wordwrap since we are adding to the previous word
555 def stuff_append_back(stuff, idx, a):
556 if not stuff:
557 stuff.append (a)
558 else:
559 point = len(stuff[idx])-1
560 while stuff[idx][point] is ' ':
561 point = point - 1
562 point = point +1
563 stuff[idx] = stuff[idx][:point] + a + stuff[idx][point:]
565 def voices_append(a):
566 if current_voice_idx < 0:
567 select_voice ('default', '')
568 stuff_append (voices, current_voice_idx, a)
570 # word wrap really makes it hard to bind beams to the end of notes since it
571 # pushes out whitespace on every call. The _back functions do an append
572 # prior to the last space, effectively tagging whatever they are given
573 # onto the last note
574 def voices_append_back(a):
575 if current_voice_idx < 0:
576 select_voice ('default', '')
577 stuff_append_back(voices, current_voice_idx, a)
579 def repeat_prepend():
580 global repeat_state
581 if current_voice_idx < 0:
582 select_voice ('default', '')
583 if not using_old:
584 repeat_state[current_voice_idx] = 't'
587 def lyrics_append(a):
588 a = re.sub ('#', '\\#', a) # latex does not like naked #'s
589 a = re.sub ('"', '\\"', a) # latex does not like naked "'s
590 a = '\t{ "' + a + '" }\n'
591 stuff_append (lyrics, current_lyric_idx, a)
593 # break lyrics to words and put "'s around words containing numbers and '"'s
594 def fix_lyric(str):
595 ret = ''
596 while str != '':
597 m = re.match('[ \t]*([^ \t]*)[ \t]*(.*$)', str)
598 if m:
599 word = m.group(1)
600 str = m.group(2)
601 word = re.sub('"', '\\"', word) # escape "
602 if re.match('.*[0-9"\(]', word):
603 word = re.sub('_', ' ', word) # _ causes probs inside ""
604 ret = ret + '\"' + word + '\" '
605 else:
606 ret = ret + word + ' '
607 else:
608 return (ret)
609 return (ret)
611 def slyrics_append(a):
612 a = re.sub ( '_', ' _ ', a) # _ to ' _ '
613 a = re.sub ( '([^-])-([^-])', '\\1- \\2', a) # split words with "-" unless was originally "--"
614 a = re.sub ( '\\\\- ', '-', a) # unless \-
615 a = re.sub ( '~', '_', a) # ~ to space('_')
616 a = re.sub ( '\*', '_ ', a) # * to to space
617 a = re.sub ( '#', '\\#', a) # latex does not like naked #'s
618 if re.match('.*[0-9"\(]', a): # put numbers and " and ( into quoted string
619 a = fix_lyric(a)
620 a = re.sub ( '$', ' ', a) # insure space between lines
621 __main__.lyric_idx = lyric_idx + 1
622 if len(slyrics[current_voice_idx]) <= lyric_idx:
623 slyrics[current_voice_idx].append(a)
624 else:
625 v = slyrics[current_voice_idx][lyric_idx]
626 slyrics[current_voice_idx][lyric_idx] = wordwrap(a, slyrics[current_voice_idx][lyric_idx])
629 def try_parse_header_line (ln, state):
630 global length_specified
631 m = re.match ('^([A-Za-z]): *(.*)$', ln)
633 if m:
634 g =m.group (1)
635 a = m.group (2)
636 if g == 'T': #title
637 a = re.sub('[ \t]*$','', a) #strip trailing blanks
638 if header.has_key('title'):
639 if a:
640 if len(header['title']):
641 # the non-ascii character
642 # in the string below is a
643 # punctuation dash. (TeX ---)
644 header['title'] = header['title'] + ' — ' + a
645 else:
646 header['subtitle'] = a
647 else:
648 header['title'] = a
649 if g == 'M': # Meter
650 if a == 'C':
651 if not state.common_time:
652 state.common_time = 1
653 voices_append (" \\override Staff.TimeSignature #\'style = #'C\n")
654 a = '4/4'
655 if a == 'C|':
656 if not state.common_time:
657 state.common_time = 1
658 voices_append ("\\override Staff.TimeSignature #\'style = #'C\n")
659 a = '2/2'
660 if not length_specified:
661 set_default_len_from_time_sig (a)
662 else:
663 length_specified = 0
664 if not a == 'none':
665 voices_append ('\\time %s' % a)
666 state.next_bar = ''
667 if g == 'K': # KEY
668 a = check_clef(a)
669 if a:
670 m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info
671 if m:
672 # there may or may not be a space
673 # between the key letter and the mode
674 if key_lookup.has_key(m.group(2)[0:3]):
675 key_info = m.group(1) + m.group(2)[0:3]
676 clef_info = m.group(2)[4:]
677 else:
678 key_info = m.group(1)
679 clef_info = m.group(2)
680 __main__.global_key = compute_key (key_info)
681 k = lily_key (key_info)
682 if k:
683 voices_append ('\\key %s' % k)
684 check_clef(clef_info)
685 else:
686 __main__.global_key = compute_key (a)
687 k = lily_key (a)
688 if k:
689 voices_append ('\\key %s \\major' % k)
690 if g == 'N': # Notes
691 header ['footnotes'] = header['footnotes'] + '\\\\\\\\' + a
692 if g == 'O': # Origin
693 header ['origin'] = a
694 if g == 'X': # Reference Number
695 header ['crossRefNumber'] = a
696 if g == 'A': # Area
697 header ['area'] = a
698 if g == 'H': # History
699 header_append ('history', a)
700 if g == 'B': # Book
701 header ['book'] = a
702 if g == 'C': # Composer
703 if header.has_key('composer'):
704 if a:
705 header['composer'] = header['composer'] + '\\\\\\\\' + a
706 else:
707 header['composer'] = a
708 if g == 'S':
709 header ['subtitle'] = a
710 if g == 'L': # Default note length
711 set_default_length (ln)
712 if g == 'V': # Voice
713 voice = re.sub (' .*$', '', a)
714 rest = re.sub ('^[^ \t]* *', '', a)
715 if state.next_bar:
716 voices_append(state.next_bar)
717 state.next_bar = ''
718 select_voice (voice, rest)
719 if g == 'W': # Words
720 lyrics_append(a)
721 if g == 'w': # vocals
722 slyrics_append (a)
723 if g == 'Q': #tempo
724 try_parse_q (a)
725 return ''
726 return ln
728 # we use in this order specified accidental, active accidental for bar,
729 # active accidental for key
730 def pitch_to_lilypond_name (name, acc, bar_acc, key):
731 s = ''
732 if acc == UNDEF:
733 if not nobarlines:
734 acc = bar_acc
735 if acc == UNDEF:
736 acc = key
737 if acc == -1:
738 s = 'es'
739 elif acc == 1:
740 s = 'is'
742 if name > 4:
743 name = name -7
744 return(chr (name + ord('c')) + s)
747 def octave_to_lilypond_quotes (o):
748 o = o + 2
749 s =''
750 if o < 0:
751 o = -o
752 s=','
753 else:
754 s ='\''
756 return s * o
758 def parse_num (str):
759 durstr = ''
760 while str and str[0] in DIGITS:
761 durstr = durstr + str[0]
762 str = str[1:]
764 n = None
765 if durstr:
766 n =string.atoi (durstr)
767 return (str,n)
770 def duration_to_lilypond_duration (multiply_tup, defaultlen, dots):
771 base = 1
772 # (num / den) / defaultlen < 1/base
773 while base * multiply_tup[0] < multiply_tup[1]:
774 base = base * 2
775 if base == 1:
776 if (multiply_tup[0] / multiply_tup[1]) == 2:
777 base = '\\breve'
778 if (multiply_tup[0] / multiply_tup[1]) == 3:
779 base = '\\breve'
780 dots = 1
781 if (multiply_tup[0] / multiply_tup[1]) == 4:
782 base = '\longa'
783 return '%s%s' % ( base, '.'* dots)
785 class Parser_state:
786 def __init__ (self):
787 self.in_acc = {}
788 self.next_articulation = ''
789 self.next_bar = ''
790 self.next_dots = 0
791 self.next_den = 1
792 self.parsing_tuplet = 0
793 self.plus_chord = 0
794 self.base_octave = 0
795 self.common_time = 0
796 self.parsing_beam = 0
800 # return (str, num,den,dots)
801 def parse_duration (str, parser_state):
802 num = 0
803 den = parser_state.next_den
804 parser_state.next_den = 1
806 (str, num) = parse_num (str)
807 if not num:
808 num = 1
809 if len(str):
810 if str[0] == '/':
811 if len(str[0]):
812 while str[:1] == '/':
813 str= str[1:]
814 d = 2
815 if str[0] in DIGITS:
816 (str, d) =parse_num (str)
818 den = den * d
820 den = den * default_len
822 current_dots = parser_state.next_dots
823 parser_state.next_dots = 0
824 if re.match ('[ \t]*[<>]', str):
825 while str[0] in HSPACE:
826 str = str[1:]
827 while str[0] == '>':
828 str = str [1:]
829 current_dots = current_dots + 1
830 parser_state.next_den = parser_state.next_den * 2
832 while str[0] == '<':
833 str = str [1:]
834 den = den * 2
835 parser_state.next_dots = parser_state.next_dots + 1
839 try_dots = [3, 2, 1]
840 for d in try_dots:
841 f = 1 << d
842 multiplier = (2*f-1)
843 if num % multiplier == 0 and den % f == 0:
844 num = num / multiplier
845 den = den / f
846 current_dots = current_dots + d
848 return (str, num,den,current_dots)
851 def try_parse_rest (str, parser_state):
852 if not str or str[0] <> 'z' and str[0] <> 'x':
853 return str
855 __main__.lyric_idx = -1
857 if parser_state.next_bar:
858 voices_append(parser_state.next_bar)
859 parser_state.next_bar = ''
861 if str[0] == 'z':
862 rest = 'r'
863 else:
864 rest = 's'
865 str = str[1:]
867 (str, num,den,d) = parse_duration (str, parser_state)
868 voices_append ('%s%s' % (rest, duration_to_lilypond_duration ((num,den), default_len, d)))
869 if parser_state.next_articulation:
870 voices_append (parser_state.next_articulation)
871 parser_state.next_articulation = ''
873 return str
875 artic_tbl = {
876 '.' : '-.',
877 'T' : '^\\trill',
878 'H' : '^\\fermata',
879 'u' : '^\\upbow',
880 'K' : '^\\ltoe',
881 'k' : '^\\accent',
882 'M' : '^\\tenuto',
883 '~' : '^"~" ',
884 'J' : '', # ignore slide
885 'R' : '', # ignore roll
886 'S' : '^\\segno',
887 'O' : '^\\coda',
888 'v' : '^\\downbow'
891 def try_parse_articulation (str, state):
892 while str and artic_tbl.has_key(str[:1]):
893 state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
894 if not artic_tbl[str[:1]]:
895 sys.stderr.write("Warning: ignoring `%s'\n" % str[:1] )
897 str = str[1:]
901 # s7m2 input doesnt care about spaces
902 if re.match('[ \t]*\(', str):
903 str = string.lstrip (str)
905 slur_begin =0
906 while str[:1] =='(' and str[1] not in DIGITS:
907 slur_begin = slur_begin + 1
908 state.next_articulation = state.next_articulation + '('
909 str = str[1:]
911 return str
914 # remember accidental for rest of bar
916 def set_bar_acc(note, octave, acc, state):
917 if acc == UNDEF:
918 return
919 n_oct = note + octave * 7
920 state.in_acc[n_oct] = acc
922 # get accidental set in this bar or UNDEF if not set
923 def get_bar_acc(note, octave, state):
924 n_oct = note + octave * 7
925 if state.in_acc.has_key(n_oct):
926 return(state.in_acc[n_oct])
927 else:
928 return(UNDEF)
930 def clear_bar_acc(state):
931 for k in state.in_acc.keys():
932 del state.in_acc[k]
935 # if we are parsing a beam, close it off
936 def close_beam_state(state):
937 if state.parsing_beam and global_options.beams:
938 state.parsing_beam = 0
939 voices_append_back( ']' )
942 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP !
943 def try_parse_note (str, parser_state):
944 mud = ''
946 slur_begin =0
947 if not str:
948 return str
950 articulation =''
951 acc = UNDEF
952 if str[0] in '^=_':
953 c = str[0]
954 str = str[1:]
955 if c == '^':
956 acc = 1
957 if c == '=':
958 acc = 0
959 if c == '_':
960 acc = -1
962 octave = parser_state.base_octave
963 if str[0] in "ABCDEFG":
964 str = string.lower (str[0]) + str[1:]
965 octave = octave - 1
968 notename = 0
969 if str[0] in "abcdefg":
970 notename = (ord(str[0]) - ord('a') + 5)%7
971 str = str[1:]
972 else:
973 return str # failed; not a note!
976 __main__.lyric_idx = -1
978 if parser_state.next_bar:
979 voices_append(parser_state.next_bar)
980 parser_state.next_bar = ''
982 while str[0] == ',':
983 octave = octave - 1
984 str = str[1:]
985 while str[0] == '\'':
986 octave = octave + 1
987 str = str[1:]
989 (str, num,den,current_dots) = parse_duration (str, parser_state)
991 if re.match('[ \t]*\)', str):
992 str = string.lstrip (str)
994 slur_end =0
995 while str[:1] ==')':
996 slur_end = slur_end + 1
997 str = str[1:]
1000 bar_acc = get_bar_acc(notename, octave, parser_state)
1001 pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename])
1002 oct = octave_to_lilypond_quotes (octave)
1003 if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
1004 mod='!'
1005 else:
1006 mod = ''
1007 voices_append ("%s%s%s%s" %
1008 (pit, oct, mod,
1009 duration_to_lilypond_duration ((num,den), default_len, current_dots)))
1011 set_bar_acc(notename, octave, acc, parser_state)
1012 if parser_state.next_articulation:
1013 articulation = articulation + parser_state.next_articulation
1014 parser_state.next_articulation = ''
1016 voices_append (articulation)
1018 if parser_state.parsing_tuplet:
1019 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
1020 if not parser_state.parsing_tuplet:
1021 voices_append ("}")
1022 if slur_begin:
1023 voices_append ('-(' * slur_begin )
1024 if slur_end:
1025 voices_append ('-)' *slur_end )
1027 if global_options.beams and \
1028 str[0] in '^=_ABCDEFGabcdefg' and \
1029 not parser_state.parsing_beam and \
1030 not parser_state.parsing_tuplet:
1031 parser_state.parsing_beam = 1
1032 voices_append_back( '[' )
1034 return str
1036 def junk_space (str,state):
1037 while str and str[0] in '\t\n\r ':
1038 str = str[1:]
1039 close_beam_state(state)
1041 return str
1044 def try_parse_guitar_chord (str, state):
1045 if str[:1] =='"':
1046 str = str[1:]
1047 gc = ''
1048 if str[0] == '_' or (str[0] == '^'):
1049 position = str[0]
1050 str = str[1:]
1051 else:
1052 position = '^'
1053 while str and str[0] != '"':
1054 gc = gc + str[0]
1055 str = str[1:]
1057 if str:
1058 str = str[1:]
1059 gc = re.sub('#', '\\#', gc) # escape '#'s
1060 state.next_articulation = ("%c\"%s\"" % (position, gc)) \
1061 + state.next_articulation
1062 return str
1064 def try_parse_escape (str):
1065 if not str or str [0] != '\\':
1066 return str
1068 str = str[1:]
1069 if str[:1] =='K':
1070 key_table = compute_key ()
1071 return str
1074 # |] thin-thick double bar line
1075 # || thin-thin double bar line
1076 # [| thick-thin double bar line
1077 # :| left repeat
1078 # |: right repeat
1079 # :: left-right repeat
1080 # |1 volta 1
1081 # |2 volta 2
1082 old_bar_dict = {
1083 '|]' : '|.',
1084 '||' : '||',
1085 '[|' : '||',
1086 ':|' : ':|',
1087 '|:' : '|:',
1088 '::' : ':|:',
1089 '|1' : '|',
1090 '|2' : '|',
1091 ':|2' : ':|',
1092 '|' : '|'
1094 bar_dict = {
1095 '|]' : '\\bar "|."',
1096 '||' : '\\bar "||"',
1097 '[|' : '\\bar "||"',
1098 ':|' : '}',
1099 '|:' : '\\repeat volta 2 {',
1100 '::' : '} \\repeat volta 2 {',
1101 '|1' : '} \\alternative{{',
1102 '|2' : '} {',
1103 ':|2' : '} {',
1104 '|' : '\\bar "|"'
1108 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
1109 alternative_opener = ['|1', '|2', ':|2']
1110 repeat_ender = ['::', ':|']
1111 repeat_opener = ['::', '|:']
1112 in_repeat = [''] * 8
1113 doing_alternative = [''] * 8
1114 using_old = ''
1116 def try_parse_bar (str,state):
1117 global in_repeat, doing_alternative, using_old
1118 do_curly = ''
1119 bs = None
1120 if current_voice_idx < 0:
1121 select_voice ('default', '')
1122 # first try the longer one
1123 for trylen in [3,2,1]:
1124 if str[:trylen] and bar_dict.has_key (str[:trylen]):
1125 s = str[:trylen]
1126 if using_old:
1127 bs = "\\bar \"%s\"" % old_bar_dict[s]
1128 else:
1129 bs = "%s" % bar_dict[s]
1130 str = str[trylen:]
1131 if s in alternative_opener:
1132 if not in_repeat[current_voice_idx]:
1133 using_old = 't'
1134 bs = "\\bar \"%s\"" % old_bar_dict[s]
1135 else:
1136 doing_alternative[current_voice_idx] = 't'
1138 if s in repeat_ender:
1139 if not in_repeat[current_voice_idx]:
1140 sys.stderr.write("Warning: inserting repeat to beginning of notes.\n")
1141 repeat_prepend()
1142 in_repeat[current_voice_idx] = ''
1143 else:
1144 if doing_alternative[current_voice_idx]:
1145 do_curly = 't'
1146 if using_old:
1147 bs = "\\bar \"%s\"" % old_bar_dict[s]
1148 else:
1149 bs = bar_dict[s]
1150 doing_alternative[current_voice_idx] = ''
1151 in_repeat[current_voice_idx] = ''
1152 if s in repeat_opener:
1153 in_repeat[current_voice_idx] = 't'
1154 if using_old:
1155 bs = "\\bar \"%s\"" % old_bar_dict[s]
1156 else:
1157 bs = bar_dict[s]
1158 break
1159 if str[:1] == '|':
1160 state.next_bar = '|\n'
1161 str = str[1:]
1162 clear_bar_acc(state)
1163 close_beam_state(state)
1165 if bs <> None or state.next_bar != '':
1166 if state.parsing_tuplet:
1167 state.parsing_tuplet =0
1168 voices_append ('} ')
1170 if bs <> None:
1171 clear_bar_acc(state)
1172 close_beam_state(state)
1173 voices_append (bs)
1174 if do_curly != '':
1175 voices_append("} ")
1176 do_curly = ''
1177 return str
1179 def try_parse_tie (str):
1180 if str[:1] =='-':
1181 str = str[1:]
1182 voices_append (' ~ ')
1183 return str
1185 def bracket_escape (str, state):
1186 m = re.match ( '^([^\]]*)] *(.*)$', str)
1187 if m:
1188 cmd = m.group (1)
1189 str = m.group (2)
1190 try_parse_header_line (cmd, state)
1191 return str
1193 def try_parse_chord_delims (str, state):
1194 if str[:1] =='[':
1195 str = str[1:]
1196 if re.match('[A-Z]:', str): # bracket escape
1197 return bracket_escape(str, state)
1198 if state.next_bar:
1199 voices_append(state.next_bar)
1200 state.next_bar = ''
1201 voices_append ('<<')
1203 if str[:1] == '+':
1204 str = str[1:]
1205 if state.plus_chord:
1206 voices_append ('>>')
1207 state.plus_chord = 0
1208 else:
1209 if state.next_bar:
1210 voices_append(state.next_bar)
1211 state.next_bar = ''
1212 voices_append ('<<')
1213 state.plus_chord = 1
1215 ch = ''
1216 if str[:1] ==']':
1217 str = str[1:]
1218 ch = '>>'
1220 end = 0
1221 while str[:1] ==')':
1222 end = end + 1
1223 str = str[1:]
1226 voices_append ("\\spanrequest \\stop \"slur\"" * end)
1227 voices_append (ch)
1228 return str
1230 def try_parse_grace_delims (str, state):
1231 if str[:1] =='{':
1232 if state.next_bar:
1233 voices_append(state.next_bar)
1234 state.next_bar = ''
1235 str = str[1:]
1236 voices_append ('\\grace { ')
1238 if str[:1] =='}':
1239 str = str[1:]
1240 voices_append ('}')
1242 return str
1244 def try_parse_comment (str):
1245 global nobarlines
1246 if (str[0] == '%'):
1247 if str[0:5] == '%MIDI':
1248 #the nobarlines option is necessary for an abc to lilypond translator for
1249 #exactly the same reason abc2midi needs it: abc requires the user to enter
1250 #the note that will be printed, and MIDI and lilypond expect entry of the
1251 #pitch that will be played.
1253 #In standard 19th century musical notation, the algorithm for translating
1254 #between printed note and pitch involves using the barlines to determine
1255 #the scope of the accidentals.
1257 #Since ABC is frequently used for music in styles that do not use this
1258 #convention, such as most music written before 1700, or ethnic music in
1259 #non-western scales, it is necessary to be able to tell a translator that
1260 #the barlines should not affect its interpretation of the pitch.
1261 if 'nobarlines' in str:
1262 nobarlines = 1
1263 elif str[0:3] == '%LY':
1264 p = string.find(str, 'voices')
1265 if (p > -1):
1266 voices_append(str[p+7:])
1267 voices_append("\n")
1268 p = string.find(str, 'slyrics')
1269 if (p > -1):
1270 slyrics_append(str[p+8:])
1272 #write other kinds of appending if we ever need them.
1273 return str
1275 lineno = 0
1276 happy_count = 100
1277 def parse_file (fn):
1278 f = open (fn)
1279 ls = f.readlines ()
1280 ls = map (lambda x: re.sub ("\r$", '', x), ls)
1282 select_voice('default', '')
1283 global lineno
1284 lineno = 0
1285 sys.stderr.write ("Line ... ")
1286 sys.stderr.flush ()
1287 __main__.state = state_list[current_voice_idx]
1289 for ln in ls:
1290 lineno = lineno + 1
1292 if not (lineno % happy_count):
1293 sys.stderr.write ('[%d]'% lineno)
1294 sys.stderr.flush ()
1295 m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice
1296 if m:
1297 if m.group(2):
1298 try_parse_comment(m.group(2))
1299 voices_append ('%% %s\n' % m.group(2))
1300 ln = m.group (1)
1302 orig_ln = ln
1304 ln = try_parse_header_line (ln, state)
1306 # Try nibbling characters off until the line doesn't change.
1307 prev_ln = ''
1308 while ln != prev_ln:
1309 prev_ln = ln
1310 ln = try_parse_chord_delims (ln, state)
1311 ln = try_parse_rest (ln, state)
1312 ln = try_parse_articulation (ln,state)
1313 ln = try_parse_note (ln, state)
1314 ln = try_parse_bar (ln, state)
1315 ln = try_parse_tie (ln)
1316 ln = try_parse_escape (ln)
1317 ln = try_parse_guitar_chord (ln, state)
1318 ln = try_parse_tuplet_begin (ln, state)
1319 ln = try_parse_group_end (ln, state)
1320 ln = try_parse_grace_delims (ln, state)
1321 ln = junk_space (ln, state)
1323 if ln:
1324 error ("%s: %d: Huh? Don't understand\n" % (fn, lineno))
1325 left = orig_ln[0:-len (ln)]
1326 sys.stderr.write (left + '\n')
1327 sys.stderr.write (' ' * len (left) + ln + '\n')
1330 def identify():
1331 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1333 authors = """
1334 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1335 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1338 def print_version ():
1339 print r"""abc2ly (GNU lilypond) %s""" % version
1341 def get_option_parser ():
1342 p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
1343 version="abc2ly (LilyPond) @TOPLEVEL_VERSION@",
1344 description=_ ('''abc2ly converts ABC music files (see
1345 %s) to LilyPond input.''') % 'http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt')
1347 p.add_option ('-o', '--output', metavar='FILE',
1348 help=_ ("write output to FILE"),
1349 action='store')
1350 p.add_option ('-s', '--strict', help=_ ("be strict about succes"),
1351 action='store_true')
1352 p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"))
1353 p.add_option_group ('bugs',
1354 description=(_ ('Report bugs via')
1355 + ''' http://post.gmane.org/post.php'''
1356 '''?group=gmane.comp.gnu.lilypond.bugs\n'''))
1357 return p
1360 option_parser = get_option_parser ()
1361 (global_options, files) = option_parser.parse_args ()
1364 identify ()
1366 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1367 for f in files:
1368 if f == '-':
1369 f = ''
1371 sys.stderr.write ('Parsing `%s\'...\n' % f)
1372 parse_file (f)
1374 if not global_options.output:
1375 global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1376 sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
1377 outf = open (global_options.output, 'w')
1379 # don't substitute @VERSION@. We want this to reflect
1380 # the last version that was verified to work.
1381 outf.write ('\\version "2.7.40"\n')
1383 # dump_global (outf)
1384 dump_header (outf, header)
1385 dump_slyrics (outf)
1386 dump_voices (outf)
1387 dump_score (outf)
1388 dump_lyrics (outf)
1389 sys.stderr.write ('\n')