diff --git a/src/light9/midifade/eventqueue.py b/src/light9/midifade/eventqueue.py --- a/src/light9/midifade/eventqueue.py +++ b/src/light9/midifade/eventqueue.py @@ -1,9 +1,19 @@ import asyncio +from dataclasses import dataclass import logging import traceback log = logging.getLogger() +@dataclass +class Event: + dev: str + type: str + control: int + value: int + def __repr__(self): + return f"{self.value}>" + class EventQueue: """midi events come in fast; graph consumes them slower""" @@ -11,29 +21,32 @@ class EventQueue: def __init__(self, MAX_SEND_RATE: float, onMessage) -> None: self.MAX_SEND_RATE = MAX_SEND_RATE self.onMessage = onMessage - self.msgs = asyncio.Queue() + self.newEvents: asyncio.Queue[Event] = asyncio.Queue() def callbackFor(self, dev): mainThreadLoop = asyncio.get_running_loop() def cb(message): # this is running in mido's thread - log.info(f'enqueue {message} {"*" * message.dict()["value"]}') + d = message.dict() + ev = Event(dev, d['type'], d['control'], d['value']) + log.info(f'enqueue {ev} {"*" * ev.value}') mainThreadLoop.call_soon_threadsafe( - self.msgs.put_nowait, - message.dict() | {'dev': dev}, + self.newEvents.put_nowait, + ev, ) return cb async def run(self): while True: - recents = [await self.msgs.get()] - while not self.msgs.empty(): - recents.append(self.msgs.get_nowait()) + recentEvents = [await self.newEvents.get()] + while not self.newEvents.empty(): + recentEvents.append(self.newEvents.get_nowait()) + log.info(f'{recentEvents=}') try: - for msg in reduceToLatestValue(recents): - # log.info(f'handle {msg=}') + for msg in latestEventPerControl(recentEvents): + log.info(f'handle {msg=}') await self.onMessage(msg) except Exception: traceback.print_exc() @@ -41,9 +54,14 @@ class EventQueue: await asyncio.sleep(1 / self.MAX_SEND_RATE) -def reduceToLatestValue(ms: list[dict]) -> list[dict]: - merge = {} - for m in ms: - normal_key = tuple(sorted(dict((k, v) for k, v in m.items() if k != 'value'))) - merge[normal_key] = m - return list(merge.values()) +def latestEventPerControl(evs: list[Event]) -> list[Event]: + ret = [] + seenControl = set() + for ev in reversed(evs): + c = (ev.dev, ev.control) + if c in seenControl: + continue + seenControl.add(c) + ret.append(ev) + ret.reverse() + return ret diff --git a/src/light9/midifade/midifade.py b/src/light9/midifade/midifade.py --- a/src/light9/midifade/midifade.py +++ b/src/light9/midifade/midifade.py @@ -11,7 +11,7 @@ import logging import mido from light9 import networking from light9.effect.edit import clamp -from light9.midifade.eventqueue import EventQueue +from light9.midifade.eventqueue import Event, EventQueue from light9.midifade.mididevs import connectToMidiOutput, listenToMidiInputs from light9.midifade.pages import Pages from light9.namespaces import L9 @@ -44,28 +44,28 @@ def changeGrandMaster(graph: SyncedGraph graph.patchObject(ctx, L9.grandMaster, L9['value'], decimalLiteral(newValue)) -async def onMessage(graph: SyncedGraph, pages: Pages, ctx: URIRef, _lastSet: dict[int, int], m: dict): - if m['type'] == 'active_sensing': +async def onMessage(graph: SyncedGraph, pages: Pages, ctx: URIRef, _lastSet: dict[int, int], m: Event): + if m.type == 'active_sensing': return - if m['type'] == 'control_change': - if m['dev'] == 'bcf2000' and m['control'] == 91: + if m.type == 'control_change': + if m.dev == 'bcf2000' and m.control == 91: pages.changePage(-1) return - if m['dev'] == 'bcf2000' and m['control'] == 92: + if m.dev == 'bcf2000' and m.control == 92: pages.changePage(1) return - if m['dev'] == 'bcf2000' and m['control'] == 8: - changeGrandMaster(graph, clamp(m['value'] / 127 * 1.5, 0, 1), ctx) + if m.dev == 'bcf2000' and m.control == 8: + changeGrandMaster(graph, clamp(m.value / 127 * 1.5, 0, 1), ctx) return try: - fader = pages.lookupFader(m['dev'], m['control']) + fader = pages.lookupFader(m.dev, m.control) except KeyError: log.info(f'unknown control {m}') return try: - await writeHwValueToGraph(graph, ctx, fader, m['value'] / 127) - _lastSet[m['control']] = m['value'] + await writeHwValueToGraph(graph, ctx, fader, m.value / 127) + _lastSet[m.control] = m.value except ValueError as e: log.warning(f'{e!r} - ignoring') else: diff --git a/src/light9/midifade/pages.py b/src/light9/midifade/pages.py --- a/src/light9/midifade/pages.py +++ b/src/light9/midifade/pages.py @@ -70,6 +70,7 @@ class Pages: return mapping def compileCurrents(self): + log.info('compileCurrents') self.currentFaders.clear() try: new = self.getChansToFaders() @@ -78,6 +79,8 @@ class Pages: self.currentFaders.update(new) def lookupFader(self, dev: str, control: int) -> URIRef: + if not self.currentFaders: + log.warn("lookupFader called when we had no current control->fader mapping") return { 'quneo': { 44: L9['show/dance2023/fadePage1f0'],