mirror of
https://github.com/onyx-and-iris/xair-api-python.git
synced 2026-04-07 16:43:32 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7fbc7612c3 | |||
| 3111508b38 | |||
|
|
c27d68b009 | ||
| c39a3fc87d | |||
|
|
6cc3bdff96 | ||
| 669aba4cc4 | |||
| ce4fccdc85 | |||
| deb298537f | |||
| f963997f35 | |||
| 39f3a9dd98 | |||
|
|
6cccfd0f56 | ||
| 05df111e69 | |||
| 2b91863337 | |||
| cf4a1d295f | |||
| b5b5633577 | |||
| 7b6e70028b | |||
| fd4a228086 | |||
| b45abf2492 | |||
| 53ad8fdff5 | |||
| 5a988e8d37 | |||
| 1ada889135 | |||
| 7ede9b1ef3 | |||
| 01d5d9801c | |||
| a558e7daf7 | |||
| e5aec24751 | |||
| 3010b44b09 | |||
| f26de42b89 | |||
| 6bdd4a0040 | |||
|
|
b53ed46014 | ||
| 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 |
53
.github/workflows/publish.yml
vendored
Normal file
53
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install Poetry
|
||||
run: |
|
||||
pip install poetry==2.3.1
|
||||
poetry --version
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
poetry install --only-root
|
||||
poetry build
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: ./dist
|
||||
|
||||
pypi-publish:
|
||||
needs: build
|
||||
name: Upload release to PyPI
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/p/xair-api
|
||||
permissions:
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: ./dist
|
||||
|
||||
- name: Publish package distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: ./dist
|
||||
19
.github/workflows/ruff.yml
vendored
Normal file
19
.github/workflows/ruff.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Ruff
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ruff:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: astral-sh/ruff-action@v3
|
||||
with:
|
||||
args: 'format --check --diff'
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -130,4 +130,8 @@ dmypy.json
|
||||
|
||||
# config, quick test
|
||||
config.toml
|
||||
quick.py
|
||||
test-*.py
|
||||
tools/*
|
||||
!tools/README.md
|
||||
|
||||
.vscode/
|
||||
13
.pre-commit-config.yaml
Normal file
13
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v2.3.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: https://github.com/python-poetry/poetry
|
||||
rev: '2.3.2'
|
||||
hooks:
|
||||
- id: poetry-check
|
||||
- id: poetry-lock
|
||||
78
CHANGELOG.md
78
CHANGELOG.md
@@ -9,7 +9,83 @@ Before any major/minor/patch bump all unit tests will be run to verify they pass
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
- [x]
|
||||
- [ ]
|
||||
|
||||
## [2.4.0] - 2025-01-04
|
||||
|
||||
### Added
|
||||
|
||||
- HeadAmp class to all kinds for enabling phantom power and setting preamp gain.
|
||||
- headamp example.
|
||||
|
||||
## [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
|
||||
|
||||
|
||||
138
README.md
138
README.md
@@ -1,8 +1,8 @@
|
||||
[](https://badge.fury.io/py/xair-api)
|
||||
[](https://github.com/onyx-and-iris/xair-api-python/blob/dev/LICENSE)
|
||||
[](https://github.com/psf/black)
|
||||
[](https://pycqa.github.io/isort/)
|
||||

|
||||
[](https://python-poetry.org/)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||

|
||||
|
||||
# Xair API
|
||||
|
||||
@@ -40,30 +40,42 @@ import xair_api
|
||||
|
||||
|
||||
def main():
|
||||
kind_id = 'XR18'
|
||||
ip = '<ip address>'
|
||||
|
||||
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
|
||||
print(
|
||||
f"strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}"
|
||||
f'strip 09 ({mixer.strip[8].config.name}) on has been set to {mixer.strip[8].mix.on}'
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
kind_id = "MR18"
|
||||
ip = "<ip address>"
|
||||
|
||||
if __name__ == '__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`
|
||||
- `XR18`
|
||||
- `XR16`
|
||||
- `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)
|
||||
|
||||
`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
|
||||
|
||||
`mixer.fx`
|
||||
|
||||
An FX tuple containing a class for each FX channel
|
||||
|
||||
`mixer.fxsend`
|
||||
|
||||
An FXSend tuple containing a class for each FX Send channel
|
||||
@@ -90,18 +106,18 @@ An FXSend tuple containing a class for each FX Send channel
|
||||
|
||||
An FXReturn tuple containing a class for each FX Return channel
|
||||
|
||||
`mixer.aux`
|
||||
`mixer.auxreturn`
|
||||
|
||||
A class representing aux channel
|
||||
|
||||
`mixer.rtn`
|
||||
|
||||
An RTN tuple containing a class for each rtn channel
|
||||
A class representing auxreturn channel
|
||||
|
||||
`mixer.config`
|
||||
|
||||
A class representing the main config settings
|
||||
|
||||
`mixer.headamp`
|
||||
|
||||
A class representing the channel preamps (phantom power/gain).
|
||||
|
||||
### `LR`
|
||||
|
||||
Contains the subclasses:
|
||||
@@ -110,7 +126,7 @@ Contains the subclasses:
|
||||
### `Strip`
|
||||
|
||||
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`
|
||||
|
||||
@@ -122,15 +138,22 @@ Contains the subclasses:
|
||||
Contains the subclasses:
|
||||
(`Config`, `Mix`, `Group`)
|
||||
|
||||
### `Aux`
|
||||
### `FXRtn`
|
||||
|
||||
Contains the subclasses:
|
||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
||||
|
||||
### `Rtn`
|
||||
### `AuxRtn`
|
||||
|
||||
Contains the subclasses:
|
||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
||||
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
||||
|
||||
### `HeadAmp`
|
||||
|
||||
The following properties are available:
|
||||
|
||||
- `gain`: float, from -12.0 to 60.0
|
||||
- `phantom`: bool
|
||||
|
||||
### `Subclasses`
|
||||
|
||||
@@ -258,12 +281,14 @@ For the subclass `monitor` the following properties are available
|
||||
|
||||
- `level`: float, -inf to 10.0
|
||||
- `source`: int, from 0 to 14
|
||||
- `chmode` bool
|
||||
- `busmode` bool
|
||||
- `dim` bool
|
||||
- `mono` bool
|
||||
- `mute` bool
|
||||
- `dimfpl` bool
|
||||
- `sourcetrim`: float, from -18.0 to 18.0
|
||||
- `chmode`: bool
|
||||
- `busmode`: bool
|
||||
- `dim`: bool
|
||||
- `dimgain`: float, from -40.0 to 0.0
|
||||
- `mono`: bool
|
||||
- `mute`: bool
|
||||
- `dimfpl`: bool
|
||||
|
||||
for example: `config.monitor.chmode`
|
||||
|
||||
@@ -275,21 +300,64 @@ tuple containing a class for each mute group
|
||||
|
||||
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`
|
||||
|
||||
Unplug any expensive equipment before running tests.
|
||||
Save your current settings to a snapshot first.
|
||||
Install [poetry](https://python-poetry.org/docs/#installation) and then:
|
||||
|
||||
First make sure you installed the [development dependencies](https://github.com/onyx-and-iris/xair-api-python#installation)
|
||||
```powershell
|
||||
poetry poe test-xair
|
||||
poetry poe test-x32
|
||||
```
|
||||
|
||||
To run all tests:
|
||||
|
||||
`pytest -v`.
|
||||
Unplug any expensive equipment and save your current settings to a snapshot first.
|
||||
|
||||
## License
|
||||
|
||||
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
|
||||
|
||||
[Peter Dikant](https://github.com/peterdikant) for writing the base class
|
||||
|
||||
12
__main__.py
12
__main__.py
@@ -2,16 +2,16 @@ import xair_api
|
||||
|
||||
|
||||
def main():
|
||||
kind_id = 'XR18'
|
||||
ip = '<ip address>'
|
||||
|
||||
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
|
||||
print(
|
||||
f"strip 09 ({mixer.strip[8].config.name}) has been set to {mixer.strip[8].config.on}"
|
||||
f'strip 09 ({mixer.strip[8].config.name}) has been set to {mixer.strip[8].config.on}'
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
kind_id = "MR18"
|
||||
ip = "<ip address>"
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
20
examples/headamp/__main__.py
Normal file
20
examples/headamp/__main__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# Warning this script enables the phantom power for strip 09
|
||||
|
||||
import logging
|
||||
|
||||
import xair_api
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
|
||||
def main():
|
||||
with xair_api.connect('XR18', ip='mixer.local') as mixer:
|
||||
mixer.headamp[8].phantom = True
|
||||
for i in range(-12, -6):
|
||||
mixer.headamp[8].gain = i
|
||||
print(mixer.headamp[8].gain)
|
||||
input('Press Enter to continue...')
|
||||
|
||||
|
||||
if __name__ == '__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,39 +0,0 @@
|
||||
import obsws_python as obs
|
||||
import xair_api
|
||||
|
||||
|
||||
class Observer:
|
||||
def __init__(self, mixer):
|
||||
self._mixer = mixer
|
||||
self._cl = obs.EventClient()
|
||||
self._cl.callback.register(self.on_current_program_scene_changed)
|
||||
|
||||
def on_current_program_scene_changed(self, data):
|
||||
scene = data.scene_name
|
||||
print(f"Switched to scene {scene}")
|
||||
match scene:
|
||||
case "START":
|
||||
print("Toggling strip 01 on")
|
||||
self._mixer.strip[0].mix.on = not self._mixer.strip[0].mix.on
|
||||
case "BRB":
|
||||
print("Setting strip 08 fader")
|
||||
self._mixer.strip[7].mix.fader = -12.8
|
||||
case "END":
|
||||
print("Settings strip 02 color")
|
||||
self._mixer.strip[1].config.color = 8
|
||||
case "LIVE":
|
||||
self._mixer.config.mute_group[0].on = True
|
||||
print(f"Mute Group 1 is {self._mixer.config.mute_group[0].on}")
|
||||
|
||||
|
||||
def main():
|
||||
with xair_api.connect("MR18", ip="mixer.local") as mixer:
|
||||
Observer(mixer)
|
||||
|
||||
while cmd := input("<Enter> to exit\n"):
|
||||
if not cmd:
|
||||
break
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -19,7 +19,7 @@ password = "mystrongpass"
|
||||
|
||||
## Use
|
||||
|
||||
Change the xair ip argument from `mixer.local` to the ip of your xair mixer. Run the code and switch between scenes in OBS.
|
||||
Change the xair ip argument from `mixer.local` to the ip of your xair mixer. Run the code and switch between scenes in OBS. Closing OBS will end the script.
|
||||
|
||||
## Notes
|
||||
|
||||
83
examples/xair_obs/__main__.py
Normal file
83
examples/xair_obs/__main__.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import threading
|
||||
from logging import config
|
||||
|
||||
import obsws_python as obsws
|
||||
|
||||
import xair_api
|
||||
|
||||
config.dictConfig(
|
||||
{
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'stream': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'standard',
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'xair_api.xair': {
|
||||
'handlers': ['stream'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': False,
|
||||
}
|
||||
},
|
||||
'root': {'handlers': ['stream'], 'level': 'WARNING'},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class Observer:
|
||||
def __init__(self, mixer, stop_event):
|
||||
self._mixer = mixer
|
||||
self._stop_event = stop_event
|
||||
self._client = obsws.EventClient()
|
||||
self._client.callback.register(
|
||||
(
|
||||
self.on_current_program_scene_changed,
|
||||
self.on_exit_started,
|
||||
)
|
||||
)
|
||||
|
||||
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):
|
||||
scene = data.scene_name
|
||||
print(f'Switched to scene {scene}')
|
||||
match scene:
|
||||
case 'START':
|
||||
print('Toggling strip 01 on')
|
||||
self._mixer.strip[0].mix.on = not self._mixer.strip[0].mix.on
|
||||
case 'BRB':
|
||||
print('Setting strip 08 fader')
|
||||
self._mixer.strip[7].mix.fader = -12.8
|
||||
case 'END':
|
||||
print('Settings strip 02 color')
|
||||
self._mixer.strip[1].config.color = 8
|
||||
case 'LIVE':
|
||||
self._mixer.config.mute_group[0].on = True
|
||||
print(f'Mute Group 1 is {self._mixer.config.mute_group[0].on}')
|
||||
|
||||
def on_exit_started(self, _):
|
||||
self._stop_event.set()
|
||||
|
||||
|
||||
def main():
|
||||
with xair_api.connect('MR18', ip='mixer.local') as mixer:
|
||||
stop_event = threading.Event()
|
||||
|
||||
with Observer(mixer, stop_event):
|
||||
stop_event.wait()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
475
poetry.lock
generated
475
poetry.lock
generated
@@ -1,252 +1,371 @@
|
||||
[[package]]
|
||||
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"]
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "22.8.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.2"
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
name = "cachetools"
|
||||
version = "5.5.0"
|
||||
description = "Extensible memoizing collections and decorators"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"},
|
||||
{file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "5.2.0"
|
||||
description = "Universal encoding detector for Python 3"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
|
||||
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
category = "dev"
|
||||
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"
|
||||
groups = ["dev"]
|
||||
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.9"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87"},
|
||||
{file = "distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.2.2"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
||||
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.20.3"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "filelock-3.20.3-py3-none-any.whl", hash = "sha256:4b0dda527ee31078689fc205ec4f1c1bf7d56cf88b6dc9426c4f230e46c2dce1"},
|
||||
{file = "filelock-3.20.3.tar.gz", hash = "sha256:18c57ee915c7ec61cff0ecf7f0f869936c7c30191bb0cf406f1341778d0834e1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "1.1.1"
|
||||
description = "iniconfig: brain-dead simple config-ini parsing"
|
||||
category = "dev"
|
||||
version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "isort"
|
||||
version = "5.10.1"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1,<4.0"
|
||||
|
||||
[package.extras]
|
||||
pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
|
||||
requirements_deprecated_finder = ["pipreqs", "pip-api"]
|
||||
colors = ["colorama (>=0.4.3,<0.5.0)"]
|
||||
plugins = ["setuptools"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "0.4.3"
|
||||
description = "Experimental type system extensions for programs checked with the mypy typechecker."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"},
|
||||
{file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "21.3"
|
||||
version = "24.2"
|
||||
description = "Core utilities for Python packages"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[package.dependencies]
|
||||
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.10.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "2.5.2"
|
||||
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
category = "dev"
|
||||
version = "4.3.6"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"},
|
||||
{file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"]
|
||||
test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"]
|
||||
docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
|
||||
type = ["mypy (>=1.11.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.0.0"
|
||||
version = "1.5.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
|
||||
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["pre-commit", "tox"]
|
||||
testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "py"
|
||||
version = "1.11.0"
|
||||
description = "library with cross-python path, ini-parsing, io, code, log facilities"
|
||||
category = "dev"
|
||||
name = "pyenv-inspect"
|
||||
version = "0.4.0"
|
||||
description = "An auxiliary library for the virtualenv-pyenv and tox-pyenv-redux plugins"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pyenv-inspect-0.4.0.tar.gz", hash = "sha256:ec429d1d81b67ab0b08a0408414722a79d24fd1845a5b264267e44e19d8d60f0"},
|
||||
{file = "pyenv_inspect-0.4.0-py3-none-any.whl", hash = "sha256:618683ae7d3e6db14778d58aa0fc6b3170180d944669b5d35a8aa4fb7db550d2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.0.9"
|
||||
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
category = "dev"
|
||||
name = "pyproject-api"
|
||||
version = "1.8.0"
|
||||
description = "API to interact with the python pyproject.toml based projects"
|
||||
optional = false
|
||||
python-versions = ">=3.6.8"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"},
|
||||
{file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
packaging = ">=24.1"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
diagrams = ["railroad-diagrams", "jinja2"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx-autodoc-typehints (>=2.4.1)"]
|
||||
testing = ["covdefaults (>=2.3)", "pytest (>=8.3.3)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "setuptools (>=75.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "7.1.3"
|
||||
version = "8.3.4"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
|
||||
{file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
attrs = ">=19.2.0"
|
||||
colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
py = ">=1.8.2"
|
||||
tomli = ">=1.0.0"
|
||||
pluggy = ">=1.5,<2"
|
||||
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"]
|
||||
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-randomly"
|
||||
version = "3.12.0"
|
||||
version = "3.16.0"
|
||||
description = "Pytest plugin to randomly order tests and control random.seed."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest_randomly-3.16.0-py3-none-any.whl", hash = "sha256:8633d332635a1a0983d3bba19342196807f6afb17c3eef78e02c2f85dade45d6"},
|
||||
{file = "pytest_randomly-3.16.0.tar.gz", hash = "sha256:11bf4d23a26484de7860d82f726c0629837cf4064b79157bd18ec9d41d7feb26"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytest = "*"
|
||||
|
||||
[[package]]
|
||||
name = "python-osc"
|
||||
version = "1.8.0"
|
||||
version = "1.9.3"
|
||||
description = "Open Sound Control server and client implementations in pure Python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "python_osc-1.9.3-py3-none-any.whl", hash = "sha256:7def2075be72f07bae5a4c1a55cc7d907b247f4a5d910f3159ed30ac2b1f17cc"},
|
||||
{file = "python_osc-1.9.3.tar.gz", hash = "sha256:bd0fa40def43ce509894709feb0e18f02192aca192c5e6c8fe2ba69e58f21794"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.8.6"
|
||||
description = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "ruff-0.8.6-py3-none-linux_armv6l.whl", hash = "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3"},
|
||||
{file = "ruff-0.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1"},
|
||||
{file = "ruff-0.8.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf"},
|
||||
{file = "ruff-0.8.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117"},
|
||||
{file = "ruff-0.8.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe"},
|
||||
{file = "ruff-0.8.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d"},
|
||||
{file = "ruff-0.8.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a"},
|
||||
{file = "ruff-0.8.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76"},
|
||||
{file = "ruff-0.8.6-py3-none-win32.whl", hash = "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764"},
|
||||
{file = "ruff-0.8.6-py3-none-win_amd64.whl", hash = "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905"},
|
||||
{file = "ruff-0.8.6-py3-none-win_arm64.whl", hash = "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162"},
|
||||
{file = "ruff-0.8.6.tar.gz", hash = "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.0.1"
|
||||
version = "2.2.1"
|
||||
description = "A lil' TOML parser"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
|
||||
{file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
|
||||
{file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
|
||||
{file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
|
||||
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
|
||||
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tox"
|
||||
version = "4.23.2"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "tox-4.23.2-py3-none-any.whl", hash = "sha256:452bc32bb031f2282881a2118923176445bac783ab97c874b8770ab4c3b76c38"},
|
||||
{file = "tox-4.23.2.tar.gz", hash = "sha256:86075e00e555df6e82e74cfc333917f91ecb47ffbc868dcafbd2672e332f4a2c"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cachetools = ">=5.5"
|
||||
chardet = ">=5.2"
|
||||
colorama = ">=0.4.6"
|
||||
filelock = ">=3.16.1"
|
||||
packaging = ">=24.1"
|
||||
platformdirs = ">=4.3.6"
|
||||
pluggy = ">=1.5"
|
||||
pyproject-api = ">=1.8"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
|
||||
virtualenv = ">=20.26.6"
|
||||
|
||||
[package.extras]
|
||||
test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
description = "Backported and Experimental Type Hints for Python 3.9+"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
markers = "python_version == \"3.10\""
|
||||
files = [
|
||||
{file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
|
||||
{file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.36.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f"},
|
||||
{file = "virtualenv-20.36.1.tar.gz", hash = "sha256:8befb5c81842c641f8ee658481e42641c68b5eab3521d8e092d18320902466ba"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = {version = ">=3.20.1,<4", markers = "python_version >= \"3.10\""}
|
||||
platformdirs = ">=3.9.1,<5"
|
||||
typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "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) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv-pyenv"
|
||||
version = "0.5.0"
|
||||
description = "A virtualenv Python discovery plugin for pyenv-installed interpreters"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "virtualenv-pyenv-0.5.0.tar.gz", hash = "sha256:7b0e5fe3dfbdf484f4cf9b01e1f98111e398db6942237910f666356e6293597f"},
|
||||
{file = "virtualenv_pyenv-0.5.0-py3-none-any.whl", hash = "sha256:21750247e36c55b3c547cfdeb08f51a3867fe7129922991a4f9c96980c0a4a5d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pyenv-inspect = ">=0.4,<0.5"
|
||||
virtualenv = "*"
|
||||
|
||||
[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.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10"
|
||||
content-hash = "dbba803b0ac29e3120c3f302260b600a8ef58bfac91c41a58f242804f17b89ba"
|
||||
|
||||
146
pyproject.toml
146
pyproject.toml
@@ -1,26 +1,138 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "xair-api"
|
||||
version = "1.1.1"
|
||||
version = "2.4.3"
|
||||
description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
|
||||
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
||||
license = "MIT"
|
||||
authors = [{ name = "Onyx and Iris", email = "code@onyxandiris.online" }]
|
||||
license = { text = "MIT" }
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/onyx-and-iris/xair-api-python"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"python-osc (>=1.9.3,<2.0.0)",
|
||||
"tomli (>=2.0.1,<3.0) ; python_version < '3.11'",
|
||||
]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
python-osc = "^1.8.0"
|
||||
tomli = { version = "^2.0.1", python = "<3.11" }
|
||||
[tool.poetry.requires-plugins]
|
||||
poethepoet = "^0.35.0"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^7.1.2"
|
||||
pytest-randomly = "^3.12.0"
|
||||
black = "^22.6.0"
|
||||
isort = "^5.10.1"
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^8.3.4"
|
||||
pytest-randomly = "^3.16.0"
|
||||
ruff = "^0.8.6"
|
||||
tox = "^4.23.2"
|
||||
virtualenv-pyenv = "^0.5.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.poetry.scripts]
|
||||
obs = "examples.xair-obs.__main__:main"
|
||||
[tool.poe.tasks]
|
||||
obs.script = "scripts:ex_obs"
|
||||
sends.script = "scripts:ex_sends"
|
||||
headamp.script = "scripts:ex_headamp"
|
||||
test-xair.script = "scripts:test_xair"
|
||||
test-x32.script = "scripts:test_x32"
|
||||
test-all.script = "scripts:test_all"
|
||||
|
||||
[tool.tox]
|
||||
legacy_tox_ini = """
|
||||
[tox]
|
||||
envlist = py310,py311,py312,py313
|
||||
|
||||
[testenv]
|
||||
passenv =
|
||||
TEST_MODULE
|
||||
setenv = VIRTUALENV_DISCOVERY=pyenv
|
||||
allowlist_externals = poetry
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root
|
||||
commands =
|
||||
poetry run pytest tests/{env:TEST_MODULE}
|
||||
|
||||
[testenv:obs]
|
||||
setenv = VIRTUALENV_DISCOVERY=pyenv
|
||||
allowlist_externals = poetry
|
||||
deps = obsws-python
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root --without dev
|
||||
commands =
|
||||
poetry run python examples/xair_obs
|
||||
"""
|
||||
|
||||
[tool.ruff]
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
".eggs",
|
||||
".git",
|
||||
".git-rewrite",
|
||||
".hg",
|
||||
".mypy_cache",
|
||||
".nox",
|
||||
".pants.d",
|
||||
".pytype",
|
||||
".ruff_cache",
|
||||
".svn",
|
||||
".tox",
|
||||
".venv",
|
||||
"__pypackages__",
|
||||
"_build",
|
||||
"buck-out",
|
||||
"build",
|
||||
"dist",
|
||||
"node_modules",
|
||||
"venv",
|
||||
]
|
||||
|
||||
# Same as Black.
|
||||
line-length = 88
|
||||
indent-width = 4
|
||||
|
||||
# Assume Python 3.10
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
||||
# McCabe complexity (`C901`) by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
|
||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["ALL"]
|
||||
unfixable = []
|
||||
|
||||
# Allow unused variables when underscore-prefixed.
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
|
||||
[tool.ruff.format]
|
||||
# Unlike Black, use single quotes for strings.
|
||||
quote-style = "single"
|
||||
|
||||
# Like Black, indent with spaces, rather than tabs.
|
||||
indent-style = "space"
|
||||
|
||||
# Like Black, respect magic trailing commas.
|
||||
skip-magic-trailing-comma = false
|
||||
|
||||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
|
||||
# Enable auto-formatting of code examples in docstrings. Markdown,
|
||||
# reStructuredText code/literal blocks and doctests are all supported.
|
||||
#
|
||||
# This is currently disabled by default, but it is planned for this
|
||||
# to be opt-out in the future.
|
||||
docstring-code-format = false
|
||||
|
||||
# Set the line length limit used when formatting code snippets in
|
||||
# docstrings.
|
||||
#
|
||||
# This only has an effect when the `docstring-code-format` setting is
|
||||
# enabled.
|
||||
docstring-code-line-length = "dynamic"
|
||||
|
||||
[tool.ruff.lint.mccabe]
|
||||
max-complexity = 10
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["E402", "F401"]
|
||||
|
||||
35
scripts.py
Normal file
35
scripts.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def ex_obs():
|
||||
subprocess.run(['tox', 'r', '-e', 'obs'])
|
||||
|
||||
|
||||
def ex_sends():
|
||||
path = Path.cwd() / 'examples' / 'sends' / '.'
|
||||
subprocess.run([sys.executable, str(path)])
|
||||
|
||||
|
||||
def ex_headamp():
|
||||
path = Path.cwd() / 'examples' / 'headamp' / '.'
|
||||
subprocess.run([sys.executable, str(path)])
|
||||
|
||||
|
||||
def test_xair():
|
||||
subprocess.run(['tox'], env=os.environ.copy() | {'TEST_MODULE': 'xair'})
|
||||
|
||||
|
||||
def test_x32():
|
||||
path = Path.cwd() / 'tools' / 'x32.exe'
|
||||
proc = subprocess.Popen([str(path), '-i', 'x32.local'])
|
||||
subprocess.run(['tox'], env=os.environ.copy() | {'TEST_MODULE': 'x32'})
|
||||
proc.terminate()
|
||||
|
||||
|
||||
def test_all():
|
||||
steps = [test_xair, test_x32]
|
||||
for step in steps:
|
||||
step()
|
||||
@@ -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 {
|
||||
$coverage = "./tests/pytest_coverage.log"
|
||||
$run_tests = "pytest -v --capture=tee-sys --junitxml=./tests/.coverage.xml"
|
||||
"Running tests in directory $PSScriptRoot" | Write-Host
|
||||
$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"
|
||||
|
||||
if ( Test-Path $coverage ) { Clear-Content $coverage }
|
||||
@@ -13,7 +14,7 @@ Function RunTests {
|
||||
}
|
||||
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 {
|
||||
@@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from tests import data, tests
|
||||
from tests.xair import data, tests
|
||||
|
||||
"""
|
||||
Not every subclass is tested for every superclass to avoid redundancy.
|
||||
@@ -19,20 +19,20 @@ class TestSetAndGetLRMixHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "lr")
|
||||
self.target = getattr(self.target, "mix")
|
||||
self.target = getattr(tests, 'lr')
|
||||
self.target = getattr(self.target, 'mix')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("on", True), ("on", False)],
|
||||
'param,value',
|
||||
[('on', True), ('on', False)],
|
||||
)
|
||||
def test_it_sets_and_gets_lr_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("fader", -80.6), ("fader", -67.0)],
|
||||
'param,value',
|
||||
[('fader', -80.6), ('fader', -67.0)],
|
||||
)
|
||||
def test_it_sets_and_gets_lr_float_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -45,10 +45,10 @@ class TestSetAndGetLRConfigHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "lr")
|
||||
self.target = getattr(self.target, "config")
|
||||
self.target = getattr(tests, 'lr')
|
||||
self.target = getattr(self.target, 'config')
|
||||
|
||||
@pytest.mark.parametrize("param,value", [("name", "test0"), ("name", "test1")])
|
||||
@pytest.mark.parametrize('param,value', [('name', 'test0'), ('name', 'test1')])
|
||||
def test_it_sets_and_gets_lr_string_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
@@ -60,20 +60,20 @@ class TestSetAndGetLRInsertHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "lr")
|
||||
self.target = getattr(self.target, "insert")
|
||||
self.target = getattr(tests, 'lr')
|
||||
self.target = getattr(self.target, 'insert')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("on", True), ("on", False)],
|
||||
'param,value',
|
||||
[('on', True), ('on', False)],
|
||||
)
|
||||
def test_it_sets_and_gets_lr_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("sel", 0), ("sel", 4)],
|
||||
'param,value',
|
||||
[('sel', 0), ('sel', 4)],
|
||||
)
|
||||
def test_it_sets_and_gets_lr_int_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -86,16 +86,16 @@ class TestSetAndGetLRGEQHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "lr")
|
||||
self.target = getattr(self.target, "geq")
|
||||
self.target = getattr(tests, 'lr')
|
||||
self.target = getattr(self.target, 'geq')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("slider_20", -13.5),
|
||||
("slider_20", 5.5),
|
||||
("slider_6k3", -8.5),
|
||||
("slider_6k3", 8.5),
|
||||
('slider_20', -13.5),
|
||||
('slider_20', 5.5),
|
||||
('slider_6k3', -8.5),
|
||||
('slider_6k3', 8.5),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_lr_int_params(self, param, value):
|
||||
@@ -106,18 +106,35 @@ class TestSetAndGetLRGEQHigher:
|
||||
""" 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")
|
||||
self.target = getattr(tests, 'strip')
|
||||
self.target = getattr(self.target[data.strip], 'mix')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("on", True), ("on", False), ("lr", True), ("lr", False)],
|
||||
'param,value',
|
||||
[('on', True), ('on', False), ('lr', True), ('lr', False)],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -130,16 +147,16 @@ class TestSetAndGetStripPreampHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "strip")
|
||||
self.target = getattr(self.target[data.strip], "preamp")
|
||||
self.target = getattr(tests, 'strip')
|
||||
self.target = getattr(self.target[data.strip], 'preamp')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("highpasson", True),
|
||||
("highpasson", False),
|
||||
("usbinput", True),
|
||||
("usbinput", False),
|
||||
('highpasson', True),
|
||||
('highpasson', False),
|
||||
('usbinput', True),
|
||||
('usbinput', False),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_bool_params(self, param, value):
|
||||
@@ -147,16 +164,16 @@ class TestSetAndGetStripPreampHigher:
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("highpassfilter", 20), ("highpassfilter", 399)],
|
||||
'param,value',
|
||||
[('highpassfilter', 20), ('highpassfilter', 399)],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_int_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("usbtrim", -16.5), ("usbtrim", 5.5)],
|
||||
'param,value',
|
||||
[('usbtrim', -16.5), ('usbtrim', 5.5)],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_float_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -169,12 +186,12 @@ class TestSetAndGetStripConfigHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "strip")
|
||||
self.target = getattr(self.target[data.strip], "config")
|
||||
self.target = getattr(tests, 'strip')
|
||||
self.target = getattr(self.target[data.strip], 'config')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("inputsource", 0), ("inputsource", 18), ("usbreturn", 3), ("usbreturn", 12)],
|
||||
'param,value',
|
||||
[('inputsource', 0), ('inputsource', 18), ('usbreturn', 3), ('usbreturn', 12)],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_int_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -187,18 +204,18 @@ class TestSetAndGetStripGateHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "strip")
|
||||
self.target = getattr(self.target[data.strip], "gate")
|
||||
self.target = getattr(tests, 'strip')
|
||||
self.target = getattr(self.target[data.strip], 'gate')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("on", True),
|
||||
("on", False),
|
||||
("invert", True),
|
||||
("invert", False),
|
||||
("filteron", True),
|
||||
("filteron", False),
|
||||
('on', True),
|
||||
('on', False),
|
||||
('invert', True),
|
||||
('invert', False),
|
||||
('filteron', True),
|
||||
('filteron', False),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_bool_params(self, param, value):
|
||||
@@ -206,16 +223,16 @@ class TestSetAndGetStripGateHigher:
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("range", 11),
|
||||
("range", 48),
|
||||
("attack", 5),
|
||||
("attack", 110),
|
||||
("release", 360),
|
||||
("release", 2505),
|
||||
("filtertype", 0),
|
||||
("filtertype", 8),
|
||||
('range', 11),
|
||||
('range', 48),
|
||||
('attack', 5),
|
||||
('attack', 110),
|
||||
('release', 360),
|
||||
('release', 2505),
|
||||
('filtertype', 0),
|
||||
('filtertype', 8),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_int_params(self, param, value):
|
||||
@@ -223,22 +240,22 @@ class TestSetAndGetStripGateHigher:
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("mode", "exp2"), ("mode", "duck")],
|
||||
'param,value',
|
||||
[('mode', 'exp2'), ('mode', 'duck')],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_string_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("threshold", -80.0),
|
||||
("threshold", 0.0),
|
||||
("hold", 355),
|
||||
("hold", 63.2),
|
||||
("filterfreq", 37.2),
|
||||
("filterfreq", 12765),
|
||||
('threshold', -80.0),
|
||||
('threshold', 0.0),
|
||||
('hold', 355),
|
||||
('hold', 63.2),
|
||||
('filterfreq', 37.2),
|
||||
('filterfreq', 12765),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_strip_float_params(self, param, value):
|
||||
@@ -252,22 +269,22 @@ class TestSetAndGetStripAutomixHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "strip")
|
||||
self.target = getattr(self.target[data.strip], "automix")
|
||||
self.target = getattr(tests, 'strip')
|
||||
self.target = getattr(self.target[data.strip], 'automix')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("group", 0), ("group", 2)],
|
||||
'param,value',
|
||||
[('group', 0), ('group', 2)],
|
||||
)
|
||||
def test_it_sets_and_gets_fxsend_int_params(self, param, value):
|
||||
def test_it_sets_and_gets_strip_int_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("weight", -10.5), ("weight", 3.5)],
|
||||
'param,value',
|
||||
[('weight', -10.5), ('weight', 3.5)],
|
||||
)
|
||||
def test_it_sets_and_gets_fxsend_float_params(self, param, value):
|
||||
def test_it_sets_and_gets_strip_float_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@@ -281,12 +298,12 @@ class TestSetAndGetBusConfigHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "bus")
|
||||
self.target = getattr(self.target[data.bus], "config")
|
||||
self.target = getattr(tests, 'bus')
|
||||
self.target = getattr(self.target[data.bus], 'config')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("color", 0), ("color", 15)],
|
||||
'param,value',
|
||||
[('color', 0), ('color', 15)],
|
||||
)
|
||||
def test_it_sets_and_gets_bus_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -299,26 +316,26 @@ class TestSetAndGetBusDynHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "bus")
|
||||
self.target = getattr(self.target[data.bus], "dyn")
|
||||
self.target = getattr(tests, 'bus')
|
||||
self.target = getattr(self.target[data.bus], 'dyn')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("on", True), ("on", False)],
|
||||
'param,value',
|
||||
[('on', True), ('on', False)],
|
||||
)
|
||||
def test_it_sets_and_gets_bus_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
'param,value',
|
||||
[
|
||||
("mode", "comp"),
|
||||
("mode", "exp"),
|
||||
("env", "lin"),
|
||||
("env", "log"),
|
||||
("det", "peak"),
|
||||
("det", "rms"),
|
||||
('mode', 'comp'),
|
||||
('mode', 'exp'),
|
||||
('env', 'lin'),
|
||||
('env', 'log'),
|
||||
('det', 'peak'),
|
||||
('det', 'rms'),
|
||||
],
|
||||
)
|
||||
def test_it_sets_and_gets_bus_string_params(self, param, value):
|
||||
@@ -326,26 +343,26 @@ class TestSetAndGetBusDynHigher:
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
|
||||
class TestSetAndGetBusDynHigher:
|
||||
class TestSetAndGetBusEQHigher:
|
||||
"""EQ"""
|
||||
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "bus")
|
||||
self.target = getattr(self.target[data.bus], "eq")
|
||||
self.target = getattr(tests, 'bus')
|
||||
self.target = getattr(self.target[data.bus], 'eq')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("on", True), ("on", False)],
|
||||
'param,value',
|
||||
[('on', True), ('on', False)],
|
||||
)
|
||||
def test_it_sets_and_gets_bus_bool_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
assert getattr(self.target, param) == value
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("mode", "peq"), ("mode", "geq"), ("mode", "teq")],
|
||||
'param,value',
|
||||
[('mode', 'peq'), ('mode', 'geq'), ('mode', 'teq')],
|
||||
)
|
||||
def test_it_sets_and_gets_bus_string_params(self, param, value):
|
||||
setattr(self.target, param, value)
|
||||
@@ -361,13 +378,13 @@ class TestSetAndGetFXSendGroupHigher:
|
||||
__test__ = True
|
||||
|
||||
def setup_class(self):
|
||||
self.target = getattr(tests, "fxsend")
|
||||
self.target = getattr(self.target[data.fx], "group")
|
||||
self.target = getattr(tests, 'fxsend')
|
||||
self.target = getattr(self.target[data.fx], 'group')
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"param,value",
|
||||
[("dca", 0), ("dca", 12), ("mute", 3), ("mute", 8)],
|
||||
'param,value',
|
||||
[('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)
|
||||
assert getattr(self.target, param) == value
|
||||
3
tools/README.md
Normal file
3
tools/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# X32 Tests
|
||||
|
||||
Download the [x32 Emulator](https://sites.google.com/site/patrickmaillot/x32#h.p_rE4IH0Luimc0) and place it in this directory in order to run the x32 tests.
|
||||
@@ -1,3 +1,3 @@
|
||||
from .xair import request_remote_obj as connect
|
||||
|
||||
_ALL__ = ["connect"]
|
||||
_ALL__ = ['connect']
|
||||
|
||||
47
xair_api/adapter.py
Normal file
47
xair_api/adapter.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from .bus import Bus as IBus
|
||||
from .headamp import HeadAmp as IHeadAmp
|
||||
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)}'
|
||||
|
||||
|
||||
class HeadAmp(IHeadAmp):
|
||||
@property
|
||||
def address(self):
|
||||
return f'/headamp/{str(self.index).zfill(3)}'
|
||||
@@ -1,7 +1,10 @@
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
||||
from .meta import mute_prop
|
||||
from .shared import EQ, GEQ, Config, Dyn, Group, Insert, Mix
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IBus(abc.ABC):
|
||||
@@ -10,13 +13,13 @@ class IBus(abc.ABC):
|
||||
def __init__(self, remote, index: int):
|
||||
self._remote = remote
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
@@ -36,12 +39,12 @@ class Bus(IBus):
|
||||
Returns a Bus class of a kind.
|
||||
"""
|
||||
BUS_cls = type(
|
||||
f"Bus{remote.kind}",
|
||||
f'Bus{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (
|
||||
Config,
|
||||
@@ -52,11 +55,12 @@ class Bus(IBus):
|
||||
Mix,
|
||||
Group,
|
||||
)
|
||||
}
|
||||
},
|
||||
'mute': mute_prop(),
|
||||
},
|
||||
)
|
||||
return BUS_cls(remote, index)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/bus/{self.index}"
|
||||
return f'/bus/{self.index}'
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from . import kinds
|
||||
from .errors import XAirRemoteError
|
||||
from . import kinds, util
|
||||
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):
|
||||
@@ -11,13 +12,13 @@ class IConfig(abc.ABC):
|
||||
|
||||
def __init__(self, remote):
|
||||
self._remote = remote
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
@@ -35,41 +36,37 @@ class Config(IConfig):
|
||||
Returns a Config class of a kind.
|
||||
"""
|
||||
LINKS_cls = _make_links_mixins[remote.kind.id_]
|
||||
MUTEGROUP_cls = type(f"MuteGroup", (Config.MuteGroup, cls), {})
|
||||
MONITOR_cls = type(f"ConfigMonitor", (Config.Monitor, cls), {})
|
||||
MUTEGROUP_cls = type('MuteGroup', (Config.MuteGroup, cls), {})
|
||||
MONITOR_cls = type('ConfigMonitor', (Config.Monitor, cls), {})
|
||||
CONFIG_cls = type(
|
||||
f"Config{remote.kind}",
|
||||
f'Config{remote.kind}',
|
||||
(cls, LINKS_cls),
|
||||
{
|
||||
"mute_group": tuple(MUTEGROUP_cls(remote, i) for i in range(4)),
|
||||
"monitor": MONITOR_cls(remote),
|
||||
'mute_group': tuple(MUTEGROUP_cls(remote, i) for i in range(4)),
|
||||
'monitor': MONITOR_cls(remote),
|
||||
},
|
||||
)
|
||||
return CONFIG_cls(remote)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/config"
|
||||
return '/config'
|
||||
|
||||
@property
|
||||
def amixenable(self) -> bool:
|
||||
return self.getter("mute")[0] == 1
|
||||
return self.getter('mute')[0] == 1
|
||||
|
||||
@amixenable.setter
|
||||
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
|
||||
def amixlock(self) -> bool:
|
||||
return self.getter("amixlock")[0] == 1
|
||||
return self.getter('amixlock')[0] == 1
|
||||
|
||||
@amixlock.setter
|
||||
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:
|
||||
def __init__(self, remote, i):
|
||||
@@ -79,143 +76,128 @@ class Config(IConfig):
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Config.MuteGroup, self).address
|
||||
return f"{root}/mute"
|
||||
return f'{root}/mute'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter(f"{self.i}")[0] == 1
|
||||
return self.getter(f'{self.i}')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Config.Monitor, self).address
|
||||
return f"{root}/solo"
|
||||
return f'{root}/solo'
|
||||
|
||||
@property
|
||||
@util.db_from
|
||||
def level(self) -> float:
|
||||
retval = self.getter("level")[0]
|
||||
return _get_level_val(retval)
|
||||
return self.getter('level')[0]
|
||||
|
||||
@level.setter
|
||||
@util.db_to
|
||||
def level(self, val: float):
|
||||
_set_level_val(self, val)
|
||||
self.setter('level', val)
|
||||
|
||||
@property
|
||||
def source(self) -> int:
|
||||
return int(self.getter("source")[0])
|
||||
return int(self.getter('source')[0])
|
||||
|
||||
@source.setter
|
||||
def source(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError("source is an int parameter")
|
||||
self.setter(f"source", val)
|
||||
self.setter('source', val)
|
||||
|
||||
@property
|
||||
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
|
||||
def sourcetrim(self, val: float):
|
||||
if not isinstance(val, float):
|
||||
raise XAirRemoteError(
|
||||
"sourcetrim is a float parameter, expected value in range -18 to 18"
|
||||
if not -18 <= val <= 18:
|
||||
self.logger.warning(
|
||||
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
|
||||
def chmode(self) -> bool:
|
||||
return self.getter("chmode")[0] == 1
|
||||
return self.getter('chmode')[0] == 1
|
||||
|
||||
@chmode.setter
|
||||
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
|
||||
def busmode(self) -> bool:
|
||||
return self.getter("busmode")[0] == 1
|
||||
return self.getter('busmode')[0] == 1
|
||||
|
||||
@busmode.setter
|
||||
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
|
||||
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
|
||||
def dimgain(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"dimgain is an int parameter, expected value in range -40 to 0"
|
||||
if not -40 <= val <= 0:
|
||||
self.logger.warning(
|
||||
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
|
||||
def dim(self) -> bool:
|
||||
return self.getter("dim")[0] == 1
|
||||
return self.getter('dim')[0] == 1
|
||||
|
||||
@dim.setter
|
||||
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
|
||||
def mono(self) -> bool:
|
||||
return self.getter("mono")[0] == 1
|
||||
return self.getter('mono')[0] == 1
|
||||
|
||||
@mono.setter
|
||||
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
|
||||
def mute(self) -> bool:
|
||||
return self.getter("mute")[0] == 1
|
||||
return self.getter('mute')[0] == 1
|
||||
|
||||
@mute.setter
|
||||
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
|
||||
def dimfpl(self) -> bool:
|
||||
return self.getter("dimfpl")[0] == 1
|
||||
return self.getter('dimfpl')[0] == 1
|
||||
|
||||
@dimfpl.setter
|
||||
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)
|
||||
|
||||
|
||||
def _make_links_mixin(kind):
|
||||
"""Creates a links mixin"""
|
||||
return type(
|
||||
f"Links{kind}",
|
||||
f'Links{kind}',
|
||||
(),
|
||||
{
|
||||
"link_eq": bool_prop("linkcfg/eq"),
|
||||
"link_dyn": bool_prop("linkcfg/dyn"),
|
||||
"link_fader_mute": bool_prop("linkcfg/fdrmute"),
|
||||
'link_eq': bool_prop('linkcfg/eq'),
|
||||
'link_dyn': bool_prop('linkcfg/dyn'),
|
||||
'link_fader_mute': bool_prop('linkcfg/fdrmute'),
|
||||
**{
|
||||
f"chlink{i}_{i+1}": bool_prop(f"chlink/{i}-{i+1}")
|
||||
f'chlink{i}_{i+1}': bool_prop(f'chlink/{i}-{i+1}')
|
||||
for i in range(1, kind.num_strip, 2)
|
||||
},
|
||||
**{
|
||||
f"buslink{i}_{i+1}": bool_prop(f"buslink/{i}-{i+1}")
|
||||
f'buslink{i}_{i+1}': bool_prop(f'buslink/{i}-{i+1}')
|
||||
for i in range(1, kind.num_bus, 2)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IDCA(abc.ABC):
|
||||
@@ -9,13 +10,13 @@ class IDCA(abc.ABC):
|
||||
def __init__(self, remote, index: int):
|
||||
self._remote = remote
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str) -> tuple:
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
@@ -27,34 +28,36 @@ class DCA(IDCA):
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/dca/{self.index}"
|
||||
return f'/dca/{self.index}'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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
|
||||
def name(self) -> str:
|
||||
return self.getter("config/name")[0]
|
||||
return self.getter('config/name')[0]
|
||||
|
||||
@name.setter
|
||||
def name(self, val: str):
|
||||
if not isinstance(val, str):
|
||||
raise XAirRemoteError("name is a str parameter")
|
||||
self.setter("config/name")[0]
|
||||
self.setter('config/name', val)
|
||||
|
||||
@property
|
||||
def color(self) -> int:
|
||||
return self.getter("config/color")[0]
|
||||
return self.getter('config/color')[0]
|
||||
|
||||
@color.setter
|
||||
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):
|
||||
"""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 logging
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
||||
from .meta import mute_prop
|
||||
from .shared import Config, Group, Mix
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IFX(abc.ABC):
|
||||
@@ -10,19 +13,35 @@ class IFX(abc.ABC):
|
||||
def __init__(self, remote, index: int):
|
||||
self._remote = remote
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
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):
|
||||
"""Concrete class for fxsend"""
|
||||
|
||||
@@ -36,37 +55,20 @@ class FXSend(IFX):
|
||||
Returns an FXSend class of a kind.
|
||||
"""
|
||||
FXSEND_cls = type(
|
||||
f"FXSend{remote.kind}",
|
||||
f'FXSend{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (Config, Mix, Group)
|
||||
}
|
||||
},
|
||||
'mute': mute_prop(),
|
||||
},
|
||||
)
|
||||
return FXSEND_cls(remote, index)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/fxsend/{self.index}"
|
||||
|
||||
|
||||
class FXReturn(IFX):
|
||||
"""Concrete class for fxreturn"""
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/fx/{self.index}"
|
||||
|
||||
@property
|
||||
def type(self) -> int:
|
||||
return self.getter("type")[0]
|
||||
|
||||
@type.setter
|
||||
def type(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError("type is an integer parameter")
|
||||
self.setter("type", val)
|
||||
return f'/fxsend/{self.index}'
|
||||
|
||||
49
xair_api/headamp.py
Normal file
49
xair_api/headamp.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from . import util
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IHeadAmp(abc.ABC):
|
||||
"""Abstract Base Class for headamps"""
|
||||
|
||||
def __init__(self, remote, index: int):
|
||||
self._remote = remote
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
pass
|
||||
|
||||
|
||||
class HeadAmp(IHeadAmp):
|
||||
"""Concrete class for headamps"""
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
return f'/headamp/{str(self.index).zfill(2)}'
|
||||
|
||||
@property
|
||||
def gain(self):
|
||||
return round(util.lin_get(-12, 60, self.getter('gain')[0]), 1)
|
||||
|
||||
@gain.setter
|
||||
def gain(self, val):
|
||||
self.setter('gain', util.lin_set(-12, 60, val))
|
||||
|
||||
@property
|
||||
def phantom(self):
|
||||
return self.getter('phantom')[0] == 1
|
||||
|
||||
@phantom.setter
|
||||
def phantom(self, val):
|
||||
self.setter('phantom', 1 if val else 0)
|
||||
@@ -1,61 +1,56 @@
|
||||
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(frozen=True)
|
||||
class KindMap:
|
||||
id_: str
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.id_
|
||||
|
||||
|
||||
@dataclass
|
||||
class MR18KindMap(KindMap):
|
||||
# note ch 17-18 defined as aux rtn
|
||||
id_: str
|
||||
@dataclass(frozen=True)
|
||||
class X32KindMap(KindMap):
|
||||
num_dca: int = 8
|
||||
num_strip: int = 32
|
||||
num_bus: int = 16
|
||||
num_fx: int = 8
|
||||
num_auxrtn: int = 8
|
||||
num_matrix: int = 6
|
||||
num_headamp: int = 127
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class XR18KindMap(KindMap):
|
||||
# note ch 17-18 defined as aux return
|
||||
num_dca: int = 4
|
||||
num_strip: int = 16
|
||||
num_bus: int = 6
|
||||
num_fx: int = 4
|
||||
num_rtn: int = 4
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class XR16KindMap(KindMap):
|
||||
id_: str
|
||||
num_dca: int = 4
|
||||
num_strip: int = 16
|
||||
num_bus: int = 4
|
||||
num_fx: int = 4
|
||||
num_rtn: int = 4
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class XR12KindMap(KindMap):
|
||||
id_: str
|
||||
num_dca: int = 4
|
||||
num_strip: int = 12
|
||||
num_bus: int = 2
|
||||
num_fx: int = 4
|
||||
num_rtn: int = 4
|
||||
|
||||
|
||||
_kinds = {
|
||||
"XR18": MR18KindMap(id_="XR18"),
|
||||
"MR18": MR18KindMap(id_="MR18"),
|
||||
"XR16": XR16KindMap(id_="XR16"),
|
||||
"XR12": XR12KindMap(id_="XR12"),
|
||||
'X32': X32KindMap(id_='X32'),
|
||||
'MR18': XR18KindMap(id_='MR18'),
|
||||
'XR18': XR18KindMap(id_='XR18'),
|
||||
'XR16': XR16KindMap(id_='XR16'),
|
||||
'XR12': XR12KindMap(id_='XR12'),
|
||||
}
|
||||
|
||||
|
||||
@@ -63,4 +58,4 @@ def get(kind_id):
|
||||
return _kinds[kind_id]
|
||||
|
||||
|
||||
all = list(kind for kind in _kinds.values())
|
||||
all = list(_kinds.values())
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
import abc
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
||||
from .meta import mute_prop
|
||||
from .shared import EQ, GEQ, Config, Dyn, Insert, Mix
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
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
|
||||
if index is not None:
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
@@ -23,10 +29,10 @@ class ILR(abc.ABC):
|
||||
|
||||
|
||||
class LR(ILR):
|
||||
"""Concrete class for buses"""
|
||||
"""Concrete class for lr"""
|
||||
|
||||
@classmethod
|
||||
def make(cls, remote):
|
||||
def make(cls, remote, index=None):
|
||||
"""
|
||||
Factory function for LR
|
||||
|
||||
@@ -35,26 +41,27 @@ class LR(ILR):
|
||||
Returns an LR class of a kind.
|
||||
"""
|
||||
LR_cls = type(
|
||||
f"LR{remote.kind}",
|
||||
f'LR{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||
)(remote)
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (
|
||||
Config,
|
||||
Dyn,
|
||||
Insert,
|
||||
GEQ.make(),
|
||||
EQ.make_sixband(cls, remote),
|
||||
EQ.make_sixband(cls, remote, index),
|
||||
Mix,
|
||||
)
|
||||
},
|
||||
'mute': mute_prop(),
|
||||
},
|
||||
)
|
||||
return LR_cls(remote)
|
||||
return LR_cls(remote, index)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/lr"
|
||||
return '/lr'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
from .errors import XAirRemoteError
|
||||
from .util import lin_get, lin_set
|
||||
|
||||
|
||||
@@ -9,8 +8,6 @@ def bool_prop(param):
|
||||
return self.getter(param)[0] == 1
|
||||
|
||||
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)
|
||||
|
||||
return property(fget, fset)
|
||||
@@ -23,8 +20,6 @@ def string_prop(param):
|
||||
return self.getter(param)[0]
|
||||
|
||||
def fset(self, val):
|
||||
if not isinstance(val, str):
|
||||
raise XAirRemoteError(f"{param} is a string parameter")
|
||||
self.setter(param, val)
|
||||
|
||||
return property(fget, fset)
|
||||
@@ -37,8 +32,6 @@ def int_prop(param):
|
||||
return int(self.getter(param)[0])
|
||||
|
||||
def fset(self, val):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(f"{param} is an integer parameter")
|
||||
self.setter(param, val)
|
||||
|
||||
return property(fget, fset)
|
||||
@@ -51,27 +44,32 @@ def float_prop(param):
|
||||
return round(self.getter(param)[0], 1)
|
||||
|
||||
def fset(self, val):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(f"{param} is a float parameter")
|
||||
self.setter(param, val)
|
||||
|
||||
return property(fget, fset)
|
||||
|
||||
|
||||
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:
|
||||
return round(lin_get(-15, 15, self.getter(param)[0]), 1)
|
||||
|
||||
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))
|
||||
|
||||
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,50 +1,53 @@
|
||||
import abc
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
||||
from .meta import mute_prop
|
||||
from .shared import EQ, Config, Group, Mix, Preamp, Send
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IRtn(abc.ABC):
|
||||
"""Abstract Base Class for aux"""
|
||||
"""Abstract Base Class for rtn"""
|
||||
|
||||
def __init__(self, remote, index: Optional[int] = None):
|
||||
self._remote = remote
|
||||
if index is not None:
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str):
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
pass
|
||||
|
||||
|
||||
class Aux(IRtn):
|
||||
"""Concrete class for aux"""
|
||||
class AuxRtn(IRtn):
|
||||
"""Concrete class for auxrtn"""
|
||||
|
||||
@classmethod
|
||||
def make(cls, remote):
|
||||
def make(cls, remote, index=None):
|
||||
"""
|
||||
Factory function for aux
|
||||
Factory function for auxrtn
|
||||
|
||||
Creates a mixin of shared subclasses, sets them as class attributes.
|
||||
|
||||
Returns an Aux class of a kind.
|
||||
Returns an AuxRtn class of a kind.
|
||||
"""
|
||||
AUX_cls = type(
|
||||
f"Aux{remote.kind}",
|
||||
AUXRTN_cls = type(
|
||||
f'AuxRtn{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||
)(remote)
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (
|
||||
Config,
|
||||
Preamp,
|
||||
@@ -52,35 +55,40 @@ class Aux(IRtn):
|
||||
Mix,
|
||||
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
|
||||
def address(self):
|
||||
return "/rtn/aux"
|
||||
return '/rtn/aux'
|
||||
|
||||
|
||||
class Rtn(IRtn):
|
||||
"""Concrete class for rtn"""
|
||||
class FxRtn(IRtn):
|
||||
"""Concrete class for fxrtn"""
|
||||
|
||||
@classmethod
|
||||
def make(cls, remote, index):
|
||||
"""
|
||||
Factory function for rtn
|
||||
Factory function for fxrtn
|
||||
|
||||
Creates a mixin of shared subclasses, sets them as class attributes.
|
||||
|
||||
Returns an Rtn class of a kind.
|
||||
Returns an FxRtn class of a kind.
|
||||
"""
|
||||
RTN_cls = type(
|
||||
f"Rtn{remote.kind.id_}",
|
||||
FXRTN_cls = type(
|
||||
f'FxRtn{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind.id_}", (_cls, cls), {}
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (
|
||||
Config,
|
||||
@@ -89,11 +97,16 @@ class Rtn(IRtn):
|
||||
Mix,
|
||||
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
|
||||
def address(self):
|
||||
return f"/rtn/{self.index}"
|
||||
return f'/rtn/{self.index}'
|
||||
|
||||
@@ -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 .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
|
||||
"""
|
||||
|
||||
|
||||
@@ -13,552 +12,529 @@ class Config:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Config, self).address
|
||||
return f"{root}/config"
|
||||
return f'{root}/config'
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.getter("name")[0]
|
||||
return self.getter('name')[0]
|
||||
|
||||
@name.setter
|
||||
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
|
||||
def color(self) -> int:
|
||||
return self.getter("color")[0]
|
||||
return self.getter('color')[0]
|
||||
|
||||
@color.setter
|
||||
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
|
||||
def inputsource(self) -> int:
|
||||
return self.getter("insrc")[0]
|
||||
return self.getter('insrc')[0]
|
||||
|
||||
@inputsource.setter
|
||||
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
|
||||
def usbreturn(self) -> int:
|
||||
return self.getter("rtnsrc")[0]
|
||||
return self.getter('rtnsrc')[0]
|
||||
|
||||
@usbreturn.setter
|
||||
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)
|
||||
|
||||
|
||||
class Preamp:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Preamp, self).address
|
||||
return f"{root}/preamp"
|
||||
return f'{root}/preamp'
|
||||
|
||||
@property
|
||||
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
|
||||
def usbtrim(self, val: float):
|
||||
if not isinstance(val, float):
|
||||
raise XAirRemoteError(
|
||||
"usbtrim is a float parameter, expected value in range -18 to 18"
|
||||
if not -18 <= val <= 18:
|
||||
self.logger.warning(
|
||||
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
|
||||
def usbinput(self) -> bool:
|
||||
return self.getter("rtnsw")[0] == 1
|
||||
return self.getter('rtnsw')[0] == 1
|
||||
|
||||
@usbinput.setter
|
||||
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
|
||||
def invert(self) -> bool:
|
||||
return self.getter("invert")[0] == 1
|
||||
return self.getter('invert')[0] == 1
|
||||
|
||||
@invert.setter
|
||||
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
|
||||
def highpasson(self) -> bool:
|
||||
return self.getter("hpon")[0] == 1
|
||||
return self.getter('hpon')[0] == 1
|
||||
|
||||
@highpasson.setter
|
||||
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
|
||||
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
|
||||
def highpassfilter(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError("highpassfilter is an int parameter")
|
||||
self.setter("hpf", log_set(20, 400, val))
|
||||
if not 20 <= val <= 400:
|
||||
self.logger.warning(
|
||||
f'highpassfilter got {val}, expected value in range 20 to 400'
|
||||
)
|
||||
self.setter('hpf', util.log_set(20, 400, val))
|
||||
|
||||
|
||||
class Gate:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Gate, self).address
|
||||
return f"{root}/gate"
|
||||
return f'{root}/gate'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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 mode(self) -> str:
|
||||
opts = ("gate", "exp2", "exp3", "exp4", "duck")
|
||||
return opts[self.getter("mode")[0]]
|
||||
opts = ('exp2', 'exp3', 'exp4', 'gate', 'duck')
|
||||
return opts[self.getter('mode')[0]]
|
||||
|
||||
@mode.setter
|
||||
def mode(self, val: str):
|
||||
opts = ("gate", "exp2", "exp3", "exp4", "duck")
|
||||
if not isinstance(val, str) and val not in opts:
|
||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
||||
self.setter("mode", opts.index(val))
|
||||
opts = ('exp2', 'exp3', 'exp4', 'gate', 'duck')
|
||||
if val not in opts:
|
||||
self.logger.warning(f'mode got {val}, expected one of {opts}')
|
||||
self.setter('mode', opts.index(val))
|
||||
|
||||
@property
|
||||
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
|
||||
def threshold(self, val: float):
|
||||
if not isinstance(val, float):
|
||||
raise XAirRemoteError(
|
||||
"threshold is a float parameter, expected value in range -80 to 0"
|
||||
if not -80 <= val <= 0:
|
||||
self.logger.warning(
|
||||
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
|
||||
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
|
||||
def range(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"range is an int parameter, expected value in range 3 to 60"
|
||||
)
|
||||
self.setter("range", lin_set(3, 60, val))
|
||||
if not 3 <= val <= 60:
|
||||
self.logger.warning(f'range got {val}, expected value in range 3 to 60')
|
||||
self.setter('range', util.lin_set(3, 60, val))
|
||||
|
||||
@property
|
||||
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
|
||||
def attack(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"attack is an int parameter, expected value in range 0 to 120"
|
||||
)
|
||||
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
|
||||
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)
|
||||
|
||||
@hold.setter
|
||||
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
|
||||
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
|
||||
def release(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"release is an int parameter, expected value in range 5 to 4000"
|
||||
)
|
||||
self.setter("release", log_set(5, 4000, val))
|
||||
if not 5 <= val <= 4000:
|
||||
self.logger.warning(f'release got {val}, expected value in range 5 to 4000')
|
||||
self.setter('release', util.log_set(5, 4000, val))
|
||||
|
||||
@property
|
||||
def keysource(self):
|
||||
return self.getter("keysrc")[0]
|
||||
return self.getter('keysrc')[0]
|
||||
|
||||
@keysource.setter
|
||||
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
|
||||
def filteron(self):
|
||||
return self.getter("filter/on")[0] == 1
|
||||
return self.getter('filter/on')[0] == 1
|
||||
|
||||
@filteron.setter
|
||||
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
|
||||
def filtertype(self) -> int:
|
||||
return int(self.getter("filter/type")[0])
|
||||
return int(self.getter('filter/type')[0])
|
||||
|
||||
@filtertype.setter
|
||||
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
|
||||
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)
|
||||
|
||||
@filterfreq.setter
|
||||
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:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Dyn, self).address
|
||||
return f"{root}/dyn"
|
||||
return f'{root}/dyn'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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 mode(self) -> str:
|
||||
opts = ("comp", "exp")
|
||||
return opts[self.getter("mode")[0]]
|
||||
opts = ('comp', 'exp')
|
||||
return opts[self.getter('mode')[0]]
|
||||
|
||||
@mode.setter
|
||||
def mode(self, val: str):
|
||||
opts = ("comp", "exp")
|
||||
if not isinstance(val, str) and val not in opts:
|
||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
||||
self.setter("mode", opts.index(val))
|
||||
opts = ('comp', 'exp')
|
||||
if val not in opts:
|
||||
self.logger.warning(f'mode got {val}, expected one of {opts}')
|
||||
self.setter('mode', opts.index(val))
|
||||
|
||||
@property
|
||||
def det(self) -> str:
|
||||
opts = ("peak", "rms")
|
||||
return opts[self.getter("det")[0]]
|
||||
opts = ('peak', 'rms')
|
||||
return opts[self.getter('det')[0]]
|
||||
|
||||
@det.setter
|
||||
def det(self, val: str):
|
||||
opts = ("peak", "rms")
|
||||
if not isinstance(val, str) and val not in opts:
|
||||
raise XAirRemoteError(f"det is a string parameter, expected one of {opts}")
|
||||
self.setter("det", opts.index(val))
|
||||
opts = ('peak', 'rms')
|
||||
if val not in opts:
|
||||
self.logger.warning(f'det got {val}, expected one of {opts}')
|
||||
self.setter('det', opts.index(val))
|
||||
|
||||
@property
|
||||
def env(self) -> str:
|
||||
opts = ("lin", "log")
|
||||
return opts[self.getter("env")[0]]
|
||||
opts = ('lin', 'log')
|
||||
return opts[self.getter('env')[0]]
|
||||
|
||||
@env.setter
|
||||
def env(self, val: str):
|
||||
opts = ("lin", "log")
|
||||
if not isinstance(val, str) and val not in opts:
|
||||
raise XAirRemoteError(f"env is a string parameter, expected one of {opts}")
|
||||
self.setter("env", opts.index(val))
|
||||
opts = ('lin', 'log')
|
||||
if val not in opts:
|
||||
self.logger.warning(f'env got {val}, expected one of {opts}')
|
||||
self.setter('env', opts.index(val))
|
||||
|
||||
@property
|
||||
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
|
||||
def threshold(self, val: float):
|
||||
if not isinstance(val, float):
|
||||
raise XAirRemoteError(
|
||||
"threshold is a float parameter, expected value in range -80 to 0"
|
||||
if not -60 <= val <= 0:
|
||||
self.logger.warning(
|
||||
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
|
||||
def ratio(self) -> Union[float, int]:
|
||||
opts = (1.1, 1.3, 1.5, 2.0, 2.5, 3.0, 4.0, 5.0, 7.0, 10, 20, 100)
|
||||
return opts[self.getter("ratio")[0]]
|
||||
return opts[self.getter('ratio')[0]]
|
||||
|
||||
@ratio.setter
|
||||
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
|
||||
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
|
||||
def knee(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"knee is an int parameter, expected value in range 0 to 5"
|
||||
)
|
||||
self.setter("knee", lin_set(0, 5, val))
|
||||
if not 0 <= val <= 5:
|
||||
self.logger.warning(f'knee got {val}, expected value in range 0 to 5')
|
||||
self.setter('knee', util.lin_set(0, 5, val))
|
||||
|
||||
@property
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
@hold.setter
|
||||
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
|
||||
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
|
||||
def release(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"release is an int parameter, expected value in range 5 to 4000"
|
||||
)
|
||||
self.setter("release", log_set(5, 4000, val))
|
||||
if not 5 <= val <= 4000:
|
||||
self.logger.warning(f'release got {val}, expected value in range 5 to 4000')
|
||||
self.setter('release', util.log_set(5, 4000, val))
|
||||
|
||||
@property
|
||||
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
|
||||
def mix(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError(
|
||||
"mix is an int parameter, expected value in range 0 to 5"
|
||||
)
|
||||
self.setter("mix", lin_set(0, 100, val))
|
||||
if not 0 <= val <= 100:
|
||||
self.logger.warning(f'mix got {val}, expected value in range 0 to 100')
|
||||
self.setter('mix', util.lin_set(0, 100, val))
|
||||
|
||||
@property
|
||||
def keysource(self):
|
||||
return self.getter("keysrc")[0]
|
||||
return self.getter('keysrc')[0]
|
||||
|
||||
@keysource.setter
|
||||
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
|
||||
def auto(self) -> bool:
|
||||
return self.getter("auto")[0] == 1
|
||||
return self.getter('auto')[0] == 1
|
||||
|
||||
@auto.setter
|
||||
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
|
||||
def filteron(self):
|
||||
return self.getter("filter/on")[0] == 1
|
||||
return self.getter('filter/on')[0] == 1
|
||||
|
||||
@filteron.setter
|
||||
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
|
||||
def filtertype(self) -> int:
|
||||
return int(self.getter("filter/type")[0])
|
||||
return int(self.getter('filter/type')[0])
|
||||
|
||||
@filtertype.setter
|
||||
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
|
||||
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)
|
||||
|
||||
@filterfreq.setter
|
||||
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:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Insert, self).address
|
||||
return f"{root}/insert"
|
||||
return f'{root}/insert'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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 sel(self) -> int:
|
||||
return self.getter("sel")[0]
|
||||
return self.getter('sel')[0]
|
||||
|
||||
@sel.setter
|
||||
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)
|
||||
|
||||
|
||||
class EQ:
|
||||
@classmethod
|
||||
def make_fourband(cls, _cls, remote, index=None):
|
||||
EQBand_cls = type("EQBand", (EQ.EQBand, _cls), {})
|
||||
EQBand_cls = type('EQBand', (EQ.EQBand, _cls), {})
|
||||
return type(
|
||||
"EQ",
|
||||
'EQ',
|
||||
(cls,),
|
||||
{
|
||||
"low": EQBand_cls(1, remote, index),
|
||||
"lomid": EQBand_cls(2, remote, index),
|
||||
"himid": EQBand_cls(3, remote, index),
|
||||
"high": EQBand_cls(4, remote, index),
|
||||
'low': EQBand_cls(1, remote, index),
|
||||
'lomid': EQBand_cls(2, remote, index),
|
||||
'himid': EQBand_cls(3, remote, index),
|
||||
'high': EQBand_cls(4, remote, index),
|
||||
},
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def make_sixband(cls, _cls, remote, index=None):
|
||||
EQBand_cls = type("EQBand", (EQ.EQBand, _cls), {})
|
||||
EQBand_cls = type('EQBand', (EQ.EQBand, _cls), {})
|
||||
return type(
|
||||
"EQ",
|
||||
'EQ',
|
||||
(cls,),
|
||||
{
|
||||
"low": EQBand_cls(1, remote, index),
|
||||
"low2": EQBand_cls(2, remote, index),
|
||||
"lomid": EQBand_cls(3, remote, index),
|
||||
"himid": EQBand_cls(4, remote, index),
|
||||
"high2": EQBand_cls(5, remote, index),
|
||||
"high": EQBand_cls(6, remote, index),
|
||||
'low': EQBand_cls(1, remote, index),
|
||||
'low2': EQBand_cls(2, remote, index),
|
||||
'lomid': EQBand_cls(3, remote, index),
|
||||
'himid': EQBand_cls(4, remote, index),
|
||||
'high2': EQBand_cls(5, remote, index),
|
||||
'high': EQBand_cls(6, remote, index),
|
||||
},
|
||||
)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(EQ, self).address
|
||||
return f"{root}/eq"
|
||||
return f'{root}/eq'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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 mode(self) -> str:
|
||||
opts = ("peq", "geq", "teq")
|
||||
return opts[self.getter("mode")[0]]
|
||||
opts = ('peq', 'geq', 'teq')
|
||||
return opts[self.getter('mode')[0]]
|
||||
|
||||
@mode.setter
|
||||
def mode(self, val: str):
|
||||
opts = ("peq", "geq", "teq")
|
||||
if not isinstance(val, str) and val not in opts:
|
||||
raise XAirRemoteError(f"mode is a string parameter, expected one of {opts}")
|
||||
self.setter("mode", opts.index(val))
|
||||
opts = ('peq', 'geq', 'teq')
|
||||
if val not in opts:
|
||||
self.logger.warning(f'mode got {val}, expected one of {opts}')
|
||||
self.setter('mode', opts.index(val))
|
||||
|
||||
class EQBand:
|
||||
def __init__(self, i, remote, index):
|
||||
if index is None:
|
||||
super(EQ.EQBand, self).__init__(remote)
|
||||
else:
|
||||
super(EQ.EQBand, self).__init__(remote, index)
|
||||
self.i = i
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(EQ.EQBand, self).address
|
||||
return f"{root}/eq/{self.i}"
|
||||
return f'{root}/eq/{self.i}'
|
||||
|
||||
@property
|
||||
def type(self) -> int:
|
||||
return int(self.getter("type")[0])
|
||||
return int(self.getter('type')[0])
|
||||
|
||||
@type.setter
|
||||
def type(self, val: int):
|
||||
if not isinstance(val, int):
|
||||
raise XAirRemoteError("type is an int parameter")
|
||||
self.setter(f"type", val)
|
||||
self.setter('type', val)
|
||||
|
||||
@property
|
||||
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)
|
||||
|
||||
@frequency.setter
|
||||
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
|
||||
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
|
||||
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
|
||||
def quality(self) -> float:
|
||||
retval = log_get(0.3, 10, self.getter("q")[0])
|
||||
retval = util.log_get(0.3, 10, 1.0 - self.getter('q')[0])
|
||||
return round(retval, 1)
|
||||
|
||||
@quality.setter
|
||||
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', 1.0 - util.log_set(0.3, 10, val))
|
||||
|
||||
|
||||
class GEQ:
|
||||
@@ -573,7 +549,7 @@ class GEQ:
|
||||
f"slider_{param}": geq_prop(param)
|
||||
for param in [
|
||||
"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",
|
||||
"10k", "12k5", "16k", "20k",
|
||||
]
|
||||
@@ -585,96 +561,113 @@ class GEQ:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(GEQ, self).address
|
||||
return f"{root}/geq"
|
||||
return f'{root}/geq'
|
||||
|
||||
|
||||
class Mix:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Mix, self).address
|
||||
return f"{root}/mix"
|
||||
return f'{root}/mix'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on")[0] == 1
|
||||
return self.getter('on')[0] == 1
|
||||
|
||||
@on.setter
|
||||
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
|
||||
@util.db_from
|
||||
def fader(self) -> float:
|
||||
retval = self.getter("fader")[0]
|
||||
return _get_fader_val(retval)
|
||||
return self.getter('fader')[0]
|
||||
|
||||
@fader.setter
|
||||
@util.db_to
|
||||
def fader(self, val: float):
|
||||
_set_fader_val(self, val)
|
||||
self.setter('fader', val)
|
||||
|
||||
@property
|
||||
def lr(self) -> bool:
|
||||
return self.getter("lr")[0] == 1
|
||||
return self.getter('lr')[0] == 1
|
||||
|
||||
@lr.setter
|
||||
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)
|
||||
|
||||
|
||||
class Group:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Group, self).address
|
||||
return f"{root}/grp"
|
||||
return f'{root}/grp'
|
||||
|
||||
@property
|
||||
def dca(self) -> int:
|
||||
return self.getter("dca")[0]
|
||||
return self.getter('dca')[0]
|
||||
|
||||
@dca.setter
|
||||
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
|
||||
def mute(self) -> int:
|
||||
return self.getter("mute")[0]
|
||||
return self.getter('mute')[0]
|
||||
|
||||
@mute.setter
|
||||
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)
|
||||
|
||||
|
||||
class Automix:
|
||||
@property
|
||||
def address(self) -> str:
|
||||
root = super(Automix, self).address
|
||||
return f"{root}/automix"
|
||||
return f'{root}/automix'
|
||||
|
||||
@property
|
||||
def group(self) -> int:
|
||||
return self.getter("group")[0]
|
||||
return self.getter('group')[0]
|
||||
|
||||
@group.setter
|
||||
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
|
||||
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
|
||||
def weight(self, val: float):
|
||||
if not isinstance(val, float):
|
||||
raise XAirRemoteError(
|
||||
"weight is a float parameter, expected value in range -12 to 12"
|
||||
if not -12 <= val <= 12:
|
||||
self.logger.warning(
|
||||
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 logging
|
||||
|
||||
from .errors import XAirRemoteError
|
||||
from .shared import EQ, GEQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp
|
||||
from .meta import mute_prop
|
||||
from .shared import EQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp, Send
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IStrip(abc.ABC):
|
||||
@@ -10,13 +13,13 @@ class IStrip(abc.ABC):
|
||||
def __init__(self, remote, index: int):
|
||||
self._remote = remote
|
||||
self.index = index + 1
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def getter(self, param: str) -> tuple:
|
||||
self._remote.send(f"{self.address}/{param}")
|
||||
return self._remote.info_response
|
||||
return self._remote.query(f'{self.address}/{param}')
|
||||
|
||||
def setter(self, param: str, val: int):
|
||||
self._remote.send(f"{self.address}/{param}", val)
|
||||
self._remote.send(f'{self.address}/{param}', val)
|
||||
|
||||
@abc.abstractmethod
|
||||
def address(self):
|
||||
@@ -35,13 +38,14 @@ class Strip(IStrip):
|
||||
|
||||
Returns a Strip class of a kind.
|
||||
"""
|
||||
|
||||
STRIP_cls = type(
|
||||
f"Strip{remote.kind}",
|
||||
f'Strip{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
_cls.__name__.lower(): type(
|
||||
f"{_cls.__name__}{remote.kind}", (_cls, cls), {}
|
||||
f'{_cls.__name__}{remote.kind}', (_cls, cls), {}
|
||||
)(remote, index)
|
||||
for _cls in (
|
||||
Config,
|
||||
@@ -55,10 +59,15 @@ class Strip(IStrip):
|
||||
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)
|
||||
|
||||
@property
|
||||
def address(self) -> str:
|
||||
return f"/ch/{str(self.index).zfill(2)}"
|
||||
return f'/ch/{str(self.index).zfill(2)}'
|
||||
|
||||
@@ -1,5 +1,35 @@
|
||||
import functools
|
||||
import time
|
||||
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):
|
||||
return min + (max - min) * val
|
||||
@@ -17,7 +47,13 @@ def log_set(min, max, val):
|
||||
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:
|
||||
return 10
|
||||
elif retval >= 0.5:
|
||||
@@ -31,47 +67,28 @@ def _get_fader_val(retval):
|
||||
else:
|
||||
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:
|
||||
self.setter("fader", 1)
|
||||
val = 1
|
||||
elif val >= -10:
|
||||
self.setter("fader", (val + 30) / 40)
|
||||
val = (val + 30) / 40
|
||||
elif val >= -30:
|
||||
self.setter("fader", (val + 50) / 80)
|
||||
val = (val + 50) / 80
|
||||
elif val >= -60:
|
||||
self.setter("fader", (val + 70) / 160)
|
||||
val = (val + 70) / 160
|
||||
elif val >= -90:
|
||||
self.setter("fader", (val + 90) / 480)
|
||||
val = (val + 90) / 480
|
||||
else:
|
||||
self.setter("fader", 0)
|
||||
val = 0
|
||||
|
||||
func(param, val, **kwargs)
|
||||
|
||||
def _get_level_val(retval):
|
||||
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)
|
||||
return wrapper
|
||||
|
||||
140
xair_api/xair.py
140
xair_api/xair.py
@@ -1,8 +1,9 @@
|
||||
import abc
|
||||
import logging
|
||||
import threading
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
try:
|
||||
import tomllib
|
||||
@@ -13,32 +14,32 @@ from pythonosc.dispatcher import Dispatcher
|
||||
from pythonosc.osc_message_builder import OscMessageBuilder
|
||||
from pythonosc.osc_server import BlockingOSCUDPServer
|
||||
|
||||
from . import kinds
|
||||
from . import adapter, kinds, util
|
||||
from .bus import Bus
|
||||
from .config import Config
|
||||
from .dca import DCA
|
||||
from .errors import XAirRemoteError
|
||||
from .fx import FXReturn, FXSend
|
||||
from .errors import XAirRemoteConnectionTimeoutError, XAirRemoteError
|
||||
from .fx import FX, FXSend
|
||||
from .headamp import HeadAmp
|
||||
from .kinds import KindMap
|
||||
from .lr import LR
|
||||
from .rtn import Aux, Rtn
|
||||
from .rtn import AuxRtn, FxRtn
|
||||
from .strip import Strip
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OSCClientServer(BlockingOSCUDPServer):
|
||||
def __init__(self, address: str, dispatcher: Dispatcher):
|
||||
super().__init__(("", 0), dispatcher)
|
||||
super().__init__(('', 0), dispatcher)
|
||||
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)
|
||||
if value is None:
|
||||
values = list()
|
||||
elif isinstance(value, list):
|
||||
values = value
|
||||
else:
|
||||
values = [value]
|
||||
for val in values:
|
||||
vals = vals if vals is not None else []
|
||||
if not isinstance(vals, list):
|
||||
vals = [vals]
|
||||
for val in vals:
|
||||
builder.add_arg(val)
|
||||
msg = builder.build()
|
||||
self.socket.sendto(msg.dgram, self.xr_address)
|
||||
@@ -47,21 +48,18 @@ class OSCClientServer(BlockingOSCUDPServer):
|
||||
class XAirRemote(abc.ABC):
|
||||
"""Handles the communication with the mixer via the OSC protocol"""
|
||||
|
||||
_CONNECT_TIMEOUT = 0.5
|
||||
_WAIT_TIME = 0.025
|
||||
_REFRESH_TIMEOUT = 5
|
||||
|
||||
XAIR_PORT = 10024
|
||||
|
||||
info_response = []
|
||||
_info_response = []
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
dispatcher = Dispatcher()
|
||||
dispatcher.set_default_handler(self.msg_handler)
|
||||
self.xair_ip = kwargs["ip"] or self._ip_from_toml()
|
||||
self.xair_port = kwargs["port"] or self.XAIR_PORT
|
||||
if not (self.xair_ip and self.xair_port):
|
||||
raise XAirRemoteError("No valid ip or password detected")
|
||||
self.xair_ip = kwargs['ip'] or self._ip_from_toml()
|
||||
self.xair_port = kwargs['port']
|
||||
self._delay = kwargs['delay']
|
||||
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)
|
||||
|
||||
def __enter__(self):
|
||||
@@ -71,34 +69,37 @@ class XAirRemote(abc.ABC):
|
||||
return self
|
||||
|
||||
def _ip_from_toml(self) -> str:
|
||||
filepath = Path.cwd() / "config.toml"
|
||||
with open(filepath, "rb") as f:
|
||||
filepath = Path.cwd() / 'config.toml'
|
||||
with open(filepath, 'rb') as f:
|
||||
conn = tomllib.load(f)
|
||||
return conn["connection"].get("ip")
|
||||
return conn['connection'].get('ip')
|
||||
|
||||
@util.timeout
|
||||
def validate_connection(self):
|
||||
self.send("/xinfo")
|
||||
time.sleep(self._CONNECT_TIMEOUT)
|
||||
if len(self.info_response) > 0:
|
||||
print(f"Successfully connected to {self.info_response[2]}.")
|
||||
else:
|
||||
print(
|
||||
"Error: Failed to setup OSC connection to mixer. Please check for correct ip address."
|
||||
if not self.query('/xinfo'):
|
||||
raise XAirRemoteConnectionTimeoutError(self.xair_ip, self.xair_port)
|
||||
self.logger.info(
|
||||
f'Successfully connected to {self.info_response[2]} at {self.info_response[0]}.'
|
||||
)
|
||||
|
||||
@property
|
||||
def info_response(self):
|
||||
return self._info_response
|
||||
|
||||
def run_server(self):
|
||||
self.server.serve_forever()
|
||||
|
||||
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):
|
||||
self.server.send_message(address, param)
|
||||
time.sleep(self._WAIT_TIME)
|
||||
def send(self, addr: str, param: Optional[str] = None):
|
||||
self.logger.debug(f"sending: {addr} {param if param is not None else ''}")
|
||||
self.server.send_message(addr, param)
|
||||
|
||||
def _query(self, address):
|
||||
def query(self, address):
|
||||
self.send(address)
|
||||
time.sleep(self._WAIT_TIME)
|
||||
time.sleep(self._delay)
|
||||
return self.info_response
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_tr):
|
||||
@@ -112,8 +113,37 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
||||
The returned class will subclass XAirRemote.
|
||||
"""
|
||||
|
||||
def init(self, *args, **kwargs):
|
||||
defaultkwargs = {"ip": None, "port": None}
|
||||
def init_x32(self, *args, **kwargs):
|
||||
defaultkwargs = {
|
||||
'ip': None,
|
||||
'port': 10023,
|
||||
'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)
|
||||
self.headamp = tuple(adapter.HeadAmp(self, i) for i in range(kind.num_headamp))
|
||||
|
||||
def init_xair(self, *args, **kwargs):
|
||||
defaultkwargs = {
|
||||
'ip': None,
|
||||
'port': 10024,
|
||||
'delay': 0.02,
|
||||
'connect_timeout': 2,
|
||||
}
|
||||
kwargs = defaultkwargs | kwargs
|
||||
XAirRemote.__init__(self, *args, **kwargs)
|
||||
self.kind = kind
|
||||
@@ -121,17 +151,26 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
||||
self.strip = tuple(Strip.make(self, i) for i in range(kind.num_strip))
|
||||
self.bus = tuple(Bus.make(self, i) for i in range(kind.num_bus))
|
||||
self.dca = tuple(DCA(self, i) for i in range(kind.num_dca))
|
||||
self.fx = tuple(FX(self, i) for i in range(kind.num_fx))
|
||||
self.fxsend = tuple(FXSend.make(self, i) for i in range(kind.num_fx))
|
||||
self.fxreturn = tuple(FXReturn(self, i) for i in range(kind.num_fx))
|
||||
self.fxreturn = tuple(FxRtn.make(self, i) for i in range(kind.num_fx))
|
||||
self.auxreturn = AuxRtn.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))
|
||||
self.headamp = tuple(HeadAmp(self, i) for i in range(kind.num_strip))
|
||||
|
||||
if kind.id_ == 'X32':
|
||||
return type(
|
||||
f"XAirRemote{kind}",
|
||||
f'XAirRemote{kind}',
|
||||
(XAirRemote,),
|
||||
{
|
||||
"__init__": init,
|
||||
'__init__': init_x32,
|
||||
},
|
||||
)
|
||||
return type(
|
||||
f'XAirRemote{kind}',
|
||||
(XAirRemote,),
|
||||
{
|
||||
'__init__': init_xair,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -145,9 +184,10 @@ def request_remote_obj(kind_id: str, *args, **kwargs) -> XAirRemote:
|
||||
|
||||
Returns a reference to an XAirRemote class of a kind
|
||||
"""
|
||||
|
||||
XAIRREMOTE_cls = None
|
||||
try:
|
||||
XAIRREMOTE_cls = _remotes[kind_id]
|
||||
except ValueError as e:
|
||||
raise SystemExit(e)
|
||||
except KeyError as e:
|
||||
raise XAirRemoteError(f"Unknown mixer kind '{kind_id}'") from e
|
||||
return XAIRREMOTE_cls(*args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user