Mercurial > code > home > repos > light9
changeset 1414:c35ec37c3c6e
sequencer reloads effecteval on the fly. plus some /stats support.
Ignore-this: 964f4c9007de6532457e0a507d2106f1
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Fri, 10 Jun 2016 06:56:34 +0000 |
parents | cb1379b3555b |
children | edc17fdcaaf1 |
files | bin/effectsequencer light9/effect/effecteval.py light9/effect/sequencer.py light9/subclient.py light9/web/index.html light9/web/timeline/timeline.coffee show/dance2016/effect.n3 show/dance2016/song1.n3 |
diffstat | 8 files changed, 169 insertions(+), 68 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/effectsequencer Fri Jun 10 03:27:22 2016 +0000 +++ b/bin/effectsequencer Fri Jun 10 06:56:34 2016 +0000 @@ -32,7 +32,6 @@ scales.IntStat('errors'), ) def launch(self, *args): - print 'launch' self.seq = Sequencer( self.graph, lambda settings: sendToCollector('effectSequencer', self.session,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/light9/effect/effecteval.py Fri Jun 10 06:56:34 2016 +0000 @@ -0,0 +1,77 @@ +from __future__ import division +from rdflib import URIRef, Literal +from light9.namespaces import L9, RDF +from webcolors import rgb_to_hex +import math + + +class EffectEval(object): + """ + runs one effect's code to turn effect attr settings into output + device settings. No state; suitable for reload(). + """ + def __init__(self, graph, effect): + self.graph = graph + self.effect = effect + + #for ds in g.objects(g.value(uri, L9['effectClass']), L9['deviceSetting']): + # self.setting = (g.value(ds, L9['device']), g.value(ds, L9['attr'])) + + def outputFromEffect(self, effectSettings, songTime): + """ + From effect attr settings, like strength=0.75, to output device + settings like light1/bright=0.72;light2/bright=0.78. This runs + the effect code. + """ + attr, value = effectSettings[0] + value = float(value) + assert attr == L9['strength'] + c = int(255 * value) + color = [0, 0, 0] + if self.effect == L9['effect/RedStrip']: # throwaway + + mov = URIRef('http://light9.bigasterisk.com/device/moving1') + col = [ + (songTime + .0) % 1.0, + (songTime + .4) % 1.0, + (songTime + .8) % 1.0, + ] + return [ + # device, attr, lev + + (mov, L9['color'], Literal(rgb_to_hex([value*x*255 for x in col]))), + (mov, L9['rx'], Literal(100 + 70 * math.sin(songTime*2))), + ] * (value>0) + + elif self.effect == L9['effect/BlueStrip']: + color[2] = c + elif self.effect == L9['effect/WorkLight']: + color[1] = c + elif self.effect == L9['effect/Curtain']: + color[0] = color[2] = 70/255 * c + elif self.effect == L9['effect/Strobe']: + attr, value = effectSettings[0] + assert attr == L9['strength'] + strength = float(value) + rate = 2 + duty = .3 + offset = 0 + f = (((songTime + offset) * rate) % 1.0) + c = (f < duty) * strength + col = rgb_to_hex([c * 255, c * 255, c * 255]) + return [ + (L9['device/colorStrip'], L9['color'], Literal(col)), + ] + else: + color[0] = color[1] = color[2] = c + + return [ + # device, attr, lev + (URIRef('http://light9.bigasterisk.com/device/moving1'), + URIRef("http://light9.bigasterisk.com/color"), + Literal(rgb_to_hex(color))) + ] + + + +
--- a/light9/effect/sequencer.py Fri Jun 10 03:27:22 2016 +0000 +++ b/light9/effect/sequencer.py Fri Jun 10 06:56:34 2016 +0000 @@ -9,13 +9,22 @@ import json, logging, bisect import treq import math +import time +from twisted.internet.inotify import INotify +from twisted.python.filepath import FilePath from light9 import networking from light9.namespaces import L9, RDF from light9.vidref.musictime import MusicTime +from light9.effect import effecteval +from greplin import scales log = logging.getLogger('sequencer') +stats = scales.collection('/sequencer/', + scales.PmfStat('update'), + scales.DoubleStat('recentFps'), + ) def sendToCollector(client, session, settings): return treq.put(networking.collector.path('attrs'), data=json.dumps({'settings': settings, @@ -24,10 +33,11 @@ class Note(object): - def __init__(self, graph, uri): + def __init__(self, graph, uri, effectevalModule): g = self.graph = graph self.uri = uri - self.effectEval = EffectEval(graph, g.value(uri, L9['effectClass'])) + self.effectEval = effectevalModule.EffectEval( + graph, g.value(uri, L9['effectClass'])) floatVal = lambda s, p: float(g.value(s, p).toPython()) originTime = floatVal(uri, L9['originTime']) self.points = [] @@ -66,81 +76,56 @@ return self.effectEval.outputFromEffect(effectSettings, t) -class EffectEval(object): - """ - runs one effect's code to turn effect attr settings into output - device settings - """ - def __init__(self, graph, effect): - self.graph = graph - self.effect = effect - - #for ds in g.objects(g.value(uri, L9['effectClass']), L9['deviceSetting']): - # self.setting = (g.value(ds, L9['device']), g.value(ds, L9['attr'])) - - def outputFromEffect(self, effectSettings, songTime): - """ - From effect attr settings, like strength=0.75, to output device - settings like light1/bright=0.72;light2/bright=0.78. This runs - the effect code. - """ - attr, value = effectSettings[0] - value = float(value) - assert attr == L9['strength'] - c = int(255 * value) - color = [0, 0, 0] - if self.effect == L9['effect/RedStrip']: # throwaway - - mov = URIRef('http://light9.bigasterisk.com/device/moving1') - col = [ - (songTime + .1) % 1.0, - (songTime + .4) % 1.0, - (songTime + .8) % 1.0, - ] - print 'col', col - return [ - # device, attr, lev - - (mov, L9['color'], Literal(rgb_to_hex([value*x*255 for x in col]))), - (mov, L9['rx'], Literal(100 + 70 * math.sin(songTime*2))), - ] - - elif self.effect == L9['effect/BlueStrip']: - color[2] = c - elif self.effect == L9['effect/WorkLight']: - color[1] = c - elif self.effect == L9['effect/Curtain']: - color[0] = color[2] = 70/255 * c - else: - color[0] = color[1] = color[2] = c - - return [ - # device, attr, lev - (URIRef('http://light9.bigasterisk.com/device/moving1'), - URIRef("http://light9.bigasterisk.com/color"), - Literal(rgb_to_hex(color))) - ] - class Sequencer(object): def __init__(self, graph, sendToCollector): self.graph = graph self.sendToCollector = sendToCollector self.music = MusicTime(period=.2, pollCurvecalc=False) + self.recentUpdateTimes = [] + self.lastStatLog = 0 self.notes = {} # song: [notes] self.graph.addHandler(self.compileGraph) self.update() + self.watchCode() + + def watchCode(self): + self.notifier = INotify() + self.notifier.startReading() + self.notifier.watch( + FilePath(effecteval.__file__.replace('.pyc', '.py')), + callbacks=[self.codeChange]) + + def codeChange(self, watch, path, mask): + def go(): + reload(effecteval) + self.graph.addHandler(self.compileGraph) + # in case we got an event at the start of the write + reactor.callLater(.1, go) + def compileGraph(self): """rebuild our data from the graph""" g = self.graph + + log.info("compileGraph") + reload(effecteval) + for song in g.subjects(RDF.type, L9['Song']): self.notes[song] = [] for note in g.objects(song, L9['note']): - self.notes[song].append(Note(g, note)) + self.notes[song].append(Note(g, note, effecteval)) + + @stats.update.time() + def update(self): + now = time.time() + self.recentUpdateTimes = self.recentUpdateTimes[-20:] + [now] + stats.recentFps = len(self.recentUpdateTimes) / (self.recentUpdateTimes[-1] - self.recentUpdateTimes[0] + .0001) + if now > self.lastStatLog + 10: + log.info("%.2f fps", stats.recentFps) + self.lastStatLog = now - def update(self): - reactor.callLater(1/30, self.update) + reactor.callLater(1/50, self.update) musicState = self.music.getLatest() song = URIRef(musicState['song']) if musicState.get('song') else None @@ -155,6 +140,6 @@ # away. might be better for collector not to merge our # past requests, and then we can omit zeroed notes? outs = note.outputSettings(t) - print 'out', outs + #print 'out', outs settings.extend(outs) self.sendToCollector(settings)
--- a/light9/subclient.py Fri Jun 10 03:27:22 2016 +0000 +++ b/light9/subclient.py Fri Jun 10 06:56:34 2016 +0000 @@ -20,5 +20,4 @@ def _send_sub(self): outputSettings = self.get_output_settings() - print outputSettings sendToCollector('subclient', self.session, outputSettings)
--- a/light9/web/index.html Fri Jun 10 03:27:22 2016 +0000 +++ b/light9/web/index.html Fri Jun 10 06:56:34 2016 +0000 @@ -28,6 +28,7 @@ <div> <span class="left"><a class="big" href="{{name}}/">{{name}}</a></span> <span><button on-click="click">window</button></span> + <span><a href="{{name}}/stats">stats</a></span> </div> </template> <script> @@ -49,16 +50,17 @@ <h1>light9 home page</h1> <div style="float: left"> + <service-button-row name="."></service-button-row> <service-button-row name="rdfdb"></service-button-row> - <service-button-row name="effectEval"></service-button-row> + <service-button-row name="collector"></service-button-row> + <service-button-row name="effectSequencer"></service-button-row> <service-button-row name="ascoltami"></service-button-row> - <service-button-row name="subComposer"></service-button-row> <service-button-row name="subServer"></service-button-row> <service-button-row name="picamserve"></service-button-row> <service-button-row name="vidref"></service-button-row> - <service-button-row name="."></service-button-row> <service-button-row name="live"></service-button-row> <service-button-row name="timeline"></service-button-row> + <service-button-row name="subComposer"></service-button-row> </div> </body>
--- a/light9/web/timeline/timeline.coffee Fri Jun 10 03:27:22 2016 +0000 +++ b/light9/web/timeline/timeline.coffee Fri Jun 10 06:56:34 2016 +0000 @@ -469,9 +469,22 @@ if elem elem.remove() delete @elemById[uri] + + anyPointsInView: (pts) -> + for pt in pts + if pt.e(1) > -100 && pt.e(1) < 2500 + return true + return false setNote: (uri, curvePts, effectLabel) -> - elem = @getOrCreateElem(uri, 'notes', 'path', {style:"fill:#53774b; stroke:#000000; stroke-width:1.5;"}) + areaId = uri + '/area' + labelId = uri + '/label' + if not @anyPointsInView(curvePts) + @clearElem(areaId) + @clearElem(labelId) + return + elem = @getOrCreateElem(areaId, 'notes', 'path', + {style:"fill:#53774b; stroke:#000000; stroke-width:1.5;"}) elem.setAttribute('d', svgPathFromPoints(curvePts)) elem = @getOrCreateElem(uri+'/label', 'noteLabels', 'text', {style: "font-size:13px;line-height:125%;font-family:'Verana Sans';text-align:start;text-anchor:start;fill:#000000;"}) @@ -504,5 +517,9 @@ ]) setAdjusterConnector: (uri, center, target) -> + id = uri + '/adj' + if not @anyPointsInView([center, target]) + @clearElem(uri) + return elem = @getOrCreateElem(uri, 'connectors', 'path', {style: "fill:none;stroke:#d4d4d4;stroke-width:0.9282527;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:2.78475821, 2.78475821;stroke-dashoffset:0;"}) elem.setAttribute('d', svgPathFromPoints([center, target]))
--- a/show/dance2016/effect.n3 Fri Jun 10 03:27:22 2016 +0000 +++ b/show/dance2016/effect.n3 Fri Jun 10 06:56:34 2016 +0000 @@ -38,6 +38,10 @@ :fc1p3 :time 0.15; :value 0 . +effect:Strobe a :Effect; rdfs:label "strobe"; + :publishAttr :strength, :rate, :offset, :duty . + + effect:WorkLight a :Effect; rdfs:label "work light"; :publishAttr :strength;
--- a/show/dance2016/song1.n3 Fri Jun 10 03:27:22 2016 +0000 +++ b/show/dance2016/song1.n3 Fri Jun 10 06:56:34 2016 +0000 @@ -7,9 +7,18 @@ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> . :a01 :effectAttr :chaseOffset; :value 0.12 . -<http://light9.bigasterisk.com/show/dance2016/song1> :note song:n1, song:n25, song:n26, song:n27, song:n28 . +<http://light9.bigasterisk.com/show/dance2016/song1> :note song:n0, song:n1, song:n2, song:n25, song:n26, song:n27, song:n28 . :ao0 :effectAttr :chaseTempo; :value 100 . +song:n0 a :Note; :curve song:n0c0; :effectClass effect:WorkLight; + :originTime 161.162 . + +song:n0c0 a :Curve; :attr :strength; :point song:n0c0p0, song:n0c0p1, song:n0c0p2, song:n0c0p3 . +song:n0c0p0 :time 0.000; :value 0.000 . +song:n0c0p1 :time 1.000; :value 1.000 . +song:n0c0p2 :time 2.000; :value 1.000 . +song:n0c0p3 :time 3.000; :value 0.000 . + song:n1 a :Note; :attrOverride :ao0, :ao1; :curve song:n1c1; :effectClass effect:RedStrip; :originTime 41.011 . @@ -19,6 +28,9 @@ song:n1c1p2 :time 2; :value 1 . song:n1c1p3 :time 3; :value 0 . +song:n2 a :Note; :curve song:n2c0; :effectClass effect:Strobe; + :originTime 47.087 . + song:n25 a :Note; :curve song:n25c0; :effectClass effect:BlueStrip; :originTime 71.234 . @@ -54,3 +66,9 @@ song:n28c0p1 :time 1.000; :value 1.000 . song:n28c0p2 :time 2.000; :value 1.000 . song:n28c0p3 :time 3.000; :value 0.000 . + +song:n2c0 a :Curve; :attr :strength; :point song:n2c0p0, song:n2c0p1, song:n2c0p2, song:n2c0p3 . +song:n2c0p0 :time 0.000; :value 0.000 . +song:n2c0p1 :time 1.000; :value 1.000 . +song:n2c0p2 :time 2.000; :value 1.000 . +song:n2c0p3 :time 3.000; :value 0.000 .