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