# HG changeset patch # User Drew Perttula # Date 2019-06-06 11:59:44 # Node ID f77dfa8e82b93681c0502ca820f10ae4557b8c24 # Parent 8e2d456b3612d2c1dda3c437366d111886f5f1d3 new 'image' effect for animating light colors Ignore-this: 8df08111173bd96ff8d8093d4243c3ba diff --git a/light9/effect/effecteval.py b/light9/effect/effecteval.py --- a/light9/effect/effecteval.py +++ b/light9/effect/effecteval.py @@ -8,6 +8,7 @@ import logging from light9.effect.settings import DeviceSettings from light9.effect.scale import scale from typing import Dict, Tuple, Any +from PIL import Image import random random.seed(0) @@ -385,3 +386,27 @@ def effect_lightning(effectSettings, str if n > .4: out[(dev, L9['color'])] = col return out + +def sample(img, x, y, repeat=False): + if 0 <= x < img.width: + return img.getpixel((x, y)) + elif not repeat: + return (0, 0, 0) + else: + return img.getpixel((x % img.width, y)) + +def effect_image(effectSettings, strength, songTime, noteTime): + out = {} + imgPath = f'cur/anim/{effectSettings[L9["image"]]}' + t_offset = effectSettings.get(L9['tOffset'], 0) + pxPerSec = effectSettings.get(L9['pxPerSec'], 30) + img = Image.open(imgPath) + x = (noteTime * pxPerSec) + + scl = effectSettings.get(L9['strength'], 1) + for dev, y in [(L9['theater/skyline/device/strip1'], 0), + (L9['theater/skyline/device/strip2'], 1), + (L9['theater/skyline/device/strip3'], 2)]: + color = sample(img, x, y, effectSettings.get(L9['repeat'], False)) + out[(dev, L9['color'])] = scale(rgb_to_hex(color), scl) + return out diff --git a/light9/effect/sequencer.py b/light9/effect/sequencer.py --- a/light9/effect/sequencer.py +++ b/light9/effect/sequencer.py @@ -12,6 +12,7 @@ from twisted.python.filepath import File 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 @@ -47,6 +48,13 @@ compileStats = scales.collection( ) +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, @@ -59,7 +67,7 @@ class Note(object): for s in g.objects(uri, L9['setting']): settingValues = dict(g.predicate_objects(s)) ea = settingValues[L9['effectAttr']] - self.baseEffectSettings[ea] = settingValues[L9['value']] + self.baseEffectSettings[ea] = pyType(settingValues[L9['value']]) def floatVal(s, p): return float(g.value(s, p).toPython()) @@ -112,7 +120,7 @@ class Note(object): 'effectClass': self.effectEval.effect, } effectSettings: Dict[DeviceAttr, Union[float, str]] = dict( - (DeviceAttr(da), v.toPython()) + (DeviceAttr(da), v) for da, v in self.baseEffectSettings.items()) effectSettings[L9['strength']] = self.evalCurve(t) @@ -234,6 +242,7 @@ class Sequencer(object): @updateStats.updateFps.rate() @inlineCallbacks def update(self) -> Deferred: + with updateStats.s0_getMusic.time(): musicState = self.music.getLatest() if not musicState.get('song') or not isinstance(