CVSROOT: /cvsroot/lilypond Module name: ikebana Branch: Changes by: Han-Wen Nienhuys <[EMAIL PROTECTED]> 05/07/28 11:14:10
Modified files: . : ChangeLog ikebana.py music.py notation.py notationcanvas.py server.ly Added files: . : rational.py Log message: * server.ly: use Completion_heads_engraver * music.py (Music.set_start): use Rationals for lengths. * rational.py (Rational.ceil): new class. * notation.py (add_start_skip): new function CVSWeb URLs: http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/rational.py?rev=1.1 http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/ChangeLog.diff?tr1=1.3&tr2=1.4&r1=text&r2=text http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/ikebana.py.diff?tr1=1.5&tr2=1.6&r1=text&r2=text http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/music.py.diff?tr1=1.4&tr2=1.5&r1=text&r2=text http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/notation.py.diff?tr1=1.5&tr2=1.6&r1=text&r2=text http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/notationcanvas.py.diff?tr1=1.5&tr2=1.6&r1=text&r2=text http://savannah.gnu.org/cgi-bin/viewcvs/lilypond/ikebana/server.ly.diff?tr1=1.1.1.1&tr2=1.2&r1=text&r2=text Patches: Index: ikebana/ChangeLog diff -u ikebana/ChangeLog:1.3 ikebana/ChangeLog:1.4 --- ikebana/ChangeLog:1.3 Thu Jul 21 23:14:18 2005 +++ ikebana/ChangeLog Thu Jul 28 11:14:10 2005 @@ -1,3 +1,13 @@ +2005-07-28 Han-Wen Nienhuys <[EMAIL PROTECTED]> + + * server.ly: use Completion_heads_engraver + + * music.py (Music.set_start): use Rationals for lengths. + + * rational.py (Rational.ceil): new class. + + * notation.py (add_start_skip): new function + 2005-07-22 Han-Wen Nienhuys <[EMAIL PROTECTED]> * ikebana.py (NotationApplication.create_window): add zoom. Index: ikebana/ikebana.py diff -u ikebana/ikebana.py:1.5 ikebana/ikebana.py:1.6 --- ikebana/ikebana.py:1.5 Thu Jul 21 23:14:18 2005 +++ ikebana/ikebana.py Thu Jul 28 11:14:10 2005 @@ -12,7 +12,6 @@ def mainquit (*args): gtk.main_quit () - class NotationApplication: def __init__ (self): self.music = music.Music_document () @@ -48,9 +47,6 @@ win.add (vbox) toolbar.show () - toolbar.add_button ('zoom out', 'minus', lambda: canvas.zoom (-1), 0) - toolbar.add_button ('zoom in', 'Shift+plus', lambda: canvas.zoom (1), 0) - win.show() return win Index: ikebana/music.py diff -u ikebana/music.py:1.4 ikebana/music.py:1.5 --- ikebana/music.py:1.4 Thu Jul 21 13:50:36 2005 +++ ikebana/music.py Thu Jul 28 11:14:10 2005 @@ -1,5 +1,5 @@ import string - +from rational import Rational class Duration: def __init__ (self): @@ -28,16 +28,17 @@ return d def length (self): - dot_fact = ((1 << (1 + self.dots))-1.0)/ (1 << self.dots) + dot_fact = Rational( (1 << (1 + self.dots))-1, + 1 << self.dots) log = abs (self.duration_log) dur = 1 << log if self.duration_log < 0: - base = 1.0 * dur + base = Rational (dur) else: - base = 1.0 / dur + base = Rational (1, dur) - return base * dot_fact * self.factor[0]/self.factor[1] + return base * dot_fact * Rational (self.factor[0], self.factor[1]) class Pitch: def __init__ (self): @@ -79,11 +80,11 @@ def __init__ (self): self.tag = None self.parent = None - self.start = 0.0 + self.start = Rational (0) pass def length(self): - return 0.0 + return Rational (0) def set_tag (self, counter, tag_dict): self.tag = counter @@ -105,7 +106,6 @@ return "(make-music '%s %s %s)" % (name, tag, props) def set_start (self, start): - start = round (start * 384) / 384.0 self.start = start def find_first (self, predicate): @@ -122,7 +122,7 @@ def recompute (self): self.tag_dict = {} self.music.set_tag (0, self.tag_dict) - self.music.set_start (0.0) + self.music.set_start (Rational (0)) class NestedMusic(Music): def __init__ (self): @@ -200,7 +200,8 @@ def ly_expression (self): return '{ %s }' % string.join (map (lambda x:x.ly_expression(), - self.elements)) + self.elements)) + def lisp_sub_expression (self, pred): name = self.name() tag = '' @@ -222,7 +223,7 @@ return "EventChord" def length (self): - l = 0.0 + l = Rational (0) for e in self.elements: l = max(l, e.length()) return l @@ -242,6 +243,10 @@ def name (self): return "Event" +class ArpeggioEvent(Music): + def name (self): + return 'ArpeggioEvent' + class RhythmicEvent(Event): def __init__ (self): Event.__init__ (self) @@ -290,17 +295,18 @@ evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) - evc = EventChord() + evc = EventChord() n = NoteEvent() - n.duration.duration_log = l - n.pitch.step = 2 + n.duration.duration_log = 0 + n.pitch.step = 3 evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) - evc = EventChord() + + evc = EventChord() n = NoteEvent() n.duration.duration_log = l - n.pitch.step = 3 + n.pitch.step = 2 evc.insert_around (None, n, 0) m.insert_around (None, evc, 0) @@ -309,7 +315,7 @@ if __name__ == '__main__': expr = test_expr() - expr.set_start (0.0) + expr.set_start (Rational (0)) start = 0.25 stop = 0.5 Index: ikebana/notation.py diff -u ikebana/notation.py:1.5 ikebana/notation.py:1.6 --- ikebana/notation.py:1.5 Thu Jul 21 23:14:18 2005 +++ ikebana/notation.py Thu Jul 28 11:14:10 2005 @@ -7,11 +7,21 @@ import music import pango import math +from rational import Rational + +display_dpi = 75 + +def get_display_dpi(): + str = os.popen ('xdpyinfo | grep "dots per inch"').read () + m = re.match ('([0-9]+)x([0-9]+) dots') + if m: + display_dpi = (string.atoi (m.group (2)) + string.atoi (m.group (2)))/2 copy_lilypond_input = 1 time_sig = (4, 4) -measure_length = (1.0 * time_sig[0]) / time_sig[1] +measure_length = Rational (time_sig[0], time_sig[1]) + scale = "'((0 . 0) (1 . 0) (2 . -2) (3 . 0) (4 . 0) (5 . -2) (6 . -2))" clefsetting = """ (context-spec-music @@ -22,8 +32,10 @@ (make-property-set 'middleCPosition 0) 'Staff) """ -server = 'maagd' -# server = 'localhost' +try: + server = os.environ['IKEBANASERVER'] +except KeyError: + server = 'localhost' lilypond_input_log_file = open ("input.log", 'w') @@ -47,7 +59,7 @@ break cont = len (data) > 0 retval += data - + return retval def set_measure_number (str, num): @@ -69,7 +81,6 @@ %s) )) 'Staff) - %s))""" % (time_sig[0], time_sig[1], time_sig[0], time_sig[1], time_sig[1], num, scale, str) @@ -83,6 +94,15 @@ open (filename, 'w').write (str) base = os.path.splitext (filename)[0] + '.ps' os.system ('(lilypond %s && gv %s)& ' % (filename, base)) + +def add_start_skip (str, start_skip): + return """(make-music 'SequentialMusic 'elements + (list + (make-music 'SkipMusic + 'duration (ly:make-duration 0 0 %d %d)) + %s)) +""" % (start_skip.num, start_skip.den, str) + class Lilypond_socket_parser: @@ -130,13 +150,15 @@ self.notation = Notation (self) self.parser = Lilypond_socket_parser (self.interpret_line) - self.start_moment = 0.0 - self.stop_moment = 3.0 + self.start_moment = Rational (0) + self.stop_moment = Rational (3) def interpret_line (self, offset, cause, bbox, name, fields): notation_item = self.notation.add_item (offset, cause, bbox, fields) notation_item.name = name - + def touch_document (self): + self.document.touched = True + def update_notation(self): doc = self.document doc.recompute() @@ -145,20 +167,34 @@ def sub(x): ok = (x.start >= self.start_moment and - x.start +x.length() <= self.stop_moment) + x.start + x.length() <= self.stop_moment) return ok + + def sub2 (x): + return x.name() in ('RestEvent','NoteEvent') and sub(x) + + start_note = expr.find_first (sub2) + + start_skip = start_note.start - start_note.start.floor() + + print 'start_skip = ' , start_skip + str = expr.lisp_sub_expression (sub) - str = set_measure_number (str, int (self.start_moment) + 1) + str = add_start_skip (str, start_skip) + + bar_count = (self.start_moment / measure_length).floor() + str = set_measure_number (str, bar_count.num) str = talk_to_lilypond (str) self.parse_socket_file (str) def ensure_visible (self, when): - new_start = max (math.floor (when - measure_length), 0.0) - new_stop = new_start + 3 * measure_length + new_start = max ((when - measure_length).floor(), Rational(0)) + new_stop = new_start + Rational (3) * measure_length if new_start <> self.start_moment or new_stop <> self.stop_moment: - self.document.touched = True + print "render interval", new_start, new_stop + self.touch_document() self.start_moment = new_start self.stop_moment = new_stop @@ -185,105 +221,7 @@ self.args = [] self.canvas_item = None self.music_expression = None - - def create_round_box_canvas_item (self, canvas): - root = canvas.root() - type = gnomecanvas.CanvasRect - (b, w, d, h, blot) = tuple (self.args) - w = root.add (type, - fill_color = 'black', - x1 = - b, - y1 = - d, - x2 = w, - y2 = h) - - return w - - def create_line_canvas_item (self, canvas): - type = gnomecanvas.CanvasLine - (thick, x1, y1, x2, y2) = tuple (self.args) - w = canvas.root().add (type, - fill_color = 'black', - width_units = thick, - points = [x1, y1, x2, y2] - ) - return w - - def create_glyph_item (self, canvas): - type = gnomecanvas.CanvasText - (index, font_name, magnification, name) = tuple (self.args) - (family, style) = string.split (font_name, '-') - - w = canvas.root().add (type, - fill_color = 'black', - family = family, - family_set = True, - anchor = gtk.ANCHOR_WEST, - y_offset = 0.15, - - size_points = canvas.pixel_scale * 0.75 * magnification, - x = 0, y = 0, - text = unichr (index)) - return w - - - def create_polygon_item (self, canvas): - type = gnomecanvas.CanvasPolygon - - (blot, fill) = tuple (self.args[:2]) - coords = self.args[2:] - w = canvas.root ().add (type, - fill_color = 'black', - outline_color = 'black', - width_units = blot, - points = coords) - - return w - - def create_text_item (self, canvas): - type = gnomecanvas.CanvasText - (descr, str) = tuple (self.args) - - magnification = 0.5 - -#ugh: how to get pango_descr_from_string() in pygtk? - - if descr.find (',') == -1: - (fam,rest) = tuple (string.split (descr, ' ')) - else: - (fam,rest) = tuple (string.split (descr, ',')) - size = string.atof (rest) - w = canvas.root().add (type, - fill_color = 'black', - family_set = True, - family = fam, - anchor = gtk.ANCHOR_SOUTH_WEST, - y_offset = 0.75, - size_points = size * canvas.pixel_scale * 0.87 * magnification, - text = str) - return w - def create_canvas_item (self, canvas): - citem = None - try: - method = Notation_item.dispatch_table[self.tag] - citem = method (self, canvas) - citem.move (*self.offset) - citem.notation_item = self - - canvas.register_notation_canvas_item (citem) - except KeyError: - print 'no such key', self.tag - - return citem - - dispatch_table = {'draw_round_box' : create_round_box_canvas_item, - 'drawline': create_line_canvas_item, - 'glyphshow':create_glyph_item, - 'polygon': create_polygon_item, - 'utf-8' : create_text_item, - } - class Notation: """A complete line/system/page of LilyPond output. Consists of a number of Notation_items""" @@ -302,7 +240,7 @@ def add_item (self, offset, cause, bbox, fields): item = Notation_item() - item.tag = fields[0] + item.tag = fields[0] item.args = map (eval, fields[1:]) item.offset = offset item.origin_tag = cause @@ -324,10 +262,11 @@ w.destroy() for i in self.items: - c_item = i.create_canvas_item (canvas) + c_item = canvas.create_canvas_item (i) canvas.set_cursor_to_music (self.music_cursor) - + self.touched = False + def set_cursor (self, music_expr): self.music_cursor = music_expr self.cursor_touched = True @@ -341,7 +280,27 @@ mus = mus.parent.get_neighbor (mus, dir) mus = mus.find_first (lambda x: x.name() in ('NoteEvent', 'RestEvent')) self.set_cursor (mus) - + + def cursor_move_chord (self, dir): + mus = self.music_cursor + if mus.name ()=='NoteEvent': + current_steps = mus.pitch.steps() + other_steps = [(note, note.pitch.steps()) for + note in mus.parent.elements if note.name()=='NoteEvent'] + + def cmp(a,b): + if a[1] > b[1]: + return 1 + if a[1] < b[1]: + return -1 + return 0 + + bound_set = [(note, dir * step) for (note, step) in other_steps + if dir * (step - current_steps) > 0] + bound_set.sort (cmp) + if bound_set: + self.set_cursor (bound_set[0][0]) + def insert_at_cursor (self, music, dir): mus = self.music_cursor if mus.parent.name() == 'EventChord': @@ -497,6 +456,22 @@ p.alteration = new_alt self.touch_document () + def set_alteration (self, alter): + if self.music_cursor.name() == 'NoteEvent': + p = self.music_cursor.pitch + p.alteration = alter + self.touch_document () + + def toggle_arpeggio (self): + par = self.music_cursor.parent + if par.name()== "EventChord": + arps = [e for e in par.elements if e.name()=='ArpeggioEvent'] + if arps: + par.delete_element (arps[0]) + else: + arp = music.ArpeggioEvent() + par.insert_around (self.music_cursor, arp, -1) + self.touch_document() def print_score(self): doc = self.notation_controller.document ly = doc.music.ly_expression() Index: ikebana/notationcanvas.py diff -u ikebana/notationcanvas.py:1.5 ikebana/notationcanvas.py:1.6 --- ikebana/notationcanvas.py:1.5 Thu Jul 21 23:14:18 2005 +++ ikebana/notationcanvas.py Thu Jul 28 11:14:10 2005 @@ -2,6 +2,7 @@ import gnomecanvas import music import math +import string class Notation_toolbar (gtk.VBox): def __init__ (self, notation, check_refresh_callback): @@ -66,21 +67,31 @@ lambda: self.notation.print_score(), 0), ('q', 'quit', lambda: gtk.main_quit(), 0), - ('Left', '<-', + ('Left', 'left', lambda: self.notation.cursor_move (-1), 0), - ('Right', '->', + ('Right', 'right', lambda: self.notation.cursor_move (1), 0), + ('Up', 'up', + lambda: self.notation.cursor_move_chord (1), 0), + ('Down', 'down', + lambda: self.notation.cursor_move_chord (-1), 0), ('space', 'new', lambda: self.notation.add_note (), 0), ('BackSpace', 'backspace', lambda: self.notation.backspace (), 0), - ('Shift+Up', '#', - lambda: self.notation.change_alteration (2), 1), - ('Shift+Down', 'b', - lambda: self.notation.change_alteration (-2), 1), - ('Up', 'up', + ('Ctrl+Shift+at', 'bb', + lambda: self.notation.set_alteration (-4), 1), + ('Shift+at', 'b', + lambda: self.notation.set_alteration (-2), 1), + ('Shift+exclam', 'natural', + lambda: self.notation.set_alteration (0), 1), + ('Shift+numbersign', '#', + lambda: self.notation.set_alteration (2), 1), + ('x', 'x', + lambda: self.notation.set_alteration (4), 1), + ('Shift+Up', 'higher', lambda: self.notation.change_step (1), 1), - ('Down', 'down', + ('Shift+Down', 'lower', lambda: self.notation.change_step (-1), 1), ('apostrophe', 'oct up', lambda: self.notation.change_octave (1), 1), @@ -90,6 +101,8 @@ lambda: self.notation.change_dots (), 1), ('slash', 'shorter', lambda: self.notation.change_duration_log (1), 1), + ('Shift+asciitilde', "arpeggio", + lambda: self.notation.toggle_arpeggio (), 1), ('Shift+asterisk', 'longer', lambda: self.notation.change_duration_log (-1), 1), ('r', 'rest', @@ -125,7 +138,6 @@ self.add_button (text, key_name, func, row) - class Notation_canvas (gnomecanvas.Canvas): """The canvas for drawing notation symbols.""" @@ -178,8 +190,10 @@ def click_event (self, widget, event = None): if event <> None and event.type == gtk.gdk.BUTTON_PRESS: - if event.button == 1 and widget.notation_item.name in ('Rest', 'NoteHead'): - + if (event.button == 1 + and widget.notation_item.name in ('Rest', + 'NoteHead')): + notat = self.notation_canvas_controller.notation notat.set_cursor (widget.notation_item.music_expression) self.notation_canvas_controller.check_update() @@ -206,6 +220,152 @@ fact = pow (1.25, delta) self.pixel_scale *= fact self.set_pixels_per_unit (self.pixel_scale) + + + def create_canvas_item (canvas, item): + citem = None + try: + method = canvas.dispatch_table[item.tag] + citem = method (canvas, item) + citem.move (*item.offset) + citem.notation_item = item + + canvas.register_notation_canvas_item (citem) + except KeyError: + print 'no such key', item.tag + + return citem + + def create_round_box_canvas_item (self, item): + canvas = self + root = canvas.root() + type = gnomecanvas.CanvasRect + (b, w, d, h, blot) = tuple (item.args) + w = root.add (type, + fill_color = 'black', + x1 = - b, + y1 = - d, + x2 = w, + y2 = h) + + return w + + def create_line_canvas_item (self, item): + canvas = self + type = gnomecanvas.CanvasLine + (thick, x1, y1, x2, y2) = tuple (item.args) + w = canvas.root().add (type, + fill_color = 'black', + width_units = thick, + points = [x1, y1, x2, y2] + ) + return w + + def create_glyph_item (self, item): + canvas = self + type = gnomecanvas.CanvasText + (index, font_name, magnification, name) = tuple (item.args) + (family, style) = string.split (font_name, '-') + + sz = canvas.pixel_scale * 0.75 * magnification + sz *= 1.2 + + w = canvas.root().add (type, + fill_color = 'black', + family = family, + family_set = True, + anchor = gtk.ANCHOR_WEST, + y_offset = 0.15, + + size_points = sz, + x = 0, y = 0, + + text = unichr (index)) + return w + + + def create_polygon_item (self, item): + type = gnomecanvas.CanvasPolygon + canvas = self + + (blot, fill) = tuple (item.args[:2]) + coords = item.args[2:] + w = canvas.root ().add (type, + fill_color = 'black', + outline_color = 'black', + width_units = blot, + points = coords) + + return w + + def create_bezier_sandwich (self, item): + type = gnomecanvas.CanvasBpath + canvas = self + + (thick,points) = tuple (item.args) + + (tcl, tcr, tr, tl, + bcr, bcl, bl, br) = tuple (points) + + + path = [(gnomecanvas.MOVETO_OPEN, tl[0], tl[1]), + (gnomecanvas.CURVETO, + tcl[0], tcl[1], + tcr[0], tcr[1], + tr[0], tr[1]), + (gnomecanvas.LINETO, + br[0], br[1]), + (gnomecanvas.CURVETO, + bcr[0], bcr[1], + bcl[0], bcl[1], + bl[0], bl[1]), + (gnomecanvas.LINETO, + tl[0], tl[1]) + ] + + pathdef = gnomecanvas.path_def_new(path) + # pathdef.closepath() + w = canvas.root().add (type, + fill_color = 'black', + outline_color = 'black', + width_units = thick) + w.set_bpath (pathdef) + + return w + + def create_text_item (self, item): + canvas = self + type = gnomecanvas.CanvasText + (descr, str) = tuple (item.args) + + magnification = 0.5 + + #ugh: how to get pango_descr_from_string() in pygtk? + + if descr.find (',') == -1: + (fam,rest) = tuple (string.split (descr, ' ')) + else: + (fam,rest) = tuple (string.split (descr, ',')) + size = string.atof (rest) + w = canvas.root().add (type, + fill_color = 'black', + family_set = True, + family = fam, + anchor = gtk.ANCHOR_SOUTH_WEST, + y_offset = 0.75, + size_points = size * canvas.pixel_scale * 0.87 * magnification, + text = str) + return w + + dispatch_table = {'draw_round_box' : create_round_box_canvas_item, + 'drawline': create_line_canvas_item, + 'glyphshow':create_glyph_item, + 'polygon': create_polygon_item, + 'utf-8' : create_text_item, + 'bezier_sandwich': create_bezier_sandwich, + } + + class Notation_canvas_controller: """The connection between canvas and the abstract notation graphics.""" Index: ikebana/server.ly diff -u ikebana/server.ly:1.1 ikebana/server.ly:1.2 --- ikebana/server.ly:1.1 Wed Jul 13 19:10:53 2005 +++ ikebana/server.ly Thu Jul 28 11:14:10 2005 @@ -10,6 +10,11 @@ \Score \override BarNumber #'break-visibility = #all-visible } + \context { + \Voice + \remove "Note_heads_engraver" + \consists "Completion_heads_engraver" + } } #(define (render-socket-music music socket) @@ -55,7 +60,9 @@ (close client) (display (format "Finished. Time elapsed: ~a\n" (/ (- (get-internal-real-time) start-time) (* 1.0 internal-time-units-per-second)) + )) + (gc) ; do GC while app is refreshing the screen. ))))) @@ -76,3 +83,4 @@ #(render-socket-music test-exp "test") + _______________________________________________ Lilypond-cvs mailing list Lilypond-cvs@gnu.org http://lists.gnu.org/mailman/listinfo/lilypond-cvs