3 # musedata = musedata.stanford.edu
4 # musedata = COBOL for musicians.
12 # * multiple voices (they use `Backspace' (shudder)
21 program_name = 'musedata2ly'
22 version = '@TOPLEVEL_VERSION@'
23 if version == '@' + 'TOPLEVEL_VERSION' + '@':
24 version = '(unknown version)' # uGUHGUHGHGUGH
35 'END': 'encodingdate',
41 'YEN': 'encodingcountry',
45 'ENC': 'musedataencoder',
47 'AFT': 'musedatastage'
52 def __init__ (self, fn):
55 ls = open (fn).readlines ()
59 m = re.match('!!!([A-Z]+):[ \t]+(.*)$',l)
63 val = re.sub ('[ \t]+', ' ', val)
66 key =ref_header_dict [key]
68 sys.stderr.write ('\nUnknown ref key \`%s\'' % key)
79 for (k,v) in self.dict.items ():
80 str = str +' %s = "%s"\n' % (k,v)
81 str = '\\header {\n%s}' % str
87 actab = {-2: 'eses', -1: 'es', 0 : '', 1: 'is', 2:'isis'}
89 def pitch_to_lily_string (tup):
92 nm = chr((n + 2) % 7 + ord ('a'))
105 return '\\time %s\n' % s
109 def get_divisions_per_quarter (s):
110 divisions = string.atoi (s)
113 def get_directive (s):
116 def get_transposing (s):
119 def get_num_instruments (s):
122 def get_lilypond_notename (p, ac):
125 s = chr (p + ord ('c'))
139 DIGITS = "0123456789"
151 'Q' : get_divisions_per_quarter,
153 'X' : get_transposing,
154 'I': get_num_instruments,
170 's': '"\\\\textsharp"',
171 'n': '"\\\\textnatural"',
172 'b': '"\\\\textflat"',
178 def __init__ (self, dict):
193 self.basic_duration = 4
196 self.note_suffix = self.note_prefix = ''
197 self.chord_suffix = self.chord_prefix = ''
199 def add_script (self,s):
200 self.scripts.append (s)
201 def set_duration (self, d):
202 self.basic_duration = d
203 def add_syllable (self, s):
204 self.syllables.append (s)
205 def add_pitch (self,t):
206 self.pitches.append (t)
212 if self.basic_duration == 0.5:
215 sd = '%d' % self.basic_duration
217 sd = sd + '.' * self.dots
218 for p in self.pitches:
221 str = str + pitch_to_lily_string (p) + sd
223 for s in self.scripts:
226 str = self.note_prefix +str + self.note_suffix
228 if len (self.pitches) > 1:
230 elif len (self.pitches) == 0:
233 str = self.chord_prefix + str + self.chord_suffix
238 def append_entry (self, e):
239 self.entries.append (e)
240 def append_chord (self,c ):
241 self.chords.append (c)
242 self.entries.append (c)
243 def last_chord (self):
244 return self.chords[-1]
245 def __init__ (self, fn):
248 'tagline' :'automatically converted from Musedata',
249 'copyright' : 'all rights reserved -- free for noncommercial use'
250 # musedata license (argh)
255 lines = open (fn).readlines ()
256 lines = self.parse_header (lines)
257 lines = self.append_lines (lines)
258 str = string.join (lines, '\n')
259 lines = re.split ('[\n\r]+', str)
260 self.parse_body (lines)
262 def parse_header (self, lines):
263 enter = string.split (lines[3], ' ')
264 self.header_dict['enteredby'] = string.join (enter[1:])
265 self.header_dict['enteredon'] = enter[0]
266 self.header_dict['opus'] = lines[4]
267 self.header_dict['source'] = lines[5]
268 self.header_dict['title'] = lines[6]
269 self.header_dict['subtitle'] = lines[7]
270 self.header_dict['instrument']= lines[8]
271 self.header_dict['musedatamisc'] =lines[9]
272 self.header_dict['musedatagroups'] =lines[10]
273 self.header_dict['musedatagroupnumber']=lines[11]
277 if lines[0][0] == '$':
282 def parse_musical_attributes (self,l):
283 atts = re.split('([A-Z][0-9]?):', l)
293 self.divs_per_q = string.atoi (found['Q'])
297 self.append_entry (Attribute_set (found))
298 def append_entry (self, e):
299 self.entries.append (e)
301 def parse_line_comment (self,l):
304 def parse_note_line (self,l):
307 print DIGITS+DIGITS+DIGITS
322 ch = self.last_chord ()
326 self.append_chord (ch)
329 ch.cue = ch.cue or cue
330 ch.grace = ch.grace or grace
332 while pi[0] in SPACES:
336 name = ((ord (pi[0]) -ord('A')) + 5) % 7
339 while pi and pi[0] in '#f':
346 oct = string.atoi (pi) - 3
348 pittup = (oct, name ,alter)
349 ch.add_pitch (pittup)
352 if ch.cue or ch.grace:
359 base_dur = 1 << (9 - (ord (c) - ord ('0')))
361 base_dur = (4 * self.divs_per_q) / string.atoi (di)
362 ch.set_duration (base_dur)
364 ch.tied = ch.tied or tied
370 elif l[18:19] == ':':
375 elif l[26:27] == ']':
379 additional = l[32:44]
382 s = list('([{z').index (c)
383 ch.slurstart.append( s)
384 sluropen = list(')]}x').index (c)
385 ch.slurstop.append( s)
403 scr = script_table[c]
407 sys.stderr.write ("\nFixme: script `%s' not done\n" % c)
410 sylls = string.split (text,'|')
413 ch.add_syllable (syl)
416 def parse_measure_line (self,l):
420 def parse_duration (l):
422 while l[0] in '0123456789':
426 num = string.atoi (s)
434 if num % multiplier == 0 and den % f == 0:
435 num = num / multiplier
437 current_dots = current_dots + d
440 sys.stderr.write ('huh. Durations left')
441 return '%s%s' % (den, '.' * current_dots)
443 def append_lines (self,ls):
447 nls[-1] = nls[-1]+l[1:]
454 for e in self.entries:
456 next = ' ' + e.dump()
457 if len (ln) + len (next) > 72:
464 s = '\\notes {\n %s \n}' % s
467 def parse_body (self,lines):
474 comment_switch = not comment_switch
483 self.parse_musical_attributes (l)
485 self.parse_line_comment (l)
487 self.parse_musical_directions (l)
488 elif c in 'ABCDEFGr ':
489 self.parse_note_line (l)
491 self.parse_measure_line (l)
495 pass # ignore sound & print
497 sys.stderr.write ("\nUnrecognized record `%s'\n"% l)
505 """Usage: musedata2ly [OPTION]... FILE1 [FILE2 ...]
507 Convert musedata to LilyPond.
511 -o,--output=FILE set output filename to FILE
512 -v,--version version information
513 -r,--ref=REF read background information from ref-file REF
515 Musedata (http://www.ccarh.org/musedata/) is an electronic library of
516 classical music scores, currently comprising XXX scores. The music is
517 encoded in so-called Musedata format
518 (http://www.ccarh.org/publications/books/beyondmidi/online/musedata).
519 musedata2ly converts a set of musedata files to one .ly file, and will
520 include a \header field if a .ref file is supplied
522 Report bugs to bug-gnu-music@gnu.org.
524 Written by Han-Wen Nienhuys <hanwen@cs.uu.nl>
528 def print_version ():
529 sys.stdout.write ("""musedata2ly (GNU LilyPond) %s
531 This is free software. It is covered by the GNU General Public License,
532 and you are welcome to change it and/or distribute copies of it under
533 certain conditions. Invoke as `midi2ly --warranty' for more information.
535 Copyright (c) 2000 by Han-Wen Nienhuys <hanwen@cs.uu.nl>
538 sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version))
542 (options, files) = getopt.getopt (sys.argv[1:], 'r:vo:h', ['ref=', 'help','version', 'output='])
548 if o== '--help' or o == '-h':
551 elif o == '--version' or o == '-v':
554 elif o == '--ref' or o == '-r':
556 elif o == '--output' or o == '-o':
574 sys.stderr.write ('Processing `%s\'\n' % f)
578 id = os.path.basename (f)
579 id = re.sub ('[^a-zA-Z0-9]', 'x', id)
581 ly =ly + '\n\n%s = \\context Staff = "%s" %s\n\n' % (id, id, e.dump ())
583 found_ids = found_ids + '\\%s\n' % id
585 found_ids = '\n\n\n\\score { < %s > } ' % found_ids
589 head = Ref_parser (ref_file)
594 t = head.dict['title']
595 st= head.dict['subtitle']
601 t = re.sub ("^ +(.*) +$", r"\1", t)
602 t = re.sub ("\\.", '', t)
603 out_filename = re.sub ('[^a-zA-Z0-9-]', '-', t)
604 out_filename = out_filename+ '.ly'
605 ly_head = head.dump ()
608 out_filename = 'musedata.ly'
610 sys.stderr.write ('Writing `%s\'\n' % out_filename)
612 fo = open (out_filename, 'w')
613 fo.write ('%% lily was here -- automatically converted by musedata.ly\n')
614 fo.write(ly_head + ly + found_ids)