Files @ 2072a0dd7b19
Branch filter:

Location: light9/bin/curvecalc - annotation

drewp@bigasterisk.com
factor out LIGHT9_SHOW
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
2072a0dd7b19
f41004d5a507
f41004d5a507
9b360ee8636e
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
9b360ee8636e
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
a65a1634fce4
a65a1634fce4
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
a65a1634fce4
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
2072a0dd7b19
f41004d5a507
f41004d5a507
f41004d5a507
2072a0dd7b19
f41004d5a507
f41004d5a507
f41004d5a507
2072a0dd7b19
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
2072a0dd7b19
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
f41004d5a507
#!/usr/bin/python

"""
todo: curveview should preserve more objects, for speed maybe

"""
from __future__ import division
import xmlrpclib,time,socket,sys,textwrap,math,glob,random,os
from bisect import bisect_left,bisect,bisect_right
import Tkinter as tk
from dispatch import dispatcher
from twisted.internet import reactor,tksupport
import twisted
from twisted.web.xmlrpc import Proxy

import run_local
from light9 import Submaster, dmxclient, networking, showconfig
from light9.TLUtility import make_attributes_from_args
from light9.zoomcontrol import Zoomcontrol
from light9.curve import Curve, Curveview, Curveset, Curvesetview

sys.path.append("../../semprini")
from lengther import wavelength # for measuring duration of .wav

class Music:
    def __init__(self):
        self.player=None # xmlrpc Proxy to player
        self.recenttime=0
        
    def current_time(self):
        """return deferred which gets called with the current time"""
        if self.player is None:
            self.player = Proxy(networking.musicUrl())
#            d = self.player.callRemote("songlength")
#            d.addCallback(lambda l: dispatcher.send("max time",maxtime=l))
#            d = self.player.callRemote("songname")
#            d.addCallback(lambda n: dispatcher.send("songname",name=n))
        d = self.player.callRemote('gettime')
        def sendtime(t):
            dispatcher.send("input time",val=t)
            return t # pass along to the real receiver
        def error(e):
            pass#self.player=None
        d.addCallback(sendtime)
        return d
        
class Subexpr:
    curveset = None
    def __init__(self,curveset,expr=""):
        self.curveset = curveset
        self.lasteval = None
        self.expr=expr
        self._smooth_random_items = [random.random() for x in range(100)]
    def eval(self,t):
        if self.expr=="":
            dispatcher.send("expr_error",sender=self,exc="no expr, using 0")
            return 0
        glo = self.curveset.globalsdict()
        glo['t'] = t

        glo['nsin'] = lambda x: (math.sin(x * (2 * math.pi)) + 1) / 2
        glo['ncos'] = lambda x: (math.cos(x * (2 * math.pi)) + 1) / 2
        glo['within'] = lambda a, b: a < t < b
        glo['bef'] = lambda x: t < x
        glo['aft'] = lambda x: x < t
        glo['smoove'] = lambda x: -2 * (x ** 3) + 3 * (x ** 2)

        def smooth_random(speed=1):
            """1 = new stuff each second, <1 is slower, fade-ier"""
            x = (t * speed) % len(self._smooth_random_items)
            x1 = int(x)
            x2 = (int(x) + 1) % len(self._smooth_random_items)
            y1 = self._smooth_random_items[x1]
            y2 = self._smooth_random_items[x2]
            return y1 + (y2 - y1) * ((x - x1))

        def notch_random(speed=1):
            """1 = new stuff each second, <1 is slower, notch-ier"""
            x = (t * speed) % len(self._smooth_random_items)
            x1 = int(x)
            y1 = self._smooth_random_items[x1]
            return y1
            
        glo['noise'] = smooth_random
        glo['notch'] = notch_random

        try:
            self.lasteval = eval(self.expr,glo)
        except Exception,e:
            dispatcher.send("expr_error",sender=self,exc=e)
        else:
            dispatcher.send("expr_error",sender=self,exc="ok")
        return self.lasteval

    def expr():
        doc = "python expression for level as a function of t, using curves"
        def fget(self):
            return self._expr
        def fset(self, value):
            self._expr = value
            dispatcher("expr_changed",sender=self)
        return locals()
    expr = property(**expr())

class Subexprview(tk.Frame):
    def __init__(self,master,se,**kw):
        self.subexpr=se
        tk.Frame.__init__(self,master,**kw)
        self.evar = tk.StringVar()
        e = self.ent = tk.Entry(self,textvariable=self.evar)
        e.pack(side='left',fill='x',exp=1)
        self.expr_changed()
        self.evar.trace_variable('w',self.evar_changed)
        dispatcher.connect(self.expr_changed,"expr_changed",
                           sender=self.subexpr)
        self.error = tk.Label(self)
        self.error.pack(side='left')
        dispatcher.connect(lambda exc: self.error.config(text=str(exc)),
                           "expr_error",sender=self.subexpr,weak=0)
    def expr_changed(self):
        if self.subexpr.expr!=self.evar.get():
            self.evar.set(self.subexpr.expr)
    def evar_changed(self,*args):
        self.subexpr.expr = self.evar.get()

class Subterm:
    """one Submaster and its Subexpr"""
    def __init__(self,submaster,subexpr):
        make_attributes_from_args('submaster','subexpr')
    def scaled(self,t):
        return self.submaster * self.subexpr.eval(t)

class Subtermview(tk.Frame):
    def __init__(self,master,st,**kw):
        self.subterm = st
        tk.Frame.__init__(self,master,bd=1,relief='raised',**kw)
        l = tk.Label(self,text="sub %r" % self.subterm.submaster.name)
        l.pack(side='left')
        sev=Subexprview(self,self.subterm.subexpr)
        sev.pack(side='left',fill='both',exp=1)

