mirror of
https://github.com/onyx-and-iris/voicemeeter-api-python.git
synced 2026-04-09 08:43:29 +00:00
Compare commits
13 Commits
add-bits-k
...
868017c79f
| Author | SHA1 | Date | |
|---|---|---|---|
| 868017c79f | |||
| 795296d71e | |||
| e21a458c6f | |||
| b79d9494a2 | |||
| 328bea347c | |||
| 38bd284ba6 | |||
| da1d5132a8 | |||
| 7b725a51e3 | |||
| cf7301712c | |||
| a6f52be9ac | |||
| 01633f06da | |||
| 79a0c93466 | |||
| c1b2a543cc |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -128,10 +128,11 @@ dmypy.json
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# test reports
|
||||
tests/reports/junit-*.xml
|
||||
|
||||
# test/config
|
||||
quick.py
|
||||
test-*.py
|
||||
config.toml
|
||||
vm-api.log
|
||||
logging.json
|
||||
|
||||
.vscode/
|
||||
7
.pre-commit-config.yaml
Normal file
7
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
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
|
||||
69
README.md
69
README.md
@@ -1,12 +1,10 @@
|
||||
[](https://badge.fury.io/py/voicemeeter-api)
|
||||
[](https://github.com/onyx-and-iris/voicemeeter-api-python/blob/dev/LICENSE)
|
||||
[](https://python-poetry.org/)
|
||||
[](https://github.com/psf/black)
|
||||
[](https://pycqa.github.io/isort/)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||

|
||||

|
||||

|
||||
[](./tests/reports/basic.html)
|
||||
[](./tests/reports/banana.html)
|
||||
[](./tests/reports/potato.html)
|
||||
|
||||
# Python Wrapper for Voicemeeter API
|
||||
|
||||
@@ -46,24 +44,24 @@ class ManyThings:
|
||||
self.vm = vm
|
||||
|
||||
def things(self):
|
||||
self.vm.strip[0].label = "podmic"
|
||||
self.vm.strip[0].label = 'podmic'
|
||||
self.vm.strip[0].mute = True
|
||||
print(
|
||||
f"strip 0 ({self.vm.strip[0].label}) mute has been set to {self.vm.strip[0].mute}"
|
||||
f'strip 0 ({self.vm.strip[0].label}) mute has been set to {self.vm.strip[0].mute}'
|
||||
)
|
||||
|
||||
def other_things(self):
|
||||
self.vm.bus[3].gain = -6.3
|
||||
self.vm.bus[4].eq.on = True
|
||||
info = (
|
||||
f"bus 3 gain has been set to {self.vm.bus[3].gain}",
|
||||
f"bus 4 eq has been set to {self.vm.bus[4].eq.on}",
|
||||
f'bus 3 gain has been set to {self.vm.bus[3].gain}',
|
||||
f'bus 4 eq has been set to {self.vm.bus[4].eq.on}',
|
||||
)
|
||||
print("\n".join(info))
|
||||
print('\n'.join(info))
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "banana"
|
||||
KIND_ID = 'banana'
|
||||
|
||||
with voicemeeterlib.api(KIND_ID) as vm:
|
||||
do = ManyThings(vm)
|
||||
@@ -73,18 +71,17 @@ def main():
|
||||
# set many parameters at once
|
||||
vm.apply(
|
||||
{
|
||||
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
||||
"bus-2": {"mute": True, "eq": {"on": True}},
|
||||
"button-0": {"state": True},
|
||||
"vban-in-0": {"on": True},
|
||||
"vban-out-1": {"name": "streamname"},
|
||||
'strip-2': {'A1': True, 'B1': True, 'gain': -6.0},
|
||||
'bus-2': {'mute': True, 'eq': {'on': True}},
|
||||
'button-0': {'state': True},
|
||||
'vban-in-0': {'on': True},
|
||||
'vban-out-1': {'name': 'streamname'},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
```
|
||||
|
||||
Otherwise you must remember to call `vm.login()`, `vm.logout()` at the start/end of your code.
|
||||
@@ -151,8 +148,8 @@ Set mute state as value for the app matching name.
|
||||
example:
|
||||
|
||||
```python
|
||||
vm.strip[5].appmute("Spotify", True)
|
||||
vm.strip[5].appgain("Spotify", 0.5)
|
||||
vm.strip[5].appmute('Spotify', True)
|
||||
vm.strip[5].appgain('Spotify', 0.5)
|
||||
```
|
||||
|
||||
#### Strip.Comp
|
||||
@@ -368,7 +365,7 @@ example:
|
||||
|
||||
```python
|
||||
print(vm.strip[0].device.name)
|
||||
vm.bus[0].device.asio = "Audient USB Audio ASIO Driver"
|
||||
vm.bus[0].device.asio = 'Audient USB Audio ASIO Driver'
|
||||
```
|
||||
|
||||
strip|bus device parameters are defined for physical channels only.
|
||||
@@ -427,7 +424,7 @@ vm.recorder.B2 = False
|
||||
vm.recorder.load(r'C:\music\mytune.mp3')
|
||||
|
||||
# set the goto time to 1m 30s
|
||||
vm.recorder.goto("00:01:30")
|
||||
vm.recorder.goto('00:01:30')
|
||||
```
|
||||
|
||||
#### Recorder.Mode
|
||||
@@ -681,11 +678,11 @@ get() may return None if no value for requested key in midi cache
|
||||
```python
|
||||
vm.apply(
|
||||
{
|
||||
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
||||
"bus-2": {"mute": True, "eq": {"on": True}},
|
||||
"button-0": {"state": True},
|
||||
"vban-in-0": {"on": True},
|
||||
"vban-out-1": {"name": "streamname"},
|
||||
'strip-2': {'A1': True, 'B1': True, 'gain': -6.0},
|
||||
'bus-2': {'mute': True, 'eq': {'on': True}},
|
||||
'button-0': {'state': True},
|
||||
'vban-in-0': {'on': True},
|
||||
'vban-out-1': {'name': 'streamname'},
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -693,8 +690,8 @@ vm.apply(
|
||||
Or for each class you may do:
|
||||
|
||||
```python
|
||||
vm.strip[0].apply({"mute": True, "gain": 3.2, "A1": True})
|
||||
vm.vban.outstream[0].apply({"on": True, "name": "streamname", "bit": 24})
|
||||
vm.strip[0].apply({'mute': True, 'gain': 3.2, 'A1': True})
|
||||
vm.vban.outstream[0].apply({'on': True, 'name': 'streamname', 'bit': 24})
|
||||
```
|
||||
|
||||
## Config Files
|
||||
@@ -795,7 +792,7 @@ The following methods are available:
|
||||
example:
|
||||
|
||||
```python
|
||||
vm.event.remove(["pdirty", "mdirty", "midi"])
|
||||
vm.event.remove(['pdirty', 'mdirty', 'midi'])
|
||||
|
||||
# get a list of currently subscribed
|
||||
print(vm.event.get())
|
||||
@@ -865,17 +862,19 @@ import voicemeeterlib
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
with voicemeeterlib.api("banana") as vm:
|
||||
...
|
||||
with voicemeeterlib.api('banana') as vm:
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
### Run tests
|
||||
|
||||
To run all tests:
|
||||
Install [poetry](https://python-poetry.org/docs/#installation) and then:
|
||||
|
||||
```
|
||||
pytest -v
|
||||
```powershell
|
||||
poetry poe test-basic
|
||||
poetry poe test-banana
|
||||
poetry poe test-potato
|
||||
```
|
||||
|
||||
### Official Documentation
|
||||
|
||||
24
__main__.py
24
__main__.py
@@ -6,24 +6,24 @@ class ManyThings:
|
||||
self.vm = vm
|
||||
|
||||
def things(self):
|
||||
self.vm.strip[0].label = "podmic"
|
||||
self.vm.strip[0].label = 'podmic'
|
||||
self.vm.strip[0].mute = True
|
||||
print(
|
||||
f"strip 0 ({self.vm.strip[0].label}) mute has been set to {self.vm.strip[0].mute}"
|
||||
f'strip 0 ({self.vm.strip[0].label}) mute has been set to {self.vm.strip[0].mute}'
|
||||
)
|
||||
|
||||
def other_things(self):
|
||||
self.vm.bus[3].gain = -6.3
|
||||
self.vm.bus[4].eq.on = True
|
||||
info = (
|
||||
f"bus 3 gain has been set to {self.vm.bus[3].gain}",
|
||||
f"bus 4 eq has been set to {self.vm.bus[4].eq.on}",
|
||||
f'bus 3 gain has been set to {self.vm.bus[3].gain}',
|
||||
f'bus 4 eq has been set to {self.vm.bus[4].eq.on}',
|
||||
)
|
||||
print("\n".join(info))
|
||||
print('\n'.join(info))
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "banana"
|
||||
KIND_ID = 'banana'
|
||||
|
||||
with voicemeeterlib.api(KIND_ID) as vm:
|
||||
do = ManyThings(vm)
|
||||
@@ -33,14 +33,14 @@ def main():
|
||||
# set many parameters at once
|
||||
vm.apply(
|
||||
{
|
||||
"strip-2": {"A1": True, "B1": True, "gain": -6.0},
|
||||
"bus-2": {"mute": True, "eq": {"on": True}},
|
||||
"button-0": {"state": True},
|
||||
"vban-in-0": {"on": True},
|
||||
"vban-out-1": {"name": "streamname"},
|
||||
'strip-2': {'A1': True, 'B1': True, 'gain': -6.0},
|
||||
'bus-2': {'mute': True, 'eq': {'on': True}},
|
||||
'button-0': {'state': True},
|
||||
'vban-in-0': {'on': True},
|
||||
'vban-out-1': {'name': 'streamname'},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -8,46 +8,46 @@ logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class App:
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
self._vm = vm
|
||||
# register the callbacks for each event
|
||||
self.vm.observer.add(
|
||||
self._vm.observer.add(
|
||||
[self.on_pdirty, self.on_mdirty, self.on_ldirty, self.on_midi]
|
||||
)
|
||||
|
||||
def __enter__(self):
|
||||
self.vm.init_thread()
|
||||
self._vm.init_thread()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.vm.end_thread()
|
||||
self._vm.end_thread()
|
||||
|
||||
def on_pdirty(self):
|
||||
print("pdirty!")
|
||||
print('pdirty!')
|
||||
|
||||
def on_mdirty(self):
|
||||
print("mdirty!")
|
||||
print('mdirty!')
|
||||
|
||||
def on_ldirty(self):
|
||||
for bus in self.vm.bus:
|
||||
for bus in self._vm.bus:
|
||||
if bus.levels.isdirty:
|
||||
print(bus, bus.levels.all)
|
||||
|
||||
def on_midi(self):
|
||||
current = self.vm.midi.current
|
||||
print(f"Value of midi button {current} is {self.vm.midi.get(current)}")
|
||||
current = self._vm.midi.current
|
||||
print(f'Value of midi button {current} is {self._vm.midi.get(current)}')
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "banana"
|
||||
KIND_ID = 'banana'
|
||||
|
||||
with voicemeeterlib.api(KIND_ID) as vm:
|
||||
with App(vm) as app:
|
||||
with App(vm):
|
||||
for i in range(5, 0, -1):
|
||||
print(f"events start in {i} seconds")
|
||||
print(f'events start in {i} seconds')
|
||||
time.sleep(1)
|
||||
vm.event.add(["pdirty", "ldirty", "midi", "mdirty"])
|
||||
vm.event.add(['pdirty', 'ldirty', 'midi', 'mdirty'])
|
||||
time.sleep(30)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -21,14 +21,14 @@ logging.basicConfig(level=logging.DEBUG)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
argparser = argparse.ArgumentParser(description="creates a basic dsl")
|
||||
argparser.add_argument("-i", action="store_true")
|
||||
argparser = argparse.ArgumentParser(description='creates a basic dsl')
|
||||
argparser.add_argument('-i', action='store_true')
|
||||
args = argparser.parse_args()
|
||||
|
||||
|
||||
ParamKinds = IntEnum(
|
||||
"ParamKinds",
|
||||
"bool float string",
|
||||
'ParamKinds',
|
||||
'bool float string',
|
||||
)
|
||||
|
||||
|
||||
@@ -51,12 +51,12 @@ class BoolStrategy(Strategy):
|
||||
"""Convert a string representation of truth to it's numeric form."""
|
||||
|
||||
val = val.lower()
|
||||
if val in ("y", "yes", "t", "true", "on", "1"):
|
||||
if val in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||
return 1
|
||||
elif val in ("n", "no", "f", "false", "off", "0"):
|
||||
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
|
||||
return 0
|
||||
else:
|
||||
raise ValueError("invalid truth value %r" % (val,))
|
||||
raise ValueError('invalid truth value %r' % (val,))
|
||||
|
||||
|
||||
class FloatStrategy(Strategy):
|
||||
@@ -66,7 +66,7 @@ class FloatStrategy(Strategy):
|
||||
|
||||
class StringStrategy(Strategy):
|
||||
def run(self):
|
||||
setattr(self.target, self.param, " ".join(self.val))
|
||||
setattr(self.target, self.param, ' '.join(self.val))
|
||||
|
||||
|
||||
class Context:
|
||||
@@ -86,16 +86,16 @@ class Context:
|
||||
|
||||
|
||||
class Parser:
|
||||
IS_STRING = ("label",)
|
||||
IS_STRING = ('label',)
|
||||
|
||||
def __init__(self, vm):
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
self.vm = vm
|
||||
self.kls = Group(OneOrMore(Word(alphanums)))
|
||||
self.token = Suppress("->")
|
||||
self.token = Suppress('->')
|
||||
self.param = Group(OneOrMore(Word(alphanums)))
|
||||
self.value = Combine(
|
||||
Optional("-") + Word(nums) + Optional(".") + Optional(Word(nums))
|
||||
Optional('-') + Word(nums) + Optional('.') + Optional(Word(nums))
|
||||
) | Group(OneOrMore(Word(alphanums)))
|
||||
self.event = (
|
||||
self.kls
|
||||
@@ -110,7 +110,7 @@ class Parser:
|
||||
|
||||
res = list()
|
||||
for cmd in cmds:
|
||||
self.logger.debug(f"running command: {cmd}")
|
||||
self.logger.debug(f'running command: {cmd}')
|
||||
match cmd_parsed := self.event.parseString(cmd):
|
||||
case [[kls, index], [param]]:
|
||||
target = getattr(self.vm, kls)[int(index)]
|
||||
@@ -125,13 +125,14 @@ class Parser:
|
||||
context = self._get_context(ParamKinds.bool, target, param, val)
|
||||
context.run()
|
||||
except ValueError as e:
|
||||
self.logger.error(f"{e}... switching to float strategy")
|
||||
self.logger.error(f'{e}... switching to float strategy')
|
||||
context.strategy = FloatStrategy(target, param, val)
|
||||
context.run()
|
||||
case [
|
||||
[kls, index],
|
||||
[secondary, param],
|
||||
[val] | val,
|
||||
[val]
|
||||
| val,
|
||||
]:
|
||||
primary = getattr(self.vm, kls)[int(index)]
|
||||
target = getattr(primary, secondary)
|
||||
@@ -139,12 +140,12 @@ class Parser:
|
||||
context = self._get_context(ParamKinds.bool, target, param, val)
|
||||
context.run()
|
||||
except ValueError as e:
|
||||
self.logger.error(f"{e}... switching to float strategy")
|
||||
self.logger.error(f'{e}... switching to float strategy')
|
||||
context.strategy = FloatStrategy(target, param, val)
|
||||
context.run()
|
||||
case _:
|
||||
self.logger.error(
|
||||
f"unable to determine the kind of parameter from {cmd_parsed}"
|
||||
f'unable to determine the kind of parameter from {cmd_parsed}'
|
||||
)
|
||||
time.sleep(0.05)
|
||||
return res
|
||||
@@ -165,7 +166,7 @@ class Parser:
|
||||
|
||||
|
||||
def interactive_mode(parser):
|
||||
while cmd := input("Please enter command (Press <Enter> to exit)\n"):
|
||||
while cmd := input('Please enter command (Press <Enter> to exit)\n'):
|
||||
if res := parser.parse((cmd,)):
|
||||
print(res)
|
||||
|
||||
@@ -183,7 +184,7 @@ def main():
|
||||
)
|
||||
# fmt: on
|
||||
|
||||
with voicemeeterlib.api("potato") as vm:
|
||||
with voicemeeterlib.api('potato') as vm:
|
||||
parser = Parser(vm)
|
||||
if args.i:
|
||||
interactive_mode(parser)
|
||||
@@ -193,5 +194,5 @@ def main():
|
||||
print(res)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import logging
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
import voicemeeterlib
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
import tkinter as tk
|
||||
from tkinter import ttk
|
||||
|
||||
|
||||
class App(tk.Tk):
|
||||
@@ -12,9 +12,9 @@ class App(tk.Tk):
|
||||
|
||||
def __init__(self, vm):
|
||||
super().__init__()
|
||||
self.vm = vm
|
||||
self.title(f"{vm} - version {vm.version}")
|
||||
self.vm.observer.add(self.on_ldirty)
|
||||
self._vm = vm
|
||||
self.title(f'{vm} - version {vm.version}')
|
||||
self._vm.observer.add(self.on_ldirty)
|
||||
|
||||
# create widget variables
|
||||
self.button_var = tk.BooleanVar(value=vm.strip[self.INDEX].mute)
|
||||
@@ -24,14 +24,14 @@ class App(tk.Tk):
|
||||
|
||||
# initialize style table
|
||||
self.style = ttk.Style()
|
||||
self.style.theme_use("clam")
|
||||
self.style.theme_use('clam')
|
||||
self.style.configure(
|
||||
"Mute.TButton",
|
||||
foreground="#cd5c5c" if vm.strip[self.INDEX].mute else "#5a5a5a",
|
||||
'Mute.TButton',
|
||||
foreground='#cd5c5c' if vm.strip[self.INDEX].mute else '#5a5a5a',
|
||||
)
|
||||
|
||||
# create labelframe and grid it onto the mainframe
|
||||
self.labelframe = tk.LabelFrame(self, text=self.vm.strip[self.INDEX].label)
|
||||
self.labelframe = tk.LabelFrame(self, text=self._vm.strip[self.INDEX].label)
|
||||
self.labelframe.grid(padx=1)
|
||||
|
||||
# create slider and grid it onto the labelframe
|
||||
@@ -39,7 +39,7 @@ class App(tk.Tk):
|
||||
self.labelframe,
|
||||
from_=12,
|
||||
to_=-60,
|
||||
orient="vertical",
|
||||
orient='vertical',
|
||||
variable=self.slider_var,
|
||||
command=lambda arg: self.on_slider_move(arg),
|
||||
)
|
||||
@@ -47,15 +47,15 @@ class App(tk.Tk):
|
||||
column=0,
|
||||
row=0,
|
||||
)
|
||||
slider.bind("<Double-Button-1>", self.on_button_double_click)
|
||||
slider.bind('<Double-Button-1>', self.on_button_double_click)
|
||||
|
||||
# create level meter and grid it onto the labelframe
|
||||
level_meter = ttk.Progressbar(
|
||||
self.labelframe,
|
||||
orient="vertical",
|
||||
orient='vertical',
|
||||
variable=self.meter_var,
|
||||
maximum=72,
|
||||
mode="determinate",
|
||||
mode='determinate',
|
||||
)
|
||||
level_meter.grid(column=1, row=0)
|
||||
|
||||
@@ -66,8 +66,8 @@ class App(tk.Tk):
|
||||
# create button and grid it onto the labelframe
|
||||
button = ttk.Button(
|
||||
self.labelframe,
|
||||
text="Mute",
|
||||
style="Mute.TButton",
|
||||
text='Mute',
|
||||
style='Mute.TButton',
|
||||
command=lambda: self.on_button_press(),
|
||||
)
|
||||
button.grid(column=0, row=2, columnspan=2, padx=1, pady=2)
|
||||
@@ -76,23 +76,23 @@ class App(tk.Tk):
|
||||
|
||||
def on_slider_move(self, *args):
|
||||
val = round(self.slider_var.get(), 1)
|
||||
self.vm.strip[self.INDEX].gain = val
|
||||
self._vm.strip[self.INDEX].gain = val
|
||||
self.gainlabel_var.set(val)
|
||||
|
||||
def on_button_press(self):
|
||||
self.button_var.set(not self.button_var.get())
|
||||
self.vm.strip[self.INDEX].mute = self.button_var.get()
|
||||
self._vm.strip[self.INDEX].mute = self.button_var.get()
|
||||
self.style.configure(
|
||||
"Mute.TButton", foreground="#cd5c5c" if self.button_var.get() else "#5a5a5a"
|
||||
'Mute.TButton', foreground='#cd5c5c' if self.button_var.get() else '#5a5a5a'
|
||||
)
|
||||
|
||||
def on_button_double_click(self, e):
|
||||
self.slider_var.set(0)
|
||||
self.gainlabel_var.set(0)
|
||||
self.vm.strip[self.INDEX].gain = 0
|
||||
self._vm.strip[self.INDEX].gain = 0
|
||||
|
||||
def _get_level(self):
|
||||
val = max(self.vm.strip[self.INDEX].levels.postfader)
|
||||
val = max(self._vm.strip[self.INDEX].levels.postfader)
|
||||
return 0 if self.button_var.get() else 72 + val - 12
|
||||
|
||||
def on_ldirty(self):
|
||||
@@ -100,10 +100,10 @@ class App(tk.Tk):
|
||||
|
||||
|
||||
def main():
|
||||
with voicemeeterlib.api("banana", ldirty=True) as vm:
|
||||
with voicemeeterlib.api('banana', ldirty=True) as vm:
|
||||
app = App(vm)
|
||||
app.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -7,16 +7,16 @@ logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "potato"
|
||||
KIND_ID = 'potato'
|
||||
|
||||
vm = voicemeeterlib.api(KIND_ID)
|
||||
vm.login()
|
||||
for _ in range(500):
|
||||
print(
|
||||
"\n".join(
|
||||
'\n'.join(
|
||||
[
|
||||
f"{vm.strip[5]}: {vm.strip[5].levels.postmute}",
|
||||
f"{vm.bus[0]}: {vm.bus[0].levels.all}",
|
||||
f'{vm.strip[5]}: {vm.strip[5].levels.postmute}',
|
||||
f'{vm.bus[0]}: {vm.bus[0].levels.all}',
|
||||
]
|
||||
)
|
||||
)
|
||||
@@ -24,5 +24,5 @@ def main():
|
||||
vm.logout()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -10,42 +10,42 @@ class App:
|
||||
MACROBUTTON = 0
|
||||
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
self.vm.observer.add(self.on_midi)
|
||||
self._vm = vm
|
||||
self._vm.observer.add(self.on_midi)
|
||||
|
||||
def on_midi(self):
|
||||
if self.get_info() == self.MIDI_BUTTON:
|
||||
self.on_midi_press()
|
||||
|
||||
def get_info(self):
|
||||
current = self.vm.midi.current
|
||||
print(f"Value of midi button {current} is {self.vm.midi.get(current)}")
|
||||
current = self._vm.midi.current
|
||||
print(f'Value of midi button {current} is {self._vm.midi.get(current)}')
|
||||
return current
|
||||
|
||||
def on_midi_press(self):
|
||||
"""if midi button 48 is pressed and strip 3 level max > -40, then set trigger for macrobutton 0"""
|
||||
|
||||
if (
|
||||
self.vm.midi.get(self.MIDI_BUTTON) == 127
|
||||
and max(self.vm.strip[3].levels.postfader) > -40
|
||||
self._vm.midi.get(self.MIDI_BUTTON) == 127
|
||||
and max(self._vm.strip[3].levels.postfader) > -40
|
||||
):
|
||||
print(
|
||||
f"Strip 3 level max is greater than -40 and midi button {self.MIDI_BUTTON} is pressed"
|
||||
f'Strip 3 level max is greater than -40 and midi button {self.MIDI_BUTTON} is pressed'
|
||||
)
|
||||
self.vm.button[self.MACROBUTTON].trigger = True
|
||||
self._vm.button[self.MACROBUTTON].trigger = True
|
||||
else:
|
||||
self.vm.button[self.MACROBUTTON].trigger = False
|
||||
self._vm.button[self.MACROBUTTON].trigger = False
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "banana"
|
||||
KIND_ID = 'banana'
|
||||
|
||||
with voicemeeterlib.api(KIND_ID, midi=True) as vm:
|
||||
App(vm)
|
||||
|
||||
while cmd := input("Press <Enter> to exit\n"):
|
||||
while _ := input('Press <Enter> to exit\n'):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import time
|
||||
import threading
|
||||
from logging import config
|
||||
|
||||
import obsws_python as obsws
|
||||
@@ -7,91 +7,100 @@ import voicemeeterlib
|
||||
|
||||
config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"formatters": {
|
||||
"standard": {
|
||||
"format": "%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s"
|
||||
'version': 1,
|
||||
'formatters': {
|
||||
'standard': {
|
||||
'format': '%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s'
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"stream": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "standard",
|
||||
'handlers': {
|
||||
'stream': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'standard',
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"voicemeeterlib.iremote": {"handlers": ["stream"], "level": "DEBUG"}
|
||||
'loggers': {
|
||||
'voicemeeterlib.iremote': {
|
||||
'handlers': ['stream'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': False,
|
||||
}
|
||||
},
|
||||
'root': {'handlers': ['stream'], 'level': 'WARNING'},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class MyClient:
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
self.client = obsws.EventClient()
|
||||
self.client.callback.register(
|
||||
def __init__(self, vm, stop_event):
|
||||
self._vm = vm
|
||||
self._stop_event = stop_event
|
||||
self._client = obsws.EventClient()
|
||||
self._client.callback.register(
|
||||
(
|
||||
self.on_current_program_scene_changed,
|
||||
self.on_exit_started,
|
||||
)
|
||||
)
|
||||
self.is_running = True
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_traceback):
|
||||
self._client.disconnect()
|
||||
|
||||
def on_start(self):
|
||||
self.vm.strip[0].mute = True
|
||||
self.vm.strip[1].B1 = True
|
||||
self.vm.strip[2].B2 = True
|
||||
self._vm.strip[0].mute = True
|
||||
self._vm.strip[1].B1 = True
|
||||
self._vm.strip[2].B2 = True
|
||||
|
||||
def on_brb(self):
|
||||
self.vm.strip[7].fadeto(0, 500)
|
||||
self.vm.bus[0].mute = True
|
||||
self._vm.strip[7].fadeto(0, 500)
|
||||
self._vm.bus[0].mute = True
|
||||
|
||||
def on_end(self):
|
||||
self.vm.apply(
|
||||
self._vm.apply(
|
||||
{
|
||||
"strip-0": {"mute": True, "comp": {"ratio": 4.3}},
|
||||
"strip-1": {"mute": True, "B1": False, "gate": {"attack": 2.3}},
|
||||
"strip-2": {"mute": True, "B1": False},
|
||||
"vban-in-0": {"on": False},
|
||||
'strip-0': {'mute': True, 'comp': {'ratio': 4.3}},
|
||||
'strip-1': {'mute': True, 'B1': False, 'gate': {'attack': 2.3}},
|
||||
'strip-2': {'mute': True, 'B1': False},
|
||||
'vban-in-0': {'on': False},
|
||||
}
|
||||
)
|
||||
|
||||
def on_live(self):
|
||||
self.vm.strip[0].mute = False
|
||||
self.vm.strip[7].fadeto(-6, 500)
|
||||
self.vm.strip[7].A3 = True
|
||||
self.vm.vban.instream[0].on = True
|
||||
self._vm.strip[0].mute = False
|
||||
self._vm.strip[7].fadeto(-6, 500)
|
||||
self._vm.strip[7].A3 = True
|
||||
self._vm.vban.instream[0].on = True
|
||||
|
||||
def on_current_program_scene_changed(self, data):
|
||||
def fget(scene):
|
||||
run = {
|
||||
"START": self.on_start,
|
||||
"BRB": self.on_brb,
|
||||
"END": self.on_end,
|
||||
"LIVE": self.on_live,
|
||||
}
|
||||
return run.get(scene)
|
||||
|
||||
scene = data.scene_name
|
||||
print(f"Switched to scene {scene}")
|
||||
if fn := fget(scene):
|
||||
fn()
|
||||
print(f'Switched to scene {scene}')
|
||||
match scene:
|
||||
case 'START':
|
||||
self.on_start()
|
||||
case 'BRB':
|
||||
self.on_brb()
|
||||
case 'END':
|
||||
self.on_end()
|
||||
case 'LIVE':
|
||||
self.on_live()
|
||||
|
||||
def on_exit_started(self, _):
|
||||
self.client.unsubscribe()
|
||||
self.is_running = False
|
||||
self._stop_event.set()
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "potato"
|
||||
KIND_ID = 'potato'
|
||||
|
||||
with voicemeeterlib.api(KIND_ID) as vm:
|
||||
client = MyClient(vm)
|
||||
while client.is_running:
|
||||
time.sleep(0.1)
|
||||
stop_event = threading.Event()
|
||||
|
||||
with MyClient(vm, stop_event):
|
||||
stop_event.wait()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -7,39 +7,39 @@ logging.basicConfig(level=logging.INFO)
|
||||
|
||||
class App:
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
self._vm = vm
|
||||
# register your app as event observer
|
||||
self.vm.observer.add(self)
|
||||
self._vm.observer.add(self)
|
||||
|
||||
def __str__(self):
|
||||
return type(self).__name__
|
||||
|
||||
# define an 'on_update' callback function to receive event updates
|
||||
def on_update(self, event):
|
||||
if event == "pdirty":
|
||||
print("pdirty!")
|
||||
elif event == "mdirty":
|
||||
print("mdirty!")
|
||||
elif event == "ldirty":
|
||||
for bus in self.vm.bus:
|
||||
if event == 'pdirty':
|
||||
print('pdirty!')
|
||||
elif event == 'mdirty':
|
||||
print('mdirty!')
|
||||
elif event == 'ldirty':
|
||||
for bus in self._vm.bus:
|
||||
if bus.levels.isdirty:
|
||||
print(bus, bus.levels.all)
|
||||
elif event == "midi":
|
||||
current = self.vm.midi.current
|
||||
print(f"Value of midi button {current} is {self.vm.midi.get(current)}")
|
||||
elif event == 'midi':
|
||||
current = self._vm.midi.current
|
||||
print(f'Value of midi button {current} is {self._vm.midi.get(current)}')
|
||||
|
||||
|
||||
def main():
|
||||
KIND_ID = "banana"
|
||||
KIND_ID = 'banana'
|
||||
|
||||
with voicemeeterlib.api(
|
||||
KIND_ID, **{k: True for k in ("pdirty", "mdirty", "ldirty", "midi")}
|
||||
KIND_ID, **{k: True for k in ('pdirty', 'mdirty', 'ldirty', 'midi')}
|
||||
) as vm:
|
||||
App(vm)
|
||||
|
||||
while cmd := input("Press <Enter> to exit\n"):
|
||||
while _ := input('Press <Enter> to exit\n'):
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
403
poetry.lock
generated
403
poetry.lock
generated
@@ -1,93 +1,36 @@
|
||||
# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "24.3.0"
|
||||
description = "The uncompromising code formatter."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"},
|
||||
{file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"},
|
||||
{file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"},
|
||||
{file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"},
|
||||
{file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"},
|
||||
{file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"},
|
||||
{file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"},
|
||||
{file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"},
|
||||
{file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"},
|
||||
{file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"},
|
||||
{file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"},
|
||||
{file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"},
|
||||
{file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"},
|
||||
{file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"},
|
||||
{file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"},
|
||||
{file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"},
|
||||
{file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"},
|
||||
{file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
packaging = ">=22.0"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
colorama = ["colorama (>=0.4.3)"]
|
||||
d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"]
|
||||
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
|
||||
uvloop = ["uvloop (>=0.15.2)"]
|
||||
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "cachetools"
|
||||
version = "5.3.1"
|
||||
version = "5.5.0"
|
||||
description = "Extensible memoizing collections and decorators"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"},
|
||||
{file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"},
|
||||
{file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"},
|
||||
{file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "chardet"
|
||||
version = "5.1.0"
|
||||
version = "5.2.0"
|
||||
description = "Universal encoding detector for Python 3"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"},
|
||||
{file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"},
|
||||
{file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"},
|
||||
{file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.1.3"
|
||||
description = "Composable command line interface toolkit"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
|
||||
{file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
description = "Cross-platform colored terminal text."
|
||||
optional = false
|
||||
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"},
|
||||
@@ -95,24 +38,27 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "distlib"
|
||||
version = "0.3.6"
|
||||
version = "0.3.9"
|
||||
description = "Distribution utilities"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"},
|
||||
{file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"},
|
||||
{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.1.1"
|
||||
version = "1.2.2"
|
||||
description = "Backport of PEP 654 (exception groups)"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["dev"]
|
||||
markers = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"},
|
||||
{file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"},
|
||||
{file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"},
|
||||
{file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@@ -120,18 +66,20 @@ test = ["pytest (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.12.2"
|
||||
version = "3.16.1"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"},
|
||||
{file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"},
|
||||
{file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
|
||||
{file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
|
||||
typing = ["typing-extensions (>=4.12.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
@@ -139,119 +87,99 @@ version = "2.0.0"
|
||||
description = "brain-dead simple config-ini parsing"
|
||||
optional = false
|
||||
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 = "isort"
|
||||
version = "5.12.0"
|
||||
description = "A Python utility / library to sort Python imports."
|
||||
optional = false
|
||||
python-versions = ">=3.8.0"
|
||||
files = [
|
||||
{file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"},
|
||||
{file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
colors = ["colorama (>=0.4.3)"]
|
||||
pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"]
|
||||
plugins = ["setuptools"]
|
||||
requirements-deprecated-finder = ["pip-api", "pipreqs"]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
description = "Type system extensions for programs checked with the mypy type checker."
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.1"
|
||||
version = "24.2"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
|
||||
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.11.1"
|
||||
description = "Utility library for gitignore style pattern matching of file paths."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"},
|
||||
{file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"},
|
||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.6.0"
|
||||
description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
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-3.6.0-py3-none-any.whl", hash = "sha256:ffa199e3fbab8365778c4a10e1fbf1b9cd50707de826eb304b50e57ec0cc8d38"},
|
||||
{file = "platformdirs-3.6.0.tar.gz", hash = "sha256:57e28820ca8094678b807ff529196506d7a21e17156cb1cddb3e74cebce54640"},
|
||||
{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 (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"]
|
||||
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.1.0"
|
||||
version = "1.5.0"
|
||||
description = "plugin and hook calling mechanisms for python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pluggy-1.1.0-py3-none-any.whl", hash = "sha256:d81d19a3a88d82ed06998353ce5d5c02587ef07ee2d808ae63904ab0ccef0087"},
|
||||
{file = "pluggy-1.1.0.tar.gz", hash = "sha256:c500b592c5512df35622e4faf2135aa0b7e989c7d31344194b4afb9d5e47b1bf"},
|
||||
{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 = "pyenv-inspect"
|
||||
version = "0.4.0"
|
||||
description = "An auxiliary library for the virtualenv-pyenv and tox-pyenv-redux plugins"
|
||||
optional = false
|
||||
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 = "pyproject-api"
|
||||
version = "1.5.2"
|
||||
version = "1.8.0"
|
||||
description = "API to interact with the python pyproject.toml based projects"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pyproject_api-1.5.2-py3-none-any.whl", hash = "sha256:9cffcbfb64190f207444d7579d315f3278f2c04ba46d685fad93197b5326d348"},
|
||||
{file = "pyproject_api-1.5.2.tar.gz", hash = "sha256:999f58fa3c92b23ebd31a6bad5d1f87d456744d75e05391be7f5c729015d3d91"},
|
||||
{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 = ">=23.1"
|
||||
packaging = ">=24.1"
|
||||
tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"]
|
||||
testing = ["covdefaults (>=2.3)", "importlib-metadata (>=6.6)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "setuptools (>=67.8)", "wheel (>=0.40)"]
|
||||
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.4.4"
|
||||
version = "8.3.4"
|
||||
description = "pytest: simple powerful testing with Python"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"},
|
||||
{file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"},
|
||||
{file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"},
|
||||
{file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -259,136 +187,177 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
|
||||
exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""}
|
||||
iniconfig = "*"
|
||||
packaging = "*"
|
||||
pluggy = ">=0.12,<2.0"
|
||||
tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""}
|
||||
pluggy = ">=1.5,<2"
|
||||
tomli = {version = ">=1", markers = "python_version < \"3.11\""}
|
||||
|
||||
[package.extras]
|
||||
testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "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."
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.9"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "pytest-randomly-3.12.0.tar.gz", hash = "sha256:d60c2db71ac319aee0fc6c4110a7597d611a8b94a5590918bfa8583f00caccb2"},
|
||||
{file = "pytest_randomly-3.12.0-py3-none-any.whl", hash = "sha256:f4f2e803daf5d1ba036cc22bf4fe9dbbf99389ec56b00e5cba732fb5c1d07fdd"},
|
||||
{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 = "pytest-repeat"
|
||||
version = "0.9.1"
|
||||
description = "pytest plugin for repeating tests"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
files = [
|
||||
{file = "pytest-repeat-0.9.1.tar.gz", hash = "sha256:5cd3289745ab3156d43eb9c8e7f7d00a926f3ae5c9cf425bec649b2fe15bad5b"},
|
||||
{file = "pytest_repeat-0.9.1-py2.py3-none-any.whl", hash = "sha256:4474a7d9e9137f6d8cc8ae297f8c4168d33c56dd740aa78cfffe562557e6b96e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.1.3"
|
||||
description = "An extremely fast Python linter, written in Rust."
|
||||
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.1.3-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b46d43d51f7061652eeadb426a9e3caa1e0002470229ab2fc19de8a7b0766901"},
|
||||
{file = "ruff-0.1.3-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:b8afeb9abd26b4029c72adc9921b8363374f4e7edb78385ffaa80278313a15f9"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca3cf365bf32e9ba7e6db3f48a4d3e2c446cd19ebee04f05338bc3910114528b"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4874c165f96c14a00590dcc727a04dca0cfd110334c24b039458c06cf78a672e"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eec2dd31eed114e48ea42dbffc443e9b7221976554a504767ceaee3dd38edeb8"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dc3ec4edb3b73f21b4aa51337e16674c752f1d76a4a543af56d7d04e97769613"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2e3de9ed2e39160800281848ff4670e1698037ca039bda7b9274f849258d26ce"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c595193881922cc0556a90f3af99b1c5681f0c552e7a2a189956141d8666fe8"},
|
||||
{file = "ruff-0.1.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f75e670d529aa2288cd00fc0e9b9287603d95e1536d7a7e0cafe00f75e0dd9d"},
|
||||
{file = "ruff-0.1.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:76dd49f6cd945d82d9d4a9a6622c54a994689d8d7b22fa1322983389b4892e20"},
|
||||
{file = "ruff-0.1.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:918b454bc4f8874a616f0d725590277c42949431ceb303950e87fef7a7d94cb3"},
|
||||
{file = "ruff-0.1.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8859605e729cd5e53aa38275568dbbdb4fe882d2ea2714c5453b678dca83784"},
|
||||
{file = "ruff-0.1.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:0b6c55f5ef8d9dd05b230bb6ab80bc4381ecb60ae56db0330f660ea240cb0d4a"},
|
||||
{file = "ruff-0.1.3-py3-none-win32.whl", hash = "sha256:3e7afcbdcfbe3399c34e0f6370c30f6e529193c731b885316c5a09c9e4317eef"},
|
||||
{file = "ruff-0.1.3-py3-none-win_amd64.whl", hash = "sha256:7a18df6638cec4a5bd75350639b2bb2a2366e01222825562c7346674bdceb7ea"},
|
||||
{file = "ruff-0.1.3-py3-none-win_arm64.whl", hash = "sha256:12fd53696c83a194a2db7f9a46337ce06445fb9aa7d25ea6f293cf75b21aca9f"},
|
||||
{file = "ruff-0.1.3.tar.gz", hash = "sha256:3ba6145369a151401d5db79f0a47d50e470384d0d89d0d6f7fab0b589ad07c34"},
|
||||
{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"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main", "dev"]
|
||||
markers = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
{file = "tomli-2.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.6.3"
|
||||
version = "4.23.2"
|
||||
description = "tox is a generic virtualenv management and test command line tool"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "tox-4.6.3-py3-none-any.whl", hash = "sha256:2946a0bb38924c3a9f9575c7fb4ca1f4c11a7c69c61592f176778892155cb50c"},
|
||||
{file = "tox-4.6.3.tar.gz", hash = "sha256:9e2c5091a117d03b583c57c4c40aecd068099c17d40520e7b165e85c19334534"},
|
||||
{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.3.1"
|
||||
chardet = ">=5.1"
|
||||
cachetools = ">=5.5"
|
||||
chardet = ">=5.2"
|
||||
colorama = ">=0.4.6"
|
||||
filelock = ">=3.12.2"
|
||||
packaging = ">=23.1"
|
||||
platformdirs = ">=3.5.3"
|
||||
pluggy = ">=1"
|
||||
pyproject-api = ">=1.5.2"
|
||||
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\""}
|
||||
virtualenv = ">=20.23.1"
|
||||
typing-extensions = {version = ">=4.12.2", markers = "python_version < \"3.11\""}
|
||||
virtualenv = ">=20.26.6"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-argparse-cli (>=1.11.1)", "sphinx-autodoc-typehints (>=1.23.2,!=1.23.4)", "sphinx-copybutton (>=0.5.2)", "sphinx-inline-tabs (>=2023.4.21)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
|
||||
testing = ["build[virtualenv] (>=0.10)", "covdefaults (>=2.3)", "detect-test-pollution (>=1.1.1)", "devpi-process (>=0.3.1)", "diff-cover (>=7.6)", "distlib (>=0.3.6)", "flaky (>=3.7)", "hatch-vcs (>=0.3)", "hatchling (>=1.17.1)", "psutil (>=5.9.5)", "pytest (>=7.3.2)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-xdist (>=3.3.1)", "re-assert (>=1.1)", "time-machine (>=2.10)", "wheel (>=0.40)"]
|
||||
test = ["devpi-process (>=1.0.2)", "pytest (>=8.3.3)", "pytest-mock (>=3.14)"]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.10.0"
|
||||
version = "4.12.2"
|
||||
description = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
markers = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
|
||||
{file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
|
||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.23.1"
|
||||
version = "20.28.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
python-versions = ">=3.8"
|
||||
groups = ["dev"]
|
||||
files = [
|
||||
{file = "virtualenv-20.23.1-py3-none-any.whl", hash = "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419"},
|
||||
{file = "virtualenv-20.23.1.tar.gz", hash = "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1"},
|
||||
{file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"},
|
||||
{file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
distlib = ">=0.3.6,<1"
|
||||
filelock = ">=3.12,<4"
|
||||
platformdirs = ">=3.5.1,<4"
|
||||
distlib = ">=0.3.7,<1"
|
||||
filelock = ">=3.12.2,<4"
|
||||
platformdirs = ">=3.9.1,<5"
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "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.3.1)", "pytest-env (>=0.8.1)", "pytest-freezer (>=0.4.6)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=67.8)", "time-machine (>=2.9)"]
|
||||
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)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"]
|
||||
|
||||
[[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 = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "d4193db5ebc240288a21da72fa78788dfb71c08b6ecd25e365ce7fb1b0f27ac4"
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10"
|
||||
content-hash = "6339967c3f6cad8e4db7047ef3d12a5b059a279d0f7c98515c961477680bab8f"
|
||||
|
||||
182
pyproject.toml
182
pyproject.toml
@@ -1,114 +1,50 @@
|
||||
[tool.poetry]
|
||||
[project]
|
||||
name = "voicemeeter-api"
|
||||
version = "2.6.0"
|
||||
version = "2.6.1"
|
||||
description = "A Python wrapper for the Voiceemeter API"
|
||||
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/voicemeeter-api-python"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"tomli (>=2.0.1,<3.0) ; python_version < '3.11'",
|
||||
]
|
||||
|
||||
[tool.poetry]
|
||||
packages = [{ include = "voicemeeterlib" }]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
tomli = { version = "^2.0.1", python = "<3.11" }
|
||||
[tool.poetry.requires-plugins]
|
||||
poethepoet = "^0.32.1"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
pytest = "^7.4.4"
|
||||
pytest-randomly = "^3.12.0"
|
||||
pytest-repeat = "^0.9.1"
|
||||
black = ">=22.3,<25.0"
|
||||
isort = "^5.10.1"
|
||||
tox = "^4.6.3"
|
||||
ruff = "^0.1.3"
|
||||
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]
|
||||
dsl = "scripts:ex_dsl"
|
||||
events = "scripts:ex_events"
|
||||
gui = "scripts:ex_gui"
|
||||
levels = "scripts:ex_levels"
|
||||
midi = "scripts:ex_midi"
|
||||
obs = "scripts:ex_obs"
|
||||
observer = "scripts:ex_observer"
|
||||
basic = "scripts:test_basic"
|
||||
banana = "scripts:test_banana"
|
||||
potato = "scripts:test_potato"
|
||||
all = "scripts:test_all"
|
||||
[tool.poe.tasks]
|
||||
dsl.script = "scripts:ex_dsl"
|
||||
callbacks.script = "scripts:ex_callbacks"
|
||||
gui.script = "scripts:ex_gui"
|
||||
levels.script = "scripts:ex_levels"
|
||||
midi.script = "scripts:ex_midi"
|
||||
obs.script = "scripts:ex_obs"
|
||||
observer.script = "scripts:ex_observer"
|
||||
test-basic.script = "scripts:test_basic"
|
||||
test-banana.script = "scripts:test_banana"
|
||||
test-potato.script = "scripts:test_potato"
|
||||
test-all.script = "scripts:test_all"
|
||||
generate-badges.script = "scripts:generate_badges"
|
||||
|
||||
[tool.tox]
|
||||
legacy_tox_ini = """
|
||||
[tox]
|
||||
envlist = py310,py311,py312
|
||||
|
||||
[testenv]
|
||||
allowlist_externals = poetry
|
||||
commands =
|
||||
poetry install -v
|
||||
poetry run pytest tests/
|
||||
"""
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E",
|
||||
"F",
|
||||
]
|
||||
ignore = [
|
||||
"E501",
|
||||
]
|
||||
fixable = [
|
||||
"A",
|
||||
"B",
|
||||
"C",
|
||||
"D",
|
||||
"E",
|
||||
"F",
|
||||
"G",
|
||||
"I",
|
||||
"N",
|
||||
"Q",
|
||||
"S",
|
||||
"T",
|
||||
"W",
|
||||
"ANN",
|
||||
"ARG",
|
||||
"BLE",
|
||||
"COM",
|
||||
"DJ",
|
||||
"DTZ",
|
||||
"EM",
|
||||
"ERA",
|
||||
"EXE",
|
||||
"FBT",
|
||||
"ICN",
|
||||
"INP",
|
||||
"ISC",
|
||||
"NPY",
|
||||
"PD",
|
||||
"PGH",
|
||||
"PIE",
|
||||
"PL",
|
||||
"PT",
|
||||
"PTH",
|
||||
"PYI",
|
||||
"RET",
|
||||
"RSE",
|
||||
"RUF",
|
||||
"SIM",
|
||||
"SLF",
|
||||
"TCH",
|
||||
"TID",
|
||||
"TRY",
|
||||
"UP",
|
||||
"YTT",
|
||||
]
|
||||
unfixable = []
|
||||
exclude = [
|
||||
".bzr",
|
||||
".direnv",
|
||||
@@ -132,14 +68,62 @@ exclude = [
|
||||
"node_modules",
|
||||
"venv",
|
||||
]
|
||||
|
||||
# Same as Black.
|
||||
line-length = 88
|
||||
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||
indent-width = 4
|
||||
|
||||
# Assume Python 3.10
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.mccabe]
|
||||
[tool.ruff.lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
# Enable flake8-errmsg (EM) warnings.
|
||||
# Enable flake8-bugbear (B) warnings.
|
||||
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
||||
# McCabe complexity (`C901`) by default.
|
||||
select = ["E4", "E7", "E9", "EM", "F", "B"]
|
||||
ignore = []
|
||||
|
||||
# Allow fix for all enabled rules (when `--fix`) is provided.
|
||||
fixable = ["ALL"]
|
||||
unfixable = ["B"]
|
||||
|
||||
# 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.per-file-ignores]
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = [
|
||||
"E402",
|
||||
"F401",
|
||||
|
||||
37
scripts.py
37
scripts.py
@@ -5,55 +5,58 @@ from pathlib import Path
|
||||
|
||||
|
||||
def ex_dsl():
|
||||
scriptpath = Path.cwd() / "examples" / "dsl" / "."
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
subprocess.run(['tox', 'r', '-e', 'dsl'])
|
||||
|
||||
|
||||
def ex_events():
|
||||
scriptpath = Path.cwd() / "examples" / "events" / "."
|
||||
def ex_callbacks():
|
||||
scriptpath = Path.cwd() / 'examples' / 'events' / 'callbacks' / '.'
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
|
||||
|
||||
def ex_gui():
|
||||
scriptpath = Path.cwd() / "examples" / "gui" / "."
|
||||
scriptpath = Path.cwd() / 'examples' / 'gui' / '.'
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
|
||||
|
||||
def ex_levels():
|
||||
scriptpath = Path.cwd() / "examples" / "levels" / "."
|
||||
scriptpath = Path.cwd() / 'examples' / 'levels' / '.'
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
|
||||
|
||||
def ex_midi():
|
||||
scriptpath = Path.cwd() / "examples" / "midi" / "."
|
||||
scriptpath = Path.cwd() / 'examples' / 'midi' / '.'
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
|
||||
|
||||
def ex_obs():
|
||||
scriptpath = Path.cwd() / "examples" / "obs" / "."
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
subprocess.run(['tox', 'r', '-e', 'obs'])
|
||||
|
||||
|
||||
def ex_observer():
|
||||
scriptpath = Path.cwd() / "examples" / "observer" / "."
|
||||
scriptpath = Path.cwd() / 'examples' / 'events' / 'observer' / '.'
|
||||
subprocess.run([sys.executable, str(scriptpath)])
|
||||
|
||||
|
||||
def test_basic():
|
||||
os.environ["KIND"] = "basic"
|
||||
subprocess.run(["tox"])
|
||||
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'basic'})
|
||||
|
||||
|
||||
def test_banana():
|
||||
os.environ["KIND"] = "banana"
|
||||
subprocess.run(["tox"])
|
||||
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'banana'})
|
||||
|
||||
|
||||
def test_potato():
|
||||
os.environ["KIND"] = "potato"
|
||||
subprocess.run(["tox"])
|
||||
subprocess.run(['tox'], env=os.environ.copy() | {'KIND': 'potato'})
|
||||
|
||||
|
||||
def test_all():
|
||||
steps = [test_basic, test_banana, test_potato]
|
||||
[step() for step in steps]
|
||||
for step in steps:
|
||||
step()
|
||||
|
||||
|
||||
def generate_badges():
|
||||
for kind in ['basic', 'banana', 'potato']:
|
||||
subprocess.run(
|
||||
['tox', 'r', '-e', 'genbadges'], env=os.environ.copy() | {'KIND': kind}
|
||||
)
|
||||
|
||||
@@ -31,10 +31,8 @@ class Data:
|
||||
return (2 * self.phys_in) + (8 * self.virt_in)
|
||||
|
||||
|
||||
# get KIND_ID from env var, otherwise set to random
|
||||
KIND_ID = os.environ.get(
|
||||
"KIND", random.choice(tuple(kind_id.name.lower() for kind_id in KindId))
|
||||
)
|
||||
# get KIND from environment, if not set default to potato
|
||||
KIND_ID = os.environ.get('KIND', 'potato')
|
||||
vm = voicemeeterlib.api(KIND_ID)
|
||||
kind = kindmap(KIND_ID)
|
||||
|
||||
@@ -56,7 +54,7 @@ data = Data(
|
||||
|
||||
|
||||
def setup_module():
|
||||
print(f"\nRunning tests for kind [{data.name}]\n", file=sys.stdout)
|
||||
print(f'\nRunning tests for kind [{data.name}]\n', file=sys.stdout)
|
||||
vm.login()
|
||||
vm.command.reset()
|
||||
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
Function RunTests {
|
||||
$coverage = "./tests/pytest_coverage.log"
|
||||
$run_tests = "pytest --run-slow -v --capture=tee-sys --junitxml=./tests/.coverage.xml"
|
||||
$match_pattern = "^=|^\s*$|^Running|^Using|^plugins|^collecting|^tests"
|
||||
|
||||
if ( Test-Path $coverage ) { Clear-Content $coverage }
|
||||
|
||||
ForEach ($line in $(Invoke-Expression $run_tests)) {
|
||||
If ( $line -Match $match_pattern ) {
|
||||
if ( $line -Match "^Running tests for kind \[(\w+)\]" ) { $kind = $Matches[1] }
|
||||
$line | Tee-Object -FilePath $coverage -Append
|
||||
}
|
||||
}
|
||||
Write-Output "$(Get-TimeStamp)" | Out-File $coverage -Append
|
||||
|
||||
Invoke-Expression "genbadge tests -t 90 -i ./tests/.coverage.xml -o ./tests/$kind.svg"
|
||||
}
|
||||
|
||||
Function Get-TimeStamp {
|
||||
|
||||
return "[{0:MM/dd/yy} {0:HH:mm:ss}]" -f (Get-Date)
|
||||
|
||||
}
|
||||
|
||||
if ($MyInvocation.InvocationName -ne ".") {
|
||||
Invoke-Expression ".\.venv\Scripts\Activate.ps1"
|
||||
|
||||
@("potato") | ForEach-Object {
|
||||
$env:KIND = $_
|
||||
RunTests
|
||||
}
|
||||
|
||||
|
||||
Invoke-Expression "deactivate"
|
||||
}
|
||||
319
tests/reports/assets/style.css
Normal file
319
tests/reports/assets/style.css
Normal file
@@ -0,0 +1,319 @@
|
||||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
/* do not increase min-width as some may use split screens */
|
||||
min-width: 800px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* SUMMARY INFORMATION
|
||||
******************************/
|
||||
#environment td {
|
||||
padding: 5px;
|
||||
border: 1px solid #e6e6e6;
|
||||
vertical-align: top;
|
||||
}
|
||||
#environment tr:nth-child(odd) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
#environment ul {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* TEST RESULT COLORS
|
||||
******************************/
|
||||
span.passed,
|
||||
.passed .col-result {
|
||||
color: green;
|
||||
}
|
||||
|
||||
span.skipped,
|
||||
span.xfailed,
|
||||
span.rerun,
|
||||
.skipped .col-result,
|
||||
.xfailed .col-result,
|
||||
.rerun .col-result {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
span.error,
|
||||
span.failed,
|
||||
span.xpassed,
|
||||
.error .col-result,
|
||||
.failed .col-result,
|
||||
.xpassed .col-result {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.col-links__extra {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* RESULTS TABLE
|
||||
*
|
||||
* 1. Table Layout
|
||||
* 2. Extra
|
||||
* 3. Sorting items
|
||||
*
|
||||
******************************/
|
||||
/*------------------
|
||||
* 1. Table Layout
|
||||
*------------------*/
|
||||
#results-table {
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
#results-table th,
|
||||
#results-table td {
|
||||
padding: 5px;
|
||||
border: 1px solid #e6e6e6;
|
||||
text-align: left;
|
||||
}
|
||||
#results-table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 2. Extra
|
||||
*------------------*/
|
||||
.logwrapper {
|
||||
max-height: 230px;
|
||||
overflow-y: scroll;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.logwrapper.expanded {
|
||||
max-height: none;
|
||||
}
|
||||
.logwrapper.expanded .logexpander:after {
|
||||
content: "collapse [-]";
|
||||
}
|
||||
.logwrapper .logexpander {
|
||||
z-index: 1;
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
width: max-content;
|
||||
border: 1px solid;
|
||||
border-radius: 3px;
|
||||
padding: 5px 7px;
|
||||
margin: 10px 0 10px calc(100% - 80px);
|
||||
cursor: pointer;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
.logwrapper .logexpander:after {
|
||||
content: "expand [+]";
|
||||
}
|
||||
.logwrapper .logexpander:hover {
|
||||
color: #000;
|
||||
border-color: #000;
|
||||
}
|
||||
.logwrapper .log {
|
||||
min-height: 40px;
|
||||
position: relative;
|
||||
top: -50px;
|
||||
height: calc(100% + 50px);
|
||||
border: 1px solid #e6e6e6;
|
||||
color: black;
|
||||
display: block;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
padding: 5px;
|
||||
padding-right: 80px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
div.media {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin: 0 5px;
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.media-container {
|
||||
display: grid;
|
||||
grid-template-columns: 25px auto 25px;
|
||||
align-items: center;
|
||||
flex: 1 1;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.media-container--fullscreen {
|
||||
grid-template-columns: 0px auto 0px;
|
||||
}
|
||||
|
||||
.media-container__nav--right,
|
||||
.media-container__nav--left {
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.media-container__viewport {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
height: inherit;
|
||||
}
|
||||
.media-container__viewport img,
|
||||
.media-container__viewport video {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.media__name,
|
||||
.media__counter {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
flex: 0 0 25px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.collapsible td:not(.col-links) {
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapsible td:not(.col-links):hover::after {
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.col-result {
|
||||
width: 130px;
|
||||
}
|
||||
.col-result:hover::after {
|
||||
content: " (hide details)";
|
||||
}
|
||||
|
||||
.col-result.collapsed:hover::after {
|
||||
content: " (show details)";
|
||||
}
|
||||
|
||||
#environment-header h2:hover::after {
|
||||
content: " (hide details)";
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#environment-header.collapsed h2:hover::after {
|
||||
content: " (show details)";
|
||||
color: #bbb;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 3. Sorting items
|
||||
*------------------*/
|
||||
.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.sortable.desc:after {
|
||||
content: " ";
|
||||
position: relative;
|
||||
left: 5px;
|
||||
bottom: -12.5px;
|
||||
border: 10px solid #4caf50;
|
||||
border-bottom: 0;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
.sortable.asc:after {
|
||||
content: " ";
|
||||
position: relative;
|
||||
left: 5px;
|
||||
bottom: 12.5px;
|
||||
border: 10px solid #4caf50;
|
||||
border-top: 0;
|
||||
border-left-color: transparent;
|
||||
border-right-color: transparent;
|
||||
}
|
||||
|
||||
.hidden, .summary__reload__button.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.summary__data {
|
||||
flex: 0 0 550px;
|
||||
}
|
||||
.summary__reload {
|
||||
flex: 1 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.summary__reload__button {
|
||||
flex: 0 0 300px;
|
||||
display: flex;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
background-color: #4caf50;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.summary__reload__button:hover {
|
||||
background-color: #46a049;
|
||||
}
|
||||
.summary__spacer {
|
||||
flex: 0 0 550px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.filters,
|
||||
.collapse {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.filters button,
|
||||
.collapse button {
|
||||
color: #999;
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.filters button:hover,
|
||||
.collapse button:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.filter__label {
|
||||
margin-right: 10px;
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 159"><title>tests: 159</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">159</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">159</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 158"><title>tests: 158</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">158</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">158</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 116"><title>tests: 116</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">116</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">116</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 115"><title>tests: 115</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">115</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">115</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 184"><title>tests: 184</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">184</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">184</text></g></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="68" height="20" role="img" aria-label="tests: 183"><title>tests: 183</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="68" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="37" height="20" fill="#555"/><rect x="37" width="31" height="20" fill="#4c1"/><rect width="68" 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="515" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">183</text><text x="515" y="140" transform="scale(.1)" fill="#fff" textLength="210">183</text></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
770
tests/reports/banana.html
Normal file
770
tests/reports/banana.html
Normal file
File diff suppressed because one or more lines are too long
770
tests/reports/basic.html
Normal file
770
tests/reports/basic.html
Normal file
File diff suppressed because one or more lines are too long
770
tests/reports/potato.html
Normal file
770
tests/reports/potato.html
Normal file
File diff suppressed because one or more lines are too long
42
tox.ini
Normal file
42
tox.ini
Normal file
@@ -0,0 +1,42 @@
|
||||
[tox]
|
||||
envlist = py310,py311,py312,py313
|
||||
|
||||
[testenv]
|
||||
passenv = *
|
||||
setenv = VIRTUALENV_DISCOVERY=pyenv
|
||||
allowlist_externals = poetry
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root
|
||||
commands =
|
||||
poetry run pytest tests
|
||||
|
||||
[testenv:genbadges]
|
||||
passenv = *
|
||||
setenv = VIRTUALENV_DISCOVERY=pyenv
|
||||
allowlist_externals = poetry
|
||||
deps =
|
||||
genbadge[all]
|
||||
pytest-html
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root
|
||||
commands =
|
||||
poetry run pytest --capture=tee-sys --junitxml=./tests/reports/junit-${KIND}.xml --html=./tests/reports/${KIND}.html tests
|
||||
poetry run genbadge tests -t 90 -i ./tests/reports/junit-${KIND}.xml -o ./tests/reports/badge-${KIND}.svg
|
||||
|
||||
[testenv:dsl]
|
||||
setenv = VIRTUALENV_DISCOVERY=pyenv
|
||||
allowlist_externals = poetry
|
||||
deps = pyparsing
|
||||
commands_pre =
|
||||
poetry install --no-interaction --no-root --without dev
|
||||
commands =
|
||||
poetry run python examples/dsl
|
||||
|
||||
[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/obs
|
||||
@@ -1,3 +1,3 @@
|
||||
from .factory import request_remote_obj as api
|
||||
|
||||
__ALL__ = ["api"]
|
||||
__ALL__ = ['api']
|
||||
|
||||
@@ -4,13 +4,13 @@ from enum import IntEnum
|
||||
from math import log
|
||||
from typing import Union
|
||||
|
||||
from . import kinds
|
||||
from .iremote import IRemote
|
||||
from .kinds import kinds_all
|
||||
from .meta import bus_mode_prop, device_prop, float_prop
|
||||
|
||||
BusModes = IntEnum(
|
||||
"BusModes",
|
||||
"normal amix bmix repeat composite tvmix upmix21 upmix41 upmix61 centeronly lfeonly rearonly",
|
||||
'BusModes',
|
||||
'normal amix bmix repeat composite tvmix upmix21 upmix41 upmix61 centeronly lfeonly rearonly',
|
||||
start=0,
|
||||
)
|
||||
|
||||
@@ -28,85 +28,85 @@ class Bus(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"bus[{self.index}]"
|
||||
return f'bus[{self.index}]'
|
||||
|
||||
@property
|
||||
def mute(self) -> bool:
|
||||
return self.getter("mute") == 1
|
||||
return self.getter('mute') == 1
|
||||
|
||||
@mute.setter
|
||||
def mute(self, val: bool):
|
||||
self.setter("mute", 1 if val else 0)
|
||||
self.setter('mute', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def mono(self) -> bool:
|
||||
return self.getter("mono") == 1
|
||||
return self.getter('mono') == 1
|
||||
|
||||
@mono.setter
|
||||
def mono(self, val: bool):
|
||||
self.setter("mono", 1 if val else 0)
|
||||
self.setter('mono', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def sel(self) -> bool:
|
||||
return self.getter("sel") == 1
|
||||
return self.getter('sel') == 1
|
||||
|
||||
@sel.setter
|
||||
def sel(self, val: bool):
|
||||
self.setter("sel", 1 if val else 0)
|
||||
self.setter('sel', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
return self.getter("Label", is_string=True)
|
||||
return self.getter('Label', is_string=True)
|
||||
|
||||
@label.setter
|
||||
def label(self, val: str):
|
||||
self.setter("Label", str(val))
|
||||
self.setter('Label', str(val))
|
||||
|
||||
@property
|
||||
def gain(self) -> float:
|
||||
return round(self.getter("gain"), 1)
|
||||
return round(self.getter('gain'), 1)
|
||||
|
||||
@gain.setter
|
||||
def gain(self, val: float):
|
||||
self.setter("gain", val)
|
||||
self.setter('gain', val)
|
||||
|
||||
@property
|
||||
def monitor(self) -> bool:
|
||||
return self.getter("monitor") == 1
|
||||
return self.getter('monitor') == 1
|
||||
|
||||
@monitor.setter
|
||||
def monitor(self, val: bool):
|
||||
self.setter("monitor", 1 if val else 0)
|
||||
self.setter('monitor', 1 if val else 0)
|
||||
|
||||
def fadeto(self, target: float, time_: int):
|
||||
self.setter("FadeTo", f"({target}, {time_})")
|
||||
self.setter('FadeTo', f'({target}, {time_})')
|
||||
time.sleep(self._remote.DELAY)
|
||||
|
||||
def fadeby(self, change: float, time_: int):
|
||||
self.setter("FadeBy", f"({change}, {time_})")
|
||||
self.setter('FadeBy', f'({change}, {time_})')
|
||||
time.sleep(self._remote.DELAY)
|
||||
|
||||
|
||||
class BusEQ(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Bus[{self.index}].eq"
|
||||
return f'Bus[{self.index}].eq'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on") == 1
|
||||
return self.getter('on') == 1
|
||||
|
||||
@on.setter
|
||||
def on(self, val: bool):
|
||||
self.setter("on", 1 if val else 0)
|
||||
self.setter('on', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def ab(self) -> bool:
|
||||
return self.getter("ab") == 1
|
||||
return self.getter('ab') == 1
|
||||
|
||||
@ab.setter
|
||||
def ab(self, val: bool):
|
||||
self.setter("ab", 1 if val else 0)
|
||||
self.setter('ab', 1 if val else 0)
|
||||
|
||||
|
||||
class PhysicalBus(Bus):
|
||||
@@ -118,19 +118,19 @@ class PhysicalBus(Bus):
|
||||
Returns a PhysicalBus class.
|
||||
"""
|
||||
kls = (cls,)
|
||||
if kind.name == "potato":
|
||||
if kind.name == 'potato':
|
||||
EFFECTS_cls = _make_effects_mixin()
|
||||
kls += (EFFECTS_cls,)
|
||||
return type(
|
||||
"PhysicalBus",
|
||||
'PhysicalBus',
|
||||
kls,
|
||||
{
|
||||
"device": BusDevice.make(remote, i),
|
||||
'device': BusDevice.make(remote, i),
|
||||
},
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self.index}"
|
||||
return f'{type(self).__name__}{self.index}'
|
||||
|
||||
|
||||
class BusDevice(IRemote):
|
||||
@@ -142,16 +142,16 @@ class BusDevice(IRemote):
|
||||
Returns a BusDevice class of a kind.
|
||||
"""
|
||||
DEVICE_cls = type(
|
||||
f"BusDevice{remote.kind}",
|
||||
f'BusDevice{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
param: device_prop(param)
|
||||
for param in [
|
||||
"wdm",
|
||||
"ks",
|
||||
"mme",
|
||||
"asio",
|
||||
'wdm',
|
||||
'ks',
|
||||
'mme',
|
||||
'asio',
|
||||
]
|
||||
},
|
||||
},
|
||||
@@ -160,15 +160,15 @@ class BusDevice(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Bus[{self.index}].device"
|
||||
return f'Bus[{self.index}].device'
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.getter("name", is_string=True)
|
||||
return self.getter('name', is_string=True)
|
||||
|
||||
@property
|
||||
def sr(self) -> int:
|
||||
return int(self.getter("sr"))
|
||||
return int(self.getter('sr'))
|
||||
|
||||
|
||||
class VirtualBus(Bus):
|
||||
@@ -182,21 +182,21 @@ class VirtualBus(Bus):
|
||||
Returns a VirtualBus class.
|
||||
"""
|
||||
kls = (cls,)
|
||||
if kind.name == "basic":
|
||||
if kind.name == 'basic':
|
||||
return type(
|
||||
"VirtualBus",
|
||||
'VirtualBus',
|
||||
kls,
|
||||
{
|
||||
"device": BusDevice.make(remote, i),
|
||||
'device': BusDevice.make(remote, i),
|
||||
},
|
||||
)
|
||||
elif kind.name == "potato":
|
||||
elif kind.name == 'potato':
|
||||
EFFECTS_cls = _make_effects_mixin()
|
||||
kls += (EFFECTS_cls,)
|
||||
return type("VirtualBus", kls, {})
|
||||
return type('VirtualBus', kls, {})
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self.index}"
|
||||
return f'{type(self).__name__}{self.index}'
|
||||
|
||||
|
||||
class BusLevel(IRemote):
|
||||
@@ -217,7 +217,7 @@ class BusLevel(IRemote):
|
||||
return round(20 * log(x, 10), 1) if x > 0 else -200.0
|
||||
|
||||
if not self._remote.stopped() and self._remote.event.ldirty:
|
||||
vals = self._remote.cache["bus_level"][self.range[0] : self.range[-1]]
|
||||
vals = self._remote.cache['bus_level'][self.range[0] : self.range[-1]]
|
||||
else:
|
||||
vals = [self._remote.get_level(mode, i) for i in range(*self.range)]
|
||||
|
||||
@@ -225,7 +225,7 @@ class BusLevel(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Bus[{self.index}]"
|
||||
return f'Bus[{self.index}]'
|
||||
|
||||
@property
|
||||
def all(self) -> tuple:
|
||||
@@ -248,14 +248,14 @@ def make_bus_level_map(kind):
|
||||
return tuple((i, i + 8) for i in range(0, (kind.phys_out + kind.virt_out) * 8, 8))
|
||||
|
||||
|
||||
_make_bus_level_maps = {kind.name: make_bus_level_map(kind) for kind in kinds_all}
|
||||
_make_bus_level_maps = {kind.name: make_bus_level_map(kind) for kind in kinds.all}
|
||||
|
||||
|
||||
def _make_bus_mode_mixin():
|
||||
"""Creates a mixin of Bus Modes."""
|
||||
|
||||
def identifier(self) -> str:
|
||||
return f"Bus[{self.index}].mode"
|
||||
return f'Bus[{self.index}].mode'
|
||||
|
||||
def get(self) -> str:
|
||||
time.sleep(0.01)
|
||||
@@ -276,15 +276,15 @@ def _make_bus_mode_mixin():
|
||||
):
|
||||
if val:
|
||||
return BusModes(i + 1).name
|
||||
return "normal"
|
||||
return 'normal'
|
||||
|
||||
return type(
|
||||
"BusModeMixin",
|
||||
'BusModeMixin',
|
||||
(IRemote,),
|
||||
{
|
||||
"identifier": property(identifier),
|
||||
'identifier': property(identifier),
|
||||
**{mode.name: bus_mode_prop(mode.name) for mode in BusModes},
|
||||
"get": get,
|
||||
'get': get,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -292,12 +292,12 @@ def _make_bus_mode_mixin():
|
||||
def _make_effects_mixin():
|
||||
"""creates an fx mixin"""
|
||||
return type(
|
||||
"FX",
|
||||
'FX',
|
||||
(),
|
||||
{
|
||||
**{
|
||||
f"return{param}": float_prop(f"return{param}")
|
||||
for param in ["reverb", "delay", "fx1", "fx2"]
|
||||
f'return{param}': float_prop(f'return{param}')
|
||||
for param in ['reverb', 'delay', 'fx1', 'fx2']
|
||||
},
|
||||
},
|
||||
)
|
||||
@@ -316,12 +316,12 @@ def bus_factory(is_phys_bus, remote, i) -> Union[PhysicalBus, VirtualBus]:
|
||||
)
|
||||
BUSMODEMIXIN_cls = _make_bus_mode_mixin()
|
||||
return type(
|
||||
f"{BUS_cls.__name__}{remote.kind}",
|
||||
f'{BUS_cls.__name__}{remote.kind}',
|
||||
(BUS_cls,),
|
||||
{
|
||||
"levels": BusLevel(remote, i),
|
||||
"mode": BUSMODEMIXIN_cls(remote, i),
|
||||
"eq": BusEQ(remote, i),
|
||||
'levels': BusLevel(remote, i),
|
||||
'mode': BUSMODEMIXIN_cls(remote, i),
|
||||
'eq': BusEQ(remote, i),
|
||||
},
|
||||
)(remote, i)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class CBindings(metaclass=ABCMeta):
|
||||
Maps expected ctype argument and res types for each binding.
|
||||
"""
|
||||
|
||||
logger_cbindings = logger.getChild("CBindings")
|
||||
logger_cbindings = logger.getChild('CBindings')
|
||||
|
||||
bind_login = libc.VBVMR_Login
|
||||
bind_login.restype = LONG
|
||||
@@ -38,17 +38,17 @@ class CBindings(metaclass=ABCMeta):
|
||||
bind_get_voicemeeter_version.restype = LONG
|
||||
bind_get_voicemeeter_version.argtypes = [ct.POINTER(LONG)]
|
||||
|
||||
if hasattr(libc, "VBVMR_MacroButton_IsDirty"):
|
||||
if hasattr(libc, 'VBVMR_MacroButton_IsDirty'):
|
||||
bind_macro_button_is_dirty = libc.VBVMR_MacroButton_IsDirty
|
||||
bind_macro_button_is_dirty.restype = LONG
|
||||
bind_macro_button_is_dirty.argtypes = None
|
||||
|
||||
if hasattr(libc, "VBVMR_MacroButton_GetStatus"):
|
||||
if hasattr(libc, 'VBVMR_MacroButton_GetStatus'):
|
||||
bind_macro_button_get_status = libc.VBVMR_MacroButton_GetStatus
|
||||
bind_macro_button_get_status.restype = LONG
|
||||
bind_macro_button_get_status.argtypes = [LONG, ct.POINTER(FLOAT), LONG]
|
||||
|
||||
if hasattr(libc, "VBVMR_MacroButton_SetStatus"):
|
||||
if hasattr(libc, 'VBVMR_MacroButton_SetStatus'):
|
||||
bind_macro_button_set_status = libc.VBVMR_MacroButton_SetStatus
|
||||
bind_macro_button_set_status.restype = LONG
|
||||
bind_macro_button_set_status.argtypes = [LONG, FLOAT, LONG]
|
||||
@@ -121,5 +121,5 @@ class CBindings(metaclass=ABCMeta):
|
||||
raise CAPIError(func.__name__, res)
|
||||
return res
|
||||
except CAPIError as e:
|
||||
self.logger_cbindings.exception(f"{type(e).__name__}: {e}")
|
||||
self.logger_cbindings.exception(f'{type(e).__name__}: {e}')
|
||||
raise
|
||||
|
||||
@@ -17,33 +17,33 @@ class Command(IRemote):
|
||||
Returns a Command class of a kind.
|
||||
"""
|
||||
CMD_cls = type(
|
||||
f"Command{remote.kind}",
|
||||
f'Command{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
param: action_fn(param) for param in ["show", "shutdown", "restart"]
|
||||
param: action_fn(param) for param in ['show', 'shutdown', 'restart']
|
||||
},
|
||||
"hide": action_fn("show", val=0),
|
||||
'hide': action_fn('show', val=0),
|
||||
},
|
||||
)
|
||||
return CMD_cls(remote)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "Command"
|
||||
return 'Command'
|
||||
|
||||
def set_showvbanchat(self, val: bool):
|
||||
self.setter("DialogShow.VBANCHAT", 1 if val else 0)
|
||||
self.setter('DialogShow.VBANCHAT', 1 if val else 0)
|
||||
|
||||
showvbanchat = property(fset=set_showvbanchat)
|
||||
|
||||
def set_lock(self, val: bool):
|
||||
self.setter("lock", 1 if val else 0)
|
||||
self.setter('lock', 1 if val else 0)
|
||||
|
||||
lock = property(fset=set_lock)
|
||||
|
||||
def reset(self):
|
||||
self._remote.apply_config("reset")
|
||||
self._remote.apply_config('reset')
|
||||
|
||||
@@ -20,72 +20,72 @@ class TOMLStrBuilder:
|
||||
def __init__(self, kind):
|
||||
self.kind = kind
|
||||
self.higher = itertools.chain(
|
||||
[f"strip-{i}" for i in range(kind.num_strip)],
|
||||
[f"bus-{i}" for i in range(kind.num_bus)],
|
||||
[f'strip-{i}' for i in range(kind.num_strip)],
|
||||
[f'bus-{i}' for i in range(kind.num_bus)],
|
||||
)
|
||||
|
||||
def init_config(self, profile=None):
|
||||
self.virt_strip_params = (
|
||||
[
|
||||
"mute = false",
|
||||
"mono = false",
|
||||
"solo = false",
|
||||
"gain = 0.0",
|
||||
'mute = false',
|
||||
'mono = false',
|
||||
'solo = false',
|
||||
'gain = 0.0',
|
||||
]
|
||||
+ [f"A{i} = false" for i in range(1, self.kind.phys_out + 1)]
|
||||
+ [f"B{i} = false" for i in range(1, self.kind.virt_out + 1)]
|
||||
+ [f'A{i} = false' for i in range(1, self.kind.phys_out + 1)]
|
||||
+ [f'B{i} = false' for i in range(1, self.kind.virt_out + 1)]
|
||||
)
|
||||
self.phys_strip_params = self.virt_strip_params + [
|
||||
"comp.knob = 0.0",
|
||||
"gate.knob = 0.0",
|
||||
"denoiser.knob = 0.0",
|
||||
"eq.on = false",
|
||||
'comp.knob = 0.0',
|
||||
'gate.knob = 0.0',
|
||||
'denoiser.knob = 0.0',
|
||||
'eq.on = false',
|
||||
]
|
||||
self.bus_params = [
|
||||
"mono = false",
|
||||
"eq.on = false",
|
||||
"mute = false",
|
||||
"gain = 0.0",
|
||||
'mono = false',
|
||||
'eq.on = false',
|
||||
'mute = false',
|
||||
'gain = 0.0',
|
||||
]
|
||||
|
||||
if profile == "reset":
|
||||
if profile == 'reset':
|
||||
self.reset_config()
|
||||
|
||||
def reset_config(self):
|
||||
self.phys_strip_params = list(
|
||||
map(lambda x: x.replace("B1 = false", "B1 = true"), self.phys_strip_params)
|
||||
map(lambda x: x.replace('B1 = false', 'B1 = true'), self.phys_strip_params)
|
||||
)
|
||||
self.virt_strip_params = list(
|
||||
map(lambda x: x.replace("A1 = false", "A1 = true"), self.virt_strip_params)
|
||||
map(lambda x: x.replace('A1 = false', 'A1 = true'), self.virt_strip_params)
|
||||
)
|
||||
|
||||
def build(self, profile="reset"):
|
||||
def build(self, profile='reset'):
|
||||
self.init_config(profile)
|
||||
toml_str = str()
|
||||
for eachclass in self.higher:
|
||||
toml_str += f"[{eachclass}]\n"
|
||||
toml_str += f'[{eachclass}]\n'
|
||||
toml_str = self.join(eachclass, toml_str)
|
||||
return toml_str
|
||||
|
||||
def join(self, eachclass, toml_str):
|
||||
kls, index = eachclass.split("-")
|
||||
kls, index = eachclass.split('-')
|
||||
match kls:
|
||||
case "strip":
|
||||
toml_str += ("\n").join(
|
||||
case 'strip':
|
||||
toml_str += ('\n').join(
|
||||
self.phys_strip_params
|
||||
if int(index) < self.kind.phys_in
|
||||
else self.virt_strip_params
|
||||
)
|
||||
case "bus":
|
||||
toml_str += ("\n").join(self.bus_params)
|
||||
case 'bus':
|
||||
toml_str += ('\n').join(self.bus_params)
|
||||
case _:
|
||||
pass
|
||||
return toml_str + "\n"
|
||||
return toml_str + '\n'
|
||||
|
||||
|
||||
class TOMLDataExtractor:
|
||||
def __init__(self, file):
|
||||
with open(file, "rb") as f:
|
||||
with open(file, 'rb') as f:
|
||||
self._data = tomllib.load(f)
|
||||
|
||||
@property
|
||||
@@ -103,10 +103,10 @@ def dataextraction_factory(file):
|
||||
|
||||
this opens the possibility for other parsers to be added
|
||||
"""
|
||||
if file.suffix == ".toml":
|
||||
if file.suffix == '.toml':
|
||||
extractor = TOMLDataExtractor
|
||||
else:
|
||||
raise ValueError("Cannot extract data from {}".format(file))
|
||||
raise ValueError('Cannot extract data from {}'.format(file))
|
||||
return extractor(file)
|
||||
|
||||
|
||||
@@ -140,25 +140,25 @@ class Loader(metaclass=SingletonType):
|
||||
def defaults(self, kind):
|
||||
self.builder = TOMLStrBuilder(kind)
|
||||
toml_str = self.builder.build()
|
||||
self.register("reset", tomllib.loads(toml_str))
|
||||
self.register('reset', tomllib.loads(toml_str))
|
||||
|
||||
def parse(self, identifier, data):
|
||||
if identifier in self._configs:
|
||||
self.logger.info(
|
||||
f"config file with name {identifier} already in memory, skipping.."
|
||||
f'config file with name {identifier} already in memory, skipping..'
|
||||
)
|
||||
return
|
||||
try:
|
||||
self.parser = dataextraction_factory(data)
|
||||
except tomllib.TOMLDecodeError as e:
|
||||
ERR_MSG = (str(e), f"When attempting to load {identifier}.toml")
|
||||
ERR_MSG = (str(e), f'When attempting to load {identifier}.toml')
|
||||
self.logger.error(f"{type(e).__name__}: {' '.join(ERR_MSG)}")
|
||||
return
|
||||
return True
|
||||
|
||||
def register(self, identifier, data=None):
|
||||
self._configs[identifier] = data if data else self.parser.data
|
||||
self.logger.info(f"config {self.name}/{identifier} loaded into memory")
|
||||
self.logger.info(f'config {self.name}/{identifier} loaded into memory')
|
||||
|
||||
def deregister(self):
|
||||
self._configs.clear()
|
||||
@@ -181,18 +181,18 @@ def loader(kind):
|
||||
|
||||
returns configs loaded into memory
|
||||
"""
|
||||
logger_loader = logger.getChild("loader")
|
||||
logger_loader = logger.getChild('loader')
|
||||
loader = Loader(kind)
|
||||
|
||||
for path in (
|
||||
Path.cwd() / "configs" / kind.name,
|
||||
Path.home() / ".config" / "voicemeeter" / kind.name,
|
||||
Path.home() / "Documents" / "Voicemeeter" / "configs" / kind.name,
|
||||
Path.cwd() / 'configs' / kind.name,
|
||||
Path.home() / '.config' / 'voicemeeter' / kind.name,
|
||||
Path.home() / 'Documents' / 'Voicemeeter' / 'configs' / kind.name,
|
||||
):
|
||||
if path.is_dir():
|
||||
logger_loader.info(f"Checking [{path}] for TOML config files:")
|
||||
for file in path.glob("*.toml"):
|
||||
identifier = file.with_suffix("").stem
|
||||
logger_loader.info(f'Checking [{path}] for TOML config files:')
|
||||
for file in path.glob('*.toml'):
|
||||
identifier = file.with_suffix('').stem
|
||||
if loader.parse(identifier, file):
|
||||
loader.register(identifier)
|
||||
return loader.configs
|
||||
@@ -207,5 +207,5 @@ def request_config(kind_id: str):
|
||||
try:
|
||||
configs = loader(kindmap(kind_id))
|
||||
except KeyError as e:
|
||||
raise VMError(f"Unknown Voicemeeter kind {kind_id}") from e
|
||||
raise VMError(f'Unknown Voicemeeter kind {kind_id}') from e
|
||||
return configs
|
||||
|
||||
@@ -31,8 +31,8 @@ class Adapter(IRemote):
|
||||
return self._remote.get_num_devices(direction)
|
||||
|
||||
vals = self._remote.get_device_description(index, direction)
|
||||
types = {1: "mme", 3: "wdm", 4: "ks", 5: "asio"}
|
||||
return {"name": vals[0], "type": types[vals[1]], "id": vals[2]}
|
||||
types = {1: 'mme', 3: 'wdm', 4: 'ks', 5: 'asio'}
|
||||
return {'name': vals[0], 'type': types[vals[1]], 'id': vals[2]}
|
||||
|
||||
|
||||
class Device(Adapter):
|
||||
@@ -47,26 +47,26 @@ class Device(Adapter):
|
||||
"""
|
||||
|
||||
def num_ins(cls) -> int:
|
||||
return cls.getter(direction="in")
|
||||
return cls.getter(direction='in')
|
||||
|
||||
def num_outs(cls) -> int:
|
||||
return cls.getter(direction="out")
|
||||
return cls.getter(direction='out')
|
||||
|
||||
DEVICE_cls = type(
|
||||
f"Device{remote.kind}",
|
||||
f'Device{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
"ins": property(num_ins),
|
||||
"outs": property(num_outs),
|
||||
'ins': property(num_ins),
|
||||
'outs': property(num_outs),
|
||||
},
|
||||
)
|
||||
return DEVICE_cls(remote)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
def input(self, index: int) -> dict:
|
||||
return self.getter(index=index, direction="in")
|
||||
return self.getter(index=index, direction='in')
|
||||
|
||||
def output(self, index: int) -> dict:
|
||||
return self.getter(index=index, direction="out")
|
||||
return self.getter(index=index, direction='out')
|
||||
|
||||
@@ -13,12 +13,12 @@ class CAPIError(VMError):
|
||||
self.fn_name = fn_name
|
||||
self.code = code
|
||||
if self.code == -9:
|
||||
message = " ".join(
|
||||
message = ' '.join(
|
||||
(
|
||||
f"no bind for {self.fn_name}.",
|
||||
"are you using an old version of the API?",
|
||||
f'no bind for {self.fn_name}.',
|
||||
'are you using an old version of the API?',
|
||||
)
|
||||
)
|
||||
else:
|
||||
message = f"{self.fn_name} returned {self.code}"
|
||||
message = f'{self.fn_name} returned {self.code}'
|
||||
super().__init__(message)
|
||||
|
||||
@@ -12,47 +12,47 @@ class Event:
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def info(self, msg=None):
|
||||
info = (f"{msg} events",) if msg else ()
|
||||
info = (f'{msg} events',) if msg else ()
|
||||
if self.any():
|
||||
info += (f"now listening for {', '.join(self.get())} events",)
|
||||
else:
|
||||
info += ("not listening for any events",)
|
||||
self.logger.info(", ".join(info))
|
||||
info += ('not listening for any events',)
|
||||
self.logger.info(', '.join(info))
|
||||
|
||||
@property
|
||||
def pdirty(self) -> bool:
|
||||
return self.subs["pdirty"]
|
||||
return self.subs['pdirty']
|
||||
|
||||
@pdirty.setter
|
||||
def pdirty(self, val: bool):
|
||||
self.subs["pdirty"] = val
|
||||
self.subs['pdirty'] = val
|
||||
self.info(f"pdirty {'added to' if val else 'removed from'}")
|
||||
|
||||
@property
|
||||
def mdirty(self) -> bool:
|
||||
return self.subs["mdirty"]
|
||||
return self.subs['mdirty']
|
||||
|
||||
@mdirty.setter
|
||||
def mdirty(self, val: bool):
|
||||
self.subs["mdirty"] = val
|
||||
self.subs['mdirty'] = val
|
||||
self.info(f"mdirty {'added to' if val else 'removed from'}")
|
||||
|
||||
@property
|
||||
def midi(self) -> bool:
|
||||
return self.subs["midi"]
|
||||
return self.subs['midi']
|
||||
|
||||
@midi.setter
|
||||
def midi(self, val: bool):
|
||||
self.subs["midi"] = val
|
||||
self.subs['midi'] = val
|
||||
self.info(f"midi {'added to' if val else 'removed from'}")
|
||||
|
||||
@property
|
||||
def ldirty(self) -> bool:
|
||||
return self.subs["ldirty"]
|
||||
return self.subs['ldirty']
|
||||
|
||||
@ldirty.setter
|
||||
def ldirty(self, val: bool):
|
||||
self.subs["ldirty"] = val
|
||||
self.subs['ldirty'] = val
|
||||
self.info(f"ldirty {'added to' if val else 'removed from'}")
|
||||
|
||||
def get(self) -> list:
|
||||
|
||||
@@ -29,8 +29,8 @@ class FactoryBuilder:
|
||||
"""
|
||||
|
||||
BuilderProgress = IntEnum(
|
||||
"BuilderProgress",
|
||||
"strip bus command macrobutton vban device option recorder patch fx",
|
||||
'BuilderProgress',
|
||||
'strip bus command macrobutton vban device option recorder patch fx',
|
||||
start=0,
|
||||
)
|
||||
|
||||
@@ -38,22 +38,22 @@ class FactoryBuilder:
|
||||
self._factory = factory
|
||||
self.kind = kind
|
||||
self._info = (
|
||||
f"Finished building strips for {self._factory}",
|
||||
f"Finished building buses for {self._factory}",
|
||||
f"Finished building commands for {self._factory}",
|
||||
f"Finished building macrobuttons for {self._factory}",
|
||||
f"Finished building vban in/out streams for {self._factory}",
|
||||
f"Finished building device for {self._factory}",
|
||||
f"Finished building option for {self._factory}",
|
||||
f"Finished building recorder for {self._factory}",
|
||||
f"Finished building patch for {self._factory}",
|
||||
f"Finished building fx for {self._factory}",
|
||||
f'Finished building strips for {self._factory}',
|
||||
f'Finished building buses for {self._factory}',
|
||||
f'Finished building commands for {self._factory}',
|
||||
f'Finished building macrobuttons for {self._factory}',
|
||||
f'Finished building vban in/out streams for {self._factory}',
|
||||
f'Finished building device for {self._factory}',
|
||||
f'Finished building option for {self._factory}',
|
||||
f'Finished building recorder for {self._factory}',
|
||||
f'Finished building patch for {self._factory}',
|
||||
f'Finished building fx for {self._factory}',
|
||||
)
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def _pinfo(self, name: str) -> None:
|
||||
"""prints progress status for each step"""
|
||||
name = name.split("_")[1]
|
||||
name = name.split('_')[1]
|
||||
self.logger.debug(self._info[int(getattr(self.BuilderProgress, name))])
|
||||
|
||||
def make_strip(self):
|
||||
@@ -108,17 +108,17 @@ class FactoryBase(Remote):
|
||||
|
||||
def __init__(self, kind_id: str, **kwargs):
|
||||
defaultkwargs = {
|
||||
"sync": False,
|
||||
"ratelimit": 0.033,
|
||||
"pdirty": False,
|
||||
"mdirty": False,
|
||||
"midi": False,
|
||||
"ldirty": False,
|
||||
"timeout": 2,
|
||||
"bits": 64,
|
||||
'sync': False,
|
||||
'ratelimit': 0.033,
|
||||
'pdirty': False,
|
||||
'mdirty': False,
|
||||
'midi': False,
|
||||
'ldirty': False,
|
||||
'timeout': 2,
|
||||
'bits': 64,
|
||||
}
|
||||
if "subs" in kwargs:
|
||||
defaultkwargs |= kwargs.pop("subs") # for backwards compatibility
|
||||
if 'subs' in kwargs:
|
||||
defaultkwargs |= kwargs.pop('subs') # for backwards compatibility
|
||||
kwargs = defaultkwargs | kwargs
|
||||
self.kind = kindmap(kind_id)
|
||||
super().__init__(**kwargs)
|
||||
@@ -135,7 +135,7 @@ class FactoryBase(Remote):
|
||||
self._configs = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Voicemeeter {self.kind}"
|
||||
return f'Voicemeeter {self.kind}'
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
@@ -225,15 +225,15 @@ def remote_factory(kind_id: str, **kwargs) -> Remote:
|
||||
Returns a Remote class of a kind
|
||||
"""
|
||||
match kind_id:
|
||||
case "basic":
|
||||
case 'basic':
|
||||
_factory = BasicFactory
|
||||
case "banana":
|
||||
case 'banana':
|
||||
_factory = BananaFactory
|
||||
case "potato":
|
||||
case 'potato':
|
||||
_factory = PotatoFactory
|
||||
case _:
|
||||
raise ValueError(f"Unknown Voicemeeter kind '{kind_id}'")
|
||||
return type(f"Remote{kind_id.capitalize()}", (_factory,), {})(kind_id, **kwargs)
|
||||
return type(f'Remote{kind_id.capitalize()}', (_factory,), {})(kind_id, **kwargs)
|
||||
|
||||
|
||||
def request_remote_obj(kind_id: str, **kwargs) -> Remote:
|
||||
@@ -243,12 +243,12 @@ def request_remote_obj(kind_id: str, **kwargs) -> Remote:
|
||||
Returns a reference to a Remote class of a kind
|
||||
"""
|
||||
|
||||
logger_entry = logger.getChild("request_remote_obj")
|
||||
logger_entry = logger.getChild('request_remote_obj')
|
||||
|
||||
REMOTE_obj = None
|
||||
try:
|
||||
REMOTE_obj = remote_factory(kind_id, **kwargs)
|
||||
except (ValueError, TypeError) as e:
|
||||
logger_entry.exception(f"{type(e).__name__}: {e}")
|
||||
logger_entry.exception(f'{type(e).__name__}: {e}')
|
||||
raise VMError(str(e)) from e
|
||||
return REMOTE_obj
|
||||
|
||||
@@ -7,21 +7,21 @@ from .error import InstallError
|
||||
|
||||
BITS = 64 if ct.sizeof(ct.c_void_p) == 8 else 32
|
||||
|
||||
if platform.system() != "Windows":
|
||||
raise InstallError("Only Windows OS supported")
|
||||
if platform.system() != 'Windows':
|
||||
raise InstallError('Only Windows OS supported')
|
||||
|
||||
|
||||
VM_KEY = "VB:Voicemeeter {17359A74-1236-5467}"
|
||||
REG_KEY = "\\".join(
|
||||
VM_KEY = 'VB:Voicemeeter {17359A74-1236-5467}'
|
||||
REG_KEY = '\\'.join(
|
||||
filter(
|
||||
None,
|
||||
(
|
||||
"SOFTWARE",
|
||||
"WOW6432Node" if BITS == 64 else "",
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"CurrentVersion",
|
||||
"Uninstall",
|
||||
'SOFTWARE',
|
||||
'WOW6432Node' if BITS == 64 else '',
|
||||
'Microsoft',
|
||||
'Windows',
|
||||
'CurrentVersion',
|
||||
'Uninstall',
|
||||
),
|
||||
)
|
||||
)
|
||||
@@ -29,20 +29,20 @@ REG_KEY = "\\".join(
|
||||
|
||||
def get_vmpath():
|
||||
with winreg.OpenKey(
|
||||
winreg.HKEY_LOCAL_MACHINE, r"{}".format("\\".join((REG_KEY, VM_KEY)))
|
||||
winreg.HKEY_LOCAL_MACHINE, r'{}'.format('\\'.join((REG_KEY, VM_KEY)))
|
||||
) as vm_key:
|
||||
return winreg.QueryValueEx(vm_key, r"UninstallString")[0].strip('"')
|
||||
return winreg.QueryValueEx(vm_key, r'UninstallString')[0].strip('"')
|
||||
|
||||
|
||||
try:
|
||||
vm_parent = Path(get_vmpath()).parent
|
||||
except FileNotFoundError as e:
|
||||
raise InstallError("Unable to fetch DLL path from the registry") from e
|
||||
raise InstallError('Unable to fetch DLL path from the registry') from e
|
||||
|
||||
DLL_NAME = f'VoicemeeterRemote{"64" if BITS == 64 else ""}.dll'
|
||||
|
||||
dll_path = vm_parent.joinpath(DLL_NAME)
|
||||
if not dll_path.is_file():
|
||||
raise InstallError(f"Could not find {dll_path}")
|
||||
raise InstallError(f'Could not find {dll_path}')
|
||||
|
||||
libc = ct.CDLL(str(dll_path))
|
||||
|
||||
@@ -19,19 +19,19 @@ class IRemote(metaclass=ABCMeta):
|
||||
|
||||
def getter(self, param, **kwargs):
|
||||
"""Gets a parameter value"""
|
||||
self.logger.debug(f"getter: {self._cmd(param)}")
|
||||
self.logger.debug(f'getter: {self._cmd(param)}')
|
||||
return self._remote.get(self._cmd(param), **kwargs)
|
||||
|
||||
def setter(self, param, val):
|
||||
"""Sets a parameter value"""
|
||||
self.logger.debug(f"setter: {self._cmd(param)}={val}")
|
||||
self.logger.debug(f'setter: {self._cmd(param)}={val}')
|
||||
self._remote.set(self._cmd(param), val)
|
||||
|
||||
def _cmd(self, param):
|
||||
cmd = (self.identifier,)
|
||||
if param:
|
||||
cmd += (f".{param}",)
|
||||
return "".join(cmd)
|
||||
cmd += (f'.{param}',)
|
||||
return ''.join(cmd)
|
||||
|
||||
@abstractmethod
|
||||
def identifier(self):
|
||||
@@ -39,7 +39,7 @@ class IRemote(metaclass=ABCMeta):
|
||||
|
||||
def apply(self, data: dict):
|
||||
def fget(attr, val):
|
||||
if attr == "mode":
|
||||
if attr == 'mode':
|
||||
return (getattr(self, attr), val, 1)
|
||||
return (self, attr, val)
|
||||
|
||||
@@ -49,7 +49,7 @@ class IRemote(metaclass=ABCMeta):
|
||||
target, attr, val = fget(attr, val)
|
||||
setattr(target, attr, val)
|
||||
else:
|
||||
self.logger.error(f"invalid attribute {attr} for {self}")
|
||||
self.logger.error(f'invalid attribute {attr} for {self}')
|
||||
else:
|
||||
target = getattr(self, attr)
|
||||
target.apply(val)
|
||||
|
||||
@@ -22,7 +22,7 @@ class SingletonType(type):
|
||||
return cls._instances[cls]
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class KindMapClass(metaclass=SingletonType):
|
||||
name: str
|
||||
ins: tuple
|
||||
@@ -68,9 +68,8 @@ class KindMapClass(metaclass=SingletonType):
|
||||
return self.name.capitalize()
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class BasicMap(KindMapClass):
|
||||
name: str
|
||||
ins: tuple = (2, 1)
|
||||
outs: tuple = (1, 1)
|
||||
vban: tuple = (4, 4, 1, 1)
|
||||
@@ -79,9 +78,8 @@ class BasicMap(KindMapClass):
|
||||
composite: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class BananaMap(KindMapClass):
|
||||
name: str
|
||||
ins: tuple = (3, 2)
|
||||
outs: tuple = (3, 2)
|
||||
vban: tuple = (8, 8, 1, 1)
|
||||
@@ -90,9 +88,8 @@ class BananaMap(KindMapClass):
|
||||
composite: int = 8
|
||||
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class PotatoMap(KindMapClass):
|
||||
name: str
|
||||
ins: tuple = (5, 3)
|
||||
outs: tuple = (5, 3)
|
||||
vban: tuple = (8, 8, 1, 1)
|
||||
@@ -103,14 +100,14 @@ class PotatoMap(KindMapClass):
|
||||
|
||||
def kind_factory(kind_id):
|
||||
match kind_id:
|
||||
case "basic":
|
||||
case 'basic':
|
||||
_kind_map = BasicMap
|
||||
case "banana":
|
||||
case 'banana':
|
||||
_kind_map = BananaMap
|
||||
case "potato":
|
||||
case 'potato':
|
||||
_kind_map = PotatoMap
|
||||
case _:
|
||||
raise ValueError(f"Unknown Voicemeeter kind {kind_id}")
|
||||
raise ValueError(f'Unknown Voicemeeter kind {kind_id}')
|
||||
return _kind_map(name=kind_id)
|
||||
|
||||
|
||||
@@ -123,4 +120,4 @@ def request_kind_map(kind_id):
|
||||
return KIND_obj
|
||||
|
||||
|
||||
kinds_all = list(request_kind_map(kind_id.name.lower()) for kind_id in KindId)
|
||||
all = kinds_all = [request_kind_map(kind_id.name.lower()) for kind_id in KindId]
|
||||
|
||||
@@ -3,8 +3,8 @@ from enum import IntEnum
|
||||
from .iremote import IRemote
|
||||
|
||||
ButtonModes = IntEnum(
|
||||
"ButtonModes",
|
||||
"state stateonly trigger",
|
||||
'ButtonModes',
|
||||
'state stateonly trigger',
|
||||
start=1,
|
||||
)
|
||||
|
||||
@@ -16,12 +16,12 @@ class Adapter(IRemote):
|
||||
pass
|
||||
|
||||
def getter(self, mode):
|
||||
self.logger.debug(f"getter: button[{self.index}].{ButtonModes(mode).name}")
|
||||
self.logger.debug(f'getter: button[{self.index}].{ButtonModes(mode).name}')
|
||||
return self._remote.get_buttonstatus(self.index, mode)
|
||||
|
||||
def setter(self, mode, val):
|
||||
self.logger.debug(
|
||||
f"setter: button[{self.index}].{ButtonModes(mode).name}={val}"
|
||||
f'setter: button[{self.index}].{ButtonModes(mode).name}={val}'
|
||||
)
|
||||
self._remote.set_buttonstatus(self.index, val, mode)
|
||||
|
||||
@@ -30,7 +30,7 @@ class MacroButton(Adapter):
|
||||
"""Defines concrete implementation for macrobutton"""
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self._remote.kind}{self.index}"
|
||||
return f'{type(self).__name__}{self._remote.kind}{self.index}'
|
||||
|
||||
@property
|
||||
def state(self) -> bool:
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
from typing import Optional
|
||||
|
||||
from . import kinds
|
||||
from .iremote import IRemote
|
||||
from .kinds import kinds_all
|
||||
|
||||
|
||||
class FX(IRemote):
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "FX"
|
||||
return 'FX'
|
||||
|
||||
@property
|
||||
def reverb(self) -> bool:
|
||||
return self.getter("reverb.On") == 1
|
||||
return self.getter('reverb.On') == 1
|
||||
|
||||
@reverb.setter
|
||||
def reverb(self, val: bool):
|
||||
self.setter("reverb.On", 1 if val else 0)
|
||||
self.setter('reverb.On', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def reverb_ab(self) -> bool:
|
||||
return self.getter("reverb.ab") == 1
|
||||
return self.getter('reverb.ab') == 1
|
||||
|
||||
@reverb_ab.setter
|
||||
def reverb_ab(self, val: bool):
|
||||
self.setter("reverb.ab", 1 if val else 0)
|
||||
self.setter('reverb.ab', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def delay(self) -> bool:
|
||||
return self.getter("delay.On") == 1
|
||||
return self.getter('delay.On') == 1
|
||||
|
||||
@delay.setter
|
||||
def delay(self, val: bool):
|
||||
self.setter("delay.On", 1 if val else 0)
|
||||
self.setter('delay.On', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def delay_ab(self) -> bool:
|
||||
return self.getter("delay.ab") == 1
|
||||
return self.getter('delay.ab') == 1
|
||||
|
||||
@delay_ab.setter
|
||||
def delay_ab(self, val: bool):
|
||||
self.setter("delay.ab", 1 if val else 0)
|
||||
self.setter('delay.ab', 1 if val else 0)
|
||||
|
||||
|
||||
class Patch(IRemote):
|
||||
@@ -57,50 +57,50 @@ class Patch(IRemote):
|
||||
"""
|
||||
ASIO_cls = _make_asio_mixins(remote)[remote.kind.name]
|
||||
return type(
|
||||
f"Patch{remote.kind}",
|
||||
f'Patch{remote.kind}',
|
||||
(cls, ASIO_cls),
|
||||
{
|
||||
"composite": tuple(Composite(remote, i) for i in range(8)),
|
||||
"insert": tuple(Insert(remote, i) for i in range(remote.kind.insert)),
|
||||
'composite': tuple(Composite(remote, i) for i in range(8)),
|
||||
'insert': tuple(Insert(remote, i) for i in range(remote.kind.insert)),
|
||||
},
|
||||
)(remote)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "patch"
|
||||
return 'patch'
|
||||
|
||||
@property
|
||||
def postfadercomp(self) -> bool:
|
||||
return self.getter("postfadercomposite") == 1
|
||||
return self.getter('postfadercomposite') == 1
|
||||
|
||||
@postfadercomp.setter
|
||||
def postfadercomp(self, val: bool):
|
||||
self.setter("postfadercomposite", 1 if val else 0)
|
||||
self.setter('postfadercomposite', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def postfxinsert(self) -> bool:
|
||||
return self.getter("postfxinsert") == 1
|
||||
return self.getter('postfxinsert') == 1
|
||||
|
||||
@postfxinsert.setter
|
||||
def postfxinsert(self, val: bool):
|
||||
self.setter("postfxinsert", 1 if val else 0)
|
||||
self.setter('postfxinsert', 1 if val else 0)
|
||||
|
||||
|
||||
class Asio(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "patch"
|
||||
return 'patch'
|
||||
|
||||
|
||||
class AsioIn(Asio):
|
||||
def get(self) -> int:
|
||||
return int(self.getter(f"asio[{self.index}]"))
|
||||
return int(self.getter(f'asio[{self.index}]'))
|
||||
|
||||
def set(self, val: int):
|
||||
self.setter(f"asio[{self.index}]", val)
|
||||
self.setter(f'asio[{self.index}]', val)
|
||||
|
||||
|
||||
class AsioOut(Asio):
|
||||
@@ -109,10 +109,10 @@ class AsioOut(Asio):
|
||||
self._param = param
|
||||
|
||||
def get(self) -> int:
|
||||
return int(self.getter(f"out{self._param}[{self.index}]"))
|
||||
return int(self.getter(f'out{self._param}[{self.index}]'))
|
||||
|
||||
def set(self, val: int):
|
||||
self.setter(f"out{self._param}[{self.index}]", val)
|
||||
self.setter(f'out{self._param}[{self.index}]', val)
|
||||
|
||||
|
||||
def _make_asio_mixin(remote, kind):
|
||||
@@ -120,46 +120,46 @@ def _make_asio_mixin(remote, kind):
|
||||
asio_in, asio_out = kind.asio
|
||||
|
||||
return type(
|
||||
f"ASIO{kind}",
|
||||
f'ASIO{kind}',
|
||||
(IRemote,),
|
||||
{
|
||||
"asio": tuple(AsioIn(remote, i) for i in range(asio_in)),
|
||||
'asio': tuple(AsioIn(remote, i) for i in range(asio_in)),
|
||||
**{
|
||||
param: tuple(AsioOut(remote, i, param) for i in range(asio_out))
|
||||
for param in ["A2", "A3", "A4", "A5"]
|
||||
for param in ['A2', 'A3', 'A4', 'A5']
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _make_asio_mixins(remote):
|
||||
return {kind.name: _make_asio_mixin(remote, kind) for kind in kinds_all}
|
||||
return {kind.name: _make_asio_mixin(remote, kind) for kind in kinds.all}
|
||||
|
||||
|
||||
class Composite(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "patch"
|
||||
return 'patch'
|
||||
|
||||
def get(self) -> int:
|
||||
return int(self.getter(f"composite[{self.index}]"))
|
||||
return int(self.getter(f'composite[{self.index}]'))
|
||||
|
||||
def set(self, val: int):
|
||||
self.setter(f"composite[{self.index}]", val)
|
||||
self.setter(f'composite[{self.index}]', val)
|
||||
|
||||
|
||||
class Insert(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "patch"
|
||||
return 'patch'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter(f"insert[{self.index}]") == 1
|
||||
return self.getter(f'insert[{self.index}]') == 1
|
||||
|
||||
@on.setter
|
||||
def on(self, val: bool):
|
||||
self.setter(f"insert[{self.index}]", 1 if val else 0)
|
||||
self.setter(f'insert[{self.index}]', 1 if val else 0)
|
||||
|
||||
|
||||
class Option(IRemote):
|
||||
@@ -173,61 +173,61 @@ class Option(IRemote):
|
||||
Returns a Option class of a kind.
|
||||
"""
|
||||
return type(
|
||||
f"Option{remote.kind}",
|
||||
f'Option{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
"delay": tuple(Delay(remote, i) for i in range(remote.kind.phys_out)),
|
||||
'delay': tuple(Delay(remote, i) for i in range(remote.kind.phys_out)),
|
||||
},
|
||||
)(remote)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "option"
|
||||
return 'option'
|
||||
|
||||
@property
|
||||
def sr(self) -> int:
|
||||
return int(self.getter("sr"))
|
||||
return int(self.getter('sr'))
|
||||
|
||||
@sr.setter
|
||||
def sr(self, val: int):
|
||||
opts = (44100, 48000, 88200, 96000, 176400, 192000)
|
||||
if val not in opts:
|
||||
self.logger.warning(f"sr got: {val} but expected a value in {opts}")
|
||||
self.setter("sr", val)
|
||||
self.logger.warning(f'sr got: {val} but expected a value in {opts}')
|
||||
self.setter('sr', val)
|
||||
|
||||
@property
|
||||
def asiosr(self) -> bool:
|
||||
return self.getter("asiosr") == 1
|
||||
return self.getter('asiosr') == 1
|
||||
|
||||
@asiosr.setter
|
||||
def asiosr(self, val: bool):
|
||||
self.setter("asiosr", 1 if val else 0)
|
||||
self.setter('asiosr', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def monitoronsel(self) -> bool:
|
||||
return self.getter("monitoronsel") == 1
|
||||
return self.getter('monitoronsel') == 1
|
||||
|
||||
@monitoronsel.setter
|
||||
def monitoronsel(self, val: bool):
|
||||
self.setter("monitoronsel", 1 if val else 0)
|
||||
self.setter('monitoronsel', 1 if val else 0)
|
||||
|
||||
def buffer(self, driver, buffer):
|
||||
self.setter(f"buffer.{driver}", buffer)
|
||||
self.setter(f'buffer.{driver}', buffer)
|
||||
|
||||
|
||||
class Delay(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "option"
|
||||
return 'option'
|
||||
|
||||
def get(self) -> int:
|
||||
return int(self.getter(f"delay[{self.index}]"))
|
||||
return int(self.getter(f'delay[{self.index}]'))
|
||||
|
||||
def set(self, val: int):
|
||||
self.setter(f"delay[{self.index}]", val)
|
||||
self.setter(f'delay[{self.index}]', val)
|
||||
|
||||
|
||||
class Midi:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import re
|
||||
|
||||
from . import kinds
|
||||
from .error import VMError
|
||||
from .iremote import IRemote
|
||||
from .kinds import kinds_all
|
||||
from .meta import action_fn, bool_prop
|
||||
|
||||
|
||||
@@ -23,91 +23,91 @@ class Recorder(IRemote):
|
||||
CHANNELOUTMIXIN_cls = _make_channelout_mixins[remote.kind.name]
|
||||
ARMCHANNELMIXIN_cls = _make_armchannel_mixins(remote)[remote.kind.name]
|
||||
REC_cls = type(
|
||||
f"Recorder{remote.kind}",
|
||||
f'Recorder{remote.kind}',
|
||||
(cls, CHANNELOUTMIXIN_cls, ARMCHANNELMIXIN_cls),
|
||||
{
|
||||
**{
|
||||
param: action_fn(param)
|
||||
for param in [
|
||||
"play",
|
||||
"stop",
|
||||
"pause",
|
||||
"replay",
|
||||
"record",
|
||||
"ff",
|
||||
"rew",
|
||||
'play',
|
||||
'stop',
|
||||
'pause',
|
||||
'replay',
|
||||
'record',
|
||||
'ff',
|
||||
'rew',
|
||||
]
|
||||
},
|
||||
"mode": RecorderMode(remote),
|
||||
'mode': RecorderMode(remote),
|
||||
},
|
||||
)
|
||||
return REC_cls(remote)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}"
|
||||
return f'{type(self).__name__}'
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return "recorder"
|
||||
return 'recorder'
|
||||
|
||||
@property
|
||||
def samplerate(self) -> int:
|
||||
return int(self.getter("samplerate"))
|
||||
return int(self.getter('samplerate'))
|
||||
|
||||
@samplerate.setter
|
||||
def samplerate(self, val: int):
|
||||
opts = (22050, 24000, 32000, 44100, 48000, 88200, 96000, 176400, 192000)
|
||||
if val not in opts:
|
||||
self.logger.warning(f"samplerate got: {val} but expected a value in {opts}")
|
||||
self.setter("samplerate", val)
|
||||
self.logger.warning(f'samplerate got: {val} but expected a value in {opts}')
|
||||
self.setter('samplerate', val)
|
||||
|
||||
@property
|
||||
def bitresolution(self) -> int:
|
||||
return int(self.getter("bitresolution"))
|
||||
return int(self.getter('bitresolution'))
|
||||
|
||||
@bitresolution.setter
|
||||
def bitresolution(self, val: int):
|
||||
opts = (8, 16, 24, 32)
|
||||
if val not in opts:
|
||||
self.logger.warning(
|
||||
f"bitresolution got: {val} but expected a value in {opts}"
|
||||
f'bitresolution got: {val} but expected a value in {opts}'
|
||||
)
|
||||
self.setter("bitresolution", val)
|
||||
self.setter('bitresolution', val)
|
||||
|
||||
@property
|
||||
def channel(self) -> int:
|
||||
return int(self.getter("channel"))
|
||||
return int(self.getter('channel'))
|
||||
|
||||
@channel.setter
|
||||
def channel(self, val: int):
|
||||
if not 1 <= val <= 8:
|
||||
self.logger.warning(f"channel got: {val} but expected a value from 1 to 8")
|
||||
self.setter("channel", val)
|
||||
self.logger.warning(f'channel got: {val} but expected a value from 1 to 8')
|
||||
self.setter('channel', val)
|
||||
|
||||
@property
|
||||
def kbps(self):
|
||||
return int(self.getter("kbps"))
|
||||
return int(self.getter('kbps'))
|
||||
|
||||
@kbps.setter
|
||||
def kbps(self, val: int):
|
||||
opts = (32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320)
|
||||
if val not in opts:
|
||||
self.logger.warning(f"kbps got: {val} but expected a value in {opts}")
|
||||
self.setter("kbps", val)
|
||||
self.logger.warning(f'kbps got: {val} but expected a value in {opts}')
|
||||
self.setter('kbps', val)
|
||||
|
||||
@property
|
||||
def gain(self) -> float:
|
||||
return round(self.getter("gain"), 1)
|
||||
return round(self.getter('gain'), 1)
|
||||
|
||||
@gain.setter
|
||||
def gain(self, val: float):
|
||||
self.setter("gain", val)
|
||||
self.setter('gain', val)
|
||||
|
||||
def load(self, file: str):
|
||||
try:
|
||||
self.setter("load", file)
|
||||
self.setter('load', file)
|
||||
except UnicodeError:
|
||||
raise VMError("File full directory must be a raw string")
|
||||
raise VMError('File full directory must be a raw string')
|
||||
|
||||
# loop forwarder methods, for backwards compatibility
|
||||
@property
|
||||
@@ -121,69 +121,69 @@ class Recorder(IRemote):
|
||||
def goto(self, time_str):
|
||||
def get_sec():
|
||||
"""Get seconds from time string"""
|
||||
h, m, s = time_str.split(":")
|
||||
h, m, s = time_str.split(':')
|
||||
return int(h) * 3600 + int(m) * 60 + int(s)
|
||||
|
||||
time_str = str(time_str) # coerce the type
|
||||
if (
|
||||
re.match(
|
||||
r"^(?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d)$",
|
||||
r'^(?:[01]\d|2[0123]):(?:[012345]\d):(?:[012345]\d)$',
|
||||
time_str,
|
||||
)
|
||||
is not None
|
||||
):
|
||||
self.setter("goto", get_sec())
|
||||
self.setter('goto', get_sec())
|
||||
else:
|
||||
self.logger.warning(
|
||||
"goto expects a string that matches the format 'hh:mm:ss'"
|
||||
)
|
||||
|
||||
def filetype(self, val: str):
|
||||
opts = {"wav": 1, "aiff": 2, "bwf": 3, "mp3": 100}
|
||||
opts = {'wav': 1, 'aiff': 2, 'bwf': 3, 'mp3': 100}
|
||||
try:
|
||||
self.setter("filetype", opts[val.lower()])
|
||||
self.setter('filetype', opts[val.lower()])
|
||||
except KeyError:
|
||||
self.logger.warning(
|
||||
f"filetype got: {val} but expected a value in {list(opts.keys())}"
|
||||
f'filetype got: {val} but expected a value in {list(opts.keys())}'
|
||||
)
|
||||
|
||||
|
||||
class RecorderMode(IRemote):
|
||||
@property
|
||||
def identifier(self):
|
||||
return "recorder.mode"
|
||||
return 'recorder.mode'
|
||||
|
||||
@property
|
||||
def recbus(self) -> bool:
|
||||
return self.getter("recbus") == 1
|
||||
return self.getter('recbus') == 1
|
||||
|
||||
@recbus.setter
|
||||
def recbus(self, val: bool):
|
||||
self.setter("recbus", 1 if val else 0)
|
||||
self.setter('recbus', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def playonload(self) -> bool:
|
||||
return self.getter("playonload") == 1
|
||||
return self.getter('playonload') == 1
|
||||
|
||||
@playonload.setter
|
||||
def playonload(self, val: bool):
|
||||
self.setter("playonload", 1 if val else 0)
|
||||
self.setter('playonload', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def loop(self) -> bool:
|
||||
return self.getter("loop") == 1
|
||||
return self.getter('loop') == 1
|
||||
|
||||
@loop.setter
|
||||
def loop(self, val: bool):
|
||||
self.setter("loop", 1 if val else 0)
|
||||
self.setter('loop', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def multitrack(self) -> bool:
|
||||
return self.getter("multitrack") == 1
|
||||
return self.getter('multitrack') == 1
|
||||
|
||||
@multitrack.setter
|
||||
def multitrack(self, val: bool):
|
||||
self.setter("multitrack", 1 if val else 0)
|
||||
self.setter('multitrack', 1 if val else 0)
|
||||
|
||||
|
||||
class RecorderArmChannel(IRemote):
|
||||
@@ -192,51 +192,51 @@ class RecorderArmChannel(IRemote):
|
||||
self._i = i
|
||||
|
||||
def set(self, val: bool):
|
||||
self.setter("", 1 if val else 0)
|
||||
self.setter('', 1 if val else 0)
|
||||
|
||||
|
||||
class RecorderArmStrip(RecorderArmChannel):
|
||||
@property
|
||||
def identifier(self):
|
||||
return f"recorder.armstrip[{self._i}]"
|
||||
return f'recorder.armstrip[{self._i}]'
|
||||
|
||||
|
||||
class RecorderArmBus(RecorderArmChannel):
|
||||
@property
|
||||
def identifier(self):
|
||||
return f"recorder.armbus[{self._i}]"
|
||||
return f'recorder.armbus[{self._i}]'
|
||||
|
||||
|
||||
def _make_armchannel_mixin(remote, kind):
|
||||
"""Creates an armchannel out mixin"""
|
||||
return type(
|
||||
f"ArmChannelMixin{kind}",
|
||||
f'ArmChannelMixin{kind}',
|
||||
(),
|
||||
{
|
||||
"armstrip": tuple(
|
||||
'armstrip': tuple(
|
||||
RecorderArmStrip(remote, i) for i in range(kind.num_strip)
|
||||
),
|
||||
"armbus": tuple(RecorderArmBus(remote, i) for i in range(kind.num_bus)),
|
||||
'armbus': tuple(RecorderArmBus(remote, i) for i in range(kind.num_bus)),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def _make_armchannel_mixins(remote):
|
||||
return {kind.name: _make_armchannel_mixin(remote, kind) for kind in kinds_all}
|
||||
return {kind.name: _make_armchannel_mixin(remote, kind) for kind in kinds.all}
|
||||
|
||||
|
||||
def _make_channelout_mixin(kind):
|
||||
"""Creates a channel out mixin"""
|
||||
return type(
|
||||
f"ChannelOutMixin{kind}",
|
||||
f'ChannelOutMixin{kind}',
|
||||
(),
|
||||
{
|
||||
**{f"A{i}": bool_prop(f"A{i}") for i in range(1, kind.phys_out + 1)},
|
||||
**{f"B{i}": bool_prop(f"B{i}") for i in range(1, kind.virt_out + 1)},
|
||||
**{f'A{i}': bool_prop(f'A{i}') for i in range(1, kind.phys_out + 1)},
|
||||
**{f'B{i}': bool_prop(f'B{i}') for i in range(1, kind.virt_out + 1)},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
_make_channelout_mixins = {
|
||||
kind.name: _make_channelout_mixin(kind) for kind in kinds_all
|
||||
kind.name: _make_channelout_mixin(kind) for kind in kinds.all
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class Remote(CBindings):
|
||||
self.midi = Midi()
|
||||
self.subject = self.observer = Subject()
|
||||
self.event = Event(
|
||||
{k: kwargs.pop(k) for k in ("pdirty", "mdirty", "midi", "ldirty")}
|
||||
{k: kwargs.pop(k) for k in ('pdirty', 'mdirty', 'midi', 'ldirty')}
|
||||
)
|
||||
self.gui = VmGui()
|
||||
self.stop_event = None
|
||||
@@ -41,7 +41,7 @@ class Remote(CBindings):
|
||||
|
||||
if self.bits not in (32, 64):
|
||||
self.logger.warning(
|
||||
f"kwarg bits got {self.bits}, expected either 32 or 64, defaulting to 64"
|
||||
f'kwarg bits got {self.bits}, expected either 32 or 64, defaulting to 64'
|
||||
)
|
||||
self.bits = 64
|
||||
|
||||
@@ -61,7 +61,7 @@ class Remote(CBindings):
|
||||
"""Starts updates thread."""
|
||||
self.event.info()
|
||||
|
||||
self.logger.debug("initiating events thread")
|
||||
self.logger.debug('initiating events thread')
|
||||
self.stop_event = threading.Event()
|
||||
self.stop_event.clear()
|
||||
queue = Queue()
|
||||
@@ -79,7 +79,7 @@ class Remote(CBindings):
|
||||
self.gui.launched = self.call(self.bind_login, ok=(0, 1)) == 0
|
||||
if not self.gui.launched:
|
||||
self.logger.info(
|
||||
"Voicemeeter engine running but GUI not launched. Launching the GUI now."
|
||||
'Voicemeeter engine running but GUI not launched. Launching the GUI now.'
|
||||
)
|
||||
self.run_voicemeeter(self.kind.name)
|
||||
|
||||
@@ -103,7 +103,7 @@ class Remote(CBindings):
|
||||
"""Returns Voicemeeter's version as a string"""
|
||||
ver = ct.c_long()
|
||||
self.call(self.bind_get_voicemeeter_version, ct.byref(ver))
|
||||
return "{}.{}.{}.{}".format(
|
||||
return '{}.{}.{}.{}'.format(
|
||||
(ver.value & 0xFF000000) >> 24,
|
||||
(ver.value & 0x00FF0000) >> 16,
|
||||
(ver.value & 0x0000FF00) >> 8,
|
||||
@@ -121,16 +121,16 @@ class Remote(CBindings):
|
||||
try:
|
||||
return self.call(self.bind_macro_button_is_dirty, ok=(0, 1)) == 1
|
||||
except AttributeError as e:
|
||||
self.logger.exception(f"{type(e).__name__}: {e}")
|
||||
raise CAPIError("VBVMR_MacroButton_IsDirty", -9) from e
|
||||
self.logger.exception(f'{type(e).__name__}: {e}')
|
||||
raise CAPIError('VBVMR_MacroButton_IsDirty', -9) from e
|
||||
|
||||
@property
|
||||
def ldirty(self) -> bool:
|
||||
"""True iff levels have been updated."""
|
||||
self._strip_buf, self._bus_buf = self._get_levels()
|
||||
return not (
|
||||
self.cache.get("strip_level") == self._strip_buf
|
||||
and self.cache.get("bus_level") == self._bus_buf
|
||||
self.cache.get('strip_level') == self._strip_buf
|
||||
and self.cache.get('bus_level') == self._bus_buf
|
||||
)
|
||||
|
||||
def clear_dirty(self) -> None:
|
||||
@@ -138,9 +138,9 @@ class Remote(CBindings):
|
||||
while self.pdirty or self.mdirty:
|
||||
pass
|
||||
except CAPIError as e:
|
||||
if not (e.fn_name == "VBVMR_MacroButton_IsDirty" and e.code == -9):
|
||||
if not (e.fn_name == 'VBVMR_MacroButton_IsDirty' and e.code == -9):
|
||||
raise
|
||||
self.logger.error(f"{e} clearing pdirty only.")
|
||||
self.logger.error(f'{e} clearing pdirty only.')
|
||||
while self.pdirty:
|
||||
pass
|
||||
|
||||
@@ -159,7 +159,7 @@ class Remote(CBindings):
|
||||
"""Sets a string or float parameter. Caches value"""
|
||||
if isinstance(val, str):
|
||||
if len(val) >= 512:
|
||||
raise VMError("String is too long")
|
||||
raise VMError('String is too long')
|
||||
self.call(
|
||||
self.bind_set_parameter_string_w, param.encode(), ct.c_wchar_p(val)
|
||||
)
|
||||
@@ -181,8 +181,8 @@ class Remote(CBindings):
|
||||
ct.c_long(mode),
|
||||
)
|
||||
except AttributeError as e:
|
||||
self.logger.exception(f"{type(e).__name__}: {e}")
|
||||
raise CAPIError("VBVMR_MacroButton_GetStatus", -9) from e
|
||||
self.logger.exception(f'{type(e).__name__}: {e}')
|
||||
raise CAPIError('VBVMR_MacroButton_GetStatus', -9) from e
|
||||
return int(c_state.value)
|
||||
|
||||
def set_buttonstatus(self, id_: int, val: int, mode: int) -> None:
|
||||
@@ -196,26 +196,26 @@ class Remote(CBindings):
|
||||
ct.c_long(mode),
|
||||
)
|
||||
except AttributeError as e:
|
||||
self.logger.exception(f"{type(e).__name__}: {e}")
|
||||
raise CAPIError("VBVMR_MacroButton_SetStatus", -9) from e
|
||||
self.cache[f"mb_{id_}_{mode}"] = int(c_state.value)
|
||||
self.logger.exception(f'{type(e).__name__}: {e}')
|
||||
raise CAPIError('VBVMR_MacroButton_SetStatus', -9) from e
|
||||
self.cache[f'mb_{id_}_{mode}'] = int(c_state.value)
|
||||
|
||||
def get_num_devices(self, direction: str = None) -> int:
|
||||
"""Retrieves number of physical devices connected"""
|
||||
if direction not in ("in", "out"):
|
||||
raise VMError("Expected a direction: in or out")
|
||||
func = getattr(self, f"bind_{direction}put_get_device_number")
|
||||
if direction not in ('in', 'out'):
|
||||
raise VMError('Expected a direction: in or out')
|
||||
func = getattr(self, f'bind_{direction}put_get_device_number')
|
||||
res = self.call(func, ok_exp=lambda r: r >= 0)
|
||||
return res
|
||||
|
||||
def get_device_description(self, index: int, direction: str = None) -> tuple:
|
||||
"""Returns a tuple of device parameters"""
|
||||
if direction not in ("in", "out"):
|
||||
raise VMError("Expected a direction: in or out")
|
||||
if direction not in ('in', 'out'):
|
||||
raise VMError('Expected a direction: in or out')
|
||||
type_ = ct.c_long()
|
||||
name = ct.create_unicode_buffer(256)
|
||||
hwid = ct.create_unicode_buffer(256)
|
||||
func = getattr(self, f"bind_{direction}put_get_device_desc_w")
|
||||
func = getattr(self, f'bind_{direction}put_get_device_desc_w')
|
||||
self.call(
|
||||
func,
|
||||
ct.c_long(index),
|
||||
@@ -257,7 +257,7 @@ class Remote(CBindings):
|
||||
)
|
||||
if res > 0:
|
||||
vals = tuple(
|
||||
grouper(3, (int.from_bytes(buf[i], "little") for i in range(res)))
|
||||
grouper(3, (int.from_bytes(buf[i], 'little') for i in range(res)))
|
||||
)
|
||||
for msg in vals:
|
||||
ch, pitch, vel = msg
|
||||
@@ -271,7 +271,7 @@ class Remote(CBindings):
|
||||
def sendtext(self, script: str):
|
||||
"""Sets many parameters from a script"""
|
||||
if len(script) > 48000:
|
||||
raise ValueError("Script too large, max size 48kB")
|
||||
raise ValueError('Script too large, max size 48kB')
|
||||
self.call(self.bind_set_parameters, script.encode())
|
||||
time.sleep(self.DELAY * 5)
|
||||
|
||||
@@ -283,12 +283,15 @@ class Remote(CBindings):
|
||||
"""
|
||||
|
||||
def target(key):
|
||||
match key.split("-"):
|
||||
case ["strip" | "bus" | "button" as kls, index] if index.isnumeric():
|
||||
match key.split('-'):
|
||||
case ['strip' | 'bus' | 'button' as kls, index] if index.isnumeric():
|
||||
target = getattr(self, kls)
|
||||
case [
|
||||
"vban",
|
||||
"in" | "instream" | "out" | "outstream" as direction,
|
||||
'vban',
|
||||
'in'
|
||||
| 'instream'
|
||||
| 'out'
|
||||
| 'outstream' as direction,
|
||||
index,
|
||||
] if index.isnumeric():
|
||||
target = getattr(
|
||||
@@ -306,20 +309,20 @@ class Remote(CBindings):
|
||||
"""applies a config from memory"""
|
||||
ERR_MSG = (
|
||||
f"No config with name '{name}' is loaded into memory",
|
||||
f"Known configs: {list(self.configs.keys())}",
|
||||
f'Known configs: {list(self.configs.keys())}',
|
||||
)
|
||||
try:
|
||||
config = self.configs[name]
|
||||
except KeyError as e:
|
||||
self.logger.error(("\n").join(ERR_MSG))
|
||||
raise VMError(("\n").join(ERR_MSG)) from e
|
||||
self.logger.error(('\n').join(ERR_MSG))
|
||||
raise VMError(('\n').join(ERR_MSG)) from e
|
||||
|
||||
if "extends" in config:
|
||||
extended = config["extends"]
|
||||
if 'extends' in config:
|
||||
extended = config['extends']
|
||||
config = {
|
||||
k: v
|
||||
for k, v in deep_merge(self.configs[extended], config)
|
||||
if k not in ("extends")
|
||||
if k not in ('extends')
|
||||
}
|
||||
self.logger.debug(
|
||||
f"profile '{name}' extends '{extended}', profiles merged.."
|
||||
@@ -329,7 +332,7 @@ class Remote(CBindings):
|
||||
|
||||
def end_thread(self):
|
||||
if not self.stopped():
|
||||
self.logger.debug("events thread shutdown started")
|
||||
self.logger.debug('events thread shutdown started')
|
||||
self.stop_event.set()
|
||||
self.producer.join() # wait for producer thread to complete cycle
|
||||
|
||||
@@ -337,7 +340,7 @@ class Remote(CBindings):
|
||||
"""Logout of the API"""
|
||||
time.sleep(0.1)
|
||||
self.call(self.bind_logout)
|
||||
self.logger.info(f"{type(self).__name__}: Successfully logged out of {self}")
|
||||
self.logger.info(f'{type(self).__name__}: Successfully logged out of {self}')
|
||||
|
||||
def __exit__(self, exc_type, exc_value, exc_traceback) -> None:
|
||||
"""teardown procedures"""
|
||||
|
||||
@@ -3,8 +3,8 @@ from abc import abstractmethod
|
||||
from math import log
|
||||
from typing import Union
|
||||
|
||||
from . import kinds
|
||||
from .iremote import IRemote
|
||||
from .kinds import kinds_all
|
||||
from .meta import bool_prop, device_prop, float_prop
|
||||
|
||||
|
||||
@@ -21,62 +21,62 @@ class Strip(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"strip[{self.index}]"
|
||||
return f'strip[{self.index}]'
|
||||
|
||||
@property
|
||||
def mono(self) -> bool:
|
||||
return self.getter("mono") == 1
|
||||
return self.getter('mono') == 1
|
||||
|
||||
@mono.setter
|
||||
def mono(self, val: bool):
|
||||
self.setter("mono", 1 if val else 0)
|
||||
self.setter('mono', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def solo(self) -> bool:
|
||||
return self.getter("solo") == 1
|
||||
return self.getter('solo') == 1
|
||||
|
||||
@solo.setter
|
||||
def solo(self, val: bool):
|
||||
self.setter("solo", 1 if val else 0)
|
||||
self.setter('solo', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def mute(self) -> bool:
|
||||
return self.getter("mute") == 1
|
||||
return self.getter('mute') == 1
|
||||
|
||||
@mute.setter
|
||||
def mute(self, val: bool):
|
||||
self.setter("mute", 1 if val else 0)
|
||||
self.setter('mute', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def limit(self) -> int:
|
||||
return int(self.getter("limit"))
|
||||
return int(self.getter('limit'))
|
||||
|
||||
@limit.setter
|
||||
def limit(self, val: int):
|
||||
self.setter("limit", val)
|
||||
self.setter('limit', val)
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
return self.getter("Label", is_string=True)
|
||||
return self.getter('Label', is_string=True)
|
||||
|
||||
@label.setter
|
||||
def label(self, val: str):
|
||||
self.setter("Label", str(val))
|
||||
self.setter('Label', str(val))
|
||||
|
||||
@property
|
||||
def gain(self) -> float:
|
||||
return round(self.getter("gain"), 1)
|
||||
return round(self.getter('gain'), 1)
|
||||
|
||||
@gain.setter
|
||||
def gain(self, val: float):
|
||||
self.setter("gain", val)
|
||||
self.setter('gain', val)
|
||||
|
||||
def fadeto(self, target: float, time_: int):
|
||||
self.setter("FadeTo", f"({target}, {time_})")
|
||||
self.setter('FadeTo', f'({target}, {time_})')
|
||||
time.sleep(self._remote.DELAY)
|
||||
|
||||
def fadeby(self, change: float, time_: int):
|
||||
self.setter("FadeBy", f"({change}, {time_})")
|
||||
self.setter('FadeBy', f'({change}, {time_})')
|
||||
time.sleep(self._remote.DELAY)
|
||||
|
||||
|
||||
@@ -90,203 +90,203 @@ class PhysicalStrip(Strip):
|
||||
"""
|
||||
EFFECTS_cls = _make_effects_mixins(is_phys)[remote.kind.name]
|
||||
return type(
|
||||
"PhysicalStrip",
|
||||
'PhysicalStrip',
|
||||
(cls, EFFECTS_cls),
|
||||
{
|
||||
"comp": StripComp(remote, i),
|
||||
"gate": StripGate(remote, i),
|
||||
"denoiser": StripDenoiser(remote, i),
|
||||
"eq": StripEQ(remote, i),
|
||||
"device": StripDevice.make(remote, i),
|
||||
'comp': StripComp(remote, i),
|
||||
'gate': StripGate(remote, i),
|
||||
'denoiser': StripDenoiser(remote, i),
|
||||
'eq': StripEQ(remote, i),
|
||||
'device': StripDevice.make(remote, i),
|
||||
},
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self.index}"
|
||||
return f'{type(self).__name__}{self.index}'
|
||||
|
||||
@property
|
||||
def audibility(self) -> float:
|
||||
return round(self.getter("audibility"), 1)
|
||||
return round(self.getter('audibility'), 1)
|
||||
|
||||
@audibility.setter
|
||||
def audibility(self, val: float):
|
||||
self.setter("audibility", val)
|
||||
self.setter('audibility', val)
|
||||
|
||||
|
||||
class StripComp(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}].comp"
|
||||
return f'Strip[{self.index}].comp'
|
||||
|
||||
@property
|
||||
def knob(self) -> float:
|
||||
return round(self.getter(""), 1)
|
||||
return round(self.getter(''), 1)
|
||||
|
||||
@knob.setter
|
||||
def knob(self, val: float):
|
||||
self.setter("", val)
|
||||
self.setter('', val)
|
||||
|
||||
@property
|
||||
def gainin(self) -> float:
|
||||
return round(self.getter("GainIn"), 1)
|
||||
return round(self.getter('GainIn'), 1)
|
||||
|
||||
@gainin.setter
|
||||
def gainin(self, val: float):
|
||||
self.setter("GainIn", val)
|
||||
self.setter('GainIn', val)
|
||||
|
||||
@property
|
||||
def ratio(self) -> float:
|
||||
return round(self.getter("Ratio"), 1)
|
||||
return round(self.getter('Ratio'), 1)
|
||||
|
||||
@ratio.setter
|
||||
def ratio(self, val: float):
|
||||
self.setter("Ratio", val)
|
||||
self.setter('Ratio', val)
|
||||
|
||||
@property
|
||||
def threshold(self) -> float:
|
||||
return round(self.getter("Threshold"), 1)
|
||||
return round(self.getter('Threshold'), 1)
|
||||
|
||||
@threshold.setter
|
||||
def threshold(self, val: float):
|
||||
self.setter("Threshold", val)
|
||||
self.setter('Threshold', val)
|
||||
|
||||
@property
|
||||
def attack(self) -> float:
|
||||
return round(self.getter("Attack"), 1)
|
||||
return round(self.getter('Attack'), 1)
|
||||
|
||||
@attack.setter
|
||||
def attack(self, val: float):
|
||||
self.setter("Attack", val)
|
||||
self.setter('Attack', val)
|
||||
|
||||
@property
|
||||
def release(self) -> float:
|
||||
return round(self.getter("Release"), 1)
|
||||
return round(self.getter('Release'), 1)
|
||||
|
||||
@release.setter
|
||||
def release(self, val: float):
|
||||
self.setter("Release", val)
|
||||
self.setter('Release', val)
|
||||
|
||||
@property
|
||||
def knee(self) -> float:
|
||||
return round(self.getter("Knee"), 2)
|
||||
return round(self.getter('Knee'), 2)
|
||||
|
||||
@knee.setter
|
||||
def knee(self, val: float):
|
||||
self.setter("Knee", val)
|
||||
self.setter('Knee', val)
|
||||
|
||||
@property
|
||||
def gainout(self) -> float:
|
||||
return round(self.getter("GainOut"), 1)
|
||||
return round(self.getter('GainOut'), 1)
|
||||
|
||||
@gainout.setter
|
||||
def gainout(self, val: float):
|
||||
self.setter("GainOut", val)
|
||||
self.setter('GainOut', val)
|
||||
|
||||
@property
|
||||
def makeup(self) -> bool:
|
||||
return self.getter("makeup") == 1
|
||||
return self.getter('makeup') == 1
|
||||
|
||||
@makeup.setter
|
||||
def makeup(self, val: bool):
|
||||
self.setter("makeup", 1 if val else 0)
|
||||
self.setter('makeup', 1 if val else 0)
|
||||
|
||||
|
||||
class StripGate(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}].gate"
|
||||
return f'Strip[{self.index}].gate'
|
||||
|
||||
@property
|
||||
def knob(self) -> float:
|
||||
return round(self.getter(""), 1)
|
||||
return round(self.getter(''), 1)
|
||||
|
||||
@knob.setter
|
||||
def knob(self, val: float):
|
||||
self.setter("", val)
|
||||
self.setter('', val)
|
||||
|
||||
@property
|
||||
def threshold(self) -> float:
|
||||
return round(self.getter("Threshold"), 1)
|
||||
return round(self.getter('Threshold'), 1)
|
||||
|
||||
@threshold.setter
|
||||
def threshold(self, val: float):
|
||||
self.setter("Threshold", val)
|
||||
self.setter('Threshold', val)
|
||||
|
||||
@property
|
||||
def damping(self) -> float:
|
||||
return round(self.getter("Damping"), 1)
|
||||
return round(self.getter('Damping'), 1)
|
||||
|
||||
@damping.setter
|
||||
def damping(self, val: float):
|
||||
self.setter("Damping", val)
|
||||
self.setter('Damping', val)
|
||||
|
||||
@property
|
||||
def bpsidechain(self) -> int:
|
||||
return int(self.getter("BPSidechain"))
|
||||
return int(self.getter('BPSidechain'))
|
||||
|
||||
@bpsidechain.setter
|
||||
def bpsidechain(self, val: int):
|
||||
self.setter("BPSidechain", val)
|
||||
self.setter('BPSidechain', val)
|
||||
|
||||
@property
|
||||
def attack(self) -> float:
|
||||
return round(self.getter("Attack"), 1)
|
||||
return round(self.getter('Attack'), 1)
|
||||
|
||||
@attack.setter
|
||||
def attack(self, val: float):
|
||||
self.setter("Attack", val)
|
||||
self.setter('Attack', val)
|
||||
|
||||
@property
|
||||
def hold(self) -> float:
|
||||
return round(self.getter("Hold"), 1)
|
||||
return round(self.getter('Hold'), 1)
|
||||
|
||||
@hold.setter
|
||||
def hold(self, val: float):
|
||||
self.setter("Hold", val)
|
||||
self.setter('Hold', val)
|
||||
|
||||
@property
|
||||
def release(self) -> float:
|
||||
return round(self.getter("Release"), 1)
|
||||
return round(self.getter('Release'), 1)
|
||||
|
||||
@release.setter
|
||||
def release(self, val: float):
|
||||
self.setter("Release", val)
|
||||
self.setter('Release', val)
|
||||
|
||||
|
||||
class StripDenoiser(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}].denoiser"
|
||||
return f'Strip[{self.index}].denoiser'
|
||||
|
||||
@property
|
||||
def knob(self) -> float:
|
||||
return round(self.getter(""), 1)
|
||||
return round(self.getter(''), 1)
|
||||
|
||||
@knob.setter
|
||||
def knob(self, val: float):
|
||||
self.setter("", val)
|
||||
self.setter('', val)
|
||||
|
||||
|
||||
class StripEQ(IRemote):
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}].eq"
|
||||
return f'Strip[{self.index}].eq'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on") == 1
|
||||
return self.getter('on') == 1
|
||||
|
||||
@on.setter
|
||||
def on(self, val: bool):
|
||||
self.setter("on", 1 if val else 0)
|
||||
self.setter('on', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def ab(self) -> bool:
|
||||
return self.getter("ab") == 1
|
||||
return self.getter('ab') == 1
|
||||
|
||||
@ab.setter
|
||||
def ab(self, val: bool):
|
||||
self.setter("ab", 1 if val else 0)
|
||||
self.setter('ab', 1 if val else 0)
|
||||
|
||||
|
||||
class StripDevice(IRemote):
|
||||
@@ -298,16 +298,16 @@ class StripDevice(IRemote):
|
||||
Returns a StripDevice class of a kind.
|
||||
"""
|
||||
DEVICE_cls = type(
|
||||
f"StripDevice{remote.kind}",
|
||||
f'StripDevice{remote.kind}',
|
||||
(cls,),
|
||||
{
|
||||
**{
|
||||
param: device_prop(param)
|
||||
for param in [
|
||||
"wdm",
|
||||
"ks",
|
||||
"mme",
|
||||
"asio",
|
||||
'wdm',
|
||||
'ks',
|
||||
'mme',
|
||||
'asio',
|
||||
]
|
||||
},
|
||||
},
|
||||
@@ -316,15 +316,15 @@ class StripDevice(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}].device"
|
||||
return f'Strip[{self.index}].device'
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.getter("name", is_string=True)
|
||||
return self.getter('name', is_string=True)
|
||||
|
||||
@property
|
||||
def sr(self) -> int:
|
||||
return int(self.getter("sr"))
|
||||
return int(self.getter('sr'))
|
||||
|
||||
|
||||
class VirtualStrip(Strip):
|
||||
@@ -337,65 +337,65 @@ class VirtualStrip(Strip):
|
||||
"""
|
||||
EFFECTS_cls = _make_effects_mixins(is_phys)[remote.kind.name]
|
||||
return type(
|
||||
"VirtualStrip",
|
||||
'VirtualStrip',
|
||||
(cls, EFFECTS_cls),
|
||||
{},
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self.index}"
|
||||
return f'{type(self).__name__}{self.index}'
|
||||
|
||||
@property
|
||||
def mc(self) -> bool:
|
||||
return self.getter("mc") == 1
|
||||
return self.getter('mc') == 1
|
||||
|
||||
@mc.setter
|
||||
def mc(self, val: bool):
|
||||
self.setter("mc", 1 if val else 0)
|
||||
self.setter('mc', 1 if val else 0)
|
||||
|
||||
mono = mc
|
||||
|
||||
@property
|
||||
def k(self) -> int:
|
||||
return int(self.getter("karaoke"))
|
||||
return int(self.getter('karaoke'))
|
||||
|
||||
@k.setter
|
||||
def k(self, val: int):
|
||||
self.setter("karaoke", val)
|
||||
self.setter('karaoke', val)
|
||||
|
||||
@property
|
||||
def bass(self) -> float:
|
||||
return round(self.getter("EQGain1"), 1)
|
||||
return round(self.getter('EQGain1'), 1)
|
||||
|
||||
@bass.setter
|
||||
def bass(self, val: float):
|
||||
self.setter("EQGain1", val)
|
||||
self.setter('EQGain1', val)
|
||||
|
||||
@property
|
||||
def mid(self) -> float:
|
||||
return round(self.getter("EQGain2"), 1)
|
||||
return round(self.getter('EQGain2'), 1)
|
||||
|
||||
@mid.setter
|
||||
def mid(self, val: float):
|
||||
self.setter("EQGain2", val)
|
||||
self.setter('EQGain2', val)
|
||||
|
||||
med = mid
|
||||
|
||||
@property
|
||||
def treble(self) -> float:
|
||||
return round(self.getter("EQGain3"), 1)
|
||||
return round(self.getter('EQGain3'), 1)
|
||||
|
||||
high = treble
|
||||
|
||||
@treble.setter
|
||||
def treble(self, val: float):
|
||||
self.setter("EQGain3", val)
|
||||
self.setter('EQGain3', val)
|
||||
|
||||
def appgain(self, name: str, gain: float):
|
||||
self.setter("AppGain", f'("{name}", {gain})')
|
||||
self.setter('AppGain', f'("{name}", {gain})')
|
||||
|
||||
def appmute(self, name: str, mute: bool = None):
|
||||
self.setter("AppMute", f'("{name}", {1 if mute else 0})')
|
||||
self.setter('AppMute', f'("{name}", {1 if mute else 0})')
|
||||
|
||||
|
||||
class StripLevel(IRemote):
|
||||
@@ -416,7 +416,7 @@ class StripLevel(IRemote):
|
||||
return round(20 * log(x, 10), 1) if x > 0 else -200.0
|
||||
|
||||
if not self._remote.stopped() and self._remote.event.ldirty:
|
||||
vals = self._remote.cache["strip_level"][self.range[0] : self.range[-1]]
|
||||
vals = self._remote.cache['strip_level'][self.range[0] : self.range[-1]]
|
||||
else:
|
||||
vals = [self._remote.get_level(mode, i) for i in range(*self.range)]
|
||||
|
||||
@@ -424,7 +424,7 @@ class StripLevel(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}]"
|
||||
return f'Strip[{self.index}]'
|
||||
|
||||
@property
|
||||
def prefader(self) -> tuple:
|
||||
@@ -467,7 +467,7 @@ def make_strip_level_map(kind):
|
||||
return phys_map + virt_map
|
||||
|
||||
|
||||
_make_strip_level_maps = {kind.name: make_strip_level_map(kind) for kind in kinds_all}
|
||||
_make_strip_level_maps = {kind.name: make_strip_level_map(kind) for kind in kinds.all}
|
||||
|
||||
|
||||
class GainLayer(IRemote):
|
||||
@@ -477,24 +477,24 @@ class GainLayer(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"Strip[{self.index}]"
|
||||
return f'Strip[{self.index}]'
|
||||
|
||||
@property
|
||||
def gain(self):
|
||||
return self.getter(f"GainLayer[{self._i}]")
|
||||
return self.getter(f'GainLayer[{self._i}]')
|
||||
|
||||
@gain.setter
|
||||
def gain(self, val):
|
||||
self.setter(f"GainLayer[{self._i}]", val)
|
||||
self.setter(f'GainLayer[{self._i}]', val)
|
||||
|
||||
|
||||
def _make_gainlayer_mixin(remote, index):
|
||||
"""Creates a GainLayer mixin"""
|
||||
return type(
|
||||
"GainlayerMixin",
|
||||
'GainlayerMixin',
|
||||
(),
|
||||
{
|
||||
"gainlayer": tuple(
|
||||
'gainlayer': tuple(
|
||||
GainLayer(remote, index, i) for i in range(remote.kind.num_bus)
|
||||
)
|
||||
},
|
||||
@@ -504,17 +504,17 @@ def _make_gainlayer_mixin(remote, index):
|
||||
def _make_channelout_mixin(kind):
|
||||
"""Creates a channel out property mixin"""
|
||||
return type(
|
||||
f"ChannelOutMixin{kind}",
|
||||
f'ChannelOutMixin{kind}',
|
||||
(),
|
||||
{
|
||||
**{f"A{i}": bool_prop(f"A{i}") for i in range(1, kind.phys_out + 1)},
|
||||
**{f"B{i}": bool_prop(f"B{i}") for i in range(1, kind.virt_out + 1)},
|
||||
**{f'A{i}': bool_prop(f'A{i}') for i in range(1, kind.phys_out + 1)},
|
||||
**{f'B{i}': bool_prop(f'B{i}') for i in range(1, kind.virt_out + 1)},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
_make_channelout_mixins = {
|
||||
kind.name: _make_channelout_mixin(kind) for kind in kinds_all
|
||||
kind.name: _make_channelout_mixin(kind) for kind in kinds.all
|
||||
}
|
||||
|
||||
|
||||
@@ -522,12 +522,12 @@ def _make_effects_mixin(kind, is_phys):
|
||||
"""creates an effects mixin for a kind"""
|
||||
|
||||
def _make_xy_cls():
|
||||
pan = {param: float_prop(param) for param in ["pan_x", "pan_y"]}
|
||||
color = {param: float_prop(param) for param in ["color_x", "color_y"]}
|
||||
fx = {param: float_prop(param) for param in ["fx_x", "fx_y"]}
|
||||
pan = {param: float_prop(param) for param in ['pan_x', 'pan_y']}
|
||||
color = {param: float_prop(param) for param in ['color_x', 'color_y']}
|
||||
fx = {param: float_prop(param) for param in ['fx_x', 'fx_y']}
|
||||
if is_phys:
|
||||
return type(
|
||||
"XYPhys",
|
||||
'XYPhys',
|
||||
(),
|
||||
{
|
||||
**pan,
|
||||
@@ -536,7 +536,7 @@ def _make_effects_mixin(kind, is_phys):
|
||||
},
|
||||
)
|
||||
return type(
|
||||
"XYVirt",
|
||||
'XYVirt',
|
||||
(),
|
||||
{**pan},
|
||||
)
|
||||
@@ -544,32 +544,32 @@ def _make_effects_mixin(kind, is_phys):
|
||||
def _make_fx_cls():
|
||||
if is_phys:
|
||||
return type(
|
||||
"FX",
|
||||
'FX',
|
||||
(),
|
||||
{
|
||||
**{
|
||||
param: float_prop(param)
|
||||
for param in ["reverb", "delay", "fx1", "fx2"]
|
||||
for param in ['reverb', 'delay', 'fx1', 'fx2']
|
||||
},
|
||||
**{
|
||||
f"post{param}": bool_prop(f"post{param}")
|
||||
for param in ["reverb", "delay", "fx1", "fx2"]
|
||||
f'post{param}': bool_prop(f'post{param}')
|
||||
for param in ['reverb', 'delay', 'fx1', 'fx2']
|
||||
},
|
||||
},
|
||||
)
|
||||
return type("FX", (), {})
|
||||
return type('FX', (), {})
|
||||
|
||||
if kind.name == "basic":
|
||||
if kind.name == 'basic':
|
||||
steps = (_make_xy_cls,)
|
||||
elif kind.name == "banana":
|
||||
elif kind.name == 'banana':
|
||||
steps = (_make_xy_cls,)
|
||||
elif kind.name == "potato":
|
||||
elif kind.name == 'potato':
|
||||
steps = (_make_xy_cls, _make_fx_cls)
|
||||
return type(f"Effects{kind}", tuple(step() for step in steps), {})
|
||||
return type(f'Effects{kind}', tuple(step() for step in steps), {})
|
||||
|
||||
|
||||
def _make_effects_mixins(is_phys):
|
||||
return {kind.name: _make_effects_mixin(kind, is_phys) for kind in kinds_all}
|
||||
return {kind.name: _make_effects_mixin(kind, is_phys) for kind in kinds.all}
|
||||
|
||||
|
||||
def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip]:
|
||||
@@ -588,14 +588,14 @@ def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip
|
||||
CHANNELOUTMIXIN_cls = _make_channelout_mixins[remote.kind.name]
|
||||
|
||||
_kls = (STRIP_cls, CHANNELOUTMIXIN_cls)
|
||||
if remote.kind.name == "potato":
|
||||
if remote.kind.name == 'potato':
|
||||
GAINLAYERMIXIN_cls = _make_gainlayer_mixin(remote, i)
|
||||
_kls += (GAINLAYERMIXIN_cls,)
|
||||
return type(
|
||||
f"{STRIP_cls.__name__}{remote.kind}",
|
||||
f'{STRIP_cls.__name__}{remote.kind}',
|
||||
_kls,
|
||||
{
|
||||
"levels": StripLevel(remote, i),
|
||||
'levels': StripLevel(remote, i),
|
||||
},
|
||||
)(remote, i)
|
||||
|
||||
|
||||
@@ -20,10 +20,10 @@ class Subject:
|
||||
"""run callbacks on update"""
|
||||
|
||||
for o in self._observers:
|
||||
if hasattr(o, "on_update"):
|
||||
if hasattr(o, 'on_update'):
|
||||
o.on_update(event)
|
||||
else:
|
||||
if o.__name__ == f"on_{event}":
|
||||
if o.__name__ == f'on_{event}':
|
||||
o()
|
||||
|
||||
def add(self, observer):
|
||||
@@ -34,15 +34,15 @@ class Subject:
|
||||
for o in iterator:
|
||||
if o not in self._observers:
|
||||
self._observers.append(o)
|
||||
self.logger.info(f"{o} added to event observers")
|
||||
self.logger.info(f'{o} added to event observers')
|
||||
else:
|
||||
self.logger.error(f"Failed to add {o} to event observers")
|
||||
self.logger.error(f'Failed to add {o} to event observers')
|
||||
except TypeError:
|
||||
if observer not in self._observers:
|
||||
self._observers.append(observer)
|
||||
self.logger.info(f"{observer} added to event observers")
|
||||
self.logger.info(f'{observer} added to event observers')
|
||||
else:
|
||||
self.logger.error(f"Failed to add {observer} to event observers")
|
||||
self.logger.error(f'Failed to add {observer} to event observers')
|
||||
|
||||
register = add
|
||||
|
||||
@@ -54,15 +54,15 @@ class Subject:
|
||||
for o in iterator:
|
||||
try:
|
||||
self._observers.remove(o)
|
||||
self.logger.info(f"{o} removed from event observers")
|
||||
self.logger.info(f'{o} removed from event observers')
|
||||
except ValueError:
|
||||
self.logger.error(f"Failed to remove {o} from event observers")
|
||||
self.logger.error(f'Failed to remove {o} from event observers')
|
||||
except TypeError:
|
||||
try:
|
||||
self._observers.remove(observer)
|
||||
self.logger.info(f"{observer} removed from event observers")
|
||||
self.logger.info(f'{observer} removed from event observers')
|
||||
except ValueError:
|
||||
self.logger.error(f"Failed to remove {observer} from event observers")
|
||||
self.logger.error(f'Failed to remove {observer} from event observers')
|
||||
|
||||
deregister = remove
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class Producer(threading.Thread):
|
||||
"""Continously send job queue to the Updater thread at a rate of self._remote.ratelimit."""
|
||||
|
||||
def __init__(self, remote, queue, stop_event):
|
||||
super().__init__(name="producer", daemon=False)
|
||||
super().__init__(name='producer', daemon=False)
|
||||
self._remote = remote
|
||||
self.queue = queue
|
||||
self.stop_event = stop_event
|
||||
@@ -23,35 +23,35 @@ class Producer(threading.Thread):
|
||||
def run(self):
|
||||
while not self.stopped():
|
||||
if self._remote.event.pdirty:
|
||||
self.queue.put("pdirty")
|
||||
self.queue.put('pdirty')
|
||||
if self._remote.event.mdirty:
|
||||
self.queue.put("mdirty")
|
||||
self.queue.put('mdirty')
|
||||
if self._remote.event.midi:
|
||||
self.queue.put("midi")
|
||||
self.queue.put('midi')
|
||||
if self._remote.event.ldirty:
|
||||
self.queue.put("ldirty")
|
||||
self.queue.put('ldirty')
|
||||
time.sleep(self._remote.ratelimit)
|
||||
self.logger.debug(f"terminating {self.name} thread")
|
||||
self.logger.debug(f'terminating {self.name} thread')
|
||||
self.queue.put(None)
|
||||
|
||||
|
||||
class Updater(threading.Thread):
|
||||
def __init__(self, remote, queue):
|
||||
super().__init__(name="updater", daemon=True)
|
||||
super().__init__(name='updater', daemon=True)
|
||||
self._remote = remote
|
||||
self.queue = queue
|
||||
self._remote._strip_comp = [False] * (self._remote.kind.num_strip_levels)
|
||||
self._remote._bus_comp = [False] * (self._remote.kind.num_bus_levels)
|
||||
(
|
||||
self._remote.cache["strip_level"],
|
||||
self._remote.cache["bus_level"],
|
||||
self._remote.cache['strip_level'],
|
||||
self._remote.cache['bus_level'],
|
||||
) = self._remote._get_levels()
|
||||
self.logger = logger.getChild(self.__class__.__name__)
|
||||
|
||||
def _update_comps(self, strip_level, bus_level):
|
||||
self._remote._strip_comp, self._remote._bus_comp = (
|
||||
tuple(not x for x in comp(self._remote.cache["strip_level"], strip_level)),
|
||||
tuple(not x for x in comp(self._remote.cache["bus_level"], bus_level)),
|
||||
tuple(not x for x in comp(self._remote.cache['strip_level'], strip_level)),
|
||||
tuple(not x for x in comp(self._remote.cache['bus_level'], bus_level)),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
@@ -61,15 +61,15 @@ class Updater(threading.Thread):
|
||||
Generate _strip_comp, _bus_comp and update level cache if ldirty.
|
||||
"""
|
||||
while event := self.queue.get():
|
||||
if event == "pdirty" and self._remote.pdirty:
|
||||
if event == 'pdirty' and self._remote.pdirty:
|
||||
self._remote.subject.notify(event)
|
||||
elif event == "mdirty" and self._remote.mdirty:
|
||||
elif event == 'mdirty' and self._remote.mdirty:
|
||||
self._remote.subject.notify(event)
|
||||
elif event == "midi" and self._remote.get_midi_message():
|
||||
elif event == 'midi' and self._remote.get_midi_message():
|
||||
self._remote.subject.notify(event)
|
||||
elif event == "ldirty" and self._remote.ldirty:
|
||||
elif event == 'ldirty' and self._remote.ldirty:
|
||||
self._update_comps(self._remote._strip_buf, self._remote._bus_buf)
|
||||
self._remote.cache["strip_level"] = self._remote._strip_buf
|
||||
self._remote.cache["bus_level"] = self._remote._bus_buf
|
||||
self._remote.cache['strip_level'] = self._remote._strip_buf
|
||||
self._remote.cache['bus_level'] = self._remote._bus_buf
|
||||
self._remote.subject.notify(event)
|
||||
self.logger.debug(f"terminating {self.name} thread")
|
||||
self.logger.debug(f'terminating {self.name} thread')
|
||||
|
||||
@@ -22,16 +22,16 @@ def timeout(func):
|
||||
try:
|
||||
time.sleep(0.1) # ensure at least 0.1 delay before clearing dirty
|
||||
remote.logger.info(
|
||||
f"{type(remote).__name__}: Successfully logged into {remote} version {remote.version}"
|
||||
f'{type(remote).__name__}: Successfully logged into {remote} version {remote.version}'
|
||||
)
|
||||
remote.logger.debug(f"login time: {round(time.time() - start, 2)}")
|
||||
remote.logger.debug(f'login time: {round(time.time() - start, 2)}')
|
||||
err = None
|
||||
break
|
||||
except CAPIError as e:
|
||||
err = e
|
||||
continue
|
||||
if err:
|
||||
raise VMError("Timeout logging into the api")
|
||||
raise VMError('Timeout logging into the api')
|
||||
remote.clear_dirty()
|
||||
|
||||
return wrapper
|
||||
@@ -48,15 +48,15 @@ def polling(func):
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
get = func.__name__ == "get"
|
||||
mb_get = func.__name__ == "get_buttonstatus"
|
||||
get = func.__name__ == 'get'
|
||||
mb_get = func.__name__ == 'get_buttonstatus'
|
||||
remote, *remaining = args
|
||||
|
||||
if get:
|
||||
param, *rem = remaining
|
||||
elif mb_get:
|
||||
id, mode, *rem = remaining
|
||||
param = f"mb_{id}_{mode}"
|
||||
param = f'mb_{id}_{mode}'
|
||||
|
||||
if param in remote.cache:
|
||||
return remote.cache.pop(param)
|
||||
@@ -73,15 +73,15 @@ def script(func):
|
||||
def wrapper(*args):
|
||||
remote, script = args
|
||||
if isinstance(script, dict):
|
||||
params = ""
|
||||
params = ''
|
||||
for key, val in script.items():
|
||||
obj, m2, *rem = key.split("-")
|
||||
obj, m2, *rem = key.split('-')
|
||||
index = int(m2) if m2.isnumeric() else int(*rem)
|
||||
params += ";".join(
|
||||
params += ';'.join(
|
||||
f"{obj}{f'.{m2}stream' if not m2.isnumeric() else ''}[{index}].{k}={int(v) if isinstance(v, bool) else v}"
|
||||
for k, v in val.items()
|
||||
)
|
||||
params += ";"
|
||||
params += ';'
|
||||
script = params
|
||||
return func(remote, script)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from abc import abstractmethod
|
||||
|
||||
from . import kinds
|
||||
from .iremote import IRemote
|
||||
from .kinds import kinds_all
|
||||
|
||||
|
||||
class VbanStream(IRemote):
|
||||
@@ -17,94 +17,94 @@ class VbanStream(IRemote):
|
||||
|
||||
@property
|
||||
def identifier(self) -> str:
|
||||
return f"vban.{self.direction}stream[{self.index}]"
|
||||
return f'vban.{self.direction}stream[{self.index}]'
|
||||
|
||||
@property
|
||||
def on(self) -> bool:
|
||||
return self.getter("on") == 1
|
||||
return self.getter('on') == 1
|
||||
|
||||
@on.setter
|
||||
def on(self, val: bool):
|
||||
self.setter("on", 1 if val else 0)
|
||||
self.setter('on', 1 if val else 0)
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.getter("name", is_string=True)
|
||||
return self.getter('name', is_string=True)
|
||||
|
||||
@name.setter
|
||||
def name(self, val: str):
|
||||
self.setter("name", val)
|
||||
self.setter('name', val)
|
||||
|
||||
@property
|
||||
def ip(self) -> str:
|
||||
return self.getter("ip", is_string=True)
|
||||
return self.getter('ip', is_string=True)
|
||||
|
||||
@ip.setter
|
||||
def ip(self, val: str):
|
||||
self.setter("ip", val)
|
||||
self.setter('ip', val)
|
||||
|
||||
@property
|
||||
def port(self) -> int:
|
||||
return int(self.getter("port"))
|
||||
return int(self.getter('port'))
|
||||
|
||||
@port.setter
|
||||
def port(self, val: int):
|
||||
if not 1024 <= val <= 65535:
|
||||
self.logger.warning(
|
||||
f"port got: {val} but expected a value from 1024 to 65535"
|
||||
f'port got: {val} but expected a value from 1024 to 65535'
|
||||
)
|
||||
self.setter("port", val)
|
||||
self.setter('port', val)
|
||||
|
||||
@property
|
||||
def sr(self) -> int:
|
||||
return int(self.getter("sr"))
|
||||
return int(self.getter('sr'))
|
||||
|
||||
@sr.setter
|
||||
def sr(self, val: int):
|
||||
opts = (11025, 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000)
|
||||
if val not in opts:
|
||||
self.logger.warning(f"sr got: {val} but expected a value in {opts}")
|
||||
self.setter("sr", val)
|
||||
self.logger.warning(f'sr got: {val} but expected a value in {opts}')
|
||||
self.setter('sr', val)
|
||||
|
||||
@property
|
||||
def channel(self) -> int:
|
||||
return int(self.getter("channel"))
|
||||
return int(self.getter('channel'))
|
||||
|
||||
@channel.setter
|
||||
def channel(self, val: int):
|
||||
if not 1 <= val <= 8:
|
||||
self.logger.warning(f"channel got: {val} but expected a value from 1 to 8")
|
||||
self.setter("channel", val)
|
||||
self.logger.warning(f'channel got: {val} but expected a value from 1 to 8')
|
||||
self.setter('channel', val)
|
||||
|
||||
@property
|
||||
def bit(self) -> int:
|
||||
return 16 if (int(self.getter("bit") == 1)) else 24
|
||||
return 16 if (int(self.getter('bit') == 1)) else 24
|
||||
|
||||
@bit.setter
|
||||
def bit(self, val: int):
|
||||
if val not in (16, 24):
|
||||
self.logger.warning(f"bit got: {val} but expected value 16 or 24")
|
||||
self.setter("bit", 1 if (val == 16) else 2)
|
||||
self.logger.warning(f'bit got: {val} but expected value 16 or 24')
|
||||
self.setter('bit', 1 if (val == 16) else 2)
|
||||
|
||||
@property
|
||||
def quality(self) -> int:
|
||||
return int(self.getter("quality"))
|
||||
return int(self.getter('quality'))
|
||||
|
||||
@quality.setter
|
||||
def quality(self, val: int):
|
||||
if not 0 <= val <= 4:
|
||||
self.logger.warning(f"quality got: {val} but expected a value from 0 to 4")
|
||||
self.setter("quality", val)
|
||||
self.logger.warning(f'quality got: {val} but expected a value from 0 to 4')
|
||||
self.setter('quality', val)
|
||||
|
||||
@property
|
||||
def route(self) -> int:
|
||||
return int(self.getter("route"))
|
||||
return int(self.getter('route'))
|
||||
|
||||
@route.setter
|
||||
def route(self, val: int):
|
||||
if not 0 <= val <= 8:
|
||||
self.logger.warning(f"route got: {val} but expected a value from 0 to 8")
|
||||
self.setter("route", val)
|
||||
self.logger.warning(f'route got: {val} but expected a value from 0 to 8')
|
||||
self.setter('route', val)
|
||||
|
||||
|
||||
class VbanInstream(VbanStream):
|
||||
@@ -115,11 +115,11 @@ class VbanInstream(VbanStream):
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self._remote.kind}{self.index}"
|
||||
return f'{type(self).__name__}{self._remote.kind}{self.index}'
|
||||
|
||||
@property
|
||||
def direction(self) -> str:
|
||||
return "in"
|
||||
return 'in'
|
||||
|
||||
@property
|
||||
def sr(self) -> int:
|
||||
@@ -154,11 +154,11 @@ class VbanOutstream(VbanStream):
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
return f"{type(self).__name__}{self._remote.kind}{self.index}"
|
||||
return f'{type(self).__name__}{self._remote.kind}{self.index}'
|
||||
|
||||
@property
|
||||
def direction(self) -> str:
|
||||
return "out"
|
||||
return 'out'
|
||||
|
||||
|
||||
class VbanAudioOutstream(VbanOutstream):
|
||||
@@ -174,27 +174,27 @@ def _make_stream_pair(remote, kind):
|
||||
|
||||
def _make_cls(i, direction):
|
||||
match direction:
|
||||
case "in":
|
||||
case 'in':
|
||||
if i < num_instream:
|
||||
return VbanAudioInstream(remote, i)
|
||||
elif i < num_instream + num_midi:
|
||||
return VbanMidiInstream(remote, i)
|
||||
else:
|
||||
return VbanTextInstream(remote, i)
|
||||
case "out":
|
||||
case 'out':
|
||||
if i < num_outstream:
|
||||
return VbanAudioOutstream(remote, i)
|
||||
else:
|
||||
return VbanMidiOutstream(remote, i)
|
||||
|
||||
return (
|
||||
tuple(_make_cls(i, "in") for i in range(num_instream + num_midi + num_text)),
|
||||
tuple(_make_cls(i, "out") for i in range(num_outstream + num_midi)),
|
||||
tuple(_make_cls(i, 'in') for i in range(num_instream + num_midi + num_text)),
|
||||
tuple(_make_cls(i, 'out') for i in range(num_outstream + num_midi)),
|
||||
)
|
||||
|
||||
|
||||
def _make_stream_pairs(remote):
|
||||
return {kind.name: _make_stream_pair(remote, kind) for kind in kinds_all}
|
||||
return {kind.name: _make_stream_pair(remote, kind) for kind in kinds.all}
|
||||
|
||||
|
||||
class Vban:
|
||||
@@ -209,10 +209,10 @@ class Vban:
|
||||
self.instream, self.outstream = _make_stream_pairs(remote)[remote.kind.name]
|
||||
|
||||
def enable(self):
|
||||
self.remote.set("vban.Enable", 1)
|
||||
self.remote.set('vban.Enable', 1)
|
||||
|
||||
def disable(self):
|
||||
self.remote.set("vban.Enable", 0)
|
||||
self.remote.set('vban.Enable', 0)
|
||||
|
||||
|
||||
def vban_factory(remote) -> Vban:
|
||||
@@ -222,7 +222,7 @@ def vban_factory(remote) -> Vban:
|
||||
Returns a class that represents the VBAN module.
|
||||
"""
|
||||
VBAN_cls = Vban
|
||||
return type(f"{VBAN_cls.__name__}", (VBAN_cls,), {})(remote)
|
||||
return type(f'{VBAN_cls.__name__}', (VBAN_cls,), {})(remote)
|
||||
|
||||
|
||||
def request_vban_obj(remote) -> Vban:
|
||||
|
||||
Reference in New Issue
Block a user