Changeset - bf07831a5339
[Not reviewed]
default
0 5 1
drewp@bigasterisk.com - 20 months ago 2023-05-20 01:20:19
drewp@bigasterisk.com
refactor
6 files changed with 121 insertions and 132 deletions:
0 comments (0 inline, 0 general)
light9/collector/weblisteners.py
Show inline comments
 
@@ -39,8 +39,7 @@ class WebListeners(object):
 
        self.clients = [(c, t) for c, t in self.clients if c != client]
 
        log.info('delClient %s, %s left', client, len(self.clients))
 

	
 
    def outputAttrsSet(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any], outputMap: Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputUri,
 
                                                                                                                               DmxMessageIndex]]):
 
    def outputAttrsSet(self, dev: DeviceUri, attrs: Dict[OutputAttr, Any], outputMap: Dict[Tuple[DeviceUri, OutputAttr], Tuple[OutputUri, DmxMessageIndex]]):
 
        """called often- don't be slow"""
 
        self.pendingMessageForDev[dev] = (attrs, outputMap)
 
        # maybe put on a stack for flusher or something
light9/effect/effecteval.py
Show inline comments
 
@@ -20,10 +20,10 @@ log.info("reload effecteval")
 

	
 

	
 
def literalColor(rnorm, gnorm, bnorm):
 
    return Literal(
 
        rgb_to_hex([int(rnorm * 255),
 
                    int(gnorm * 255),
 
                    int(bnorm * 255)]))
 
    return Literal(rgb_to_hex((
 
        int(rnorm * 255),  #
 
        int(gnorm * 255),  # 
 
        int(bnorm * 255))))
 

	
 

	
 
def literalColorHsv(h, s, v):
 
