Files @ 0761cdd5bff0
Branch filter:

Location: light9/src/light9/midifade/writeback.py

drewp@bigasterisk.com
fix delay on setting midi, so it doesn't grab faders when you're sliding them
import asyncio
from functools import partial
import logging
from dataclasses import dataclass
import time
from typing import cast

from light9.typedgraph import typedValue
import mido
from debouncer import DebounceOptions, debounce
from light9.midifade.pages import Pages
from light9.namespaces import L9
from rdfdb.syncedgraph.syncedgraph import SyncedGraph
from rdflib import Literal, URIRef

log = logging.getLogger()


class FaderSyncer:
    """get this midi output set to the right value
    
    todo: note that these could be active at the moment we switch fader pages,
    which might result in the wrong output
    """

    def __init__(self, fader: URIRef, getHwValue, getGraphValue, setHwValue, getLastMidiInTime):
        self.fader = fader
        self.getHwValue = getHwValue
        self.getGraphValue = getGraphValue
        self.setHwValue = setHwValue
        self.getLastMidiInTime = getLastMidiInTime
        self.done = False
        asyncio.create_task(self.task())

    async def task(self):
        try:
            await self._waitForPauseInInput()
            
            goal = self.getGraphValue()
            hw = self.getHwValue()
            if abs(goal - hw) > 2:
                log.info(f'hw at {hw} {goal=} -> send to hw')
                self.setHwValue(goal)
        finally:
            self.done = True

    async def _waitForPauseInInput(self):
        while True:
            if time.time() > self.getLastMidiInTime() + 1:
                return
            await asyncio.sleep(.05)



@dataclass
class WriteBackFaders:
    graph: SyncedGraph
    pages: Pages
    bcf_out: mido.ports.BaseOutput
    _lastSet: dict[int, int]
    _lastMidiInTime: dict[int, float]
    _syncer: dict[int, FaderSyncer]

    def getCurrentValue(self, f):
        return self._lastSet.get(f, 0)

    def onGraphPatch(self):
        for midi_ctl_addr, f in self.pages.getChansToFaders().items():
            fset = typedValue(URIRef, self.graph, f, L9.setting)
            self.graph.value(fset, L9.value)  # dependency for handler

            existingSyncer = self._syncer.get(midi_ctl_addr, None)
            if not existingSyncer or existingSyncer.done:
                self._syncer[midi_ctl_addr] = FaderSyncer(
                    f,
                    partial(self.getCurrentValue, midi_ctl_addr),
                    partial(self.getMidiValue, fset),
                    partial(self.sendToBcf, midi_ctl_addr),
                    partial(self._lastMidiInTime.get, midi_ctl_addr, 0),
                )

    def getMidiValue(self, fset: URIRef) -> int:
        with self.graph.currentState() as g:
            value = cast(Literal, g.value(fset, L9.value)).toPython()
            return int(value * 127)

    def sendToBcf(self, control: int, value: int):
        self._lastSet[control] = value
        msg = mido.Message('control_change', control=control, value=value)
        self.bcf_out.send(msg)