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