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