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