]> git.donarmstrong.com Git - lilypond.git/blob - scripts/abc2ly.py
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / scripts / abc2ly.py
1 #!@TARGET_PYTHON@
2 # -*- coding: utf-8 -*-
3
4 # once upon a rainy monday afternoon.
5
6 #    This file is part of LilyPond, the GNU music typesetter.
7 #
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.
12 #
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.
17 #
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/>.
20
21 #
22 #   ...
23 #
24 # (not finished.)
25 # ABC standard v1.6:  http://abcnotation.com/
26 #
27 # Enhancements  (Roy R. Rankin)
28 #
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)
45
46 # Enhancements (Laura Conrad)
47 #
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.
54
55 # Enhancements (Guy Gascoigne-Piggford)
56 #
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.
61
62 # Limitations
63 #
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 # ???
73
74
75
76 #TODO:
77 #
78 # * coding style
79 # * lilylib
80 # * GNU style messages:  warning:FILE:LINE:
81 # * l10n
82
83 # Convert to new chord styles.
84 #
85 # UNDEF -> None
86 #
87  
88
89 import __main__
90 import getopt
91 import sys
92 import re
93 import os
94
95 program_name = sys.argv[0]
96
97
98 """
99 @relocate-preamble@
100 """
101
102 import lilylib as ly
103 global _;_=ly._
104
105 version = '@TOPLEVEL_VERSION@'
106 if version == '@' + 'TOPLEVEL_VERSION' + '@':
107     version = '(unknown version)'                # uGUHGUHGHGUGH  
108
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 = ''
131
132
133 def error (msg):
134     sys.stderr.write (msg)
135     if global_options.strict:
136         sys.exit (1)
137
138
139 def alphabet (i):
140     return chr (i + ord('A'))
141     
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)]
157
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
170
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;
179
180     state.base_octave = octave
181     voices_append ("\\clef \""+clef+mod+"\"\n")
182     return s
183
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 )
207                     
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
213
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 ('}')
222
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")
230
231 def dump_default_bar (outf):
232     """
233     Nowadays abc2ly outputs explicits barlines (?)
234     """
235     ## < 2.2
236     outf.write ("\n\\set Score.defaultBarType = \"empty\"\n")
237
238
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}")
252
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}")
273
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)
288
289 def dump_score (outf):
290     outf.write (r"""
291
292 \score{
293     <<
294 """)
295
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")
310
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
318
319             outf.write ( " \\words%sV%s } " % ( m, chr (l)) )
320             l += 1
321
322     outf.write ("\n    >>")
323     outf.write ("\n\t\\layout {\n")
324     outf.write ("\t}\n\t\\midi {%s}\n}\n" % midi_specs)
325
326
327
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
334
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
344
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
359
360
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
366
367     t = tup[0]
368     p = p + 12 * (t / 7)
369     t = t % 7
370     
371     if t > 2:
372         p = p- 1
373         
374     p = p + t* 2 + tup[1]
375     return p
376
377 def fifth_above_pitch (tup):
378     (n, a)  = (tup[0] + 4, tup[1])
379
380     difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
381     a = a + difference
382     
383     return (n,a)
384
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
394
395         p = (t % 7, a)
396     return l
397
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
407
408         p = (t % 7, a)
409     return l
410
411 def quart_above_pitch (tup):
412     (n, a)  = (tup[0] + 3, tup[1])
413
414     difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
415     a = a + difference
416     
417     return (n,a)
418
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'        
433 }
434
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
450
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]))
459
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)
473
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        
494 }
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:]
500     
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)
511     
512     sharp_key_seq = sharp_keys ()
513     flat_key_seq = flat_keys ()
514
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))
521
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")
529     
530     key_table = [0] * 7
531     for a in accseq:
532         key_table[a] = key_table[a] + accsign
533
534     return key_table
535
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',
544     }
545
546
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
557
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
563     
564 def header_append (key, a):
565     s = ''
566     if header.has_key (key):
567         s = header[key] + "\n"
568         header [key] = s + a
569
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 + ' '
575
576 def stuff_append (stuff, idx, a):
577     if not stuff:
578         stuff.append (a)
579     else:
580         stuff [idx] = wordwrap(a, stuff[idx])
581
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:]
592
593 def voices_append(a):
594     if current_voice_idx < 0:
595         select_voice ('default', '')
596     stuff_append (voices, current_voice_idx, a)
597
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)
606
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'
613
614     
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)
620
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)
638
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])
655
656
657 def try_parse_header_line (ln, state):
658     global length_specified
659     m = re.match ('^([A-Za-z]): *(.*)$', ln)
660
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
758
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'
772     
773     if name > 4:
774         name = name -7
775     return(chr (name  + ord('c')) + s)
776
777
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 ='\''
786
787     return s * o
788
789 def parse_num (str):
790     durstr = ''
791     while str and str[0] in DIGITS:
792         durstr = durstr + str[0]
793         str = str[1:]
794
795     n = None
796     if durstr:
797         n = int (durstr)
798     return (str,n)
799
800
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)
815
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
828
829
830
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
836
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)
848
849                     den = den * d
850
851     den = den * default_len
852     
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
862         
863         while str[0] == '<':
864             str = str [1:]
865             den = den * 2
866             parser_state.next_dots = parser_state.next_dots + 1
867
868
869
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
878         
879     return (str, num,den,current_dots)
880
881
882 def try_parse_rest (str, parser_state):
883     if not str or str[0] <> 'z' and str[0] <> 'x':
884         return str
885
886     __main__.lyric_idx = -1
887
888     if parser_state.next_bar:
889         voices_append(parser_state.next_bar)
890         parser_state.next_bar = ''
891
892     if str[0] == 'z':
893         rest = 'r'
894     else:
895         rest = 's'
896     str = str[1:]
897
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 = ''
903
904     return str
905
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'
920 }
921     
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] )
927
928         str = str[1:]
929
930     
931         
932     # s7m2 input doesnt care about spaces
933     if re.match('[ \t]*\(', str):
934         str = str.lstrip ()
935
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:]
941
942     return str
943         
944 #
945 # remember accidental for rest of bar
946 #
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
952
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)
960
961 def clear_bar_acc(state):
962     state.in_acc = {}
963
964
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( ']' )
970
971         
972 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
973 def try_parse_note (str, parser_state):
974     mud = ''
975
976     slur_begin =0
977     if not str:
978         return str
979
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
991
992     octave = parser_state.base_octave
993     if str[0] in "ABCDEFG":
994         str = str[0].lower () + str[1:]
995         octave = octave - 1
996
997
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!
1004
1005     
1006     __main__.lyric_idx = -1
1007
1008     if parser_state.next_bar:
1009         voices_append(parser_state.next_bar)
1010         parser_state.next_bar = ''
1011
1012     while str[0] == ',':
1013         octave = octave - 1
1014         str = str[1:]
1015     while str[0] == '\'':
1016         octave = octave + 1
1017         str = str[1:]
1018
1019     (str, num,den,current_dots) = parse_duration (str, parser_state)
1020
1021     if re.match('[ \t]*\)', str):
1022         str = str.lstrip ()
1023     
1024     slur_end =0
1025     while str[:1] ==')':
1026         slur_end = slur_end + 1
1027         str = str[1:]
1028
1029     
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)))
1040     
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 = ''
1045
1046     voices_append (articulation)
1047
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 )
1056
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( '[' )
1063         
1064     return str
1065
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)
1070
1071     return str
1072
1073
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:]
1086             
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
1093
1094 def try_parse_escape (str):
1095     if not str or str [0] != '\\':
1096         return str
1097     
1098     str = str[1:]
1099     if str[:1] =='K':
1100         key_table = compute_key ()
1101     return str
1102
1103 #
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 '|' :  '|'
1123 }
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 "|"'
1135  }
1136
1137
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 = ''
1145
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'
1167
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)
1194     
1195     if bs <> None or state.next_bar != '':
1196         if state.parsing_tuplet:
1197             state.parsing_tuplet =0
1198             voices_append ('} ')
1199         
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
1208
1209 def try_parse_tie (str):
1210     if str[:1] =='-':
1211         str = str[1:]
1212         voices_append (' ~ ')
1213     return str
1214
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
1222
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 ('<<')
1232
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
1244
1245     ch = ''
1246     if str[:1] ==']':
1247         str = str[1:]
1248         ch = '>>'
1249
1250     end = 0
1251     while str[:1] ==')':
1252         end = end + 1
1253         str = str[1:]
1254
1255     
1256     voices_append ("\\spanrequest \\stop \"slur\"" * end)
1257     voices_append (ch)
1258     return str
1259
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 { ')
1267
1268     if str[:1] =='}':
1269         str = str[1:]
1270         voices_append ('}')
1271
1272     return str
1273
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.
1282 #
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.
1286 #
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:])
1301             
1302 #write other kinds of appending  if we ever need them.                        
1303     return str
1304
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)
1311
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]
1318     
1319     for ln in ls:
1320         lineno = lineno + 1
1321
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)
1331
1332         orig_ln = ln
1333         
1334         ln = try_parse_header_line (ln, state)
1335
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)
1352
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')        
1358
1359
1360 def identify():
1361     sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1362
1363 authors = """
1364 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1365 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1366 """
1367
1368 def print_version ():
1369     print r"""abc2ly (GNU lilypond) %s""" % version
1370
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)
1377
1378     p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
1379     p.add_option("--version",
1380                  action="version",
1381                  help=_ ("show version number and exit"))
1382
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
1398
1399
1400 option_parser = get_option_parser ()
1401 (global_options, files) = option_parser.parse_args ()
1402
1403
1404 identify ()
1405
1406 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1407 for f in files:
1408     if f == '-':
1409         f = ''
1410
1411     sys.stderr.write ('Parsing `%s\'...\n' % f)
1412     parse_file (f)
1413
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')
1418
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')
1422
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')
1430