Changeset - 1583e997fe0e
[Not reviewed]
default
0 1 1
Drew Perttula - 11 years ago 2014-06-09 03:55:50
drewp@bigasterisk.com
pull EffectLoop to another module
Ignore-this: 6880f3f0b2aed3107c871296ad6cfbb9
2 files changed with 110 insertions and 97 deletions:
0 comments (0 inline, 0 general)
bin/effecteval
Show inline comments
 
@@ -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
light9/effecteval/effectloop.py
Show inline comments
 
new file 100644
 
import time, json, logging, traceback
 
from twisted.internet import reactor
 
from twisted.internet.defer import inlineCallbacks, returnValue
 
from rdflib import URIRef, Literal
 
import cyclone.httpclient
 
from light9.namespaces import L9, RDF, RDFS
 
from light9.effecteval.effect import EffectNode
 
from light9 import networking
 
from light9 import Submaster
 
from light9 import dmxclient
 
log = logging.getLogger('effectloop')
 

	
 
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()))
 
        
0 comments (0 inline, 0 general)