add support for all strip level modes to observers

is_updated now returns a bool (if that channel is updated (dirty))

observer example updated.

minor ver bump
This commit is contained in:
onyx-and-iris 2022-07-06 13:14:23 +01:00
parent 1b75d53cfb
commit 9af2fc98aa
5 changed files with 62 additions and 57 deletions

View File

@ -6,7 +6,20 @@ class Observer:
self.vm = vm self.vm = vm
def on_update(self, subject): def on_update(self, subject):
print(subject) if subject == "pdirty":
print("pdirty!")
if subject == "mdirty":
print("mdirty!")
if subject == "ldirty":
info = (
"levels changed:",
f"[strip 0 {self.vm.strip[0].levels.is_updated}]",
f"[strip 1 {self.vm.strip[1].levels.is_updated}]",
f"[strip 2 {self.vm.strip[2].levels.is_updated}]",
f"[strip 3 {self.vm.strip[3].levels.is_updated}]",
f"[strip 4 {self.vm.strip[4].levels.is_updated}]",
)
print(" ".join(info))
def main(): def main():

View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "voicemeeter-api" name = "voicemeeter-api"
version = "0.1.10" version = "0.2.0"
description = "A Python wrapper for the Voiceemeter API" description = "A Python wrapper for the Voiceemeter API"
authors = ["onyx-and-iris <code@onyxandiris.online>"] authors = ["onyx-and-iris <code@onyxandiris.online>"]
license = "MIT" license = "MIT"

View File

@ -20,7 +20,8 @@ class Remote(CBindings):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.cache = {} self.cache = {}
self.subject = Subject() self.subject = Subject()
self._strip_levels, self._bus_levels = self.all_levels self.strip_mode = 0
self.running = True
for attr, val in kwargs.items(): for attr, val in kwargs.items():
setattr(self, attr, val) setattr(self, attr, val)
@ -38,21 +39,23 @@ class Remote(CBindings):
def init_thread(self): def init_thread(self):
"""Starts updates thread.""" """Starts updates thread."""
self.running = True
t = Thread(target=self._updates, daemon=True) t = Thread(target=self._updates, daemon=True)
t.start() t.start()
def _updates(self): def _updates(self):
"""Continously update observers of dirty states.""" """Continously update observers of dirty states."""
self.cache["strip_level"], self.cache["bus_level"] = self._get_levels()
while self.running: while self.running:
if self.pdirty: if self.pdirty:
self.subject.notify("pdirty") self.subject.notify("pdirty")
if self.mdirty: if self.mdirty:
self.subject.notify("mdirty") self.subject.notify("mdirty")
if self.ldirty: if self.ldirty:
self._strip_levels = self.strip_buf self.cache["strip_level"] = self._strip_buf
self._bus_levels = self.bus_buf self.cache["bus_level"] = self._bus_buf
self.subject.notify("ldirty") self.subject.notify("ldirty")
time.sleep(self.ratelimit) time.sleep(self.ratelimit)
def login(self) -> NoReturn: def login(self) -> NoReturn:
@ -105,18 +108,17 @@ class Remote(CBindings):
@property @property
def ldirty(self) -> bool: def ldirty(self) -> bool:
"""True iff levels have been updated.""" """True iff levels have been updated."""
self.strip_buf, self.bus_buf = self.all_levels self._strip_buf, self._bus_buf = self._get_levels()
self._strip_comp, self._bus_comp = ( self._strip_comp, self._bus_comp = (
tuple(not a == b for a, b in zip(self.strip_buf, self._strip_levels)), tuple(
tuple(not a == b for a, b in zip(self.bus_buf, self._bus_levels)), not a == b
) for a, b in zip(self.cache.get("strip_level"), self._strip_buf)
return any( ),
any(l) tuple(
for l in ( not a == b for a, b in zip(self.cache.get("bus_level"), self._bus_buf)
self._strip_comp, ),
self._bus_comp,
)
) )
return any(any(l) for l in (self._strip_comp, self._bus_comp))
def clear_dirty(self): def clear_dirty(self):
while self.pdirty or self.mdirty: while self.pdirty or self.mdirty:
@ -198,16 +200,19 @@ class Remote(CBindings):
) )
return (name.value, type_.value, hwid.value) return (name.value, type_.value, hwid.value)
@property def get_level(self, type_: int, index: int) -> float:
def all_levels(self) -> Iterable: """Retrieves a single level value"""
val = ct.c_float()
self.vm_get_level(ct.c_long(type_), ct.c_long(index), ct.byref(val))
return val.value
def _get_levels(self) -> Iterable:
""" """
returns both level arrays (strip_levels, bus_levels) BEFORE math conversion returns both level arrays (strip_levels, bus_levels) BEFORE math conversion
strip levels in PREFADER mode.
""" """
return ( return (
tuple( tuple(
self.get_level(0, i) self.get_level(self.strip_mode, i)
for i in range(2 * self.kind.phys_in + 8 * self.kind.virt_in) for i in range(2 * self.kind.phys_in + 8 * self.kind.virt_in)
), ),
tuple( tuple(
@ -216,20 +221,6 @@ class Remote(CBindings):
), ),
) )
def get_level(self, type_: int, index: int) -> float:
"""Retrieves a single level value"""
val = ct.c_float()
self.vm_get_level(ct.c_long(type_), ct.c_long(index), ct.byref(val))
return val.value
@property
def strip_levels(self):
return self._strip_levels
@property
def bus_levels(self):
return self._bus_levels
@script @script
def sendtext(self, script: str): def sendtext(self, script: str):
"""Sets many parameters from a script""" """Sets many parameters from a script"""

