diff --git a/pyproject.toml b/pyproject.toml index e156b7c..42fcd83 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "voicemeeter-api" -version = "0.2.2" +version = "0.2.3" description = "A Python wrapper for the Voiceemeter API" authors = ["onyx-and-iris "] license = "MIT" diff --git a/voicemeeterlib/base.py b/voicemeeterlib/base.py index 72fe0c7..1cfb99e 100644 --- a/voicemeeterlib/base.py +++ b/voicemeeterlib/base.py @@ -9,7 +9,7 @@ from .cbindings import CBindings from .error import VMError from .kinds import KindId from .subject import Subject -from .util import polling, script +from .util import comp, polling, script class Remote(CBindings): @@ -44,7 +44,13 @@ class Remote(CBindings): t.start() def _updates(self): - """Continously update observers of dirty states.""" + """ + Continously update observers of dirty states. + + Generate _strip_comp, _bus_comp and update level cache if ldirty. + + Runs updates at a rate of self.ratelimit. + """ self.cache["strip_level"], self.cache["bus_level"] = self._get_levels() while self.running: @@ -53,6 +59,12 @@ class Remote(CBindings): if self.mdirty: self.subject.notify("mdirty") if self.ldirty: + self._strip_comp, self._bus_comp = ( + tuple( + not x for x in comp(self.cache["strip_level"], self._strip_buf) + ), + tuple(not x for x in comp(self.cache["bus_level"], self._bus_buf)), + ) self.cache["strip_level"] = self._strip_buf self.cache["bus_level"] = self._bus_buf self.subject.notify("ldirty") @@ -110,16 +122,10 @@ class Remote(CBindings): def ldirty(self) -> bool: """True iff levels have been updated.""" self._strip_buf, self._bus_buf = self._get_levels() - self._strip_comp, self._bus_comp = ( - tuple( - not a == b - for a, b in zip(self.cache.get("strip_level"), self._strip_buf) - ), - tuple( - not a == b for a, b in zip(self.cache.get("bus_level"), self._bus_buf) - ), + return not ( + self.cache.get("strip_level") == self._strip_buf + and self.cache.get("bus_level") == self._bus_buf ) - return any(any(l) for l in (self._strip_comp, self._bus_comp)) def clear_dirty(self): while self.pdirty or self.mdirty: diff --git a/voicemeeterlib/bus.py b/voicemeeterlib/bus.py index 533c0c6..f79ea00 100644 --- a/voicemeeterlib/bus.py +++ b/voicemeeterlib/bus.py @@ -116,16 +116,21 @@ class BusLevel(IRemote): self.range = self.level_map[self.index] def getter(self, mode): - """Returns a tuple of level values for the channel.""" + """ + Returns a tuple of level values for the channel. + + If observables thread running fetch values from cache otherwise call CAPI func. + """ def fget(i): - if self._remote.running and "bus_level" in self._remote.cache: - res = self._remote.cache["bus_level"][i] - else: - res = self._remote.get_level(mode, i) - return round(20 * log(res, 10), 1) if res > 0 else -200.0 + return round(20 * log(i, 10), 1) if i > 0 else -200.0 - return tuple(fget(i) for i in range(*self.range)) + if self._remote.running and "bus_level" in self._remote.cache: + 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)] + + return tuple(fget(i) for i in vals) @property def identifier(self) -> str: @@ -137,7 +142,13 @@ class BusLevel(IRemote): @property def is_updated(self) -> bool: - return any(self._remote._bus_comp[self.range[0] : self.range[-1]]) + """ + Returns dirty status for this specific channel. + + Expected to be used in a callback only. + """ + if self._remote.running: + return any(self._remote._bus_comp[self.range[0] : self.range[-1]]) def _make_bus_mode_mixin(): diff --git a/voicemeeterlib/strip.py b/voicemeeterlib/strip.py index b77e1ba..1844c64 100644 --- a/voicemeeterlib/strip.py +++ b/voicemeeterlib/strip.py @@ -189,16 +189,21 @@ class StripLevel(IRemote): self.range = self.level_map[self.index] def getter(self, mode): - """Returns a tuple of level values for the channel.""" + """ + Returns a tuple of level values for the channel. + + If observables thread running fetch values from cache otherwise call CAPI func. + """ def fget(i): - if self._remote.running and "strip_level" in self._remote.cache: - res = self._remote.cache["strip_level"][i] - else: - res = self._remote.get_level(mode, i) - return round(20 * log(res, 10), 1) if res > 0 else -200.0 + return round(20 * log(i, 10), 1) if i > 0 else -200.0 - return tuple(fget(i) for i in range(*self.range)) + if self._remote.running and "strip_level" in self._remote.cache: + 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)] + + return tuple(fget(i) for i in vals) @property def identifier(self) -> str: @@ -221,7 +226,13 @@ class StripLevel(IRemote): @property def is_updated(self) -> bool: - return any(self._remote._strip_comp[self.range[0] : self.range[-1]]) + """ + Returns dirty status for this specific channel. + + Expected to be used in a callback only. + """ + if self._remote.running: + return any(self._remote._strip_comp[self.range[0] : self.range[-1]]) class GainLayer(IRemote): diff --git a/voicemeeterlib/util.py b/voicemeeterlib/util.py index d1c2b5f..2b92237 100644 --- a/voicemeeterlib/util.py +++ b/voicemeeterlib/util.py @@ -1,4 +1,5 @@ import functools +from typing import Iterator def polling(func): @@ -50,3 +51,13 @@ def script(func): return func(remote, script) return wrapper + + +def comp(t0: tuple, t1: tuple) -> Iterator[bool]: + """ + Generator function, accepts two tuples. + + Evaluates equality of each member in both tuples. + """ + for a, b in zip(t0, t1): + yield a == b