Changeset - 5bcb950024af
bin/bumppad
Show inline comments
 
@@ -34,49 +34,50 @@ class pad(tk.Frame):
 

	
 
            sub = Submaster(subname)
 
            self.levs[sub] = 0
 

	
 
            l = tk.Label(self,
 
                         font="arial 12 bold",
 
                         anchor='w',
 
                         height=2,
 
                         relief='groove',
 
                         bd=5,
 
                         text="%s\n%s" % (key.replace('KP_', ''), sub.name))
 
            l.grid(column=xy[0], row=xy[1], sticky='news')
 

	
 
            root.bind(
 
                "<KeyPress-%s>" % key, lambda ev, sub=sub: self.bumpto(sub, 1))
 
            root.bind("<KeyRelease-%s>" % key,
 
                      lambda ev, sub=sub: self.bumpto(sub, 0))
 

	
 
    def bumpto(self, sub, lev):
 
        now = time.time()
 
        self.levs[sub] = lev * self.mag.get()
 
        self.master.after_idle(self.output)
 

	
 
    def output(self):
 
        dmx = sub_maxes(*[s * l for s, l in list(self.levs.items())]).get_dmx_list()
 
        dmx = sub_maxes(*[s * l
 
                          for s, l in list(self.levs.items())]).get_dmx_list()
 
        dmxclient.outputlevels(dmx, clientid="bumppad")
 

	
 

	
 
root = tk.Tk()
 
root.tk_setPalette("maroon4")
 
root.wm_title("bumppad")
 
mag = tk.DoubleVar()
 

	
 
tk.Label(root,
 
         text="Keypad press/release activate sub; 1..5 set mag",
 
         font="Helvetica -12 italic",
 
         anchor='w').pack(side='bottom', fill='x')
 

	
 
pad(root, root, mag).pack(side='left', fill='both', exp=1)
 

	
 
