Changeset - ce8b66e68cd5
[Not reviewed]
default
0 3 0
drewp@bigasterisk.com - 3 years ago 2022-05-31 07:12:51
drewp@bigasterisk.com
type fixes
3 files changed with 9 insertions and 8 deletions:
0 comments (0 inline, 0 general)
light9/effect/sequencer/sequencer.py
Show inline comments
 
'''
 
copies from effectloop.py, which this should replace
 
'''
 

	
 
from louie import dispatcher
 
from rdflib import URIRef
 
from twisted.internet import reactor
 
from twisted.internet import defer
 
from twisted.internet.defer import Deferred, inlineCallbacks
 
from twisted.internet.inotify import INotify
 
from twisted.python.filepath import FilePath
 
import cyclone.sse
 
import logging, bisect, time
 
import traceback
 
from decimal import Decimal
 
from typing import Any, Callable, Dict, List, Tuple, cast, Union
 

	
 
from light9.ascoltami.musictime_client import MusicTime
 
from light9.effect import effecteval
 
from light9.effect.settings import DeviceSettings
 
from light9.effect.simple_outputs import SimpleOutputs
 
from light9.namespaces import L9, RDF
 
from light9.newtypes import DeviceUri, DeviceAttr, NoteUri, Curve, Song
 
from rdfdb.syncedgraph import SyncedGraph
 
from rdfdb.syncedgraph.syncedgraph import SyncedGraph
 
from light9.metrics import metrics
 

	
 
import imp
 

	
 
log = logging.getLogger('sequencer')
 

	
 

	
 
def pyType(n):
 
    ret = n.toPython()
 
    if isinstance(ret, Decimal):
 
        return float(ret)
 
    return ret
 

	
 

	
 
class Note(object):
 

	
 
    def __init__(self, graph: SyncedGraph, uri: NoteUri, effectevalModule,
 
                 simpleOutputs):
 
        g = self.graph = graph
 
        self.uri = uri
 
        self.effectEval = effectevalModule.EffectEval(
 
            graph, g.value(uri, L9['effectClass']), simpleOutputs)
 
        self.baseEffectSettings: Dict[URIRef, Any] = {}  # {effectAttr: value}
 
        for s in g.objects(uri, L9['setting']):
 
@@ -81,59 +81,59 @@ class Note(object):
 
        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: float) -> Tuple[List[Tuple[DeviceUri, DeviceAttr, float]], Dict]:
 
        """
 
        list of (device, attr, value), and a report for web
 
        """
 
        report = {
 
            'note': str(self.uri),
 
            'effectClass': self.effectEval.effect,
 
        }
 
        effectSettings: Dict[DeviceAttr, Union[float, str]] = dict(
 
            (DeviceAttr(da), v) for da, v in self.baseEffectSettings.items())
 
        effectSettings[L9['strength']] = self.evalCurve(t)
 
        effectSettings[cast(DeviceAttr, L9['strength'])] = self.evalCurve(t)
 

	
 
        def prettyFormat(x: Union[float, str]):
 
            if isinstance(x, float):
 
                return round(x, 4)
 
            return x
 

	
 
        report['effectSettings'] = dict(
 
            (str(k), prettyFormat(v))
 
            for k, v in sorted(effectSettings.items()))
 
        report['nonZero'] = cast(float, effectSettings[L9['strength']]) > 0
 
        report['nonZero'] = cast(float, effectSettings[cast(DeviceAttr, L9['strength'])]) > 0
 
        out, evalReport = self.effectEval.outputFromEffect(
 
            list(effectSettings.items()),
 
            songTime=t,
 
            # note: not using origin here since it's going away
 
            noteTime=t - self.points[0][0])
 
        report['devicesAffected'] = len(out.devices())
 
        return out, report
 

	
 

	
 
class CodeWatcher(object):
 

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

	
 
        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():
 
            log.info("reload effecteval")
 
