Mercurial > code > home > repos > light9
changeset 1777:299d49de85a8
effectSequencer info display
Ignore-this: 88d2b52511206f38b90b2658bc63ec9c
author | Drew Perttula <drewp@bigasterisk.com> |
---|---|
date | Mon, 04 Jun 2018 07:05:42 +0000 |
parents | b680d6f50a93 |
children | 178b11c9a429 |
files | bin/keyboardcomposer light9/effect/effecteval.py light9/effect/sequencer.html light9/effect/sequencer.py |
diffstat | 4 files changed, 130 insertions(+), 154 deletions(-) [+] |
line wrap: on
line diff
--- a/bin/keyboardcomposer Sun Jun 03 21:04:00 2018 +0000 +++ b/bin/keyboardcomposer Mon Jun 04 07:05:42 2018 +0000 @@ -460,12 +460,12 @@ strength = _graph.value(setting, L9['level']) if strength: now = time.time() - outputSettings.append( - self.effectEval[effect].outputFromEffect( - [(L9['strength'], strength)], - songTime=now, - # should be counting from when you bumped up from 0 - noteTime=now)) + 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)
--- a/light9/effect/effecteval.py Sun Jun 03 21:04:00 2018 +0000 +++ b/light9/effect/effecteval.py Mon Jun 04 07:05:42 2018 +0000 @@ -51,8 +51,9 @@ strength = float(effectSettings[L9['strength']]) if strength <= 0: - return DeviceSettings(self.graph, []) + return DeviceSettings(self.graph, []), {'zero': True} + report = {} out = {} # (dev, attr): value out.update(self.simpleOutputs.values( @@ -63,12 +64,12 @@ try: func = globals()[tail] except KeyError: - pass + report['error'] = 'effect code not found for %s' % self.effect else: out.update(func(effectSettings, strength, songTime, noteTime)) outList = [(d, a, v) for (d, a), v in out.iteritems()] - return DeviceSettings(self.graph, outList) + return DeviceSettings(self.graph, outList), report def effect_Curtain(effectSettings, strength, songTime, noteTime):
--- a/light9/effect/sequencer.html Sun Jun 03 21:04:00 2018 +0000 +++ b/light9/effect/sequencer.html Mon Jun 04 07:05:42 2018 +0000 @@ -13,155 +13,127 @@ <script src="/lib/underscore/underscore-min.js"></script> <link rel="stylesheet" href="/style.css"> - <style> - td { white-space: nowrap; } - </style> </head> <body> - <dom-module id="light9-collector-device"> + <dom-module id="light9-sequencer-ui"> <template> <style> :host { display: block; - break-inside: avoid-column; } - h3 { - margin-top: 12px; - margin-bottom: 0; + td { + white-space: nowrap; + padding: 0 20px; } - td.nonzero { - background: #310202; - color: #e25757; + tr.active { background: #151515; } + .inactive > * { opacity: .5; } + .effectSetting { + display: inline-block; + background: #1b1e21; + margin: 1px 3px; } - td.full { - background: #2b0000; - color: red; - font-weight: bold; + .chart { + height: 40px; + background: #222; + display: inline-flex; + align-items: flex-end; + } + .chart > div { + background: #a4a54f; + width: 8px; + margin: 0 1px; } </style> - <h3><resource-display graph="{{graph}}" uri="{{uri}}"></resource-display></h3> - <table class="borders"> + <rdfdb-synced-graph graph="{{graph}}"></rdfdb-synced-graph> + + <h1>Sequencer <a href="stats">[stats]</a></h1> + + <p>fps={{report.recentFps}}</p> + + <p>Recent frame times (best to worst): + <div class="chart"> + <template is="dom-repeat" items="{{report.recentDeltasStyle}}"> + <div style$="{{item}}"></div> + </template> + </div> + </p> + + <h2>Song</h2> + + <resource-display graph="{{graph}}" uri="{{report.song}}"></resource-display> + t={{report.roundT}} + + <h3>Notes</h3> + + <table> <tr> - <th>output attr</th> - <th>value</th> - <th>output chan</th> + <th>Note</th> + <th>Effect class</th> + <th>Effect settings</th> + <th>Devices affected</th> </tr> - <template is="dom-repeat" items="{{attrs}}"> - <tr> - <td>{{item.attr}}</td> - <td class$="{{item.valClass}}">{{item.val}} →</td> - <td>{{item.chan}}</td> + <template is="dom-repeat" items="{{report.songNotes}}"> + + <tr class$="{{item.rowClass}}"> + <td> + <resource-display graph="{{graph}}" uri="{{item.note}}"></resource-display> + </td> + <td> + <resource-display graph="{{graph}}" uri="{{item.effectClass}}"></resource-display> + </td> + <td> + <template is="dom-repeat" items="{{item.effectSettingsPairs}}"> + <span class="effectSetting"> + <resource-display graph="{{graph}}" uri="{{item.effectAttr}}"></resource-display>: {{item.value}} + </span> + </template> + </td> + <td> + {{item.devicesAffected}} + </td> </tr> </template> + </table> </template> <script> HTMLImports.whenReady(function () { - Polymer({ - is: "light9-collector-device", - properties: { - graph: {type: Object, notify: true}, - uri: {type: Object, notify: true}, - attrs: {type: Array, notify: true}, - }, - observers: [ - "initUpdates(updates)", - ], - initUpdates: function(updates) { - updates.addListener(function(msg) { - if (msg.outputAttrsSet && msg.outputAttrsSet.dev == this.uri.value) { - this.set('attrs', msg.outputAttrsSet.attrs); - this.attrs.forEach(function(row) { - row.valClass = row.val == 255 ? 'full' : (row.val ? 'nonzero' : ''); - }); - } - }.bind(this)); - }, - }); + Polymer({ + is: "light9-sequencer-ui", + properties: { + graph: {type: Object, notify: true}, + report: {type: Object, notify: true}, + }, + ready: function() { + var source = new EventSource('updates'); + source.addEventListener('message', (e) => { + const report = JSON.parse(e.data); + report.roundT = Math.floor((report.t || 0) * 1000) / 1000; + report.recentFps = Math.floor((report.recentFps || 0) * 10) / 10; + report.recentDeltasStyle = (report.recentDeltas || []).map((dt) => { + const height = Math.min(40, dt / 0.025 * 20); + return `height: ${height}px;` + }); + (report.songNotes || []).forEach((note) => { + note.rowClass = note.nonZero ? 'active' : 'inactive'; + note.effectSettingsPairs = []; + const attrs = Object.keys(note.effectSettings); + attrs.sort(); + attrs.forEach((attr) => { + note.effectSettingsPairs.push( + {effectAttr: attr, value: note.effectSettings[attr]}); + }); + }); + this.report = report; + }); + }, + }); }); </script> </dom-module> - - <dom-module id="light9-collector-ui"> - <template> - <rdfdb-synced-graph graph="{{graph}}"></rdfdb-synced-graph> - - <h1>Collector <a href="stats">[stats]</a></h1> - - <h2>Devices</h2> - <div style="column-width: 18em"> - <template is="dom-repeat" items="{{devices}}"> - <light9-collector-device - graph="{{graph}}" updates="{{updates}}" - uri="{{item}}"></light9-collector-device> - </template> - </div> - </template> - <script> - class Updates { - constructor() { - this.listeners = []; - - } - addListener(cb) { - this.listeners.push(cb); - } - onMessage(msg) { - this.listeners.forEach(function(lis) { - lis(msg); - }); - } - } - HTMLImports.whenReady(function () { - Polymer({ - is: "light9-collector-ui", - properties: { - graph: {type: Object, notify: true}, - updates: {type: Object, notify: true}, - devices: {type: Array}, - }, - observers: [ - 'onGraph(graph)', - ], - ready: function() { - this.updates = new Updates(); - var sock = new WebSocket( - window.location.href.replace(/^http/, 'ws') + 'updates'); - sock.onmessage = function(ev) { - this.updates.onMessage(JSON.parse(ev.data)); - }.bind(this); - }, - onGraph: function(graph) { - this.graph.runHandler(this.findDevices.bind(this), 'findDevices'); - }, - findDevices: function() { - var U = function(x) { - return this.graph.Uri(x); - }; - this.set('devices', []); - - let classes = this.graph.subjects(U('rdf:type'), U(':DeviceClass')); - _.uniq(_.sortBy(classes, 'value'), true).forEach(function(dc) { - _.sortBy(this.graph.subjects(U('rdf:type'), dc), 'value').forEach(function(dev) { - this.push('devices', dev); - }.bind(this)); - }.bind(this)); - } - }); - }); - </script> - </dom-module> - - list notes of song, time with note, eff attrs, function stuff - update rates, stutters from reloads. - <script> - - var source = new EventSource('updates'); - source.onmessage = function(e) { - console.log(JSON.parse(e.data)); - }; - </script> + <light9-sequencer-ui></light9-sequencer-ui> + </body> </html>
--- a/light9/effect/sequencer.py Sun Jun 03 21:04:00 2018 +0000 +++ b/light9/effect/sequencer.py Mon Jun 04 07:05:42 2018 +0000 @@ -3,16 +3,14 @@ ''' from __future__ import division -from rdflib import URIRef, Literal +from louie import dispatcher +from rdflib import URIRef from twisted.internet import reactor, defer -from webcolors import rgb_to_hex -import json, logging, bisect -import treq -import math -import time from twisted.internet.inotify import INotify from twisted.python.filepath import FilePath -from louie import dispatcher +import cyclone.sse +import json, logging, bisect, time +import treq from light9 import networking from light9.namespaces import L9, RDF @@ -134,18 +132,21 @@ """ list of (device, attr, value), and a report for web """ - report = {'note': str(self.uri)} + report = {'note': str(self.uri), + 'effectClass': self.effectEval.effect, + } effectSettings = self.baseEffectSettings.copy() effectSettings[L9['strength']] = self.evalCurve(t) report['effectSettings'] = dict( (str(k), str(v)) for k,v in sorted(effectSettings.items())) - out = self.effectEval.outputFromEffect( + report['nonZero'] = effectSettings[L9['strength']] > 0 + out, evalReport = self.effectEval.outputFromEffect( effectSettings.items(), songTime=t, # note: not using origin here since it's going away noteTime=t - self.points[0][0]) - print 'out', out.asList() + report['devicesAffected'] = len(out.devices()) return out, report @@ -211,23 +212,27 @@ @stats.update.time() def update(self): now = time.time() - self.recentUpdateTimes = self.recentUpdateTimes[-20:] + [now] + self.recentUpdateTimes = self.recentUpdateTimes[-40:] + [now] stats.recentFps = len(self.recentUpdateTimes) / (self.recentUpdateTimes[-1] - self.recentUpdateTimes[0] + .0001) - if now > self.lastStatLog + 10: - dispatcher.send('state', update={'recentFps': stats.recentFps}) + if 1 or now > self.lastStatLog + 1: + dispatcher.send('state', update={ + 'recentDeltas': sorted([round(t1 - t0, 4) for t0, t1 in + zip(self.recentUpdateTimes[:-1], + self.recentUpdateTimes[1:])]), + 'recentFps': stats.recentFps}) self.lastStatLog = now reactor.callLater(1 / self.fps, self.update) musicState = self.music.getLatest() song = URIRef(musicState['song']) if musicState.get('song') else None - dispatcher.send('state', update={'song': str(song)}) if 't' not in musicState: return t = musicState['t'] + dispatcher.send('state', update={'song': str(song), 't': t}) settings = [] - songNotes = sorted(self.notes.get(song, [])) + songNotes = sorted(self.notes.get(song, []), key=lambda n: n.uri) noteReports = [] for note in songNotes: s, report = note.outputSettings(t) @@ -236,7 +241,6 @@ dispatcher.send('state', update={'songNotes': noteReports}) self.sendToCollector(DeviceSettings.fromList(self.graph, settings)) -import cyclone.sse class Updates(cyclone.sse.SSEHandler): def __init__(self, application, request, **kwargs): cyclone.sse.SSEHandler.__init__(self, application, request, @@ -249,7 +253,6 @@ self.state.update(update) def bind(self): - print 'new client', self.settings.seq self.numConnected += 1 if self.numConnected == 1: @@ -259,9 +262,9 @@ if self.numConnected == 0: return self.sendEvent(self.state) - reactor.callLater(2, self.loop) + reactor.callLater(.1, self.loop) def unbind(self): self.numConnected -= 1 - print 'bye', self.numConnected +