diff --git a/bin/effecteval b/bin/effecteval --- a/bin/effecteval +++ b/bin/effecteval @@ -11,6 +11,7 @@ sys.path.append(".") from light9 import networking, showconfig, Submaster, dmxclient from light9.curvecalc.curve import CurveResource from light9.effecteval.effect import EffectNode +from light9.effecteval.effectloop import EffectLoop from light9.greplin_cyclone import StatsForCyclone from light9.namespaces import L9, RDF, RDFS from light9.rdfdb.patch import Patch @@ -122,103 +123,6 @@ class SongEffectsEval(PrettyErrorHandler self.write(maxDict(effectDmxDict(e) for e in effects)) # return dmx dict for all effects in the song, already combined -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 - self.currentSong = None - self.currentEffects = [] - self.lastLogTime = 0 - self.lastLogMsg = "" - self.graph.addHandler(self.setEffects) - self.period = 1 / 30 - self.coastSecs = .3 # main reason to keep this low is to notice play/pause - - self.songTimeFromRequest = 0 - self.requestTime = 0 # unix sec for when we fetched songTime - reactor.callLater(self.period, self.sendLevels) - - def setEffects(self): - self.currentEffects = [] - if self.currentSong is None: - return - - for effectUri in self.graph.objects(self.currentSong, L9['effect']): - self.currentEffects.append(EffectNode(self.graph, effectUri)) - - @inlineCallbacks - def getSongTime(self): - now = time.time() - if now - self.requestTime < self.coastSecs: - estimated = self.songTimeFromRequest - if self.currentSong is not None and self.currentPlaying: - estimated += now - self.requestTime - returnValue((estimated, self.currentSong)) - else: - response = json.loads((yield cyclone.httpclient.fetch( - networking.musicPlayer.path('time'))).body) - self.requestTime = now - self.currentPlaying = response['playing'] - self.songTimeFromRequest = response['t'] - returnValue( - (response['t'], (response['song'] and URIRef(response['song'])))) - - @inlineCallbacks - def sendLevels(self): - t1 = time.time() - try: - with self.stats.sendLevels.time(): - with self.stats.getMusic.time(): - songTime, song = yield self.getSongTime() - - if song != self.currentSong: - self.currentSong = song - # this may be piling on the handlers - self.graph.addHandler(self.setEffects) - - if song is None: - return - - outSubs = [] - for e in self.currentEffects: - try: - outSubs.append(e.eval(songTime)) - except Exception as exc: - log.error("effect %s: %s" % (e.uri, exc)) - out = Submaster.sub_maxes(*outSubs) - - self.logLevels(t1, out) - dmx = out.get_dmx_list() - with self.stats.writeDmx.time(): - yield dmxclient.outputlevels(dmx, twisted=True) - - elapsed = time.time() - t1 - dt = max(0, self.period - elapsed) - except Exception: - self.stats.errors += 1 - traceback.print_exc() - dt = 1 - - reactor.callLater(dt, self.sendLevels) - - 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 out.get_levels().items())) - class App(object): def __init__(self, show): self.show = show