From ad88286509f637d8082ed4fd5c4e4842a21e334e Mon Sep 17 00:00:00 2001 From: onyx-and-iris Date: Sat, 17 Jan 2026 12:29:10 +0000 Subject: [PATCH] implement 3d parameters --- tests/test_lower.py | 2 +- vban_cmd/kinds.py | 2 +- vban_cmd/meta.py | 45 +++++++++++++++++++++- vban_cmd/packet.py | 39 +++++++++++++++++++ vban_cmd/strip.py | 91 ++++++++++++++++++++++++++++++++++++++++++--- vban_cmd/util.py | 14 +++++++ vban_cmd/vban.py | 4 +- 7 files changed, 186 insertions(+), 11 deletions(-) diff --git a/tests/test_lower.py b/tests/test_lower.py index d6e3817..5bb9c8e 100644 --- a/tests/test_lower.py +++ b/tests/test_lower.py @@ -11,7 +11,7 @@ class TestPublicPacketLower: def test_it_gets_an_rt0_data_packet(self): assert vban.public_packets[0].voicemeetertype in ( - kind.name for kind in kinds.kinds_all + kind.name for kind in kinds.all ) diff --git a/vban_cmd/kinds.py b/vban_cmd/kinds.py index e591fec..edb692f 100644 --- a/vban_cmd/kinds.py +++ b/vban_cmd/kinds.py @@ -111,4 +111,4 @@ def request_kind_map(kind_id): return KIND_obj -kinds_all = list(request_kind_map(kind_id.name.lower()) for kind_id in KindId) +all = list(request_kind_map(kind_id.name.lower()) for kind_id in KindId) diff --git a/vban_cmd/meta.py b/vban_cmd/meta.py index 776cd90..e020355 100644 --- a/vban_cmd/meta.py +++ b/vban_cmd/meta.py @@ -1,7 +1,7 @@ from functools import partial from .enums import NBS -from .util import cache_bool, cache_string +from .util import cache_bool, cache_float, cache_string def channel_bool_prop(param): @@ -97,3 +97,46 @@ def action_fn(param, val=1): self.setter(param, val) return fdo + + +def xy_prop(param): + """meta function for XY pad parameters""" + + @partial(cache_float, param=param) + def fget(self): + cmd = self._cmd(param) + self.logger.debug(f'getter: {cmd}') + _type, axis = param.split('_') + if self.public_packets[NBS.one] is None: + return 0.0 + x, y = getattr( + self.public_packets[NBS.one].strips[self.index], f'position_{_type.lower()}' + ) + return x if axis == 'x' else y + + def fset(self, val): + self.setter(param, val) + + return property(fget, fset) + + +def send_prop(param): + """meta function for send parameters""" + + @partial(cache_float, param=param) + def fget(self): + cmd = self._cmd(param) + self.logger.debug(f'getter: {cmd}') + if self.public_packets[NBS.one] is None: + return 0.0 + val = getattr(self.public_packets[NBS.one].strips[self.index], f'send_{param}') + match param: + case 'reverb' | 'fx1': + return val[0] + case 'delay' | 'fx2': + return val[1] + + def fset(self, val): + self.setter(param, val) + + return property(fget, fset) diff --git a/vban_cmd/packet.py b/vban_cmd/packet.py index 62a8c4e..c930f15 100644 --- a/vban_cmd/packet.py +++ b/vban_cmd/packet.py @@ -365,6 +365,45 @@ class VbanVMParamStrip: def mode(self) -> int: return int.from_bytes(self._mode, 'little') + @property + def position_pan(self) -> tuple[int, int]: + return ( + round(int.from_bytes(self._pos3D_x, 'little', signed=True) * 0.01, 2), + round(int.from_bytes(self._pos3D_y, 'little', signed=True) * 0.01, 2), + ) + + @property + def position_color(self) -> tuple[int, int]: + return ( + round(int.from_bytes(self._posColor_x, 'little', signed=True) * 0.01, 2), + round(int.from_bytes(self._posColor_y, 'little', signed=True) * 0.01, 2), + ) + + @property + def position_fx(self) -> tuple[int, int]: + return ( + round(int.from_bytes(self._posMod_x, 'little', signed=True) * 0.01, 2), + round(int.from_bytes(self._posMod_y, 'little', signed=True) * 0.01, 2), + ) + + @property + def send_reverb(self) -> tuple[float, float]: + return ( + round(int.from_bytes(self._send_reverb, 'little', signed=True) * 0.01, 2), + round(int.from_bytes(self._send_delay, 'little', signed=True) * 0.01, 2), + ) + + send_delay = send_reverb + + @property + def send_fx1(self) -> tuple[float, float]: + return ( + round(int.from_bytes(self._send_fx1, 'little', signed=True) * 0.01, 2), + round(int.from_bytes(self._send_fx2, 'little', signed=True) * 0.01, 2), + ) + + send_fx2 = send_fx1 + @property def eqgains(self) -> tuple[float, float, float]: return tuple( diff --git a/vban_cmd/strip.py b/vban_cmd/strip.py index eca112a..dc9b804 100644 --- a/vban_cmd/strip.py +++ b/vban_cmd/strip.py @@ -2,10 +2,16 @@ import time from abc import abstractmethod from typing import Union +from . import kinds from .enums import NBS, EQGains from .iremote import IRemote -from .kinds import kinds_all -from .meta import channel_bool_prop, channel_label_prop, strip_output_prop +from .meta import ( + channel_bool_prop, + channel_label_prop, + send_prop, + strip_output_prop, + xy_prop, +) class Strip(IRemote): @@ -53,10 +59,11 @@ class Strip(IRemote): class PhysicalStrip(Strip): @classmethod - def make(cls, remote, index): + def make(cls, remote, index, is_phys): + EFFECTS_cls = _make_effects_mixins(is_phys)[remote.kind.name] return type( f'PhysicalStrip{remote.kind}', - (cls,), + (cls, EFFECTS_cls), { 'comp': StripComp(remote, index), 'gate': StripGate(remote, index), @@ -254,6 +261,20 @@ class StripEQ(IRemote): class VirtualStrip(Strip): + @classmethod + def make(cls, remote, i, is_phys): + """ + Factory method for VirtualStrip. + + Returns a VirtualStrip class. + """ + EFFECTS_cls = _make_effects_mixins(is_phys)[remote.kind.name] + return type( + 'VirtualStrip', + (cls, EFFECTS_cls), + {}, + ) + def __str__(self): return f'{type(self).__name__}{self.index}' @@ -432,10 +453,64 @@ def _make_channelout_mixin(kind): _make_channelout_mixins = { - kind.name: _make_channelout_mixin(kind) for kind in kinds_all + kind.name: _make_channelout_mixin(kind) for kind in kinds.all } +def _make_effects_mixin(kind, is_phys): + """creates an effects mixin for a kind""" + + def _make_xy_cls(): + pan = {param: xy_prop(param) for param in ['pan_x', 'pan_y']} + color = {param: xy_prop(param) for param in ['color_x', 'color_y']} + fx = {param: xy_prop(param) for param in ['fx_x', 'fx_y']} + if is_phys: + return type( + 'XYPhys', + (), + { + **pan, + **color, + **fx, + }, + ) + return type( + 'XYVirt', + (), + {**pan}, + ) + + def _make_sends_cls(): + if is_phys: + return type( + 'FX', + (), + { + **{ + param: send_prop(param) + for param in ['reverb', 'delay', 'fx1', 'fx2'] + }, + # **{ + # f'post{param}': bool_prop(f'post{param}') + # for param in ['reverb', 'delay', 'fx1', 'fx2'] + # }, + }, + ) + return type('FX', (), {}) + + if kind.name == 'basic': + steps = (_make_xy_cls,) + elif kind.name == 'banana': + steps = (_make_xy_cls,) + elif kind.name == 'potato': + steps = (_make_xy_cls, _make_sends_cls) + return type(f'Effects{kind}', tuple(step() for step in steps), {}) + + +def _make_effects_mixins(is_phys): + return {kind.name: _make_effects_mixin(kind, is_phys) for kind in kinds.all} + + def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip]: """ Factory method for strips @@ -444,7 +519,11 @@ def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip Returns a physical or virtual strip subclass """ - STRIP_cls = PhysicalStrip.make(remote, i) if is_phys_strip else VirtualStrip + STRIP_cls = ( + PhysicalStrip.make(remote, i, is_phys_strip) + if is_phys_strip + else VirtualStrip.make(remote, i, is_phys_strip) + ) CHANNELOUTMIXIN_cls = _make_channelout_mixins[remote.kind.name] GAINLAYERMIXIN_cls = _make_gainlayer_mixin(remote, i) diff --git a/vban_cmd/util.py b/vban_cmd/util.py index ae272db..9af9f4e 100644 --- a/vban_cmd/util.py +++ b/vban_cmd/util.py @@ -29,6 +29,20 @@ def cache_string(func, param): return wrapper +def cache_float(func, param): + """Check cache for a float prop""" + + def wrapper(*args, **kwargs): + self, *rem = args + if self._cmd(param) in self._remote.cache: + return round(self._remote.cache.pop(self._cmd(param)), 2) + if self._remote.sync: + self._remote.clear_dirty() + return func(*args, **kwargs) + + return wrapper + + def depth(d): if isinstance(d, dict): return 1 + (max(map(depth, d.values())) if d else 0) diff --git a/vban_cmd/vban.py b/vban_cmd/vban.py index ffd0643..f3d3107 100644 --- a/vban_cmd/vban.py +++ b/vban_cmd/vban.py @@ -1,7 +1,7 @@ from abc import abstractmethod +from . import kinds from .iremote import IRemote -from .kinds import kinds_all class VbanStream(IRemote): @@ -194,7 +194,7 @@ def _make_stream_pair(remote, kind): def _make_stream_pairs(remote): - return {kind.name: _make_stream_pair(remote, kind) for kind in kinds_all} + return {kind.name: _make_stream_pair(remote, kind) for kind in kinds.all} class Vban: