]> git.donarmstrong.com Git - lilypond.git/blob - scripts/abc2ly.py
2fb57404e6924213d2242e6e8ce024675ef12615
[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                                 __main__.global_key  =compute_key (a)# ugh.
509                                 m = re.match ('^([^ \t]*) *(.*)$', a) # seperate clef info
510                                 if m:
511                                         voices_append ('\\key %s;' % lily_key(m.group(1)))
512                                         check_clef(m.group(2))
513                                 else:
514                                         voices_append ('\\key %s;' % lily_key(a))
515                 if g == 'O': # Origin
516                         header ['origin'] = a
517                 if g == 'X': # Reference Number
518                         header ['crossRefNumber'] = a
519                 if g == 'A': #  Area
520                         header ['area'] = a
521                 if g == 'H':    # History
522                         header_append ('history', a)
523                 if g == 'B':    # Book
524                         header ['book'] = a
525                 if g == 'C':    # Composer
526                         if header.has_key('composer'):
527                                 if a:
528                                         header['composer'] = header['composer'] + '\\\\\\\\' + a
529                         else:
530                                 header['composer'] =  a
531                 if g == 'S':
532                         header ['subtitle'] = a
533                 if g == 'L':    # Default note length
534                         set_default_length (ln)
535                 if g == 'V':    # Voice 
536                         voice = re.sub (' .*$', '', a)
537                         rest = re.sub ('^[^ \t]*  *', '', a)
538                         if state.next_bar:
539                                 voices_append(state.next_bar)
540                                 state.next_bar = ''
541                         select_voice (voice, rest)
542                 if g == 'W':    # Words
543                         lyrics_append(a);
544                 if g == 'w':    # vocals
545                         slyrics_append (a);
546
547                 return ''
548         return ln
549
550 # we use in this order specified accidental, active accidental for bar,
551 # active accidental for key
552 def pitch_to_mudela_name (name, acc, bar_acc, key):
553         s = ''
554         if acc == UNDEF:
555                 acc = bar_acc
556         if acc == UNDEF:
557                 acc = key
558         if acc == -1:
559                 s = 'es'
560         elif acc == 1:
561                 s =  'is'
562         
563         if name > 4:
564                 name = name -7
565         return(chr (name  + ord('c')) + s)
566
567
568 def octave_to_mudela_quotes (o):
569         o = o + 2
570         s =''
571         if o < 0:
572                 o = -o
573                 s=','
574         else:
575                 s ='\''
576
577         return s * o
578
579 def parse_num (str):
580         durstr = ''
581         while str and str[0] in DIGITS:
582                 durstr = durstr + str[0]
583                 str = str[1:]
584
585         n = None
586         if durstr:
587                 n  =string.atoi (durstr) 
588         return (str,n)
589
590
591 def duration_to_mudela_duration  (multiply_tup, defaultlen, dots):
592         base = 1
593
594         # (num /  den)  / defaultlen < 1/base
595         while base * multiply_tup[0] < multiply_tup[1]:
596                 base = base * 2
597
598
599         return '%d%s' % ( base, '.'* dots)
600
601 class Parser_state:
602         def __init__ (self):
603                 self.in_acc = {}
604                 self.next_articulation = ''
605                 self.next_bar = ''
606                 self.next_dots = 0
607                 self.next_den = 1
608                 self.parsing_tuplet = 0
609                 self.plus_chord = 0
610                 self.base_octave = 0
611                 self.common_time = 0
612
613
614
615 # return (str, num,den,dots) 
616 def parse_duration (str, parser_state):
617         num = 0
618         den = parser_state.next_den
619         parser_state.next_den = 1
620
621         (str, num) = parse_num (str)
622         if not num:
623                 num = 1
624         
625         if str[0] == '/':
626                 while str[:1] == '/':
627                         str= str[1:]
628                         d = 2
629                         if str[0] in DIGITS:
630                                 (str, d) =parse_num (str)
631
632                         den = den * d
633
634         den = den * default_len
635         
636         current_dots = parser_state.next_dots
637         parser_state.next_dots = 0
638         if re.match ('[ \t]*[<>]', str):
639                 while str[0] in HSPACE:
640                         str = str[1:]
641                 while str[0] == '>':
642                         str = str [1:]
643                         current_dots = current_dots + 1;
644                         parser_state.next_den = parser_state.next_den * 2
645                 
646                 while str[0] == '<':
647                         str = str [1:]
648                         den = den * 2
649                         parser_state.next_dots = parser_state.next_dots + 1
650
651
652
653         try_dots = [3, 2, 1]
654         for d in try_dots:
655                 f = 1 << d
656                 multiplier = (2*f-1)
657                 if num % multiplier == 0 and den % f == 0:
658                         num = num / multiplier
659                         den = den / f
660                         current_dots = current_dots + d
661                 
662         return (str, num,den,current_dots)
663
664
665 def try_parse_rest (str, parser_state):
666         if not str or str[0] <> 'z' and str[0] <> 'x':
667                 return str
668
669         __main__.lyric_idx = -1
670
671         if parser_state.next_bar:
672                 voices_append(parser_state.next_bar)
673                 parser_state.next_bar = ''
674
675         if str[0] == 'z':
676                 rest = 'r'
677         else:
678                 rest = 's'
679         str = str[1:]
680
681         (str, num,den,d) = parse_duration (str, parser_state)
682         voices_append ('%s%s' % (rest, duration_to_mudela_duration ((num,den), default_len, d)))
683         if parser_state.next_articulation:
684                 voices_append (parser_state.next_articulation)
685                 parser_state.next_articulation = ''
686
687         return str
688
689 artic_tbl = {
690         '.'  : '-.',
691          'T' : '^\\trill',
692          'H' : '^\\fermata',
693          'u' : '^\\upbow',
694          'K' : '^\\ltoe',
695          'k' : '^\\accent',
696          'M' : '^\\tenuto',
697          '~' : '^"~" ',
698          'J' : '',              # ignore slide
699          'R' : '',              # ignore roll
700          'v' : '^\\downbow'
701 }
702         
703 def try_parse_articulation (str, state):
704         
705         while str and  artic_tbl.has_key(str[:1]):
706                 state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
707                 if not artic_tbl[str[:1]]:
708                         sys.stderr.write("Warning: ignoring %s\n" % str[:1] )
709
710                 str = str[1:]
711
712         
713                 
714         # s7m2 input doesnt care about spaces
715         if re.match('[ \t]*\(', str):
716                 str = string.lstrip (str)
717
718         slur_begin =0
719         while str[:1] =='(' and str[1] not in DIGITS:
720                 slur_begin = slur_begin + 1
721                 state.next_articulation = state.next_articulation + '('
722                 str = str[1:]
723
724         return str
725                 
726 #
727 # remember accidental for rest of bar
728 #
729 def set_bar_acc(note, octave, acc, state):
730         if acc == UNDEF:
731                 return
732         n_oct = note + octave * 7
733         state.in_acc[n_oct] = acc
734
735 # get accidental set in this bar or UNDEF if not set
736 def get_bar_acc(note, octave, state):
737         n_oct = note + octave * 7
738         if state.in_acc.has_key(n_oct):
739                 return(state.in_acc[n_oct])
740         else:
741                 return(UNDEF)
742
743 def clear_bar_acc(state):
744         for k in state.in_acc.keys():
745                 del state.in_acc[k]
746         
747
748                 
749 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
750 def try_parse_note (str, parser_state):
751         mud = ''
752
753         slur_begin =0
754         if not str:
755                 return str
756
757         articulation =''
758         acc = UNDEF
759         if str[0] in '^=_':
760                 c = str[0]
761                 str = str[1:]
762                 if c == '^':
763                         acc = 1
764                 if c == '=':
765                         acc = 0
766                 if c == '_':
767                         acc = -1
768
769         octave = parser_state.base_octave
770         if str[0] in "ABCDEFG":
771                 str = string.lower (str[0]) + str[1:]
772                 octave = octave - 1
773
774
775         notename = 0
776         if str[0] in "abcdefg":
777                 notename = (ord(str[0]) - ord('a') + 5)%7
778                 str = str[1:]
779         else:
780                 return str              # failed; not a note!
781
782         
783         __main__.lyric_idx = -1
784
785         if parser_state.next_bar:
786                 voices_append(parser_state.next_bar)
787                 parser_state.next_bar = ''
788
789         while str[0] == ',':
790                  octave = octave - 1
791                  str = str[1:]
792         while str[0] == '\'':
793                  octave = octave + 1
794                  str = str[1:]
795
796         (str, num,den,current_dots) = parse_duration (str, parser_state)
797
798
799         if re.match('[ \t]*\)', str):
800                 str = string.lstrip (str)
801         
802         slur_end =0
803         while str[:1] ==')':
804                 slur_end = slur_end + 1
805                 str = str[1:]
806
807         
808         if slur_end:
809                 voices_append ('%s' % ')' *slur_end )
810
811         bar_acc = get_bar_acc(notename, octave, parser_state)
812         pit = pitch_to_mudela_name(notename, acc, bar_acc, global_key[notename])
813         oct = octave_to_mudela_quotes (octave)
814         if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
815                 mod='!'
816         else:
817                 mod = ''
818         voices_append ("%s%s%s%s" %
819                 (pit, oct, mod,
820                  duration_to_mudela_duration ((num,den), default_len, current_dots)))
821         
822         set_bar_acc(notename, octave, acc, parser_state)
823         if parser_state.next_articulation:
824                 articulation = articulation + parser_state.next_articulation
825                 parser_state.next_articulation = ''
826
827         voices_append (articulation)
828
829         if parser_state.parsing_tuplet:
830                 parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
831                 if not parser_state.parsing_tuplet:
832                         voices_append ("}")
833         if slur_begin:
834                 voices_append ('%s' % '(' * slur_begin )
835
836
837         return str
838
839 def junk_space (str):
840         while str and str[0] in '\t\n ':
841                 str = str[1:]
842
843         return str
844
845
846 def try_parse_guitar_chord (str, state):
847         if str[:1] =='"':
848                 str = str[1:]
849                 gc = ''
850                 while str and str[0] != '"':
851                         gc = gc + str[0]
852                         str = str[1:]
853                         
854                 if str:
855                         str = str[1:]
856                 gc = re.sub('#', '\\#', gc)     # escape '#'s
857                 state.next_articulation = ("-\"%s\"" % gc) + state.next_articulation
858         return str
859
860 def try_parse_escape (str):
861         if not str or str [0] != '\\':
862                 return str
863         
864         str = str[1:]
865         if str[:1] =='K':
866                 key_table = compute_key ()
867         return str
868
869 #
870 # |] thin-thick double bar line
871 # || thin-thin double bar line
872 # [| thick-thin double bar line
873 # :| left repeat
874 # |: right repeat
875 # :: left-right repeat
876 # |1 volta 1
877 # |2 volta 2
878 bar_dict = {
879 '|]' : '|.',
880 '||' : '||',
881 '[|' : '||',
882 ':|' : ':|',
883 '|:' : '|:',
884 '::' : '::',
885 '|1' : '|',
886 '|2' : '|',
887 ':|2' : ':|'
888 }
889
890
891 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
892
893 def try_parse_bar (str,state):
894         bs = None
895
896         # first try the longer one
897         for trylen in [3,2]:
898                 if str[:trylen] and bar_dict.has_key (str[:trylen]):
899                         s = str[:trylen]
900                         bs = "\\bar \"%s\";" % bar_dict[s]
901                         if s in warn_about:
902                                 sys.stderr.write('Warning kludging for barline `%s\'\n' % s)
903                         str = str[trylen:]
904                         break
905
906         if str[:1] == '|':
907                 state.next_bar = '|\n'
908                 str = str[1:]
909                 clear_bar_acc(state)
910         
911         if bs <> None or state.next_bar != '':
912                 if state.parsing_tuplet:
913                         state.parsing_tuplet =0
914                         voices_append ('} ')
915                 
916         if bs <> None:
917                 clear_bar_acc(state)
918                 voices_append (bs)
919
920         return str
921
922 def try_parse_tie (str):
923         if str[:1] =='-':
924                 str = str[1:]
925                 voices_append (' ~ ')
926         return str
927
928 def bracket_escape (str, state):
929         m = re.match ( '^([^\]]*)] *(.*)$', str)
930         if m:
931                 cmd = m.group (1)
932                 str = m.group (2)
933                 try_parse_header_line (cmd, state)
934         return str
935
936 def try_parse_chord_delims (str, state):
937         if str[:1] =='[':
938                 str = str[1:]
939                 if re.match('[A-Z]:', str):     # bracket escape
940                         return bracket_escape(str, state)
941                 if state.next_bar:
942                         voices_append(state.next_bar)
943                         state.next_bar = ''
944                 voices_append ('<')
945
946         if str[:1] == '+':
947                 str = str[1:]
948                 if state.plus_chord:
949                         voices_append ('>')
950                         state.plus_chord = 0
951                 else:
952                         if state.next_bar:
953                                 voices_append(state.next_bar)
954                                 state.next_bar = ''
955                         voices_append ('<')
956                         state.plus_chord = 1
957
958         ch = ''
959         if str[:1] ==']':
960                 str = str[1:]
961                 ch = '>'
962
963         end = 0
964         while str[:1] ==')':
965                 end = end + 1
966                 str = str[1:]
967
968         
969         voices_append ("\\spanrequest \\stop \"slur\"" * end);
970         voices_append (ch)
971         return str
972
973 def try_parse_grace_delims (str, state):
974         if str[:1] =='{':
975                 if state.next_bar:
976                         voices_append(state.next_bar)
977                         state.next_bar = ''
978                 str = str[1:]
979                 voices_append ('\\grace { ')
980
981         if str[:1] =='}':
982                 str = str[1:]
983                 voices_append ('}')
984
985         return str
986
987
988 happy_count = 100
989 def parse_file (fn):
990         f = open (fn)
991         ls = f.readlines ()
992
993         select_voice('default', '')
994         lineno = 0
995         sys.stderr.write ("Line ... ")
996         sys.stderr.flush ()
997         __main__.state = state_list[current_voice_idx]
998         
999         for ln in ls:
1000                 lineno = lineno + 1
1001
1002                 if not (lineno % happy_count):
1003                         sys.stderr.write ('[%d]'% lineno)
1004                         sys.stderr.flush ()
1005                 m = re.match  ('^([^%]*)%(.*)$',ln)  # add comments to current voice
1006                 if m:
1007                         if m.group(2):
1008                                 voices_append ('%% %s\n' % m.group(2))
1009                         ln = m.group (1)
1010
1011                 orig_ln = ln
1012                 
1013                 ln = try_parse_header_line (ln, state)
1014
1015                 # Try nibbling characters off until the line doesn't change.
1016                 prev_ln = ''
1017                 while ln != prev_ln:
1018                         prev_ln = ln
1019                         ln = try_parse_chord_delims (ln, state)
1020                         ln = try_parse_rest (ln, state)
1021                         ln = try_parse_articulation (ln,state)
1022                         ln = try_parse_note  (ln, state)
1023                         ln = try_parse_bar (ln, state)
1024                         ln = try_parse_tie (ln)
1025                         ln = try_parse_escape (ln)
1026                         ln = try_parse_guitar_chord (ln, state)
1027                         ln = try_parse_tuplet_begin (ln, state)
1028                         ln = try_parse_group_end (ln, state)
1029                         ln = try_parse_grace_delims (ln, state)
1030                         ln = junk_space (ln)
1031
1032                 if ln:
1033                         msg = "%s: %d: Huh?  Don't understand\n" % (fn, lineno)
1034                         sys.stderr.write (msg)
1035                         left = orig_ln[0:-len (ln)]
1036                         sys.stderr.write (left + '\n')
1037                         sys.stderr.write (' ' *  len (left) + ln + '\n')        
1038
1039
1040 def identify():
1041         sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1042
1043 def help ():
1044         print r"""
1045 Convert ABC to Mudela.
1046
1047 Usage: abc2ly [OPTION]... ABC-FILE
1048
1049 Options:
1050   -h, --help          this help
1051   -o, --output=FILE   set output filename to FILE
1052   -v, --version       version information
1053 """
1054
1055 def print_version ():
1056         print r"""abc2ly (GNU lilypond) %s""" % version
1057
1058
1059
1060 (options, files) = getopt.getopt (sys.argv[1:], 'vo:h', ['help','version', 'output='])
1061 out_filename = ''
1062
1063 for opt in options:
1064         o = opt[0]
1065         a = opt[1]
1066         if o== '--help' or o == '-h':
1067                 help ()
1068                 sys.exit (0)
1069         if o == '--version' or o == '-v':
1070                 print_version ()
1071                 sys.exit(0)
1072                 
1073         if o == '--output' or o == '-o':
1074                 out_filename = a
1075         else:
1076                 print o
1077                 raise getopt.error
1078
1079 identify()
1080
1081 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1082 for f in files:
1083         if f == '-':
1084                 f = ''
1085
1086         sys.stderr.write ('Parsing... [%s]\n' % f)
1087         parse_file (f)
1088
1089         if not out_filename:
1090                 out_filename = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1091         sys.stderr.write ('Ly output to: %s...' % out_filename)
1092         outf = open (out_filename, 'w')
1093
1094 #       dump_global (outf)
1095         dump_header (outf ,header)
1096         dump_slyrics (outf)
1097         dump_voices (outf)
1098         dump_score (outf)
1099         dump_lyrics (outf)
1100         sys.stderr.write ('\n')
1101