@@ -82,9 +82,7 @@ class EffectEval(object):
 
        the effect code.
 
        """
 
        # both callers need to apply note overrides
 
        effectSettings = dict(
 
            effectSettings
 
        )  # we should make everything into nice float and Color objects too
 
        effectSettings = dict(effectSettings)  # we should make everything into nice float and Color objects too
 

	
 
        strength = float(effectSettings[L9['strength']])
 
        if strength <= 0:
 
@@ -93,10 +91,7 @@ class EffectEval(object):
 
        report = {}
 
        out: Dict[Tuple[URIRef, URIRef], Any] = {}  # (dev, attr): value
 

	
 
        out.update(
 
            self.simpleOutputs.values(
 
                self.effect, strength,
 
                effectSettings.get(L9['colorScale'], None)))
 
        out.update(self.simpleOutputs.values(self.effect, strength, effectSettings.get(L9['colorScale'], None)))
 

	
 
        if self.effect.startswith(L9['effect/']):
 
            tail = 'effect_' + self.effect[len(L9['effect/']):]
 
@@ -112,9 +107,7 @@ class EffectEval(object):
 

	
 

	
 
def effect_Curtain(effectSettings, strength, songTime, noteTime):
 
    return {(L9['device/lowPattern%s' % n], L9['color']):
 
            literalColor(strength, strength, strength)
 
            for n in range(301, 308 + 1)}
 
    return {(L9['device/lowPattern%s' % n], L9['color']): literalColor(strength, strength, strength) for n in range(301, 308 + 1)}
 

	
 

	
 
def effect_animRainbow(effectSettings, strength, songTime, noteTime):
 
@@ -124,10 +117,8 @@ def effect_animRainbow(effectSettings, s
 
    tr, tg, tb = hex_to_rgb(tint)
 
    for n in range(1, 5 + 1):
 
        scl = strength * nsin(songTime + n * .3)**3
 
        col = literalColor(
 
            scl * lerp(nsin(songTime + n * .2), tr / 255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .2 + .3), tg / 255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .3 + .6), tb / 255, tintStrength))
 
        col = literalColor(scl * lerp(nsin(songTime + n * .2), tr / 255, tintStrength), scl * lerp(nsin(songTime + n * .2 + .3), tg / 255, tintStrength),
 
                           scl * lerp(nsin(songTime + n * .3 + .6), tb / 255, tintStrength))
 

	
 
        dev = L9['device/aura%s' % n]
 
        out.update({
 
@@ -136,10 +127,8 @@ def effect_animRainbow(effectSettings, s
 
        })
 
        ang = songTime * 4
 
        out.update({
 
            (dev, L9['rx']):
 
            lerp(.27, .7, (n - 1) / 4) + .2 * math.sin(ang + n),
 
            (dev, L9['ry']):
 
            lerp(.46, .52, (n - 1) / 4) + .5 * math.cos(ang + n),
 
            (dev, L9['rx']): lerp(.27, .7, (n - 1) / 4) + .2 * math.sin(ang + n),
 
            (dev, L9['ry']): lerp(.46, .52, (n - 1) / 4) + .5 * math.cos(ang + n),
 
        })
 
    return out
 

	
 
@@ -160,10 +149,8 @@ def effect_auraSparkles(effectSettings, 
 
        })
 
        ang = songTime * 4
 
        out.update({
 
            (dev, L9['rx']):
 
            lerp(.27, .8, (n - 1) / 4) + .2 * math.sin(ang + n),
 
            (dev, L9['ry']):
 
            lerp(.46, .52, (n - 1) / 4) + .4 * math.cos(ang + n),
 
            (dev, L9['rx']): lerp(.27, .8, (n - 1) / 4) + .2 * math.sin(ang + n),
 
            (dev, L9['ry']): lerp(.46, .52, (n - 1) / 4) + .4 * math.cos(ang + n),
 
        })
 
    return out
 

	
 
@@ -171,8 +158,7 @@ def effect_auraSparkles(effectSettings, 
 
def effect_qpan(effectSettings, strength, songTime, noteTime):
 
    dev = L9['device/q2']
 
    dur = 4
 
    col = scale(scale('#ffffff', strength),
 
                effectSettings.get(L9['colorScale']) or '#ffffff')
 
    col = scale(scale('#ffffff', strength), effectSettings.get(L9['colorScale']) or '#ffffff')
 
    return {
 
        (dev, L9['color']): col,
 
        (dev, L9['focus']): 0.589,
 
@@ -189,10 +175,8 @@ def effect_pulseRainbow(effectSettings, 
 
    tr, tg, tb = hex_to_rgb(tint)
 
    for n in range(1, 5 + 1):
 
        scl = strength
 
        col = literalColor(
 
            scl * lerp(nsin(songTime + n * .2), tr / 255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .2 + .3), tg / 255, tintStrength),
 
            scl * lerp(nsin(songTime + n * .3 + .6), tb / 255, tintStrength))
 
        col = literalColor(scl * lerp(nsin(songTime + n * .2), tr / 255, tintStrength), scl * lerp(nsin(songTime + n * .2 + .3), tg / 255, tintStrength),
 
                           scl * lerp(nsin(songTime + n * .3 + .6), tb / 255, tintStrength))
 

	
 
        dev = L9['device/aura%s' % n]
 
        out.update({
 
@@ -248,10 +232,8 @@ def effect_qsweep(effectSettings, streng
 
            (dev, L9['zoom']): effectSettings.get(L9['zoom'], .5),
 
        })
 
        out.update({
 
            (dev, L9['rx']):
 
            lerp(.3, .8, nsin(songTime / period + n / 4)),
 
            (dev, L9['ry']):
 
            effectSettings.get(L9['ry'], .2),
 
            (dev, L9['rx']): lerp(.3, .8, nsin(songTime / period + n / 4)),
 
            (dev, L9['ry']): effectSettings.get(L9['ry'], .2),
 
        })
 
    return out
 

	
 
@@ -269,16 +251,12 @@ def effect_qsweepusa(effectSettings, str
 
    for n in range(1, 3 + 1):
 
        dev = L9['device/q%s' % n]
 
        out.update({
 
            (dev, L9['color']):
 
            scale(colmap[n], effectSettings.get(L9['strength'], 1)),
 
            (dev, L9['zoom']):
 
            effectSettings.get(L9['zoom'], .5),
 
            (dev, L9['color']): scale(colmap[n], effectSettings.get(L9['strength'], 1)),
 
            (dev, L9['zoom']): effectSettings.get(L9['zoom'], .5),
 
        })
 
        out.update({
 
            (dev, L9['rx']):
 
            lerp(.3, .8, nsin(songTime / period + n / 4)),
 
            (dev, L9['ry']):
 
            effectSettings.get(L9['ry'], .5),
 
            (dev, L9['rx']): lerp(.3, .8, nsin(songTime / period + n / 4)),
 
            (dev, L9['ry']): effectSettings.get(L9['ry'], .5),
 
        })
 
    return out
 

	
 
@@ -379,21 +357,18 @@ def effect_Strobe(effectSettings, streng
 
    offset = 0
 
    f = (((songTime + offset) * rate) % 1.0)
 
    c = (f < duty) * strength
 
    col = rgb_to_hex([int(c * 255), int(c * 255), int(c * 255)])
 
    col = rgb_to_hex((int(c * 255), int(c * 255), int(c * 255)))
 
    return {(L9['device/colorStrip'], L9['color']): Literal(col)}
 

	
 

	
 
def effect_lightning(effectSettings, strength, songTime, noteTime):
 
    devs = [
 
        L9['device/veryLow1'], L9['device/veryLow2'], L9['device/veryLow3'],
 
        L9['device/veryLow4'], L9['device/veryLow5'], L9['device/backlight1'],
 
        L9['device/backlight2'], L9['device/backlight3'],
 
        L9['device/backlight4'], L9['device/backlight5'], L9['device/down2'],
 
        L9['device/down3'], L9['device/down4'], L9['device/hexLow3'],
 
        L9['device/hexLow5'], L9['device/postL1'], L9['device/postR1']
 
        L9['device/veryLow1'], L9['device/veryLow2'], L9['device/veryLow3'], L9['device/veryLow4'], L9['device/veryLow5'], L9['device/backlight1'],
 
        L9['device/backlight2'], L9['device/backlight3'], L9['device/backlight4'], L9['device/backlight5'], L9['device/down2'], L9['device/down3'],
 
        L9['device/down4'], L9['device/hexLow3'], L9['device/hexLow5'], L9['device/postL1'], L9['device/postR1']
 
    ]
 
    out = {}
 
    col = rgb_to_hex([int(255 * strength)] * 3)
 
    col = rgb_to_hex((int(255 * strength),) * 3)
 
    for i, dev in enumerate(devs):
 
        n = noise(songTime * 8 + i * 6.543)
 
        if n > .4:
 
@@ -452,7 +427,7 @@ def effect_image(effectSettings, strengt
 
            out[(SKY[f'cycGreen{column}'], L9['brightness'])] = color[1]
 
            out[(SKY[f'cycBlue{column}'], L9['brightness'])] = color[2]
 
        else:
 
            out[(dev, L9['color'])] = rgb_to_hex(map(_8bit, color))
 
            out[(dev, L9['color'])] = rgb_to_hex(tuple(map(_8bit, color)))
 
    return out
 

	
 

	
 
@@ -527,13 +502,7 @@ def effect_parNoise(effectSettings, stre
 
    gamma = .6
 
    for dev in [SKY['strip1'], SKY['strip2'], SKY['strip3']]:
 
        out[(dev, L9['color'])] = scale(
 
            rgb_to_hex(
 
                (_8bit(r * math.pow(max(.01, noise(speed * songTime)), gamma)),
 
                 _8bit(g *
 
                       math.pow(max(.01, noise(speed * songTime + 10)), gamma)),
 
                 _8bit(
 
                     b *
 
                     math.pow(max(.01, noise(speed * songTime + 20)), gamma)))),
 
            strength)
 
            rgb_to_hex((_8bit(r * math.pow(max(.01, noise(speed * songTime)), gamma)), _8bit(g * math.pow(max(.01, noise(speed * songTime + 10)), gamma)),
 
                        _8bit(b * math.pow(max(.01, noise(speed * songTime + 20)), gamma)))), strength)
 

	
 
    return out
light9/effect/sequencer/eval_faders.py
Show inline comments
 
new file 100644
 

	
 
import asyncio
 
import logging
 
import time
 
from typing import Callable, Coroutine, List, cast
 
from light9.effect.sequencer.sequencer import Note
 

	
 
from rdfdb.syncedgraph.syncedgraph import SyncedGraph
 
from rdflib import URIRef
 

	
 
from light9.effect import effecteval
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.simple_outputs import SimpleOutputs
 
from light9.metrics import metrics
 
from light9.namespaces import L9, RDF
 
from light9.newtypes import NoteUri
 

	
 
log = logging.getLogger('sequencer')
 
class FaderEval:
 
    """peer to Sequencer, but this one takes the current :Fader settings -> sendToCollector
 
    
 
    The current faders become Notes in here, for more code reuse.
 
    """
 
    def __init__(self,
 
                 graph: SyncedGraph,
 
                 sendToCollector: Callable[[DeviceSettings], Coroutine[None ,None,None]],
 
                 ):
 
        self.graph = graph
 
        self.sendToCollector = sendToCollector
 

	
 
        # Notes without times- always on
 
        self.notes: List[Note] = []
 

	
 
        self.simpleOutputs = SimpleOutputs(self.graph)
 
        self.graph.addHandler(self.compileGraph)
 
        self.lastLoopSucceeded = False
 

	
 
        # self.codeWatcher = CodeWatcher(onChange=self.onCodeChange)
 
        log.info('startupdating task')
 
        asyncio.create_task(self.startUpdating())
 

	
 
    async def startUpdating(self):
 
        await self.graph.addAsyncHandler(self.update)
 
        log.info('startupdating task done')
 

	
 
    def onCodeChange(self):
 
        log.debug('seq.onCodeChange')
 
        self.graph.addHandler(self.compileGraph)
 
        #self.updateLoop()
 

	
 
    @metrics('compile_graph_fader').time()
 
    def compileGraph(self) -> None:
 
        """rebuild our data from the graph"""
 
        self.notes = []
 
        for fader in self.graph.subjects(RDF.type, L9['Fader']):          
 
            def compileFader() -> Note:
 
                return self.compileFader(cast(URIRef, fader))
 

	
 
            self.notes.append(compileFader())
 
        if self.notes:
 
            asyncio.create_task(self.startUpdating())
 

	
 

	
 
    @metrics('compile_fader').time()
 
    def compileFader(self, fader: URIRef) -> Note:
 
        return Note(self.graph, NoteUri(cast(NoteUri, fader)), effecteval,
 
                self.simpleOutputs, timed=False)
 
    
 
    @metrics('update_call_fader').time()
 
    async def update(self):
 
        settings = []
 
        for note in self.notes:
 
            effectValue = self.graph.value(note.uri, L9['value'])
 
            if effectValue is None:
 
                log.info(f'skip note {note}, no :value')
 
                continue
 
            s, report = note.outputSettings(t=time.time(), strength=float(effectValue))
 
            settings.append(s)
 
        devSettings = DeviceSettings.fromList(self.graph, settings)
 
        with metrics('update_s3_send_fader').time():  # our measurement
 
            sendSecs = await self.sendToCollector(devSettings)
light9/effect/sequencer/sequencer.py
Show inline comments
 
@@ -158,7 +158,7 @@ class CodeWatcher(object):
 
            self.onChange()
 

	
 
        # in case we got an event at the start of the write
 
        reactor.callLater(.1, go)
 
        reactor.callLater(.1, go) # type: ignore
 

	
 

	
 
class Sequencer(object):
 
@@ -173,7 +173,7 @@ class Sequencer(object):
 
        metrics('update_loop_goal_fps').set(self.fps)
 
        metrics('update_loop_goal_latency').set(1 / self.fps)
 
        self.sendToCollector = sendToCollector
 
        self.music = MusicTime(period=.2, pollCurvecalc=False)
 
        self.music = MusicTime(period=.2)
 

	
 
        self.recentUpdateTimes: List[float] = []
 
        self.lastStatLog = 0.0
 
@@ -278,67 +278,3 @@ class Sequencer(object):
 
        # (sometimes it's None, not sure why, and neither is mypy)
 
        #if isinstance(sendSecs, float):
 
        #    metrics('update_s3_send_client').observe(sendSecs)
 

	
 
class FaderEval:
 
    """peer to Sequencer, but this one takes the current :Fader settings -> sendToCollector
 
    
 
    The current faders become Notes in here, for more code reuse.
 
    """
 
    def __init__(self,
 
                 graph: SyncedGraph,
 
                 sendToCollector: Callable[[DeviceSettings], Coroutine[None ,None,None]],
 
                 ):
 
        self.graph = graph
 
        self.sendToCollector = sendToCollector
 

	
 
        # Notes without times- always on
 
        self.notes: List[Note] = []
 

	
 
        self.simpleOutputs = SimpleOutputs(self.graph)
 
        self.graph.addHandler(self.compileGraph)
 
        self.lastLoopSucceeded = False
 

	
 
        # self.codeWatcher = CodeWatcher(onChange=self.onCodeChange)
 
        log.info('startupdating task')
 
        asyncio.create_task(self.startUpdating())
 

	
 
    async def startUpdating(self):
 
        await self.graph.addAsyncHandler(self.update)
 
        log.info('startupdating task done')
 

	
 
    def onCodeChange(self):
 
        log.debug('seq.onCodeChange')
 
        self.graph.addHandler(self.compileGraph)
 
        #self.updateLoop()
 

	
 
    @metrics('compile_graph_fader').time()
 
    def compileGraph(self) -> None:
 
        """rebuild our data from the graph"""
 
        self.notes = []
 
        for fader in self.graph.subjects(RDF.type, L9['Fader']):          
 
            def compileFader() -> Note:
 
                return self.compileFader(cast(URIRef, fader))
 

	
 
            self.notes.append(compileFader())
 
        if self.notes:
 
            asyncio.create_task(self.startUpdating())
 

	
 

	
 
    @metrics('compile_fader').time()
 
    def compileFader(self, fader: URIRef) -> Note:
 
        return Note(self.graph, NoteUri(cast(NoteUri, fader)), effecteval,
 
                self.simpleOutputs, timed=False)
 
    
 
    @metrics('update_call_fader').time()
 
    async def update(self):
 
        settings = []
 
        for note in self.notes:
 
            effectValue = self.graph.value(note.uri, L9['value'])
 
            if effectValue is None:
 
                log.info(f'skip note {note}, no :value')
 
                continue
 
            s, report = note.outputSettings(t=time.time(), strength=float(effectValue))
 
            settings.append(s)
 
        devSettings = DeviceSettings.fromList(self.graph, settings)
 
        with metrics('update_s3_send_fader').time():  # our measurement
 
            sendSecs = await self.sendToCollector(devSettings)
light9/effect/sequencer/service.py
Show inline comments
 
@@ -9,7 +9,8 @@ import time
 

	
 
from light9 import networking
 
from light9.collector.collector_client_asyncio import sendToCollector
 
from light9.effect.sequencer.sequencer import FaderEval, Sequencer, StateUpdate
 
from light9.effect.sequencer.eval_faders import FaderEval
 
from light9.effect.sequencer.sequencer import Sequencer, StateUpdate
 
from light9.effect.settings import DeviceSettings
 
from light9.metrics import metrics
 
from light9.run_local import log
light9/effect/simple_outputs.py
Show inline comments
 
@@ -6,13 +6,16 @@ from rdflib import URIRef
 

	
 

	
 
class SimpleOutputs(object):
 
    """
 
    Watches graph for effects that are just fading output attrs. 
 
    Call `values` to get (dev,attr):value settings.
 
    """
 

	
 
    def __init__(self, graph):
 
        self.graph = graph
 

	
 
        # effect : [(dev, attr, value, isScaled)]
 
        self.effectOutputs: Dict[URIRef, List[
 
            Tuple[URIRef, URIRef, Any, bool]]] = {}
 
        self.effectOutputs: Dict[URIRef, List[Tuple[URIRef, URIRef, Any, bool]]] = {}
 

	
 
        self.graph.addHandler(self.updateEffectsFromGraph)
 

	
0 comments (0 inline, 0 general)