mirror of
https://github.com/onyx-and-iris/xair-api-python.git
synced 2026-04-07 16:43:32 +00:00
Compare commits
58 Commits
main
...
caaf2689ff
| Author | SHA1 | Date | |
|---|---|---|---|
| caaf2689ff | |||
| 7e7aa1b4de | |||
| 2dc096e306 | |||
| ed397e57aa | |||
| 718ecbd982 | |||
| 69cabb3db0 | |||
| 6a2df6352d | |||
| 9c1fa36aed | |||
| 3a70a4c578 | |||
| 8b1b2c7f79 | |||
| 1e5e458105 | |||
| e05460e998 | |||
| d27824d1cf | |||
| 764195a452 | |||
| b295fee6e1 | |||
| 06be2f2831 | |||
| 2d0c0f91f0 | |||
| 6e017b4afc | |||
| 85664c8465 | |||
| a3473d5922 | |||
| e9ef113b5c | |||
| 56ec9a17c0 | |||
| 9a7d98d58b | |||
| f3cf215a76 | |||
| a62a46d61a | |||
| 5eeaff2371 | |||
| c2cf2fe523 | |||
| 265c26eb67 | |||
| 467b769ea4 | |||
| 27d0811091 | |||
| df2d158618 | |||
| 035c8d6507 | |||
| cab3888946 | |||
| eddfb89fa9 | |||
| a1062e92b5 | |||
| ac382c4c32 | |||
|
|
a09b07e1c2 | ||
|
|
e7d38bb9d7 | ||
|
|
2255da4e53 | ||
|
|
6ab7d03a11 | ||
|
|
87217f6f9c | ||
|
|
fb8d3dee75 | ||
|
|
4973eb3215 | ||
|
|
65a817ed87 | ||
|
|
0b20ac953f | ||
|
|
7015383b98 | ||
|
|
981f4b57f8 | ||
|
|
2732e56a86 | ||
|
|
315dc64feb | ||
|
|
4a36e4a2ce | ||
|
|
858275beda | ||
|
|
d6fe34aef4 | ||
|
|
0606c8d107 | ||
|
|
78cd0b489a | ||
|
|
6944ba5128 | ||
|
|
e4dc4d0b13 | ||
|
|
079d1b308d | ||
|
|
a69734b738 |
71
CHANGELOG.md
71
CHANGELOG.md
@@ -9,7 +9,76 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
- [x]
|
- [ ]
|
||||||
|
|
||||||
|
## [2.3.2] - 2024-02-16
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Configurable kwarg `connect_timeout` added. Defaults to 2 seconds.
|
||||||
|
- New error class `XAirRemoteConnectionTimeoutError`. Raised if a connection validation times out.
|
||||||
|
- timeout kwarg + Errors section added to README.
|
||||||
|
|
||||||
|
## [2.3.1] - 2024-02-15
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Module level loggers implemented
|
||||||
|
- class loggers are now child loggers
|
||||||
|
- Passing an incorrect kind_id to the entry point now raises an XAirRemoteError.
|
||||||
|
- Passing a value out of bounds to a setter now logs a warning instead of raising an exception.
|
||||||
|
- Send class added to README.
|
||||||
|
|
||||||
|
## [2.2.4] - 2024-02-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Send class mixed into Strip, AuxRtn, FxRtn. May now be accessed with {Class}.send
|
||||||
|
- Sends example added
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- delay kwarg now applies to getters. See [Issue #6](https://github.com/onyx-and-iris/xair-api-python/issues/6).
|
||||||
|
|
||||||
|
## [2.2.0] - 2022-11-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- mute prop to Bus, FX, LR, RTN, Strip classes.
|
||||||
|
|
||||||
|
## [2.1.0] - 2022-11-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- delay keyword argument
|
||||||
|
- bounds checks for vals passed to lin_set/log_set
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- type checks, prefer duck typing
|
||||||
|
|
||||||
|
## [2.0.0] - 2022-11-07
|
||||||
|
|
||||||
|
Some support for the X32 mixer has been added using an adapter module but the code related to the XAir api has been left largely untouched.
|
||||||
|
However, a couple of changes have been made which are breaking, they are as follows:
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- FX class added to fx module. This now deals with osc addresses that begin with "/fx/". Call it with mixer.fx.
|
||||||
|
- FxRtn class added to rtn module. This now deals with addresses that begin with "/rtn/". Call it with mixer.fxreturn
|
||||||
|
- Aux class renamed to AuxRtn in rtn module. Call it with mixer.auxreturn.
|
||||||
|
|
||||||
|
These changes were made to better resemble the underlying osc api and to better describe the function of the classes.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- A small number of X32 tests. More will be added. XAir tests moved into it's own test module.
|
||||||
|
- XAirRemote lower level section added to README.
|
||||||
|
- Links to OSC command documentation added to README.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- mixer.aux was renamed to mixer.auxreturn
|
||||||
|
|
||||||
## [1.1.0] - 2022-09-05
|
## [1.1.0] - 2022-09-05
|
||||||
|
|
||||||
|
|||||||
105
README.md
105
README.md
@@ -2,7 +2,7 @@
|
|||||||
[](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE)
|
[](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE)
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||
[](https://pycqa.github.io/isort/)
|
[](https://pycqa.github.io/isort/)
|
||||||

|

|
||||||
|
|
||||||
# Xair API
|
# Xair API
|
||||||
|
|
||||||
@@ -40,6 +40,9 @@ import xair_api
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
kind_id = "XR18"
|
||||||
|
ip = "<ip address>"
|
||||||
|
|
||||||
with xair_api.connect(kind_id, ip=ip) as mixer:
|
with xair_api.connect(kind_id, ip=ip) as mixer:
|
||||||
mixer.strip[8].config.name = "sm7b"
|
mixer.strip[8].config.name = "sm7b"
|
||||||
mixer.strip[8].mix.on = True
|
mixer.strip[8].mix.on = True
|
||||||
@@ -49,21 +52,30 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
kind_id = "MR18"
|
|
||||||
ip = "<ip address>"
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
```
|
```
|
||||||
|
|
||||||
## API
|
#### `xair_api.connect(kind_id, ip=ip, delay=0.02, connect_timeout=2)`
|
||||||
|
|
||||||
Currently the following devices are support:
|
Currently the following devices are supported:
|
||||||
|
|
||||||
- `XR18`
|
|
||||||
- `MR18`
|
- `MR18`
|
||||||
|
- `XR18`
|
||||||
- `XR16`
|
- `XR16`
|
||||||
- `XR12`
|
- `XR12`
|
||||||
|
|
||||||
|
The `X32` is partially supported. However, this document covers specifically the `XAir` series.
|
||||||
|
|
||||||
|
The following keyword arguments may be passed:
|
||||||
|
|
||||||
|
- `ip`: ip address of the mixer
|
||||||
|
- `port`: mixer port, defaults to 10023 for x32 and 10024 for xair
|
||||||
|
- `delay`: a delay between each command (applies to the getters). Defaults to 20ms.
|
||||||
|
- a note about delay, stability may rely on network connection. For wired connections the delay can be safely reduced.
|
||||||
|
- `connect_timeout`: amount of time to wait for a validated connection. Defaults to 2s.
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
### XAirRemote class (higher level)
|
### XAirRemote class (higher level)
|
||||||
|
|
||||||
`mixer.lr`
|
`mixer.lr`
|
||||||
@@ -82,6 +94,10 @@ A Bus tuple containing a class for each output bus channel
|
|||||||
|
|
||||||
A DCA tuple containing a class for each DCA group
|
A DCA tuple containing a class for each DCA group
|
||||||
|
|
||||||
|
`mixer.fx`
|
||||||
|
|
||||||
|
An FX tuple containing a class for each FX channel
|
||||||
|
|
||||||
`mixer.fxsend`
|
`mixer.fxsend`
|
||||||
|
|
||||||
An FXSend tuple containing a class for each FX Send channel
|
An FXSend tuple containing a class for each FX Send channel
|
||||||
@@ -90,13 +106,9 @@ An FXSend tuple containing a class for each FX Send channel
|
|||||||
|
|
||||||
An FXReturn tuple containing a class for each FX Return channel
|
An FXReturn tuple containing a class for each FX Return channel
|
||||||
|
|
||||||
`mixer.aux`
|
`mixer.auxreturn`
|
||||||
|
|
||||||
A class representing aux channel
|
A class representing auxreturn channel
|
||||||
|
|
||||||
`mixer.rtn`
|
|
||||||
|
|
||||||
An RTN tuple containing a class for each rtn channel
|
|
||||||
|
|
||||||
`mixer.config`
|
`mixer.config`
|
||||||
|
|
||||||
@@ -110,7 +122,7 @@ Contains the subclasses:
|
|||||||
### `Strip`
|
### `Strip`
|
||||||
|
|
||||||
Contains the subclasses:
|
Contains the subclasses:
|
||||||
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`)
|
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`, `Send`)
|
||||||
|
|
||||||
### `Bus`
|
### `Bus`
|
||||||
|
|
||||||
@@ -122,15 +134,15 @@ Contains the subclasses:
|
|||||||
Contains the subclasses:
|
Contains the subclasses:
|
||||||
(`Config`, `Mix`, `Group`)
|
(`Config`, `Mix`, `Group`)
|
||||||
|
|
||||||
### `Aux`
|
### `FXRtn`
|
||||||
|
|
||||||
Contains the subclasses:
|
Contains the subclasses:
|
||||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
||||||
|
|
||||||
### `Rtn`
|
### `AuxRtn`
|
||||||
|
|
||||||
Contains the subclasses:
|
Contains the subclasses:
|
||||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
||||||
|
|
||||||
### `Subclasses`
|
### `Subclasses`
|
||||||
|
|
||||||
@@ -258,12 +270,14 @@ For the subclass `monitor` the following properties are available
|
|||||||
|
|
||||||
- `level`: float, -inf to 10.0
|
- `level`: float, -inf to 10.0
|
||||||
- `source`: int, from 0 to 14
|
- `source`: int, from 0 to 14
|
||||||
- `chmode` bool
|
- `sourcetrim`: float, from -18.0 to 18.0
|
||||||
- `busmode` bool
|
- `chmode`: bool
|
||||||
- `dim` bool
|
- `busmode`: bool
|
||||||
- `mono` bool
|
- `dim`: bool
|
||||||
- `mute` bool
|
- `dimgain`: float, from -40.0 to 0.0
|
||||||
- `dimfpl` bool
|
- `mono`: bool
|
||||||
|
- `mute`: bool
|
||||||
|
- `dimfpl`: bool
|
||||||
|
|
||||||
for example: `config.monitor.chmode`
|
for example: `config.monitor.chmode`
|
||||||
|
|
||||||
@@ -275,6 +289,43 @@ tuple containing a class for each mute group
|
|||||||
|
|
||||||
for example: `config.mute_group[0].on = True`
|
for example: `config.mute_group[0].on = True`
|
||||||
|
|
||||||
|
### `Send`
|
||||||
|
|
||||||
|
- `level`: float, -inf to 10.0
|
||||||
|
|
||||||
|
for example: `mixer.strip[10].send[3].level = -16.5`
|
||||||
|
|
||||||
|
### XAirRemote class (lower level)
|
||||||
|
|
||||||
|
Send an OSC command directly to the mixer
|
||||||
|
|
||||||
|
- `send(osc command, value)`
|
||||||
|
|
||||||
|
for example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
mixer.send("/ch/01/mix/on", 1)
|
||||||
|
mixer.send("/bus/2/config/name", "somename")
|
||||||
|
```
|
||||||
|
|
||||||
|
Query the value of a command:
|
||||||
|
|
||||||
|
- `query(osc command)`
|
||||||
|
|
||||||
|
for example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
print(mixer.query("/ch/01/mix/on"))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Errors
|
||||||
|
|
||||||
|
- `errors.XAirRemoteError`: Base error class for XAIR Remote.
|
||||||
|
- `errors.XAirRemoteConnectionTimeoutError`:Exception raised when a connection attempt times out.
|
||||||
|
- The following attributes are available:
|
||||||
|
- `ip`: IP of the mixer.
|
||||||
|
- `port`: Port of the mixer.
|
||||||
|
|
||||||
### `Tests`
|
### `Tests`
|
||||||
|
|
||||||
Unplug any expensive equipment before running tests.
|
Unplug any expensive equipment before running tests.
|
||||||
@@ -290,6 +341,12 @@ To run all tests:
|
|||||||
|
|
||||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[XAir OSC Commands](https://behringer.world/wiki/doku.php?id=x-air_osc)
|
||||||
|
|
||||||
|
[X32 OSC Commands](https://wiki.munichmakerlab.de/images/1/17/UNOFFICIAL_X32_OSC_REMOTE_PROTOCOL_%281%29.pdf)
|
||||||
|
|
||||||
## Special Thanks
|
## Special Thanks
|
||||||
|
|
||||||
[Peter Dikant](https://github.com/peterdikant) for writing the base class
|
[Peter Dikant](https://github.com/peterdikant) for writing the base class
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import xair_api
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
kind_id = "XR18"
|
||||||
|
ip = "<ip address>"
|
||||||
|
|
||||||
with xair_api.connect(kind_id, ip=ip) as mixer:
|
with xair_api.connect(kind_id, ip=ip) as mixer:
|
||||||
mixer.strip[8].config.name = "sm7b"
|
mixer.strip[8].config.name = "sm7b"
|
||||||
mixer.strip[8].config.on = True
|
mixer.strip[8].config.on = True
|
||||||
@@ -11,7 +14,4 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
kind_id = "MR18"
|
|
||||||
ip = "<ip address>"
|
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
24
examples/sends/__main__.py
Normal file
24
examples/sends/__main__.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
import xair_api
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with xair_api.connect("XR18", ip="mixer.local") as mixer:
|
||||||
|
for send in mixer.strip[0].send:
|
||||||
|
send.level = -22.8
|
||||||
|
|
||||||
|
mixer.strip[15].send[0].level = -16.5
|
||||||
|
print(mixer.strip[15].send[0].level)
|
||||||
|
|
||||||
|
mixer.auxreturn.send[0].level = -15.5
|
||||||
|
print(mixer.auxreturn.send[0].level)
|
||||||
|
|
||||||
|
mixer.fxreturn[0].send[0].level = -14.5
|
||||||
|
print(mixer.fxreturn[0].send[0].level)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
import obsws_python as obs
|
import obsws_python as obs
|
||||||
|
|
||||||
import xair_api
|
import xair_api
|
||||||
|
|
||||||
|
|
||||||
class Observer:
|
class Observer:
|
||||||
def __init__(self, mixer):
|
def __init__(self, mixer):
|
||||||
self._mixer = mixer
|
self._mixer = mixer
|
||||||
self._cl = obs.EventClient()
|
self._client = obs.EventClient()
|
||||||
self._cl.callback.register(self.on_current_program_scene_changed)
|
self._client.callback.register(self.on_current_program_scene_changed)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_traceback):
|
||||||
|
self._client.disconnect()
|
||||||
|
|
||||||
def on_current_program_scene_changed(self, data):
|
def on_current_program_scene_changed(self, data):
|
||||||
scene = data.scene_name
|
scene = data.scene_name
|
||||||
@@ -28,11 +35,9 @@ class Observer:
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
with xair_api.connect("MR18", ip="mixer.local") as mixer:
|
with xair_api.connect("MR18", ip="mixer.local") as mixer:
|
||||||
Observer(mixer)
|
with Observer(mixer):
|
||||||
|
while _ := input("Press <Enter> to exit\n"):
|
||||||
while cmd := input("<Enter> to exit\n"):
|
pass
|
||||||
if not cmd:
|
|
||||||
break
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
338
poetry.lock
generated
338
poetry.lock
generated
@@ -1,24 +1,36 @@
|
|||||||
[[package]]
|
# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand.
|
||||||
name = "attrs"
|
|
||||||
version = "22.1.0"
|
|
||||||
description = "Classes Without Boilerplate"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.5"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
|
|
||||||
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
|
|
||||||
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
|
|
||||||
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "22.8.0"
|
version = "22.8.0"
|
||||||
description = "The uncompromising code formatter."
|
description = "The uncompromising code formatter."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6.2"
|
python-versions = ">=3.6.2"
|
||||||
|
files = [
|
||||||
|
{file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"},
|
||||||
|
{file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"},
|
||||||
|
{file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"},
|
||||||
|
{file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"},
|
||||||
|
{file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"},
|
||||||
|
{file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"},
|
||||||
|
{file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"},
|
||||||
|
{file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"},
|
||||||
|
{file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"},
|
||||||
|
{file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"},
|
||||||
|
{file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"},
|
||||||
|
{file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"},
|
||||||
|
{file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"},
|
||||||
|
{file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"},
|
||||||
|
{file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"},
|
||||||
|
{file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"},
|
||||||
|
{file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"},
|
||||||
|
{file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"},
|
||||||
|
{file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"},
|
||||||
|
{file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"},
|
||||||
|
{file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"},
|
||||||
|
{file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"},
|
||||||
|
{file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
click = ">=8.0.0"
|
click = ">=8.0.0"
|
||||||
@@ -33,144 +45,236 @@ d = ["aiohttp (>=3.7.4)"]
|
|||||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||||
uvloop = ["uvloop (>=0.15.2)"]
|
uvloop = ["uvloop (>=0.15.2)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cachetools"
|
||||||
|
version = "5.3.2"
|
||||||
|
description = "Extensible memoizing collections and decorators"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"},
|
||||||
|
{file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chardet"
|
||||||
|
version = "5.2.0"
|
||||||
|
description = "Universal encoding detector for Python 3"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
|
||||||
|
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "click"
|
name = "click"
|
||||||
version = "8.1.3"
|
version = "8.1.3"
|
||||||
description = "Composable command line interface toolkit"
|
description = "Composable command line interface toolkit"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||||
|
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorama"
|
name = "colorama"
|
||||||
version = "0.4.5"
|
version = "0.4.6"
|
||||||
description = "Cross-platform colored terminal text."
|
description = "Cross-platform colored terminal text."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||||
|
files = [
|
||||||
|
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||||
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "distlib"
|
||||||
|
version = "0.3.8"
|
||||||
|
description = "Distribution utilities"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
|
||||||
|
{file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "exceptiongroup"
|
||||||
|
version = "1.2.0"
|
||||||
|
description = "Backport of PEP 654 (exception groups)"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
|
||||||
|
{file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
test = ["pytest (>=6)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filelock"
|
||||||
|
version = "3.13.1"
|
||||||
|
description = "A platform independent file lock."
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
|
||||||
|
{file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||||
|
testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"]
|
||||||
|
typing = ["typing-extensions (>=4.8)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iniconfig"
|
name = "iniconfig"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
||||||
|
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "isort"
|
name = "isort"
|
||||||
version = "5.10.1"
|
version = "5.10.1"
|
||||||
description = "A Python utility / library to sort Python imports."
|
description = "A Python utility / library to sort Python imports."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6.1,<4.0"
|
python-versions = ">=3.6.1,<4.0"
|
||||||
|
files = [
|
||||||
|
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
|
||||||
|
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
|
|
||||||
requirements_deprecated_finder = ["pipreqs", "pip-api"]
|
|
||||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||||
|
pipfile-deprecated-finder = ["pipreqs", "requirementslib"]
|
||||||
plugins = ["setuptools"]
|
plugins = ["setuptools"]
|
||||||
|
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mypy-extensions"
|
name = "mypy-extensions"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
||||||
|
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "21.3"
|
version = "23.2"
|
||||||
description = "Core utilities for Python packages"
|
description = "Core utilities for Python packages"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
[package.dependencies]
|
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pathspec"
|
name = "pathspec"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
description = "Utility library for gitignore style pattern matching of file paths."
|
description = "Utility library for gitignore style pattern matching of file paths."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"},
|
||||||
|
{file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "platformdirs"
|
name = "platformdirs"
|
||||||
version = "2.5.2"
|
version = "4.2.0"
|
||||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"},
|
||||||
|
{file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
|
docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"]
|
||||||
test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
|
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pluggy"
|
name = "pluggy"
|
||||||
version = "1.0.0"
|
version = "1.4.0"
|
||||||
description = "plugin and hook calling mechanisms for python"
|
description = "plugin and hook calling mechanisms for python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.6"
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"},
|
||||||
|
{file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
dev = ["pre-commit", "tox"]
|
dev = ["pre-commit", "tox"]
|
||||||
testing = ["pytest", "pytest-benchmark"]
|
testing = ["pytest", "pytest-benchmark"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "py"
|
name = "pyproject-api"
|
||||||
version = "1.11.0"
|
version = "1.6.1"
|
||||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
description = "API to interact with the python pyproject.toml based projects"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "pyproject_api-1.6.1-py3-none-any.whl", hash = "sha256:4c0116d60476b0786c88692cf4e325a9814965e2469c5998b830bba16b183675"},
|
||||||
|
{file = "pyproject_api-1.6.1.tar.gz", hash = "sha256:1817dc018adc0d1ff9ca1ed8c60e1623d5aaca40814b953af14a9cf9a5cae538"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[package.dependencies]
|
||||||
name = "pyparsing"
|
packaging = ">=23.1"
|
||||||
version = "3.0.9"
|
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
|
||||||
category = "dev"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=3.6.8"
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
diagrams = ["railroad-diagrams", "jinja2"]
|
docs = ["furo (>=2023.8.19)", "sphinx (<7.2)", "sphinx-autodoc-typehints (>=1.24)"]
|
||||||
|
testing = ["covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "setuptools (>=68.1.2)", "wheel (>=0.41.2)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "7.1.3"
|
version = "7.4.4"
|
||||||
description = "pytest: simple powerful testing with Python"
|
description = "pytest: simple powerful testing with Python"
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
||||||
|
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
attrs = ">=19.2.0"
|
|
||||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||||
|
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||||
iniconfig = "*"
|
iniconfig = "*"
|
||||||
packaging = "*"
|
packaging = "*"
|
||||||
pluggy = ">=0.12,<2.0"
|
pluggy = ">=0.12,<2.0"
|
||||||
py = ">=1.8.2"
|
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
||||||
tomli = ">=1.0.0"
|
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest-randomly"
|
name = "pytest-randomly"
|
||||||
version = "3.12.0"
|
version = "3.12.0"
|
||||||
description = "Pytest plugin to randomly order tests and control random.seed."
|
description = "Pytest plugin to randomly order tests and control random.seed."
|
||||||
category = "dev"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"},
|
||||||
|
{file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"},
|
||||||
|
]
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
pytest = "*"
|
pytest = "*"
|
||||||
@@ -179,74 +283,72 @@ pytest = "*"
|
|||||||
name = "python-osc"
|
name = "python-osc"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
description = "Open Sound Control server and client implementations in pure Python"
|
description = "Open Sound Control server and client implementations in pure Python"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
|
files = [
|
||||||
|
{file = "python-osc-1.8.0.tar.gz", hash = "sha256:2f8c187c68d239960fb2eddcb5346a62a9b35e64f2de045b3e5e509f475ca73d"},
|
||||||
|
{file = "python_osc-1.8.0-py3-none-any.whl", hash = "sha256:9e2abb2fc9ba2c356f8e951609a03c9c7017bf0bad82cca8490e9b8af9e92a0b"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tomli"
|
name = "tomli"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
description = "A lil' TOML parser"
|
description = "A lil' TOML parser"
|
||||||
category = "main"
|
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.7"
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
[metadata]
|
|
||||||
lock-version = "1.1"
|
|
||||||
python-versions = "^3.10"
|
|
||||||
content-hash = "80440f75f4191b46dc73824fbfc4fd2fc1ea4dfbdba08591cabb600a86ae2400"
|
|
||||||
|
|
||||||
[metadata.files]
|
|
||||||
attrs = []
|
|
||||||
black = []
|
|
||||||
click = [
|
|
||||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
|
||||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
|
||||||
]
|
|
||||||
colorama = [
|
|
||||||
{file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"},
|
|
||||||
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
|
|
||||||
]
|
|
||||||
iniconfig = [
|
|
||||||
{file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
|
|
||||||
{file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
|
|
||||||
]
|
|
||||||
isort = [
|
|
||||||
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
|
|
||||||
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
|
|
||||||
]
|
|
||||||
mypy-extensions = [
|
|
||||||
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
|
|
||||||
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
|
|
||||||
]
|
|
||||||
packaging = [
|
|
||||||
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
|
|
||||||
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
|
|
||||||
]
|
|
||||||
pathspec = []
|
|
||||||
platformdirs = [
|
|
||||||
{file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"},
|
|
||||||
{file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"},
|
|
||||||
]
|
|
||||||
pluggy = [
|
|
||||||
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
|
|
||||||
{file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
|
|
||||||
]
|
|
||||||
py = [
|
|
||||||
{file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
|
|
||||||
{file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
|
|
||||||
]
|
|
||||||
pyparsing = [
|
|
||||||
{file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"},
|
|
||||||
{file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"},
|
|
||||||
]
|
|
||||||
pytest = []
|
|
||||||
pytest-randomly = [
|
|
||||||
{file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"},
|
|
||||||
{file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"},
|
|
||||||
]
|
|
||||||
python-osc = []
|
|
||||||
tomli = [
|
|
||||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tox"
|
||||||
|
version = "4.12.1"
|
||||||
|
description = "tox is a generic virtualenv management and test command line tool"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.8"
|
||||||
|
files = [
|
||||||
|
{file = "tox-4.12.1-py3-none-any.whl", hash = "sha256:c07ea797880a44f3c4f200ad88ad92b446b83079d4ccef89585df64cc574375c"},
|
||||||
|
{file = "tox-4.12.1.tar.gz", hash = "sha256:61aafbeff1bd8a5af84e54ef6e8402f53c6a6066d0782336171ddfbf5362122e"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
cachetools = ">=5.3.2"
|
||||||
|
chardet = ">=5.2"
|
||||||
|
colorama = ">=0.4.6"
|
||||||
|
filelock = ">=3.13.1"
|
||||||
|
packaging = ">=23.2"
|
||||||
|
platformdirs = ">=4.1"
|
||||||
|
pluggy = ">=1.3"
|
||||||
|
pyproject-api = ">=1.6.1"
|
||||||
|
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||||
|
virtualenv = ">=20.25"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.25.2)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.11)"]
|
||||||
|
testing = ["build[virtualenv] (>=1.0.3)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.2)", "devpi-process (>=1)", "diff-cover (>=8.0.2)", "distlib (>=0.3.8)", "flaky (>=3.7)", "hatch-vcs (>=0.4)", "hatchling (>=1.21)", "psutil (>=5.9.7)", "pytest (>=7.4.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-xdist (>=3.5)", "re-assert (>=1.1)", "time-machine (>=2.13)", "wheel (>=0.42)"]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "virtualenv"
|
||||||
|
version = "20.25.0"
|
||||||
|
description = "Virtual Python Environment builder"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=3.7"
|
||||||
|
files = [
|
||||||
|
{file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
|
||||||
|
{file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
distlib = ">=0.3.7,<1"
|
||||||
|
filelock = ">=3.12.2,<4"
|
||||||
|
platformdirs = ">=3.9.1,<5"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||||
|
test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
lock-version = "2.0"
|
||||||
|
python-versions = "^3.10"
|
||||||
|
content-hash = "43084819c12a97bdb6adbd7fd0b077292e8c4e78e4921fd8c0f14dd192a334ff"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "xair-api"
|
name = "xair-api"
|
||||||
version = "1.1.1"
|
version = "2.3.2"
|
||||||
description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
|
description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
|
||||||
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
@@ -12,15 +12,32 @@ python = "^3.10"
|
|||||||
python-osc = "^1.8.0"
|
python-osc = "^1.8.0"
|
||||||
tomli = { version = "^2.0.1", python = "<3.11" }
|
tomli = { version = "^2.0.1", python = "<3.11" }
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
pytest = "^7.1.2"
|
pytest = "^7.4.4"
|
||||||
pytest-randomly = "^3.12.0"
|
pytest-randomly = "^3.12.0"
|
||||||
black = "^22.6.0"
|
black = "^22.6.0"
|
||||||
isort = "^5.10.1"
|
isort = "^5.10.1"
|
||||||
|
tox = "^4.12.1"
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
requires = ["poetry-core>=1.0.0"]
|
requires = ["poetry-core>=1.0.0"]
|
||||||
build-backend = "poetry.core.masonry.api"
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
obs = "examples.xair-obs.__main__:main"
|
obs = "scripts:ex_obs"
|
||||||
|
sends = "scripts:ex_sends"
|
||||||
|
xair = "scripts:test_xair"
|
||||||
|
x32 = "scripts:test_x32"
|
||||||
|
all = "scripts:test_all"
|
||||||
|
|
||||||
|
[tool.tox]
|
||||||
|
legacy_tox_ini = """
|
||||||
|
[tox]
|
||||||
|
envlist = py310,py311,py312
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
allowlist_externals = poetry
|
||||||
|
commands =
|
||||||
|
poetry install -v
|
||||||
|
poetry run pytest tests/
|
||||||
|
"""
|
||||||
|
|||||||
27
scripts.py
Normal file
27
scripts.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def ex_obs():
|
||||||
|
path = Path.cwd() / "examples" / "xair_obs" / "."
|
||||||
|
subprocess.run([sys.executable, str(path)])
|
||||||
|
|
||||||
|
|
||||||
|
def ex_sends():
|
||||||
|
path = Path.cwd() / "examples" / "sends" / "."
|
||||||
|
subprocess.run([sys.executable, str(path)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_xair():
|
||||||
|
path = Path.cwd() / "tests" / "xair"
|
||||||
|
subprocess.run(["pytest", "-v", str(path)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_x32():
|
||||||
|
path = Path.cwd() / "tests" / "x32"
|
||||||
|
subprocess.run(["pytest", "-v", str(path)])
|
||||||
|
|
||||||
|
|
||||||
|
def test_all():
|
||||||
|
subprocess.run(["tox"])
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import sys
|
|
||||||
import threading
|
|
||||||
from dataclasses import dataclass
|
|
||||||
|
|
||||||
import xair_api
|
|
||||||
from xair_api import kinds
|
|
||||||
|
|
||||||
kind_id = "MR18"
|
|
||||||
ip = "mixer.local"
|
|
||||||
|
|
||||||
tests = xair_api.connect(kind_id, ip=ip)
|
|
||||||
|
|
||||||
kind = kinds.get(kind_id)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class Data:
|
|
||||||
"""bounds data to map tests to a kind"""
|
|
||||||
|
|
||||||
name: str = kind.id_
|
|
||||||
dca: int = kind.num_dca - 1
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
|
||||||
def setup_module():
|
|
||||||
print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout)
|
|
||||||
tests.worker = threading.Thread(target=tests.run_server)
|
|
||||||
tests.worker.daemon = True
|
|
||||||
tests.worker.start()
|
|
||||||
tests.validate_connection()
|
|
||||||
|
|
||||||
|
|
||||||
def teardown_module():
|
|
||||||
tests.server.shutdown()
|
|
||||||
|
|||||||
41
tests/x32/__init__.py
Normal file
41
tests/x32/__init__.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import xair_api
|
||||||
|
from xair_api import kinds
|
||||||
|
|
||||||
|
kind_id = "X32"
|
||||||
|
ip = "x32.local"
|
||||||
|
|
||||||
|
tests = xair_api.connect(kind_id, ip=ip)
|
||||||
|
|
||||||
|
kind = kinds.get(kind_id)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Data:
|
||||||
|
"""bounds test data to a kind"""
|
||||||
|
|
||||||
|
name: str = kind.id_
|
||||||
|
dca: int = kind.num_dca - 1
|
||||||
|
strip: int = kind.num_strip - 1
|
||||||
|
bus: int = kind.num_bus - 1
|
||||||
|
fx: int = kind.num_fx - 1
|
||||||
|
auxrtn: int = kind.num_auxrtn - 1
|
||||||
|
matrix: int = kind.num_matrix - 1
|
||||||
|
|
||||||
|
|
||||||
|
data = Data()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module():
|
||||||
|
print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout)
|
||||||
|
tests.worker = threading.Thread(target=tests.run_server)
|
||||||
|
tests.worker.daemon = True
|
||||||
|
tests.worker.start()
|
||||||
|
tests.validate_connection()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module():
|
||||||
|
tests.server.shutdown()
|
||||||
144
tests/x32/test_adapter.py
Normal file
144
tests/x32/test_adapter.py
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from tests.x32 import data, tests
|
||||||
|
|
||||||
|
""" STRIP TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetStripMuteHigher:
|
||||||
|
"""Mute"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "strip")[data.strip]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("mute", True), ("mute", False)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_strip_mute_bool_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetStripMixHigher:
|
||||||
|
"""Mix"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "strip")
|
||||||
|
self.target = getattr(self.target[data.strip], "mix")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("on", True), ("on", False)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_strip_bool_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
""" BUS TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetBusConfigHigher:
|
||||||
|
"""Config"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "bus")
|
||||||
|
self.target = getattr(self.target[data.bus], "config")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("color", 0), ("color", 15)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_bus_int_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
""" AUXIN TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetAuxInPreampHigher:
|
||||||
|
"""Preamp"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "auxin")
|
||||||
|
self.target = getattr(self.target[data.auxrtn], "preamp")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("invert", True), ("invert", False)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_auxrtn_bool_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
""" FX RETURN TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetFXReturnEQHigher:
|
||||||
|
"""EQ"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "fxreturn")
|
||||||
|
self.target = getattr(self.target[data.fx], "eq")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("on", True), ("on", False)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_fxrtn_bool_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
""" MATRIX TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetMatrixDynHigher:
|
||||||
|
"""Dyn"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "matrix")
|
||||||
|
self.target = getattr(self.target[data.matrix], "dyn")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("mode", "comp"), ("mode", "exp")],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_matrix_string_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
|
""" MAIN STEREO TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetMainStereoInsertHigher:
|
||||||
|
"""Insert"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "mainst")
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("mode", "comp"), ("mode", "exp")],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_mainst_string_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
@@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 67"><title>tests: 67</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">67</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">67</text></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="60" height="20" role="img" aria-label="tests: 77"><title>tests: 77</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="60" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="23" height="20" fill="#4c1"/><rect width="60" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="195" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="270">tests</text><text x="195" y="140" transform="scale(.1)" fill="#fff" textLength="270">tests</text><text aria-hidden="true" x="475" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="130">77</text><text x="475" y="140" transform="scale(.1)" fill="#fff" textLength="130">77</text></g></svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
39
tests/xair/__init__.py
Normal file
39
tests/xair/__init__.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import sys
|
||||||
|
import threading
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
import xair_api
|
||||||
|
from xair_api import kinds
|
||||||
|
|
||||||
|
kind_id = "MR18"
|
||||||
|
ip = "mixer.local"
|
||||||
|
|
||||||
|
tests = xair_api.connect(kind_id, ip=ip)
|
||||||
|
|
||||||
|
kind = kinds.get(kind_id)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Data:
|
||||||
|
"""bounds test data to a kind"""
|
||||||
|
|
||||||
|
name: str = kind.id_
|
||||||
|
dca: int = kind.num_dca - 1
|
||||||
|
strip: int = kind.num_strip - 1
|
||||||
|
bus: int = kind.num_bus - 1
|
||||||
|
fx: int = kind.num_fx - 1
|
||||||
|
|
||||||
|
|
||||||
|
data = Data()
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module():
|
||||||
|
print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout)
|
||||||
|
tests.worker = threading.Thread(target=tests.run_server)
|
||||||
|
tests.worker.daemon = True
|
||||||
|
tests.worker.start()
|
||||||
|
tests.validate_connection()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module():
|
||||||
|
tests.server.shutdown()
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
Function RunTests {
|
Function RunTests {
|
||||||
$coverage = "./tests/pytest_coverage.log"
|
"Running tests in directory $PSScriptRoot" | Write-Host
|
||||||
$run_tests = "pytest -v --capture=tee-sys --junitxml=./tests/.coverage.xml"
|
$coverage = Join-Path $PSScriptRoot "pytest_coverage.log"
|
||||||
|
$run_tests = "pytest -v $PSScriptRoot --capture=tee-sys --junitxml=$(Join-Path $PSScriptRoot ".coverage.xml")"
|
||||||
$match_pattern = "^=|^\s*$|^Running|^Using|^plugins|^collecting|^tests"
|
$match_pattern = "^=|^\s*$|^Running|^Using|^plugins|^collecting|^tests"
|
||||||
|
|
||||||
if ( Test-Path $coverage ) { Clear-Content $coverage }
|
if ( Test-Path $coverage ) { Clear-Content $coverage }
|
||||||
@@ -13,7 +14,7 @@ Function RunTests {
|
|||||||
}
|
}
|
||||||
Write-Output "$(Get-TimeStamp)" | Out-File $coverage -Append
|
Write-Output "$(Get-TimeStamp)" | Out-File $coverage -Append
|
||||||
|
|
||||||
Invoke-Expression "genbadge tests -t 90 -i ./tests/.coverage.xml -o ./tests/$kind.svg"
|
Invoke-Expression "genbadge tests -t 90 -i $(Join-Path $PSScriptRoot ".coverage.xml") -o $(Join-Path $PSScriptRoot "$kind.svg")"
|
||||||
}
|
}
|
||||||
|
|
||||||
Function Get-TimeStamp {
|
Function Get-TimeStamp {
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from tests import data, tests
|
from tests.xair import data, tests
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Not every subclass is tested for every superclass to avoid redundancy.
|
Not every subclass is tested for every superclass to avoid redundancy.
|
||||||
@@ -106,6 +106,23 @@ class TestSetAndGetLRGEQHigher:
|
|||||||
""" STRIP TESTS """
|
""" STRIP TESTS """
|
||||||
|
|
||||||
|
|
||||||
|
class TestSetAndGetStripMuteHigher:
|
||||||
|
"""Mute"""
|
||||||
|
|
||||||
|
__test__ = True
|
||||||
|
|
||||||
|
def setup_class(self):
|
||||||
|
self.target = getattr(tests, "strip")[data.strip]
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"param,value",
|
||||||
|
[("mute", True), ("mute", False)],
|
||||||
|
)
|
||||||
|
def test_it_sets_and_gets_strip_mute_bool_params(self, param, value):
|
||||||
|
setattr(self.target, param, value)
|
||||||
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
class TestSetAndGetStripMixHigher:
|
class TestSetAndGetStripMixHigher:
|
||||||
"""Mix"""
|
"""Mix"""
|
||||||
|
|
||||||
@@ -259,7 +276,7 @@ class TestSetAndGetStripAutomixHigher:
|
|||||||
"param,value",
|
"param,value",
|
||||||
[("group", 0), ("group", 2)],
|
[("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)
|
setattr(self.target, param, value)
|
||||||
assert getattr(self.target, param) == value
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
@@ -267,7 +284,7 @@ class TestSetAndGetStripAutomixHigher:
|
|||||||
"param,value",
|
"param,value",
|
||||||
[("weight", -10.5), ("weight", 3.5)],
|
[("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)
|
setattr(self.target, param, value)
|
||||||
assert getattr(self.target, param) == value
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
@@ -326,7 +343,7 @@ class TestSetAndGetBusDynHigher:
|
|||||||
assert getattr(self.target, param) == value
|
assert getattr(self.target, param) == value
|
||||||
|
|
||||||
|
|
||||||
class TestSetAndGetBusDynHigher:
|
class TestSetAndGetBusEQHigher:
|
||||||
"""EQ"""
|
"""EQ"""
|
||||||
|
|
||||||
__test__ = True
|
__test__ = True
|
||||||
@@ -368,6 +385,6 @@ class TestSetAndGetFXSendGroupHigher:
|
|||||||
"param,value",
|
"param,value",
|
||||||
[("dca", 0), ("dca", 12), ("mute", 3), ("mute", 8)],
|
[("dca", 0), ("dca", 12), ("mute", 3), ("mute", 8)],
|
||||||
)
|
)
|
||||||
def test_it_sets_and_gets_bus_bool_params(self, param, value):
|
def test_it_sets_and_gets_fxsend_int_params(self, param, value):
|
||||||
setattr(self.target, param, value)
|
setattr(self.target, param, value)
|
||||||
assert getattr(self.target, param) == value
|
assert getattr(self.target, param) == value
|
||||||
40
xair_api/adapter.py
Normal file
40
xair_api/adapter.py
Normal file
@@ -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 "/main/st"
|
||||||
|
|
||||||
|
|
||||||
|
class MainMono(ILR):
|
||||||
|
@property
|
||||||
|
def address(self) -> str:
|
||||||
|
return "/main/m"
|
||||||
|
|
||||||
|
|
||||||
|
class Matrix(ILR):
|
||||||
|
@property
|
||||||
|
def address(self) -> str:
|
||||||
|
return f"/mtx/{str(self.index).zfill(2)}"
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from .meta import mute_prop
|
||||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
from .shared import EQ, GEQ, Config, Dyn, Group, Insert, Mix
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IBus(abc.ABC):
|
class IBus(abc.ABC):
|
||||||
@@ -10,10 +13,10 @@ class IBus(abc.ABC):
|
|||||||
def __init__(self, remote, index: int):
|
def __init__(self, remote, index: int):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
self.index = index + 1
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str):
|
def getter(self, param: str):
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -52,7 +55,8 @@ class Bus(IBus):
|
|||||||
Mix,
|
Mix,
|
||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return BUS_cls(remote, index)
|
return BUS_cls(remote, index)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
|
||||||
from . import kinds
|
from . import kinds, util
|
||||||
from .errors import XAirRemoteError
|
|
||||||
from .meta import bool_prop
|
from .meta import bool_prop
|
||||||
from .util import _get_level_val, _set_level_val, lin_get, lin_set
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IConfig(abc.ABC):
|
class IConfig(abc.ABC):
|
||||||
@@ -11,10 +12,10 @@ class IConfig(abc.ABC):
|
|||||||
|
|
||||||
def __init__(self, remote):
|
def __init__(self, remote):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str):
|
def getter(self, param: str):
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -35,8 +36,8 @@ class Config(IConfig):
|
|||||||
Returns a Config class of a kind.
|
Returns a Config class of a kind.
|
||||||
"""
|
"""
|
||||||
LINKS_cls = _make_links_mixins[remote.kind.id_]
|
LINKS_cls = _make_links_mixins[remote.kind.id_]
|
||||||
MUTEGROUP_cls = type(f"MuteGroup", (Config.MuteGroup, cls), {})
|
MUTEGROUP_cls = type("MuteGroup", (Config.MuteGroup, cls), {})
|
||||||
MONITOR_cls = type(f"ConfigMonitor", (Config.Monitor, cls), {})
|
MONITOR_cls = type("ConfigMonitor", (Config.Monitor, cls), {})
|
||||||
CONFIG_cls = type(
|
CONFIG_cls = type(
|
||||||
f"Config{remote.kind}",
|
f"Config{remote.kind}",
|
||||||
(cls, LINKS_cls),
|
(cls, LINKS_cls),
|
||||||
@@ -49,7 +50,7 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self) -> str:
|
def address(self) -> str:
|
||||||
return f"/config"
|
return "/config"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def amixenable(self) -> bool:
|
def amixenable(self) -> bool:
|
||||||
@@ -57,8 +58,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@amixenable.setter
|
@amixenable.setter
|
||||||
def amixenable(self, val: bool):
|
def amixenable(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("amixenable is a bool parameter")
|
|
||||||
self.setter("amixenable", 1 if val else 0)
|
self.setter("amixenable", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -67,8 +66,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@amixlock.setter
|
@amixlock.setter
|
||||||
def amixlock(self, val: bool):
|
def amixlock(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("amixlock is a bool parameter")
|
|
||||||
self.setter("amixlock", 1 if val else 0)
|
self.setter("amixlock", 1 if val else 0)
|
||||||
|
|
||||||
class MuteGroup:
|
class MuteGroup:
|
||||||
@@ -87,8 +84,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter(f"{self.i}", 1 if val else 0)
|
self.setter(f"{self.i}", 1 if val else 0)
|
||||||
|
|
||||||
class Monitor:
|
class Monitor:
|
||||||
@@ -98,13 +93,14 @@ class Config(IConfig):
|
|||||||
return f"{root}/solo"
|
return f"{root}/solo"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@util.db_from
|
||||||
def level(self) -> float:
|
def level(self) -> float:
|
||||||
retval = self.getter("level")[0]
|
return self.getter("level")[0]
|
||||||
return _get_level_val(retval)
|
|
||||||
|
|
||||||
@level.setter
|
@level.setter
|
||||||
|
@util.db_to
|
||||||
def level(self, val: float):
|
def level(self, val: float):
|
||||||
_set_level_val(self, val)
|
self.setter("level", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self) -> int:
|
def source(self) -> int:
|
||||||
@@ -112,21 +108,19 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@source.setter
|
@source.setter
|
||||||
def source(self, val: int):
|
def source(self, val: int):
|
||||||
if not isinstance(val, int):
|
self.setter("source", val)
|
||||||
raise XAirRemoteError("source is an int parameter")
|
|
||||||
self.setter(f"source", val)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sourcetrim(self) -> float:
|
def sourcetrim(self) -> float:
|
||||||
return round(lin_get(-18, 18, self.getter("sourcetrim")[0]), 1)
|
return round(util.lin_get(-18, 18, self.getter("sourcetrim")[0]), 1)
|
||||||
|
|
||||||
@sourcetrim.setter
|
@sourcetrim.setter
|
||||||
def sourcetrim(self, val: float):
|
def sourcetrim(self, val: float):
|
||||||
if not isinstance(val, float):
|
if not -18 <= val <= 18:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"sourcetrim is a float parameter, expected value in range -18 to 18"
|
f"sourcetrim got {val}, expected value in range -18.0 to 18.0"
|
||||||
)
|
)
|
||||||
self.setter("sourcetrim", lin_set(-18, 18, val))
|
self.setter("sourcetrim", util.lin_set(-18, 18, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chmode(self) -> bool:
|
def chmode(self) -> bool:
|
||||||
@@ -134,8 +128,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@chmode.setter
|
@chmode.setter
|
||||||
def chmode(self, val: bool):
|
def chmode(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("chmode is a bool parameter")
|
|
||||||
self.setter("chmode", 1 if val else 0)
|
self.setter("chmode", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -144,21 +136,19 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@busmode.setter
|
@busmode.setter
|
||||||
def busmode(self, val: bool):
|
def busmode(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("busmode is a bool parameter")
|
|
||||||
self.setter("busmode", 1 if val else 0)
|
self.setter("busmode", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dimgain(self) -> int:
|
def dimgain(self) -> int:
|
||||||
return int(lin_get(-40, 0, self.getter("dimatt")[0]))
|
return int(util.lin_get(-40, 0, self.getter("dimatt")[0]))
|
||||||
|
|
||||||
@dimgain.setter
|
@dimgain.setter
|
||||||
def dimgain(self, val: int):
|
def dimgain(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not -40 <= val <= 0:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"dimgain is an int parameter, expected value in range -40 to 0"
|
f"dimgain got {val}, expected value in range -40 to 0"
|
||||||
)
|
)
|
||||||
self.setter("dimatt", lin_set(-40, 0, val))
|
self.setter("dimatt", util.lin_set(-40, 0, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dim(self) -> bool:
|
def dim(self) -> bool:
|
||||||
@@ -166,8 +156,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@dim.setter
|
@dim.setter
|
||||||
def dim(self, val: bool):
|
def dim(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("dim is a bool parameter")
|
|
||||||
self.setter("dim", 1 if val else 0)
|
self.setter("dim", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -176,8 +164,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@mono.setter
|
@mono.setter
|
||||||
def mono(self, val: bool):
|
def mono(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("mono is a bool parameter")
|
|
||||||
self.setter("mono", 1 if val else 0)
|
self.setter("mono", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -186,8 +172,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@mute.setter
|
@mute.setter
|
||||||
def mute(self, val: bool):
|
def mute(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("mute is a bool parameter")
|
|
||||||
self.setter("mute", 1 if val else 0)
|
self.setter("mute", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -196,8 +180,6 @@ class Config(IConfig):
|
|||||||
|
|
||||||
@dimfpl.setter
|
@dimfpl.setter
|
||||||
def dimfpl(self, val: bool):
|
def dimfpl(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("dimfpl is a bool parameter")
|
|
||||||
self.setter("dimfpl", 1 if val else 0)
|
self.setter("dimfpl", 1 if val else 0)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IDCA(abc.ABC):
|
class IDCA(abc.ABC):
|
||||||
@@ -9,10 +10,10 @@ class IDCA(abc.ABC):
|
|||||||
def __init__(self, remote, index: int):
|
def __init__(self, remote, index: int):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
self.index = index + 1
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str) -> tuple:
|
def getter(self, param: str) -> tuple:
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -35,19 +36,23 @@ class DCA(IDCA):
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def mute(self) -> bool:
|
||||||
|
return not self.on
|
||||||
|
|
||||||
|
@mute.setter
|
||||||
|
def mute(self, val: bool):
|
||||||
|
self.on = not val
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
return self.getter("config/name")[0]
|
return self.getter("config/name")[0]
|
||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, val: str):
|
def name(self, val: str):
|
||||||
if not isinstance(val, str):
|
self.setter("config/name", val)
|
||||||
raise XAirRemoteError("name is a str parameter")
|
|
||||||
self.setter("config/name")[0]
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color(self) -> int:
|
def color(self) -> int:
|
||||||
@@ -55,6 +60,4 @@ class DCA(IDCA):
|
|||||||
|
|
||||||
@color.setter
|
@color.setter
|
||||||
def color(self, val: int):
|
def color(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("color is an int parameter")
|
|
||||||
self.setter("config/color", val)
|
self.setter("config/color", val)
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
class XAirRemoteError(Exception):
|
class XAirRemoteError(Exception):
|
||||||
"""Base error class for XAIR Remote."""
|
"""Base error class for XAIR Remote."""
|
||||||
|
|
||||||
pass
|
|
||||||
|
class XAirRemoteConnectionTimeoutError(XAirRemoteError):
|
||||||
|
"""Exception raised when a connection attempt times out"""
|
||||||
|
|
||||||
|
def __init__(self, ip, port):
|
||||||
|
self.ip = ip
|
||||||
|
self.port = port
|
||||||
|
|
||||||
|
super().__init__(
|
||||||
|
f"Timeout attempting to connect to mixer at {self.ip}:{self.port}"
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from .meta import mute_prop
|
||||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
from .shared import Config, Group, Mix
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IFX(abc.ABC):
|
class IFX(abc.ABC):
|
||||||
@@ -10,10 +13,10 @@ class IFX(abc.ABC):
|
|||||||
def __init__(self, remote, index: int):
|
def __init__(self, remote, index: int):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
self.index = index + 1
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str):
|
def getter(self, param: str):
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -23,6 +26,22 @@ class IFX(abc.ABC):
|
|||||||
pass
|
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):
|
||||||
|
self.setter("type", val)
|
||||||
|
|
||||||
|
|
||||||
class FXSend(IFX):
|
class FXSend(IFX):
|
||||||
"""Concrete class for fxsend"""
|
"""Concrete class for fxsend"""
|
||||||
|
|
||||||
@@ -44,7 +63,8 @@ class FXSend(IFX):
|
|||||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||||
)(remote, index)
|
)(remote, index)
|
||||||
for _cls in (Config, Mix, Group)
|
for _cls in (Config, Mix, Group)
|
||||||
}
|
},
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return FXSEND_cls(remote, index)
|
return FXSEND_cls(remote, index)
|
||||||
@@ -52,21 +72,3 @@ class FXSend(IFX):
|
|||||||
@property
|
@property
|
||||||
def address(self) -> str:
|
def address(self) -> str:
|
||||||
return f"/fxsend/{self.index}"
|
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)
|
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
from dataclasses import dataclass
|
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
|
@dataclass
|
||||||
class KindMap:
|
class KindMap:
|
||||||
@@ -21,14 +8,24 @@ class KindMap:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MR18KindMap(KindMap):
|
class X32KindMap(KindMap):
|
||||||
# note ch 17-18 defined as aux rtn
|
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 XR18KindMap(KindMap):
|
||||||
|
# note ch 17-18 defined as aux return
|
||||||
id_: str
|
id_: str
|
||||||
num_dca: int = 4
|
num_dca: int = 4
|
||||||
num_strip: int = 16
|
num_strip: int = 16
|
||||||
num_bus: int = 6
|
num_bus: int = 6
|
||||||
num_fx: int = 4
|
num_fx: int = 4
|
||||||
num_rtn: int = 4
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -38,7 +35,6 @@ class XR16KindMap(KindMap):
|
|||||||
num_strip: int = 16
|
num_strip: int = 16
|
||||||
num_bus: int = 4
|
num_bus: int = 4
|
||||||
num_fx: int = 4
|
num_fx: int = 4
|
||||||
num_rtn: int = 4
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -48,12 +44,12 @@ class XR12KindMap(KindMap):
|
|||||||
num_strip: int = 12
|
num_strip: int = 12
|
||||||
num_bus: int = 2
|
num_bus: int = 2
|
||||||
num_fx: int = 4
|
num_fx: int = 4
|
||||||
num_rtn: int = 4
|
|
||||||
|
|
||||||
|
|
||||||
_kinds = {
|
_kinds = {
|
||||||
"XR18": MR18KindMap(id_="XR18"),
|
"X32": X32KindMap(id_="X32"),
|
||||||
"MR18": MR18KindMap(id_="MR18"),
|
"MR18": XR18KindMap(id_="MR18"),
|
||||||
|
"XR18": XR18KindMap(id_="XR18"),
|
||||||
"XR16": XR16KindMap(id_="XR16"),
|
"XR16": XR16KindMap(id_="XR16"),
|
||||||
"XR12": XR12KindMap(id_="XR12"),
|
"XR12": XR12KindMap(id_="XR12"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from .meta import mute_prop
|
||||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
from .shared import EQ, GEQ, Config, Dyn, Insert, Mix
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class ILR(abc.ABC):
|
class ILR(abc.ABC):
|
||||||
"""Abstract Base Class for buses"""
|
"""Abstract Base Class for lr"""
|
||||||
|
|
||||||
def __init__(self, remote):
|
def __init__(self, remote, index: Optional[int] = None):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
|
if index is not None:
|
||||||
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str):
|
def getter(self, param: str):
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -23,10 +29,10 @@ class ILR(abc.ABC):
|
|||||||
|
|
||||||
|
|
||||||
class LR(ILR):
|
class LR(ILR):
|
||||||
"""Concrete class for buses"""
|
"""Concrete class for lr"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make(cls, remote):
|
def make(cls, remote, index=None):
|
||||||
"""
|
"""
|
||||||
Factory function for LR
|
Factory function for LR
|
||||||
|
|
||||||
@@ -41,20 +47,21 @@ class LR(ILR):
|
|||||||
**{
|
**{
|
||||||
_cls.__name__.lower(): type(
|
_cls.__name__.lower(): type(
|
||||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||||
)(remote)
|
)(remote, index)
|
||||||
for _cls in (
|
for _cls in (
|
||||||
Config,
|
Config,
|
||||||
Dyn,
|
Dyn,
|
||||||
Insert,
|
Insert,
|
||||||
GEQ.make(),
|
GEQ.make(),
|
||||||
EQ.make_sixband(cls, remote),
|
EQ.make_sixband(cls, remote, index),
|
||||||
Mix,
|
Mix,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return LR_cls(remote)
|
return LR_cls(remote, index)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self) -> str:
|
def address(self) -> str:
|
||||||
return f"/lr"
|
return "/lr"
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from .errors import XAirRemoteError
|
|
||||||
from .util import lin_get, lin_set
|
from .util import lin_get, lin_set
|
||||||
|
|
||||||
|
|
||||||
@@ -9,8 +8,6 @@ def bool_prop(param):
|
|||||||
return self.getter(param)[0] == 1
|
return self.getter(param)[0] == 1
|
||||||
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError(f"{param} is a boolean parameter")
|
|
||||||
self.setter(param, 1 if val else 0)
|
self.setter(param, 1 if val else 0)
|
||||||
|
|
||||||
return property(fget, fset)
|
return property(fget, fset)
|
||||||
@@ -23,8 +20,6 @@ def string_prop(param):
|
|||||||
return self.getter(param)[0]
|
return self.getter(param)[0]
|
||||||
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
if not isinstance(val, str):
|
|
||||||
raise XAirRemoteError(f"{param} is a string parameter")
|
|
||||||
self.setter(param, val)
|
self.setter(param, val)
|
||||||
|
|
||||||
return property(fget, fset)
|
return property(fget, fset)
|
||||||
@@ -37,8 +32,6 @@ def int_prop(param):
|
|||||||
return int(self.getter(param)[0])
|
return int(self.getter(param)[0])
|
||||||
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError(f"{param} is an integer parameter")
|
|
||||||
self.setter(param, val)
|
self.setter(param, val)
|
||||||
|
|
||||||
return property(fget, fset)
|
return property(fget, fset)
|
||||||
@@ -51,27 +44,32 @@ def float_prop(param):
|
|||||||
return round(self.getter(param)[0], 1)
|
return round(self.getter(param)[0], 1)
|
||||||
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError(f"{param} is a float parameter")
|
|
||||||
self.setter(param, val)
|
self.setter(param, val)
|
||||||
|
|
||||||
return property(fget, fset)
|
return property(fget, fset)
|
||||||
|
|
||||||
|
|
||||||
def geq_prop(param):
|
def geq_prop(param):
|
||||||
# fmt: off
|
|
||||||
opts = {
|
|
||||||
"1k": 1000, "1k25": 1250, "1k6": 1600, "2k": 2000, "3k15": 3150, "4k": 4000,
|
|
||||||
"5k": 5000, "6k3": 6300, "8k": 8000, "10k": 10000, "12k5": 12500, "16k": 16000,
|
|
||||||
"20k": 20000,
|
|
||||||
}
|
|
||||||
# fmt: on
|
|
||||||
param = param.replace("_", ".")
|
param = param.replace("_", ".")
|
||||||
|
|
||||||
def fget(self) -> float:
|
def fget(self) -> float:
|
||||||
return round(lin_get(-15, 15, self.getter(param)[0]), 1)
|
return round(lin_get(-15, 15, self.getter(param)[0]), 1)
|
||||||
|
|
||||||
def fset(self, val):
|
def fset(self, val):
|
||||||
|
if not -15 <= val <= 15:
|
||||||
|
self.logger.warning(
|
||||||
|
f"slider_{param} got {val}, expected value in range -15.0 to 15.0"
|
||||||
|
)
|
||||||
self.setter(param, lin_set(-15, 15, val))
|
self.setter(param, lin_set(-15, 15, val))
|
||||||
|
|
||||||
return property(fget, fset)
|
return property(fget, fset)
|
||||||
|
|
||||||
|
|
||||||
|
def mute_prop():
|
||||||
|
def fget(self):
|
||||||
|
return not self.mix.on
|
||||||
|
|
||||||
|
def fset(self, val):
|
||||||
|
self.mix.on = not val
|
||||||
|
|
||||||
|
return property(fget, fset)
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from .meta import mute_prop
|
||||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
from .shared import EQ, Config, Group, Mix, Preamp, Send
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IRtn(abc.ABC):
|
class IRtn(abc.ABC):
|
||||||
"""Abstract Base Class for aux"""
|
"""Abstract Base Class for rtn"""
|
||||||
|
|
||||||
def __init__(self, remote, index: Optional[int] = None):
|
def __init__(self, remote, index: Optional[int] = None):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
if index is not None:
|
if index is not None:
|
||||||
self.index = index + 1
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str):
|
def getter(self, param: str):
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -25,26 +28,26 @@ class IRtn(abc.ABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Aux(IRtn):
|
class AuxRtn(IRtn):
|
||||||
"""Concrete class for aux"""
|
"""Concrete class for auxrtn"""
|
||||||
|
|
||||||
@classmethod
|
@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.
|
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(
|
AUXRTN_cls = type(
|
||||||
f"Aux{remote.kind}",
|
f"AuxRtn{remote.kind}",
|
||||||
(cls,),
|
(cls,),
|
||||||
{
|
{
|
||||||
**{
|
**{
|
||||||
_cls.__name__.lower(): type(
|
_cls.__name__.lower(): type(
|
||||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||||
)(remote)
|
)(remote, index)
|
||||||
for _cls in (
|
for _cls in (
|
||||||
Config,
|
Config,
|
||||||
Preamp,
|
Preamp,
|
||||||
@@ -52,35 +55,40 @@ class Aux(IRtn):
|
|||||||
Mix,
|
Mix,
|
||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
"send": tuple(
|
||||||
|
Send.make(cls, i, remote)
|
||||||
|
for i in range(remote.kind.num_bus + remote.kind.num_fx)
|
||||||
|
),
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return AUX_cls(remote)
|
return AUXRTN_cls(remote, index)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
return "/rtn/aux"
|
return "/rtn/aux"
|
||||||
|
|
||||||
|
|
||||||
class Rtn(IRtn):
|
class FxRtn(IRtn):
|
||||||
"""Concrete class for rtn"""
|
"""Concrete class for fxrtn"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def make(cls, remote, index):
|
def make(cls, remote, index):
|
||||||
"""
|
"""
|
||||||
Factory function for rtn
|
Factory function for fxrtn
|
||||||
|
|
||||||
Creates a mixin of shared subclasses, sets them as class attributes.
|
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(
|
FXRTN_cls = type(
|
||||||
f"Rtn{remote.kind.id_}",
|
f"FxRtn{remote.kind}",
|
||||||
(cls,),
|
(cls,),
|
||||||
{
|
{
|
||||||
**{
|
**{
|
||||||
_cls.__name__.lower(): type(
|
_cls.__name__.lower(): type(
|
||||||
f"{_cls.__name__}{remote.kind.id_}", (_cls, cls), {}
|
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||||
)(remote, index)
|
)(remote, index)
|
||||||
for _cls in (
|
for _cls in (
|
||||||
Config,
|
Config,
|
||||||
@@ -89,10 +97,15 @@ class Rtn(IRtn):
|
|||||||
Mix,
|
Mix,
|
||||||
Group,
|
Group,
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
"send": tuple(
|
||||||
|
Send.make(cls, i, remote, index)
|
||||||
|
for i in range(remote.kind.num_bus + remote.kind.num_fx)
|
||||||
|
),
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return RTN_cls(remote, index)
|
return FXRTN_cls(remote, index)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def address(self):
|
def address(self):
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
from typing import Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from . import util
|
||||||
from .meta import geq_prop
|
from .meta import geq_prop
|
||||||
from .util import _get_fader_val, _set_fader_val, lin_get, lin_set, log_get, log_set
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Classes shared by /ch, /rtn, /rt/aux, /bus, /fxsend, /lr
|
Classes shared by /ch, /rtn, /rtn/aux, /bus, /fxsend, /lr
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@@ -21,8 +20,6 @@ class Config:
|
|||||||
|
|
||||||
@name.setter
|
@name.setter
|
||||||
def name(self, val: str):
|
def name(self, val: str):
|
||||||
if not isinstance(val, str):
|
|
||||||
raise XAirRemoteError("name is a string parameter")
|
|
||||||
self.setter("name", val)
|
self.setter("name", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -31,8 +28,6 @@ class Config:
|
|||||||
|
|
||||||
@color.setter
|
@color.setter
|
||||||
def color(self, val: int):
|
def color(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("color is an int parameter")
|
|
||||||
self.setter("color", val)
|
self.setter("color", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -41,8 +36,6 @@ class Config:
|
|||||||
|
|
||||||
@inputsource.setter
|
@inputsource.setter
|
||||||
def inputsource(self, val: int):
|
def inputsource(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("inputsource is an int parameter")
|
|
||||||
self.setter("insrc", val)
|
self.setter("insrc", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -51,8 +44,6 @@ class Config:
|
|||||||
|
|
||||||
@usbreturn.setter
|
@usbreturn.setter
|
||||||
def usbreturn(self, val: int):
|
def usbreturn(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("usbreturn is an int parameter")
|
|
||||||
self.setter("rtnsrc", val)
|
self.setter("rtnsrc", val)
|
||||||
|
|
||||||
|
|
||||||
@@ -64,15 +55,15 @@ class Preamp:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def usbtrim(self) -> float:
|
def usbtrim(self) -> float:
|
||||||
return round(lin_get(-18, 18, self.getter("rtntrim")[0]), 1)
|
return round(util.lin_get(-18, 18, self.getter("rtntrim")[0]), 1)
|
||||||
|
|
||||||
@usbtrim.setter
|
@usbtrim.setter
|
||||||
def usbtrim(self, val: float):
|
def usbtrim(self, val: float):
|
||||||
if not isinstance(val, float):
|
if not -18 <= val <= 18:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"usbtrim is a float parameter, expected value in range -18 to 18"
|
f"usbtrim got {val}, expected value in range -18.0 to 18.0"
|
||||||
)
|
)
|
||||||
self.setter("rtntrim", lin_set(-18, 18, val))
|
self.setter("rtntrim", util.lin_set(-18, 18, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def usbinput(self) -> bool:
|
def usbinput(self) -> bool:
|
||||||
@@ -80,8 +71,6 @@ class Preamp:
|
|||||||
|
|
||||||
@usbinput.setter
|
@usbinput.setter
|
||||||
def usbinput(self, val: bool):
|
def usbinput(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("rtnsw is a bool parameter")
|
|
||||||
self.setter("rtnsw", 1 if val else 0)
|
self.setter("rtnsw", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -90,8 +79,6 @@ class Preamp:
|
|||||||
|
|
||||||
@invert.setter
|
@invert.setter
|
||||||
def invert(self, val: bool):
|
def invert(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("invert is a bool parameter")
|
|
||||||
self.setter("invert", 1 if val else 0)
|
self.setter("invert", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -100,19 +87,19 @@ class Preamp:
|
|||||||
|
|
||||||
@highpasson.setter
|
@highpasson.setter
|
||||||
def highpasson(self, val: bool):
|
def highpasson(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("hpon is a bool parameter")
|
|
||||||
self.setter("hpon", 1 if val else 0)
|
self.setter("hpon", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def highpassfilter(self) -> int:
|
def highpassfilter(self) -> int:
|
||||||
return int(log_get(20, 400, self.getter("hpf")[0]))
|
return int(util.log_get(20, 400, self.getter("hpf")[0]))
|
||||||
|
|
||||||
@highpassfilter.setter
|
@highpassfilter.setter
|
||||||
def highpassfilter(self, val: int):
|
def highpassfilter(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 20 <= val <= 400:
|
||||||
raise XAirRemoteError("highpassfilter is an int parameter")
|
self.logger.warning(
|
||||||
self.setter("hpf", log_set(20, 400, val))
|
f"highpassfilter got {val}, expected value in range 20 to 400"
|
||||||
|
)
|
||||||
|
self.setter("hpf", util.log_set(20, 400, val))
|
||||||
|
|
||||||
|
|
||||||
class Gate:
|
class Gate:
|
||||||
@@ -127,8 +114,6 @@ class Gate:
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -139,66 +124,64 @@ class Gate:
|
|||||||
@mode.setter
|
@mode.setter
|
||||||
def mode(self, val: str):
|
def mode(self, val: str):
|
||||||
opts = ("gate", "exp2", "exp3", "exp4", "duck")
|
opts = ("gate", "exp2", "exp3", "exp4", "duck")
|
||||||
if not isinstance(val, str) and val not in opts:
|
if val not in opts:
|
||||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
||||||
self.setter("mode", opts.index(val))
|
self.setter("mode", opts.index(val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threshold(self) -> float:
|
def threshold(self) -> float:
|
||||||
return round(lin_get(-80, 0, self.getter("thr")[0]), 1)
|
return round(util.lin_get(-80, 0, self.getter("thr")[0]), 1)
|
||||||
|
|
||||||
@threshold.setter
|
@threshold.setter
|
||||||
def threshold(self, val: float):
|
def threshold(self, val: float):
|
||||||
if not isinstance(val, float):
|
if not -80 <= val <= 0:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"threshold is a float parameter, expected value in range -80 to 0"
|
f"threshold got {val}, expected value in range -80.0 to 0.0"
|
||||||
)
|
)
|
||||||
self.setter("thr", lin_set(-80, 0, val))
|
self.setter("thr", util.lin_set(-80, 0, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def range(self) -> int:
|
def range(self) -> int:
|
||||||
return int(lin_get(3, 60, self.getter("range")[0]))
|
return int(util.lin_get(3, 60, self.getter("range")[0]))
|
||||||
|
|
||||||
@range.setter
|
@range.setter
|
||||||
def range(self, val: int):
|
def range(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 3 <= val <= 60:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"range got {val}, expected value in range 3 to 60")
|
||||||
"range is an int parameter, expected value in range 3 to 60"
|
self.setter("range", util.lin_set(3, 60, val))
|
||||||
)
|
|
||||||
self.setter("range", lin_set(3, 60, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attack(self) -> int:
|
def attack(self) -> int:
|
||||||
return int(lin_get(0, 120, self.getter("attack")[0]))
|
return int(util.lin_get(0, 120, self.getter("attack")[0]))
|
||||||
|
|
||||||
@attack.setter
|
@attack.setter
|
||||||
def attack(self, val: int):
|
def attack(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 0 <= val <= 120:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"attack got {val}, expected value in range 0 to 120")
|
||||||
"attack is an int parameter, expected value in range 0 to 120"
|
self.setter("attack", util.lin_set(0, 120, val))
|
||||||
)
|
|
||||||
self.setter("attack", lin_set(0, 120, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hold(self) -> Union[float, int]:
|
def hold(self) -> Union[float, int]:
|
||||||
val = log_get(0.02, 2000, self.getter("hold")[0])
|
val = util.log_get(0.02, 2000, self.getter("hold")[0])
|
||||||
return round(val, 1) if val < 100 else int(val)
|
return round(val, 1) if val < 100 else int(val)
|
||||||
|
|
||||||
@hold.setter
|
@hold.setter
|
||||||
def hold(self, val: float):
|
def hold(self, val: float):
|
||||||
self.setter("hold", log_set(0.02, 2000, val))
|
if not 0.02 <= val <= 2000:
|
||||||
|
self.logger.warning(
|
||||||
|
f"hold got {val}, expected value in range 0.02 to 2000.0"
|
||||||
|
)
|
||||||
|
self.setter("hold", util.log_set(0.02, 2000, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def release(self) -> int:
|
def release(self) -> int:
|
||||||
return int(log_get(5, 4000, self.getter("release")[0]))
|
return int(util.log_get(5, 4000, self.getter("release")[0]))
|
||||||
|
|
||||||
@release.setter
|
@release.setter
|
||||||
def release(self, val: int):
|
def release(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 5 <= val <= 4000:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"release got {val}, expected value in range 5 to 4000")
|
||||||
"release is an int parameter, expected value in range 5 to 4000"
|
self.setter("release", util.log_set(5, 4000, val))
|
||||||
)
|
|
||||||
self.setter("release", log_set(5, 4000, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keysource(self):
|
def keysource(self):
|
||||||
@@ -206,8 +189,6 @@ class Gate:
|
|||||||
|
|
||||||
@keysource.setter
|
@keysource.setter
|
||||||
def keysource(self, val):
|
def keysource(self, val):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("keysource is an int parameter")
|
|
||||||
self.setter("keysrc", val)
|
self.setter("keysrc", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -216,8 +197,6 @@ class Gate:
|
|||||||
|
|
||||||
@filteron.setter
|
@filteron.setter
|
||||||
def filteron(self, val: bool):
|
def filteron(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("filteron is a boolean parameter")
|
|
||||||
self.setter("filter/on", 1 if val else 0)
|
self.setter("filter/on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -226,18 +205,20 @@ class Gate:
|
|||||||
|
|
||||||
@filtertype.setter
|
@filtertype.setter
|
||||||
def filtertype(self, val: int):
|
def filtertype(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("filtertype is an int parameter")
|
|
||||||
self.setter("filter/type", val)
|
self.setter("filter/type", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filterfreq(self) -> Union[float, int]:
|
def filterfreq(self) -> Union[float, int]:
|
||||||
retval = log_get(20, 20000, self.getter("filter/f")[0])
|
retval = util.log_get(20, 20000, self.getter("filter/f")[0])
|
||||||
return int(retval) if retval > 1000 else round(retval, 1)
|
return int(retval) if retval > 1000 else round(retval, 1)
|
||||||
|
|
||||||
@filterfreq.setter
|
@filterfreq.setter
|
||||||
def filterfreq(self, val: Union[float, int]):
|
def filterfreq(self, val: Union[float, int]):
|
||||||
self.setter("filter/f", log_set(20, 20000, val))
|
if not 20 <= val <= 20000:
|
||||||
|
self.logger.warning(
|
||||||
|
f"filterfreq got {val}, expected value in range 20 to 20000"
|
||||||
|
)
|
||||||
|
self.setter("filter/f", util.log_set(20, 20000, val))
|
||||||
|
|
||||||
|
|
||||||
class Dyn:
|
class Dyn:
|
||||||
@@ -252,8 +233,6 @@ class Dyn:
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -264,8 +243,8 @@ class Dyn:
|
|||||||
@mode.setter
|
@mode.setter
|
||||||
def mode(self, val: str):
|
def mode(self, val: str):
|
||||||
opts = ("comp", "exp")
|
opts = ("comp", "exp")
|
||||||
if not isinstance(val, str) and val not in opts:
|
if val not in opts:
|
||||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
||||||
self.setter("mode", opts.index(val))
|
self.setter("mode", opts.index(val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -276,8 +255,8 @@ class Dyn:
|
|||||||
@det.setter
|
@det.setter
|
||||||
def det(self, val: str):
|
def det(self, val: str):
|
||||||
opts = ("peak", "rms")
|
opts = ("peak", "rms")
|
||||||
if not isinstance(val, str) and val not in opts:
|
if val not in opts:
|
||||||
raise XAirRemoteError(f"det is a string parameter, expected one of {opts}")
|
self.logger.warning(f"det got {val}, expected one of {opts}")
|
||||||
self.setter("det", opts.index(val))
|
self.setter("det", opts.index(val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -288,21 +267,21 @@ class Dyn:
|
|||||||
@env.setter
|
@env.setter
|
||||||
def env(self, val: str):
|
def env(self, val: str):
|
||||||
opts = ("lin", "log")
|
opts = ("lin", "log")
|
||||||
if not isinstance(val, str) and val not in opts:
|
if val not in opts:
|
||||||
raise XAirRemoteError(f"env is a string parameter, expected one of {opts}")
|
self.logger.warning(f"env got {val}, expected one of {opts}")
|
||||||
self.setter("env", opts.index(val))
|
self.setter("env", opts.index(val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threshold(self) -> float:
|
def threshold(self) -> float:
|
||||||
return round(lin_get(-60, 0, self.getter("thr")[0]), 1)
|
return round(util.lin_get(-60, 0, self.getter("thr")[0]), 1)
|
||||||
|
|
||||||
@threshold.setter
|
@threshold.setter
|
||||||
def threshold(self, val: float):
|
def threshold(self, val: float):
|
||||||
if not isinstance(val, float):
|
if not -60 <= val <= 0:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"threshold is a float parameter, expected value in range -80 to 0"
|
f"threshold got {val}, expected value in range -60.0 to 0"
|
||||||
)
|
)
|
||||||
self.setter("thr", lin_set(-60, 0, val))
|
self.setter("thr", util.lin_set(-60, 0, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ratio(self) -> Union[float, int]:
|
def ratio(self) -> Union[float, int]:
|
||||||
@@ -311,70 +290,70 @@ class Dyn:
|
|||||||
|
|
||||||
@ratio.setter
|
@ratio.setter
|
||||||
def ratio(self, val: int):
|
def ratio(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("ratio is an int parameter")
|
|
||||||
self.setter("ratio", val)
|
self.setter("ratio", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def knee(self) -> int:
|
def knee(self) -> int:
|
||||||
return int(lin_get(0, 5, self.getter("knee")[0]))
|
return int(util.lin_get(0, 5, self.getter("knee")[0]))
|
||||||
|
|
||||||
@knee.setter
|
@knee.setter
|
||||||
def knee(self, val: int):
|
def knee(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 0 <= val <= 5:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"knee got {val}, expected value in range 0 to 5")
|
||||||
"knee is an int parameter, expected value in range 0 to 5"
|
self.setter("knee", util.lin_set(0, 5, val))
|
||||||
)
|
|
||||||
self.setter("knee", lin_set(0, 5, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mgain(self) -> float:
|
def mgain(self) -> float:
|
||||||
return round(lin_get(0, 24, self.getter("mgain")[0]), 1)
|
return round(util.lin_get(0, 24, self.getter("mgain")[0]), 1)
|
||||||
|
|
||||||
@mgain.setter
|
@mgain.setter
|
||||||
def mgain(self, val: float):
|
def mgain(self, val: float):
|
||||||
self.setter("mgain", lin_set(0, 24, val))
|
if not 0 <= val <= 24:
|
||||||
|
self.logger.warning(f"mgain got {val}, expected value in range 0.0 to 24.0")
|
||||||
|
self.setter("mgain", util.lin_set(0, 24, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attack(self) -> int:
|
def attack(self) -> int:
|
||||||
return int(lin_get(0, 120, self.getter("attack")[0]))
|
return int(util.lin_get(0, 120, self.getter("attack")[0]))
|
||||||
|
|
||||||
@attack.setter
|
@attack.setter
|
||||||
def attack(self, val: int):
|
def attack(self, val: int):
|
||||||
self.setter("attack", lin_set(0, 120, val))
|
if not 0 <= val <= 120:
|
||||||
|
self.logger.warning(f"attack got {val}, expected value in range 0 to 120")
|
||||||
|
self.setter("attack", util.lin_set(0, 120, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hold(self) -> Union[float, int]:
|
def hold(self) -> Union[float, int]:
|
||||||
val = log_get(0.02, 2000, self.getter("hold")[0])
|
val = util.log_get(0.02, 2000, self.getter("hold")[0])
|
||||||
return round(val, 1) if val < 100 else int(val)
|
return round(val, 1) if val < 100 else int(val)
|
||||||
|
|
||||||
@hold.setter
|
@hold.setter
|
||||||
def hold(self, val: float):
|
def hold(self, val: float):
|
||||||
self.setter("hold", log_set(0.02, 2000, val))
|
if not 0.02 <= val <= 2000:
|
||||||
|
self.logger.warning(
|
||||||
|
f"hold got {val}, expected value in range 0.02 to 2000.0"
|
||||||
|
)
|
||||||
|
self.setter("hold", util.log_set(0.02, 2000, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def release(self) -> int:
|
def release(self) -> int:
|
||||||
return int(log_get(5, 4000, self.getter("release")[0]))
|
return int(util.log_get(5, 4000, self.getter("release")[0]))
|
||||||
|
|
||||||
@release.setter
|
@release.setter
|
||||||
def release(self, val: int):
|
def release(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 5 <= val <= 4000:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"release got {val}, expected value in range 5 to 4000")
|
||||||
"release is an int parameter, expected value in range 5 to 4000"
|
self.setter("release", util.log_set(5, 4000, val))
|
||||||
)
|
|
||||||
self.setter("release", log_set(5, 4000, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mix(self) -> int:
|
def mix(self) -> int:
|
||||||
return int(lin_get(0, 100, self.getter("mix")[0]))
|
return int(util.lin_get(0, 100, self.getter("mix")[0]))
|
||||||
|
|
||||||
@mix.setter
|
@mix.setter
|
||||||
def mix(self, val: int):
|
def mix(self, val: int):
|
||||||
if not isinstance(val, int):
|
if not 0 <= val <= 100:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(f"mix got {val}, expected value in range 0 to 100")
|
||||||
"mix is an int parameter, expected value in range 0 to 5"
|
self.setter("mix", util.lin_set(0, 100, val))
|
||||||
)
|
|
||||||
self.setter("mix", lin_set(0, 100, val))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keysource(self):
|
def keysource(self):
|
||||||
@@ -382,8 +361,6 @@ class Dyn:
|
|||||||
|
|
||||||
@keysource.setter
|
@keysource.setter
|
||||||
def keysource(self, val):
|
def keysource(self, val):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("keysource is an int parameter")
|
|
||||||
self.setter("keysrc", val)
|
self.setter("keysrc", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -392,8 +369,6 @@ class Dyn:
|
|||||||
|
|
||||||
@auto.setter
|
@auto.setter
|
||||||
def auto(self, val: bool):
|
def auto(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("auto is a boolean parameter")
|
|
||||||
self.setter("auto", 1 if val else 0)
|
self.setter("auto", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -402,8 +377,6 @@ class Dyn:
|
|||||||
|
|
||||||
@filteron.setter
|
@filteron.setter
|
||||||
def filteron(self, val: bool):
|
def filteron(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("filteron is a boolean parameter")
|
|
||||||
self.setter("filter/on", 1 if val else 0)
|
self.setter("filter/on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -412,18 +385,20 @@ class Dyn:
|
|||||||
|
|
||||||
@filtertype.setter
|
@filtertype.setter
|
||||||
def filtertype(self, val: int):
|
def filtertype(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("filtertype is an int parameter")
|
|
||||||
self.setter("filter/type", val)
|
self.setter("filter/type", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filterfreq(self) -> Union[float, int]:
|
def filterfreq(self) -> Union[float, int]:
|
||||||
retval = log_get(20, 20000, self.getter("filter/f")[0])
|
retval = util.log_get(20, 20000, self.getter("filter/f")[0])
|
||||||
return int(retval) if retval > 1000 else round(retval, 1)
|
return int(retval) if retval > 1000 else round(retval, 1)
|
||||||
|
|
||||||
@filterfreq.setter
|
@filterfreq.setter
|
||||||
def filterfreq(self, val: Union[float, int]):
|
def filterfreq(self, val: Union[float, int]):
|
||||||
self.setter("filter/f", log_set(20, 20000, val))
|
if not 20 <= val <= 20000:
|
||||||
|
self.logger.warning(
|
||||||
|
f"filterfreq got {val}, expected value in range 20 to 20000"
|
||||||
|
)
|
||||||
|
self.setter("filter/f", util.log_set(20, 20000, val))
|
||||||
|
|
||||||
|
|
||||||
class Insert:
|
class Insert:
|
||||||
@@ -438,8 +413,6 @@ class Insert:
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -448,8 +421,6 @@ class Insert:
|
|||||||
|
|
||||||
@sel.setter
|
@sel.setter
|
||||||
def sel(self, val: int):
|
def sel(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("sel is an int parameter")
|
|
||||||
self.setter("sel", val)
|
self.setter("sel", val)
|
||||||
|
|
||||||
|
|
||||||
@@ -495,8 +466,6 @@ class EQ:
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -507,15 +476,12 @@ class EQ:
|
|||||||
@mode.setter
|
@mode.setter
|
||||||
def mode(self, val: str):
|
def mode(self, val: str):
|
||||||
opts = ("peq", "geq", "teq")
|
opts = ("peq", "geq", "teq")
|
||||||
if not isinstance(val, str) and val not in opts:
|
if val not in opts:
|
||||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
||||||
self.setter("mode", opts.index(val))
|
self.setter("mode", opts.index(val))
|
||||||
|
|
||||||
class EQBand:
|
class EQBand:
|
||||||
def __init__(self, i, remote, index):
|
def __init__(self, i, remote, index):
|
||||||
if index is None:
|
|
||||||
super(EQ.EQBand, self).__init__(remote)
|
|
||||||
else:
|
|
||||||
super(EQ.EQBand, self).__init__(remote, index)
|
super(EQ.EQBand, self).__init__(remote, index)
|
||||||
self.i = i
|
self.i = i
|
||||||
|
|
||||||
@@ -530,35 +496,45 @@ class EQ:
|
|||||||
|
|
||||||
@type.setter
|
@type.setter
|
||||||
def type(self, val: int):
|
def type(self, val: int):
|
||||||
if not isinstance(val, int):
|
self.setter("type", val)
|
||||||
raise XAirRemoteError("type is an int parameter")
|
|
||||||
self.setter(f"type", val)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def frequency(self) -> float:
|
def frequency(self) -> float:
|
||||||
retval = log_get(20, 20000, self.getter("f")[0])
|
retval = util.log_get(20, 20000, self.getter("f")[0])
|
||||||
return round(retval, 1)
|
return round(retval, 1)
|
||||||
|
|
||||||
@frequency.setter
|
@frequency.setter
|
||||||
def frequency(self, val: float):
|
def frequency(self, val: float):
|
||||||
self.setter("f", log_set(20, 20000, val))
|
if not 20 <= val <= 20000:
|
||||||
|
self.logger.warning(
|
||||||
|
f"frequency got {val}, expected value in range 20.0 to 20000.0"
|
||||||
|
)
|
||||||
|
self.setter("f", util.log_set(20, 20000, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gain(self) -> float:
|
def gain(self) -> float:
|
||||||
return round(lin_get(-15, 15, self.getter("g")[0]), 1)
|
return round(util.lin_get(-15, 15, self.getter("g")[0]), 1)
|
||||||
|
|
||||||
@gain.setter
|
@gain.setter
|
||||||
def gain(self, val: float):
|
def gain(self, val: float):
|
||||||
self.setter("g", lin_set(-15, 15, val))
|
if not -15 <= val <= 15:
|
||||||
|
self.logger.warning(
|
||||||
|
f"gain got {val}, expected value in range -15.0 to 15.0"
|
||||||
|
)
|
||||||
|
self.setter("g", util.lin_set(-15, 15, val))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def quality(self) -> float:
|
def quality(self) -> float:
|
||||||
retval = log_get(0.3, 10, self.getter("q")[0])
|
retval = util.log_get(0.3, 10, self.getter("q")[0])
|
||||||
return round(retval, 1)
|
return round(retval, 1)
|
||||||
|
|
||||||
@quality.setter
|
@quality.setter
|
||||||
def quality(self, val: float):
|
def quality(self, val: float):
|
||||||
self.setter("q", log_set(0.3, 10, val))
|
if not 0.3 <= val <= 10:
|
||||||
|
self.logger.warning(
|
||||||
|
f"quality got {val}, expected value in range 0.3 to 10.0"
|
||||||
|
)
|
||||||
|
self.setter("q", util.log_set(0.3, 10, val))
|
||||||
|
|
||||||
|
|
||||||
class GEQ:
|
class GEQ:
|
||||||
@@ -573,7 +549,7 @@ class GEQ:
|
|||||||
f"slider_{param}": geq_prop(param)
|
f"slider_{param}": geq_prop(param)
|
||||||
for param in [
|
for param in [
|
||||||
"20", "25", "31_5", "40", "50", "63", "80", "100", "125",
|
"20", "25", "31_5", "40", "50", "63", "80", "100", "125",
|
||||||
"160", "200", "250", "315" "400", "500", "630", "800", "1k",
|
"160", "200", "250", "315", "400", "500", "630", "800", "1k",
|
||||||
"1k25", "1k6", "2k", "2k5", "3k15", "4k", "5k", "6k3", "8k",
|
"1k25", "1k6", "2k", "2k5", "3k15", "4k", "5k", "6k3", "8k",
|
||||||
"10k", "12k5", "16k", "20k",
|
"10k", "12k5", "16k", "20k",
|
||||||
]
|
]
|
||||||
@@ -600,18 +576,17 @@ class Mix:
|
|||||||
|
|
||||||
@on.setter
|
@on.setter
|
||||||
def on(self, val: bool):
|
def on(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("on is a boolean parameter")
|
|
||||||
self.setter("on", 1 if val else 0)
|
self.setter("on", 1 if val else 0)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@util.db_from
|
||||||
def fader(self) -> float:
|
def fader(self) -> float:
|
||||||
retval = self.getter("fader")[0]
|
return self.getter("fader")[0]
|
||||||
return _get_fader_val(retval)
|
|
||||||
|
|
||||||
@fader.setter
|
@fader.setter
|
||||||
|
@util.db_to
|
||||||
def fader(self, val: float):
|
def fader(self, val: float):
|
||||||
_set_fader_val(self, val)
|
self.setter("fader", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lr(self) -> bool:
|
def lr(self) -> bool:
|
||||||
@@ -619,8 +594,6 @@ class Mix:
|
|||||||
|
|
||||||
@lr.setter
|
@lr.setter
|
||||||
def lr(self, val: bool):
|
def lr(self, val: bool):
|
||||||
if not isinstance(val, bool):
|
|
||||||
raise XAirRemoteError("lr is a boolean parameter")
|
|
||||||
self.setter("lr", 1 if val else 0)
|
self.setter("lr", 1 if val else 0)
|
||||||
|
|
||||||
|
|
||||||
@@ -636,8 +609,6 @@ class Group:
|
|||||||
|
|
||||||
@dca.setter
|
@dca.setter
|
||||||
def dca(self, val: int):
|
def dca(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("dca is an int parameter")
|
|
||||||
self.setter("dca", val)
|
self.setter("dca", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -646,8 +617,6 @@ class Group:
|
|||||||
|
|
||||||
@mute.setter
|
@mute.setter
|
||||||
def mute(self, val: int):
|
def mute(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("mute is an int parameter")
|
|
||||||
self.setter("mute", val)
|
self.setter("mute", val)
|
||||||
|
|
||||||
|
|
||||||
@@ -663,18 +632,42 @@ class Automix:
|
|||||||
|
|
||||||
@group.setter
|
@group.setter
|
||||||
def group(self, val: int):
|
def group(self, val: int):
|
||||||
if not isinstance(val, int):
|
|
||||||
raise XAirRemoteError("group is an int parameter")
|
|
||||||
self.setter("group", val)
|
self.setter("group", val)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def weight(self) -> float:
|
def weight(self) -> float:
|
||||||
return round(lin_get(-12, 12, self.getter("weight")[0]), 1)
|
return round(util.lin_get(-12, 12, self.getter("weight")[0]), 1)
|
||||||
|
|
||||||
@weight.setter
|
@weight.setter
|
||||||
def weight(self, val: float):
|
def weight(self, val: float):
|
||||||
if not isinstance(val, float):
|
if not -12 <= val <= 12:
|
||||||
raise XAirRemoteError(
|
self.logger.warning(
|
||||||
"weight is a float parameter, expected value in range -12 to 12"
|
f"weight got {val}, expected value in range -12.0 to 12.0"
|
||||||
)
|
)
|
||||||
self.setter("weight", lin_set(-12, 12, val))
|
self.setter("weight", util.lin_set(-12, 12, val))
|
||||||
|
|
||||||
|
|
||||||
|
class Send:
|
||||||
|
def __init__(self, i, remote, index: Optional[int] = None):
|
||||||
|
super(Send, self).__init__(remote, index)
|
||||||
|
self.i = i + 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def make(cls, _cls, i, remote, index=None):
|
||||||
|
SEND_cls = type("Send", (cls, _cls), {})
|
||||||
|
return SEND_cls(i, remote, index)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def address(self) -> str:
|
||||||
|
root = super(Send, self).address
|
||||||
|
return f"{root}/mix/{str(self.i).zfill(2)}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
@util.db_from
|
||||||
|
def level(self) -> float:
|
||||||
|
return self.getter("level")[0]
|
||||||
|
|
||||||
|
@level.setter
|
||||||
|
@util.db_to
|
||||||
|
def level(self, val: float):
|
||||||
|
self.setter("level", val)
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
|
|
||||||
from .errors import XAirRemoteError
|
from .meta import mute_prop
|
||||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
from .shared import EQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp, Send
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class IStrip(abc.ABC):
|
class IStrip(abc.ABC):
|
||||||
@@ -10,10 +13,10 @@ class IStrip(abc.ABC):
|
|||||||
def __init__(self, remote, index: int):
|
def __init__(self, remote, index: int):
|
||||||
self._remote = remote
|
self._remote = remote
|
||||||
self.index = index + 1
|
self.index = index + 1
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
|
||||||
def getter(self, param: str) -> tuple:
|
def getter(self, param: str) -> tuple:
|
||||||
self._remote.send(f"{self.address}/{param}")
|
return self._remote.query(f"{self.address}/{param}")
|
||||||
return self._remote.info_response
|
|
||||||
|
|
||||||
def setter(self, param: str, val: int):
|
def setter(self, param: str, val: int):
|
||||||
self._remote.send(f"{self.address}/{param}", val)
|
self._remote.send(f"{self.address}/{param}", val)
|
||||||
@@ -35,6 +38,7 @@ class Strip(IStrip):
|
|||||||
|
|
||||||
Returns a Strip class of a kind.
|
Returns a Strip class of a kind.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
STRIP_cls = type(
|
STRIP_cls = type(
|
||||||
f"Strip{remote.kind}",
|
f"Strip{remote.kind}",
|
||||||
(cls,),
|
(cls,),
|
||||||
@@ -55,6 +59,11 @@ class Strip(IStrip):
|
|||||||
Automix,
|
Automix,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
"send": tuple(
|
||||||
|
Send.make(cls, i, remote, index)
|
||||||
|
for i in range(remote.kind.num_bus + remote.kind.num_fx)
|
||||||
|
),
|
||||||
|
"mute": mute_prop(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
return STRIP_cls(remote, index)
|
return STRIP_cls(remote, index)
|
||||||
|
|||||||
@@ -1,5 +1,35 @@
|
|||||||
|
import functools
|
||||||
|
import time
|
||||||
from math import exp, log
|
from math import exp, log
|
||||||
|
|
||||||
|
from .errors import XAirRemoteConnectionTimeoutError
|
||||||
|
|
||||||
|
|
||||||
|
def timeout(func):
|
||||||
|
"""
|
||||||
|
Times out the validate_connection function once time elapsed exceeds remote.connect_timeout.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
remote, *_ = args
|
||||||
|
|
||||||
|
err = None
|
||||||
|
start = time.time()
|
||||||
|
while time.time() < start + remote.connect_timeout:
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
remote.logger.debug(f"login time: {round(time.time() - start, 2)}")
|
||||||
|
err = None
|
||||||
|
break
|
||||||
|
except XAirRemoteConnectionTimeoutError as e:
|
||||||
|
err = e
|
||||||
|
continue
|
||||||
|
if err:
|
||||||
|
raise err
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def lin_get(min, max, val):
|
def lin_get(min, max, val):
|
||||||
return min + (max - min) * val
|
return min + (max - min) * val
|
||||||
@@ -17,7 +47,13 @@ def log_set(min, max, val):
|
|||||||
return log(val / min) / log(max / min)
|
return log(val / min) / log(max / min)
|
||||||
|
|
||||||
|
|
||||||
def _get_fader_val(retval):
|
def db_from(func):
|
||||||
|
"""fader|level converter for getters"""
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
retval = func(*args, **kwargs)
|
||||||
|
|
||||||
if retval >= 1:
|
if retval >= 1:
|
||||||
return 10
|
return 10
|
||||||
elif retval >= 0.5:
|
elif retval >= 0.5:
|
||||||
@@ -31,47 +67,28 @@ def _get_fader_val(retval):
|
|||||||
else:
|
else:
|
||||||
return -90
|
return -90
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
def _set_fader_val(self, val):
|
|
||||||
|
def db_to(func):
|
||||||
|
"""fader|level converter for setters"""
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
param, val = args
|
||||||
if val >= 10:
|
if val >= 10:
|
||||||
self.setter("fader", 1)
|
val = 1
|
||||||
elif val >= -10:
|
elif val >= -10:
|
||||||
self.setter("fader", (val + 30) / 40)
|
val = (val + 30) / 40
|
||||||
elif val >= -30:
|
elif val >= -30:
|
||||||
self.setter("fader", (val + 50) / 80)
|
val = (val + 50) / 80
|
||||||
elif val >= -60:
|
elif val >= -60:
|
||||||
self.setter("fader", (val + 70) / 160)
|
val = (val + 70) / 160
|
||||||
elif val >= -90:
|
elif val >= -90:
|
||||||
self.setter("fader", (val + 90) / 480)
|
val = (val + 90) / 480
|
||||||
else:
|
else:
|
||||||
self.setter("fader", 0)
|
val = 0
|
||||||
|
|
||||||
|
func(param, val, **kwargs)
|
||||||
|
|
||||||
def _get_level_val(retval):
|
return wrapper
|
||||||
if retval >= 1:
|
|
||||||
return 10
|
|
||||||
elif retval >= 0.5:
|
|
||||||
return round((40 * retval) - 30, 1)
|
|
||||||
elif retval >= 0.25:
|
|
||||||
return round((80 * retval) - 50, 1)
|
|
||||||
elif retval >= 0.0625:
|
|
||||||
return round((160 * retval) - 70, 1)
|
|
||||||
elif retval >= 0:
|
|
||||||
return round((480 * retval) - 90, 1)
|
|
||||||
else:
|
|
||||||
return -90
|
|
||||||
|
|
||||||
|
|
||||||
def _set_level_val(self, val):
|
|
||||||
if val >= 10:
|
|
||||||
self.setter("level", 1)
|
|
||||||
elif val >= -10:
|
|
||||||
self.setter("level", (val + 30) / 40)
|
|
||||||
elif val >= -30:
|
|
||||||
self.setter("level", (val + 50) / 80)
|
|
||||||
elif val >= -60:
|
|
||||||
self.setter("level", (val + 70) / 160)
|
|
||||||
elif val >= -90:
|
|
||||||
self.setter("level", (val + 90) / 480)
|
|
||||||
else:
|
|
||||||
self.setter("level", 0)
|
|
||||||
|
|||||||
125
xair_api/xair.py
125
xair_api/xair.py
@@ -1,8 +1,9 @@
|
|||||||
import abc
|
import abc
|
||||||
|
import logging
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional, Union
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tomllib
|
import tomllib
|
||||||
@@ -13,32 +14,31 @@ from pythonosc.dispatcher import Dispatcher
|
|||||||
from pythonosc.osc_message_builder import OscMessageBuilder
|
from pythonosc.osc_message_builder import OscMessageBuilder
|
||||||
from pythonosc.osc_server import BlockingOSCUDPServer
|
from pythonosc.osc_server import BlockingOSCUDPServer
|
||||||
|
|
||||||
from . import kinds
|
from . import adapter, kinds, util
|
||||||
from .bus import Bus
|
from .bus import Bus
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .dca import DCA
|
from .dca import DCA
|
||||||
from .errors import XAirRemoteError
|
from .errors import XAirRemoteConnectionTimeoutError, XAirRemoteError
|
||||||
from .fx import FXReturn, FXSend
|
from .fx import FX, FXSend
|
||||||
from .kinds import KindMap
|
from .kinds import KindMap
|
||||||
from .lr import LR
|
from .lr import LR
|
||||||
from .rtn import Aux, Rtn
|
from .rtn import AuxRtn, FxRtn
|
||||||
from .strip import Strip
|
from .strip import Strip
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class OSCClientServer(BlockingOSCUDPServer):
|
class OSCClientServer(BlockingOSCUDPServer):
|
||||||
def __init__(self, address: str, dispatcher: Dispatcher):
|
def __init__(self, address: str, dispatcher: Dispatcher):
|
||||||
super().__init__(("", 0), dispatcher)
|
super().__init__(("", 0), dispatcher)
|
||||||
self.xr_address = address
|
self.xr_address = address
|
||||||
|
|
||||||
def send_message(self, address: str, value: str):
|
def send_message(self, address: str, vals: Optional[Union[str, list]]):
|
||||||
builder = OscMessageBuilder(address=address)
|
builder = OscMessageBuilder(address=address)
|
||||||
if value is None:
|
vals = vals if vals is not None else []
|
||||||
values = list()
|
if not isinstance(vals, list):
|
||||||
elif isinstance(value, list):
|
vals = [vals]
|
||||||
values = value
|
for val in vals:
|
||||||
else:
|
|
||||||
values = [value]
|
|
||||||
for val in values:
|
|
||||||
builder.add_arg(val)
|
builder.add_arg(val)
|
||||||
msg = builder.build()
|
msg = builder.build()
|
||||||
self.socket.sendto(msg.dgram, self.xr_address)
|
self.socket.sendto(msg.dgram, self.xr_address)
|
||||||
@@ -47,21 +47,18 @@ class OSCClientServer(BlockingOSCUDPServer):
|
|||||||
class XAirRemote(abc.ABC):
|
class XAirRemote(abc.ABC):
|
||||||
"""Handles the communication with the mixer via the OSC protocol"""
|
"""Handles the communication with the mixer via the OSC protocol"""
|
||||||
|
|
||||||
_CONNECT_TIMEOUT = 0.5
|
_info_response = []
|
||||||
_WAIT_TIME = 0.025
|
|
||||||
_REFRESH_TIMEOUT = 5
|
|
||||||
|
|
||||||
XAIR_PORT = 10024
|
|
||||||
|
|
||||||
info_response = []
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
dispatcher = Dispatcher()
|
dispatcher = Dispatcher()
|
||||||
dispatcher.set_default_handler(self.msg_handler)
|
dispatcher.set_default_handler(self.msg_handler)
|
||||||
self.xair_ip = kwargs["ip"] or self._ip_from_toml()
|
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):
|
self._delay = kwargs["delay"]
|
||||||
raise XAirRemoteError("No valid ip or password detected")
|
self.connect_timeout = kwargs["connect_timeout"]
|
||||||
|
self.logger = logger.getChild(self.__class__.__name__)
|
||||||
|
if not self.xair_ip:
|
||||||
|
raise XAirRemoteError("No valid ip detected")
|
||||||
self.server = OSCClientServer((self.xair_ip, self.xair_port), dispatcher)
|
self.server = OSCClientServer((self.xair_ip, self.xair_port), dispatcher)
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
@@ -76,29 +73,32 @@ class XAirRemote(abc.ABC):
|
|||||||
conn = tomllib.load(f)
|
conn = tomllib.load(f)
|
||||||
return conn["connection"].get("ip")
|
return conn["connection"].get("ip")
|
||||||
|
|
||||||
|
@util.timeout
|
||||||
def validate_connection(self):
|
def validate_connection(self):
|
||||||
self.send("/xinfo")
|
if not self.query("/xinfo"):
|
||||||
time.sleep(self._CONNECT_TIMEOUT)
|
raise XAirRemoteConnectionTimeoutError(self.xair_ip, self.xair_port)
|
||||||
if len(self.info_response) > 0:
|
self.logger.info(
|
||||||
print(f"Successfully connected to {self.info_response[2]}.")
|
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."
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def info_response(self):
|
||||||
|
return self._info_response
|
||||||
|
|
||||||
def run_server(self):
|
def run_server(self):
|
||||||
self.server.serve_forever()
|
self.server.serve_forever()
|
||||||
|
|
||||||
def msg_handler(self, addr, *data):
|
def msg_handler(self, addr, *data):
|
||||||
self.info_response = data[:]
|
self.logger.debug(f"received: {addr} {data if data else ''}")
|
||||||
|
self._info_response = data[:]
|
||||||
|
|
||||||
def send(self, address: str, param: Optional[str] = None):
|
def send(self, addr: str, param: Optional[str] = None):
|
||||||
self.server.send_message(address, param)
|
self.logger.debug(f"sending: {addr} {param if param is not None else ''}")
|
||||||
time.sleep(self._WAIT_TIME)
|
self.server.send_message(addr, param)
|
||||||
|
|
||||||
def _query(self, address):
|
def query(self, address):
|
||||||
self.send(address)
|
self.send(address)
|
||||||
time.sleep(self._WAIT_TIME)
|
time.sleep(self._delay)
|
||||||
return self.info_response
|
return self.info_response
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_tr):
|
def __exit__(self, exc_type, exc_value, exc_tr):
|
||||||
@@ -112,8 +112,36 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
|||||||
The returned class will subclass XAirRemote.
|
The returned class will subclass XAirRemote.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def init(self, *args, **kwargs):
|
def init_x32(self, *args, **kwargs):
|
||||||
defaultkwargs = {"ip": None, "port": None}
|
defaultkwargs = {
|
||||||
|
"ip": None,
|
||||||
|
"port": 10023,
|
||||||
|
"delay": 0.02,
|
||||||
|
"connect_timeout": 2,
|
||||||
|
}
|
||||||
|
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.auxin = tuple(adapter.AuxRtn.make(self, i) for i in range(kind.num_auxrtn))
|
||||||
|
self.config = Config.make(self)
|
||||||
|
|
||||||
|
def init_xair(self, *args, **kwargs):
|
||||||
|
defaultkwargs = {
|
||||||
|
"ip": None,
|
||||||
|
"port": 10024,
|
||||||
|
"delay": 0.02,
|
||||||
|
"connect_timeout": 2,
|
||||||
|
}
|
||||||
kwargs = defaultkwargs | kwargs
|
kwargs = defaultkwargs | kwargs
|
||||||
XAirRemote.__init__(self, *args, **kwargs)
|
XAirRemote.__init__(self, *args, **kwargs)
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
@@ -121,17 +149,25 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
|||||||
self.strip = tuple(Strip.make(self, i) for i in range(kind.num_strip))
|
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.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.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.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.auxreturn = AuxRtn.make(self)
|
||||||
self.config = Config.make(self)
|
self.config = Config.make(self)
|
||||||
self.aux = Aux.make(self)
|
|
||||||
self.rtn = tuple(Rtn.make(self, i) for i in range(kind.num_rtn))
|
|
||||||
|
|
||||||
|
if kind.id_ == "X32":
|
||||||
return type(
|
return type(
|
||||||
f"XAirRemote{kind}",
|
f"XAirRemote{kind}",
|
||||||
(XAirRemote,),
|
(XAirRemote,),
|
||||||
{
|
{
|
||||||
"__init__": init,
|
"__init__": init_x32,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return type(
|
||||||
|
f"XAirRemote{kind}",
|
||||||
|
(XAirRemote,),
|
||||||
|
{
|
||||||
|
"__init__": init_xair,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -145,9 +181,10 @@ def request_remote_obj(kind_id: str, *args, **kwargs) -> XAirRemote:
|
|||||||
|
|
||||||
Returns a reference to an XAirRemote class of a kind
|
Returns a reference to an XAirRemote class of a kind
|
||||||
"""
|
"""
|
||||||
|
|
||||||
XAIRREMOTE_cls = None
|
XAIRREMOTE_cls = None
|
||||||
try:
|
try:
|
||||||
XAIRREMOTE_cls = _remotes[kind_id]
|
XAIRREMOTE_cls = _remotes[kind_id]
|
||||||
except ValueError as e:
|
except KeyError as e:
|
||||||
raise SystemExit(e)
|
raise XAirRemoteError(f"Unknown mixer kind '{kind_id}'") from e
|
||||||
return XAIRREMOTE_cls(*args, **kwargs)
|
return XAIRREMOTE_cls(*args, **kwargs)
|
||||||
|
|||||||
Reference in New Issue
Block a user