@@ -168,49 +168,49 @@ class Sequencer(object):
 
        self.codeWatcher = CodeWatcher(onChange=self.onCodeChange)
 
        self.updateLoop()
 

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

	
 
    @metrics('compile_graph').time()
 
    def compileGraph(self) -> None:
 
        """rebuild our data from the graph"""
 
        for song in self.graph.subjects(RDF.type, L9['Song']):
 

	
 
            def compileSong(song: Song = cast(Song, song)) -> None:
 
                self.compileSong(song)
 

	
 
            self.graph.addHandler(compileSong)
 

	
 
    @metrics('compile_song').time()
 
    def compileSong(self, song: Song) -> None:
 
        anyErrors = False
 
        self.notes[song] = []
 
        for note in self.graph.objects(song, L9['note']):
 
            try:
 
                n = Note(self.graph, NoteUri(note), effecteval,
 
                n = Note(self.graph, NoteUri(cast(NoteUri, note)), effecteval,
 
                         self.simpleOutputs)
 
            except Exception:
 
                log.warn(f"failed to build Note {note} - skipping")
 
                anyErrors = True
 
                continue
 
            self.notes[song].append(n)
 
        if not anyErrors:
 
            log.info('built all notes')
 

	
 
    @inlineCallbacks
 
    def updateLoop(self) -> None:
 
        frameStart = time.time()
 
        try:
 
            sec = yield self.update()
 
        except Exception as e:
 
            self.lastLoopSucceeded = False
 
            traceback.print_exc()
 
            log.warn('updateLoop: %r', e)
 
            reactor.callLater(1, self.updateLoop)
 
        else:
 
            took = time.time() - frameStart
 
            metrics('update_loop_latency').observe(took)
 

	
 
            if not self.lastLoopSucceeded:
light9/effect/sequencer/service.py
Show inline comments
 
"""
 
plays back effect notes from the timeline
 
"""
 

	
 
from run_local import log
 
import functools
 
from light9.run_local import log
 
from twisted.internet import reactor
 
from light9.metrics import metrics, metricsRoute
 
from rdfdb.syncedgraph import SyncedGraph
 
from rdfdb.syncedgraph.syncedgraph import SyncedGraph
 
from light9 import networking, showconfig
 
import optparse, sys, logging
 
import cyclone.web
 
from rdflib import URIRef
 
from light9.effect.sequencer.sequencer import Sequencer, Updates
 
from light9.effect.sequencer.sequencer import Sequencer
 
from light9.collector.collector_client import sendToCollector
 

	
 
from light9 import clientsession
 

	
 

	
 
class App(object):
 

	
 
    def __init__(self, show, session):
 
        self.show = show
 
        self.session = session
 

	
 
        self.graph = SyncedGraph(networking.rdfdb.url, "effectSequencer")
 
        self.graph.initiallySynced.addCallback(self.launch)
 

	
 
    def launch(self, *args):
 
        self.seq = Sequencer(
 
            self.graph,
 
            lambda settings: sendToCollector(
 
                'effectSequencer',
 
                self.session,
 
                settings,
 
                # This seems to be safe here (and lets us get from
 
                # 20fpx to 40fpx), even though it leads to big stalls
 
                # if I use it on KC.
light9/effect/sequencer/web/vite.config.ts
Show inline comments
 
import { defineConfig } from "vite";
 

	
 
const servicePort = 8213;
 
export default defineConfig({
 
  base: "/effectSeequencer/",
 
  base: "/effectSequencer/",
 
  root: "./light9/effect/sequencer/web",
 
  publicDir: "../web",
 
  server: {
 
    host: "0.0.0.0",
 
    strictPort: true,
 
    port: servicePort + 100,
 
    hmr: {
 
      port: servicePort + 200,
 
    },
 
  },
 
  clearScreen: false,
 
  define: {
 
    global: {},
 
  },
 
});
0 comments (0 inline, 0 general)