3 # musedata = musedata.stanford.edu
4 # musedata = COBOL for musicians.
12 # * multiple voices (they use `Backspace' (shudder)
17 # I completely forgot how this was supposed to work --hwn 5/2002
26 program_name = 'musedata2ly'
27 version = '@TOPLEVEL_VERSION@'
28 if version == '@' + 'TOPLEVEL_VERSION' + '@':
29 version = '(unknown version)' # uGUHGUHGHGUGH
40 'END': 'encodingdate',
46 'YEN': 'encodingcountry',
50 'ENC': 'musedataencoder',
52 'AFT': 'musedatastage'
57 def __init__ (self, fn):
60 ls = open (fn).readlines ()
64 m = re.match('!!!([A-Z]+):[ \t]+(.*)$',l)
68 val = re.sub ('[ \t]+', ' ', val)
71 key =ref_header_dict [key]
73 sys.stderr.write ('\nUnknown ref key \`%s\'' % key)
84 for (k,v) in self.dict.items ():
85 str = str +' %s = "%s"\n' % (k,v)
86 str = '\\header {\n%s}' % str
92 actab = {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
94 def pitch_to_lily_string (tup):
97 nm = chr((n + 2) % 7 + ord ('a'))
110 return '\\time %s\n' % s
114 def get_divisions_per_quarter (s):
115 divisions = string.atoi (s)
118 def get_directive (s):
121 def get_transposing (s):
124 def get_num_instruments (s):
127 def get_lilypond_notename (p, ac):
130 s = chr (p + ord ('c'))
144 DIGITS = "0123456789"
156 'Q' : get_divisions_per_quarter,
158 'X' : get_transposing,
159 'I': get_num_instruments,
163 def __init__ (self, dict):
167 if self. dict.has_key ('T'):
168 s = s+ get_timesig (self.dict['T'])
185 's': '"\\\\textsharp"',
186 'n': '"\\\\textnatural"',
187 'b': '"\\\\textflat"',
203 self.basic_duration = 4
206 self.note_suffix = self.note_prefix = ''
207 self.chord_suffix = self.chord_prefix = ''
209 def add_script (self,s):
210 self.scripts.append (s)
211 def set_duration (self, d):
212 self.basic_duration = d
213 def add_syllable (self, s):
214 self.syllables.append (s)
215 def add_pitch (self,t):
216 self.pitches.append (t)
222 if self.basic_duration == 0.5:
225 sd = '%d' % self.basic_duration
227 sd = sd + '.' * self.dots
229 for p in self.pitches:
232 str = str + pitch_to_lily_string (p)
234 if len (self.pitches) > 1:
236 elif len (self.pitches) == 0:
239 str = str + sd + '(' * len (self.slurstart) + ')' * len (self.slurstart)
240 for s in self.scripts:
243 str = self.note_prefix +str + self.note_suffix
244 str = self.chord_prefix + str + self.chord_suffix
252 def append_entry (self, e):
253 self.entries.append (e)
254 def append_chord (self,c ):
255 self.chords.append (c)
256 self.entries.append (c)
257 def last_chord (self):
258 return self.chords[-1]
259 def __init__ (self, fn):
262 'tagline' :'automatically converted from Musedata',
263 'copyright' : 'all rights reserved -- free for noncommercial use'
264 # musedata license (argh)
270 lines = open (fn).readlines ()
271 lines = map (lambda x: re.sub ("\r$", '', x), lines)
272 lines = self.parse_header (lines)
273 lines = self.append_lines (lines)
274 str = string.join (lines, '\n')
275 lines = re.split ('[\n\r]+', str)
276 self.parse_body (lines)
278 def parse_header (self, lines):
279 enter = string.split (lines[3], ' ')
280 self.header_dict['enteredby'] = string.join (enter[1:])
281 self.header_dict['enteredon'] = enter[0]
282 self.header_dict['opus'] = lines[4]
283 self.header_dict['source'] = lines[5]
284 self.header_dict['title'] = lines[6]
285 self.header_dict['subtitle'] = lines[7]
286 self.header_dict['instrument']= lines[8]
287 self.header_dict['musedatamisc'] =lines[9]
288 self.header_dict['musedatagroups'] =lines[10]
289 self.header_dict['musedatagroupnumber']=lines[11]
293 if lines[0][0] == '$':
298 def parse_musical_attributes (self,l):
299 atts = re.split('([A-Z][0-9]?):', l)
309 self.divs_per_q = string.atoi (found['Q'])
313 self.append_entry (Attribute_set (found))
314 def append_entry (self, e):
315 self.entries.append (e)
317 def parse_line_comment (self,l):
320 def parse_note_line (self,l):
323 print DIGITS+DIGITS+DIGITS
338 ch = self.last_chord ()
342 self.append_chord (ch)
345 ch.cue = ch.cue or cue
346 ch.grace = ch.grace or grace
348 while pi[0] in SPACES:
352 name = ((ord (pi[0]) -ord('A')) + 5) % 7
355 while pi and pi[0] in '#f':
362 oct = string.atoi (pi) - 3
364 pittup = (oct, name ,alter)
365 ch.add_pitch (pittup)
376 if ch.cue or ch.grace:
383 base_dur = 1 << (9 - (ord (c) - ord ('0')))
391 base_dur = (4 * self.divs_per_q* fact[1]) / (string.atoi (di)* fact[0])
392 ch.set_duration (base_dur)
394 ch.tied = ch.tied or tied
398 elif l[26:27] == ']':
402 additional = l[32:44]
405 ch.slurstart.append( 0)
408 ch.slurstop.append( 0)
426 scr = script_table[c]
430 sys.stderr.write ("\nFixme: script `%s' not done\n" % c)
433 sylls = string.split (text,'|')
436 ch.add_syllable (syl)
439 def parse_measure_line (self,l):
440 self.append_entry (Measure_start())
443 def parse_duration (l):
445 while l[0] in '0123456789':
449 num = string.atoi (s)
457 if num % multiplier == 0 and den % f == 0:
458 num = num / multiplier
460 current_dots = current_dots + d
463 sys.stderr.write ('huh. Durations left')
464 return '%s%s' % (den, '.' * current_dots)
466 def append_lines (self,ls):
470 nls[-1] = nls[-1]+l[1:]
477 for e in self.entries:
479 next = ' ' + e.dump()
480 if len (ln) + len (next) > 72:
487 s = '\\notes {\n %s \n}' % s
490 def parse_body (self,lines):
498 comment_switch = not comment_switch
507 self.parse_musical_attributes (l)
509 self.parse_line_comment (l)
511 self.parse_musical_directions (l)
512 elif c in 'ABCDEFGr ':
513 self.parse_note_line (l)
515 self.parse_measure_line (l)
519 pass # ignore sound & print
521 sys.stderr.write ("\nUnrecognized record `%s'\n"% l)
529 """Usage: musedata2ly [OPTIONS]... FILE1 [FILE2 ...]
531 Convert musedata to LilyPond.
534 -h,--help print this help
535 -o,--output=FILE set output filename to FILE
536 -v,--version show version information
537 -r,--ref=REF read background information from ref-file REF
539 Musedata (http://www.ccarh.org/musedata/) is an electronic library of
540 classical music scores, currently comprising XXX scores. The music is
541 encoded in so-called Musedata format
542 (http://www.ccarh.org/publications/books/beyondmidi/online/musedata).
543 musedata2ly converts a set of musedata files to one .ly file, and will
544 include a \header field if a .ref file is supplied.
546 This converter is not complete -- this is left to the user as an excercise.
548 Report bugs to bug-lilypond@gnu.org.
550 Written by Han-Wen Nienhuys <hanwen@cs.uu.nl>.
555 def print_version ():
556 sys.stdout.write ("""musedata2ly (GNU LilyPond) %s
558 This is free software. It is covered by the GNU General Public License,
559 and you are welcome to change it and/or distribute copies of it under
560 certain conditions. Invoke as `midi2ly --warranty' for more information.
562 Copyright (c) 2000--2004 by Han-Wen Nienhuys <hanwen@cs.uu.nl>.
565 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
569 (options, files) = getopt.getopt (sys.argv[1:], 'r:vo:h', ['verbose', 'ref=', 'help','version', 'output='])
575 if o== '--help' or o == '-h':
578 elif o == '--version' or o == '-v':
581 elif o == '--ref' or o == '-r':
583 elif o == '--output' or o == '-o':
585 elif o == '--verbose' :
604 sys.stderr.write ('Processing `%s\'\n' % f)
608 id = os.path.basename (f)
609 id = re.sub ('[^a-zA-Z0-9]', 'x', id)
612 return chr (ord (match.group ()) - ord('0') + ord('A'))
614 id = re.sub ('[0-9]', num2let, id)
617 ly =ly + '\n\n%s = \\context Staff = "%s" %s\n\n' % (id, id, e.dump ())
619 found_ids = found_ids + '\\%s\n' % id
621 found_ids = '\n\n\n\\score { << %s >> } ' % found_ids
625 head = Ref_parser (ref_file)
630 t = head.dict['title']
631 st= head.dict['subtitle']
637 t = re.sub ("^ +(.*) +$", r"\1", t)
638 t = re.sub ("\\.", '', t)
639 out_filename = re.sub ('[^a-zA-Z0-9-]', '-', t)
640 out_filename = out_filename+ '.ly'
641 ly_head = head.dump ()
644 out_filename = 'musedata.ly'
646 sys.stderr.write ('Writing `%s\'\n' % out_filename)
648 fo = open (out_filename, 'w')
649 fo.write ('%% lily was here -- automatically converted by musedata.ly\n')
650 fo.write(ly_head + ly + found_ids)