Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / scripts / abc2ly.py
blob9107b8b8665903738a68586851738831d5a5b666
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 # * coding style
62 # * lilylib
63 # * GNU style messages: warning:FILE:LINE:
64 # * l10n
66 # Convert to new chord styles.
68 # UNDEF -> None
72 import __main__
73 import getopt
74 import sys
75 import re
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 (int (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 (int (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 a.count ('/') == 1:
249 array = a.split('/')
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 = array[1].split ('=')
254 denominator=array2[0]
255 perminute=array2[1]
256 duration = str (int (denominator) / int (numerator))
257 midi_specs = ' '.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 (int (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 (int (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 = int ( 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 = int (m.group (1))
311 d = int (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 = k.lower ()
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 = k.lower ()
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 Exception ("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 = int (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) - v.rfind ('\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 = int (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 = str.lstrip ()
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 state.in_acc = {}
934 # if we are parsing a beam, close it off
935 def close_beam_state(state):
936 if state.parsing_beam and global_options.beams:
937 state.parsing_beam = 0
938 voices_append_back( ']' )
941 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP !
942 def try_parse_note (str, parser_state):
943 mud = ''
945 slur_begin =0
946 if not str:
947 return str
949 articulation =''
950 acc = UNDEF
951 if str[0] in '^=_':
952 c = str[0]
953 str = str[1:]
954 if c == '^':
955 acc = 1
956 if c == '=':
957 acc = 0
958 if c == '_':
959 acc = -1
961 octave = parser_state.base_octave
962 if str[0] in "ABCDEFG":
963 str = str[0].lower () + str[1:]
964 octave = octave - 1
967 notename = 0
968 if str[0] in "abcdefg":
969 notename = (ord(str[0]) - ord('a') + 5)%7
970 str = str[1:]
971 else:
972 return str # failed; not a note!
975 __main__.lyric_idx = -1
977 if parser_state.next_bar:
978 voices_append(parser_state.next_bar)
979 parser_state.next_bar = ''
981 while str[0] == ',':
982 octave = octave - 1
983 str = str[1:]
984 while str[0] == '\'':
985 octave = octave + 1
986 str = str[1:]
988 (str, num,den,current_dots) = parse_duration (str, parser_state)
990 if re.match('[ \t]*\)', str):
991 str = str.lstrip ()
993 slur_end =0
994 while str[:1] ==')':
995 slur_end = slur_end + 1
996 str = str[1:]
999 bar_acc = get_bar_acc(notename, octave, parser_state)
1000 pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename])
1001 oct = octave_to_lilypond_quotes (octave)
1002 if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
1003 mod='!'
1004 else:
1005 mod = ''
1006 voices_append ("%s%s%s%s" %
1007 (pit, oct, mod,
1008 duration_to_lilypond_duration ((num,den), default_len, current_dots)))
1010 set_bar_acc(notename, octave, acc, parser_state)
1011 if parser_state.next_articulation:
1012 articulation = articulation + parser_state.next_articulation
1013 parser_state.next_articulation = ''
1015 voices_append (articulation)
1017 if parser_state.parsing_tuplet:
1018 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
1019 if not parser_state.parsing_tuplet:
1020 voices_append ("}")
1021 if slur_begin:
1022 voices_append ('-(' * slur_begin )
1023 if slur_end:
1024 voices_append ('-)' *slur_end )
1026 if global_options.beams and \
1027 str[0] in '^=_ABCDEFGabcdefg' and \
1028 not parser_state.parsing_beam and \
1029 not parser_state.parsing_tuplet:
1030 parser_state.parsing_beam = 1
1031 voices_append_back( '[' )
1033 return str
1035 def junk_space (str,state):
1036 while str and str[0] in '\t\n\r ':
1037 str = str[1:]
1038 close_beam_state(state)
1040 return str
1043 def try_parse_guitar_chord (str, state):
1044 if str[:1] =='"':
1045 str = str[1:]
1046 gc = ''
1047 if str[0] == '_' or (str[0] == '^'):
1048 position = str[0]
1049 str = str[1:]
1050 else:
1051 position = '^'
1052 while str and str[0] != '"':
1053 gc = gc + str[0]
1054 str = str[1:]
1056 if str:
1057 str = str[1:]
1058 gc = re.sub('#', '\\#', gc) # escape '#'s
1059 state.next_articulation = ("%c\"%s\"" % (position, gc)) \
1060 + state.next_articulation
1061 return str
1063 def try_parse_escape (str):
1064 if not str or str [0] != '\\':
1065 return str
1067 str = str[1:]
1068 if str[:1] =='K':
1069 key_table = compute_key ()
1070 return str
1073 # |] thin-thick double bar line
1074 # || thin-thin double bar line
1075 # [| thick-thin double bar line
1076 # :| left repeat
1077 # |: right repeat
1078 # :: left-right repeat
1079 # |1 volta 1
1080 # |2 volta 2
1081 old_bar_dict = {
1082 '|]' : '|.',
1083 '||' : '||',
1084 '[|' : '||',
1085 ':|' : ':|',
1086 '|:' : '|:',
1087 '::' : ':|:',
1088 '|1' : '|',
1089 '|2' : '|',
1090 ':|2' : ':|',
1091 '|' : '|'
1093 bar_dict = {
1094 '|]' : '\\bar "|."',
1095 '||' : '\\bar "||"',
1096 '[|' : '\\bar "||"',
1097 ':|' : '}',
1098 '|:' : '\\repeat volta 2 {',
1099 '::' : '} \\repeat volta 2 {',
1100 '|1' : '} \\alternative{{',
1101 '|2' : '} {',
1102 ':|2' : '} {',
1103 '|' : '\\bar "|"'
1107 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
1108 alternative_opener = ['|1', '|2', ':|2']
1109 repeat_ender = ['::', ':|']
1110 repeat_opener = ['::', '|:']
1111 in_repeat = [''] * 8
1112 doing_alternative = [''] * 8
1113 using_old = ''
1115 def try_parse_bar (str,state):
1116 global in_repeat, doing_alternative, using_old
1117 do_curly = ''
1118 bs = None
1119 if current_voice_idx < 0:
1120 select_voice ('default', '')
1121 # first try the longer one
1122 for trylen in [3,2,1]:
1123 if str[:trylen] and bar_dict.has_key (str[:trylen]):
1124 s = str[:trylen]
1125 if using_old:
1126 bs = "\\bar \"%s\"" % old_bar_dict[s]
1127 else:
1128 bs = "%s" % bar_dict[s]
1129 str = str[trylen:]
1130 if s in alternative_opener:
1131 if not in_repeat[current_voice_idx]:
1132 using_old = 't'
1133 bs = "\\bar \"%s\"" % old_bar_dict[s]
1134 else:
1135 doing_alternative[current_voice_idx] = 't'
1137 if s in repeat_ender:
1138 if not in_repeat[current_voice_idx]:
1139 sys.stderr.write("Warning: inserting repeat to beginning of notes.\n")
1140 repeat_prepend()
1141 in_repeat[current_voice_idx] = ''
1142 else:
1143 if doing_alternative[current_voice_idx]:
1144 do_curly = 't'
1145 if using_old:
1146 bs = "\\bar \"%s\"" % old_bar_dict[s]
1147 else:
1148 bs = bar_dict[s]
1149 doing_alternative[current_voice_idx] = ''
1150 in_repeat[current_voice_idx] = ''
1151 if s in repeat_opener:
1152 in_repeat[current_voice_idx] = 't'
1153 if using_old:
1154 bs = "\\bar \"%s\"" % old_bar_dict[s]
1155 else:
1156 bs = bar_dict[s]
1157 break
1158 if str[:1] == '|':
1159 state.next_bar = '|\n'
1160 str = str[1:]
1161 clear_bar_acc(state)
1162 close_beam_state(state)
1164 if bs <> None or state.next_bar != '':
1165 if state.parsing_tuplet:
1166 state.parsing_tuplet =0
1167 voices_append ('} ')
1169 if bs <> None:
1170 clear_bar_acc(state)
1171 close_beam_state(state)
1172 voices_append (bs)
1173 if do_curly != '':
1174 voices_append("} ")
1175 do_curly = ''
1176 return str
1178 def try_parse_tie (str):
1179 if str[:1] =='-':
1180 str = str[1:]
1181 voices_append (' ~ ')
1182 return str
1184 def bracket_escape (str, state):
1185 m = re.match ( '^([^\]]*)] *(.*)$', str)
1186 if m:
1187 cmd = m.group (1)
1188 str = m.group (2)
1189 try_parse_header_line (cmd, state)
1190 return str
1192 def try_parse_chord_delims (str, state):
1193 if str[:1] =='[':
1194 str = str[1:]
1195 if re.match('[A-Z]:', str): # bracket escape
1196 return bracket_escape(str, state)
1197 if state.next_bar:
1198 voices_append(state.next_bar)
1199 state.next_bar = ''
1200 voices_append ('<<')
1202 if str[:1] == '+':
1203 str = str[1:]
1204 if state.plus_chord:
1205 voices_append ('>>')
1206 state.plus_chord = 0
1207 else:
1208 if state.next_bar:
1209 voices_append(state.next_bar)
1210 state.next_bar = ''
1211 voices_append ('<<')
1212 state.plus_chord = 1
1214 ch = ''
1215 if str[:1] ==']':
1216 str = str[1:]
1217 ch = '>>'
1219 end = 0
1220 while str[:1] ==')':
1221 end = end + 1
1222 str = str[1:]
1225 voices_append ("\\spanrequest \\stop \"slur\"" * end)
1226 voices_append (ch)
1227 return str
1229 def try_parse_grace_delims (str, state):
1230 if str[:1] =='{':
1231 if state.next_bar:
1232 voices_append(state.next_bar)
1233 state.next_bar = ''
1234 str = str[1:]
1235 voices_append ('\\grace { ')
1237 if str[:1] =='}':
1238 str = str[1:]
1239 voices_append ('}')
1241 return str
1243 def try_parse_comment (str):
1244 global nobarlines
1245 if (str[0] == '%'):
1246 if str[0:5] == '%MIDI':
1247 #the nobarlines option is necessary for an abc to lilypond translator for
1248 #exactly the same reason abc2midi needs it: abc requires the user to enter
1249 #the note that will be printed, and MIDI and lilypond expect entry of the
1250 #pitch that will be played.
1252 #In standard 19th century musical notation, the algorithm for translating
1253 #between printed note and pitch involves using the barlines to determine
1254 #the scope of the accidentals.
1256 #Since ABC is frequently used for music in styles that do not use this
1257 #convention, such as most music written before 1700, or ethnic music in
1258 #non-western scales, it is necessary to be able to tell a translator that
1259 #the barlines should not affect its interpretation of the pitch.
1260 if 'nobarlines' in str:
1261 nobarlines = 1
1262 elif str[0:3] == '%LY':
1263 p = str.find ('voices')
1264 if (p > -1):
1265 voices_append(str[p+7:])
1266 voices_append("\n")
1267 p = str.find ('slyrics')
1268 if (p > -1):
1269 slyrics_append(str[p+8:])
1271 #write other kinds of appending if we ever need them.
1272 return str
1274 lineno = 0
1275 happy_count = 100
1276 def parse_file (fn):
1277 f = open (fn)
1278 ls = f.readlines ()
1279 ls = map (lambda x: re.sub ("\r$", '', x), ls)
1281 select_voice('default', '')
1282 global lineno
1283 lineno = 0
1284 sys.stderr.write ("Line ... ")
1285 sys.stderr.flush ()
1286 __main__.state = state_list[current_voice_idx]
1288 for ln in ls:
1289 lineno = lineno + 1
1291 if not (lineno % happy_count):
1292 sys.stderr.write ('[%d]'% lineno)
1293 sys.stderr.flush ()
1294 m = re.match ('^([^%]*)%(.*)$',ln) # add comments to current voice
1295 if m:
1296 if m.group(2):
1297 try_parse_comment(m.group(2))
1298 voices_append ('%% %s\n' % m.group(2))
1299 ln = m.group (1)
1301 orig_ln = ln
1303 ln = try_parse_header_line (ln, state)
1305 # Try nibbling characters off until the line doesn't change.
1306 prev_ln = ''
1307 while ln != prev_ln:
1308 prev_ln = ln
1309 ln = try_parse_chord_delims (ln, state)
1310 ln = try_parse_rest (ln, state)
1311 ln = try_parse_articulation (ln,state)
1312 ln = try_parse_note (ln, state)
1313 ln = try_parse_bar (ln, state)
1314 ln = try_parse_tie (ln)
1315 ln = try_parse_escape (ln)
1316 ln = try_parse_guitar_chord (ln, state)
1317 ln = try_parse_tuplet_begin (ln, state)
1318 ln = try_parse_group_end (ln, state)
1319 ln = try_parse_grace_delims (ln, state)
1320 ln = junk_space (ln, state)
1322 if ln:
1323 error ("%s: %d: Huh? Don't understand\n" % (fn, lineno))
1324 left = orig_ln[0:-len (ln)]
1325 sys.stderr.write (left + '\n')
1326 sys.stderr.write (' ' * len (left) + ln + '\n')
1329 def identify():
1330 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1332 authors = """
1333 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1334 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1337 def print_version ():
1338 print r"""abc2ly (GNU lilypond) %s""" % version
1340 def get_option_parser ():
1341 p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
1342 description=_ ('''abc2ly converts ABC music files (see
1343 %s) to LilyPond input.
1344 ''') % 'http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt',
1345 add_help_option=False)
1347 p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
1348 p.add_option("--version",
1349 action="version",
1350 help=_ ("show version number and exit"))
1352 p.add_option("-h", "--help",
1353 action="help",
1354 help=_ ("show this help and exit"))
1355 p.add_option ('-o', '--output', metavar='FILE',
1356 help=_ ("write output to FILE"),
1357 action='store')
1358 p.add_option ('-s', '--strict', help=_ ("be strict about success"),
1359 action='store_true')
1360 p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"))
1361 p.add_option_group ('',
1362 description=(
1363 _ ('Report bugs via %s')
1364 % 'http://post.gmane.org/post.php'
1365 '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
1366 return p
1369 option_parser = get_option_parser ()
1370 (global_options, files) = option_parser.parse_args ()
1373 identify ()
1375 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1376 for f in files:
1377 if f == '-':
1378 f = ''
1380 sys.stderr.write ('Parsing `%s\'...\n' % f)
1381 parse_file (f)
1383 if not global_options.output:
1384 global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1385 sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
1386 outf = open (global_options.output, 'w')
1388 # don't substitute @VERSION@. We want this to reflect
1389 # the last version that was verified to work.
1390 outf.write ('\\version "2.7.40"\n')
1392 # dump_global (outf)
1393 dump_header (outf, header)
1394 dump_slyrics (outf)
1395 dump_voices (outf)
1396 dump_score (outf)
1397 dump_lyrics (outf)
1398 sys.stderr.write ('\n')