diff --git a/tests/__init__.py b/tests/__init__.py index b4723ff..5a7fda0 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -22,7 +22,6 @@ class Data: strip: int = kind.num_strip - 1 bus: int = kind.num_bus - 1 fx: int = kind.num_fx - 1 - rtn: int = kind.num_rtn - 1 data = Data() diff --git a/tests/test_shared.py b/tests/test_shared.py index 196f58e..f1dfdc3 100644 --- a/tests/test_shared.py +++ b/tests/test_shared.py @@ -259,7 +259,7 @@ class TestSetAndGetStripAutomixHigher: "param,value", [("group", 0), ("group", 2)], ) - def test_it_sets_and_gets_fxsend_int_params(self, param, value): + def test_it_sets_and_gets_strip_int_params(self, param, value): setattr(self.target, param, value) assert getattr(self.target, param) == value @@ -267,7 +267,7 @@ class TestSetAndGetStripAutomixHigher: "param,value", [("weight", -10.5), ("weight", 3.5)], ) - def test_it_sets_and_gets_fxsend_float_params(self, param, value): + def test_it_sets_and_gets_strip_float_params(self, param, value): setattr(self.target, param, value) assert getattr(self.target, param) == value diff --git a/xair_api/adapter.py b/xair_api/adapter.py new file mode 100644 index 0000000..b4afa0c --- /dev/null +++ b/xair_api/adapter.py @@ -0,0 +1,40 @@ +from .bus import Bus as IBus +from .lr import LR as ILR +from .rtn import AuxRtn as IAuxRtn +from .rtn import FxRtn as IFxRtn + + +class Bus(IBus): + @property + def address(self): + return f"/bus/{str(self.index).zfill(2)}" + + +class AuxRtn(IAuxRtn): + @property + def address(self): + return f"/auxin/{str(self.index).zfill(2)}" + + +class FxRtn(IFxRtn): + @property + def address(self): + return f"/fxrtn/{str(self.index).zfill(2)}" + + +class MainStereo(ILR): + @property + def address(self) -> str: + return f"/main/st" + + +class MainMono(ILR): + @property + def address(self) -> str: + return f"/main/m" + + +class Matrix(ILR): + @property + def address(self) -> str: + return f"/mtx/{str(self.index).zfill(2)}" diff --git a/xair_api/fx.py b/xair_api/fx.py index 5da7589..f7adada 100644 --- a/xair_api/fx.py +++ b/xair_api/fx.py @@ -23,6 +23,24 @@ class IFX(abc.ABC): pass +class FX(IFX): + """Concrete class for fx""" + + @property + def address(self) -> str: + return f"/fx/{self.index}" + + @property + def type(self) -> int: + return self.getter("type")[0] + + @type.setter + def type(self, val: int): + if not isinstance(val, int): + raise XAirRemoteError("type is an integer parameter") + self.setter("type", val) + + class FXSend(IFX): """Concrete class for fxsend""" @@ -52,21 +70,3 @@ class FXSend(IFX): @property def address(self) -> str: return f"/fxsend/{self.index}" - - -class FXReturn(IFX): - """Concrete class for fxreturn""" - - @property - def address(self) -> str: - return f"/fx/{self.index}" - - @property - def type(self) -> int: - return self.getter("type")[0] - - @type.setter - def type(self, val: int): - if not isinstance(val, int): - raise XAirRemoteError("type is an integer parameter") - self.setter("type", val) diff --git a/xair_api/kinds.py b/xair_api/kinds.py index 4134576..f28798b 100644 --- a/xair_api/kinds.py +++ b/xair_api/kinds.py @@ -1,18 +1,5 @@ from dataclasses import dataclass -""" -# osc slightly different, interface would need adjusting to support this mixer. - -@dataclass -class X32KindMap: - id_: str = "X32" - num_dca: int = 8 - num_strip: int = 32 - num_bus: int = 16 - num_fx: int = 8 - num_rtn: int = 6 -""" - @dataclass class KindMap: @@ -20,6 +7,17 @@ class KindMap: return self.id_ +@dataclass +class X32KindMap(KindMap): + id_: str + num_dca: int = 8 + num_strip: int = 32 + num_bus: int = 16 + num_fx: int = 8 + num_auxrtn: int = 8 + num_matrix: int = 6 + + @dataclass class MR18KindMap(KindMap): # note ch 17-18 defined as aux rtn @@ -28,7 +26,6 @@ class MR18KindMap(KindMap): num_strip: int = 16 num_bus: int = 6 num_fx: int = 4 - num_rtn: int = 4 @dataclass @@ -38,7 +35,6 @@ class XR16KindMap(KindMap): num_strip: int = 16 num_bus: int = 4 num_fx: int = 4 - num_rtn: int = 4 @dataclass @@ -48,10 +44,10 @@ class XR12KindMap(KindMap): num_strip: int = 12 num_bus: int = 2 num_fx: int = 4 - num_rtn: int = 4 _kinds = { + "X32": X32KindMap(id_="X32"), "XR18": MR18KindMap(id_="XR18"), "MR18": MR18KindMap(id_="MR18"), "XR16": XR16KindMap(id_="XR16"), diff --git a/xair_api/lr.py b/xair_api/lr.py index d85827a..a8b7607 100644 --- a/xair_api/lr.py +++ b/xair_api/lr.py @@ -1,4 +1,5 @@ import abc +from typing import Optional from .errors import XAirRemoteError from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp @@ -7,8 +8,10 @@ from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Pre class ILR(abc.ABC): """Abstract Base Class for buses""" - def __init__(self, remote): + def __init__(self, remote, index: Optional[int] = None): self._remote = remote + if index is not None: + self.index = index + 1 def getter(self, param: str): self._remote.send(f"{self.address}/{param}") @@ -26,7 +29,7 @@ class LR(ILR): """Concrete class for buses""" @classmethod - def make(cls, remote): + def make(cls, remote, index=None): """ Factory function for LR @@ -41,19 +44,19 @@ class LR(ILR): **{ _cls.__name__.lower(): type( f"{_cls.__name__}{remote.kind}", (_cls, cls), {} - )(remote) + )(remote, index) for _cls in ( Config, Dyn, Insert, GEQ.make(), - EQ.make_sixband(cls, remote), + EQ.make_sixband(cls, remote, index), Mix, ) }, }, ) - return LR_cls(remote) + return LR_cls(remote, index) @property def address(self) -> str: diff --git a/xair_api/rtn.py b/xair_api/rtn.py index 7d7ec90..040d086 100644 --- a/xair_api/rtn.py +++ b/xair_api/rtn.py @@ -25,26 +25,24 @@ class IRtn(abc.ABC): pass -class Aux(IRtn): +class AuxRtn(IRtn): """Concrete class for aux""" @classmethod - def make(cls, remote): + def make(cls, remote, index=None): """ - Factory function for aux - + Factory function for auxrtn Creates a mixin of shared subclasses, sets them as class attributes. - - Returns an Aux class of a kind. + Returns an AuxRtn class of a kind. """ - AUX_cls = type( - f"Aux{remote.kind}", + AUXRTN_cls = type( + f"AuxRtn{remote.kind}", (cls,), { **{ _cls.__name__.lower(): type( f"{_cls.__name__}{remote.kind}", (_cls, cls), {} - )(remote) + )(remote, index) for _cls in ( Config, Preamp, @@ -55,32 +53,30 @@ class Aux(IRtn): } }, ) - return AUX_cls(remote) + return AUXRTN_cls(remote, index) @property def address(self): return "/rtn/aux" -class Rtn(IRtn): +class FxRtn(IRtn): """Concrete class for rtn""" @classmethod def make(cls, remote, index): """ - Factory function for rtn - + Factory function for fxrtn Creates a mixin of shared subclasses, sets them as class attributes. - - Returns an Rtn class of a kind. + Returns an FxRtn class of a kind. """ - RTN_cls = type( - f"Rtn{remote.kind.id_}", + FXRTN_cls = type( + f"FxRtn{remote.kind}", (cls,), { **{ _cls.__name__.lower(): type( - f"{_cls.__name__}{remote.kind.id_}", (_cls, cls), {} + f"{_cls.__name__}{remote.kind}", (_cls, cls), {} )(remote, index) for _cls in ( Config, @@ -92,7 +88,7 @@ class Rtn(IRtn): } }, ) - return RTN_cls(remote, index) + return FXRTN_cls(remote, index) @property def address(self): diff --git a/xair_api/xair.py b/xair_api/xair.py index b008699..d6c6f3a 100644 --- a/xair_api/xair.py +++ b/xair_api/xair.py @@ -1,4 +1,5 @@ import abc +import logging import threading import time from pathlib import Path @@ -13,15 +14,15 @@ from pythonosc.dispatcher import Dispatcher from pythonosc.osc_message_builder import OscMessageBuilder from pythonosc.osc_server import BlockingOSCUDPServer -from . import kinds +from . import adapter, kinds from .bus import Bus from .config import Config from .dca import DCA from .errors import XAirRemoteError -from .fx import FXReturn, FXSend +from .fx import FX, FXSend from .kinds import KindMap from .lr import LR -from .rtn import Aux, Rtn +from .rtn import AuxRtn, FxRtn from .strip import Strip @@ -47,19 +48,19 @@ class OSCClientServer(BlockingOSCUDPServer): class XAirRemote(abc.ABC): """Handles the communication with the mixer via the OSC protocol""" + logger = logging.getLogger("xair.xairremote") + _CONNECT_TIMEOUT = 0.5 _WAIT_TIME = 0.025 _REFRESH_TIMEOUT = 5 - XAIR_PORT = 10024 - info_response = [] def __init__(self, **kwargs): dispatcher = Dispatcher() dispatcher.set_default_handler(self.msg_handler) self.xair_ip = kwargs["ip"] or self._ip_from_toml() - self.xair_port = kwargs["port"] or self.XAIR_PORT + self.xair_port = kwargs["port"] if not (self.xair_ip and self.xair_port): raise XAirRemoteError("No valid ip or password detected") self.server = OSCClientServer((self.xair_ip, self.xair_port), dispatcher) @@ -80,7 +81,9 @@ class XAirRemote(abc.ABC): self.send("/xinfo") time.sleep(self._CONNECT_TIMEOUT) if len(self.info_response) > 0: - print(f"Successfully connected to {self.info_response[2]}.") + print( + f"Successfully connected to {self.info_response[2]} at {self.info_response[0]}." + ) else: print( "Error: Failed to setup OSC connection to mixer. Please check for correct ip address." @@ -90,10 +93,12 @@ class XAirRemote(abc.ABC): self.server.serve_forever() def msg_handler(self, addr, *data): + self.logger.debug(f"received: {addr} {data if data else ''}") self.info_response = data[:] - def send(self, address: str, param: Optional[str] = None): - self.server.send_message(address, param) + def send(self, addr: str, param: Optional[str] = None): + self.logger.debug(f"sending: {addr} {param if param else ''}") + self.server.send_message(addr, param) time.sleep(self._WAIT_TIME) def _query(self, address): @@ -112,8 +117,26 @@ def _make_remote(kind: KindMap) -> XAirRemote: The returned class will subclass XAirRemote. """ - def init(self, *args, **kwargs): - defaultkwargs = {"ip": None, "port": None} + def init_x32(self, *args, **kwargs): + defaultkwargs = {"ip": None, "port": 10023} + kwargs = defaultkwargs | kwargs + XAirRemote.__init__(self, *args, **kwargs) + self.kind = kind + self.mainst = adapter.MainStereo.make(self) + self.mainmono = adapter.MainMono.make(self) + self.matrix = tuple( + adapter.Matrix.make(self, i) for i in range(kind.num_matrix) + ) + self.strip = tuple(Strip.make(self, i) for i in range(kind.num_strip)) + self.bus = tuple(adapter.Bus.make(self, i) for i in range(kind.num_bus)) + self.dca = tuple(DCA(self, i) for i in range(kind.num_dca)) + self.fx = tuple(FX(self, i) for i in range(kind.num_fx)) + self.fxreturn = tuple(adapter.FxRtn.make(self, i) for i in range(kind.num_fx)) + self.config = Config.make(self) + self.auxin = tuple(adapter.AuxRtn.make(self, i) for i in range(kind.num_auxrtn)) + + def init_xair(self, *args, **kwargs): + defaultkwargs = {"ip": None, "port": 10024} kwargs = defaultkwargs | kwargs XAirRemote.__init__(self, *args, **kwargs) self.kind = kind @@ -121,17 +144,25 @@ def _make_remote(kind: KindMap) -> XAirRemote: self.strip = tuple(Strip.make(self, i) for i in range(kind.num_strip)) self.bus = tuple(Bus.make(self, i) for i in range(kind.num_bus)) self.dca = tuple(DCA(self, i) for i in range(kind.num_dca)) + self.fx = tuple(FX(self, i) for i in range(kind.num_fx)) self.fxsend = tuple(FXSend.make(self, i) for i in range(kind.num_fx)) - self.fxreturn = tuple(FXReturn(self, i) for i in range(kind.num_fx)) + self.fxreturn = tuple(FxRtn.make(self, i) for i in range(kind.num_fx)) self.config = Config.make(self) - self.aux = Aux.make(self) - self.rtn = tuple(Rtn.make(self, i) for i in range(kind.num_rtn)) + self.auxreturn = AuxRtn.make(self) + if kind.id_ == "X32": + return type( + f"XAirRemote{kind}", + (XAirRemote,), + { + "__init__": init_x32, + }, + ) return type( f"XAirRemote{kind}", (XAirRemote,), { - "__init__": init, + "__init__": init_xair, }, )