X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fetf2ly.py;h=34a1c9e01860e2aba8d835a20512973af5d97052;hb=7e72a1e50e94a7f9738d62599de79fe7745f600c;hp=292e983fb823bde772234d2070bffe81848b3439;hpb=70901d9472b25cdfd0f889b9f8624188c42dcb95;p=lilypond.git diff --git a/scripts/etf2ly.py b/scripts/etf2ly.py index 292e983fb8..34a1c9e018 100644 --- a/scripts/etf2ly.py +++ b/scripts/etf2ly.py @@ -1,7 +1,7 @@ #!@PYTHON@ # info mostly taken from looking at files. See also -# http://www.cs.uu.nl/~hanwen/lily-devel/etf.html +# http://lilypond.org/wiki/?EnigmaTransportFormat # This supports # @@ -11,13 +11,20 @@ # * slurs # * lyrics # * articulation -# +# * grace notes +# * tuplets +# # todo: # * slur/stem directions # * voices (2nd half of frame?) # * more intelligent lyrics # * beams (better use autobeam?) +# * more robust: try entertainer.etf (freenote) +# * dynamics +# * empty measures (eg. twopt03.etf from freenote) +# + program_name = 'etf2ly' version = '@TOPLEVEL_VERSION@' @@ -31,10 +38,17 @@ import re import string import os -finale_clefs= ['treble', 'alto', 'tenor', 'bass', 'percussion', 'treble8vb', 'bass8vb', 'baritone'] +finale_clefs= ['treble', 'alto', 'tenor', 'bass', 'percussion', 'treble_8', 'bass_8', 'baritone'] def lily_clef (fin): - return finale_clefs[fin] + try: + return finale_clefs[fin] + except IndexError: + sys.stderr.write ( '\nHuh? Found clef number %d\n' % fin) + + return 'treble' + + def gulp_file(f): return open (f).read () @@ -61,28 +75,83 @@ def transpose(orig, delta): -# find transposition of C-major scale that belongs here. def interpret_finale_key_sig (finale_id): + """ +find the transposition of C-major scale that belongs here. + +we are not going to insert the correct major/minor, we only want to +have the correct number of accidentals +""" + p = (0,0) - if 0 <= finale_id < 7: - while finale_id > 0: + + + bank_number = finale_id >> 8 + accidental_bits = finale_id & 0xff + + if 0 <= accidental_bits < 7: + while accidental_bits > 0: p = transpose (p, (4,0)) # a fifth up - finale_id = finale_id - 1 - elif 248 < finale_id <= 255: - while finale_id < 256: + accidental_bits = accidental_bits - 1 + elif 248 < accidental_bits <= 255: + while accidental_bits < 256: p = transpose (p, (3,0)) - finale_id = finale_id + 1 + accidental_bits = accidental_bits + 1 + if bank_number == 1: + # minor scale + p = transpose (p, (5, 0)) p = (p[0] % 7, p[1]) - return p + + return KeySignature (p, bank_number) # should cache this. -def find_scale (transposition): +def find_scale (keysig): cscale = map (lambda x: (x,0), range (0,7)) - trscale = map(lambda x, k=transposition: transpose(x, k), cscale) - + print "cscale: ", cscale + ascale = map (lambda x: (x,0), range (-2,5)) + print "ascale: ", ascale + transposition = keysig.pitch + if keysig.sig_type == 1: + transposition = transpose(transposition, (2, -1)) + transposition = (transposition[0] % 7, transposition[1]) + trscale = map(lambda x, k=transposition: transpose(x, k), ascale) + else: + trscale = map(lambda x, k=transposition: transpose(x, k), cscale) + print "trscale: ", trscale return trscale +def EDU_to_duration (edu): + log = 1 + d = 4096 + while d > edu: + d = d >> 1 + log = log << 1 + + edu = edu - d + dots = 0 + if edu == d /2: + dots = 1 + elif edu == d*3/4: + dots = 2 + return (log, dots) + +def rat_to_lily_duration (rat): + (n,d) = rat + + basedur = 1 + while d and d % 2 == 0: + basedur = basedur << 1 + d = d >> 1 + + str = 's%d' % basedur + if n <> 1: + str = str + '*%d' % n + if d <> 1: + str = str + '/%d' % d + + return str + def gcd (a,b): if b == 0: return a @@ -121,6 +190,8 @@ def rat_neg (a): (p,q) = a return (-p,q) + + def rat_subtract (a,b ): return rat_add (a, rat_neg (b)) @@ -128,79 +199,161 @@ def lily_notename (tuple2): (n, a) = tuple2 nn = chr ((n+ 2)%7 + ord ('a')) - if a == -1: - nn = nn + 'es' - elif a == -2: - nn = nn + 'eses' - elif a == 1: - nn = nn + 'is' - elif a == 2: - nn = nn + 'isis' + return nn + {-2:'eses', -1:'es', 0:'', 1:'is', 2:'isis'}[a] + - return nn +class Tuplet: + def __init__ (self, number): + self.start_note = number + self.finale = [] + + def append_finale (self, fin): + self.finale.append (fin) + + def factor (self): + n = self.finale[0][2]*self.finale[0][3] + d = self.finale[0][0]*self.finale[0][1] + return rat_simplify( (n, d)) + + def dump_start (self): + return '\\times %d/%d { ' % self.factor () + + def dump_end (self): + return ' }' + def calculate (self, chords): + edu_left = self.finale[0][0] * self.finale[0][1] + + startch = chords[self.start_note] + c = startch + while c and edu_left: + c.tuplet = self + if c == startch: + c.chord_prefix = self.dump_start () + c.chord_prefix + + if not c.grace: + edu_left = edu_left - c.EDU_duration () + if edu_left == 0: + c.chord_suffix = c.chord_suffix+ self.dump_end () + c = c.next + if edu_left: + sys.stderr.write ("\nHuh? Tuplet starting at entry %d was too short." % self.start_note) + class Slur: - def __init__ (self, number): + def __init__ (self, number, params): self.number = number - self.finale = [] + self.finale = params def append_entry (self, finale_e): self.finale.append (finale_e) def calculate (self, chords): - startnote = self.finale[0][5] - endnote = self.finale[3][2] + startnote = self.finale[5] + endnote = self.finale[3*6 + 2] + try: + cs = chords[startnote] + ce = chords[endnote] - cs = chords[startnote] - cs.note_suffix = '(' + cs.note_suffix - ce = chords[endnote] - ce.prefix = ce.prefix + ')' + if not cs or not ce: + raise IndexError + + cs.note_suffix = '-(' + cs.note_suffix + ce.note_suffix = ce.note_suffix + '-)' + + except IndexError: + sys.stderr.write ("""\nHuh? Slur no %d between (%d,%d), with %d notes""" % (self.number, startnote, endnote, len (chords))) + class Global_measure: def __init__ (self, number): self.timesig = '' self.number = number - self.keysignature = None + self.key_signature = None self.scale = None - + self.force_break = 0 + + self.repeats = [] self.finale = [] + def __str__ (self): + return `self.finale ` + def set_timesig (self, finale): (beats, fdur) = finale (log, dots) = EDU_to_duration (fdur) - assert dots == 0 + + if dots == 1: + beats = beats * 3 + log = log * 2 + dots = 0 + + if dots <> 0: + sys.stderr.write ("\nHuh? Beat duration has dots? (EDU Duration = %d)" % fdur) self.timesig = (beats, log) def length (self): return self.timesig - def set_keysig (self, finale): + def set_key_sig (self, finale): k = interpret_finale_key_sig (finale) - self.keysignature = k + self.key_signature = k self.scale = find_scale (k) + def set_flags (self,flag1, flag2): + + # flag1 isn't all that interesting. + if flag2 & 0x8000: + self.force_break = 1 + + if flag2 & 0x0008: + self.repeats.append ('start') + if flag2 & 0x0004: + self.repeats.append ('stop') + + if flag2 & 0x0002: + if flag2 & 0x0004: + self.repeats.append ('bracket') articulation_dict ={ - 11: '\\prall', - 12: '\\mordent', - 8: '\\fermata', - 18: '"arp"' , # arpeggio -}; + 94: '^', + 109: '\\prall', + 84: '\\turn', + 62: '\\mordent', + 85: '\\fermata', + 46: '.', +# 3: '>', +# 18: '\arpeggio' , +} + +class Articulation_def: + def __init__ (self, n, a, b): + self.finale_glyph = a & 0xff + self.number = n + def dump (self): + try: + return articulation_dict[self.finale_glyph] + except KeyError: + sys.stderr.write ("\nUnknown articulation no. %d" % self.finale_glyph) + sys.stderr.write ("\nPlease add an entry to articulation_dict in the Python source") + return None + class Articulation: def __init__ (self, a,b, finale): - self.type = finale[0] + self.definition = finale[0] self.notenumber = b - def calculate (self, chords): + + def calculate (self, chords, defs): c = chords[self.notenumber] - try: - a = articulation_dict[self.type] - except KeyError: - a = '"art"' - - c.note_suffix = '-' + a + c.note_suffix + adef = defs[self.definition] + lystr =adef.dump() + if lystr == None: + lystr = '"art"' + sys.stderr.write ("\nThis happened on note %d" % self.notenumber) + + c.note_suffix = '-' + lystr class Syllable: def __init__ (self, a,b , finale): @@ -244,28 +397,53 @@ class Verse: str = """\nverse%s = \\lyrics {\n %s}\n""" % (encodeint (self.number - 1) ,str) return str +class KeySignature: + def __init__(self, pitch, sig_type = 0): + self.pitch = pitch + self.sig_type = sig_type + def signature_type (self): + if self.sig_type == 1: + return "\\minor" + else: + # really only for 0, but we only know about 0 and 1 + return "\\major" + + def equal (self, other): + if other and other.pitch == self.pitch and other.sig_type == self.sig_type: + return 1 + else: + return 0 + + class Measure: def __init__(self, no): self.number = no - self.frames = [] + self.frames = [0] * 4 self.flags = 0 self.clef = 0 self.finale = [] self.global_measure = None + self.staff = None + self.valid = 1 - def add_finale_entry (self, entry): - self.finale.append (entry) + def valid (self): + return self.valid def calculate (self): - f0 = self.finale[0] - f1 = self.finale[1] - - self.clef = string.atoi (f0[0]) - self.flags = string.atoi (f0[1]) - fs = map (string.atoi, list (f0[2:]) + [f1[0]]) + fs = [] + + if len (self.finale) < 2: + fs = self.finale[0] + + self.clef = fs[1] + self.frames = [fs[0]] + else: + fs = self.finale + self.clef = fs[0] + self.flags = fs[1] + self.frames = fs[2:] - self.frames = fs class Frame: def __init__ (self, finale): @@ -280,21 +458,43 @@ class Frame: def set_measure (self, m): self.measure = m + def calculate (self): + + # do grace notes. + lastch = None + for c in self.chords: + if c.grace and (lastch == None or (not lastch.grace)): + c.chord_prefix = r'\grace {' + c.chord_prefix + elif not c.grace and lastch and lastch.grace: + lastch.chord_suffix = lastch.chord_suffix + ' } ' + + lastch = c + + + def dump (self): - str = '' + str = '%% FR(%d)\n' % self.number left = self.measure.global_measure.length () + + + ln = '' for c in self.chords: - str = str + c.ly_string () + ' ' + add = c.ly_string () + ' ' + if len (ln) + len(add) > 72: + str = str + ln + '\n' + ln = '' + ln = ln + add left = rat_subtract (left, c.length ()) + + str = str + ln + if left[0] < 0: - print self.number - print self.start, self.end - print left - raise 'bla' + sys.stderr.write ("""\nHuh? Going backwards in frame no %d, start/end (%d,%d)""" % (self.number, self.start, self.end)) + left = (0,1) if left[0]: - str = str + 's*%d/%d' % left - - str = str + '\n' + str = str + rat_to_lily_duration (left) + + str = str + ' | \n' return str def encodeint (i): @@ -306,11 +506,12 @@ class Staff: self.measures = [] def get_measure (self, no): - if len (self.measures) <= no: - self.measures = self.measures + [None]* (1 + no - len (self.measures)) + fill_list_to (self.measures, no) if self.measures[no] == None: - self.measures [no] = Measure (no) + m = Measure (no) + self.measures [no] =m + m.staff = self return self.measures[no] def staffid (self): @@ -325,25 +526,57 @@ class Staff: last_clef = None gap = (0,1) for m in self.measures[1:]: + if not m or not m.valid: + continue # ugh. + g = m.global_measure e = '' - if last_key <> g.keysignature: - e = e + "\\key %s \\major; " % lily_notename (g.keysignature) - last_key = g.keysignature - if last_time <> g.timesig : - e = e + "\\time %d/%d; " % g.timesig - last_time = g.timesig + + if g: + if g.key_signature and not g.key_signature.equal(last_key): + pitch= g.key_signature.pitch + e = e + "\\key %s %s " % (lily_notename (pitch), + g.key_signature.signature_type()) + + last_key = g.key_signature + if last_time <> g.timesig : + e = e + "\\time %d/%d " % g.timesig + last_time = g.timesig + + if 'start' in g.repeats: + e = e + ' \\bar "|:" ' + + + # we don't attempt voltas since they fail easily. + if 0 : # and g.repeat_bar == '|:' or g.repeat_bar == ':|:' or g.bracket: + strs = [] + if g.repeat_bar == '|:' or g.repeat_bar == ':|:' or g.bracket == 'end': + strs.append ('#f') + + + if g.bracket == 'start': + strs.append ('"0."') + + str = string.join (map (lambda x: '(volta %s)' % x, strs)) + + e = e + ' \\property Score.repeatCommands = #\'(%s) ' % str + + if g.force_break: + e = e + ' \\break ' + if last_clef <> m.clef : - e = e + '\\clef %s;' % lily_clef (m.clef) + e = e + '\\clef "%s"' % lily_clef (m.clef) last_clef = m.clef if e: if gap <> (0,1): - k = k +' s1*%d/%d \n ' % gap + k = k +' ' + rat_to_lily_duration (gap) + '\n' gap = (0,1) k = k + e - - gap = rat_add (gap, g.length ()) - + + if g: + gap = rat_add (gap, g.length ()) + if 'stop' in g.repeats: + k = k + ' \\bar ":|" ' k = '%sglobal = \\notes { %s }\n\n ' % (self.staffid (), k) return k @@ -359,16 +592,29 @@ class Staff: first_frame = None gap = (0,1) for m in self.measures[1:]: - fr = m.frames[x] + if not m or not m.valid: + sys.stderr.write ("Skipping non-existant or invalid measure\n") + continue + + fr = None + try: + fr = m.frames[x] + except IndexError: + sys.stderr.write ("Skipping nonexistent frame %d\n" % x) + laystr = laystr + "%% non existent frame %d (skipped) \n" % x if fr: first_frame = fr if gap <> (0,1): - laystr = laystr +'} s1*%d/%d {\n ' % gap + laystr = laystr +'} %s {\n ' % rat_to_lily_duration (gap) gap = (0,1) laystr = laystr + fr.dump () else: - gap = rat_add (gap, m.global_measure.length ()) - + if m.global_measure : + gap = rat_add (gap, m.global_measure.length ()) + else: + sys.stderr.write ( \ + "No global measure for staff %d measure %d\n" + % (self.number, m.number)) if first_frame: l = self.layerid (x) laystr = '%s = \\notes { { %s } }\n\n' % (l, laystr) @@ -381,38 +627,36 @@ class Staff: stafdef = stafdef + ' \\' + i - str = str + '%s = \\context Staff = %s <\n %s\n >\n' % \ + str = str + '%s = \\context Staff = %s <<\n %s\n >>\n' % \ (self.staffid (), self.staffid (), stafdef) return str -def EDU_to_duration (edu): - log = 1 - d = 4096 - while d > edu: - d = d >> 1 - log = log << 1 - edu = edu - d - dots = 0 - if edu == d /2: - dots = 1 - elif edu == d*3/4: - dots = 2 - return (log, dots) +def ziplist (l): + if len (l) < 2: + return [] + else: + return [(l[0], l[1])] + ziplist (l[2:]) + class Chord: - def __init__ (self, finale_entry): + def __init__ (self, number, contents): self.pitches = [] self.frame = None - self.finale = finale_entry + self.finale = contents[:7] + + self.notelist = ziplist (contents[7:]) self.duration = None self.next = None self.prev = None + self.number = number self.note_prefix= '' self.note_suffix = '' self.chord_suffix = '' self.chord_prefix = '' + self.tuplet = None + self.grace = 0 def measure (self): if not self.frame: @@ -420,34 +664,51 @@ class Chord: return self.frame.measure def length (self): + if self.grace: + return (0,1) + l = (1, self.duration[0]) d = 1 << self.duration[1] dotfact = rat_subtract ((2,1), (1,d)) - return rat_multiply (dotfact, l) + mylen = rat_multiply (dotfact, l) + + if self.tuplet: + mylen = rat_multiply (mylen, self.tuplet.factor()) + return mylen - def number (self): - return self.finale[0][0] + + def EDU_duration (self): + return self.finale[2] def set_duration (self): - ((no, prev, next, dur, pos, entryflag, extended, follow), - notelist) = self.finale - self.duration = EDU_to_duration(dur) - def find_realpitch (self): + self.duration = EDU_to_duration(self.EDU_duration ()) - ((no, prev, next, dur, pos, entryflag, extended, follow), notelist) = self.finale + def calculate (self): + self.find_realpitch () + self.set_duration () + + flag = self.finale[4] + if Chord.GRACE_MASK & flag: + self.grace = 1 + + + def find_realpitch (self): meas = self.measure () tiestart = 0 if not meas or not meas.global_measure : - print 'note %d not in measure' % self.number () + sys.stderr.write ('note %d not in measure\n' % self.number) elif not meas.global_measure.scale: - print 'note %d: no scale in this measure.' % self.number () + sys.stderr.write ('note %d: no scale in this measure.' % self.number) else: - for p in notelist: + + for p in self.notelist: (pitch, flag) = p - + + nib1 = pitch & 0x0f + if nib1 > 8: nib1 = -(nib1 - 8) rest = pitch / 16 @@ -463,12 +724,15 @@ class Chord: REST_MASK = 0x40000000L TIE_START_MASK = 0x40000000L + GRACE_MASK = 0x00800000L + def ly_string (self): s = '' rest = '' - if not (self.finale[0][5] & Chord.REST_MASK): + + if not (self.finale[4] & Chord.REST_MASK): rest = 'r' for p in self.pitches: @@ -489,47 +753,155 @@ class Chord: if rest: nn = rest - s = s + '%s%d%s' % (nn, self.duration[0], '.'* self.duration[1]) + s = s + nn if not self.pitches: - s = 'r%d%s' % (self.duration[0] , '.'* self.duration[1]) - s = self.note_prefix + s + self.note_suffix + s = 'r' if len (self.pitches) > 1: s = '<%s>' % s + + s = s + '%d%s' % (self.duration[0], '.'* self.duration[1]) + s = self.note_prefix + s + self.note_suffix s = self.chord_prefix + s + self.chord_suffix + return s -GFre = re.compile(r"""^\^GF\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -BCre = re.compile (r"""^\^BC\(([0-9-]+)\) ([0-9-]+) .*$""") -eEre = re.compile(r"""^\^eE\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) \$([0-9A-Fa-f]+) ([0-9-]+) ([0-9-]+)""") -FRre = re.compile (r"""^\^FR\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -MSre = re.compile (r"""^\^MS\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -note_re = re.compile (r"""^ +([0-9-]+) \$([A-Fa-f0-9]+)""") -Sxre = re.compile (r"""^\^Sx\(([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -IMre = re.compile (r"""^\^IM\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -vere = re.compile(r"""^\^(ve|ch|se)\(([0-9-]+),([0-9-]+)\) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+) ([0-9-]+)""") -versere = re.compile(r"""^\^verse\(([0-9]+)\)(.*)\^end""") + +def fill_list_to (list, no): + """ +Add None to LIST until it contains entry number NO. + """ + while len (list) <= no: + list.extend ([None] * (no - len(list) + 1)) + return list + +def read_finale_value (str): + """ +Pry off one value from STR. The value may be $hex, decimal, or "string". +Return: (value, rest-of-STR) + """ + while str and str[0] in ' \t\n': + str = str[1:] + + if not str: + return (None,str) + + if str[0] == '$': + str = str [1:] + + hex = '' + while str and str[0] in '0123456789ABCDEF': + hex = hex + str[0] + str = str[1:] + + + return (string.atol (hex, 16), str) + elif str[0] == '"': + str = str[1:] + s = '' + while str and str[0] <> '"': + s = s + str[0] + str = str[1:] + + return (s,str) + elif str[0] in '-0123456789': + dec = '' + while str and str[0] in '-0123456789': + dec = dec + str[0] + str = str[1:] + + return (string.atoi (dec), str) + else: + sys.stderr.write ("Can't convert `%s'\n" % str) + return (None, str) + + + + +def parse_etf_file (fn, tag_dict): + + """ Read FN, putting ETF info into + a giant dictionary. The keys of TAG_DICT indicate which tags + to put into the dict. + """ + + sys.stderr.write ('parsing ... ' ) + f = open (fn) + + gulp = re.sub ('[\n\r]+', '\n', f.read ()) + ls = string.split (gulp, '\n^') + + etf_file_dict = {} + for k in tag_dict.keys (): + etf_file_dict[k] = {} + + last_tag = None + last_numbers = None + + + for l in ls: + m = re.match ('^([a-zA-Z0-9&]+)\(([^)]+)\)', l) + if m and tag_dict.has_key (m.group (1)): + tag = m.group (1) + + indices = tuple (map (string.atoi, string.split (m.group (2), ','))) + content = l[m.end (2)+1:] + + + tdict = etf_file_dict[tag] + if not tdict.has_key (indices): + tdict[indices] = [] + + + parsed = [] + + if tag == 'verse' or tag == 'block': + m2 = re.match ('(.*)\^end', content) + if m2: + parsed = [m2.group (1)] + else: + while content: + (v, content) = read_finale_value (content) + if v <> None: + parsed.append (v) + + tdict [indices].extend (parsed) + + last_indices = indices + last_tag = tag + + continue + +# let's not do this: this really confuses when eE happens to be before a ^text. +# if last_tag and last_indices: +# etf_file_dict[last_tag][last_indices].append (l) + + sys.stderr.write ('\n') + return etf_file_dict + + + + class Etf_file: def __init__ (self, name): self.measures = [None] - self.entries = [None] self.chords = [None] self.frames = [None] + self.tuplets = [None] self.staffs = [None] self.slurs = [None] self.articulations = [None] self.syllables = [None] self.verses = [None] - + self.articulation_defs = [None] + ## do it self.parse (name) def get_global_measure (self, no): - if len (self.measures) <= no: - self.measures = self.measures + [None]* (1 + no - len (self.measures)) - + fill_list_to (self.measures, no) if self.measures[no] == None: self.measures [no] = Global_measure (no) @@ -537,150 +909,125 @@ class Etf_file: def get_staff(self,staffno): - if len (self.staffs) <= staffno: - self.staffs = self.staffs + [None] * (1 + staffno - len (self.staffs)) - + fill_list_to (self.staffs, staffno) if self.staffs[staffno] == None: self.staffs[staffno] = Staff (staffno) return self.staffs[staffno] # staff-spec - def try_IS (self, l): + def try_IS (self, indices, contents): pass - def try_BC (self, l): - m = BCre.match (l) - if m: - bn = string.atoi (m.group (1)) - where = string.atoi (m.group (2)) / 1024.0 - return m - - def try_IM (self, l): - m = IMre.match (l) - if m: - a = string.atoi (m.group (1)) - b = string.atoi (m.group (2)) - - fin = map (string.atoi, m.groups ()[2:]) - - self.articulations.append (Articulation (a,b,fin)) - return m - def try_verse (self,l): - m = versere .match (l) - if m: - a = string.atoi (m.group (1)) - body =m.group (2) - - body = re.sub (r"""\^[a-z]+\([^)]+\)""", "", body) - body = re.sub ("\^[a-z]+", "", body) - self.verses.append (Verse (a, body)) - - return m - def try_ve (self,l): - m = vere .match (l) - if m: - a = string.atoi (m.group (1)) - b = string.atoi (m.group (2)) - - fin = map (string.atoi, m.groups ()[2:]) - - self.syllables.append (Syllable (a,b,fin)) - return m - def try_eE (self, l): - m = eEre.match (l) - if m: - tup = m.groups() - (no, prev, next, dur, pos, entryflag, extended, follow) = tup - (no, prev, next, dur, pos,extended, follow) \ - = tuple (map (string.atoi, [no,prev,next,dur,pos,extended,follow])) - - entryflag = string.atol (entryflag,16) - assert (no==len (self.entries)) - current_entry = ((no, prev, next, dur, pos, entryflag, extended, follow), []) - self.entries.append (current_entry) - return m - - def try_Sx(self,l): - m = Sxre.match (l) - if m: - slurno = string.atoi (m.group (1)) - - if len (self.slurs) == slurno: - self.slurs.append (Slur (slurno)) - - params = list (m.groups ()[1:]) - params = map (string.atoi, params) - self.slurs[-1].append_entry (params) - - return m - def try_GF(self, l): - m = GFre.match (l) - if m: - (staffno,measno) = m.groups ()[0:2] - s = string.atoi (staffno) - me = string.atoi (measno) - - entry = m.groups () [2:] - st = self.get_staff (s) - meas = st.get_measure (me) - meas.add_finale_entry (entry) + def try_BC (self, indices, contents): + bn = indices[0] + where = contents[0] / 1024.0 + def try_TP(self, indices, contents): + (nil, num) = indices + + if self.tuplets[-1] == None or num <> self.tuplets[-1].start_note: + self.tuplets.append (Tuplet (num)) + + self.tuplets[-1].append_finale (contents) + + def try_IM (self, indices, contents): + (a,b) = indices + fin = contents + self.articulations.append (Articulation (a,b,fin)) + def try_verse (self, indices, contents): + a = indices[0] + body = contents[0] + + body = re.sub (r"""\^[a-z]+\([^)]+\)""", "", body) + body = re.sub ("\^[a-z]+", "", body) + self.verses.append (Verse (a, body)) + def try_ve (self,indices, contents): + (a,b) = indices + self.syllables.append (Syllable (a,b,contents)) + + def try_eE (self,indices, contents): + no = indices[0] + (prev, next, dur, pos, entryflag, extended, follow) = contents[:7] + + fill_list_to (self.chords, no) + self.chords[no] =Chord (no, contents) + + def try_Sx(self,indices, contents): + slurno = indices[0] + fill_list_to (self.slurs, slurno) + self.slurs[slurno] = Slur(slurno, contents) + + def try_IX (self, indices, contents): + n = indices[0] + a = contents[0] + b = contents[1] + + ix= None + try: + ix = self.articulation_defs[n] + except IndexError: + ix = Articulation_def (n,a,b) + self.articulation_defs.append (Articulation_def (n, a, b)) + + def try_GF(self, indices, contents): + (staffno,measno) = indices + + st = self.get_staff (staffno) + meas = st.get_measure (measno) + meas.finale = contents - # frame ? - def try_FR(self, l): - m = FRre.match (l) - if m: - (frameno, startnote, endnote, foo, bar) = m.groups () - (frameno, startnote, endnote) = tuple (map (string.atoi, [frameno, startnote, endnote])) - self.frames.append (Frame ((frameno, startnote, endnote))) - - return m - def try_MS (self, l): - m = MSre.match (l) - if m: - measno = string.atoi (m.group (1)) - keynum = string.atoi (m.group (3)) - meas =self. get_global_measure (measno) - meas.set_keysig (keynum) - - beats = string.atoi (m.group (4)) - beatlen = string.atoi (m.group (5)) - meas.set_timesig ((beats, beatlen)) - - return m - - def try_note (self, l): - m = note_re.match (l) - if m: - (pitch, flag) = m.groups () - pitch = string.atoi (pitch) - flag = string.atol (flag,16) - self.entries[-1][1].append ((pitch,flag)) - - def parse (self, name): - sys.stderr.write ('parsing ...') - sys.stderr.flush () + def try_FR(self, indices, contents): + frameno = indices [0] - ls = open (name).readlines () - for l in ls: - m = None - if not m: - m = self.try_MS (l) - if not m: - m = self.try_FR (l) - if not m: - m = self.try_GF (l) - if not m: - m = self.try_note (l) - if not m: - m = self.try_eE (l) - if not m: - m = self.try_IM (l) - if not m: - m = self.try_Sx (l) - if not m: - m = self.try_verse (l) + startnote = contents[0] + endnote = contents[1] + + fill_list_to (self.frames, frameno) + + self.frames[frameno] = Frame ((frameno, startnote, endnote)) + + def try_MS (self, indices, contents): + measno = indices[0] + keynum = contents[1] + meas =self. get_global_measure (measno) + + meas.set_key_sig (keynum) + + beats = contents[2] + beatlen = contents[3] + meas.set_timesig ((beats, beatlen)) + + meas_flag1 = contents[4] + meas_flag2 = contents[5] + + meas.set_flags (meas_flag1, meas_flag2); + + + routine_dict = { + 'MS': try_MS, + 'FR': try_FR, + 'GF': try_GF, + 'IX': try_IX, + 'Sx' : try_Sx, + 'eE' : try_eE, + 'verse' : try_verse, + 've' : try_ve, + 'IM' : try_IM, + 'TP' : try_TP, + 'BC' : try_BC, + 'IS' : try_IS, + } + + def parse (self, etf_dict): + sys.stderr.write ('reconstructing ...') + sys.stderr.flush () + for (tag,routine) in Etf_file.routine_dict.items (): + ks = etf_dict[tag].keys () + ks.sort () + for k in ks: + routine (self, k, etf_dict[tag][k]) + sys.stderr.write ('processing ...') sys.stderr.flush () @@ -691,13 +1038,23 @@ class Etf_file: continue mno = 1 for m in st.measures[1:]: - m.global_measure = self.measures[mno] + if not m: + continue + m.calculate() - + try: + m.global_measure = self.measures[mno] + except IndexError: + sys.stderr.write ("Non-existent global measure %d" % mno) + continue + frame_obj_list = [None] for frno in m.frames: - fr = self.frames[frno] - frame_obj_list.append (fr) + try: + fr = self.frames[frno] + frame_obj_list.append (fr) + except IndexError: + sys.stderr.write ("\nNon-existent frame %d" % frno) m.frames = frame_obj_list for fr in frame_obj_list[1:]: @@ -712,19 +1069,36 @@ class Etf_file: mno = mno + 1 for c in self.chords[1:]: - c.find_realpitch () - c.set_duration () + if c: + c.calculate() + + for f in self.frames[1:]: + if f: + f.calculate () + + for t in self.tuplets[1:]: + t.calculate (self.chords) + + for s in self.slurs[1:]: + if s: + s.calculate (self.chords) - for s in self.slurs [1:]: - s.calculate (self.chords) for s in self.articulations[1:]: - s.calculate (self.chords) + s.calculate (self.chords, self.articulation_defs) def get_thread (self, startno, endno): thread = [] - c = self.chords[startno] - while c and c.number () <> endno: + + c = None + try: + c = self.chords[startno] + except IndexError: + sys.stderr.write ("Huh? Frame has invalid bounds (%d,%d)\n" % (startno, endno)) + return [] + + + while c and c.number <> endno: thread.append (c) c = c.next @@ -741,8 +1115,6 @@ class Etf_file: str = str + '\n\n' + s.dump () staffs.append ('\\' + s.staffid ()) - if staffs: - str = str + '\\score { < %s > } ' % string.join (staffs) # should use \addlyrics ? @@ -752,49 +1124,55 @@ class Etf_file: if len (self.verses) > 1: sys.stderr.write ("\nLyrics found; edit to use \\addlyrics to couple to a staff\n") + if staffs: + str = str + '\\score { << %s >> } ' % string.join (staffs) + return str def __str__ (self): - return self.dump () + return 'ETF FILE %s %s' % (self.measures, self.entries) def unthread_entries (self): - self.chords = [None] - for e in self.entries[1:]: - self.chords.append (Chord (e)) - for e in self.chords[1:]: - e.prev = self.chords[e.finale[0][1]] - e.next = self.chords[e.finale[0][2]] - - - - + if not e: + continue + e.prev = self.chords[e.finale[0]] + e.next = self.chords[e.finale[1]] def identify(): sys.stderr.write ("%s from LilyPond %s\n" % (program_name, version)) def help (): - print r""" -Convert ETF to LilyPond. + sys.stdout.write("""Usage: etf2ly [OPTIONS]... ETF-FILE -Usage: etf2ly [OPTION]... ETF-FILE +Convert ETF to LilyPond. Options: - -h, --help this help + -h, --help print this help -o, --output=FILE set output filename to FILE - -v, --version version information + -v, --version show version information Enigma Transport Format is a format used by Coda Music Technology's Finale product. This program will convert a subset of ETF to a ready-to-use lilypond file. +Report bugs to bug-lilypond@gnu.org. -""" +Written by Han-Wen Nienhuys . + +""") def print_version (): - print r"""etf2ly (GNU lilypond) %s""" % version + sys.stdout.write (r"""etf2ly (GNU lilypond) %s + +This is free software. It is covered by the GNU General Public License, +and you are welcome to change it and/or distribute copies of it under +certain conditions. Invoke as `midi2ly --warranty' for more information. + +Copyright (c) 2000--2004 by Han-Wen Nienhuys +""" % version) @@ -819,15 +1197,17 @@ for opt in options: identify() -# header['tagline'] = 'Lily was here %s -- automatically converted from ABC' % version +e = None for f in files: if f == '-': f = '' sys.stderr.write ('Processing `%s\'\n' % f) - e = Etf_file(f) + + dict = parse_etf_file (f, Etf_file.routine_dict) + e = Etf_file(dict) if not out_filename: - out_filename = os.path.basename (re.sub ('.etf$', '.ly', f)) + out_filename = os.path.basename (re.sub ('(?i).etf$', '.ly', f)) if out_filename == f: out_filename = os.path.basename (f + '.ly')