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