From: Jan Nieuwenhuizen Date: Sun, 14 Oct 2001 18:12:40 +0000 (+0200) Subject: patch::: 1.5.17.jcn1 X-Git-Tag: release/1.5.18~6 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=06479a7762e68f37f779e06e8ca1cf6c6a539002;p=lilypond.git patch::: 1.5.17.jcn1 1.5.17.jcn1 =========== * Some more random hacking at midi2ly.py. 1.5.17 ====== --- diff --git a/CHANGES b/CHANGES index f2983b1216..60db4fa721 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,11 @@ +1.5.17.jcn1 +=========== + +* Some more random hacking at midi2ly.py. + +1.5.17 +====== + 1.5.16.hjj2 =========== diff --git a/VERSION b/VERSION index d157673176..ab74d3e452 100644 --- a/VERSION +++ b/VERSION @@ -2,7 +2,7 @@ PACKAGE_NAME=LilyPond MAJOR_VERSION=1 MINOR_VERSION=5 PATCH_LEVEL=17 -MY_PATCH_LEVEL= +MY_PATCH_LEVEL=jcn1 # use the above to send patches: MY_PATCH_LEVEL is always empty for a # released version. diff --git a/scripts/midi2ly.py b/scripts/midi2ly.py index 23ca1264f8..96f19649b2 100644 --- a/scripts/midi2ly.py +++ b/scripts/midi2ly.py @@ -7,8 +7,10 @@ import string LINE_BELL = 60 scale_steps = [0,2,4,5,7,9,11] -whole_clocks = 1536 -quart_clocks = 0 +clocks_per_1 = 1536 + + +program_name = 'midi2ly.py [experimental]' def split_track (track): chs = {} @@ -28,16 +30,20 @@ def split_track (track): if chs[i] == []: del chs[i] - whatarewes = [] + threads = [] for v in chs.values (): - ns = notes_on_channel (v) - dinges = unthread_notes (ns) - if len (dinges): - whatarewes.append (dinges) - return whatarewes + events = events_on_channel (v) + thread = unthread_notes (events) + if len (thread): + threads.append (thread) + return threads class Note: + names = (0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6) + accidentals = (0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0) + acc_name = ('eses', 'es', 'BUG', 'is' , 'isis') + def __init__ (self, clocks, pitch, velocity): self.velocity = velocity self.pitch = pitch @@ -52,63 +58,172 @@ class Note: return 0 def dump (self): - return dump_note (self) + # major scale: do-do + # minor scale: la-la (= + 5) ''' + + n = self.names[self.pitch % 12] + a = self.accidentals[(key.minor * 5 + self.pitch) % 12] + if a and key.flats: + a = -a + n = (n + 1) % 7 + + name = chr ((n + 2) % 7 + ord ('a')) + + if a: + name = name + self.acc_name[a + 2] + + # By tradition, all scales now consist of a sequence + # of 7 notes each with a distinct name, from amongst + # a b c d e f g. But, minor scales have a wide + # second interval at the top - the 'leading note' is + # sharped. (Why? it just works that way! Anything + # else doesn't sound as good and isn't as flexible at + # saying things. In medieval times, scales only had 6 + # notes to avoid this problem - the hexachords.) + + # So, the d minor scale is d e f g a b-flat c-sharp d + # - using d-flat for the leading note would skip the + # name c and duplicate the name d. Why isn't c-sharp + # put in the key signature? Tradition. (It's also + # supposedly based on the Pythagorean theory of the + # cycle of fifths, but that really only applies to + # major scales...) Anyway, g minor is g a b-flat c d + # e-flat f-sharp g, and all the other flat minor keys + # end up with a natural leading note. And there you + # have it. + + # John Sankey + # + # Let's also do a-minor: a b c d e f gis a + # + # --jcn + + if key.minor: + if key.sharps == 0 and key.flats == 0 and name == 'as': + name = 'gis' + elif key.flats == 1 and name == 'des': + name = 'cis' + elif key.flats == 2 and name == 'ges': + name = 'fis' + elif key.sharps == 5 and name == 'g': + name = 'fisis' + elif key.sharps == 6 and name == 'd': + name = 'cisis' + elif key.sharps == 7 and name == 'a': + name = 'gisis' + + if key.flats >= 6 and name == 'b': + name = 'ces' + if key.sharps >= 7 and name == 'e': + name = 'fes' + + s = name + + o = self.pitch / 12 - 4 + if o > 0: + s = s + "'" * o + elif o < 0: + s = s + "," * -o + + return s + dump_duration (self.clocks) + ' ' + class Time: - def __init__ (self, t, num, den): - self.skip = t + def __init__ (self, num, den): + self.clocks = 0 self.num = num self.den = den + + def dump (self): + return '\n ' + '\\time %d/%d ' % (self.num, self.den) + '\n ' + +class Tempo: + def __init__ (self, seconds_per_1): self.clocks = 0 - + self.seconds_per_1 = seconds_per_1 + def dump (self): - s = '' - if self.skip: - s = dump_skip (self.skip) - s = s + '\n ' - return s + '\\time %d/%d ' % (self.num, self.den) + '\n ' + # return '\n ' + '\\tempo 1 = %d ' % (60 / self.seconds_per_1) + '\n ' + return '\n ' + '\\tempo 4 = %d ' % (4 * 60 / self.seconds_per_1) + '\n ' class Key: key_sharps = ('c', 'g', 'd', 'a', 'e', 'b', 'fis') key_flats = ('BUG', 'f', 'bes', 'es', 'as', 'des', 'ges') - def __init__ (self, t, sharps, flats, minor): - self.skip = t + def __init__ (self, sharps, flats, minor): + self.clocks = 0 self.flats = flats self.sharps = sharps self.minor = minor - self.clocks = 0 - + def dump (self): + global key + key = self + s = '' - if self.skip: - s = dump_skip (self.skip) - s = s + '\n ' if self.sharps and self.flats: s = '\\keysignature %s ' % 'TODO' - elif self.sharps: - s = '\\notes\\key %s \major' % self.key_sharps[self.sharps] - elif self.flats: - s = '\\notes\\key %s \major' % self.key_flats[self.flats] - return s + '\n ' + else: + + if self.flats: + k = (ord ('cfbeadg'[self.flats % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7 + else: + k = (ord ('cgdaebf'[self.sharps % 7]) - ord ('a') - 2 -2 * self.minor + 7) % 7 + + if not self.minor: + name = chr ((k + 2) % 7 + ord ('a')) + else: + name = chr ((k + 2) % 7 + ord ('a')) + + # fis cis gis dis ais eis bis + sharps = (2, 4, 6, 1, 3, 5, 7) + # bes es as des ges ces fes + flats = (6, 4, 2, 7, 5, 3, 1) + a = 0 + if self.flats: + if flats[k] <= self.flats: + a = -1 + else: + if sharps[k] <= self.sharps: + a = 1 + + if a: + name = name + Note.acc_name[a + 2] + + s = '\\key ' + name + if self.minor: + s = s + ' \\minor' + else: + s = s + ' \\major' + + return '\n\n ' + s + '\n ' +# TODO: really handle lyrics class Text: - def __init__ (self, t, text): - self.skip = t - self.text = text + text_types = ( + 'SEQUENCE_NUMBER', + 'TEXT_EVENT', + 'COPYRIGHT_NOTICE', + 'SEQUENCE_TRACK_NAME', + 'INSTRUMENT_NAME', + 'LYRIC', + 'MARKER', + 'CUE_POINT',) + + def __init__ (self, type, text): self.clocks = 0 + self.type = type + self.text = text def dump (self): - s = '' - if self.skip: - s = dump_skip (self.skip) - return s + dump_text (self) + return '\n % [' + self.text_types[self.type] + '] ' + self.text + '\n ' -def notes_on_channel (channel): +def events_on_channel (channel): pitches = {} - nch = [] + notes = [] + events = [] for e in channel: t = e[0] @@ -119,34 +234,58 @@ def notes_on_channel (channel): try: (lt, vel) = pitches[e[1][1]] del pitches[e[1][1]] - - nch.append ((lt, Note (t-lt, e[1][1], vel))) - + + i = len (notes) - 1 + while i > 0: + if notes[i][0] > lt: + i = i -1 + else: + break + notes.insert (i + 1, + (lt, Note (t-lt, e[1][1], vel))) + except KeyError: pass elif e[1][0] == midi.META_EVENT: - if e[1][1] == midi.TIME_SIGNATURE: - (num, den, clocks4, count32) = map (ord, e[1][2]) - nch.append ((t, Time (t, num, den))) + if e[1][1] == midi.SET_TEMPO: + (u0, u1, u2) = map (ord, e[1][2]) + us_per_4 = u2 + 256 * (u1 + 256 * u0) + seconds_per_1 = us_per_4 * 4 / 1e6 + events.append ((t, Tempo (seconds_per_1))) + elif e[1][1] == midi.TIME_SIGNATURE: + (num, dur, clocks4, count32) = map (ord, e[1][2]) + den = 2 ** dur + events.append ((t, Time (num, den))) elif e[1][1] == midi.KEY_SIGNATURE: - (accidentals, minor) = map (ord, e[1][2]) + if len (e[1][2]) != 2: + # circumvent lilypond bug + (accidentals, minor) = (0,0) + else: + (accidentals, minor) = map (ord, e[1][2]) sharps = 0 flats = 0 if accidentals < 127: sharps = accidentals else: flats = 256 - accidentals - nch.append ((t, Key (t, sharps, flats, minor))) - elif e[1][1] == midi.TEXT_EVENT: - nch.append ((t, Text (t, e[1][2]))) + events.append ((t, Key (sharps, flats, minor))) + elif e[1][1] >= midi.SEQUENCE_NUMBER and e[1][1] <= midi.CUE_POINT: + events.append ((t, Text (e[1][1], e[1][2]))) else: sys.stderr.write ("SKIP: %s\n" % `e`) pass else: sys.stderr.write ("SKIP: %s\n" % `e`) pass - - return nch + + i = 0 + while len (notes): + if i < len (events) and notes[0][0] >= events[i][0]: + i = i + 1 + else: + events.insert (i, notes[0]) + del notes[0] + return events def unthread_notes (channel): threads = [] @@ -162,7 +301,7 @@ def unthread_notes (channel): thread.append (e) start_busy_t = t end_busy_t = t + e[1].clocks - elif e[1].__class__ == Time or e[1].__class__ == Key or e[1].__class__ == Text: + elif e[1].__class__ == Time or e[1].__class__ == Key or e[1].__class__ == Text or e[1].__class__ == Tempo: thread.append (e) else: todo.append (e) @@ -185,8 +324,8 @@ def dump_skip (clocks): return 's' + dump_duration (clocks) + ' ' def dump_duration (clocks): - g = gcd (clocks, whole_clocks) - (d, n) = (whole_clocks/ g, clocks / g) + g = gcd (clocks, clocks_per_1) + (d, n) = (clocks_per_1/ g, clocks / g) if n == 1: s = '%d' % d elif n == 3 and d != 1: @@ -195,40 +334,28 @@ def dump_duration (clocks): s = '%d*%d' % (d, n) return s -def dump_note (note): - p = note.pitch - oct = p / 12 - step = p % 12 - - i = 0 - while i < len (scale_steps): - if scale_steps[i] > step: - break - i = i+1 - - i = i-1 - str = chr ((i + 2) % 7 + ord ('a')) - if scale_steps[i] <> step: - str = str + 'is' - - return str + dump_duration (note.clocks) + ' ' - def dump (self): return self.dump () -def dump_text (text): - return '\n % ' + text.text + '\n ' - def dump_chord (ch): s = '' - if len(ch) == 1: - s = dump (ch[0]) - else: - s = '<' + string.join (map (dump, ch)) + '>' + notes = [] + for i in ch: + if i.__class__ == Note: + notes.append (i) + else: + s = s + i.dump () + if len (notes) == 1: + s = s + dump (notes[0]) + elif len (notes) > 1: + s = s + '<' + string.join (map (dump, notes)) + '>' return s # thread? def dump_channel (thread): + global key + + key = Key (0, 0, 0) last_e = None chs = [] ch = [] @@ -260,22 +387,15 @@ def dump_channel (thread): lines[-1] = lines[-1] + dump_chord (ch[1]) - last_t = t + ch[1][0].clocks + clocks = 0 + for i in ch[1]: + if i.clocks > clocks: + clocks = i.clocks + + last_t = t + clocks return string.join (lines, '\n ') + '\n' -def dump_notes (channel): - on_hold = [] - s = '' - for e in channel: - if e[0] <> last_t: - s = s + dump_chord (on_hold) - on_hold = [] - last_t = e[0] - - on_hold.append (e) - return s - def track_name (i): return 'track%c' % (i + ord ('A')) @@ -301,19 +421,21 @@ def dump_track (channels, n): def convert_midi (f): - global whole_clocks, quart_clocks + global clocks_per_1 str = open (f).read () midi_dump = midi.parse (str) - whole_clocks = midi_dump[0][1] - quart_clocks = whole_clocks / 4 + clocks_per_1 = midi_dump[0][1] tracks = [] for t in midi_dump[1]: tracks.append (split_track (t)) + tag = '%% Lily was here -- automatically converted by %s from %s' % ( program_name, f) + s = '' + s = tag + '\n\n' for i in range (len (tracks)): s = s + dump_track (tracks[i], i)