diff --git a/voicemeeterlib/base.py b/voicemeeterlib/base.py index 75cbe86..199e9c3 100644 --- a/voicemeeterlib/base.py +++ b/voicemeeterlib/base.py @@ -9,7 +9,7 @@ from .cbindings import CBindings from .error import CAPIError, VMError from .inst import bits from .kinds import KindId -from .misc import Midi +from .misc import Event, Midi from .subject import Subject from .util import comp, grouper, polling, script @@ -20,15 +20,18 @@ class Remote(CBindings): DELAY = 0.001 def __init__(self, **kwargs): + self.strip_mode = 0 self.cache = {} + self.cache["strip_level"], self.cache["bus_level"] = self._get_levels() self.midi = Midi() self.subject = Subject() - self.strip_mode = 0 self.running = None for attr, val in kwargs.items(): setattr(self, attr, val) + self.event = Event(self.subs) + def __enter__(self) -> Self: """setup procedures""" self.login() @@ -43,6 +46,7 @@ class Remote(CBindings): def init_thread(self): """Starts updates thread.""" self.running = True + print(f"Listening for {', '.join(self.event.get())} events") t = Thread(target=self._updates, daemon=True) t.start() @@ -54,14 +58,14 @@ class Remote(CBindings): Runs updates at a rate of self.ratelimit. """ - self.cache["strip_level"], self.cache["bus_level"] = self._get_levels() - while self.running: - if self.pdirty: + if self.event.pdirty and self.pdirty: self.subject.notify("pdirty") - if self.mdirty: + if self.event.mdirty and self.mdirty: self.subject.notify("mdirty") - if self.ldirty: + if self.event.midi and self.get_midi_message(): + self.subject.notify("midi") + if self.event.ldirty and self.ldirty: self._strip_comp, self._bus_comp = ( tuple( not x for x in comp(self.cache["strip_level"], self._strip_buf) @@ -71,10 +75,8 @@ class Remote(CBindings): self.cache["strip_level"] = self._strip_buf self.cache["bus_level"] = self._bus_buf self.subject.notify("ldirty") - if self.get_midi_message(): - self.subject.notify("midi") - time.sleep(self.ratelimit) + time.sleep(self.ratelimit if self.event.any() else 0.5) def login(self) -> NoReturn: """Login to the API, initialize dirty parameters""" diff --git a/voicemeeterlib/bus.py b/voicemeeterlib/bus.py index 51a8447..14b2891 100644 --- a/voicemeeterlib/bus.py +++ b/voicemeeterlib/bus.py @@ -161,13 +161,15 @@ class BusLevel(IRemote): """ Returns a tuple of level values for the channel. - If observables thread running fetch values from cache otherwise call CAPI func. + If observables thread running and level updates are subscribed to, fetch values from cache + + Otherwise call CAPI func. """ def fget(x): return round(20 * log(x, 10), 1) if x > 0 else -200.0 - if self._remote.running and "bus_level" in self._remote.cache: + if self._remote.running and self._remote.event.ldirty: vals = self._remote.cache["bus_level"][self.range[0] : self.range[-1]] else: vals = [self._remote.get_level(mode, i) for i in range(*self.range)] diff --git a/voicemeeterlib/factory.py b/voicemeeterlib/factory.py index 403faba..7e1eb90 100644 --- a/voicemeeterlib/factory.py +++ b/voicemeeterlib/factory.py @@ -102,7 +102,10 @@ class FactoryBase(Remote): """Base class for factories, subclasses Remote.""" def __init__(self, kind_id: str, **kwargs): - defaultkwargs = {"sync": False, "ratelimit": 0.033} + defaultevents = {"pdirty": True, "mdirty": True, "midi": True, "ldirty": False} + if "subs" in kwargs: + defaultevents = defaultevents | kwargs.pop("subs") + defaultkwargs = {"sync": False, "ratelimit": 0.033, "subs": defaultevents} kwargs = defaultkwargs | kwargs self.kind = kindmap(kind_id) super().__init__(**kwargs) diff --git a/voicemeeterlib/misc.py b/voicemeeterlib/misc.py index a63c0d2..1ae1bcc 100644 --- a/voicemeeterlib/misc.py +++ b/voicemeeterlib/misc.py @@ -250,3 +250,45 @@ class Midi: def _set(self, key: int, velocity: int): self.cache[key] = velocity + + +class Event: + def __init__(self, subs: dict): + self.subs = subs + + def info(self, msg): + info = ( + f"{msg} events", + f"Now listening for {', '.join(self.get())} events", + ) + print("\n".join(info)) + + @property + def pdirty(self): + return self.subs["pdirty"] + + @property + def mdirty(self): + return self.subs["mdirty"] + + @property + def midi(self): + return self.subs["midi"] + + @property + def ldirty(self): + return self.subs["ldirty"] + + def get(self) -> list: + return [k for k, v in self.subs.items() if v] + + def any(self) -> bool: + return any(self.subs.values()) + + def add(self, event): + self.subs[event] = True + self.info(f"{event} added to") + + def remove(self, event): + self.subs[event] = False + self.info(f"{event} removed from") diff --git a/voicemeeterlib/strip.py b/voicemeeterlib/strip.py index 4b407d2..689a966 100644 --- a/voicemeeterlib/strip.py +++ b/voicemeeterlib/strip.py @@ -193,13 +193,15 @@ class StripLevel(IRemote): """ Returns a tuple of level values for the channel. - If observables thread running fetch values from cache otherwise call CAPI func. + If observables thread running and level updates are subscribed to, fetch values from cache + + Otherwise call CAPI func. """ def fget(x): return round(20 * log(x, 10), 1) if x > 0 else -200.0 - if self._remote.running and "strip_level" in self._remote.cache: + if self._remote.running and self._remote.event.ldirty: vals = self._remote.cache["strip_level"][self.range[0] : self.range[-1]] else: vals = [self._remote.get_level(mode, i) for i in range(*self.range)]