View File

@ -113,15 +113,19 @@ class BusLevel(IRemote):
(i, i + 8) (i, i + 8)
for i in range(0, (remote.kind.phys_out + remote.kind.virt_out) * 8, 8) for i in range(0, (remote.kind.phys_out + remote.kind.virt_out) * 8, 8)
) )
self.range = self.level_map[self.index]
def getter(self): def getter(self, mode):
"""Returns a tuple of level values for the channel.""" """Returns a tuple of level values for the channel."""
def fget(i): def fget(i):
return round(20 * log(i, 10), 1) if i > 0 else -200.0 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
range_ = self.level_map[self.index] return tuple(fget(i) for i in range(*self.range))
return tuple(fget(i) for i in self._remote._bus_levels[range_[0] : range_[-1]])
@property @property
def identifier(self) -> str: def identifier(self) -> str:
@ -129,11 +133,11 @@ class BusLevel(IRemote):
@property @property
def all(self) -> tuple: def all(self) -> tuple:
return self.getter() return self.getter(3)
@property @property
def updated(self) -> tuple: def is_updated(self) -> bool:
return self._remote._bus_comp return any(self._remote._bus_comp[self.range[0] : self.range[-1]])
def _make_bus_mode_mixin(): def _make_bus_mode_mixin():

View File

@ -186,25 +186,19 @@ class StripLevel(IRemote):
) )
) )
self.level_map = phys_map + virt_map self.level_map = phys_map + virt_map
self.range = self.level_map[self.index]
def getter(self, mode): def getter(self, mode):
"""Returns a tuple of level values for the channel.""" """Returns a tuple of level values for the channel."""
def fget(i): def fget(i):
res = self._remote.get_level(mode, 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(res, 10), 1) if res > 0 else -200.0
range_ = self.level_map[self.index] return tuple(fget(i) for i in range(*self.range))
return tuple(fget(i) for i in range(*range_))
def getter_prefader(self):
def fget(i):
return round(20 * log(i, 10), 1) if i > 0 else -200.0
range_ = self.level_map[self.index]
return tuple(
fget(i) for i in self._remote._strip_levels[range_[0] : range_[-1]]
)
@property @property
def identifier(self) -> str: def identifier(self) -> str:
@ -212,19 +206,22 @@ class StripLevel(IRemote):
@property @property
def prefader(self) -> tuple: def prefader(self) -> tuple:
return self.getter_prefader() self._remote.strip_mode = 0
return self.getter(0)
@property @property
def postfader(self) -> tuple: def postfader(self) -> tuple:
self._remote.strip_mode = 1
return self.getter(1) return self.getter(1)
@property @property
def postmute(self) -> tuple: def postmute(self) -> tuple:
self._remote.strip_mode = 2
return self.getter(2) return self.getter(2)
@property @property
def updated(self) -> tuple: def is_updated(self) -> bool:
return self._remote._strip_comp return any(self._remote._strip_comp[self.range[0] : self.range[-1]])
class GainLayer(IRemote): class GainLayer(IRemote):