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