magscl = tk.Scale(root,
 
                  orient='vertical',
 
                  from_=1,
 
                  to=0,
 
                  res=.01,
 
                  showval=1,
 
                  variable=mag,
 
                  label='mag',
 
                  relief='raised',
bin/collector
Show inline comments
 
#!bin/python
 
"""
 
Collector receives device attrs from multiple senders, combines
 
them, and sends output attrs to hardware. The combining part has
 
custom code for some attributes.
 

	
 
Input can be over http or zmq.
 
"""
 

	
 

	
 
from run_local import log
 

	
 
from rdflib import URIRef, Literal
 
from twisted.internet import reactor, utils
 
from txzmq import ZmqEndpoint, ZmqFactory, ZmqPullConnection
 
import json
 
import logging
 
import optparse
 
import time
 
import traceback
 
import cyclone.web, cyclone.websocket
 
from greplin import scales
 

	
 
from lib.cycloneerr import PrettyErrorHandler
 
from light9.collector.output import EnttecDmx, Udmx, DummyOutput
 
from light9.collector.collector import Collector
 
from light9.namespaces import L9
 
from light9 import networking
 
from rdfdb.syncedgraph import SyncedGraph
 
from light9.greplin_cyclone import StatsForCyclone
 

	
 

	
 
def parseJsonMessage(msg):
 
    body = json.loads(msg)
bin/curvecalc
Show inline comments
 
#!bin/python
 
"""
 
now launches like this:
 
% bin/curvecalc http://light9.bigasterisk.com/show/dance2007/song1
 

	
 

	
 

	
 
todo: curveview should preserve more objects, for speed maybe
 

	
 
"""
 

	
 

	
 
import sys
 
import imp
 
sys.path.append('/usr/lib/python2.7/dist-packages')  # For gtk
 
from twisted.internet import gtk3reactor
 
gtk3reactor.install()
 
from twisted.internet import reactor
 

	
 
import time, textwrap, os, optparse, linecache, signal, traceback, json
 
import gi
 
from gi.repository import Gtk
 
from gi.repository import GObject
 
from gi.repository import Gdk
 

	
 
from urllib.parse import parse_qsl
 
import louie as dispatcher
 
from rdflib import URIRef, Literal, RDF, RDFS
 
import logging
 

	
 
from run_local import log
 
from light9 import showconfig, networking
 
from light9.curvecalc import curveview
 
from light9.curvecalc.curve import Curveset
 
from light9.curvecalc.curveedit import serveCurveEdit
 
from light9.curvecalc.musicaccess import Music
 
@@ -392,49 +391,50 @@ class Main(object):
 
        dispatcher.send("see time until end")
 

	
 
    def onZoomAll(self, item):
 
        dispatcher.send("show all")
 

	
 
    def onPlayPause(self, item):
 
        # since the X coord in a curveview affects the handling, one
 
        # of them may be able to pick this up
 
        results = dispatcher.send("onPlayPause")
 
        times = [t for listener, t in results if t is not None]
 
        self.music.playOrPause(t=times[0] if times else None)
 

	
 
    def onSave(self, *args):
 
        # only doing curves still. I hope to eliminate all this.
 
        log.info("saving curves")
 
        self.curveset.save()
 
        log.info("saved")
 

	
 
    def makeStatusLines(self, master):
 
        """various labels that listen for dispatcher signals"""
 
        for row, (signame, textfilter) in enumerate([
 
            ('input time', lambda t: "%.2fs" % t),
 
            ('output levels', lambda levels: textwrap.fill(
 
                "; ".join([
 
                    "%s:%.2f" % (n, v) for n, v in list(levels.items())[:2] if v > 0
 
                    "%s:%.2f" % (n, v) for n, v in list(levels.items())[:2] if v
 
                    > 0
 
                ]), 70)),
 
            ('update period', lambda t: "%.1fms" % (t * 1000)),
 
            ('update status', lambda x: str(x)),
 
        ]):
 
            key = Gtk.Label("%s:" % signame)
 
            value = Gtk.Label("")
 
            master.resize(row + 1, 2)
 
            master.attach(key, 0, 1, row, row + 1)
 
            master.attach(value, 1, 2, row, row + 1)
 
            key.set_alignment(1, 0)
 
            value.set_alignment(0, 0)
 

	
 
            dispatcher.connect(lambda val, value=value, tf=textfilter: value.
 
                               set_text(tf(val)),
 
                               signame,
 
                               weak=False)
 
        dispatcher.connect(lambda val: setattr(self, 'lastSeenInputTime', val),
 
                           'input time',
 
                           weak=False)
 
        master.show_all()
 

	
 
    def refreshCurveView(self):
 
        wtree = self.wtree
 
        mtimes = [
bin/dmxserver
Show inline comments
 
@@ -3,49 +3,48 @@
 
Replaced by bin/collector
 

	
 

	
 
this is the only process to talk to the dmx hardware. other clients
 
can connect to this server and present dmx output, and this server
 
will max ('pile-on') all the client requests.
 

	
 
this server has a level display which is the final set of values that
 
goes to the hardware.
 

	
 
clients shall connect to the xmlrpc server and send:
 

	
 
  their PID (or some other cookie)
 

	
 
  a length-n list of 0..1 levels which will represent the channel
 
    values for the n first dmx channels.
 

	
 
server is port 8030; xmlrpc method is called outputlevels(pid,levellist).
 

	
 
todo:
 
  save dmx on quit and restore on restart
 
  if parport fails, run in dummy mode (and make an option for that too)
 
"""
 

	
 

	
 
from twisted.internet import reactor
 
from twisted.web import xmlrpc, server
 
import sys, time, os
 
from optparse import OptionParser
 
import run_local
 
import txosc.dispatch, txosc. async
 
from light9.io import ParportDMX, UsbDMX
 

	
 
from light9.updatefreq import Updatefreq
 
from light9 import networking
 

	
 
from txzmq import ZmqEndpoint, ZmqFactory, ZmqPullConnection, ZmqRequestTimeoutError
 
import json
 

	
 

	
 
def startZmq(port, outputlevels):
 
    zf = ZmqFactory()
 
    e = ZmqEndpoint('bind', 'tcp://*:%s' % port)
 
    s = ZmqPullConnection(zf, e)
 

	
 
    def onPull(message):
 
        msg = json.loads(message[0])
 
        outputlevels(msg['clientid'], msg['levellist'])
 

	
 
@@ -106,49 +105,49 @@ class XMLRPCServe(xmlrpc.XMLRPC):
 

	
 
        self.updatefreq = Updatefreq()  # freq of actual dmx sends
 
        self.num_unshown_updates = None
 
        self.lastshownlevels = None
 
        # start the loop
 
        self.sendlevels()
 

	
 
        # the other loop
 
        self.purgeclients()
 

	
 
    def purgeclients(self):
 
        """forget about any clients who haven't sent levels in a while.
 
        this runs in a loop"""
 

	
 
        purge_age = 10  # seconds
 

	
 
        reactor.callLater(1, self.purgeclients)
 

	
 
        now = time.time()
 
        cids = list(self.lastseen.keys())
 
        for cid in cids:
 
            lastseen = self.lastseen[cid]
 
            if lastseen < now - purge_age:
 
                print(("forgetting client %s (no activity for %s sec)" %
 
                      (cid, purge_age)))
 
                       (cid, purge_age)))
 
                try:
 
                    del self.clientlevels[cid]
 
                except KeyError:
 
                    pass
 
                del self.clientfreq[cid]
 
                del self.lastseen[cid]
 

	
 
    def sendlevels(self):
 
        """sends to dmx if levels have changed, or if we havent sent
 
        in a while"""
 

	
 
        reactor.callLater(self.calldelay, self.sendlevels)
 

	
 
        if self.clientschanged:
 
            # recalc levels
 

	
 
            self.calclevels()
 

	
 
            if (self.num_unshown_updates is None or  # first time
 
                    self.options.fast_updates or  # show always
 
                (
 
                    self.combinedlevels != self.lastshownlevels and  # changed
 
                    self.num_unshown_updates > 5)):  # not too frequent
 
                self.num_unshown_updates = 0
 
@@ -163,50 +162,50 @@ class XMLRPCServe(xmlrpc.XMLRPC):
 

	
 
        # used to be a fixed 1 in here, for the max delay between
 
        # calls, instead of calldelay
 
        if self.clientschanged or time.time(
 
        ) > self.lastupdate + self.calldelay:
 
            self.lastupdate = time.time()
 
            self.sendlevels_dmx()
 

	
 
        self.clientschanged = 0  # clear the flag
 

	
 
    def calclevels(self):
 
        """combine all the known client levels into self.combinedlevels"""
 
        self.combinedlevels = []
 
        for chan in range(0, self.parportdmx.dimmers):
 
            x = 0
 
            for clientlist in list(self.clientlevels.values()):
 
                if len(clientlist) > chan:
 
                    # clamp client levels to 0..1
 
                    cl = max(0, min(1, clientlist[chan]))
 
                    x = max(x, cl)
 
            self.combinedlevels.append(x)
 

	
 
    def printlevels(self):
 
        """write all the levels to stdout"""
 
        print("Levels:", "".join(
 
            ["% 2d " % (x * 100) for x in self.combinedlevels]))
 
        print("Levels:",
 
              "".join(["% 2d " % (x * 100) for x in self.combinedlevels]))
 

	
 
    def printstats(self):
 
        """print the clock, freq, etc, with a \r at the end"""
 

	
 
        sys.stdout.write("dmxserver up at %s, [polls %s] " % (
 
            time.strftime("%H:%M:%S"),
 
            str(self.updatefreq),
 
        ))
 
        for cid, freq in list(self.clientfreq.items()):
 
            sys.stdout.write("[%s %s] " % (cid, str(freq)))
 
        sys.stdout.write("\r")
 
        sys.stdout.flush()
 

	
 
    def sendlevels_dmx(self):
 
        """output self.combinedlevels to dmx, and keep the updates/sec stats"""
 
        # they'll get divided by 100
 
        if self.parportdmx:
 
            self.parportdmx.sendlevels([l * 100 for l in self.combinedlevels])
 
        self.updatefreq.update()
 

	
 
    def xmlrpc_echo(self, x):
 
        return x
 

	
 
    def xmlrpc_outputlevels(self, cid, levellist):
bin/effecteval
Show inline comments
 
#!bin/python
 

	
 

	
 
from run_local import log
 
from twisted.internet import reactor
 
from twisted.internet.defer import inlineCallbacks, returnValue
 
import cyclone.web, cyclone.websocket, cyclone.httpclient
 
import sys, optparse, logging, subprocess, json, itertools
 
from rdflib import URIRef, Literal
 

	
 
sys.path.append('/usr/lib/pymodules/python2.7/')  # for numpy, on rpi
 
sys.path.append('/usr/lib/python2.7/dist-packages')  # For numpy
 
from light9 import networking, showconfig
 
from light9.effecteval.effect import EffectNode
 
from light9.effect.edit import getMusicStatus, songNotePatch
 
from light9.effecteval.effectloop import makeEffectLoop
 
from light9.greplin_cyclone import StatsForCyclone
 
from light9.namespaces import L9
 
from rdfdb.patch import Patch
 
from rdfdb.syncedgraph import SyncedGraph
 
from greplin import scales
 

	
 
from lib.cycloneerr import PrettyErrorHandler
 

	
 

	
 
class EffectEdit(PrettyErrorHandler, cyclone.web.RequestHandler):
 

	
bin/keyboardcomposer
Show inline comments
 
#!bin/python
 

	
 

	
 
from run_local import log
 
import cgi, time, logging
 
from optparse import OptionParser
 
import webcolors, colorsys
 
from louie import dispatcher
 
from twisted.internet import reactor, tksupport
 
from twisted.web import resource
 
from rdflib import URIRef, Literal
 
import tkinter.tix as tk
 

	
 
from light9.Fadable import Fadable
 
from light9.subclient import SubClient
 
from light9 import showconfig, networking, prof
 
from light9.uihelpers import toplevelat
 
from light9.namespaces import L9, RDF, RDFS
 
from light9.tkdnd import initTkdnd, dragSourceRegister, dropTargetRegister
 
from light9 import clientsession
 
from rdfdb.syncedgraph import SyncedGraph
 
from light9.effect.sequencer import CodeWatcher
 
import light9.effect.effecteval
 
from light9.effect.settings import DeviceSettings
 
from rdfdb.patch import Patch
 
from light9.effect.simple_outputs import SimpleOutputs
 

	
 
@@ -480,51 +479,50 @@ class KeyboardComposer(tk.Frame, SubClie
 

	
 
        row.pack(expand=1, fill=tk.BOTH)
 
        self.setup_key_nudgers(row)
 
        self.rows.append(row)
 
        return row
 

	
 
    def change_group(self, sub, row):
 
        """update this sub's group, and maybe other sub groups as needed, so
 
        this sub displays in this row"""
 
        group = row.subGroup
 
        self.graph.patchObject(context=self.session,
 
                               subject=sub,
 
                               predicate=L9['group'],
 
                               newObject=group)
 

	
 
    def highlight_row(self, row):
 
        row = self.rows[row]
 
        row['bg'] = 'red'
 

	
 
    def unhighlight_row(self, row):
 
        row = self.rows[row]
 
        row['bg'] = 'black'
 

	
 
    def get_levels(self):
 
        return dict([
 
            (uri, box.slider_var.get()) for uri, box in list(self.subbox.items())
 
        ])
 
        return dict([(uri, box.slider_var.get())
 
                     for uri, box in list(self.subbox.items())])
 

	
 
    def get_output_settings(self, _graph=None):
 
        _graph = _graph or self.graph
 
        outputSettings = []
 
        for setting in _graph.objects(self.session, L9['subSetting']):
 
            effect = _graph.value(setting, L9['sub'])
 
            strength = _graph.value(setting, L9['level'])
 
            if strength:
 
                now = time.time()
 
                out, report = self.effectEval[effect].outputFromEffect(
 
                    [(L9['strength'], strength)],
 
                    songTime=now,
 
                    # should be counting from when you bumped up from 0
 
                    noteTime=now)
 
                outputSettings.append(out)
 

	
 
        return DeviceSettings.fromList(_graph, outputSettings)
 

	
 
    def save_current_stage(self, subname):
 
        log.info("saving current levels as %s", subname)
 
        with self.graph.currentState() as g:
 
            ds = self.get_output_settings(_graph=g)
 
        effect = L9['effect/%s' % subname]
 
        ctx = URIRef(showconfig.showUri() + '/effect/' + subname)
bin/lightsim
Show inline comments
 
#!bin/python
 

	
 

	
 
import run_local
 
import sys, logging
 

	
 
sys.path.append("lib")
 
import qt4reactor
 
qt4reactor.install()
 

	
 
from twisted.internet import reactor
 
from twisted.internet.task import LoopingCall
 
from twisted.web.xmlrpc import Proxy
 
from louie import dispatcher
 
from PyQt4.QtGui import QWidget, QLabel, QVBoxLayout, QHBoxLayout, QMainWindow
 
from OpenGL.GL import *
 
from OpenGL.GLU import *
 

	
 
from light9 import networking, Patch, showconfig, dmxclient, updatefreq, prof
 
from light9.namespaces import L9
 
from lightsim.openglsim import Surface
 

	
 
log = logging.getLogger()
 
logging.basicConfig(
 
    format=
 
    "%(asctime)s %(levelname)-5s %(name)s %(filename)s:%(lineno)d: %(message)s")
 
log.setLevel(logging.DEBUG)
bin/paintserver
Show inline comments
 
#!bin/python
 

	
 

	
 
from run_local import log
 
import json
 
from twisted.internet import reactor
 
from light9.greplin_cyclone import StatsForCyclone
 
from rdfdb.syncedgraph import SyncedGraph
 
from light9 import networking, showconfig
 
from greplin import scales
 
import optparse, sys, logging
 
import cyclone.web
 
from rdflib import URIRef
 
from light9 import clientsession
 
import light9.paint.solve
 
from lib.cycloneerr import PrettyErrorHandler
 
from light9.namespaces import RDF, L9, DEV
 
import imp
 

	
 

	
 
class Solve(PrettyErrorHandler, cyclone.web.RequestHandler):
 

	
 
    def post(self):
 
        painting = json.loads(self.request.body)
 
        with self.settings.stats.solve.time():
 
            img = self.settings.solver.draw(painting)
 
            sample, sampleDist = self.settings.solver.bestMatch(
bin/subcomposer
Show inline comments
 
#!bin/python
 
"""
 
subcomposer
 
  session
 
  observable(currentSub), a Submaster which tracks the graph
 

	
 
    EditChoice widget
 
      can change currentSub to another sub
 

	
 
    Levelbox widget
 
      watch observable(currentSub) for a new sub, and also watch currentSub for edits to push to the OneLevel widgets
 

	
 
        OneLevel widget
 
          UI edits are caught here and go all the way back to currentSub
 

	
 

	
 
"""
 

	
 

	
 
from run_local import log
 
import time, logging
 

	
 
log.setLevel(logging.DEBUG)
 

	
 
from optparse import OptionParser
 
import logging, urllib.request, urllib.parse, urllib.error
 
import tkinter as tk
 
import louie as dispatcher
 
from twisted.internet import reactor, tksupport, task
 
from rdflib import URIRef, RDF, RDFS, Literal
 

	
 
from light9.dmxchanedit import Levelbox
 
from light9 import dmxclient, Submaster, prof, showconfig, networking
 
from light9.Patch import get_channel_name
 
from light9.uihelpers import toplevelat
 
from rdfdb.syncedgraph import SyncedGraph
 
from light9 import clientsession
 
from light9.tkdnd import initTkdnd
 
from light9.namespaces import L9
 
from rdfdb.patch import Patch
 
from light9.observable import Observable
 
from light9.editchoice import EditChoice, Local
 
from light9.subcomposer import subcomposerweb
 
@@ -140,50 +139,50 @@ class Subcomposer(tk.Frame):
 
                      (newUri, RDF.type, L9['LocalSubmaster'], newUri),
 
                  ]))
 
        self.graph.patchObject(self.session, self.session, L9['currentSub'],
 
                               newUri)
 

	
 
    def relocateSub(self, newUri, newName):
 
        # maybe this goes in Submaster
 
        uri = self.currentSub().uri
 

	
 
        def repl(u):
 
            if u == uri:
 
                return newUri
 
            return u
 

	
 
        delQuads = self.currentSub().allQuads()
 
        addQuads = [(repl(s), p, repl(o), newUri) for s, p, o, c in delQuads]
 
        # patch can't span contexts yet
 
        self.graph.patch(Patch(addQuads=addQuads, delQuads=[]))
 
        self.graph.patch(Patch(addQuads=[], delQuads=delQuads))
 

	
 
    def setupSubChoiceLinks(self):
 
        graph = self.graph
 

	
 
        def ann():
 
            print("currently: session=%s currentSub=%r _currentChoice=%r" % (
 
                self.session, self.currentSub(), self._currentChoice()))
 
            print("currently: session=%s currentSub=%r _currentChoice=%r" %
 
                  (self.session, self.currentSub(), self._currentChoice()))
 

	
 
        @graph.addHandler
 
        def graphChanged():
 
            # some bug where SC is making tons of graph edits and many
 
            # are failing. this calms things down.
 
            log.warn('skip graphChanged')
 
            return
 

	
 
            s = graph.value(self.session, L9['currentSub'])
 
            log.debug('HANDLER getting session currentSub from graph: %s', s)
 
            if s is None:
 
                s = self.switchToLocal()
 
            self.currentSub(Submaster.PersistentSubmaster(graph, s))
 

	
 
        @self.currentSub.subscribe
 
        def subChanged(newSub):
 
            log.debug('HANDLER currentSub changed to %s', newSub)
 
            if newSub is None:
 
                graph.patchObject(self.session, self.session, L9['currentSub'],
 
                                  None)
 
                return
 
            self.sendupdate()
 
            graph.patchObject(self.session, self.session, L9['currentSub'],
 
                              newSub.uri)
bin/tracker
Show inline comments
 
#!/usr/bin/python
 

	
 

	
 
import sys
 
sys.path.append("../../editor/pour")
 
sys.path.append("../light8")
 

	
 
from Submaster import Submaster
 
from skim.zooming import Zooming, Pair
 
from math import sqrt, sin, cos
 
from pygame.rect import Rect
 
from xmlnodebase import xmlnodeclass, collectiveelement, xmldocfile
 
from dispatch import dispatcher
 

	
 
import dmxclient
 

	
 
import tkinter as tk
 

	
 
defaultfont = "arial 8"
 

	
 

	
 
def pairdist(pair1, pair2):
 
    return pair1.dist(pair2)
 

	
 

	
 
def canvashighlighter(canvas, obj, attribute, normalval, highlightval):
 
    """creates bindings on a canvas obj that make attribute go
light9/collector/collector.py
Show inline comments
 

	
 
import time
 
import logging
 
from rdflib import Literal
 
from light9.namespaces import L9, RDF
 
from light9.collector.output import setListElem
 
from light9.collector.device import toOutputAttrs, resolve
 

	
 
# types only
 
from rdflib import Graph, URIRef
 
from typing import List, Dict, Tuple, Any, TypeVar, Generic
 
from light9.collector.output import Output
 

	
 
ClientType = TypeVar('ClientType')
 
ClientSessionType = TypeVar('ClientSessionType')
 

	
 
log = logging.getLogger('collector')
 

	
 

	
 
def outputMap(graph, outputs):
 
    # type: (Graph, List[Output]) -> Dict[Tuple[URIRef, URIRef], Tuple[Output, int]]
 
    """From rdf config graph, compute a map of
 
       (device, outputattr) : (output, index)
 
    that explains which output index to set for any device update.
 
    """
light9/collector/collector_client.py
Show inline comments
 

	
 
from light9 import networking
 
from light9.effect.settings import DeviceSettings
 
from twisted.internet import defer
 
from txzmq import ZmqEndpoint, ZmqFactory, ZmqPushConnection
 
import json, time, logging
 
import treq
 

	
 
log = logging.getLogger('coll_client')
 

	
 
_zmqClient = None
 

	
 

	
 
class TwistedZmqClient(object):
 

	
 
    def __init__(self, service):
 
        zf = ZmqFactory()
 
        e = ZmqEndpoint('connect', 'tcp://%s:%s' % (service.host, service.port))
 
        self.conn = ZmqPushConnection(zf, e)
 

	
 
    def send(self, msg):
 
        self.conn.push(msg)
 

	
 

	
 
def toCollectorJson(client, session, settings):
light9/collector/device.py
Show inline comments
 

	
 
import logging
 
import math
 
from light9.namespaces import L9, RDF, DEV
 
from rdflib import Literal
 
from webcolors import hex_to_rgb, rgb_to_hex
 
from colormath.color_objects import sRGBColor, CMYColor
 
import colormath.color_conversions
 

	
 
log = logging.getLogger('device')
 

	
 

	
 
class Device(object):
 
    pass
 

	
 

	
 
class ChauvetColorStrip(Device):
 
    """
 
     device attrs:
 
       color
 
    """
 

	
 

	
 
class Mini15(Device):
 
    """
light9/collector/output.py
Show inline comments
 

	
 
from rdflib import URIRef
 
import sys
 
import time
 
import usb.core
 
import logging
 
from twisted.internet import task, threads, reactor
 
from greplin import scales
 
log = logging.getLogger('output')
 

	
 

	
 
# eliminate this: lists are always padded now
 
def setListElem(outList, index, value, fill=0, combine=lambda old, new: new):
 
    if len(outList) < index:
 
        outList.extend([fill] * (index - len(outList)))
 
    if len(outList) <= index:
 
        outList.append(value)
 
    else:
 
        outList[index] = combine(outList[index], value)
 

	
 

	
 
class Output(object):
 
    """
 
    send an array of values to some output device. Call update as
 
    often as you want- the result will be sent as soon as possible,
light9/curvecalc/client.py
Show inline comments
 
"""
 
client code for talking to curvecalc
 
"""
 
import cyclone.httpclient
 
from light9 import networking
 
import urllib.request, urllib.parse, urllib.error
 
from run_local import log
 

	
 

	
 
def sendLiveInputPoint(curve, value):
 
    f = cyclone.httpclient.fetch(networking.curveCalc.path('liveInputPoint'),
 
                                 method='POST',
 
                                 timeout=1,
 
                                 postdata=urllib.parse.urlencode({
 
                                     'curve': curve,
 
                                     'value': str(value),
 
                                     'curve':
 
                                     curve,
 
                                     'value':
 
                                     str(value),
 
                                 }))
 

	
 
    @f.addCallback
 
    def cb(result):
 
        if result.code // 100 != 2:
 
            raise ValueError("curveCalc said %s: %s", result.code, result.body)
 

	
 
    return f
light9/curvecalc/curve.py
Show inline comments
 

	
 
import glob, time, logging, ast, os
 
from bisect import bisect_left, bisect
 
import louie as dispatcher
 
from twisted.internet import reactor
 
from rdflib import Literal
 
from light9 import showconfig
 
from light9.namespaces import L9, RDF, RDFS
 
from rdfdb.patch import Patch
 

	
 
log = logging.getLogger()
 
# todo: move to config, consolidate with ascoltami, musicPad, etc
 
introPad = 4
 
postPad = 4
 

	
 

	
 
class Curve(object):
 
    """curve does not know its name. see Curveset"""
 

	
 
    def __init__(self, uri, pointsStorage='graph'):
 
        self.uri = uri
 
        self.pointsStorage = pointsStorage
 
        self.points = []  # x-sorted list of (x,y)
 
        self._muted = False
 

	
light9/curvecalc/curveview.py
Show inline comments
 

	
 
import math, logging, traceback
 
from gi.repository import Gtk
 
from gi.repository import Gdk
 
from gi.repository import GooCanvas
 
import louie as dispatcher
 
from rdflib import Literal
 
from twisted.internet import reactor
 
from light9.curvecalc.zoomcontrol import RegionZoom
 
from light9.curvecalc import cursors
 
from light9.curvecalc.curve import introPad, postPad
 
from lib.goocanvas_compat import Points, polyline_new_line
 
import imp
 

	
 
log = logging.getLogger()
 
print("curveview.py toplevel")
 

	
 

	
 
def vlen(v):
 
    return math.sqrt(v[0] * v[0] + v[1] * v[1])
 

	
 

	
 
def angle_between(base, p0, p1):
 
    p0 = p0[0] - base[0], p0[1] - base[1]
 
    p1 = p1[0] - base[0], p1[1] - base[1]
 
@@ -534,50 +533,50 @@ class Curveview(object):
 

	
 
    def knob_in(self, curve, value):
 
        """user turned a hardware knob, which edits the point to the
 
        left of the current time"""
 
        if curve != self.curve:
 
            return
 
        idx = self.curve.index_before(self.current_time())
 
        if idx is not None:
 
            pos = self.curve.points[idx]
 
            self.curve.set_points([(idx, (pos[0], value))])
 

	
 
    def slider_in(self, curve, value=None):
 
        """user pushed on a slider. make a new key.  if value is None,
 
        the value will be the same as the last."""
 
        if curve != self.curve:
 
            return
 

	
 
        if value is None:
 
            value = self.curve.eval(self.current_time())
 

	
 
        self.curve.insert_pt((self.current_time(), value))
 

	
 
    def print_state(self, msg=""):
 
        if 0:
 
            print("%s: dragging_dots=%s selecting=%s" % (
 
                msg, self.dragging_dots, self.selecting))
 
            print("%s: dragging_dots=%s selecting=%s" %
 
                  (msg, self.dragging_dots, self.selecting))
 

	
 
    def select_points(self, pts):
 
        """set selection to the given point values (tuples, not indices)"""
 
        idxs = []
 
        for p in pts:
 
            idxs.append(self.curve.points.index(p))
 
        self.select_indices(idxs)
 

	
 
    def select_indices(self, idxs):
 
        """set selection to these point indices. This is the only
 
        writer to self.selected_points"""
 
        self.selected_points = idxs
 
        self.highlight_selected_dots()
 
        if self.selected_points and not self.selectManip:
 
            self.selectManip = SelectManip(
 
                self.canvas.get_root_item(),
 
                getSelectedIndices=lambda: sorted(self.selected_points),
 
                getWorldPoint=lambda i: self.curve.points[i],
 
                getScreenPoint=lambda i: self.screen_from_world(self.curve.
 
                                                                points[i]),
 
                getWorldTime=lambda x: self.world_from_screen(x, 0)[0],
 
                getWorldValue=lambda y: self.world_from_screen(0, y)[1],
 
                getCanvasHeight=lambda: self.canvas.props.y2,
 
                setPoints=self.setPoints,
light9/curvecalc/zoomcontrol.py
Show inline comments
 

	
 
from gi.repository import Gtk
 
from gi.repository import GObject
 
from gi.repository import GooCanvas
 
import louie as dispatcher
 
from light9.curvecalc import cursors
 
from lib.goocanvas_compat import Points, polyline_new_line
 
from twisted.internet import reactor
 

	
 

	
 
class ZoomControl(object):
 
    """
 
    please pack .widget
 
    """
 

	
 
    mintime = 0
 

	
 
    def maxtime():
 
        doc = "seconds at the right edge of the bar"
 

	
 
        def fget(self):
 
            return self._maxtime
 

	
 
        def fset(self, value):
 
            self._maxtime = value
light9/effect/effecteval.py
Show inline comments
 

	
 
from rdflib import URIRef, Literal
 
from light9.namespaces import L9, RDF, DEV
 
from webcolors import rgb_to_hex, hex_to_rgb
 
from colorsys import hsv_to_rgb
 
from decimal import Decimal
 
import math
 
import traceback
 
from noise import pnoise1
 
import logging
 
import time
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.scale import scale
 
import random
 
random.seed(0)
 
print("reload effecteval")
 

	
 
log = logging.getLogger('effecteval')
 

	
 

	
 
def literalColor(rnorm, gnorm, bnorm):
 
    return Literal(
 
        rgb_to_hex([int(rnorm * 255),
 
                    int(gnorm * 255),
 
                    int(bnorm * 255)]))
light9/effect/scale.py
Show inline comments
 

	
 
from rdflib import Literal
 
from decimal import Decimal
 
from webcolors import rgb_to_hex, hex_to_rgb
 

	
 

	
 
def scale(value, strength):
 
    if isinstance(value, Literal):
 
        value = value.toPython()
 

	
 
    if isinstance(value, Decimal):
 
        value = float(value)
 

	
 
    if isinstance(value, str):
 
        if value[0] == '#':
 
            if strength == '#ffffff':
 
                return value
 
            r, g, b = hex_to_rgb(value)
 
            if isinstance(strength, Literal):
 
                strength = strength.toPython()
 
            if isinstance(strength, str):
 
                sr, sg, sb = [v / 255 for v in hex_to_rgb(strength)]
 
            else:
 
                sr = sg = sb = strength
 
            return rgb_to_hex([int(r * sr), int(g * sg), int(b * sb)])
light9/effect/sequencer.py
Show inline comments
 
'''
 
copies from effectloop.py, which this should replace
 
'''
 

	
 

	
 
from louie import dispatcher
 
from rdflib import URIRef
 
from twisted.internet import reactor
 
from twisted.internet import defer
 
from twisted.internet.inotify import INotify
 
from twisted.python.filepath import FilePath
 
import cyclone.sse
 
import logging, bisect, time
 
import traceback
 

	
 
from light9.namespaces import L9, RDF
 
from light9.vidref.musictime import MusicTime
 
from light9.effect import effecteval
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.simple_outputs import SimpleOutputs
 

	
 
from greplin import scales
 
import imp
 

	
 
log = logging.getLogger('sequencer')
 
stats = scales.collection(
 
    '/sequencer/',
 
    scales.PmfStat('update'),
 
    scales.PmfStat('compileGraph'),
light9/effect/settings.py
Show inline comments
 

	
 
"""
 
Data structure and convertors for a table of (device,attr,value)
 
rows. These might be effect attrs ('strength'), device attrs ('rx'),
 
or output attrs (dmx channel).
 
"""
 
import decimal
 
import numpy
 
from rdflib import URIRef, Literal
 
from light9.namespaces import RDF, L9, DEV
 
from rdfdb.patch import Patch
 
import logging
 
log = logging.getLogger('settings')
 
from light9.collector.device import resolve
 

	
 

	
 
def parseHex(h):
 
    if h[0] != '#': raise ValueError(h)
 
    return [int(h[i:i + 2], 16) for i in (1, 3, 5)]
 

	
 

	
 
def parseHexNorm(h):
 
    return [x / 255 for x in parseHex(h)]
 

	
 

	
light9/effect/simple_outputs.py
Show inline comments
 

	
 
import traceback
 
from light9.namespaces import L9, RDF
 
from light9.effect.scale import scale
 

	
 

	
 
class SimpleOutputs(object):
 

	
 
    def __init__(self, graph):
 
        self.graph = graph
 

	
 
        # effect : [(dev, attr, value, isScaled)]
 
        self.effectOutputs = {}
 

	
 
        self.graph.addHandler(self.updateEffectsFromGraph)
 

	
 
    def updateEffectsFromGraph(self):
 
        for effect in self.graph.subjects(RDF.type, L9['Effect']):
 
            settings = []
 
            for setting in self.graph.objects(effect, L9['setting']):
 
                settingValues = dict(self.graph.predicate_objects(setting))
 
                try:
 
                    d = settingValues.get(L9['device'], None)
 
                    a = settingValues.get(L9['deviceAttr'], None)
 
                    v = settingValues.get(L9['value'], None)
light9/effecteval/effectloop.py
Show inline comments
 

	
 
import time, json, logging, traceback
 
import numpy
 
import serial
 
from twisted.internet import reactor, threads
 
from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
from twisted.internet.error import TimeoutError
 
from rdflib import URIRef, Literal
 
import cyclone.httpclient
 
from light9.namespaces import L9, RDF, RDFS
 
from light9.effecteval.effect import EffectNode
 
from light9 import Effects
 
from light9 import networking
 
from light9 import Submaster
 
from light9 import dmxclient
 
from light9 import prof
 
log = logging.getLogger('effectloop')
 

	
 

	
 
class EffectLoop(object):
 
    """maintains a collection of the current EffectNodes, gets time from
 
    music player, sends dmx"""
 

	
 
    def __init__(self, graph, stats):
 
        self.graph, self.stats = graph, stats
 
@@ -161,50 +160,51 @@ class EffectLoop(object):
 
                now = time.time()
 
                if now > self.lastErrorLog + 5:
 
                    if hasattr(exc, 'expr'):
 
                        log.exception('in expression %r', exc.expr)
 
                    log.error("effect %s: %s" % (e.uri, exc))
 
                    self.lastErrorLog = now
 
        log.debug('eval %s effects, got %s outputs', len(self.currentEffects),
 
                  len(outputs))
 

	
 
        return outputs
 

	
 
    def logLevels(self, now, out):
 
        # this would look nice on the top of the effecteval web pages too
 
        if log.isEnabledFor(logging.DEBUG):
 
            log.debug(self.logMessage(out))
 
        else:
 
            if now > self.lastLogTime + 5:
 
                msg = self.logMessage(out)
 
                if msg != self.lastLogMsg:
 
                    log.info(msg)
 
                    self.lastLogMsg = msg
 
                self.lastLogTime = now
 

	
 
    def logMessage(self, out):
 
        return ("send dmx: {%s}" % ", ".join(
 
            "%r: %.3g" % (str(k), v) for k, v in list(out.get_levels().items())))
 
        return ("send dmx: {%s}" %
 
                ", ".join("%r: %.3g" % (str(k), v)
 
                          for k, v in list(out.get_levels().items())))
 

	
 

	
 
Z = numpy.zeros((50, 3), dtype=numpy.float16)
 

	
 

	
 
class ControlBoard(object):
 

	
 
    def __init__(
 
            self,
 
            dev='/dev/serial/by-id/usb-FTDI_FT232R_USB_UART_A7027NYX-if00-port0'
 
    ):
 
        log.info('opening %s', dev)
 
        self._dev = serial.Serial(dev, baudrate=115200)
 

	
 
    def _8bitMessage(self, floatArray):
 
        px255 = (numpy.clip(floatArray, 0, 1) * 255).astype(numpy.uint8)
 
        return px255.reshape((-1,)).tostring()
 

	
 
    def setStrip(self, which, pixels):
 
        """
 
        which: 0 or 1 to pick the strip
 
        pixels: (50, 3) array of 0..1 floats
 
        """
 
        command = {0: '\x00', 1: '\x01'}[which]
light9/io/__init__.py
Show inline comments
 

	
 
import sys
 

	
 

	
 
class BaseIO(object):
 

	
 
    def __init__(self):
 
        self.dummy = 1
 
        self.__name__ = 'BaseIO'
 
        # please override and set __name__ to your class name
 

	
 
    def golive(self):
 
        """call this if you want to promote the dummy object becomes a live object"""
 
        print("IO: %s is going live" % self.__name__)
 
        self.dummy = 0
 
        # you'd override with additional startup stuff here,
 
        # perhaps even loading a module and saving it to a class
 
        # attr so the subclass-specific functions can use it
 

	
 
    def godummy(self):
 
        print("IO: %s is going dummy" % self.__name__)
 
        self.dummy = 1
 
        # you might override this to close ports, etc
 

	
 
    def isdummy(self):
light9/io/udmx.py
Show inline comments
 

	
 
import logging
 
import usb.core
 
from usb.util import CTRL_TYPE_VENDOR, CTRL_RECIPIENT_DEVICE, CTRL_OUT
 

	
 
log = logging.getLogger('udmx')
 
"""
 
Send dmx to one of these:
 
http://www.amazon.com/Interface-Adapter-Controller-Lighting-Freestyler/dp/B00W52VIOS
 

	
 
[4520784.059479] usb 1-2.3: new low-speed USB device number 6 using xhci_hcd
 
[4520784.157410] usb 1-2.3: New USB device found, idVendor=16c0, idProduct=05dc
 
[4520784.157416] usb 1-2.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
 
[4520784.157419] usb 1-2.3: Product: uDMX
 
[4520784.157422] usb 1-2.3: Manufacturer: www.anyma.ch
 
[4520784.157424] usb 1-2.3: SerialNumber: ilLUTZminator001
 

	
 
See https://www.illutzmination.de/udmxfirmware.html?&L=1
 
    sources/commandline/uDMX.c
 
or https://github.com/markusb/uDMX-linux/blob/master/uDMX.c
 
"""
 

	
 
cmd_SetChannelRange = 0x0002
 

	
 

	
light9/paint/solve.py
Show inline comments
 

	
 
from light9.namespaces import RDF, L9, DEV
 
from PIL import Image
 
import numpy
 
import scipy.misc, scipy.ndimage, scipy.optimize
 
import cairo
 
import logging
 

	
 
from light9.effect.settings import DeviceSettings, parseHex, toHex
 

	
 
log = logging.getLogger('solve')
 

	
 
# numpy images in this file are (x, y, c) layout.
 

	
 

	
 
def numpyFromCairo(surface):
 
    w, h = surface.get_width(), surface.get_height()
 
    a = numpy.frombuffer(surface.get_data(), numpy.uint8)
 
    a.shape = h, w, 4
 
    a = a.transpose((1, 0, 2))
 
    return a[:w, :h, :3]
 

	
 

	
 
def numpyFromPil(img):
 
    return scipy.misc.fromimage(img, mode='RGB').transpose((1, 0, 2))
light9/paint/solve_test.py
Show inline comments
 
@@ -48,60 +48,60 @@ class TestSolveBrute(TestSolve):
 

	
 
CAM_TEST = Namespace('http://light9.bigasterisk.com/test/cam/')
 

	
 

	
 
class TestSimulationLayers(unittest.TestCase):
 

	
 
    def setUp(self):
 
        self.graph = LocalSyncedGraph(
 
            files=['test/cam/lightConfig.n3', 'test/cam/bg.n3'])
 
        self.solver = solve.Solver(self.graph,
 
                                   imgSize=(100, 48),
 
                                   sessions=[L9['session0']])
 
        self.solver.loadSamples()
 

	
 
    def testBlack(self):
 
        self.assertEqual([],
 
                         self.solver.simulationLayers(
 
                             settings=DeviceSettings(self.graph, [])))
 

	
 
    def testPerfect1Match(self):
 
        layers = self.solver.simulationLayers(
 
            settings=DeviceSettings(self.graph, [(
 
                DEV['aura1'], L9['color'],
 
                "#ffffff"), (DEV['aura1'], L9['rx'],
 
                              0.5), (DEV['aura1'], L9['ry'], 0.573)]))
 
                             0.5), (DEV['aura1'], L9['ry'], 0.573)]))
 
        self.assertEqual([{
 
            'path': CAM_TEST['bg2-d.jpg'],
 
            'color': (1., 1., 1.)
 
        }], layers)
 

	
 
    def testPerfect1MatchTinted(self):
 
        layers = self.solver.simulationLayers(
 
            settings=DeviceSettings(self.graph, [(
 
                DEV['aura1'], L9['color'],
 
                "#304050"), (DEV['aura1'], L9['rx'],
 
                              0.5), (DEV['aura1'], L9['ry'], 0.573)]))
 
                             0.5), (DEV['aura1'], L9['ry'], 0.573)]))
 
        self.assertEqual([{
 
            'path': CAM_TEST['bg2-d.jpg'],
 
            'color': (.188, .251, .314)
 
        }], layers)
 

	
 
    def testPerfect2Matches(self):
 
        layers = self.solver.simulationLayers(
 
            settings=DeviceSettings(self.graph, [
 
                (DEV['aura1'], L9['color'], "#ffffff"),
 
                (DEV['aura1'], L9['rx'], 0.5),
 
                (DEV['aura1'], L9['ry'], 0.573),
 
                (DEV['aura2'], L9['color'], "#ffffff"),
 
                (DEV['aura2'], L9['rx'], 0.7),
 
                (DEV['aura2'], L9['ry'], 0.573),
 
            ]))
 
        self.assertItemsEqual([
 
            {
 
                'path': CAM_TEST['bg2-d.jpg'],
 
                'color': (1, 1, 1)
 
            },
 
            {
 
                'path': CAM_TEST['bg2-f.jpg'],
 
                'color': (1, 1, 1)
 
            },
light9/vidref/replay.py
Show inline comments
 

	
 
import os, gtk, shutil, logging, time
 
from bisect import bisect_left
 
from decimal import Decimal
 
log = logging.getLogger()
 

	
 
framerate = 15
 

	
 

	
 
def songDir(song):
 
    safeUri = song.split('://')[-1].replace('/', '_')
 
    return os.path.expanduser("~/light9-vidref/play-%s" % safeUri)
 

	
 

	
 
def takeDir(songDir, startTime):
 
    """
 
    startTime: unix seconds (str ok)
 
    """
 
    return os.path.join(songDir, str(int(startTime)))
 

	
 

	
 
def snapshotDir():
 
    return os.path.expanduser("~/light9-vidref/snapshot")
 

	
 

	
0 comments (0 inline, 0 general)