]> git.donarmstrong.com Git - lilypond.git/blob - scripts/abc2ly.py
release: 1.2.13
[lilypond.git] / scripts / abc2ly.py
1 #!@PYTHON@
2
3 # once upon a rainy monday afternoon.
4 #
5 #   ...
6 #
7 # (not finished.)
8 # ABC standard v1.6:  http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt
9
10 # Enhancements  (Roy R. Rankin)
11 #
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)
28 #
29 # Limitations
30 #
31 # Multiple tunes in single file not supported
32 # Blank T: header lines should write score and open a new score
33 # Not all header fields supported
34 # Beaming not preserved between ABC and lilypond
35 # ABC line breaks are ignored
36 # Block comments generate error and are ignored
37 # Postscript commands are ignored
38 # lyrics not resynchronized by line breaks (lyrics must fully match notes)
39 # ???
40
41
42   
43   
44 program_name = 'abc2ly'
45 version = '@TOPLEVEL_VERSION@'
46 if version == '@' + 'TOPLEVEL_VERSION' + '@':
47         version = '1.2.9'               # uGUHGUHGHGUGH
48   
49 import __main__
50 import getopt
51 import sys
52 import re
53 import string
54 import os
55
56
57 UNDEF = 255
58 state = UNDEF
59 voice_idx_dict = {}
60 header = {}
61 lyrics = []
62 slyrics = []
63 voices = []
64 state_list = []
65 current_voice_idx = -1
66 current_lyric_idx = -1
67 lyric_idx = -1
68 part_names = 0
69 default_len = 8
70 global_key = [0] * 7                    # UGH
71 names = ["One", "Two", "Three"]
72 DIGITS='0123456789'
73 HSPACE=' \t'
74
75         
76 def check_clef(s):
77       if not s:
78               return ''
79       if re.match('^treble', s):
80               s = s[6:]
81               if re.match ('^-8', s):
82                       s = s[2:]
83                       state.base_octave = -2
84                       voices_append("\\clef \"G_8\";\n")
85               else:
86                       state.base_octave = 0
87                       voices_append("\\clef treble;\n")
88       elif re.match('^alto', s):
89               s = s[4:]
90               state.base_octave = -1
91               voices_append ("\\clef alto;\n" )
92       elif re.match('^bass',s ):
93               s = s[4:]
94               state.base_octave = -2
95               voices_append ("\\clef bass;\n" )
96       return s
97
98 def select_voice (name, rol):
99         if not voice_idx_dict.has_key (name):
100               state_list.append(Parser_state())
101               voices.append ('')
102               slyrics.append ([])
103               voice_idx_dict[name] = len (voices) -1
104         __main__.current_voice_idx =  voice_idx_dict[name]
105         __main__.state = state_list[current_voice_idx]
106         while rol != '':
107               m = re.match ('^([^ \t=]*)=(.*)$', rol) # find keywork
108               if m:
109                       keyword = m.group(1)
110                       rol = m.group (2)
111                       a = re.match ('^("[^"]*"|[^ \t]*) *(.*)$', rol)
112                       if a:
113                               value = a.group (1)
114                               rol = a.group ( 2)
115                               if keyword == 'clef':
116                                       check_clef(value)
117                               elif keyword == "name":
118                                       value = re.sub ('\\\\','\\\\\\\\', value)
119                                       voices_append ("\\property Staff.instrument = %s\n" % value )
120                                       __main__.part_names = 1
121                               elif keyword == "sname" or keyword == "snm":
122                                       voices_append ("\\property Staff.instr = %s\n" % value )
123
124               else:
125                       break
126
127         return
128
129
130 def dump_header (outf,hdr):
131         outf.write ('\\header {\n')
132         ks = hdr.keys ()
133         ks.sort ()
134         for k in ks:
135                 outf.write ('\t%s = "%s";\n'% (k,hdr[k]))
136         outf.write ('}')
137
138 def dump_lyrics (outf):
139         if (len(lyrics)):
140                 outf.write("\n\\score\n{\n    \\context Lyrics\n    <\n")
141                 for i in range (len (lyrics)):
142                         outf.write ( lyrics [i])
143                         outf.write ("\n")
144                 outf.write("    >\n    \\paper{}\n}\n")
145
146 def dump_slyrics (outf):
147         ks = voice_idx_dict.keys()
148         ks.sort ()
149         for k in ks:
150                 for i in range (len(slyrics[voice_idx_dict[k]])):
151                         outf.write ("\nwords%sV%d = \\lyrics  {" % (k, i))
152                         outf.write ("\n" + slyrics [voice_idx_dict[k]][i])
153                         outf.write ("\n}")
154
155 def dump_voices (outf):
156         ks = voice_idx_dict.keys()
157         ks.sort ()
158         for k in ks:
159                 outf.write ("\nvoice%s = \\notes {" % k)
160                 outf.write ("\n" + voices [voice_idx_dict[k]])
161                 outf.write ("\n}")
162         
163 def dump_score (outf):
164         outf.write (r"""\score{
165         \notes <
166 """)
167
168         ks  = voice_idx_dict.keys ();
169         ks.sort ()
170         for k in  ks:
171                 if k == 'default' and len (voice_idx_dict) > 1:
172                         break
173                 if len ( slyrics [voice_idx_dict[k]] ):
174                         outf.write ("\n        \\addlyrics")
175                 outf.write ("\n\t\\context Staff=\"%s\"\n\t{\n" %k ) 
176                 if k != 'default':
177                         outf.write ("\t    \\$voicedefault\n")
178                 outf.write ("\t    \\$voice%s " % k)
179                 outf.write ("\n\t}\n")
180                 if len ( slyrics [voice_idx_dict[k]] ):
181                         outf.write ("\n\t\\context Lyrics=\"%s\" \n\t<\t" % k)
182                         for i in range (len(slyrics[voice_idx_dict[k]])):
183                                 outf.write("\n\t  { \\$words%sV%d }" % ( k, i) )
184                         outf.write ( "\n\t>\n" )
185         outf.write ("\n    >")
186         outf.write ("\n\t\\paper {\n")
187         if part_names:
188                 outf.write ("\t    \\translator \n\t    {\n")
189                 outf.write ("\t\t\\StaffContext\n")
190                 outf.write ("\t\t\\consists Staff_margin_engraver;\n")
191                 outf.write ("\t    }\n")
192         outf.write ("\t}\n\t\\midi {}\n}\n")
193
194
195
196 def set_default_length (s):
197         m =  re.search ('1/([0-9]+)', s)
198         if m:
199                 __main__.default_len = string.atoi ( m.group (1))
200
201 def set_default_len_from_time_sig (s):
202         m =  re.search ('([0-9]+)/([0-9]+)', s)
203         if m:
204                 n = string.atoi (m.group (1))
205                 d = string.atoi (m.group (2))
206                 if (n * 1.0 )/(d * 1.0) <  0.75:
207                         __main__.default_len =  16
208                 else:
209                         __main__.default_len = 8
210
211 def gulp_file(f):
212         try:
213                 i = open(f)
214                 i.seek (0, 2)
215                 n = i.tell ()
216                 i.seek (0,0)
217         except:
218                 sys.stderr.write ("can't open file: %s\n" % f)
219                 return ''
220         s = i.read (n)
221         if len (s) <= 0:
222                 sys.stderr.write ("gulped emty file: %s\n" % f)
223         i.close ()
224         return s
225
226
227 # pitch manipulation. Tuples are (name, alteration).
228 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
229 # pitch in semitones. 
230 def semitone_pitch  (tup):
231         p =0
232
233         t = tup[0]
234         p = p + 12 * (t / 7)
235         t = t % 7
236         
237         if t > 2:
238                 p = p- 1
239                 
240         p = p + t* 2 + tup[1]
241         return p
242
243 def fifth_above_pitch (tup):
244         (n, a)  = (tup[0] + 4, tup[1])
245
246         difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
247         a = a + difference
248         
249         return (n,a)
250
251 def sharp_keys ():
252         p = (0,0)
253         l = []
254         k = 0
255         while 1:
256                 l.append (p)
257                 (t,a) = fifth_above_pitch (p)
258                 if semitone_pitch((t,a)) % 12 == 0:
259                         break
260
261                 p = (t % 7, a)
262         return l
263
264 def flat_keys ():
265         p = (0,0)
266         l = []
267         k = 0
268         while 1:
269                 l.append (p)
270                 (t,a) = quart_above_pitch (p)
271                 if semitone_pitch((t,a)) % 12 == 0:
272                         break
273
274                 p = (t % 7, a)
275         return l
276
277 def quart_above_pitch (tup):
278         (n, a)  = (tup[0] + 3, tup[1])
279
280         difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
281         a = a + difference
282         
283         return (n,a)
284
285 key_lookup = {  # abc to lilypond key mode names
286         'm'   : 'minor',
287         'min' : 'minor',
288         'maj' : 'major',
289         'phr' : 'phrygian',
290         'ion' : 'ionian',
291         'loc' : 'locrian',
292         'aeo' : 'aeolian',
293         'mix' : 'mixolydian',
294         'lyd' : 'lydian',
295         'dor' : 'dorian'
296 }
297
298 def lily_key (k):
299         k = string.lower (k)
300         key = k[0]
301         k = k[1:]
302         if k and k[0] == '#':
303                 key = key + 'is'
304                 k = k[1:]
305         elif k and k[0] == 'b':
306                 key = key + 'es'
307                 k = k[1:]
308         if not k:
309                 return(key)
310
311         type = k[0:3]
312         if key_lookup.has_key(type):
313                 return("%s \\%s" % ( key, key_lookup[type]))
314         sys.stderr.write("Unknown key type %s ignored\n" % type)
315         return key
316
317 def shift_key (note, acc , shift):
318         s = semitone_pitch((note, acc))
319         s = (s + shift + 12) % 12
320         if s <= 4:
321                 n = s / 2
322                 a = s % 2
323         else:
324                 n = (s + 1) / 2
325                 a = (s + 1) % 2
326         if a:
327                 n = n + 1
328                 a = -1
329         return (n,a)
330
331 key_shift = { # semitone shifts for key mode names
332         'm'   : 3,
333         'min' : 3,
334         'maj' : 0,
335         'phr' : -4,
336         'ion' : 0,
337         'loc' : 1,
338         'aeo' : 3,
339         'mix' : 5,
340         'lyd' : -5,
341         'dor' : -2
342 }
343 def compute_key (k):
344         k = string.lower (k)
345         intkey = (ord (k[0]) - ord('a') + 5) % 7
346         intkeyacc =0
347         k = k[1:]
348         
349         if k and k[0] == 'b':
350                 intkeyacc = -1
351                 k = k[1:]
352         elif  k and k[0] == '#':
353                 intkeyacc = 1
354                 k = k[1:]
355         k = k[0:3]
356         if k and key_shift.has_key(k):
357                 (intkey, intkeyacc) = shift_key(intkey, intkeyacc, key_shift[k])
358         keytup = (intkey, intkeyacc)
359         
360         sharp_key_seq = sharp_keys ()
361         flat_key_seq = flat_keys ()
362
363         accseq = None
364         accsign = 0
365         if keytup in sharp_key_seq:
366                 accsign = 1
367                 key_count = sharp_key_seq.index (keytup)
368                 accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1))
369
370         elif keytup in flat_key_seq:
371                 accsign = -1
372                 key_count = flat_key_seq.index (keytup)
373                 accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
374         else:
375                 raise "Huh"
376         
377         key_table = [0] * 7
378         for a in accseq:
379                  key_table[a] = key_table[a] + accsign
380
381         return key_table
382
383 tup_lookup = {
384         '2' : '3/2',
385         '3' : '2/3',
386         '4' : '4/3',
387         '5' : '4/5',
388         '6' : '4/6',
389         '7' : '6/7',
390         '9' : '8/9',
391         }
392
393
394 def try_parse_tuplet_begin (str, state):
395         if re.match ('\([2-9]', str):
396                 dig = str[1]
397                 str = str[2:]
398                 state.parsing_tuplet = string.atoi (dig[0])
399                 
400                 voices_append ("\\times %s {" % tup_lookup[dig])
401         return str
402
403 def  try_parse_group_end (str, state):
404         if str and str[0] in HSPACE:
405                 str = str[1:]
406         return str
407
408 def header_append (key, a):
409         s = ''
410         if header.has_key (key):
411                 s = header[key] + "\n"
412         header [key] = s + a
413
414 def wordwrap(a, v):
415         linelen = len (v) - string.rfind(v, '\n')
416         if linelen + len (a) > 80:
417                 v = v + '\n'
418         return v + a + ' '
419
420 def stuff_append (stuff, idx, a):
421         if not stuff:
422                 stuff.append (a)
423         else:
424                 stuff [idx] = wordwrap(a, stuff[idx])
425
426
427
428 def voices_append(a):
429         if current_voice_idx < 0:
430                 select_voice ('default', '')
431
432         stuff_append (voices, current_voice_idx, a)
433
434 def lyrics_append(a):
435         a = re.sub ( '#', '\\#', a)     # latex does not like naked #'s
436         a = re.sub ( '"', '\\"', a)     # latex does not like naked "'s
437         a = '\t{ \\lyrics "' + a + '" }\n'
438         stuff_append (lyrics, current_lyric_idx, a)
439
440 # break lyrics to words and put "'s around words containing numbers and '"'s
441 def fix_lyric(str):
442         ret = ''
443
444         while str != '':
445                 m = re.match('[ \t]*([^ \t]*)[ \t]*(.*$)', str)
446                 if m:
447                         word = m.group(1)
448                         str = m.group(2)
449                         word = re.sub('"', '\\"', word) # escape "
450                         if re.match('.*[0-9"]', word):
451                                 word = re.sub('_', ' ', word) # _ causes probs inside ""
452                                 ret = ret + '\"' + word + '\" '
453                         else:
454                                 ret = ret + word + ' '
455                 else:
456                         return (ret)
457         return (ret)
458
459 def slyrics_append(a):
460         a = re.sub ( '_', ' _ ', a)     # _ to ' _ '
461         a = re.sub ( '-', '- ', a)      # split words with -
462         a = re.sub ( '\\\\- ', '-', a)  # unless \-
463         a = re.sub ( '~', '_', a)       # ~ to space('_')
464         a = re.sub ( '\*', '_ ', a)     # * to to space
465         a = re.sub ( '#', '\\#', a)     # latex does not like naked #'s
466         if re.match('.*[0-9"]', a):     # put numbers and " into quoted string
467                 a = fix_lyric(a)
468         a = re.sub ( '$', ' ', a)       # insure space between lines
469         __main__.lyric_idx = lyric_idx + 1
470         if len(slyrics[current_voice_idx]) <= lyric_idx:
471                 slyrics[current_voice_idx].append(a)
472         else:
473                 v = slyrics[current_voice_idx][lyric_idx]
474                 slyrics[current_voice_idx][lyric_idx] = wordwrap(a, slyrics[current_voice_idx][lyric_idx])
475
476
477 def try_parse_header_line (ln, state):
478         m = re.match ('^([A-Za-z]): *(.*)$', ln)
479
480         if m:
481                 g =m.group (1)
482                 a = m.group (2)
483                 if g == 'T':    #title
484                         a = re.sub('[ \t]*$','', a)     #strip trailing blanks
485                         if header.has_key('title'):
486                                 if a:
487                                         header['title'] = header['title'] + '\\\\\\\\' + a
488                         else:
489                                 header['title'] =  a
490                 if g == 'M':    # Meter
491                         if a == 'C':
492                                 if not state.common_time:
493                                         state.common_time = 1
494                                         voices_append ("\\property Staff.timeSignatureStyle=\"C\"\n")
495                                 a = '4/4'
496                         if a == 'C|':
497                                 if not state.common_time:
498                                         state.common_time = 1
499                                         voices_append ("\\property Staff.timeSignatureStyle=\"C\"\n")
500                                 a = '2/2'
501 #                       global_voice_stuff.append ('\\time %s;' % a)
502                         set_default_len_from_time_sig (a)
503                         voices_append ('\\time %s;' % a)
504                         state.next_bar = ''
505                 if g == 'K': # KEY
506                         a = check_clef(a)
507                         if a:
508                                 m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info
509                                 if m:
510                                         __main__.global_key  =compute_key (m.group(1))# ugh.
511                                         voices_append ('\\key %s;' % lily_key(m.group(1)))
512                                         check_clef(m.group(2))
513                                 else:
514                                         __main__.global_key  =compute_key (a)# ugh.
515                                         voices_append ('\\key %s;' % lily_key(a))
516                 if g == 'O': # Origin
517                         header ['origin'] = a
518                 if g == 'X': # Reference Number
519                         header ['crossRefNumber'] = a
520                 if g == 'A': #  Area
521                         header ['area'] = a
522                 if g == 'H':    # History
523                         header_append ('history', a)
524                 if g == 'B':    # Book
525                         header ['book'] = a
526                 if g == 'C':    # Composer
527                         if header.has_key('composer'):
528                                 if a:
529                                         header['composer'] = header['composer'] + '\\\\\\\\' + a
530                         else:
531                                 header['composer'] =  a
532                 if g == 'S':
533                         header ['subtitle'] = a
534                 if g == 'L':    # Default note length
535                         set_default_length (ln)
536                 if g == 'V':    # Voice 
537                         voice = re.sub (' .*$', '', a)
538                         rest = re.sub ('^[^ \t]*  *', '', a)
539                         if state.next_bar:
540                                 voices_append(state.next_bar)
541                                 state.next_bar = ''
542                         select_voice (voice, rest)
543                 if g == 'W':    # Words
544                         lyrics_append(a);
545                 if g == 'w':    # vocals
546                         slyrics_append (a);
547
548                 return ''
549         return ln
550
551 # we use in this order specified accidental, active accidental for bar,
552 # active accidental for key
553 def pitch_to_mudela_name (name, acc, bar_acc, key):
554         s = ''
555         if acc == UNDEF:
556                 acc = bar_acc
557         if acc == UNDEF:
558                 acc = key
559         if acc == -1:
560                 s = 'es'
561         elif acc == 1:
562                 s =  'is'
563         
564         if name > 4:
565                 name = name -7
566         return(chr (name  + ord('c')) + s)
567
568
569 def octave_to_mudela_quotes (o):
570         o = o + 2
571         s =''
572         if o < 0:
573                 o = -o
574                 s=','
575         else:
576                 s ='\''
577
578         return s * o
579
580 def parse_num (str):
581         durstr = ''
582         while str and str[0] in DIGITS:
583                 durstr = durstr + str[0]
584                 str = str[1:]
585
586         n = None
587         if durstr:
588                 n  =string.atoi (durstr) 
589         return (str,n)
590
591
592 def duration_to_mudela_duration  (multiply_tup, defaultlen, dots):
593         base = 1
594
595         # (num /  den)  / defaultlen < 1/base
596         while base * multiply_tup[0] < multiply_tup[1]:
597                 base = base * 2
598
599
600         return '%d%s' % ( base, '.'* dots)
601
602 class Parser_state:
603         def __init__ (self):
604                 self.in_acc = {}
605                 self.next_articulation = ''
606                 self.next_bar = ''
607                 self.next_dots = 0
608                 self.next_den = 1
609                 self.parsing_tuplet = 0
610                 self.plus_chord = 0
611                 self.base_octave = 0
612                 self.common_time = 0
613
614
615
616 # return (str, num,den,dots) 
617 def parse_duration (str, parser_state):
618         num = 0
619         den = parser_state.next_den
620         parser_state.next_den = 1
621
622         (str, num) = parse_num (str)
623         if not num:
624                 num = 1
625         
626         if str[0] == '/':
627                 while str[:1] == '/':
628                         str= str[1:]
629                         d = 2
630                         if str[0] in DIGITS:
631                                 (str, d) =parse_num (str)
632
633                         den = den * d
634
635         den = den * default_len
636         
637         current_dots = parser_state.next_dots
638         parser_state.next_dots = 0
639         if re.match ('[ \t]*[<>]', str):
640                 while str[0] in HSPACE:
641                         str = str[1:]
642                 while str[0] == '>':
643                         str = str [1:]
644                         current_dots = current_dots + 1;
645                         parser_state.next_den = parser_state.next_den * 2
646                 
647                 while str[0] == '<':
648                         str = str [1:]
649                         den = den * 2
650                         parser_state.next_dots = parser_state.next_dots + 1
651
652
653
654         try_dots = [3, 2, 1]
655         for d in try_dots:
656                 f = 1 << d
657                 multiplier = (2*f-1)
658                 if num % multiplier == 0 and den % f == 0:
659                         num = num / multiplier
660                         den = den / f
661                         current_dots = current_dots + d
662                 
663         return (str, num,den,current_dots)
664
665
666 def try_parse_rest (str, parser_state):
667         if not str or str[0] <> 'z' and str[0] <> 'x':
668                 return str
669
670         __main__.lyric_idx = -1
671
672         if parser_state.next_bar:
673                 voices_append(parser_state.next_bar)
674                 parser_state.next_bar = ''
675
676         if str[0] == 'z':
677                 rest = 'r'
678         else:
679                 rest = 's'
680         str = str[1:]
681
682         (str, num,den,d) = parse_duration (str, parser_state)
683         voices_append ('%s%s' % (rest, duration_to_mudela_duration ((num,den), default_len, d)))
684         if parser_state.next_articulation:
685                 voices_append (parser_state.next_articulation)
686                 parser_state.next_articulation = ''
687
688         return str
689
690 artic_tbl = {
691         '.'  : '-.',
692          'T' : '^\\trill',
693          'H' : '^\\fermata',
694          'u' : '^\\upbow',
695          'K' : '^\\ltoe',
696          'k' : '^\\accent',
697          'M' : '^\\tenuto',
698          '~' : '^"~" ',
699          'J' : '',              # ignore slide
700          'R' : '',              # ignore roll
701          'v' : '^\\downbow'
702 }
703         
704 def try_parse_articulation (str, state):
705         
706         while str and  artic_tbl.has_key(str[:1]):
707                 state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
708                 if not artic_tbl[str[:1]]:
709                         sys.stderr.write("Warning: ignoring %s\n" % str[:1] )
710
711                 str = str[1:]
712
713         
714                 
715         # s7m2 input doesnt care about spaces
716         if re.match('[ \t]*\(', str):
717                 str = string.lstrip (str)
718
719         slur_begin =0
720         while str[:1] =='(' and str[1] not in DIGITS:
721                 slur_begin = slur_begin + 1
722                 state.next_articulation = state.next_articulation + '('
723                 str = str[1:]
724
725         return str
726                 
727 #
728 # remember accidental for rest of bar
729 #
730 def set_bar_acc(note, octave, acc, state):
731         if acc == UNDEF:
732                 return
733         n_oct = note + octave * 7
734         state.in_acc[n_oct] = acc
735
736 # get accidental set in this bar or UNDEF if not set
737 def get_bar_acc(note, octave, state):
738         n_oct = note + octave * 7
739         if state.in_acc.has_key(n_oct):
740                 return(state.in_acc[n_oct])
741         else:
742                 return(UNDEF)
743
744 def clear_bar_acc(state):
745         for k in state.in_acc.keys():
746                 del state.in_acc[k]
747         
748
749                 
750 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
751 def try_parse_note (str, parser_state):
752         mud = ''
753
754         slur_begin =0
755         if not str:
756                 return str
757
758         articulation =''
759         acc = UNDEF
760         if str[0] in '^=_':
761                 c = str[0]
762                 str = str[1:]
763                 if c == '^':
764                         acc = 1
765                 if c == '=':
766                         acc = 0
767                 if c == '_':
768                         acc = -1
769
770         octave = parser_state.base_octave
771         if str[0] in "ABCDEFG":
772                 str = string.lower (str[0]) + str[1:]
773                 octave = octave - 1
774
775
776         notename = 0
777         if str[0] in "abcdefg":
778                 notename = (ord(str[0]) - ord('a') + 5)%7
779                 str = str[1:]
780         else:
781                 return str              # failed; not a note!
782
783         
784         __main__.lyric_idx = -1
785
786         if parser_state.next_bar:
787                 voices_append(parser_state.next_bar)
788                 parser_state.next_bar = ''
789
790         while str[0] == ',':
791                  octave = octave - 1
792                  str = str[1:]
793         while str[0] == '\'':
794                  octave = octave + 1
795                  str = str[1:]
796
797         (str, num,den,current_dots) = parse_duration (str, parser_state)
798
799
800         if re.match('[ \t]*\)', str):
801                 str = string.lstrip (str)
802         
803         slur_end =0
804         while str[:1] ==')':
805                 slur_end = slur_end + 1
806                 str = str[1:]
807
808         
809         if slur_end:
810                 voices_append ('%s' % ')' *slur_end )
811
812         bar_acc = get_bar_acc(notename, octave, parser_state)
813         pit = pitch_to_mudela_name(notename, acc, bar_acc, global_key[notename])
814         oct = octave_to_mudela_quotes (octave)
815         if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
816                 mod='!'
817         else:
818                 mod = ''
819         voices_append ("%s%s%s%s" %
820                 (pit, oct, mod,
821                  duration_to_mudela_duration ((num,den), default_len, current_dots)))
822         
823         set_bar_acc(notename, octave, acc, parser_state)
824         if parser_state.next_articulation:
825                 articulation = articulation + parser_state.next_articulation
826                 parser_state.next_articulation = ''
827
828         voices_append (articulation)
829
830         if parser_state.parsing_tuplet:
831                 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
832                 if not parser_state.parsing_tuplet:
833                         voices_append ("}")
834         if slur_begin:
835                 voices_append ('%s' % '(' * slur_begin )
836
837
838         return str
839
840 def junk_space (str):
841         while str and str[0] in '\t\n ':
842                 str = str[1:]
843
844         return str
845
846
847 def try_parse_guitar_chord (str, state):
848         if str[:1] =='"':
849                 str = str[1:]
850                 gc = ''
851                 while str and str[0] != '"':
852                         gc = gc + str[0]
853                         str = str[1:]
854                         
855                 if str:
856                         str = str[1:]
857                 gc = re.sub('#', '\\#', gc)     # escape '#'s
858                 state.next_articulation = ("-\"%s\"" % gc) + state.next_articulation
859         return str
860
861 def try_parse_escape (str):
862         if not str or str [0] != '\\':
863                 return str
864         
865         str = str[1:]
866         if str[:1] =='K':
867                 key_table = compute_key ()
868         return str
869
870 #
871 # |] thin-thick double bar line
872 # || thin-thin double bar line
873 # [| thick-thin double bar line
874 # :| left repeat
875 # |: right repeat
876 # :: left-right repeat
877 # |1 volta 1
878 # |2 volta 2
879 bar_dict = {
880 '|]' : '|.',
881 '||' : '||',
882 '[|' : '||',
883 ':|' : ':|',
884 '|:' : '|:',
885 '::' : '::',
886 '|1' : '|',
887 '|2' : '|',
888 ':|2' : ':|'
889 }
890
891
892 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
893
894 def try_parse_bar (str,state):
895         bs = None
896
897         # first try the longer one
898         for trylen in [3,2]:
899                 if str[:trylen] and bar_dict.has_key (str[:trylen]):
900                         s = str[:trylen]
901                         bs = "\\bar \"%s\";" % bar_dict[s]
902                         if s in warn_about:
903                                 sys.stderr.write('Warning kludging for barline `%s\'\n' % s)
904                         str = str[trylen:]
905                         break
906
907         if str[:1] == '|':
908                 state.next_bar = '|\n'
909                 str = str[1:]
910                 clear_bar_acc(state)
911         
912         if bs <> None or state.next_bar != '':
913                 if state.parsing_tuplet:
914                         state.parsing_tuplet =0
915                         voices_append ('} ')
916                 
917         if bs <> None:
918                 clear_bar_acc(state)
919                 voices_append (bs)
920
921         return str
922
923 def try_parse_tie (str):
924         if str[:1] =='-':
925                 str = str[1:]
926                 voices_append (' ~ ')
927         return str
928
929 def bracket_escape (str, state):
930         m = re.match ( '^([^\]]*)] *(.*)$', str)
931         if m:
932                 cmd = m.group (1)
933                 str = m.group (2)
934                 try_parse_header_line (cmd, state)
935         return str
936
937 def try_parse_chord_delims (str, state):
938         if str[:1] =='[':
939                 str = str[1:]
940                 if re.match('[A-Z]:', str):     # bracket escape
941                         return bracket_escape(str, state)
942                 if state.next_bar:
943                         voices_append(state.next_bar)
944                         state.next_bar = ''
945                 voices_append ('<')
946
947         if str[:1] == '+':
948                 str = str[1:]
949                 if state.plus_chord:
950                         voices_append ('>')
951                         state.plus_chord = 0
952                 else:
953                         if state.next_bar:
954                                 voices_append(state.next_bar)
955                                 state.next_bar = ''
956                         voices_append ('<')
957                         state.plus_chord = 1
958
959         ch = ''
960         if str[:1] ==']':
961                 str = str[1:]
962                 ch = '>'
963
964         end = 0
965         while str[:1] ==')':
966                 end = end + 1
967                 str = str[1:]
968
969         
970         voices_append ("\\spanrequest \\stop \"slur\"" * end);
971         voices_append (ch)
972         return str
973
974 def try_parse_grace_delims (str, state):
975         if str[:1] =='{':
976                 if state.next_bar:
977                         voices_append(state.next_bar)
978                         state.next_bar = ''
979                 str = str[1:]
980                 voices_append ('\\grace { ')
981
982         if str[:1] =='}':
983                 str = str[1:]
984                 voices_append ('}')
985
986         return str
987
988
989 happy_count = 100
990 def parse_file (fn):
991         f = open (fn)
992         ls = f.readlines ()
993
994         select_voice('default', '')
995         lineno = 0
996         sys.stderr.write ("Line ... ")
997         sys.stderr.flush ()
998         __main__.state = state_list[current_voice_idx]
999         
1000         for ln in ls:
1001                 lineno = lineno + 1
1002
1003                 if not (lineno % happy_count):
1004                         sys.stderr.write ('[%d]'% lineno)
1005                         sys.stderr.flush ()
1006                 m = re.match  ('^([^%]*)%(.*)$',ln)  # add comments to current voice
1007                 if m:
1008                         if m.group(2):
1009                                 voices_append ('%% %s\n' % m.group(2))
1010                         ln = m.group (1)
1011
1012                 orig_ln = ln
1013                 
1014                 ln = try_parse_header_line (ln, state)
1015
1016                 # Try nibbling characters off until the line doesn't change.
1017                 prev_ln = ''
1018                 while ln != prev_ln:
1019                         prev_ln = ln
1020                         ln = try_parse_chord_delims (ln, state)
1021                         ln = try_parse_rest (ln, state)
1022                         ln = try_parse_articulation (ln,state)
1023                         ln = try_parse_note  (ln, state)
1024                         ln = try_parse_bar (ln, state)
1025                         ln = try_parse_tie (ln)
1026                         ln = try_parse_escape (ln)
1027                         ln = try_parse_guitar_chord (ln, state)
1028                         ln = try_parse_tuplet_begin (ln, state)
1029                         ln = try_parse_group_end (ln, state)
1030                         ln = try_parse_grace_delims (ln, state)
1031                         ln = junk_space (ln)
1032
1033                 if ln:
1034                         msg = "%s: %d: Huh?  Don't understand\n" % (fn, lineno)
1035                         sys.stderr.write (msg)
1036                         left = orig_ln[0:-len (ln)]
1037                         sys.stderr.write (left + '\n')
1038                         sys.stderr.write (' ' *  len (left) + ln + '\n')        
1039
1040
1041 def identify():
1042         sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1043
1044 def help ():
1045         print r"""
1046 Convert ABC to Mudela.
1047
1048 Usage: abc2ly [OPTION]... ABC-FILE
1049
1050 Options:
1051   -h, --help          this help
1052   -o, --output=FILE   set output filename to FILE
1053   -v, --version       version information
1054 """
1055
1056 def print_version ():
1057         print r"""abc2ly (GNU lilypond) %s""" % version
1058
1059
1060
1061 (options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output='])
1062 out_filename = ''
1063
1064 for opt in options:
1065         o = opt[0]
1066         a = opt[1]
1067         if o== '--help' or o == '-h':
1068                 help ()
1069                 sys.exit (0)
1070         if o == '--version' or o == '-v':
1071                 print_version ()
1072                 sys.exit(0)
1073                 
1074         if o == '--output' or o == '-o':
1075                 out_filename = a
1076         else:
1077                 print o
1078                 raise getopt.error
1079
1080 identify()
1081
1082 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1083 for f in files:
1084         if f == '-':
1085                 f = ''
1086
1087         sys.stderr.write ('Parsing... [%s]\n' % f)
1088         parse_file (f)
1089
1090         if not out_filename:
1091                 out_filename = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1092         sys.stderr.write ('Ly output to: %s...' % out_filename)
1093         outf = open (out_filename, 'w')
1094
1095 #       dump_global (outf)
1096         dump_header (outf ,header)
1097         dump_slyrics (outf)
1098         dump_voices (outf)
1099         dump_score (outf)
1100         dump_lyrics (outf)
1101         sys.stderr.write ('\n')
1102