Mercurial > code > home > repos > light9
view light9/effect/sequencer.py @ 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 | 6906cacaa218 |
line wrap: on
line source
''' copies from effectloop.py, which this should replace ''' from __future__ import division from rdflib import URIRef, Literal from twisted.internet import reactor 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 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, 'client': client, 'clientSession': session})) class Note(object): def __init__(self, graph, uri, effectevalModule): g = self.graph = graph self.uri = uri 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 = [] for curve in g.objects(uri, L9['curve']): if g.value(curve, L9['attr']) != L9['strength']: continue for point in g.objects(curve, L9['point']): self.points.append(( originTime + floatVal(point, L9['time']), floatVal(point, L9['value']))) self.points.sort() def activeAt(self, t): return self.points[0][0] <= t <= self.points[-1][0] def evalCurve(self, t): i = bisect.bisect_left(self.points, (t, None)) - 1 if i == -1: return self.points[0][1] if self.points[i][0] > t: return self.points[i][1] if i >= len(self.points) - 1: return self.points[i][1] p1, p2 = self.points[i], self.points[i + 1] frac = (t - p1[0]) / (p2[0] - p1[0]) y = p1[1] + (p2[1] - p1[1]) * frac return y def outputSettings(self, t): """ list of (device, attr, value) """ effectSettings = [(L9['strength'], self.evalCurve(t))] return self.effectEval.outputFromEffect(effectSettings, t) 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, 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 reactor.callLater(1/50, self.update) musicState = self.music.getLatest() song = URIRef(musicState['song']) if musicState.get('song') else None if 't' not in musicState: return t = musicState['t'] settings = [] for note in self.notes.get(song, []): # we have to send zeros to make past settings go # 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 settings.extend(outs) self.sendToCollector(settings)