import pango
import math
+
+copy_lilypond_input = 1
+time_sig = (4, 4)
+measure_length = (1.0 * 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
+ (make-property-set 'clefGlyph "clefs.C") 'Staff)
+ (context-spec-music
+ (make-property-set 'clefPosition 0) 'Staff)
+ (context-spec-music
+ (make-property-set 'middleCPosition 0) 'Staff)
+"""
+
+lilypond_input_log_file = open ("input.log", 'w')
+
def talk_to_lilypond (expression_str):
"""Send a LISP expression to LilyPond, wait for return value."""
+ if copy_lilypond_input:
+ lilypond_input_log_file.write (expression_str)
+ lilypond_input_log_file.flush ()
+
sock = socket.socket (socket.AF_INET)
address = ("localhost", 2904)
sock.connect (address)
def set_measure_number (str, num):
return """(make-music 'SequentialMusic 'elements (list
+ (context-spec-music
+ (make-property-set 'timeSignatureFraction (cons %d %d)) 'Score)
+ (context-spec-music
+ (make-property-set 'measureLength (ly:make-moment %d %d)) 'Score)
+ (context-spec-music
+ (make-property-set 'beatLength (ly:make-moment 1 %d)) 'Score)
(context-spec-music
(make-property-set 'currentBarNumber %d) 'Score)
- %s))""" % (num,str)
+ (context-spec-music
+ (make-music 'EventChord
+ 'elements
+ (list
+ (make-music 'KeyChangeEvent
+ 'pitch-alist
+ %s)
+ ))
+ 'Staff)
+
+
+ %s))""" % (time_sig[0], time_sig[1], time_sig[0],
+ time_sig[1], time_sig[1], num, scale, str)
+
+def render_score (filename, ly):
+ print ly
+ str = '''
+myNotes = %s
+\\score { \myNotes }
+''' % ly
+ open (filename, 'w').write (str)
+ base = os.path.splitext (filename)[0] + '.ps'
+ os.system ('(lilypond %s && gv %s)& ' % (filename, base))
+
class Lilypond_socket_parser:
"""Maintain state of reading multi-line lilypond output for socket transport."""
return
elif fields[0] == 'cause':
self.cause_tag = string.atoi (fields[1])
- self.name = fields[2]
+ self.name = fields[2][1:-1]
self.bbox = tuple (map (string.atof, fields[3:]))
return
return self.interpret_socket_line (offset, self.cause_tag,
- self.bbox, fields)
+ self.bbox, self.name,
+ fields)
class Notation_controller:
"""Couple Notation and the music model. Stub for now. """
self.start_moment = 0.0
self.stop_moment = 3.0
- def interpret_line (self, offset, cause, bbox, fields):
+ def interpret_line (self, offset, cause, bbox, name, fields):
notation_item = self.notation.add_item (offset, cause, bbox, fields)
+ notation_item.name = name
def update_notation(self):
doc = self.document
ok = (x.start >= self.start_moment and
x.start +x.length() <= self.stop_moment)
return ok
-
str = expr.lisp_sub_expression (sub)
str = set_measure_number (str, int (self.start_moment) + 1)
self.parse_socket_file (str)
def ensure_visible (self, when):
- self.start_moment = max (math.floor (when - 1.0), 0.0)
- self.stop_moment = self.start_moment + 3.0
-
+ new_start = max (math.floor (when - measure_length), 0.0)
+ new_stop = new_start + 3 * measure_length
+
+ if new_start <> self.start_moment or new_stop <> self.stop_moment:
+ self.document.touched = True
+
+ self.start_moment = new_start
+ self.stop_moment = new_stop
+
def parse_socket_file (self, str):
self.notation.clear ()
lines = string.split (str, '\n')
self.parse_lines (lines)
+ self.notation.touched = True
+ self.document.touched = False
def parse_lines (self, lines):
for l in lines:
self.bbox = None
self.offset = (0,0)
self.tag = None
+ self.name = ''
self.args = []
self.canvas_item = None
self.music_expression = None
coords = self.args[2:]
w = canvas.root ().add (type,
fill_color = 'black',
+ outline_color = 'black',
width_units = blot,
points = coords)
magnification = 0.5
#ugh: how to get pango_descr_from_string() in pygtk?
-
- (fam,rest) = tuple (string.split (descr, ','))
+
+ 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_WEST,
- y_offset = 0.15,
- size_points = size * canvas.pixel_scale * 0.75 * magnification,
+ 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):
- dispatch_table = {'draw_round_box' : Notation_item.create_round_box_canvas_item,
- 'drawline': Notation_item.create_line_canvas_item,
- 'glyphshow': Notation_item.create_glyph_item,
- 'polygon': Notation_item.create_polygon_item,
- 'utf-8' : Notation_item.create_text_item,
- }
-
citem = None
try:
- method = dispatch_table[self.tag]
+ method = Notation_item.dispatch_table[self.tag]
citem = method (self, canvas)
citem.move (*self.offset)
citem.notation_item = self
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
def __init__ (self, controller):
self.items = []
self.notation_controller = controller
-
+ self.touched = True
+ self.cursor_touched = True
+
toplevel = controller.document.music
self.music_cursor = toplevel.find_first (lambda x: x.name()== "NoteEvent")
item.bbox = bbox
self.items.append (item)
-
-
+ return item
+
def clear(self):
self.items = []
-
+
def paint_on_canvas (self, canvas):
for w in canvas.root().item_list:
if w.notation_item:
canvas.set_cursor_to_music (self.music_cursor)
+ def set_cursor (self, music_expr):
+ self.music_cursor = music_expr
+ self.cursor_touched = True
+ self.ensure_cursor_visible ()
+
def cursor_move (self, dir):
mus = self.music_cursor
if mus.parent.name() == 'EventChord':
mus = mus.parent.get_neighbor (mus, dir)
mus = mus.find_first (lambda x: x.name() in ('NoteEvent', 'RestEvent'))
- self.music_cursor = mus
+ self.set_cursor (mus)
def insert_at_cursor (self, music, dir):
mus = self.music_cursor
mus = mus.parent
mus.parent.insert_around (mus, music, dir)
+ self.touch_document()
+
+ def touch_document (self):
+ self.get_document ().touched = True
+ def check_update (self):
+ if self.get_document().touched:
+ self.notation_controller.update_notation ()
+
def backspace (self):
mus = self.music_cursor
- if mus.parent.name() == 'EventChord':
+ if mus.parent.name() == 'EventChord' and len (mus.parent.elements) <= 1:
mus = mus.parent
neighbor = mus.parent.get_neighbor (mus, -1)
mus.parent.delete_element (neighbor)
-
+ self.touch_document ()
def change_octave (self, dir):
if self.music_cursor.name() == 'NoteEvent':
p = self.music_cursor.pitch
p.octave += dir
+ self.touch_document ()
- def change_step (self, step):
+ def set_step (self, step):
+ self.ensure_note ()
if self.music_cursor.name() == 'NoteEvent':
# relative mode.
p1.octave += 1
self.music_cursor.pitch = p1
+ self.touch_document ()
+
+ else:
+ print 'not a NoteEvent'
+
+ def add_step (self, step):
+ self.ensure_note ()
+ if self.music_cursor.name() == 'NoteEvent':
+
+ # relative mode.
+ p = self.music_cursor.pitch
+ p1 = p.copy()
+ p1.step = step
+
+ orig_steps = p.steps ()
+ new_steps = p1.steps ()
+ diff = new_steps - orig_steps
+ if diff >= 4:
+ p1.octave -= 1
+ elif diff <= -4:
+ p1.octave += 1
+
+ new_ev = music.NoteEvent()
+ new_ev.pitch = p1
+ new_ev.duration = self.music_cursor.duration.copy()
+
+ self.music_cursor.parent.insert_around (self.music_cursor,
+ new_ev, 1)
+ self.music_cursor = new_ev
+ self.touch_document ()
+ else:
+ print 'not a NoteEvent'
+
+ def change_step (self, dstep):
+ self.ensure_note ()
+ if self.music_cursor.name() == 'NoteEvent':
+
+ # relative mode.
+ p = self.music_cursor.pitch
+ p1 = p.copy()
+ p1.step += dstep
+
+ if p1.step > 6:
+ p1.step -= 7
+ p1.octave += 1
+ elif p1.step < 0:
+ p1.step += 7
+ p1.octave -= 1
+
+ self.music_cursor.pitch = p1
+ self.touch_document ()
else:
print 'not a NoteEvent'
dl += dir
if dl <= 6 and dl >= -3:
dur.duration_log = dl
-
+
+ self.touch_document ()
+
def ensure_note (self):
if self.music_cursor.name() == 'RestEvent':
m.parent.insert_around (None, note, 1)
m.parent.delete_element (m)
self.music_cursor = note
+ self.touch_document ()
def ensure_rest (self):
if self.music_cursor.name() == 'NoteEvent':
m.parent.insert_around (None, rest, 1)
m.parent.delete_element (m)
self.music_cursor = rest
+ self.touch_document ()
def change_dots (self):
if self.music_cursor.name() == 'NoteEvent':
p.dots = 0
elif p.dots == 0:
p.dots = 1
+ self.touch_document ()
def ensure_cursor_visible(self):
self.notation_controller.document.recompute()
new_alt = p.alteration + dir
if abs (new_alt) <= 4:
p.alteration = new_alt
+ self.touch_document ()
def print_score(self):
doc = self.notation_controller.document
ly = doc.music.ly_expression()
- print ly
+ render_score('score.ly', ly)
+
+ def add_note (self):
+ if self.music_cursor.name () == 'NoteEvent':
+ note = music.NoteEvent ()
+ note.pitch = self.music_cursor.pitch.copy()
+ note.duration = self.music_cursor.duration.copy()
+
+ ch = music.EventChord ()
+ ch.insert_around (None, note, 0)
+
+ self.insert_at_cursor (ch, 1)
+ self.cursor_move (1)
+ self.touch_document ()
+
+ elif self.music_cursor.name () == 'RestEvent':
+ rest = music.RestEvent ()
+ rest.duration = self.music_cursor.duration.copy()
+
+ ch = music.EventChord ()
+ ch.insert_around (None, rest, 0)
+
+ self.insert_at_cursor (ch, 1)
+ self.cursor_move (1)
+ self.touch_document ()
+
+
import gnomecanvas
import music
+
+class Notation_toolbar (gtk.HBox):
+ def __init__ (self, notation, check_refresh_callback):
+ gtk.HBox.__init__ (self)
+ self.button_dict = {}
+ self.key_dict = {}
+ self.notation = notation
+ self.add_buttons ()
+ self.check_refresh_callback = check_refresh_callback
+
+ def click_callback (self, widget):
+ if not self.button_dict.has_key (widget):
+ print 'no such widget?'
+ return False
+
+ cb = self.button_dict[widget]
+ cb()
+ self.check_refresh_callback()
+ return True
+
+ def keypress_callback (self, widget, event):
+ key = event.keyval
+ name = gtk.gdk.keyval_name (key)
+
+ if event.get_state () & gtk.gdk.SHIFT_MASK:
+ name = 'Shift+' + name
+ if event.get_state () & gtk.gdk.CONTROL_MASK:
+ name = 'Ctrl+' + name
+ if not self.key_dict.has_key (name):
+ print 'no such key?', name
+ return False
+
+ button = self.key_dict[name]
+ button.do_activate (button)
+ return True
+
+ def add_button (self, text, key, callback):
+ b = gtk.Button (text)
+ self.pack_start (b, expand=True)
+ b.connect ('clicked', self.click_callback)
+ b.set_focus_on_click (False)
+ self.key_dict[key] = b
+ self.button_dict[b] = callback
+ b.show ()
+
+ def add_buttons (self):
+ for (key_name, text, func) in \
+ [('Left', '<-',
+ lambda: self.notation.cursor_move (-1)),
+ ('Right', '->',
+ lambda: self.notation.cursor_move (1)),
+ ('space', 'next',
+ lambda: self.notation.add_note ()),
+ ('BackSpace', 'backspace',
+ lambda: self.notation.backspace ()),
+ ('Shift+Up', '#',
+ lambda: self.notation.change_alteration (2)),
+ ('Shift+Down', 'b',
+ lambda: self.notation.change_alteration (-2)),
+ ('Up', 'up',
+ lambda: self.notation.change_step (1)),
+ ('Down', 'down',
+ lambda: self.notation.change_step (-1)),
+ ('apostrophe', 'oct up',
+ lambda: self.notation.change_octave (1)),
+ ('comma', 'oct down',
+ lambda: self.notation.change_octave (-1)),
+ ('period', '.',
+ lambda: self.notation.change_dots ()),
+ ('slash', 'shorter',
+ lambda: self.notation.change_duration_log (1)),
+ ('Shift+asterisk', 'longer',
+ lambda: self.notation.change_duration_log (-1)),
+ ('p', 'LilyPond',
+ lambda: self.notation.print_score()),
+ ('q', 'quit',
+ lambda: gtk.main_quit()),
+ ('r', 'rest',
+ lambda: self.notation.ensure_rest ()),
+ ('Shift+C', '+C',
+ lambda: self.notation.add_step (0)),
+ ('Shift+D', '+D',
+ lambda: self.notation.add_step (1)),
+ ('Shift+E', '+E',
+ lambda: self.notation.add_step (2)),
+ ('Shift+F', '+F',
+ lambda: self.notation.add_step (3)),
+ ('Shift+G', '+G',
+ lambda: self.notation.add_step (4)),
+ ('Shift+A', '+A',
+ lambda: self.notation.add_step (5)),
+ ('Shift+B', '+B',
+ lambda: self.notation.add_step (6)),
+ ('c', 'C',
+ lambda: self.notation.set_step (0)),
+ ('d', 'D',
+ lambda: self.notation.set_step (1)),
+ ('e', 'E',
+ lambda: self.notation.set_step (2)),
+ ('f', 'F',
+ lambda: self.notation.set_step (3)),
+ ('g', 'G',
+ lambda: self.notation.set_step (4)),
+ ('a', 'A',
+ lambda: self.notation.set_step (5)),
+ ('b', 'B',
+ lambda: self.notation.set_step (6))]:
+
+ self.add_button (text, key_name, func)
+
+
class Notation_canvas (gnomecanvas.Canvas):
"""The canvas for drawing notation symbols."""
- def __init__ (self):
+ def __init__ (self, canvas_controller):
gnomecanvas.Canvas.__init__ (self,
#aa=True
)
- w,h = 800,400
+ (w,h) = (800,400)
self.set_size_request (w, h)
self.set_scroll_region (0, 0, w, h)
root = self.root ()
i = root.add (gnomecanvas.CanvasRect, x2 = w, y2 = -h,
fill_color = 'white', outline_color = 'white')
i.notation_item = None
+ self.notation_canvas_controller = canvas_controller
self.create_cursor ()
def create_cursor (self):
type = gnomecanvas.CanvasRect
w = self.root ().add (type,
- fill_color = 'None',
- outline_color = 'blue')
+ fill_color = 'lightblue',
+ outline_color = 'lightblue')
w.notation_item = None
self.cursor_widget = w
item.set (fill_color = color)
- def item_event (self, widget, event=None):
- if 0:
- pass
- elif event.type == gtk.gdk.ENTER_NOTIFY:
- self.item_set_active_state(widget, True)
- return True
- elif event.type == gtk.gdk.LEAVE_NOTIFY:
- self.item_set_active_state(widget, False)
- return True
+ 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'):
+
+ notat = self.notation_canvas_controller.notation
+ notat.set_cursor (widget.notation_item.music_expression)
+ self.notation_canvas_controller.check_update()
+
+ return True
return False
def register_notation_canvas_item (self, citem):
if citem.notation_item and citem.notation_item.music_expression:
- citem.connect ("event", self.item_event)
+ citem.connect ("event", self.click_event)
def set_cursor_to_music (self, music_expression):
c_items = [it for it in self.root().item_list if
and it.notation_item.music_expression
== music_expression)]
+ c_items = filter (lambda it: it.notation_item.name in ('NoteHead', 'Rest'),
+ c_items)
if c_items:
self.set_cursor (c_items[0].notation_item)
"""The connection between canvas and the abstract notation graphics."""
def __init__ (self, notation):
- self.canvas = Notation_canvas ()
+ self.canvas = Notation_canvas (self)
self.notation = notation
- self.canvas.connect ("key-press-event", self.keypress)
-
+
def update_cursor (self):
self.canvas.set_cursor_to_music (self.notation.music_cursor)
def update_canvas (self):
self.notation.paint_on_canvas (self.canvas)
-
- def add_note (self):
- note = music.NoteEvent ()
- if self.notation.music_cursor.name () == 'NoteEvent':
- note.pitch = self.notation.music_cursor.pitch.copy()
- note.pitch.alteration = 0
- note.duration = self.notation.music_cursor.duration.copy()
-
- ch = music.EventChord ()
- ch.insert_around (None, note, 0)
-
- self.notation.insert_at_cursor (ch, 1)
- self.notation.cursor_move (1)
-
- def keypress (self, widget, event):
- key = event.keyval
- name = gtk.gdk.keyval_name (key)
- if 0:
- pass
- elif name == 'Left':
- self.notation.cursor_move (-1)
- self.update_cursor ()
-
- elif name == 'Right':
- self.notation.cursor_move (1)
+ def check_update (self):
+ self.notation.check_update ()
+ if self.notation.touched:
+ self.update_canvas ()
+ elif self.notation.cursor_touched:
self.update_cursor ()
- elif name == 'space':
- self.add_note ()
- elif name == 'apostrophe':
- self.notation.change_octave (1)
- elif name == 'comma':
- self.notation.change_octave (-1)
- elif name == 'BackSpace':
- self.notation.backspace ()
- elif name == 'plus':
- self.notation.change_alteration (2)
- elif name == 'minus':
- self.notation.change_alteration (-2)
- elif name == 'period':
- self.notation.change_dots ()
- elif name == 'slash':
- self.notation.change_duration_log (1)
- elif name == 'asterisk':
- self.notation.change_duration_log (-1)
- elif name == 'p':
- self.notation.print_score()
- elif name == 'q':
- gtk.main_quit()
- elif name == 'r':
- self.notation.ensure_rest ()
- elif len (name) == 1 and name in 'abcdefg':
- step = (ord (name) - ord ('a') + 5) % 7
- self.notation.ensure_note ()
- self.notation.change_step (step)
- else:
- print 'Unknown key %s' % name
- return False
-
- self.notation.ensure_cursor_visible ()
- self.notation.notation_controller.update_notation ()
- self.notation.paint_on_canvas (self.canvas)
- return True
-