changeset 220:13c089886f61

added the forgotten curve.py; mpd song path fix
author drewp@bigasterisk.com
date Sun, 17 Apr 2005 07:06:10 +0000
parents 21b458127924
children 4a51d4eefa95
files bin/ascoltami light9/curve.py light9/showconfig.py
diffstat 3 files changed, 313 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/bin/ascoltami	Sun Apr 17 05:54:39 2005 +0000
+++ b/bin/ascoltami	Sun Apr 17 07:06:10 2005 +0000
@@ -109,6 +109,9 @@
         reactor.callLater(.05, self.pollStatus)
 
     def play(self, song_path):
+        self.mpd.clear()
+        p = showconfig.songInMpd(song_path)
+        self.mpd.add(p)
         self.mpd.play()
 
     def stop(self):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light9/curve.py	Sun Apr 17 07:06:10 2005 +0000
@@ -0,0 +1,297 @@
+from __future__ import division
+import sys,math,glob,random,os
+from bisect import bisect_left,bisect,bisect_right
+import Tkinter as tk
+from dispatch import dispatcher
+
+import run_local
+from light9 import Submaster, dmxclient, networking
+from light9.TLUtility import make_attributes_from_args
+
+class Curve:
+    """curve does not know its name. see Curveset"""
+    points = None # x-sorted list of (x,y)
+    def __init__(self):
+        self.points = [(0,0),(10,0)]
+
+    def load(self,filename):
+        self.points[:]=[]
+        for line in file(filename):
+            self.points.append(tuple([float(a) for a in line.split()]))
+        self.points.sort()
+        dispatcher.send("points changed",sender=self)
+
+    def save(self,filename):
+        if filename.endswith('-music') or filename.endswith('_music'):
+            print "not saving music track"
+            return
+        f = file(filename,'w')
+        for p in self.points:
+            f.write("%s %s\n" % p)
+        f.close()
+
+    def eval(self,t):
+        i = bisect_left(self.points,(t,None))-1
+
+        if self.points[i][0]>t:
+            return self.points[i][1]
+        if i>=len(self.points)-1:
+            return self.points[i][1]
+
+        p1,p2 = self.points[i],self.points[i+1]
+        frac = (t-p1[0])/(p2[0]-p1[0])
+        y = p1[1]+(p2[1]-p1[1])*frac
+        return y
+
+    def insert_pt(self,new_pt):
+        i = bisect(self.points,(new_pt[0],None))
+        self.points.insert(i,new_pt)
+    __call__=eval
+
+class Curveview(tk.Canvas):
+    def __init__(self,master,curve,**kw):
+        self.curve=curve
+        self._time = 0
+        tk.Canvas.__init__(self,master,width=10,height=10,
+                           relief='sunken',bd=1,
+                           closeenough=5,takefocus=1, **kw)
+        self.selected_points=[] # idx of points being dragged
+        self.update()
+        # self.bind("<Enter>",self.focus)
+        dispatcher.connect(self.input_time,"input time")
+        dispatcher.connect(self.update,"zoom changed")
+        dispatcher.connect(self.update,"points changed",sender=self.curve)
+        self.bind("<Configure>",self.update)
+        for x in range(1, 6):
+            def add_kb_marker_point(evt, x=x):
+                print "add_kb_marker_point", evt
+                self.add_point((self.current_time(), (x - 1) / 4.0))
+
+            self.bind("<Key-%s>" % x, add_kb_marker_point)
+
+
+        for butnum,factor in (5, 1.5),(4, 1/1.5):
+            self.bind("<ButtonPress-%s>"%butnum,
+                      lambda ev,factor=factor:
+                      dispatcher.send("zoom about mouse",
+                                      t=self.world_from_screen(ev.x,0)[0],
+                                      factor=factor))
+        self.bind("<Key-Escape>",lambda ev:
+                  dispatcher.send("see time",
+                                  t=self.current_time()))
+    def current_time(self):
+        return self._time
+
+    def screen_from_world(self,p):
+        start,end = self.zoom
+        ht = self.winfo_height()
+        return (p[0]-start)/(end-start)*self.winfo_width(), (ht-5)-p[1]*(ht-10)
+    def world_from_screen(self,x,y):
+        start,end = self.zoom
+        ht = self.winfo_height()
+        return x/self.winfo_width()*(end-start)+start, ((ht-5)-y)/(ht-10)
+    
+    def input_time(self,val):
+        t=val
+        pts = self.screen_from_world((val,0))+self.screen_from_world((val,1))
+        self.delete('timecursor')
+        self.create_line(*pts,**dict(width=2,fill='red',tags=('timecursor',)))
+        self._time = t
+    def update(self,*args):
+
+        self.zoom = dispatcher.send("zoom area")[0][1]
+        cp = self.curve.points
+
+        visible_x = (self.world_from_screen(0,0)[0],
+                     self.world_from_screen(self.winfo_width(),0)[0])
+
+        visleftidx = max(0,bisect_left(cp,(visible_x[0],None))-1)
+        visrightidx = min(len(cp)-1,bisect_left(cp,(visible_x[1],None))+1)
+                             
+        visible_points = cp[visleftidx:visrightidx+1]
+        visible_idxs = range(visleftidx,visrightidx+1)
+        
+        self.delete('curve')
+
+        self._draw_markers(visible_x)
+        
+        self._draw_line(visible_idxs,visible_points)
+        
+        self.dots = {} # idx : canvas rectangle
+
+        if len(visible_points)<50:
+            self._draw_handle_points(visible_idxs,visible_points)
+
+    def _draw_markers(self,visible_x):
+        mark = self._draw_one_marker
+
+        mark(0,"0")
+        t1,t2=visible_x
+        if t2-t1<30:
+            for t in range(int(t1),int(t2)+1):
+                mark(t,str(t))
+        mark(-4,"-4")
+
+        endtimes = dispatcher.send("get max time")
+        if endtimes:
+            endtime = endtimes[0][1]
+            mark(endtime,"end %.1f"%endtime)
+            mark(endtime+10,"post %.1f"%(endtime+10))
+        
+    def _draw_one_marker(self,t,label):
+        x = self.screen_from_world((t,0))[0]
+        self.create_line(x,self.winfo_height(),x,self.winfo_height()-20,
+                         tags=('curve',))
+        self.create_text(x,self.winfo_height()-20,text=label,anchor='s',
+                         tags=('curve',))
+
+
+    def _draw_line(self,visible_idxs,visible_points):
+        linepts=[]
+        step=1
+        linewidth=2
+        if len(visible_points)>800:
+            step = int(len(visible_points)/800)
+            linewidth=1
+        for p in visible_points[::step]:
+            linepts.extend(self.screen_from_world(p))
+        if len(linepts)<4:
+            return
+        line = self.create_line(*linepts,**dict(width=linewidth,tags='curve'))
+
+        # canvas doesnt have keyboard focus, so i can't easily change the
+        # cursor when ctrl is pressed
+        #        def curs(ev):
+        #            print ev.state
+        #        self.bind("<KeyPress>",curs)
+        #        self.bind("<KeyRelease-Control_L>",lambda ev: curs(0))
+        self.tag_bind(line,"<Control-ButtonPress-1>",self.newpointatmouse)
+
+
+    def _draw_handle_points(self,visible_idxs,visible_points):
+        for i,p in zip(visible_idxs,visible_points):
+            rad=3
+            worldp = p
+            p = self.screen_from_world(p)
+            dot = self.create_rectangle(p[0]-rad,p[1]-rad,p[0]+rad,p[1]+rad,
+                                        outline='black',fill='blue',
+                                        tags=('curve','point', 'handle%d' % i))
+            if worldp[1] == 0:
+                rad += 3
+                dot2 = self.create_oval(p[0]-rad,p[1]-rad,
+                                             p[0]+rad,p[1]+rad,
+                                             outline='darkgreen',
+                                             tags=('curve','point', 'handle%d' % i))
+            self.tag_bind('handle%d' % i,"<ButtonPress-1>",
+                          lambda ev,i=i: self.dotpress(ev,i))
+            self.bind("<Motion>",
+                      lambda ev,i=i: self.dotmotion(ev,i))
+            self.bind("<ButtonRelease-1>",
+                      lambda ev,i=i: self.dotrelease(ev,i))
+            self.dots[i]=dot
+
+        self.highlight_selected_dots()
+        
+
+    def newpointatmouse(self, ev):
+        p = self.world_from_screen(ev.x,ev.y)
+        x, y = p
+        y = max(0, y)
+        y = min(1, y)
+        p = x, y
+        self.add_point(p)
+
+    def add_point(self, p):
+        self.unselect()
+        self.curve.insert_pt(p)
+        self.update()
+
+    def highlight_selected_dots(self):
+        for i,d in self.dots.items():
+            if i in self.selected_points:
+                self.itemconfigure(d,fill='red')
+            else:
+                self.itemconfigure(d,fill='blue')
+        
+    def dotpress(self,ev,dotidx):
+        self.selected_points=[dotidx]
+        self.highlight_selected_dots()
+
+    def dotmotion(self,ev,dotidx):
+        cp = self.curve.points
+        moved=0
+        for idx in self.selected_points:
+            x,y = self.world_from_screen(ev.x,ev.y)
+            y = max(0,min(1,y))
+            if idx>0 and x<=cp[idx-1][0]:
+                continue
+            if idx<len(cp)-1 and x>=cp[idx+1][0]:
+                continue
+            moved=1
+            cp[idx] = (x,y)
+        if moved:
+            self.update()
+    def unselect(self):
+        self.selected_points=[]
+        self.highlight_selected_dots()
+        
+    def dotrelease(self,ev,dotidx):
+        self.unselect()
+        
+class Curveset:
+    curves = None # curvename : curve
+    def __init__(self):
+        self.curves = {}
+    def load(self,basename):
+        """find all files that look like basename-curvename and add
+        curves with their contents"""
+        for filename in glob.glob("%s-*"%basename):
+            curvename = filename[filename.find('-')+1:]
+            c=Curve()
+            c.load(filename)
+            curvename = curvename.replace('-','_')
+            self.add_curve(curvename,c)            
+    def save(self,basename):
+        """writes a file for each curve with a name
+        like basename-curvename"""
+        for name,cur in self.curves.items():
+            cur.save("%s-%s" % (basename,name))
+    def add_curve(self,name,curve):
+        self.curves[name] = curve
+        dispatcher.send("add_curve",sender=self,name=name)
+    def globalsdict(self):
+        return self.curves.copy()
+    def new_curve(self,name):
+        if name=="":
+            print "no name given"
+            return
+        while name in self.curves:
+           name=name+"-1"
+
+        self.add_curve(name,Curve())
+
+
+class Curvesetview(tk.Frame):
+    curves = None # curvename : Curveview
+    def __init__(self,master,curveset,**kw):
+        self.curves = {}
+        self.curveset = curveset
+        tk.Frame.__init__(self,master,**kw)
+        
+        f = tk.Frame(self,relief='raised',bd=1)
+        f.pack(side='top',fill='x')
+        tk.Button(f,text="new curve named:",
+                  command=lambda: self.curveset.new_curve(self.newcurvename.get())).pack(side='left')
+        self.newcurvename = tk.StringVar()
+        tk.Entry(f,textvariable=self.newcurvename).pack(side='left',
+                                                        fill='x',exp=1)
+        
+        
+        dispatcher.connect(self.add_curve,"add_curve",sender=self.curveset)
+    def add_curve(self,name):
+        f = tk.Frame(self,relief='raised',bd=1)
+        f.pack(side='top',fill='both',exp=1)
+        tk.Label(f,text="curve %r"%name,width=15).pack(side='left')
+        cv = Curveview(f,self.curveset.curves[name])
+        cv.pack(side='right',fill='both',exp=1)
+        self.curves[name] = cv
--- a/light9/showconfig.py	Sun Apr 17 05:54:39 2005 +0000
+++ b/light9/showconfig.py	Sun Apr 17 07:06:10 2005 +0000
@@ -3,12 +3,24 @@
 def root():
     r = getenv("LIGHT9_SHOW")
     if r is None:
-        raise OSError("LIGHT9_SHOW env variable has not been set to the show root")
+        raise OSError(
+            "LIGHT9_SHOW env variable has not been set to the show root")
     return r
 
 def musicDir():
     return path.join(root(),"music")
 
+def songInMpd(song):
+
+    """mpd only works off its own musicroot, which for me is
+    /my/music. song is a file in musicDir; this function returns a
+    version starting with the mpd path, but minus the mpd root itself.
+    the mpc ~/.mpdconf """
+    
+    if 'dance2005' in root():
+        return "projects/dance2005/%s" % song
+    raise NotImplementedError
+
 def curvesDir():
     return path.join(root(),"curves")