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