Mercurial > code > home > repos > light9
changeset 1044:a2081b9adfe4
effecteval now takes dropped subs and makes new effects out of them
Ignore-this: 69423eca1858291f486dd2a202d3732a
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Wed, 28 May 2014 07:37:36 +0000 |
parents | aa45e5379c5a |
children | c1face79c0e1 |
files | bin/effecteval light9/curvecalc/curve.py light9/effecteval/effect.html light9/effecteval/index.coffee light9/effecteval/index.html light9/namespaces.py static/style.css |
diffstat | 7 files changed, 133 insertions(+), 42 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/effecteval Wed May 28 05:57:08 2014 +0000 +++ b/bin/effecteval Wed May 28 07:37:36 2014 +0000 @@ -3,14 +3,15 @@ from twisted.internet import reactor, task from twisted.internet.defer import inlineCallbacks import cyclone.web, cyclone.websocket, cyclone.httpclient -import sys, optparse, logging, subprocess, json, re, time -from rdflib import URIRef, RDF +import sys, optparse, logging, subprocess, json, re, time, traceback +from rdflib import URIRef, RDF, Literal sys.path.append(".") from light9 import networking, showconfig, Submaster, dmxclient from light9.rdfdb.syncedgraph import SyncedGraph from light9.curvecalc.curve import Curve -from light9.namespaces import L9, DCTERMS +from light9.namespaces import L9, DCTERMS, RDF +from light9.rdfdb.patch import Patch sys.path.append("/my/proj/homeauto/lib") sys.path.append("/home/drewp/projects/homeauto/lib") @@ -20,6 +21,24 @@ def get(self): self.write(open("light9/effecteval/effect.html").read()) +class SongEffects(PrettyErrorHandler, cyclone.web.RequestHandler): + def post(self): + song = URIRef(self.get_argument('uri')) + drop = URIRef(self.get_argument('drop')) + ctx = song + now = time.time() + effect = song + "/effect/e-%f" % now + curve = song + "/curve/c-%f" % now + self.settings.graph.patch(Patch(addQuads=[ + (song, L9['effect'], effect, ctx), + (effect, RDF.type, L9['Effect'], ctx), + (effect, L9['code'], + Literal('out = sub(%s, intensity=%s)' % (drop.n3(), curve.n3())), + ctx), + (curve, RDF.type, L9['Curve'], ctx), + (curve, L9['points'], Literal('0 0'), ctx), + ])) + class SongEffectsUpdates(cyclone.websocket.WebSocketHandler): def connectionMade(self, *args, **kwargs): self.graph = self.settings.graph @@ -66,6 +85,8 @@ return URIRef('http://light9.bigasterisk.com/show/dance2014/sub/' + s[4:]) if s.startswith('song1:'): return URIRef('http://ex/effect/song1/' + s[6:]) + if (s[0], s[-1]) == ('<', '>'): + return URIRef(s[1:-1]) raise NotImplementedError class EffectNode(object): @@ -88,8 +109,12 @@ # read from disk ok? how do we know to reread? start with # mtime. the mtime check could be done occasionally so on # average we read at most one curve's mtime per effectLoop. - - self.curve.set_from_string(self.graph.value(intensityCurve, L9['points'])) + + pts = self.graph.value(intensityCurve, L9['points']) + if pts is None: + log.info("curve %r has no points" % intensityCurve) + else: + self.curve.set_from_string(pts) def eval(self, songTime): @@ -127,32 +152,36 @@ @inlineCallbacks def effectLoop(graph): t1 = time.time() - response = json.loads((yield cyclone.httpclient.fetch( - networking.musicPlayer.path('time'))).body) - if response['song'] is not None: - song = URIRef(response['song']) - songTime = response['t'] - # Possibilities to make this shut up about graph copies: - # - implement the cheap readonly currentState response - # - do multiple little currentState calls (in this code) over just - # the required triples - # - use addHandler instead and only fire dmx when there is a data - # change (and also somehow call it when there is a time change) + try: + response = json.loads((yield cyclone.httpclient.fetch( + networking.musicPlayer.path('time'))).body) + if response['song'] is not None: + song = URIRef(response['song']) + songTime = response['t'] + # Possibilities to make this shut up about graph copies: + # - implement the cheap readonly currentState response + # - do multiple little currentState calls (in this code) over just + # the required triples + # - use addHandler instead and only fire dmx when there is a data + # change (and also somehow call it when there is a time change) - outSubs = [] - with graph.currentState(tripleFilter=(song, L9['effect'], None)) as g: - for effectUri in g.objects(song, L9['effect']): - # these should be built once, not per (frequent) update - node = EffectNode(graph, effectUri) - outSubs.append(node.eval(songTime)) - out = Submaster.sub_maxes(*outSubs) - # out.get_levels() for a more readable view - dmx = out.get_dmx_list() + outSubs = [] + with graph.currentState(tripleFilter=(song, L9['effect'], None)) as g: + for effectUri in g.objects(song, L9['effect']): + # these should be built once, not per (frequent) update + node = EffectNode(graph, effectUri) + outSubs.append(node.eval(songTime)) + out = Submaster.sub_maxes(*outSubs) + # out.get_levels() for a more readable view + dmx = out.get_dmx_list() - if log.isEnabledFor(logging.DEBUG): - log.debug("send dmx: %r", out.get_levels()) - yield dmxclient.outputlevels(dmx, twisted=True) - + if log.isEnabledFor(logging.DEBUG): + log.debug("send dmx: %r", out.get_levels()) + yield dmxclient.outputlevels(dmx, twisted=True) + except Exception: + traceback.print_exc() + time.sleep(1) + loopTime = time.time() - t1 log.debug('loopTime %.1f ms', 1000 * loopTime) @@ -176,6 +205,7 @@ (r'/songEffectsUpdates', SongEffectsUpdates), (r'/static/(.*)', SFH, {'path': 'static/'}), (r'/effect/eval', EffectEval), + (r'/songEffects', SongEffects), (r'/songEffects/eval', SongEffectsEval), ], debug=True, graph=self.graph) reactor.listenTCP(networking.effectEval.port, self.cycloneApp)
--- a/light9/curvecalc/curve.py Wed May 28 05:57:08 2014 +0000 +++ b/light9/curvecalc/curve.py Wed May 28 07:37:36 2014 +0000 @@ -62,6 +62,8 @@ def eval(self, t, allow_muting=True): if self.muted and allow_muting: return 0 + if not self.points: + raise ValueError("curve has no points") i = bisect_left(self.points,(t,None))-1 if i == -1:
--- a/light9/effecteval/effect.html Wed May 28 05:57:08 2014 +0000 +++ b/light9/effecteval/effect.html Wed May 28 07:37:36 2014 +0000 @@ -3,11 +3,13 @@ <head> <title>effect</title> <meta charset="utf-8" /> + <link rel="stylesheet" href="static/style.css"> + </head> <body> - <a href="./">Effects</a> / <a data-bind="attr: {href: uri}, text: uri"></a> + <a href="./">Effects</a> / <a class="effect" data-bind="attr: {href: uri}, text: uri"></a> - <div>code: <input type="text" size="100" data-bind="value: code"></input></div> + <div>code: <input type="text" size="160" data-bind="value: code"></input></div> <script src="static/jquery-2.1.1.min.js"></script> <script src="static/knockout-3.1.0.js"></script>
--- a/light9/effecteval/index.coffee Wed May 28 05:57:08 2014 +0000 +++ b/light9/effecteval/index.coffee Wed May 28 07:37:36 2014 +0000 @@ -1,6 +1,17 @@ model = songs: ko.observableArray([]) +model.dragover = (obj, event) -> + event.preventDefault() + event.originalEvent.dataTransfer.dropEffect = 'copy' + +model.drop = (uri, event) -> + event.preventDefault() + dropped(uri, event.originalEvent.dataTransfer.getData('text/uri-list')) + +dropped = (songTargetUri, dropUri) -> + $.post('songEffects', {uri: songTargetUri, drop: dropUri}) + reconnectingWebSocket "ws://localhost:8070/songEffectsUpdates", (msg) -> console.log(msg.songs) model.songs(msg.songs)
--- a/light9/effecteval/index.html Wed May 28 05:57:08 2014 +0000 +++ b/light9/effecteval/index.html Wed May 28 07:37:36 2014 +0000 @@ -3,17 +3,21 @@ <head> <title>effecteval</title> <meta charset="utf-8" /> + <link rel="stylesheet" href="static/style.css"> </head> <body> - All effect instances: + <h1>All effect instances</h1> <!-- subscribe to a query of all effects and their songs --> - <ul data-bind="foreach: songs"> + <ul data-bind="foreach: songs" class="twoColList"> <li> - <a data-bind="attr: {href: uri}">Song <span data-bind="text: label"></span></a> + <a class="song" data-bind="attr: {href: uri}">Song <span data-bind="text: label"></span></a> + <ul> + <!-- ko foreach: effects --> + <li><a class="effect" data-bind="attr: {href: 'effect?'+jQuery.param({uri: $data})}, text: $data"></a></li> + <!-- /ko --> + <li class="dropTarget" data-bind="event: {dragover: $root.dragover, dragenter: $root.dragover, drop: function(data, event) { $root.drop(uri, event); }}">Add another (drop a sub or effect class)</li> + </ul> </li> - <ul data-bind="foreach: effects"> - <li><a data-bind="attr: {href: 'effect?'+jQuery.param({uri: $data})}, text: $data"></a></li> - </ul> </ul> <script src="static/jquery-2.1.1.min.js"></script> <script src="static/knockout-3.1.0.js"></script>
--- a/light9/namespaces.py Wed May 28 05:57:08 2014 +0000 +++ b/light9/namespaces.py Wed May 28 07:37:36 2014 +0000 @@ -1,4 +1,4 @@ -from rdflib import Namespace +from rdflib import Namespace, RDF L9 = Namespace("http://light9.bigasterisk.com/") MUS = Namespace("http://light9.bigasterisk.com/music/")
--- a/static/style.css Wed May 28 05:57:08 2014 +0000 +++ b/static/style.css Wed May 28 07:37:36 2014 +0000 @@ -3,6 +3,13 @@ color: white; font-family: sans-serif; } + + +a { +color: rgb(97, 97, 255); + + } + .songs { -moz-column-width:205px; -webkit-column-width:205px; @@ -46,7 +53,42 @@ opacity: .5; } .num { -font-size: 27px; -color: rgb(233, 122, 122); -display: inline-block; - } \ No newline at end of file + font-size: 27px; + color: rgb(233, 122, 122); + display: inline-block; +} + +.dropTarget { + padding: 5px; + border: 2px dashed gray; + font-style: italic; + color: rgb(78, 90, 107); +} +.twoColList { + -webkit-column-width: 24em; +} +.twoColList > li { + margin-bottom: 13px; +} + +.song { + color: rgb(85, 221, 85); +} +.song:before { + + content: "♫"; + color: black; + background: rgb(85, 221, 85); + border-radius: 30%; +} + +.effect:before { + content: "⛖"; +} + +.song:before, .effect:before { + margin-right: 3px; + text-decoration: none !important; + font-size: 140%; + +} \ No newline at end of file