diff --git a/pyproject.toml b/pyproject.toml index 4d3786c..578b9ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "vban-cmd" -version = "1.0.8" +version = "1.1.0" description = "Python interface for the VBAN RT Packet Service (Sendtext)" authors = ["onyx-and-iris "] license = "MIT" diff --git a/vban_cmd/base.py b/vban_cmd/base.py index 50d52b8..9035a94 100644 --- a/vban_cmd/base.py +++ b/vban_cmd/base.py @@ -1,9 +1,10 @@ +import dataclasses import socket import time from abc import ABCMeta, abstractmethod from enum import IntEnum from threading import Thread -from typing import NoReturn, Optional, Union +from typing import Iterable, NoReturn, Optional, Union from .packet import ( HEADER_SIZE, @@ -165,12 +166,22 @@ class VbanCmd(metaclass=ABCMeta): @property def pdirty(self): """True iff a parameter has changed""" - return self._pdirty + return self._pp.pdirty(self.public_packet) @property def ldirty(self): """True iff a level value has changed.""" - return self._ldirty + self._strip_comp, self._bus_comp = ( + tuple( + not a == b + for a, b in zip(self._public_packet.inputlevels, self._strip_buf) + ), + tuple( + not a == b + for a, b in zip(self._public_packet.outputlevels, self._bus_buf) + ), + ) + return any(any(l) for l in (self._strip_comp, self._bus_comp)) @property def public_packet(self): @@ -181,49 +192,35 @@ class VbanCmd(metaclass=ABCMeta): pass def _updates(self) -> NoReturn: - while self.running: - private_packet = self._get_rt() - self._strip_comp, self._bus_comp = ( - tuple( - not a == b - for a, b in zip( - private_packet.inputlevels, self.public_packet.inputlevels - ) - ), - tuple( - not a == b - for a, b in zip( - private_packet.outputlevels, self.public_packet.outputlevels - ) - ), - ) - self._pdirty = private_packet.pdirty(self.public_packet) - self._ldirty = any( - any(list_) for list_ in (self._strip_comp, self._bus_comp) - ) - - if self._public_packet != private_packet: - self._public_packet = private_packet - if self.pdirty: - self.subject.notify("pdirty") - if self.ldirty: - self.subject.notify("ldirty") - time.sleep(self.ratelimit) - - @property - def strip_levels(self): - """Returns the full strip level array for a kind, PREFADER mode, before math conversion""" - return tuple( - list(filter(lambda x: x != ((1 << 16) - 1), self.public_packet.inputlevels)) + self.cache["strip_level"], self.cache["bus_level"] = self._get_levels( + self.public_packet ) - @property - def bus_levels(self): - """Returns the full bus level array for a kind, before math conversion""" - return tuple( - list( - filter(lambda x: x != ((1 << 16) - 1), self.public_packet.outputlevels) - ) + while self.running: + start = time.time() + self._pp = self._get_rt() + self._strip_buf, self._bus_buf = self._get_levels(self._pp) + + if self.pdirty: + self._public_packet = self._pp + self.subject.notify("pdirty") + if self.ldirty: + self.cache["strip_level"] = tuple(self._strip_buf) + self.cache["bus_level"] = tuple(self._bus_buf) + self.subject.notify("ldirty") + elapsed = time.time() - start + if self.ratelimit - elapsed > 0: + time.sleep(self.ratelimit - elapsed) + + def _get_levels(self, packet) -> Iterable: + """ + returns both level arrays (strip_levels, bus_levels) BEFORE math conversion + + strip levels in PREFADER mode. + """ + return ( + [val for val in packet.inputlevels], + [val for val in packet.outputlevels], ) def apply(self, data: dict): diff --git a/vban_cmd/bus.py b/vban_cmd/bus.py index bdf3310..147d8f4 100644 --- a/vban_cmd/bus.py +++ b/vban_cmd/bus.py @@ -66,13 +66,15 @@ class BusLevel(IRemote): (i, i + 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): """Returns a tuple of level values for the channel.""" range_ = self.level_map[self.index] return tuple( - round(-i * 0.01, 1) for i in self._remote.bus_levels[range_[0] : range_[-1]] + round(-i * 0.01, 1) + for i in self._remote.cache["bus_level"][self.range[0] : self.range[-1]] ) @property @@ -84,8 +86,8 @@ class BusLevel(IRemote): return self.getter() @property - def updated(self) -> tuple: - return self._remote._bus_comp + def is_updated(self) -> bool: + return any(self._remote._bus_comp[self.range[0] : self.range[-1]]) def _make_bus_mode_mixin(): diff --git a/vban_cmd/factory.py b/vban_cmd/factory.py index 4742966..90419fe 100644 --- a/vban_cmd/factory.py +++ b/vban_cmd/factory.py @@ -64,7 +64,7 @@ class FactoryBase(VbanCmd): "streamname": "Command1", "bps": 0, "channel": 0, - "ratelimit": 0, + "ratelimit": 0.01, "sync": False, } kwargs = defaultkwargs | kwargs diff --git a/vban_cmd/packet.py b/vban_cmd/packet.py index 1f22b53..f9ff8ed 100644 --- a/vban_cmd/packet.py +++ b/vban_cmd/packet.py @@ -1,4 +1,5 @@ from dataclasses import dataclass +from typing import Generator VBAN_SERVICE_RTPACKETREGISTER = 32 VBAN_SERVICE_RTPACKET = 33 @@ -76,21 +77,24 @@ class VBAN_VMRT_Packet_Data: return int.from_bytes(self._samplerate, "little") @property - def inputlevels(self) -> tuple: + def inputlevels(self) -> Generator[float, None, None]: """returns the entire level array across all inputs""" - return tuple( - ((1 << 16) - 1) - int.from_bytes(self._inputLeveldB100[i : i + 2], "little") - for i in range(0, 68, 2) - ) + for i in range(0, 68, 2): + val = ((1 << 16) - 1) - int.from_bytes( + self._inputLeveldB100[i : i + 2], "little" + ) + if val != ((1 << 16) - 1): + yield val @property - def outputlevels(self) -> tuple: + def outputlevels(self) -> Generator[float, None, None]: """returns the entire level array across all outputs""" - return tuple( - ((1 << 16) - 1) - - int.from_bytes(self._outputLeveldB100[i : i + 2], "little") - for i in range(0, 128, 2) - ) + for i in range(0, 128, 2): + val = ((1 << 16) - 1) - int.from_bytes( + self._outputLeveldB100[i : i + 2], "little" + ) + if val != ((1 << 16) - 1): + yield val @property def stripstate(self) -> tuple: diff --git a/vban_cmd/strip.py b/vban_cmd/strip.py index 813e05e..f84c009 100644 --- a/vban_cmd/strip.py +++ b/vban_cmd/strip.py @@ -100,12 +100,12 @@ class StripLevel(IRemote): ) ) self.level_map = phys_map + virt_map + self.range = self.level_map[self.index] - def getter_prefader(self): - range_ = self.level_map[self.index] + def getter(self): return tuple( round(-i * 0.01, 1) - for i in self._remote.strip_levels[range_[0] : range_[-1]] + for i in self._remote.cache["strip_level"][self.range[0] : self.range[-1]] ) @property @@ -114,7 +114,7 @@ class StripLevel(IRemote): @property def prefader(self) -> tuple: - return self.getter_prefader() + return self.getter() @property def postfader(self) -> tuple: @@ -125,8 +125,8 @@ class StripLevel(IRemote): return @property - def updated(self) -> tuple: - return self._remote._strip_comp + def is_updated(self) -> bool: + return any(self._remote._strip_comp[self.range[0] : self.range[-1]]) class GainLayer(IRemote):