xair-api 2.2.6__tar.gz → 2.3.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xair_api-2.2.6 → xair_api-2.3.2}/PKG-INFO +20 -5
- {xair_api-2.2.6 → xair_api-2.3.2}/README.md +19 -4
- {xair_api-2.2.6 → xair_api-2.3.2}/pyproject.toml +16 -2
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/bus.py +4 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/config.py +10 -3
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/dca.py +4 -0
- xair_api-2.3.2/xair_api/errors.py +14 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/fx.py +4 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/lr.py +4 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/meta.py +3 -2
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/rtn.py +4 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/shared.py +49 -26
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/strip.py +4 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/util.py +29 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/xair.py +21 -14
- xair_api-2.2.6/xair_api/errors.py +0 -2
- {xair_api-2.2.6 → xair_api-2.3.2}/LICENSE +0 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/__init__.py +0 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/adapter.py +0 -0
- {xair_api-2.2.6 → xair_api-2.3.2}/xair_api/kinds.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: xair-api
|
|
3
|
-
Version: 2.2
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: Remote control Behringer X-Air | Midas MR mixers through OSC
|
|
5
5
|
Home-page: https://github.com/onyx-and-iris/xair-api-python
|
|
6
6
|
License: MIT
|
|
@@ -73,7 +73,7 @@ if __name__ == "__main__":
|
|
|
73
73
|
main()
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
#### `xair_api.connect(kind_id, ip=ip, delay=
|
|
76
|
+
#### `xair_api.connect(kind_id, ip=ip, delay=0.02, connect_timeout=2)`
|
|
77
77
|
|
|
78
78
|
Currently the following devices are supported:
|
|
79
79
|
|
|
@@ -90,6 +90,7 @@ The following keyword arguments may be passed:
|
|
|
90
90
|
- `port`: mixer port, defaults to 10023 for x32 and 10024 for xair
|
|
91
91
|
- `delay`: a delay between each command (applies to the getters). Defaults to 20ms.
|
|
92
92
|
- a note about delay, stability may rely on network connection. For wired connections the delay can be safely reduced.
|
|
93
|
+
- `connect_timeout`: amount of time to wait for a validated connection. Defaults to 2s.
|
|
93
94
|
|
|
94
95
|
## API
|
|
95
96
|
|
|
@@ -139,7 +140,7 @@ Contains the subclasses:
|
|
|
139
140
|
### `Strip`
|
|
140
141
|
|
|
141
142
|
Contains the subclasses:
|
|
142
|
-
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`)
|
|
143
|
+
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`, `Send`)
|
|
143
144
|
|
|
144
145
|
### `Bus`
|
|
145
146
|
|
|
@@ -154,12 +155,12 @@ Contains the subclasses:
|
|
|
154
155
|
### `FXRtn`
|
|
155
156
|
|
|
156
157
|
Contains the subclasses:
|
|
157
|
-
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
|
158
|
+
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
|
158
159
|
|
|
159
160
|
### `AuxRtn`
|
|
160
161
|
|
|
161
162
|
Contains the subclasses:
|
|
162
|
-
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
|
163
|
+
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
|
163
164
|
|
|
164
165
|
### `Subclasses`
|
|
165
166
|
|
|
@@ -306,6 +307,12 @@ tuple containing a class for each mute group
|
|
|
306
307
|
|
|
307
308
|
for example: `config.mute_group[0].on = True`
|
|
308
309
|
|
|
310
|
+
### `Send`
|
|
311
|
+
|
|
312
|
+
- `level`: float, -inf to 10.0
|
|
313
|
+
|
|
314
|
+
for example: `mixer.strip[10].send[3].level = -16.5`
|
|
315
|
+
|
|
309
316
|
### XAirRemote class (lower level)
|
|
310
317
|
|
|
311
318
|
Send an OSC command directly to the mixer
|
|
@@ -329,6 +336,14 @@ for example:
|
|
|
329
336
|
print(mixer.query("/ch/01/mix/on"))
|
|
330
337
|
```
|
|
331
338
|
|
|
339
|
+
### Errors
|
|
340
|
+
|
|
341
|
+
- `errors.XAirRemoteError`: Base error class for XAIR Remote.
|
|
342
|
+
- `errors.XAirRemoteConnectionTimeoutError`:Exception raised when a connection attempt times out.
|
|
343
|
+
- The following attributes are available:
|
|
344
|
+
- `ip`: IP of the mixer.
|
|
345
|
+
- `port`: Port of the mixer.
|
|
346
|
+
|
|
332
347
|
### `Tests`
|
|
333
348
|
|
|
334
349
|
Unplug any expensive equipment before running tests.
|
|
@@ -55,7 +55,7 @@ if __name__ == "__main__":
|
|
|
55
55
|
main()
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
#### `xair_api.connect(kind_id, ip=ip, delay=
|
|
58
|
+
#### `xair_api.connect(kind_id, ip=ip, delay=0.02, connect_timeout=2)`
|
|
59
59
|
|
|
60
60
|
Currently the following devices are supported:
|
|
61
61
|
|
|
@@ -72,6 +72,7 @@ The following keyword arguments may be passed:
|
|
|
72
72
|
- `port`: mixer port, defaults to 10023 for x32 and 10024 for xair
|
|
73
73
|
- `delay`: a delay between each command (applies to the getters). Defaults to 20ms.
|
|
74
74
|
- a note about delay, stability may rely on network connection. For wired connections the delay can be safely reduced.
|
|
75
|
+
- `connect_timeout`: amount of time to wait for a validated connection. Defaults to 2s.
|
|
75
76
|
|
|
76
77
|
## API
|
|
77
78
|
|
|
@@ -121,7 +122,7 @@ Contains the subclasses:
|
|
|
121
122
|
### `Strip`
|
|
122
123
|
|
|
123
124
|
Contains the subclasses:
|
|
124
|
-
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`)
|
|
125
|
+
(`Config`, `Preamp`, `Gate`, `Dyn`, `Insert`, `GEQ`, `EQ`, `Mix`, `Group`, `Automix`, `Send`)
|
|
125
126
|
|
|
126
127
|
### `Bus`
|
|
127
128
|
|
|
@@ -136,12 +137,12 @@ Contains the subclasses:
|
|
|
136
137
|
### `FXRtn`
|
|
137
138
|
|
|
138
139
|
Contains the subclasses:
|
|
139
|
-
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
|
140
|
+
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
|
140
141
|
|
|
141
142
|
### `AuxRtn`
|
|
142
143
|
|
|
143
144
|
Contains the subclasses:
|
|
144
|
-
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`)
|
|
145
|
+
(`Config`, `Preamp`, `EQ`, `Mix`, `Group`, `Send`)
|
|
145
146
|
|
|
146
147
|
### `Subclasses`
|
|
147
148
|
|
|
@@ -288,6 +289,12 @@ tuple containing a class for each mute group
|
|
|
288
289
|
|
|
289
290
|
for example: `config.mute_group[0].on = True`
|
|
290
291
|
|
|
292
|
+
### `Send`
|
|
293
|
+
|
|
294
|
+
- `level`: float, -inf to 10.0
|
|
295
|
+
|
|
296
|
+
for example: `mixer.strip[10].send[3].level = -16.5`
|
|
297
|
+
|
|
291
298
|
### XAirRemote class (lower level)
|
|
292
299
|
|
|
293
300
|
Send an OSC command directly to the mixer
|
|
@@ -311,6 +318,14 @@ for example:
|
|
|
311
318
|
print(mixer.query("/ch/01/mix/on"))
|
|
312
319
|
```
|
|
313
320
|
|
|
321
|
+
### Errors
|
|
322
|
+
|
|
323
|
+
- `errors.XAirRemoteError`: Base error class for XAIR Remote.
|
|
324
|
+
- `errors.XAirRemoteConnectionTimeoutError`:Exception raised when a connection attempt times out.
|
|
325
|
+
- The following attributes are available:
|
|
326
|
+
- `ip`: IP of the mixer.
|
|
327
|
+
- `port`: Port of the mixer.
|
|
328
|
+
|
|
314
329
|
### `Tests`
|
|
315
330
|
|
|
316
331
|
Unplug any expensive equipment before running tests.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "xair-api"
|
|
3
|
-
version = "2.2
|
|
3
|
+
version = "2.3.2"
|
|
4
4
|
description = "Remote control Behringer X-Air | Midas MR mixers through OSC"
|
|
5
5
|
authors = ["onyx-and-iris <code@onyxandiris.online>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -13,10 +13,11 @@ python-osc = "^1.8.0"
|
|
|
13
13
|
tomli = { version = "^2.0.1", python = "<3.11" }
|
|
14
14
|
|
|
15
15
|
[tool.poetry.group.dev.dependencies]
|
|
16
|
-
pytest = "^7.
|
|
16
|
+
pytest = "^7.4.4"
|
|
17
17
|
pytest-randomly = "^3.12.0"
|
|
18
18
|
black = "^22.6.0"
|
|
19
19
|
isort = "^5.10.1"
|
|
20
|
+
tox = "^4.12.1"
|
|
20
21
|
|
|
21
22
|
[build-system]
|
|
22
23
|
requires = ["poetry-core>=1.0.0"]
|
|
@@ -27,3 +28,16 @@ obs = "scripts:ex_obs"
|
|
|
27
28
|
sends = "scripts:ex_sends"
|
|
28
29
|
xair = "scripts:test_xair"
|
|
29
30
|
x32 = "scripts:test_x32"
|
|
31
|
+
all = "scripts:test_all"
|
|
32
|
+
|
|
33
|
+
[tool.tox]
|
|
34
|
+
legacy_tox_ini = """
|
|
35
|
+
[tox]
|
|
36
|
+
envlist = py310,py311,py312
|
|
37
|
+
|
|
38
|
+
[testenv]
|
|
39
|
+
allowlist_externals = poetry
|
|
40
|
+
commands =
|
|
41
|
+
poetry install -v
|
|
42
|
+
poetry run pytest tests/
|
|
43
|
+
"""
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
from .meta import mute_prop
|
|
4
5
|
from .shared import EQ, GEQ, Config, Dyn, Group, Insert, Mix
|
|
5
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
class IBus(abc.ABC):
|
|
8
11
|
"""Abstract Base Class for buses"""
|
|
@@ -10,6 +13,7 @@ class IBus(abc.ABC):
|
|
|
10
13
|
def __init__(self, remote, index: int):
|
|
11
14
|
self._remote = remote
|
|
12
15
|
self.index = index + 1
|
|
16
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
13
17
|
|
|
14
18
|
def getter(self, param: str):
|
|
15
19
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
from . import kinds, util
|
|
4
|
-
from .errors import XAirRemoteError
|
|
5
5
|
from .meta import bool_prop
|
|
6
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class IConfig(abc.ABC):
|
|
9
11
|
"""Abstract Base Class for config"""
|
|
10
12
|
|
|
11
13
|
def __init__(self, remote):
|
|
12
14
|
self._remote = remote
|
|
15
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
13
16
|
|
|
14
17
|
def getter(self, param: str):
|
|
15
18
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -114,7 +117,9 @@ class Config(IConfig):
|
|
|
114
117
|
@sourcetrim.setter
|
|
115
118
|
def sourcetrim(self, val: float):
|
|
116
119
|
if not -18 <= val <= 18:
|
|
117
|
-
|
|
120
|
+
self.logger.warning(
|
|
121
|
+
f"sourcetrim got {val}, expected value in range -18.0 to 18.0"
|
|
122
|
+
)
|
|
118
123
|
self.setter("sourcetrim", util.lin_set(-18, 18, val))
|
|
119
124
|
|
|
120
125
|
@property
|
|
@@ -140,7 +145,9 @@ class Config(IConfig):
|
|
|
140
145
|
@dimgain.setter
|
|
141
146
|
def dimgain(self, val: int):
|
|
142
147
|
if not -40 <= val <= 0:
|
|
143
|
-
|
|
148
|
+
self.logger.warning(
|
|
149
|
+
f"dimgain got {val}, expected value in range -40 to 0"
|
|
150
|
+
)
|
|
144
151
|
self.setter("dimatt", util.lin_set(-40, 0, val))
|
|
145
152
|
|
|
146
153
|
@property
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
2
5
|
|
|
3
6
|
|
|
4
7
|
class IDCA(abc.ABC):
|
|
@@ -7,6 +10,7 @@ class IDCA(abc.ABC):
|
|
|
7
10
|
def __init__(self, remote, index: int):
|
|
8
11
|
self._remote = remote
|
|
9
12
|
self.index = index + 1
|
|
13
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
10
14
|
|
|
11
15
|
def getter(self, param: str) -> tuple:
|
|
12
16
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class XAirRemoteError(Exception):
|
|
2
|
+
"""Base error class for XAIR Remote."""
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class XAirRemoteConnectionTimeoutError(XAirRemoteError):
|
|
6
|
+
"""Exception raised when a connection attempt times out"""
|
|
7
|
+
|
|
8
|
+
def __init__(self, ip, port):
|
|
9
|
+
self.ip = ip
|
|
10
|
+
self.port = port
|
|
11
|
+
|
|
12
|
+
super().__init__(
|
|
13
|
+
f"Timeout attempting to connect to mixer at {self.ip}:{self.port}"
|
|
14
|
+
)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
from .meta import mute_prop
|
|
4
5
|
from .shared import Config, Group, Mix
|
|
5
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
class IFX(abc.ABC):
|
|
8
11
|
"""Abstract Base Class for fxs"""
|
|
@@ -10,6 +13,7 @@ class IFX(abc.ABC):
|
|
|
10
13
|
def __init__(self, remote, index: int):
|
|
11
14
|
self._remote = remote
|
|
12
15
|
self.index = index + 1
|
|
16
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
13
17
|
|
|
14
18
|
def getter(self, param: str):
|
|
15
19
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
from typing import Optional
|
|
3
4
|
|
|
4
5
|
from .meta import mute_prop
|
|
5
6
|
from .shared import EQ, GEQ, Config, Dyn, Insert, Mix
|
|
6
7
|
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
class ILR(abc.ABC):
|
|
9
12
|
"""Abstract Base Class for lr"""
|
|
@@ -12,6 +15,7 @@ class ILR(abc.ABC):
|
|
|
12
15
|
self._remote = remote
|
|
13
16
|
if index is not None:
|
|
14
17
|
self.index = index + 1
|
|
18
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
15
19
|
|
|
16
20
|
def getter(self, param: str):
|
|
17
21
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from .errors import XAirRemoteError
|
|
2
1
|
from .util import lin_get, lin_set
|
|
3
2
|
|
|
4
3
|
|
|
@@ -58,7 +57,9 @@ def geq_prop(param):
|
|
|
58
57
|
|
|
59
58
|
def fset(self, val):
|
|
60
59
|
if not -15 <= val <= 15:
|
|
61
|
-
|
|
60
|
+
self.logger.warning(
|
|
61
|
+
f"slider_{param} got {val}, expected value in range -15.0 to 15.0"
|
|
62
|
+
)
|
|
62
63
|
self.setter(param, lin_set(-15, 15, val))
|
|
63
64
|
|
|
64
65
|
return property(fget, fset)
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
from typing import Optional
|
|
3
4
|
|
|
4
5
|
from .meta import mute_prop
|
|
5
6
|
from .shared import EQ, Config, Group, Mix, Preamp, Send
|
|
6
7
|
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
class IRtn(abc.ABC):
|
|
9
12
|
"""Abstract Base Class for rtn"""
|
|
@@ -12,6 +15,7 @@ class IRtn(abc.ABC):
|
|
|
12
15
|
self._remote = remote
|
|
13
16
|
if index is not None:
|
|
14
17
|
self.index = index + 1
|
|
18
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
15
19
|
|
|
16
20
|
def getter(self, param: str):
|
|
17
21
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from typing import Optional, Union
|
|
2
2
|
|
|
3
3
|
from . import util
|
|
4
|
-
from .errors import XAirRemoteError
|
|
5
4
|
from .meta import geq_prop
|
|
6
5
|
|
|
7
6
|
"""
|
|
@@ -61,7 +60,9 @@ class Preamp:
|
|
|
61
60
|
@usbtrim.setter
|
|
62
61
|
def usbtrim(self, val: float):
|
|
63
62
|
if not -18 <= val <= 18:
|
|
64
|
-
|
|
63
|
+
self.logger.warning(
|
|
64
|
+
f"usbtrim got {val}, expected value in range -18.0 to 18.0"
|
|
65
|
+
)
|
|
65
66
|
self.setter("rtntrim", util.lin_set(-18, 18, val))
|
|
66
67
|
|
|
67
68
|
@property
|
|
@@ -95,7 +96,9 @@ class Preamp:
|
|
|
95
96
|
@highpassfilter.setter
|
|
96
97
|
def highpassfilter(self, val: int):
|
|
97
98
|
if not 20 <= val <= 400:
|
|
98
|
-
|
|
99
|
+
self.logger.warning(
|
|
100
|
+
f"highpassfilter got {val}, expected value in range 20 to 400"
|
|
101
|
+
)
|
|
99
102
|
self.setter("hpf", util.log_set(20, 400, val))
|
|
100
103
|
|
|
101
104
|
|
|
@@ -122,7 +125,7 @@ class Gate:
|
|
|
122
125
|
def mode(self, val: str):
|
|
123
126
|
opts = ("gate", "exp2", "exp3", "exp4", "duck")
|
|
124
127
|
if val not in opts:
|
|
125
|
-
|
|
128
|
+
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
|
126
129
|
self.setter("mode", opts.index(val))
|
|
127
130
|
|
|
128
131
|
@property
|
|
@@ -132,7 +135,9 @@ class Gate:
|
|
|
132
135
|
@threshold.setter
|
|
133
136
|
def threshold(self, val: float):
|
|
134
137
|
if not -80 <= val <= 0:
|
|
135
|
-
|
|
138
|
+
self.logger.warning(
|
|
139
|
+
f"threshold got {val}, expected value in range -80.0 to 0.0"
|
|
140
|
+
)
|
|
136
141
|
self.setter("thr", util.lin_set(-80, 0, val))
|
|
137
142
|
|
|
138
143
|
@property
|
|
@@ -142,7 +147,7 @@ class Gate:
|
|
|
142
147
|
@range.setter
|
|
143
148
|
def range(self, val: int):
|
|
144
149
|
if not 3 <= val <= 60:
|
|
145
|
-
|
|
150
|
+
self.logger.warning(f"range got {val}, expected value in range 3 to 60")
|
|
146
151
|
self.setter("range", util.lin_set(3, 60, val))
|
|
147
152
|
|
|
148
153
|
@property
|
|
@@ -152,7 +157,7 @@ class Gate:
|
|
|
152
157
|
@attack.setter
|
|
153
158
|
def attack(self, val: int):
|
|
154
159
|
if not 0 <= val <= 120:
|
|
155
|
-
|
|
160
|
+
self.logger.warning(f"attack got {val}, expected value in range 0 to 120")
|
|
156
161
|
self.setter("attack", util.lin_set(0, 120, val))
|
|
157
162
|
|
|
158
163
|
@property
|
|
@@ -163,7 +168,9 @@ class Gate:
|
|
|
163
168
|
@hold.setter
|
|
164
169
|
def hold(self, val: float):
|
|
165
170
|
if not 0.02 <= val <= 2000:
|
|
166
|
-
|
|
171
|
+
self.logger.warning(
|
|
172
|
+
f"hold got {val}, expected value in range 0.02 to 2000.0"
|
|
173
|
+
)
|
|
167
174
|
self.setter("hold", util.log_set(0.02, 2000, val))
|
|
168
175
|
|
|
169
176
|
@property
|
|
@@ -173,7 +180,7 @@ class Gate:
|
|
|
173
180
|
@release.setter
|
|
174
181
|
def release(self, val: int):
|
|
175
182
|
if not 5 <= val <= 4000:
|
|
176
|
-
|
|
183
|
+
self.logger.warning(f"release got {val}, expected value in range 5 to 4000")
|
|
177
184
|
self.setter("release", util.log_set(5, 4000, val))
|
|
178
185
|
|
|
179
186
|
@property
|
|
@@ -208,7 +215,9 @@ class Gate:
|
|
|
208
215
|
@filterfreq.setter
|
|
209
216
|
def filterfreq(self, val: Union[float, int]):
|
|
210
217
|
if not 20 <= val <= 20000:
|
|
211
|
-
|
|
218
|
+
self.logger.warning(
|
|
219
|
+
f"filterfreq got {val}, expected value in range 20 to 20000"
|
|
220
|
+
)
|
|
212
221
|
self.setter("filter/f", util.log_set(20, 20000, val))
|
|
213
222
|
|
|
214
223
|
|
|
@@ -235,7 +244,7 @@ class Dyn:
|
|
|
235
244
|
def mode(self, val: str):
|
|
236
245
|
opts = ("comp", "exp")
|
|
237
246
|
if val not in opts:
|
|
238
|
-
|
|
247
|
+
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
|
239
248
|
self.setter("mode", opts.index(val))
|
|
240
249
|
|
|
241
250
|
@property
|
|
@@ -247,7 +256,7 @@ class Dyn:
|
|
|
247
256
|
def det(self, val: str):
|
|
248
257
|
opts = ("peak", "rms")
|
|
249
258
|
if val not in opts:
|
|
250
|
-
|
|
259
|
+
self.logger.warning(f"det got {val}, expected one of {opts}")
|
|
251
260
|
self.setter("det", opts.index(val))
|
|
252
261
|
|
|
253
262
|
@property
|
|
@@ -259,7 +268,7 @@ class Dyn:
|
|
|
259
268
|
def env(self, val: str):
|
|
260
269
|
opts = ("lin", "log")
|
|
261
270
|
if val not in opts:
|
|
262
|
-
|
|
271
|
+
self.logger.warning(f"env got {val}, expected one of {opts}")
|
|
263
272
|
self.setter("env", opts.index(val))
|
|
264
273
|
|
|
265
274
|
@property
|
|
@@ -269,7 +278,9 @@ class Dyn:
|
|
|
269
278
|
@threshold.setter
|
|
270
279
|
def threshold(self, val: float):
|
|
271
280
|
if not -60 <= val <= 0:
|
|
272
|
-
|
|
281
|
+
self.logger.warning(
|
|
282
|
+
f"threshold got {val}, expected value in range -60.0 to 0"
|
|
283
|
+
)
|
|
273
284
|
self.setter("thr", util.lin_set(-60, 0, val))
|
|
274
285
|
|
|
275
286
|
@property
|
|
@@ -288,7 +299,7 @@ class Dyn:
|
|
|
288
299
|
@knee.setter
|
|
289
300
|
def knee(self, val: int):
|
|
290
301
|
if not 0 <= val <= 5:
|
|
291
|
-
|
|
302
|
+
self.logger.warning(f"knee got {val}, expected value in range 0 to 5")
|
|
292
303
|
self.setter("knee", util.lin_set(0, 5, val))
|
|
293
304
|
|
|
294
305
|
@property
|
|
@@ -298,7 +309,7 @@ class Dyn:
|
|
|
298
309
|
@mgain.setter
|
|
299
310
|
def mgain(self, val: float):
|
|
300
311
|
if not 0 <= val <= 24:
|
|
301
|
-
|
|
312
|
+
self.logger.warning(f"mgain got {val}, expected value in range 0.0 to 24.0")
|
|
302
313
|
self.setter("mgain", util.lin_set(0, 24, val))
|
|
303
314
|
|
|
304
315
|
@property
|
|
@@ -308,7 +319,7 @@ class Dyn:
|
|
|
308
319
|
@attack.setter
|
|
309
320
|
def attack(self, val: int):
|
|
310
321
|
if not 0 <= val <= 120:
|
|
311
|
-
|
|
322
|
+
self.logger.warning(f"attack got {val}, expected value in range 0 to 120")
|
|
312
323
|
self.setter("attack", util.lin_set(0, 120, val))
|
|
313
324
|
|
|
314
325
|
@property
|
|
@@ -319,7 +330,9 @@ class Dyn:
|
|
|
319
330
|
@hold.setter
|
|
320
331
|
def hold(self, val: float):
|
|
321
332
|
if not 0.02 <= val <= 2000:
|
|
322
|
-
|
|
333
|
+
self.logger.warning(
|
|
334
|
+
f"hold got {val}, expected value in range 0.02 to 2000.0"
|
|
335
|
+
)
|
|
323
336
|
self.setter("hold", util.log_set(0.02, 2000, val))
|
|
324
337
|
|
|
325
338
|
@property
|
|
@@ -329,7 +342,7 @@ class Dyn:
|
|
|
329
342
|
@release.setter
|
|
330
343
|
def release(self, val: int):
|
|
331
344
|
if not 5 <= val <= 4000:
|
|
332
|
-
|
|
345
|
+
self.logger.warning(f"release got {val}, expected value in range 5 to 4000")
|
|
333
346
|
self.setter("release", util.log_set(5, 4000, val))
|
|
334
347
|
|
|
335
348
|
@property
|
|
@@ -339,7 +352,7 @@ class Dyn:
|
|
|
339
352
|
@mix.setter
|
|
340
353
|
def mix(self, val: int):
|
|
341
354
|
if not 0 <= val <= 100:
|
|
342
|
-
|
|
355
|
+
self.logger.warning(f"mix got {val}, expected value in range 0 to 100")
|
|
343
356
|
self.setter("mix", util.lin_set(0, 100, val))
|
|
344
357
|
|
|
345
358
|
@property
|
|
@@ -382,7 +395,9 @@ class Dyn:
|
|
|
382
395
|
@filterfreq.setter
|
|
383
396
|
def filterfreq(self, val: Union[float, int]):
|
|
384
397
|
if not 20 <= val <= 20000:
|
|
385
|
-
|
|
398
|
+
self.logger.warning(
|
|
399
|
+
f"filterfreq got {val}, expected value in range 20 to 20000"
|
|
400
|
+
)
|
|
386
401
|
self.setter("filter/f", util.log_set(20, 20000, val))
|
|
387
402
|
|
|
388
403
|
|
|
@@ -462,7 +477,7 @@ class EQ:
|
|
|
462
477
|
def mode(self, val: str):
|
|
463
478
|
opts = ("peq", "geq", "teq")
|
|
464
479
|
if val not in opts:
|
|
465
|
-
|
|
480
|
+
self.logger.warning(f"mode got {val}, expected one of {opts}")
|
|
466
481
|
self.setter("mode", opts.index(val))
|
|
467
482
|
|
|
468
483
|
class EQBand:
|
|
@@ -491,7 +506,9 @@ class EQ:
|
|
|
491
506
|
@frequency.setter
|
|
492
507
|
def frequency(self, val: float):
|
|
493
508
|
if not 20 <= val <= 20000:
|
|
494
|
-
|
|
509
|
+
self.logger.warning(
|
|
510
|
+
f"frequency got {val}, expected value in range 20.0 to 20000.0"
|
|
511
|
+
)
|
|
495
512
|
self.setter("f", util.log_set(20, 20000, val))
|
|
496
513
|
|
|
497
514
|
@property
|
|
@@ -501,7 +518,9 @@ class EQ:
|
|
|
501
518
|
@gain.setter
|
|
502
519
|
def gain(self, val: float):
|
|
503
520
|
if not -15 <= val <= 15:
|
|
504
|
-
|
|
521
|
+
self.logger.warning(
|
|
522
|
+
f"gain got {val}, expected value in range -15.0 to 15.0"
|
|
523
|
+
)
|
|
505
524
|
self.setter("g", util.lin_set(-15, 15, val))
|
|
506
525
|
|
|
507
526
|
@property
|
|
@@ -512,7 +531,9 @@ class EQ:
|
|
|
512
531
|
@quality.setter
|
|
513
532
|
def quality(self, val: float):
|
|
514
533
|
if not 0.3 <= val <= 10:
|
|
515
|
-
|
|
534
|
+
self.logger.warning(
|
|
535
|
+
f"quality got {val}, expected value in range 0.3 to 10.0"
|
|
536
|
+
)
|
|
516
537
|
self.setter("q", util.log_set(0.3, 10, val))
|
|
517
538
|
|
|
518
539
|
|
|
@@ -620,7 +641,9 @@ class Automix:
|
|
|
620
641
|
@weight.setter
|
|
621
642
|
def weight(self, val: float):
|
|
622
643
|
if not -12 <= val <= 12:
|
|
623
|
-
|
|
644
|
+
self.logger.warning(
|
|
645
|
+
f"weight got {val}, expected value in range -12.0 to 12.0"
|
|
646
|
+
)
|
|
624
647
|
self.setter("weight", util.lin_set(-12, 12, val))
|
|
625
648
|
|
|
626
649
|
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import abc
|
|
2
|
+
import logging
|
|
2
3
|
|
|
3
4
|
from .meta import mute_prop
|
|
4
5
|
from .shared import EQ, Automix, Config, Dyn, Gate, Group, Insert, Mix, Preamp, Send
|
|
5
6
|
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
class IStrip(abc.ABC):
|
|
8
11
|
"""Abstract Base Class for strips"""
|
|
@@ -10,6 +13,7 @@ class IStrip(abc.ABC):
|
|
|
10
13
|
def __init__(self, remote, index: int):
|
|
11
14
|
self._remote = remote
|
|
12
15
|
self.index = index + 1
|
|
16
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
13
17
|
|
|
14
18
|
def getter(self, param: str) -> tuple:
|
|
15
19
|
return self._remote.query(f"{self.address}/{param}")
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import time
|
|
2
3
|
from math import exp, log
|
|
3
4
|
|
|
5
|
+
from .errors import XAirRemoteConnectionTimeoutError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def timeout(func):
|
|
9
|
+
"""
|
|
10
|
+
Times out the validate_connection function once time elapsed exceeds remote.connect_timeout.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
@functools.wraps(func)
|
|
14
|
+
def wrapper(*args, **kwargs):
|
|
15
|
+
remote, *_ = args
|
|
16
|
+
|
|
17
|
+
err = None
|
|
18
|
+
start = time.time()
|
|
19
|
+
while time.time() < start + remote.connect_timeout:
|
|
20
|
+
try:
|
|
21
|
+
func(*args, **kwargs)
|
|
22
|
+
remote.logger.debug(f"login time: {round(time.time() - start, 2)}")
|
|
23
|
+
err = None
|
|
24
|
+
break
|
|
25
|
+
except XAirRemoteConnectionTimeoutError as e:
|
|
26
|
+
err = e
|
|
27
|
+
continue
|
|
28
|
+
if err:
|
|
29
|
+
raise err
|
|
30
|
+
|
|
31
|
+
return wrapper
|
|
32
|
+
|
|
4
33
|
|
|
5
34
|
def lin_get(min, max, val):
|
|
6
35
|
return min + (max - min) * val
|
|
@@ -14,17 +14,19 @@ from pythonosc.dispatcher import Dispatcher
|
|
|
14
14
|
from pythonosc.osc_message_builder import OscMessageBuilder
|
|
15
15
|
from pythonosc.osc_server import BlockingOSCUDPServer
|
|
16
16
|
|
|
17
|
-
from . import adapter, kinds
|
|
17
|
+
from . import adapter, kinds, util
|
|
18
18
|
from .bus import Bus
|
|
19
19
|
from .config import Config
|
|
20
20
|
from .dca import DCA
|
|
21
|
-
from .errors import XAirRemoteError
|
|
21
|
+
from .errors import XAirRemoteConnectionTimeoutError, XAirRemoteError
|
|
22
22
|
from .fx import FX, FXSend
|
|
23
23
|
from .kinds import KindMap
|
|
24
24
|
from .lr import LR
|
|
25
25
|
from .rtn import AuxRtn, FxRtn
|
|
26
26
|
from .strip import Strip
|
|
27
27
|
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
28
30
|
|
|
29
31
|
class OSCClientServer(BlockingOSCUDPServer):
|
|
30
32
|
def __init__(self, address: str, dispatcher: Dispatcher):
|
|
@@ -45,10 +47,6 @@ class OSCClientServer(BlockingOSCUDPServer):
|
|
|
45
47
|
class XAirRemote(abc.ABC):
|
|
46
48
|
"""Handles the communication with the mixer via the OSC protocol"""
|
|
47
49
|
|
|
48
|
-
logger = logging.getLogger("xair.xairremote")
|
|
49
|
-
|
|
50
|
-
_CONNECT_TIMEOUT = 0.5
|
|
51
|
-
|
|
52
50
|
_info_response = []
|
|
53
51
|
|
|
54
52
|
def __init__(self, **kwargs):
|
|
@@ -57,6 +55,8 @@ class XAirRemote(abc.ABC):
|
|
|
57
55
|
self.xair_ip = kwargs["ip"] or self._ip_from_toml()
|
|
58
56
|
self.xair_port = kwargs["port"]
|
|
59
57
|
self._delay = kwargs["delay"]
|
|
58
|
+
self.connect_timeout = kwargs["connect_timeout"]
|
|
59
|
+
self.logger = logger.getChild(self.__class__.__name__)
|
|
60
60
|
if not self.xair_ip:
|
|
61
61
|
raise XAirRemoteError("No valid ip detected")
|
|
62
62
|
self.server = OSCClientServer((self.xair_ip, self.xair_port), dispatcher)
|
|
@@ -73,13 +73,10 @@ class XAirRemote(abc.ABC):
|
|
|
73
73
|
conn = tomllib.load(f)
|
|
74
74
|
return conn["connection"].get("ip")
|
|
75
75
|
|
|
76
|
+
@util.timeout
|
|
76
77
|
def validate_connection(self):
|
|
77
|
-
self.
|
|
78
|
-
|
|
79
|
-
if not self.info_response:
|
|
80
|
-
raise XAirRemoteError(
|
|
81
|
-
"Failed to setup OSC connection to mixer. Please check for correct ip address."
|
|
82
|
-
)
|
|
78
|
+
if not self.query("/xinfo"):
|
|
79
|
+
raise XAirRemoteConnectionTimeoutError(self.xair_ip, self.xair_port)
|
|
83
80
|
self.logger.info(
|
|
84
81
|
f"Successfully connected to {self.info_response[2]} at {self.info_response[0]}."
|
|
85
82
|
)
|
|
@@ -116,7 +113,12 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
|
|
116
113
|
"""
|
|
117
114
|
|
|
118
115
|
def init_x32(self, *args, **kwargs):
|
|
119
|
-
defaultkwargs = {
|
|
116
|
+
defaultkwargs = {
|
|
117
|
+
"ip": None,
|
|
118
|
+
"port": 10023,
|
|
119
|
+
"delay": 0.02,
|
|
120
|
+
"connect_timeout": 2,
|
|
121
|
+
}
|
|
120
122
|
kwargs = defaultkwargs | kwargs
|
|
121
123
|
XAirRemote.__init__(self, *args, **kwargs)
|
|
122
124
|
self.kind = kind
|
|
@@ -134,7 +136,12 @@ def _make_remote(kind: KindMap) -> XAirRemote:
|
|
|
134
136
|
self.config = Config.make(self)
|
|
135
137
|
|
|
136
138
|
def init_xair(self, *args, **kwargs):
|
|
137
|
-
defaultkwargs = {
|
|
139
|
+
defaultkwargs = {
|
|
140
|
+
"ip": None,
|
|
141
|
+
"port": 10024,
|
|
142
|
+
"delay": 0.02,
|
|
143
|
+
"connect_timeout": 2,
|
|
144
|
+
}
|
|
138
145
|
kwargs = defaultkwargs | kwargs
|
|
139
146
|
XAirRemote.__init__(self, *args, **kwargs)
|
|
140
147
|
self.kind = kind
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|