class Output:
    lastsendtime=0
    lastsendlevs=None
    def __init__(self,subterms):
        make_attributes_from_args('subterms')
    def send_dmx(self,t):

        scaledsubs=[]
        for st in self.subterms:
            scl = st.scaled(t)
            scaledsubs.append(scl)
        out = Submaster.sub_maxes(*scaledsubs)
        levs = out.get_levels()
        now=time.time()
        if now-self.lastsendtime>5 or levs!=self.lastsendlevs:
            dispatcher.send("output levels",val=levs)
            dmxclient.outputlevels(out.get_dmx_list(),
                                   twisted=1,clientid='curvecalc')
            self.lastsendtime = now
            self.lastsendlevs = levs

def create_status_lines(master):
    for signame,textfilter in [
        ('input time',lambda t: "%.2fs"%t),
        ('output levels',
         lambda levels: textwrap.fill("; ".join(["%s:%.2f"%(n,v)
                                                 for n,v in
                                                 levels.items()[:5]
                                                 if v>0]),70)),
        ('update period',lambda t: "%.1fms"%(t*1000)),
        ]:
        l = tk.Label(master,anchor='w',justify='left')
        l.pack(side='top',fill='x')
        dispatcher.connect(lambda val,l=l,sn=signame,tf=textfilter:
                           l.config(text=sn+": "+tf(val)),
                           signame,weak=0)

def savesubterms(filename,subterms):
    s=""
    for st in subterms:
        s=s+"%s %s\n" % (st.submaster.name,st.subexpr.expr)
    
    file(filename,'w').write(s)

class SubtermSetView(tk.Frame):
    def __init__(self, master, *args, **kw):
        tk.Frame.__init__(self, master, *args, **kw)
        self.cur_row = 0
        self.cur_col = 0
        self.ncols = 2
    def add_subtermview(self, stv):
        stv.grid(row=self.cur_row, column=self.cur_col, sticky='news')
        self.columnconfigure(self.cur_col, weight=1)

        self.cur_col += 1
        self.cur_col %= self.ncols
        if self.cur_col == 0:
            self.cur_row += 1

def add_one_subterm(subname, curveset, subterms, root, ssv, expr=''):
    term = Subterm(Submaster.Submaster(subname), Subexpr(curveset,expr))
    subterms.append(term)

    stv=Subtermview(ssv,term)
    # stv.pack(side='top',fill='x')

    ssv.add_subtermview(stv)

    return term

def subterm_adder(master, curveset, subterms, root, ssv):
    f=tk.Frame(master,relief='raised',bd=1)
    newname = tk.StringVar()

    def add_cmd():
        add_one_subterm(newname.get(), curveset, subterms, root, ssv, '')

    tk.Button(f,text="new subterm named:", command=add_cmd).pack(side='left')
    tk.Entry(f,textvariable=newname).pack(side='left',fill='x',exp=1)
    return f
    
#######################################################################
root=tk.Tk()
root.tk_setPalette("gray50")
root.wm_geometry("1120x850")
root.tk_focusFollowsMouse()

music=Music()

zc = Zoomcontrol(root)
zc.pack(side='top',fill='x')

curveset = Curveset()
csv = Curvesetview(root,curveset)
csv.pack(side='top',fill='both',exp=1)

ssv = SubtermSetView(root)
ssv.pack(side='top', fill='x')

song = sys.argv[1]
root.title("Curemaster 2000MX - %s" % song)

musicfilename = showconfig.songFilename(song)
maxtime = wavelength(musicfilename)
dispatcher.send("max time",maxtime=maxtime)
dispatcher.connect(lambda: maxtime, "get max time",weak=0)
curveset.load(basename=os.path.join(showconfig.curvesDir(),song))

subterms = []
subterm_adder(root, curveset, subterms, root, ssv).pack(side='top',fill='x')
for line in file(showconfig.subtermsForSong()):
    subname,expr = line.strip().split(" ",1)

    term = add_one_subterm(subname, curveset, subterms, root, ssv, expr)
    
    # stv=Subtermview(root,term)
    # stv.pack(side='top',fill='x')

out = Output(subterms)

def savekey(*args):
    print "saving",song
    savesubterms(showconfig.subtermsForSong(song),subterms)
    curveset.save(basename="curves/"+song)
    print "saved"

    
root.bind("<Control-Key-s>",savekey)

create_status_lines(root)
    
recent_t=[]
later = None
def update():
    global later
    d = music.current_time()
    d.addCallback(update2)
    d.addErrback(updateerr)
def updateerr(e):
    global later
    print "err",e
    if later and not later.cancelled and not later.called: later.cancel()
    later = reactor.callLater(1,update)
def update2(t):
    global recent_t,later

    if later and not later.cancelled and not later.called: later.cancel()
    later = reactor.callLater(.01,update)

    recent_t = recent_t[-50:]+[t]
    period = (recent_t[-1]-recent_t[0])/len(recent_t)
    dispatcher.send("update period",val=period)
    out.send_dmx(t)
update()

#def logprint(msg):
#    print "log",msg
#twisted.python.log.addObserver(logprint)

root.bind("<Control-Key-q>",lambda ev: reactor.stop)
root.bind("<Destroy>",lambda ev: reactor.stop)
root.protocol('WM_DELETE_WINDOW', reactor.stop)
tksupport.install(root,ms=10)
if 0:
    sys.path.append("/home/drewp/projects/editor/pour")
    from utils import runstats
    runstats("reactor.run()")
else:
    reactor.run()