lilypond-1.3.139
[lilypond.git] / scripts / abc-2-ly.py
blobdd4bcf263766dec2bdb03eb79e49c928b403006f
1 #!@PYTHON@
3 # once upon a rainy monday afternoon.
5 # ...
7 # (not finished.)
8 # ABC standard v1.6: http://www.gre.ac.uk/~c.walshaw/abc2mtex/abc.txt
9 #
11 program_name = 'abc-to-ly'
12 version = '@TOPLEVEL_VERSION@'
13 import __main__
14 import getopt
15 import sys
16 import re
17 import string
18 try:
19 import mpz
20 except:
21 sys.stderr.write ("This script needs Python 1.5.1\n")
22 sys.exit (1)
25 header = {}
26 lyrics = []
27 voices = []
28 global_voice_stuff = []
29 default_len = 4
30 global_key = [0] * 7 # UGH
31 names = ["One", "Two", "Three"]
32 DIGITS='0123456789'
33 HSPACE=' \t'
35 def gcd (a, b):
36 while a % b:
37 a,b = b, a % b
38 return b
40 class Rational:
41 def __init__ (self, n, d = 1):
42 self.num = n
43 self.den = d
45 def simplify (self):
46 g = gcd (self.num, self.den)
47 self.num = self.num / g
48 self.den = self.den /g
49 if self.den < 0:
50 self.den = - self.den
51 self.num = - self.num
53 def __sub__ (self, other):
54 pass
57 def dump_global ():
58 print ("global = \\notes{")
59 for i in global_voice_stuff:
60 print (i);
61 print ("}")
64 def dump_header (hdr):
65 print '\\header {'
66 for k in hdr.keys ():
67 print '%s = "%s";\n'% (k,hdr[k])
68 print '}'
70 def dump_lyrics ():
71 for i in range (len (lyrics)):
72 print ("verse%s = \\lyrics {" % names [i])
73 print (lyrics [i])
74 print ("}")
76 def dump_voices ():
77 for i in range (len (voices)):
78 print ("voice%s = \\notes {" % names [i])
79 print (voices [i])
80 print ("}")
82 def dump_score ():
83 print ("\\score{")
84 print (" \\notes<")
85 print (" \\global")
86 for i in range (len (voices)):
87 print (" \\context Staff=%s \\voice%s" %
88 (names [i], names [i]))
89 for i in range (len (lyrics)):
90 j = i
91 if j >= len (voices):
92 j = len (voices) - 1
93 print (" \\context Lyrics=%s \\rhythm \\voice%s \\verse%s" %
94 (names [i], names [j], names [i]))
95 print (" >")
96 dump_header (header)
97 #print "%%%s" % global_voice_stuff, 1
98 print ("}")
100 def set_default_length (s):
101 m = re.search ('1/([0-9]+)', s)
102 if m:
103 __main__.default_len = string.atoi ( m.group (1))
105 def gulp_file(f):
106 try:
107 i = open(f)
108 i.seek (0, 2)
109 n = i.tell ()
110 i.seek (0,0)
111 except:
112 sys.stderr.write ("can't open file: %s\n" % f)
113 return ''
114 s = i.read (n)
115 if len (s) <= 0:
116 sys.stderr.write ("gulped emty file: %s\n" % f)
117 i.close ()
118 return s
121 # pitch manipulation. Tuples are (name, alteration).
122 # 0 is (central) C. Alteration -1 is a flat, Alteration +1 is a sharp
123 # pitch in semitones.
124 def semitone_pitch (tup):
125 p =0
127 t = tup[0]
128 p = p + 12 * (t / 7)
129 t = t % 7
131 if t > 2:
132 p = p- 1
134 p = p + t* 2 + tup[1]
135 return p
137 def fifth_above_pitch (tup):
138 (n, a) = (tup[0] + 4, tup[1])
140 difference = 7 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
141 a = a + difference
143 return (n,a)
145 def sharp_keys ():
146 p = (0,0)
147 l = []
148 k = 0
149 while 1:
150 l.append (p)
151 (t,a) = fifth_above_pitch (p)
152 if semitone_pitch((t,a)) % 12 == 0:
153 break
155 p = (t % 7, a)
156 return l
158 def flat_keys ():
159 p = (0,0)
160 l = []
161 k = 0
162 while 1:
163 l.append (p)
164 (t,a) = quart_above_pitch (p)
165 if semitone_pitch((t,a)) % 12 == 0:
166 break
168 p = (t % 7, a)
169 return l
171 def quart_above_pitch (tup):
172 (n, a) = (tup[0] + 3, tup[1])
174 difference = 5 - (semitone_pitch ((n,a)) - semitone_pitch (tup))
175 a = a + difference
177 return (n,a)
180 def compute_key (k):
181 k = string.lower (k)
182 intkey = (ord (k[0]) - ord('a') + 5) % 7
183 intkeyacc =0
184 k = k[1:]
186 if k and k[0] == 'b':
187 intkeyacc = -1
188 k = k[1:]
189 elif k and k[0] == '#':
190 intkeyacc = 1
191 k = k[1:]
193 keytup = (intkey, intkeyacc)
195 sharp_key_seq = sharp_keys ()
196 flat_key_seq = flat_keys ()
198 accseq = None
199 accsign = 0
200 if keytup in sharp_key_seq:
201 accsign = 1
202 key_count = sharp_key_seq.index (keytup)
203 accseq = map (lambda x: (4*x -1 ) % 7, range (1, key_count + 1))
205 elif keytup in flat_key_seq:
206 accsign = -1
207 key_count = flat_key_seq.index (keytup)
208 accseq = map (lambda x: (3*x + 3 ) % 7, range (1, key_count + 1))
209 else:
210 raise "Huh"
212 key_table = [0] * 7
213 for a in accseq:
214 key_table[a] = key_table[a] + accsign
217 return key_table
219 tup_lookup = {
220 '3' : '2/3',
221 '4' : '4/3',
222 '5' : '4/5',
223 '6' : '4/6',
227 def try_parse_tuplet_begin (str, state):
228 if str and str[0] in DIGITS:
229 dig = str[0]
230 str = str[1:]
231 state.parsing_tuplet = 1
233 voices_append ("\\times %s {" % tup_lookup[dig])
234 return str
236 def try_parse_group_end (str, state):
237 if str and str[0] in HSPACE:
238 str = str[1:]
239 if state.parsing_tuplet:
240 state.parsing_tuplet = 0
241 voices_append ("}")
242 return str
244 def header_append (key, a):
245 s = ''
246 if header.has_key (key):
247 s = header[key] + "\n"
248 header [key] = s + a
250 def lyrics_append (a):
251 i = len (lyrics) - 1
252 if i < 0:
253 i = 0
254 if len (lyrics) <= i:
255 lyrics.append ('')
256 lyrics [i] = lyrics [i] + a + "\n"
258 def voices_append (a):
259 i = len (voices) - 1
260 if i < 0:
261 i = 0
262 if len (voices) <= i:
263 voices.append ('')
264 voices [i] = voices [i] + a + "\n"
266 def try_parse_header_line (ln):
267 m = re.match ('^(.): *(.*)$', ln)
269 if m:
270 g =m.group (1)
271 a = m.group (2)
272 a = re.sub ('"', '\\"', a)
273 if g == 'T':
274 header['title'] = a
275 if g == 'M':
276 if a == 'C':
277 a = '4/4'
278 global_voice_stuff.append ('\\time %s;' % a)
279 if g == 'K':
280 __main__.global_key =compute_key (a)# ugh.
282 global_voice_stuff.append ('\\key %s;' % a)
283 if g == 'O':
284 header ['origin'] = a
285 if g == 'X':
286 header ['crossRefNumber'] = a
287 if g == 'A':
288 header ['area'] = a
289 if g == 'H':
290 header_append ('history', a)
291 if g == 'B':
292 header ['book'] = a
293 if g == 'S':
294 header ['subtitle'] = a
295 if g == 'L':
296 set_default_length (ln)
297 if g == 'W':
298 if not len (a):
299 lyrics.append ('')
300 else:
301 lyrics_append (a);
302 return m
304 def pitch_to_mudela_name (name, acc):
305 s = ''
306 if acc < 0:
307 s = 'es'
308 acc = -acc
309 elif acc > 0:
310 s = 'is'
312 if name > 4:
313 name = name -7
314 return chr (name + ord('c')) + s * acc
316 def octave_to_mudela_quotes (o):
317 o = o + 2
318 s =''
319 if o < 0:
320 o = -o
321 s=','
322 else:
323 s ='\''
325 return s * o
327 def parse_num (str):
328 durstr = ''
329 while str and str[0] in DIGITS:
330 durstr = durstr + str[0]
331 str = str[1:]
333 n = None
334 if durstr:
335 n =string.atoi (durstr)
336 return (str,n)
339 def duration_to_mudela_duration (multiply_tup, defaultlen, dots):
340 base = 1
342 # (num / den) / defaultlen < 1/base
343 while base * multiply_tup[0] < defaultlen * multiply_tup[1]:
344 base = base * 2
347 return '%d%s' % ( base, '.'* dots)
349 class Parser_state:
350 def __init__ (self):
351 self.next_dots = 0
352 self.next_den = 1
353 self.parsing_tuplet = 0
356 # WAT IS ABC EEN ONTZETTENDE PROGRAMMEERPOEP !
357 def try_parse_note (str, parser_state):
358 mud = ''
360 slur_begin =0
361 if not str:
362 return str
364 if str[0] == '(':
365 slur_begin = 1
366 str = str[1:]
368 acc = 0
369 if str[0] in '^=_':
370 c = str[0]
371 str = str[1:]
372 if c == '^':
373 acc = 1
374 if c == '=':
375 acc = 0
376 if c == '_':
377 acc = -1
379 octave = 0;
380 if str[0] in "ABCDEFG":
381 str = string.lower (str[0]) + str[1:]
382 octave = -1
385 notename = 0
386 if str[0] in "abcdefg":
387 notename = (ord(str[0]) - ord('a') + 5)%7
388 str = str[1:]
389 else:
390 return str # failed; not a note!
392 while str[0] == ',':
393 octave = octave - 1
394 str = str[1:]
395 while str[0] == '\'':
396 octave = octave + 1
397 str = str[1:]
399 num = 0
400 den = parser_state.next_den
401 parser_state.next_den = 1
403 (str, num) = parse_num (str)
404 if not num:
405 num = 1
407 if str[0] == '/':
408 while str[0] == '/':
409 str= str[1:]
410 d = 2
411 if str[0] in DIGITS:
412 (str, d) =parse_num (str)
414 den = den * d
416 current_dots = parser_state.next_dots
417 parser_state.next_dots = 0
418 while str[0] == '>':
419 str = str [1:]
420 current_dots = current_dots + 1;
421 parser_state.next_den = parser_state.next_den * 2
423 while str[0] == '<':
424 str = str [1:]
425 den = den * 2
426 parser_state.next_dots = parser_state.next_dots + 1
430 voices_append ("%s%s%s" %
431 (pitch_to_mudela_name (notename, acc + global_key[notename]),
432 octave_to_mudela_quotes (octave),
433 duration_to_mudela_duration ((num,den), default_len, current_dots)))
434 slur_end =0
435 if str[0] == ')':
436 slur_begin = 1
437 str = str[1:]
440 return str
442 def junk_space (str):
443 while str and str[0] in '\t\n ':
444 str = str[1:]
446 return str
449 def try_parse_guitar_chord (str):
450 if str and str[0] == '"':
451 str = str[1:]
452 gc = ''
453 while str and str[0] != '"':
454 gc = gc + str[0]
455 str = str[1:]
457 if str:
458 str = str[1:]
460 sys.stderr.write ("warning: ignoring guitar chord: %s\n" % gc)
462 return str
464 def try_parse_escape (str):
465 if not str or str [0] != '\\':
466 return str
468 str = str[1:]
469 if str and str[0] == 'K':
470 key_table = compute_key ()
472 return str
475 # |] thin-thick double bar line
476 # || thin-thin double bar line
477 # [| thick-thin double bar line
478 # :| left repeat
479 # |: right repeat
480 # :: left-right repeat
483 def try_parse_bar (str):
484 if str and str[0] == '|':
485 bs = ''
486 str = str[1:]
487 if str:
488 if str[0] == ']':
489 bs = '|.'
490 if str[0] == '|':
491 bs = '||'
492 if str[0] == '|:':
493 sys.stderr.write ("warning: repeat kludge\n")
494 bs = '|:'
495 if bs:
496 voices_append ('\\bar "%s";' % bs)
497 str = str[1:]
499 if str and str[:2] == '[|':
500 sys.stderr.write ("warning: thick-thin bar kludge\n")
501 voices_append ('\\bar "||";')
502 str = str[2:]
504 if str and str[:2] == ':|':
505 sys.stderr.write ("warning: repeat kludge\n")
506 voices_append ('\\bar ":|:";')
507 str = str[2:]
509 if str and str[:2] == '::':
510 sys.stderr.write ("warning: repeat kludge\n")
511 voices_append ('\\bar ":|:";')
512 str = str[2:]
514 return str
517 def try_parse_chord_delims (str):
518 if str and str[0] == '[':
519 str = str[1:]
520 voices_append ('<')
522 if str and str[0] == ']':
523 str = str[1:]
524 voices_append ('>')
526 return str
528 # urg, hairy to compute grace note hack using \times{}
529 def try_parse_grace_delims (str):
530 if str and str[0] == '{':
531 str = str[1:]
532 voices_append ('\\grace { ')
534 if str and str[0] == '}':
535 str = str[1:]
536 voices_append ('}')
538 return str
540 # Try nibbling characters off until the line doesn't change.
541 def try_parse_body_line (ln, state):
542 prev_ln = ''
543 while ln != prev_ln:
544 prev_ln = ln
545 ln = try_parse_chord_delims (ln)
546 ln = try_parse_note (ln, state)
547 ln = try_parse_bar (ln)
548 ln = try_parse_escape (ln)
549 ln = try_parse_guitar_chord (ln)
550 ln = try_parse_tuplet_begin (ln, state)
551 ln = try_parse_group_end (ln, state)
552 ln = try_parse_grace_delims (ln)
553 ln = junk_space (ln)
555 if ln:
556 sys.stderr.write ("Huh? Don't understand `%s'\n" % ln)
560 def parse_file (fn):
561 f = open (fn)
562 ls = f.readlines ()
564 head = 1
565 state = Parser_state ()
566 for l in ls:
567 if re.match ('^[\t ]*(%.*)?$', l):
568 continue
570 if head:
571 m = try_parse_header_line (l)
572 if not m:
573 head = 0
575 if not head:
576 m = try_parse_body_line (l,state)
579 def identify():
580 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
582 def help ():
583 print r"""
584 This is a disfunctional ABC to mudela convertor. It only gulps input, and
585 says huh when confused. Go ahead and fix me.
587 Usage: abc-2-ly INPUTFILE
589 -h, --help this help.
594 identify()
595 (options, files) = getopt.getopt (sys.argv[1:], 'h', ['help'])
597 for opt in options:
598 o = opt[0]
599 a = opt[1]
600 if o== '--help' or o == '-h':
601 help ()
602 else:
603 print o
604 raise getopt.error
607 for f in files:
608 if f == '-':
609 f = ''
610 parse_file (f)
612 dump_global ()
613 dump_lyrics ()
614 dump_voices ()
615 dump_score ()