]> git.donarmstrong.com Git - lilypond.git/blob - scripts/abc2ly.py
abc2ly: Fix church modes (Issue #512), was a simple upper/lowercase issue; Generalize...
[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                     # convert the mode to lower-case before comparing
688                     mode = m.group(2)[0:3].lower();
689                     if key_lookup.has_key(mode):
690                         # use the full mode, not only the first three letters
691                         key_info = m.group(1) + m.group(2).lower()
692                         clef_info = a[m.start(4):]
693                     else:
694                         key_info = m.group(1)
695                         clef_info = a[m.start(2):]
696                     __main__.global_key  = compute_key (key_info)
697                     k = lily_key (key_info)
698                     if k:
699                         voices_append ('\\key %s' % k)
700                     check_clef(clef_info)
701                 else:
702                     __main__.global_key  = compute_key (a)
703                     k = lily_key (a)
704                     if k:
705                         voices_append ('\\key %s \\major' % k)
706         if g == 'N': # Notes
707             header ['footnotes'] = header['footnotes'] +  '\\\\\\\\' + a
708         if g == 'O': # Origin
709             header ['origin'] = a
710         if g == 'X': # Reference Number
711             header ['crossRefNumber'] = a
712         if g == 'A': #        Area
713             header ['area'] = a
714         if g == 'H':        # History
715             header_append ('history', a)
716         if g == 'B':        # Book
717             header ['book'] = a
718         if g == 'C':        # Composer
719             if header.has_key('composer'):
720                 if a:
721                     header['composer'] = header['composer'] + '\\\\\\\\' + a
722             else:
723                 header['composer'] =  a
724         if g == 'S':
725             header ['subtitle'] = a
726         if g == 'L':        # Default note length
727             set_default_length (ln)
728         if g == 'V':        # Voice 
729             voice = re.sub (' .*$', '', a)
730             rest = re.sub ('^[^ \t]*  *', '', a)
731             if state.next_bar:
732                 voices_append(state.next_bar)
733                 state.next_bar = ''
734             select_voice (voice, rest)
735         if g == 'W':        # Words
736             lyrics_append(a)
737         if g == 'w':        # vocals
738             slyrics_append (a)
739         if g == 'Q':    #tempo
740             try_parse_q (a)
741         return ''
742     return ln
743
744 # we use in this order specified accidental, active accidental for bar,
745 # active accidental for key
746 def pitch_to_lilypond_name (name, acc, bar_acc, key):
747     s = ''
748     if acc == UNDEF:
749         if not nobarlines:
750             acc = bar_acc
751     if acc == UNDEF:
752         acc = key
753     if acc == -1:
754         s = 'es'
755     elif acc == 1:
756         s =  'is'
757     
758     if name > 4:
759         name = name -7
760     return(chr (name  + ord('c')) + s)
761
762
763 def octave_to_lilypond_quotes (o):
764     o = o + 2
765     s =''
766     if o < 0:
767         o = -o
768         s=','
769     else:
770         s ='\''
771
772     return s * o
773
774 def parse_num (str):
775     durstr = ''
776     while str and str[0] in DIGITS:
777         durstr = durstr + str[0]
778         str = str[1:]
779
780     n = None
781     if durstr:
782         n = int (durstr)
783     return (str,n)
784
785
786 def duration_to_lilypond_duration  (multiply_tup, defaultlen, dots):
787     base = 1
788     # (num /  den)  / defaultlen < 1/base
789     while base * multiply_tup[0] < multiply_tup[1]:
790         base = base * 2
791     if base == 1:
792         if (multiply_tup[0] / multiply_tup[1])  == 2:
793             base = '\\breve'
794         if (multiply_tup[0] / multiply_tup[1]) == 3:
795             base = '\\breve'
796             dots = 1
797         if (multiply_tup[0] / multiply_tup[1]) == 4:
798             base = '\\longa'
799     return '%s%s' % ( base, '.'* dots)
800
801 class Parser_state:
802     def __init__ (self):
803         self.in_acc = {}
804         self.next_articulation = ''
805         self.next_bar = ''
806         self.next_dots = 0
807         self.next_den = 1
808         self.parsing_tuplet = 0
809         self.plus_chord = 0
810         self.base_octave = 0
811         self.common_time = 0
812         self.parsing_beam = 0
813
814
815
816 # return (str, num,den,dots) 
817 def parse_duration (str, parser_state):
818     num = 0
819     den = parser_state.next_den
820     parser_state.next_den = 1
821
822     (str, num) = parse_num (str)
823     if not num:
824         num = 1
825     if len(str):
826         if str[0] == '/':
827             if len(str[0]):
828                 while str[:1] == '/':
829                     str= str[1:]
830                     d = 2
831                     if str[0] in DIGITS:
832                         (str, d) =parse_num (str)
833
834                     den = den * d
835
836     den = den * default_len
837     
838     current_dots = parser_state.next_dots
839     parser_state.next_dots = 0
840     if re.match ('[ \t]*[<>]', str):
841         while str[0] in HSPACE:
842             str = str[1:]
843         while str[0] == '>':
844             str = str [1:]
845             current_dots = current_dots + 1
846             parser_state.next_den = parser_state.next_den * 2
847         
848         while str[0] == '<':
849             str = str [1:]
850             den = den * 2
851             parser_state.next_dots = parser_state.next_dots + 1
852
853
854
855     try_dots = [3, 2, 1]
856     for d in try_dots:
857         f = 1 << d
858         multiplier = (2*f-1)
859         if num % multiplier == 0 and den % f == 0:
860             num = num / multiplier
861             den = den / f
862             current_dots = current_dots + d
863         
864     return (str, num,den,current_dots)
865
866
867 def try_parse_rest (str, parser_state):
868     if not str or str[0] <> 'z' and str[0] <> 'x':
869         return str
870
871     __main__.lyric_idx = -1
872
873     if parser_state.next_bar:
874         voices_append(parser_state.next_bar)
875         parser_state.next_bar = ''
876
877     if str[0] == 'z':
878         rest = 'r'
879     else:
880         rest = 's'
881     str = str[1:]
882
883     (str, num,den,d) = parse_duration (str, parser_state)
884     voices_append ('%s%s' % (rest, duration_to_lilypond_duration ((num,den), default_len, d)))
885     if parser_state.next_articulation:
886         voices_append (parser_state.next_articulation)
887         parser_state.next_articulation = ''
888
889     return str
890
891 artic_tbl = {
892     '.'  : '-.',
893     'T' : '^\\trill',
894     'H' : '^\\fermata',
895     'u' : '^\\upbow',
896     'K' : '^\\ltoe',
897     'k' : '^\\accent',
898     'M' : '^\\tenuto',
899     '~' : '^"~" ',
900     'J' : '',                # ignore slide
901     'R' : '',                # ignore roll
902     'S' : '^\\segno',
903     'O' : '^\\coda',
904     'v' : '^\\downbow'
905 }
906     
907 def try_parse_articulation (str, state):
908     while str and  artic_tbl.has_key(str[:1]):
909         state.next_articulation = state.next_articulation + artic_tbl[str[:1]]
910         if not artic_tbl[str[:1]]:
911             sys.stderr.write("Warning: ignoring `%s'\n" % str[:1] )
912
913         str = str[1:]
914
915     
916         
917     # s7m2 input doesnt care about spaces
918     if re.match('[ \t]*\(', str):
919         str = str.lstrip ()
920
921     slur_begin =0
922     while str[:1] =='(' and str[1] not in DIGITS:
923         slur_begin = slur_begin + 1
924         state.next_articulation = state.next_articulation + '('
925         str = str[1:]
926
927     return str
928         
929 #
930 # remember accidental for rest of bar
931 #
932 def set_bar_acc(note, octave, acc, state):
933     if acc == UNDEF:
934         return
935     n_oct = note + octave * 7
936     state.in_acc[n_oct] = acc
937
938 # get accidental set in this bar or UNDEF if not set
939 def get_bar_acc(note, octave, state):
940     n_oct = note + octave * 7
941     if state.in_acc.has_key(n_oct):
942         return(state.in_acc[n_oct])
943     else:
944         return(UNDEF)
945
946 def clear_bar_acc(state):
947     state.in_acc = {}
948
949
950 # if we are parsing a beam, close it off
951 def close_beam_state(state):
952     if state.parsing_beam and global_options.beams:
953         state.parsing_beam = 0
954         voices_append_back( ']' )
955
956         
957 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP  !
958 def try_parse_note (str, parser_state):
959     mud = ''
960
961     slur_begin =0
962     if not str:
963         return str
964
965     articulation =''
966     acc = UNDEF
967     if str[0] in '^=_':
968         c = str[0]
969         str = str[1:]
970         if c == '^':
971             acc = 1
972         if c == '=':
973             acc = 0
974         if c == '_':
975             acc = -1
976
977     octave = parser_state.base_octave
978     if str[0] in "ABCDEFG":
979         str = str[0].lower () + str[1:]
980         octave = octave - 1
981
982
983     notename = 0
984     if str[0] in "abcdefg":
985         notename = (ord(str[0]) - ord('a') + 5)%7
986         str = str[1:]
987     else:
988         return str                # failed; not a note!
989
990     
991     __main__.lyric_idx = -1
992
993     if parser_state.next_bar:
994         voices_append(parser_state.next_bar)
995         parser_state.next_bar = ''
996
997     while str[0] == ',':
998         octave = octave - 1
999         str = str[1:]
1000     while str[0] == '\'':
1001         octave = octave + 1
1002         str = str[1:]
1003
1004     (str, num,den,current_dots) = parse_duration (str, parser_state)
1005
1006     if re.match('[ \t]*\)', str):
1007         str = str.lstrip ()
1008     
1009     slur_end =0
1010     while str[:1] ==')':
1011         slur_end = slur_end + 1
1012         str = str[1:]
1013
1014     
1015     bar_acc = get_bar_acc(notename, octave, parser_state)
1016     pit = pitch_to_lilypond_name(notename, acc, bar_acc, global_key[notename])
1017     oct = octave_to_lilypond_quotes (octave)
1018     if acc != UNDEF and (acc == global_key[notename] or acc == bar_acc):
1019         mod='!'
1020     else:
1021         mod = ''
1022     voices_append ("%s%s%s%s" %
1023         (pit, oct, mod,
1024          duration_to_lilypond_duration ((num,den), default_len, current_dots)))
1025     
1026     set_bar_acc(notename, octave, acc, parser_state)
1027     if parser_state.next_articulation:
1028         articulation = articulation + parser_state.next_articulation
1029         parser_state.next_articulation = ''
1030
1031     voices_append (articulation)
1032
1033     if parser_state.parsing_tuplet:
1034         parser_state.parsing_tuplet = parser_state.parsing_tuplet - 1
1035         if not parser_state.parsing_tuplet:
1036             voices_append ("}")
1037     if slur_begin:
1038         voices_append ('-(' * slur_begin )
1039     if slur_end:
1040         voices_append ('-)' *slur_end )
1041
1042     if global_options.beams and \
1043      str[0] in '^=_ABCDEFGabcdefg' and \
1044      not parser_state.parsing_beam and \
1045      not parser_state.parsing_tuplet:
1046         parser_state.parsing_beam = 1
1047         voices_append_back( '[' )
1048         
1049     return str
1050
1051 def junk_space (str,state):
1052     while str and str[0] in '\t\n\r ':
1053         str = str[1:]
1054         close_beam_state(state)
1055
1056     return str
1057
1058
1059 def try_parse_guitar_chord (str, state):
1060     if str[:1] =='"':
1061         str = str[1:]
1062         gc = ''
1063         if str[0] == '_' or (str[0] == '^'):
1064             position = str[0]
1065             str = str[1:]
1066         else:
1067             position = '^'
1068         while str and str[0] != '"':
1069             gc = gc + str[0]
1070             str = str[1:]
1071             
1072         if str:
1073             str = str[1:]
1074         gc = re.sub('#', '\\#', gc)        # escape '#'s
1075         state.next_articulation = ("%c\"%s\"" % (position, gc)) \
1076                      + state.next_articulation
1077     return str
1078
1079 def try_parse_escape (str):
1080     if not str or str [0] != '\\':
1081         return str
1082     
1083     str = str[1:]
1084     if str[:1] =='K':
1085         key_table = compute_key ()
1086     return str
1087
1088 #
1089 # |] thin-thick double bar line
1090 # || thin-thin double bar line
1091 # [| thick-thin double bar line
1092 # :| left repeat
1093 # |: right repeat
1094 # :: left-right repeat
1095 # |1 volta 1
1096 # |2 volta 2
1097 old_bar_dict = {
1098 '|]' : '|.',
1099 '||' : '||',
1100 '[|' : '||',
1101 ':|' : ':|',
1102 '|:' : '|:',
1103 '::' : ':|:',
1104 '|1' : '|',
1105 '|2' : '|',
1106 ':|2' : ':|',
1107 '|' :  '|'
1108 }
1109 bar_dict = {
1110 '|]' : '\\bar "|."',
1111 '||' : '\\bar "||"',
1112 '[|' : '\\bar "||"',
1113 ':|' : '}',
1114 '|:' : '\\repeat volta 2 {',
1115 '::' : '} \\repeat volta 2 {',
1116 '|1' : '} \\alternative{{',
1117 '|2' : '} {',
1118 ':|2' : '} {',
1119 '|' :  '\\bar "|"'
1120  }
1121
1122
1123 warn_about = ['|:', '::', ':|', '|1', ':|2', '|2']
1124 alternative_opener = ['|1', '|2', ':|2']
1125 repeat_ender = ['::', ':|']
1126 repeat_opener = ['::', '|:']
1127 in_repeat = [''] * 8
1128 doing_alternative = [''] * 8
1129 using_old = ''
1130
1131 def try_parse_bar (str,state):
1132     global in_repeat, doing_alternative, using_old
1133     do_curly = ''
1134     bs = None
1135     if current_voice_idx < 0:
1136         select_voice ('default', '')
1137     # first try the longer one
1138     for trylen in [3,2,1]:
1139         if str[:trylen] and bar_dict.has_key (str[:trylen]):
1140             s = str[:trylen]
1141             if using_old:
1142                 bs = "\\bar \"%s\"" % old_bar_dict[s]
1143             else:
1144                 bs = "%s" % bar_dict[s]
1145             str = str[trylen:]
1146             if s in alternative_opener:
1147                 if not in_repeat[current_voice_idx]:
1148                     using_old = 't'
1149                     bs = "\\bar \"%s\"" % old_bar_dict[s]
1150                 else:
1151                     doing_alternative[current_voice_idx] = 't'
1152
1153             if s in repeat_ender:
1154                 if not in_repeat[current_voice_idx]:
1155                     sys.stderr.write("Warning: inserting repeat to beginning of notes.\n")
1156                     repeat_prepend()
1157                     in_repeat[current_voice_idx] = ''
1158                 else:
1159                     if doing_alternative[current_voice_idx]:
1160                         do_curly = 't'
1161                 if using_old:
1162                     bs = "\\bar \"%s\"" % old_bar_dict[s]
1163                 else:
1164                     bs =  bar_dict[s]
1165                 doing_alternative[current_voice_idx] = ''
1166                 in_repeat[current_voice_idx] = ''
1167             if s in repeat_opener:
1168                 in_repeat[current_voice_idx] = 't'
1169                 if using_old:
1170                     bs = "\\bar \"%s\"" % old_bar_dict[s]
1171                 else:
1172                     bs =  bar_dict[s]
1173             break
1174     if str[:1] == '|':
1175         state.next_bar = '|\n'
1176         str = str[1:]
1177         clear_bar_acc(state)
1178         close_beam_state(state)
1179     
1180     if bs <> None or state.next_bar != '':
1181         if state.parsing_tuplet:
1182             state.parsing_tuplet =0
1183             voices_append ('} ')
1184         
1185     if bs <> None:
1186         clear_bar_acc(state)
1187         close_beam_state(state)
1188         voices_append (bs)
1189         if do_curly != '':
1190             voices_append("} ")
1191             do_curly = ''
1192     return str
1193
1194 def try_parse_tie (str):
1195     if str[:1] =='-':
1196         str = str[1:]
1197         voices_append (' ~ ')
1198     return str
1199
1200 def bracket_escape (str, state):
1201     m = re.match ( '^([^\]]*)] *(.*)$', str)
1202     if m:
1203         cmd = m.group (1)
1204         str = m.group (2)
1205         try_parse_header_line (cmd, state)
1206     return str
1207
1208 def try_parse_chord_delims (str, state):
1209     if str[:1] =='[':
1210         str = str[1:]
1211         if re.match('[A-Z]:', str):        # bracket escape
1212             return bracket_escape(str, state)
1213         if state.next_bar:
1214             voices_append(state.next_bar)
1215             state.next_bar = ''
1216         voices_append ('<<')
1217
1218     if str[:1] == '+':
1219         str = str[1:]
1220         if state.plus_chord:
1221             voices_append ('>>')
1222             state.plus_chord = 0
1223         else:
1224             if state.next_bar:
1225                 voices_append(state.next_bar)
1226                 state.next_bar = ''
1227             voices_append ('<<')
1228             state.plus_chord = 1
1229
1230     ch = ''
1231     if str[:1] ==']':
1232         str = str[1:]
1233         ch = '>>'
1234
1235     end = 0
1236     while str[:1] ==')':
1237         end = end + 1
1238         str = str[1:]
1239
1240     
1241     voices_append ("\\spanrequest \\stop \"slur\"" * end)
1242     voices_append (ch)
1243     return str
1244
1245 def try_parse_grace_delims (str, state):
1246     if str[:1] =='{':
1247         if state.next_bar:
1248             voices_append(state.next_bar)
1249             state.next_bar = ''
1250         str = str[1:]
1251         voices_append ('\\grace { ')
1252
1253     if str[:1] =='}':
1254         str = str[1:]
1255         voices_append ('}')
1256
1257     return str
1258
1259 def try_parse_comment (str):
1260     global nobarlines
1261     if (str[0] == '%'):
1262         if str[0:5] == '%MIDI':
1263 #the nobarlines option is necessary for an abc to lilypond translator for
1264 #exactly the same reason abc2midi needs it: abc requires the user to enter
1265 #the note that will be printed, and MIDI and lilypond expect entry of the
1266 #pitch that will be played.
1267 #
1268 #In standard 19th century musical notation, the algorithm for translating
1269 #between printed note and pitch involves using the barlines to determine
1270 #the scope of the accidentals.
1271 #
1272 #Since ABC is frequently used for music in styles that do not use this
1273 #convention, such as most music written before 1700, or ethnic music in
1274 #non-western scales, it is necessary to be able to tell a translator that
1275 #the barlines should not affect its interpretation of the pitch.  
1276             if 'nobarlines' in str:
1277                 nobarlines = 1
1278         elif str[0:3] == '%LY':
1279             p = str.find ('voices')
1280             if (p > -1):
1281                 voices_append(str[p+7:])
1282                 voices_append("\n")
1283             p = str.find ('slyrics')
1284             if (p > -1):
1285                 slyrics_append(str[p+8:])
1286             
1287 #write other kinds of appending  if we ever need them.                        
1288     return str
1289
1290 lineno = 0
1291 happy_count = 100
1292 def parse_file (fn):
1293     f = open (fn)
1294     ls = f.readlines ()
1295     ls = map (lambda x: re.sub ("\r$", '', x), ls)
1296
1297     select_voice('default', '')
1298     global lineno
1299     lineno = 0
1300     sys.stderr.write ("Line ... ")
1301     sys.stderr.flush ()
1302     __main__.state = state_list[current_voice_idx]
1303     
1304     for ln in ls:
1305         lineno = lineno + 1
1306
1307         if not (lineno % happy_count):
1308             sys.stderr.write ('[%d]'% lineno)
1309             sys.stderr.flush ()
1310         m = re.match  ('^([^%]*)%(.*)$',ln)  # add comments to current voice
1311         if m:
1312             if m.group(2):
1313                 try_parse_comment(m.group(2))
1314                 voices_append ('%% %s\n' % m.group(2))
1315             ln = m.group (1)
1316
1317         orig_ln = ln
1318         
1319         ln = try_parse_header_line (ln, state)
1320
1321         # Try nibbling characters off until the line doesn't change.
1322         prev_ln = ''
1323         while ln != prev_ln:
1324             prev_ln = ln
1325             ln = try_parse_chord_delims (ln, state)
1326             ln = try_parse_rest (ln, state)
1327             ln = try_parse_articulation (ln,state)
1328             ln = try_parse_note  (ln, state)
1329             ln = try_parse_bar (ln, state)
1330             ln = try_parse_tie (ln)
1331             ln = try_parse_escape (ln)
1332             ln = try_parse_guitar_chord (ln, state)
1333             ln = try_parse_tuplet_begin (ln, state)
1334             ln = try_parse_group_end (ln, state)
1335             ln = try_parse_grace_delims (ln, state)
1336             ln = junk_space (ln, state)
1337
1338         if ln:
1339             error ("%s: %d: Huh?  Don't understand\n" % (fn, lineno))
1340             left = orig_ln[0:-len (ln)]
1341             sys.stderr.write (left + '\n')
1342             sys.stderr.write (' ' *  len (left) + ln + '\n')        
1343
1344
1345 def identify():
1346     sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
1347
1348 authors = """
1349 Written by Han-Wen Nienhuys <hanwen@xs4all.nl>, Laura Conrad
1350 <lconrad@laymusic.org>, Roy Rankin <Roy.Rankin@@alcatel.com.au>.
1351 """
1352
1353 def print_version ():
1354     print r"""abc2ly (GNU lilypond) %s""" % version
1355
1356 def get_option_parser ():
1357     p = ly.get_option_parser (usage=_ ("%s [OPTION]... FILE") % 'abc2ly',
1358                  description=_ ('''abc2ly converts ABC music files (see
1359 %s) to LilyPond input.
1360 ''') % 'http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt',
1361                  add_help_option=False)
1362
1363     p.version = "abc2ly (LilyPond) @TOPLEVEL_VERSION@"
1364     p.add_option("--version",
1365                  action="version",
1366                  help=_ ("show version number and exit"))
1367
1368     p.add_option("-h", "--help",
1369                  action="help",
1370                  help=_ ("show this help and exit"))
1371     p.add_option ('-o', '--output', metavar='FILE',
1372                   help=_ ("write output to FILE"),
1373                   action='store')
1374     p.add_option ('-s', '--strict', help=_ ("be strict about success"),
1375                   action='store_true')
1376     p.add_option ('-b', '--beams', help=_ ("preserve ABC's notion of beams"))
1377     p.add_option_group ('',
1378                         description=(
1379             _ ('Report bugs via %s')
1380             % 'http://post.gmane.org/post.php'
1381             '?group=gmane.comp.gnu.lilypond.bugs') + '\n')
1382     return p
1383
1384
1385 option_parser = get_option_parser ()
1386 (global_options, files) = option_parser.parse_args ()
1387
1388
1389 identify ()
1390
1391 header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version
1392 for f in files:
1393     if f == '-':
1394         f = ''
1395
1396     sys.stderr.write ('Parsing `%s\'...\n' % f)
1397     parse_file (f)
1398
1399     if not global_options.output:
1400         global_options.output = os.path.basename (os.path.splitext (f)[0]) + ".ly"
1401     sys.stderr.write ('lilypond output to: `%s\'...' % global_options.output)
1402     outf = open (global_options.output, 'w')
1403
1404 # don't substitute @VERSION@. We want this to reflect
1405 # the last version that was verified to work.
1406     outf.write ('\\version "2.7.40"\n')
1407
1408 #        dump_global (outf)
1409     dump_header (outf, header)
1410     dump_slyrics (outf)
1411     dump_voices (outf)
1412     dump_score (outf)
1413     dump_lyrics (outf)
1414     sys.stderr.write ('\n')
1415