mirror of
https://github.com/onyx-and-iris/nvda-voicemeeter.git
synced 2026-04-07 18:03:35 +00:00
Compare commits
36 Commits
9d8ea5f747
...
v0.6.3
| Author | SHA1 | Date | |
|---|---|---|---|
| 79f739f250 | |||
| b45b2da4aa | |||
| 0adfec2e63 | |||
| dacc972b17 | |||
| 2ed1cad666 | |||
| 64361b2011 | |||
| 36003fe73f | |||
| cb82033e1c | |||
| bde9020471 | |||
| 364b0deeb4 | |||
| a80e4e2d83 | |||
| 6e146bac50 | |||
| c385476cc4 | |||
| 1c09556c61 | |||
| 421688eff8 | |||
| bdd570738a | |||
| 71b137a9c2 | |||
| 912eb8c14d | |||
| 9cd65737e5 | |||
| 4a6ca2a353 | |||
| cc99b14e89 | |||
| 23458debaa | |||
| 496cc35321 | |||
| d758db9dee | |||
| 876de55ad2 | |||
| eab4b1c6a9 | |||
| 0aeb33608f | |||
| 5befe72ca1 | |||
| abab560281 | |||
| 6882adb47b | |||
| af602e087d | |||
| 01e80dc4f6 | |||
| 7262af4bcf | |||
| ef10b224d7 | |||
| 89d0253591 | |||
| 85527e0749 |
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"black-formatter.args": [
|
|
||||||
"--line-length=120"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
31
README.md
31
README.md
@@ -1,6 +1,7 @@
|
|||||||
[](https://pdm.fming.dev)
|
[](https://pdm.fming.dev)
|
||||||
[](https://github.com/psf/black)
|
[](https://github.com/psf/black)
|
||||||
[](https://pycqa.github.io/isort/)
|
[](https://pycqa.github.io/isort/)
|
||||||
|
[](https://github.com/astral-sh/ruff)
|
||||||
|
|
||||||
# NVDA Voicemeeter
|
# NVDA Voicemeeter
|
||||||
|
|
||||||
@@ -106,15 +107,35 @@ All sliders may be controlled in three different ways:
|
|||||||
- `Shift + Left|Right arrow` to move a slider by 0.1 steps.
|
- `Shift + Left|Right arrow` to move a slider by 0.1 steps.
|
||||||
- `Control + Left|Right arrow` to move a slider by 3 steps.
|
- `Control + Left|Right arrow` to move a slider by 3 steps.
|
||||||
|
|
||||||
|
To reset a slider back to its default value you may use `Control + Shift + R`.
|
||||||
|
|
||||||
To rename a strip/bus channel focus on the channel in question and press `F2`. Then enter the new channel name into the text input widget and press the `Ok` button.
|
To rename a strip/bus channel focus on the channel in question and press `F2`. Then enter the new channel name into the text input widget and press the `Ok` button.
|
||||||
|
|
||||||
Pressing the `OK` button with an empty text input will clear the label. In this case the label will be read as a default value for that channel. For example, if the leftmost Strip label were cleared, the screen reader will now read `Hardware Input 1`.
|
Pressing the `OK` button with an empty text input will clear the label. In this case the label will be read as a default value for that channel. For example, if the leftmost Strip label were cleared, the screen reader will now read `Hardware Input 1`.
|
||||||
|
|
||||||
Pressing `Cancel` will close the popup window with no affect on the label.
|
Pressing `Cancel` will close the popup window with no affect on the label.
|
||||||
|
|
||||||
#### `Menu`
|
#### `Advanced Compressor|Gate`
|
||||||
|
|
||||||
A single menu item `Voicemeeter` can be opened using `Alt` and then `v`. The menu allows you to:
|
For potato version only, you may access advanced Compressor and Gate sliders. Simply focus any Gate or Compressor slider and press `Control + A`. This will open a popup window where you can navigate between the different sliders with `TAB`. Move the sliders with the same binds you would for normal sliders. However, there are a couple of extra binds for certain controls.
|
||||||
|
|
||||||
|
For Compressor Release you may use:
|
||||||
|
|
||||||
|
- `Alt + Left|Right arrow` to move the slider by 10 steps.
|
||||||
|
- `Alt + Control + Left|Right arrow` to move the slider by 50 steps.
|
||||||
|
|
||||||
|
For Gate BP Sidechain, Attack, Hold, Release you may use:
|
||||||
|
|
||||||
|
- `Alt + Left|Right arrow` to move the slider by 10 steps.
|
||||||
|
- `Alt + Control + Left|Right arrow` to move the slider by 50 steps.
|
||||||
|
|
||||||
|
To reset a slider back to its default value you may use `Control + Shift + R`.
|
||||||
|
|
||||||
|
### Menu
|
||||||
|
|
||||||
|
#### `Voicemeeter`
|
||||||
|
|
||||||
|
The `Voicemeeter` menu can be opened using `Alt` and then `v`. It offers the following options:
|
||||||
|
|
||||||
- Restart Voicemeeter audio engine
|
- Restart Voicemeeter audio engine
|
||||||
- Save/Load current settings (as an xml file)
|
- Save/Load current settings (as an xml file)
|
||||||
@@ -124,7 +145,11 @@ The `Save Settings` option opens a popup window with two buttons, `Browse` and `
|
|||||||
|
|
||||||
`Load Settings` and `Load on Startup` both open an Open dialog box immediately.
|
`Load Settings` and `Load on Startup` both open an Open dialog box immediately.
|
||||||
|
|
||||||
### `Quick access binds`
|
#### `Theme`
|
||||||
|
|
||||||
|
The `Theme` menu can be opened using `Alt` and then `t`. Use this menu to select from a list of coloured themes. Some themes offer higher contrast colours. An application restart is required to load a new theme. Once a theme is selected it will become the default for future startups.
|
||||||
|
|
||||||
|
### Quick access binds
|
||||||
|
|
||||||
There are a number of quick binds available to assist with faster navigation and general use.
|
There are a number of quick binds available to assist with faster navigation and general use.
|
||||||
|
|
||||||
|
|||||||
BIN
img/busmode_buttonmenu.png
Normal file
BIN
img/busmode_buttonmenu.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 777 KiB After Width: | Height: | Size: 234 KiB |
BIN
img/settings.png
BIN
img/settings.png
Binary file not shown.
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
159
pdm.lock
generated
159
pdm.lock
generated
@@ -3,10 +3,9 @@
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
groups = ["default", "build", "lint", "test"]
|
groups = ["default", "build", "lint", "test"]
|
||||||
cross_platform = true
|
strategy = ["cross_platform"]
|
||||||
static_urls = false
|
lock_version = "4.4.1"
|
||||||
lock_version = "4.3"
|
content_hash = "sha256:6be74981983ee2ba3a99c9aa75b10f6c7433fec0854390f82029635cb34d3e0b"
|
||||||
content_hash = "sha256:ba53368b628b713c9cf4eb54e6f5c5c4af207c8e247d473417e4c2a4b47f645d"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "altgraph"
|
name = "altgraph"
|
||||||
@@ -19,7 +18,7 @@ files = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "black"
|
name = "black"
|
||||||
version = "23.7.0"
|
version = "24.1.1"
|
||||||
requires_python = ">=3.8"
|
requires_python = ">=3.8"
|
||||||
summary = "The uncompromising code formatter."
|
summary = "The uncompromising code formatter."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -29,15 +28,19 @@ dependencies = [
|
|||||||
"pathspec>=0.9.0",
|
"pathspec>=0.9.0",
|
||||||
"platformdirs>=2",
|
"platformdirs>=2",
|
||||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||||
|
"typing-extensions>=4.0.1; python_version < \"3.11\"",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
|
{file = "black-24.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2588021038bd5ada078de606f2a804cadd0a3cc6a79cb3e9bb3a8bf581325a4c"},
|
||||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
|
{file = "black-24.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a95915c98d6e32ca43809d46d932e2abc5f1f7d582ffbe65a5b4d1588af7445"},
|
||||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
|
{file = "black-24.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fa6a0e965779c8f2afb286f9ef798df770ba2b6cee063c650b96adec22c056a"},
|
||||||
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
|
{file = "black-24.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5242ecd9e990aeb995b6d03dc3b2d112d4a78f2083e5a8e86d566340ae80fec4"},
|
||||||
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
|
{file = "black-24.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fc1ec9aa6f4d98d022101e015261c056ddebe3da6a8ccfc2c792cbe0349d48b7"},
|
||||||
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
|
{file = "black-24.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0269dfdea12442022e88043d2910429bed717b2d04523867a85dacce535916b8"},
|
||||||
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
|
{file = "black-24.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3d64db762eae4a5ce04b6e3dd745dcca0fb9560eb931a5be97472e38652a161"},
|
||||||
|
{file = "black-24.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:5d7b06ea8816cbd4becfe5f70accae953c53c0e53aa98730ceccb0395520ee5d"},
|
||||||
|
{file = "black-24.1.1-py3-none-any.whl", hash = "sha256:5cdc2e2195212208fbcae579b931407c1fa9997584f0a415421748aeafff1168"},
|
||||||
|
{file = "black-24.1.1.tar.gz", hash = "sha256:48b5760dcbfe5cf97fd4fba23946681f3a81514c6ab8a45b50da67ac8fbc6c7b"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -63,21 +66,6 @@ files = [
|
|||||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "flake8"
|
|
||||||
version = "6.1.0"
|
|
||||||
requires_python = ">=3.8.1"
|
|
||||||
summary = "the modular source code checker: pep8 pyflakes and co"
|
|
||||||
dependencies = [
|
|
||||||
"mccabe<0.8.0,>=0.7.0",
|
|
||||||
"pycodestyle<2.12.0,>=2.11.0",
|
|
||||||
"pyflakes<3.2.0,>=3.1.0",
|
|
||||||
]
|
|
||||||
files = [
|
|
||||||
{file = "flake8-6.1.0-py2.py3-none-any.whl", hash = "sha256:ffdfce58ea94c6580c77888a86506937f9a1a227dfcd15f245d694ae20a6b6e5"},
|
|
||||||
{file = "flake8-6.1.0.tar.gz", hash = "sha256:d5b3857f07c030bdb5bf41c7f53799571d75c4491748a3adcd47de929e34cd23"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "macholib"
|
name = "macholib"
|
||||||
version = "1.16.2"
|
version = "1.16.2"
|
||||||
@@ -91,13 +79,28 @@ files = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mccabe"
|
name = "mypy"
|
||||||
version = "0.7.0"
|
version = "1.8.0"
|
||||||
requires_python = ">=3.6"
|
requires_python = ">=3.8"
|
||||||
summary = "McCabe checker, plugin for flake8"
|
summary = "Optional static typing for Python"
|
||||||
|
dependencies = [
|
||||||
|
"mypy-extensions>=1.0.0",
|
||||||
|
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||||
|
"typing-extensions>=4.1.0",
|
||||||
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"},
|
{file = "mypy-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485a8942f671120f76afffff70f259e1cd0f0cfe08f81c05d8816d958d4577d3"},
|
||||||
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
|
{file = "mypy-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:df9824ac11deaf007443e7ed2a4a26bebff98d2bc43c6da21b2b64185da011c4"},
|
||||||
|
{file = "mypy-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2afecd6354bbfb6e0160f4e4ad9ba6e4e003b767dd80d85516e71f2e955ab50d"},
|
||||||
|
{file = "mypy-1.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8963b83d53ee733a6e4196954502b33567ad07dfd74851f32be18eb932fb1cb9"},
|
||||||
|
{file = "mypy-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e46f44b54ebddbeedbd3d5b289a893219065ef805d95094d16a0af6630f5d410"},
|
||||||
|
{file = "mypy-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:855fe27b80375e5c5878492f0729540db47b186509c98dae341254c8f45f42ae"},
|
||||||
|
{file = "mypy-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4c886c6cce2d070bd7df4ec4a05a13ee20c0aa60cb587e8d1265b6c03cf91da3"},
|
||||||
|
{file = "mypy-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d19c413b3c07cbecf1f991e2221746b0d2a9410b59cb3f4fb9557f0365a1a817"},
|
||||||
|
{file = "mypy-1.8.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9261ed810972061388918c83c3f5cd46079d875026ba97380f3e3978a72f503d"},
|
||||||
|
{file = "mypy-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:51720c776d148bad2372ca21ca29256ed483aa9a4cdefefcef49006dff2a6835"},
|
||||||
|
{file = "mypy-1.8.0-py3-none-any.whl", hash = "sha256:538fd81bb5e430cc1381a443971c0475582ff9f434c16cd46d2c66763ce85d9d"},
|
||||||
|
{file = "mypy-1.8.0.tar.gz", hash = "sha256:6ff8b244d7085a0b425b56d327b480c3b29cafbd2eff27316a004f9a7391ae07"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -162,52 +165,33 @@ files = [
|
|||||||
{file = "psgdemos-1.12.1.tar.gz", hash = "sha256:4108af795477531a9b1c8675b9aa9b6628c109e660f6954baf8ba2dc3b5806e9"},
|
{file = "psgdemos-1.12.1.tar.gz", hash = "sha256:4108af795477531a9b1c8675b9aa9b6628c109e660f6954baf8ba2dc3b5806e9"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pycodestyle"
|
|
||||||
version = "2.11.0"
|
|
||||||
requires_python = ">=3.8"
|
|
||||||
summary = "Python style guide checker"
|
|
||||||
files = [
|
|
||||||
{file = "pycodestyle-2.11.0-py2.py3-none-any.whl", hash = "sha256:5d1013ba8dc7895b548be5afb05740ca82454fd899971563d2ef625d090326f8"},
|
|
||||||
{file = "pycodestyle-2.11.0.tar.gz", hash = "sha256:259bcc17857d8a8b3b4a2327324b79e5f020a13c16074670f9c8c8f872ea76d0"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pyflakes"
|
|
||||||
version = "3.1.0"
|
|
||||||
requires_python = ">=3.8"
|
|
||||||
summary = "passive checker of Python programs"
|
|
||||||
files = [
|
|
||||||
{file = "pyflakes-3.1.0-py2.py3-none-any.whl", hash = "sha256:4132f6d49cb4dae6819e5379898f2b8cce3c5f23994194c24b77d5da2e36f774"},
|
|
||||||
{file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"},
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyinstaller"
|
name = "pyinstaller"
|
||||||
version = "5.13.0"
|
version = "6.3.0"
|
||||||
requires_python = "<3.13,>=3.7"
|
requires_python = "<3.13,>=3.8"
|
||||||
summary = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
summary = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"altgraph",
|
"altgraph",
|
||||||
"macholib>=1.8; sys_platform == \"darwin\"",
|
"macholib>=1.8; sys_platform == \"darwin\"",
|
||||||
|
"packaging>=22.0",
|
||||||
"pefile>=2022.5.30; sys_platform == \"win32\"",
|
"pefile>=2022.5.30; sys_platform == \"win32\"",
|
||||||
"pyinstaller-hooks-contrib>=2021.4",
|
"pyinstaller-hooks-contrib>=2021.4",
|
||||||
"pywin32-ctypes>=0.2.1; sys_platform == \"win32\"",
|
"pywin32-ctypes>=0.2.1; sys_platform == \"win32\"",
|
||||||
"setuptools>=42.0.0",
|
"setuptools>=42.0.0",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "pyinstaller-5.13.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:7fdd319828de679f9c5e381eff998ee9b4164bf4457e7fca56946701cf002c3f"},
|
{file = "pyinstaller-6.3.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:75a6f2a6f835a2e6e0899d10e60c10caf5defd25aced38b1dd48fbbabc89de07"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0df43697c4914285ecd333be968d2cd042ab9b2670124879ee87931d2344eaf5"},
|
{file = "pyinstaller-6.3.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:de25beb176f73a944758553caacec46cc665bf3910ad8a174706d79cf6e95340"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_i686.whl", hash = "sha256:28d9742c37e9fb518444b12f8c8ab3cb4ba212d752693c34475c08009aa21ccf"},
|
{file = "pyinstaller-6.3.0-py3-none-manylinux2014_i686.whl", hash = "sha256:e436fcc0ea87c3f132baac916d508c24c84a8f6d8a06c3154fbc753f169b76c7"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e5fb17de6c325d3b2b4ceaeb55130ad7100a79096490e4c5b890224406fa42f4"},
|
{file = "pyinstaller-6.3.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:b721d793a33b6d9946c7dd95d3ea7589c0424b51cf1b9fe580f03c544f1336b2"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:78975043edeb628e23a73fb3ef0a273cda50e765f1716f75212ea3e91b09dede"},
|
{file = "pyinstaller-6.3.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:96c37a1ee5b2fd5bb25c098ef510661d6d17b6515d0b86d8fc93727dd2475ba3"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:cd7d5c06f2847195a23d72ede17c60857d6f495d6f0727dc6c9bc1235f2eb79c"},
|
{file = "pyinstaller-6.3.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:abe91106a3bbccc3f3a27af4325676ecdb6f46cb842ac663625002a870fc503b"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:24009eba63cfdbcde6d2634e9c87f545eb67249ddf3b514e0cd3b2cdaa595828"},
|
{file = "pyinstaller-6.3.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:41c937fe8f07ae02009b3b5a96ac3eb0800a4f8a97af142d4100060fe2135bb9"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:1fde4381155f21d6354dc450dcaa338cd8a40aaacf6bd22b987b0f3e1f96f3ee"},
|
{file = "pyinstaller-6.3.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:886b3b995b674905a20ad5b720b47cc395897d7b391117831027a4c8c5d67a58"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-win32.whl", hash = "sha256:2d03419904d1c25c8968b0ad21da0e0f33d8d65716e29481b5bd83f7f342b0c5"},
|
{file = "pyinstaller-6.3.0-py3-none-win32.whl", hash = "sha256:0597fb04337695e5cc5250253e0655530bf14f264b7a5b7d219cc65f6889c4bd"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-win_amd64.whl", hash = "sha256:9fc27c5a853b14a90d39c252707673c7a0efec921cd817169aff3af0fca8c127"},
|
{file = "pyinstaller-6.3.0-py3-none-win_amd64.whl", hash = "sha256:156b32ba943e0090bcc68e40ae1cb68fd92b7f1ab6fe0bdf8faf3d3cfc4e12dd"},
|
||||||
{file = "pyinstaller-5.13.0-py3-none-win_arm64.whl", hash = "sha256:3a331951f9744bc2379ea5d65d36f3c828eaefe2785f15039592cdc08560b262"},
|
{file = "pyinstaller-6.3.0-py3-none-win_arm64.whl", hash = "sha256:1eadbd1fae84e2e6c678d8b4ed6a232ec5c8fe3a839aea5a3071c4c0282f98cc"},
|
||||||
{file = "pyinstaller-5.13.0.tar.gz", hash = "sha256:5e446df41255e815017d96318e39f65a3eb807e74a796c7e7ff7f13b6366a2e9"},
|
{file = "pyinstaller-6.3.0.tar.gz", hash = "sha256:914d4c96cc99472e37ac552fdd82fbbe09e67bb592d0717fcffaa99ea74273df"},
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -249,6 +233,31 @@ files = [
|
|||||||
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.1.15"
|
||||||
|
requires_python = ">=3.7"
|
||||||
|
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||||
|
files = [
|
||||||
|
{file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"},
|
||||||
|
{file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"},
|
||||||
|
{file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "setuptools"
|
name = "setuptools"
|
||||||
version = "68.1.2"
|
version = "68.1.2"
|
||||||
@@ -269,15 +278,25 @@ files = [
|
|||||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.8.0"
|
||||||
|
requires_python = ">=3.8"
|
||||||
|
summary = "Backported and Experimental Type Hints for Python 3.8+"
|
||||||
|
files = [
|
||||||
|
{file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"},
|
||||||
|
{file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"},
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "voicemeeter-api"
|
name = "voicemeeter-api"
|
||||||
version = "2.4.9"
|
version = "2.5.3"
|
||||||
requires_python = ">=3.10,<4.0"
|
requires_python = ">=3.10,<4.0"
|
||||||
summary = "A Python wrapper for the Voiceemeter API"
|
summary = "A Python wrapper for the Voiceemeter API"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tomli<3.0.0,>=2.0.1; python_version < \"3.11\"",
|
"tomli<3.0.0,>=2.0.1; python_version < \"3.11\"",
|
||||||
]
|
]
|
||||||
files = [
|
files = [
|
||||||
{file = "voicemeeter_api-2.4.9-py3-none-any.whl", hash = "sha256:a09fd07fe3799cd5c880d491048da81d94e49aa97ec753aa1f9289acd27be965"},
|
{file = "voicemeeter_api-2.5.3-py3-none-any.whl", hash = "sha256:00c1f5b4027844d09ae19042f1662501dc28b1cee12a4238503790a50293c8f7"},
|
||||||
{file = "voicemeeter_api-2.4.9.tar.gz", hash = "sha256:47ad614a8b9ccb0b4e47acf65666ce0f0537a0890fffda9949e995bea70e679c"},
|
{file = "voicemeeter_api-2.5.3.tar.gz", hash = "sha256:b40f77e94446b4a5ab561c777109bdaf9c2478dfaa19e728c91ade3b44226704"},
|
||||||
]
|
]
|
||||||
|
|||||||
107
pyproject.toml
107
pyproject.toml
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nvda_voicemeeter"
|
name = "nvda_voicemeeter"
|
||||||
version = "0.4.1"
|
version = "0.6.3"
|
||||||
description = "A Voicemeeter app compatible with NVDA"
|
description = "A Voicemeeter app compatible with NVDA"
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "onyx-and-iris", email = "code@onyxandiris.online" },
|
{ name = "onyx-and-iris", email = "code@onyxandiris.online" },
|
||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"pysimplegui>=4.60.5",
|
"pysimplegui>=4.60.5",
|
||||||
"pyparsing>=3.1.1",
|
"pyparsing>=3.1.1",
|
||||||
"voicemeeter-api>=2.4.9",
|
"voicemeeter-api>=2.5.3",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.10,<3.12"
|
requires-python = ">=3.10,<3.12"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
@@ -17,16 +17,111 @@ readme = "README.md"
|
|||||||
text = "MIT"
|
text = "MIT"
|
||||||
|
|
||||||
[tool.pdm.dev-dependencies]
|
[tool.pdm.dev-dependencies]
|
||||||
build = [
|
|
||||||
"pyinstaller>=5.1",
|
|
||||||
]
|
|
||||||
lint = [
|
lint = [
|
||||||
"black>=23.7.0",
|
"black>=23.7.0",
|
||||||
"flake8>=6.1.0",
|
"ruff>=0.0.291",
|
||||||
|
"mypy>=1.7.0",
|
||||||
]
|
]
|
||||||
test = [
|
test = [
|
||||||
"psgdemos>=1.12.1",
|
"psgdemos>=1.12.1",
|
||||||
]
|
]
|
||||||
|
build = [
|
||||||
|
"pyinstaller>=6.3.0",
|
||||||
|
]
|
||||||
|
|
||||||
[tool.pdm.scripts.build]
|
[tool.pdm.scripts.build]
|
||||||
shell = "build.ps1"
|
shell = "build.ps1"
|
||||||
|
|
||||||
|
[tool.black]
|
||||||
|
line-length = 119
|
||||||
|
|
||||||
|
[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",
|
||||||
|
".eggs",
|
||||||
|
".git",
|
||||||
|
".git-rewrite",
|
||||||
|
".hg",
|
||||||
|
".mypy_cache",
|
||||||
|
".nox",
|
||||||
|
".pants.d",
|
||||||
|
".pytype",
|
||||||
|
".ruff_cache",
|
||||||
|
".svn",
|
||||||
|
".tox",
|
||||||
|
".venv",
|
||||||
|
"__pypackages__",
|
||||||
|
"_build",
|
||||||
|
"buck-out",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"venv",
|
||||||
|
]
|
||||||
|
line-length = 119
|
||||||
|
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||||
|
target-version = "py310"
|
||||||
|
|
||||||
|
[tool.ruff.mccabe]
|
||||||
|
max-complexity = 10
|
||||||
|
|
||||||
|
[tool.ruff.per-file-ignores]
|
||||||
|
"__init__.py" = [
|
||||||
|
"E402",
|
||||||
|
"F401",
|
||||||
|
]
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ class Builder:
|
|||||||
steps = (
|
steps = (
|
||||||
self.make_tab0_row0,
|
self.make_tab0_row0,
|
||||||
self.make_tab0_row1,
|
self.make_tab0_row1,
|
||||||
self.make_tab0_row2,
|
|
||||||
self.make_tab0_row3,
|
self.make_tab0_row3,
|
||||||
self.make_tab0_row4,
|
self.make_tab0_row4,
|
||||||
self.make_tab0_row5,
|
self.make_tab0_row5,
|
||||||
@@ -93,6 +92,8 @@ class Builder:
|
|||||||
return [[menu], [tab_group]]
|
return [[menu], [tab_group]]
|
||||||
|
|
||||||
def make_menu(self) -> psg.Menu:
|
def make_menu(self) -> psg.Menu:
|
||||||
|
themes = [f"{theme}::MENU THEME" for theme in util.get_themes_list()]
|
||||||
|
themes.append("Default::MENU THEME")
|
||||||
menu_def = [
|
menu_def = [
|
||||||
[
|
[
|
||||||
"&Voicemeeter",
|
"&Voicemeeter",
|
||||||
@@ -103,6 +104,7 @@ class Builder:
|
|||||||
"Load Settings on Startup ::MENU",
|
"Load Settings on Startup ::MENU",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
["&Theme", themes],
|
||||||
]
|
]
|
||||||
return psg.Menu(menu_def, key="menus")
|
return psg.Menu(menu_def, key="menus")
|
||||||
|
|
||||||
@@ -152,57 +154,20 @@ class Builder:
|
|||||||
[step(hardware_out) for step in (add_physical_device_opts,)]
|
[step(hardware_out) for step in (add_physical_device_opts,)]
|
||||||
return psg.Frame("Hardware Out", hardware_out)
|
return psg.Frame("Hardware Out", hardware_out)
|
||||||
|
|
||||||
def make_tab0_row2(self) -> psg.Frame:
|
|
||||||
"""tab0 row2 represents patch asio inputs to strips"""
|
|
||||||
|
|
||||||
def add_asio_checkboxes(layout, i):
|
|
||||||
nums = list(range(99))
|
|
||||||
layout.append(
|
|
||||||
[
|
|
||||||
psg.Spin(
|
|
||||||
nums,
|
|
||||||
initial_value=self.window.cache["asio"][f"ASIO CHECKBOX||{util.get_asio_checkbox_index(0, i)}"],
|
|
||||||
size=2,
|
|
||||||
enable_events=True,
|
|
||||||
key=f"ASIO CHECKBOX||IN{i} 0",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
layout.append(
|
|
||||||
[
|
|
||||||
psg.Spin(
|
|
||||||
nums,
|
|
||||||
initial_value=self.window.cache["asio"][f"ASIO CHECKBOX||{util.get_asio_checkbox_index(1, i)}"],
|
|
||||||
size=2,
|
|
||||||
enable_events=True,
|
|
||||||
key=f"ASIO CHECKBOX||IN{i} 1",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
inner = []
|
|
||||||
asio_checkboxlists = ([] for _ in range(self.kind.phys_out))
|
|
||||||
for i, checkbox_list in enumerate(asio_checkboxlists):
|
|
||||||
[step(checkbox_list, i + 1) for step in (add_asio_checkboxes,)]
|
|
||||||
inner.append(psg.Frame(f"In#{i + 1}", checkbox_list))
|
|
||||||
|
|
||||||
asio_checkboxes = [inner]
|
|
||||||
return psg.Frame("PATCH ASIO Inputs to Strips", asio_checkboxes)
|
|
||||||
|
|
||||||
def make_tab0_row3(self) -> psg.Frame:
|
def make_tab0_row3(self) -> psg.Frame:
|
||||||
"""tab0 row3 represents patch composite"""
|
"""tab0 row3 represents patch composite"""
|
||||||
|
|
||||||
def add_physical_device_opts(layout):
|
def add_physical_device_opts(layout):
|
||||||
outputs = util.get_patch_composite_list(self.vm.kind)
|
outputs = util.get_patch_composite_list(self.kind)
|
||||||
layout.append(
|
layout.append(
|
||||||
[
|
[
|
||||||
psg.ButtonMenu(
|
psg.ButtonMenu(
|
||||||
f"PC{i + 1}",
|
f"PC{i + 1}",
|
||||||
size=(6, 2),
|
size=(5, 2),
|
||||||
menu_def=["", outputs],
|
menu_def=["", outputs],
|
||||||
key=f"PATCH COMPOSITE||PC{i + 1}",
|
key=f"PATCH COMPOSITE||PC{i + 1}",
|
||||||
)
|
)
|
||||||
for i in range(self.kind.phys_out)
|
for i in range(self.kind.composite)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -320,6 +285,7 @@ class Builder:
|
|||||||
disable_number_display=True,
|
disable_number_display=True,
|
||||||
expand_x=True,
|
expand_x=True,
|
||||||
enable_events=True,
|
enable_events=True,
|
||||||
|
disabled=True,
|
||||||
orientation="horizontal",
|
orientation="horizontal",
|
||||||
key=f"STRIP {i}||SLIDER GAIN",
|
key=f"STRIP {i}||SLIDER GAIN",
|
||||||
),
|
),
|
||||||
@@ -376,7 +342,7 @@ class Builder:
|
|||||||
if i == self.kind.phys_in + 1:
|
if i == self.kind.phys_in + 1:
|
||||||
layout.append(
|
layout.append(
|
||||||
[
|
[
|
||||||
psg.Button("K", size=(6, 2), key=f"STRIP {i}||MONO"),
|
psg.Button("K", size=(6, 2), key=f"STRIP {i}||KARAOKE"),
|
||||||
psg.Button("Solo", size=(6, 2), key=f"STRIP {i}||SOLO"),
|
psg.Button("Solo", size=(6, 2), key=f"STRIP {i}||SOLO"),
|
||||||
psg.Button("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
psg.Button("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
||||||
],
|
],
|
||||||
@@ -384,7 +350,7 @@ class Builder:
|
|||||||
else:
|
else:
|
||||||
layout.append(
|
layout.append(
|
||||||
[
|
[
|
||||||
psg.Button("MC", size=(6, 2), key=f"STRIP {i}||MONO"),
|
psg.Button("MC", size=(6, 2), key=f"STRIP {i}||MC"),
|
||||||
psg.Button("Solo", size=(6, 2), key=f"STRIP {i}||SOLO"),
|
psg.Button("Solo", size=(6, 2), key=f"STRIP {i}||SOLO"),
|
||||||
psg.Button("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
psg.Button("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
||||||
],
|
],
|
||||||
@@ -392,7 +358,11 @@ class Builder:
|
|||||||
|
|
||||||
outputs = []
|
outputs = []
|
||||||
[step(outputs) for step in (add_strip_outputs,)]
|
[step(outputs) for step in (add_strip_outputs,)]
|
||||||
return psg.Frame(self.window.cache["labels"][f"STRIP {i}||LABEL"], outputs, key=f"STRIP {i}||LABEL")
|
return psg.Frame(
|
||||||
|
self.window.cache["labels"][f"STRIP {i}||LABEL"],
|
||||||
|
outputs,
|
||||||
|
key=f"STRIP {i}||LABEL",
|
||||||
|
)
|
||||||
|
|
||||||
def make_tab2_button_rows(self) -> psg.Frame:
|
def make_tab2_button_rows(self) -> psg.Frame:
|
||||||
layout = [
|
layout = [
|
||||||
@@ -412,6 +382,7 @@ class Builder:
|
|||||||
disable_number_display=True,
|
disable_number_display=True,
|
||||||
expand_x=True,
|
expand_x=True,
|
||||||
enable_events=True,
|
enable_events=True,
|
||||||
|
disabled=True,
|
||||||
orientation="horizontal",
|
orientation="horizontal",
|
||||||
key=f"STRIP {i}||SLIDER GAIN",
|
key=f"STRIP {i}||SLIDER GAIN",
|
||||||
),
|
),
|
||||||
@@ -452,7 +423,11 @@ class Builder:
|
|||||||
if self.kind.name in ("banana", "potato"):
|
if self.kind.name in ("banana", "potato"):
|
||||||
steps += (add_limit_slider,)
|
steps += (add_limit_slider,)
|
||||||
[step(layout) for step in steps]
|
[step(layout) for step in steps]
|
||||||
return psg.Frame(self.window.cache["labels"][f"STRIP {i}||LABEL"], layout, key=f"STRIP {i}||LABEL||SLIDER")
|
return psg.Frame(
|
||||||
|
self.window.cache["labels"][f"STRIP {i}||LABEL"],
|
||||||
|
layout,
|
||||||
|
key=f"STRIP {i}||LABEL||SLIDER",
|
||||||
|
)
|
||||||
|
|
||||||
def make_tab2_slider_rows(self) -> psg.Frame:
|
def make_tab2_slider_rows(self) -> psg.Frame:
|
||||||
layout = [
|
layout = [
|
||||||
@@ -464,24 +439,36 @@ class Builder:
|
|||||||
"""tab3 row represents bus composite toggle"""
|
"""tab3 row represents bus composite toggle"""
|
||||||
|
|
||||||
def add_strip_outputs(layout):
|
def add_strip_outputs(layout):
|
||||||
params = ["MONO", "EQ", "MUTE", "MODE"]
|
params = ["MONO", "EQ", "MUTE"]
|
||||||
if self.vm.kind.name == "basic":
|
if self.kind.name == "basic":
|
||||||
params.remove("EQ")
|
params.remove("EQ")
|
||||||
label = {"MODE": "BUSMODE"}
|
busmodes = [util._bus_mode_map[mode] for mode in util.get_bus_modes(self.vm)]
|
||||||
layout.append(
|
layout.append(
|
||||||
[
|
[
|
||||||
|
*[
|
||||||
psg.Button(
|
psg.Button(
|
||||||
label.get(param, param.capitalize()),
|
param.capitalize(),
|
||||||
size=(12 if param == "MODE" else 6, 2),
|
size=(6, 2),
|
||||||
key=f"BUS {i}||{param}",
|
key=f"BUS {i}||{param}",
|
||||||
)
|
)
|
||||||
for param in params
|
for param in params
|
||||||
|
],
|
||||||
|
psg.ButtonMenu(
|
||||||
|
"BUSMODE",
|
||||||
|
size=(12, 2),
|
||||||
|
menu_def=["", busmodes],
|
||||||
|
key=f"BUS {i}||MODE",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
outputs = []
|
outputs = []
|
||||||
[step(outputs) for step in (add_strip_outputs,)]
|
[step(outputs) for step in (add_strip_outputs,)]
|
||||||
return psg.Frame(self.window.cache["labels"][f"BUS {i}||LABEL"], outputs, key=f"BUS {i}||LABEL")
|
return psg.Frame(
|
||||||
|
self.window.cache["labels"][f"BUS {i}||LABEL"],
|
||||||
|
outputs,
|
||||||
|
key=f"BUS {i}||LABEL",
|
||||||
|
)
|
||||||
|
|
||||||
def make_tab3_button_rows(self) -> psg.Frame:
|
def make_tab3_button_rows(self) -> psg.Frame:
|
||||||
layout = [[self.make_tab3_button_row(i)] for i in range(self.kind.num_bus)]
|
layout = [[self.make_tab3_button_row(i)] for i in range(self.kind.num_bus)]
|
||||||
@@ -491,6 +478,7 @@ class Builder:
|
|||||||
def add_gain_slider(layout):
|
def add_gain_slider(layout):
|
||||||
layout.append(
|
layout.append(
|
||||||
[
|
[
|
||||||
|
psg.Text("Gain"),
|
||||||
psg.Slider(
|
psg.Slider(
|
||||||
range=(-60, 12),
|
range=(-60, 12),
|
||||||
default_value=self.vm.bus[i].gain,
|
default_value=self.vm.bus[i].gain,
|
||||||
@@ -498,9 +486,10 @@ class Builder:
|
|||||||
disable_number_display=True,
|
disable_number_display=True,
|
||||||
expand_x=True,
|
expand_x=True,
|
||||||
enable_events=True,
|
enable_events=True,
|
||||||
|
disabled=True,
|
||||||
orientation="horizontal",
|
orientation="horizontal",
|
||||||
key=f"BUS {i}||SLIDER GAIN",
|
key=f"BUS {i}||SLIDER GAIN",
|
||||||
)
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
from .errors import NVDAVMError
|
from .errors import NVDAVMError
|
||||||
|
|
||||||
bits = 64 if ct.sizeof(ct.c_voidp) == 8 else 32
|
BITS = 64 if ct.sizeof(ct.c_void_p) == 8 else 32
|
||||||
|
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
raise NVDAVMError("Only Windows OS supported")
|
raise NVDAVMError("Only Windows OS supported")
|
||||||
@@ -15,7 +15,7 @@ REG_KEY = "\\".join(
|
|||||||
None,
|
None,
|
||||||
(
|
(
|
||||||
"SOFTWARE",
|
"SOFTWARE",
|
||||||
"WOW6432Node" if bits == 64 else "",
|
"WOW6432Node" if BITS == 64 else "",
|
||||||
"Microsoft",
|
"Microsoft",
|
||||||
"Windows",
|
"Windows",
|
||||||
"CurrentVersion",
|
"CurrentVersion",
|
||||||
@@ -33,14 +33,14 @@ def get_nvdapath():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
NVDA_PATH = Path(get_nvdapath()) / "nvda.exe"
|
NVDA_PATH = Path(get_nvdapath()) / "nvda.exe"
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError:
|
||||||
NVDA_PATH = ""
|
NVDA_PATH = ""
|
||||||
|
|
||||||
|
|
||||||
controller_path = Path(__file__).parents[2].resolve() / "controllerClient"
|
controller_path = Path(__file__).parents[2].resolve() / "controllerClient"
|
||||||
if not controller_path.exists():
|
if not controller_path.exists():
|
||||||
controller_path = Path("controllerClient")
|
controller_path = Path("_internal") / "controllerClient"
|
||||||
|
|
||||||
DLL_PATH = controller_path / f"x{64 if bits == 64 else 86}" / f"nvdaControllerClient{bits}.dll"
|
DLL_PATH = controller_path / f"x{64 if BITS == 64 else 86}" / f"nvdaControllerClient{BITS}.dll"
|
||||||
|
|
||||||
libc = ct.CDLL(str(DLL_PATH))
|
libc = ct.CDLL(str(DLL_PATH))
|
||||||
|
|||||||
@@ -1,13 +1,20 @@
|
|||||||
|
from typing import Union
|
||||||
|
|
||||||
import PySimpleGUI as psg
|
import PySimpleGUI as psg
|
||||||
|
|
||||||
|
from . import util
|
||||||
|
|
||||||
|
|
||||||
class LabelSlider(psg.Frame):
|
class LabelSlider(psg.Frame):
|
||||||
"""Compound Label Slider element"""
|
"""Compound Label Slider Strip element"""
|
||||||
|
|
||||||
def __init__(self, parent, i, param, range_=(0, 10), *args, **kwargs):
|
def __init__(self, parent, i, param, range_=(0, 10), *args, **kwargs):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
if param in ("AUDIBILITY", "DENOISER"):
|
if param in ("AUDIBILITY", "DENOISER"):
|
||||||
size = 7
|
size = 7
|
||||||
|
else:
|
||||||
|
if psg.theme() == "HighContrast":
|
||||||
|
size = 5
|
||||||
else:
|
else:
|
||||||
size = 4
|
size = 4
|
||||||
layout = [
|
layout = [
|
||||||
@@ -33,3 +40,169 @@ class LabelSlider(psg.Frame):
|
|||||||
if param in ("COMP", "GATE", "DENOISER"):
|
if param in ("COMP", "GATE", "DENOISER"):
|
||||||
return target.knob
|
return target.knob
|
||||||
return target
|
return target
|
||||||
|
|
||||||
|
|
||||||
|
class CompSlider(psg.Slider):
|
||||||
|
"""Compressor Slider element"""
|
||||||
|
|
||||||
|
def __init__(self, vm, index, param):
|
||||||
|
self.vm = vm
|
||||||
|
self.index = index
|
||||||
|
super().__init__(
|
||||||
|
disable_number_display=True,
|
||||||
|
expand_x=True,
|
||||||
|
enable_events=True,
|
||||||
|
orientation="horizontal",
|
||||||
|
key=f"COMPRESSOR||SLIDER {param}",
|
||||||
|
**self.default_params(param),
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_params(self, param):
|
||||||
|
match param:
|
||||||
|
case "INPUT GAIN":
|
||||||
|
return {
|
||||||
|
"range": (-24, 24),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.gainin,
|
||||||
|
"resolution": 0.1,
|
||||||
|
"disabled": True,
|
||||||
|
}
|
||||||
|
case "RATIO":
|
||||||
|
return {
|
||||||
|
"range": (1, 8),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.ratio,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "THRESHOLD":
|
||||||
|
return {
|
||||||
|
"range": (-40, -3),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.threshold,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "ATTACK":
|
||||||
|
return {
|
||||||
|
"range": (0, 200),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.attack,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "RELEASE":
|
||||||
|
return {
|
||||||
|
"range": (0, 5000),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.release,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "KNEE":
|
||||||
|
return {
|
||||||
|
"range": (0, 1),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.knee,
|
||||||
|
"resolution": 0.01,
|
||||||
|
}
|
||||||
|
case "OUTPUT GAIN":
|
||||||
|
return {
|
||||||
|
"range": (-24, 24),
|
||||||
|
"default_value": self.vm.strip[self.index].comp.gainout,
|
||||||
|
"resolution": 0.1,
|
||||||
|
"disabled": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_bounds(param, val):
|
||||||
|
match param:
|
||||||
|
case "RATIO":
|
||||||
|
val = util.check_bounds(val, (1, 8))
|
||||||
|
case "THRESHOLD":
|
||||||
|
val = util.check_bounds(val, (-40, -3))
|
||||||
|
case "ATTACK":
|
||||||
|
val = util.check_bounds(val, (0, 200))
|
||||||
|
case "RELEASE":
|
||||||
|
val = util.check_bounds(val, (0, 5000))
|
||||||
|
case "KNEE":
|
||||||
|
val = util.check_bounds(val, (0, 1))
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class GateSlider(psg.Slider):
|
||||||
|
def __init__(self, vm, index, param):
|
||||||
|
self.vm = vm
|
||||||
|
self.index = index
|
||||||
|
super().__init__(
|
||||||
|
disable_number_display=True,
|
||||||
|
expand_x=True,
|
||||||
|
enable_events=True,
|
||||||
|
orientation="horizontal",
|
||||||
|
key=f"GATE||SLIDER {param}",
|
||||||
|
**self.default_params(param),
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_params(self, param):
|
||||||
|
match param:
|
||||||
|
case "THRESHOLD":
|
||||||
|
return {
|
||||||
|
"range": (-60, -10),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.threshold,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "DAMPING":
|
||||||
|
return {
|
||||||
|
"range": (-60, -10),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.damping,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "BPSIDECHAIN":
|
||||||
|
return {
|
||||||
|
"range": (100, 4000),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.bpsidechain,
|
||||||
|
"resolution": 1,
|
||||||
|
}
|
||||||
|
case "ATTACK":
|
||||||
|
return {
|
||||||
|
"range": (0, 1000),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.attack,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "HOLD":
|
||||||
|
return {
|
||||||
|
"range": (0, 5000),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.hold,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
case "RELEASE":
|
||||||
|
return {
|
||||||
|
"range": (0, 5000),
|
||||||
|
"default_value": self.vm.strip[self.index].gate.release,
|
||||||
|
"resolution": 0.1,
|
||||||
|
}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_bounds(param, val):
|
||||||
|
match param:
|
||||||
|
case "THRESHOLD":
|
||||||
|
val = util.check_bounds(val, (-60, -10))
|
||||||
|
case "DAMPING MAX":
|
||||||
|
val = util.check_bounds(val, (-60, -10))
|
||||||
|
case "BPSIDECHAIN":
|
||||||
|
val = util.check_bounds(val, (100, 4000))
|
||||||
|
case "ATTACK":
|
||||||
|
val = util.check_bounds(val, (0, 1000))
|
||||||
|
case "HOLD":
|
||||||
|
val = util.check_bounds(val, (0, 5000))
|
||||||
|
case "RELEASE":
|
||||||
|
val = util.check_bounds(val, (0, 5000))
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class LabelSliderAdvanced(psg.Frame):
|
||||||
|
"""Compound Label Slider element for Advanced Comp|Gate"""
|
||||||
|
|
||||||
|
def __init__(self, parent, index, param, slider_cls: Union[CompSlider, GateSlider], *args, **kwargs):
|
||||||
|
label_map = {
|
||||||
|
"DAMPING": "Damping Max",
|
||||||
|
"BPSIDECHAIN": "BP Sidechain",
|
||||||
|
}
|
||||||
|
|
||||||
|
layout = [
|
||||||
|
[
|
||||||
|
psg.Text(label_map.get(param, param.title()), size=10),
|
||||||
|
slider_cls(parent.vm, index, param),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
super().__init__(None, layout=layout, border_width=0, pad=0, *args, **kwargs)
|
||||||
|
|||||||
34
src/nvda_voicemeeter/configuration.py
Normal file
34
src/nvda_voicemeeter/configuration.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
SETTINGS = Path.cwd() / "settings.json"
|
||||||
|
|
||||||
|
|
||||||
|
def config_from_json():
|
||||||
|
data = {}
|
||||||
|
if not SETTINGS.exists():
|
||||||
|
return data
|
||||||
|
with open(SETTINGS, "r") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
config = config_from_json()
|
||||||
|
|
||||||
|
|
||||||
|
def get(key, default=None):
|
||||||
|
if key in config:
|
||||||
|
return config[key]
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def set(key, value):
|
||||||
|
config[key] = value
|
||||||
|
with open(SETTINGS, "w") as f:
|
||||||
|
json.dump(config, f)
|
||||||
|
|
||||||
|
|
||||||
|
def delete(key):
|
||||||
|
del config[key]
|
||||||
|
with open(SETTINGS, "w") as f:
|
||||||
|
json.dump(config, f)
|
||||||
@@ -5,7 +5,7 @@ def _make_hardware_ins_cache(vm) -> dict:
|
|||||||
def _make_hardware_outs_cache(vm) -> dict:
|
def _make_hardware_outs_cache(vm) -> dict:
|
||||||
hw_outs = {**{f"HARDWARE OUT||A{i + 1}": vm.bus[i].device.name for i in range(vm.kind.phys_out)}}
|
hw_outs = {**{f"HARDWARE OUT||A{i + 1}": vm.bus[i].device.name for i in range(vm.kind.phys_out)}}
|
||||||
if vm.kind.name == "basic":
|
if vm.kind.name == "basic":
|
||||||
hw_outs |= {f"HARDWARE OUT||A2": vm.bus[1].device.name}
|
hw_outs |= {"HARDWARE OUT||A2": vm.bus[1].device.name}
|
||||||
return hw_outs
|
return hw_outs
|
||||||
|
|
||||||
|
|
||||||
@@ -38,10 +38,15 @@ def _make_param_cache(vm, channel_type) -> dict:
|
|||||||
**{f"STRIP {i}||B3": vm.strip[i].B3 for i in range(vm.kind.num_strip)},
|
**{f"STRIP {i}||B3": vm.strip[i].B3 for i in range(vm.kind.num_strip)},
|
||||||
}
|
}
|
||||||
params |= {
|
params |= {
|
||||||
**{f"STRIP {i}||MONO": vm.strip[i].mono for i in range(vm.kind.num_strip)},
|
**{f"STRIP {i}||MONO": vm.strip[i].mono for i in range(vm.kind.phys_in)},
|
||||||
**{f"STRIP {i}||SOLO": vm.strip[i].solo for i in range(vm.kind.num_strip)},
|
**{f"STRIP {i}||SOLO": vm.strip[i].solo for i in range(vm.kind.num_strip)},
|
||||||
**{f"STRIP {i}||MUTE": vm.strip[i].mute for i in range(vm.kind.num_strip)},
|
**{f"STRIP {i}||MUTE": vm.strip[i].mute for i in range(vm.kind.num_strip)},
|
||||||
}
|
}
|
||||||
|
for i in range(vm.kind.phys_in, vm.kind.phys_in + vm.kind.virt_in):
|
||||||
|
if i == vm.kind.phys_in + 1:
|
||||||
|
params[f"STRIP {i}||KARAOKE"] = vm.strip[i].k
|
||||||
|
else:
|
||||||
|
params[f"STRIP {i}||MC"] = vm.strip[i].mc
|
||||||
else:
|
else:
|
||||||
params |= {
|
params |= {
|
||||||
**{f"BUS {i}||MONO": vm.bus[i].mono for i in range(vm.kind.num_bus)},
|
**{f"BUS {i}||MONO": vm.bus[i].mono for i in range(vm.kind.num_bus)},
|
||||||
@@ -74,10 +79,17 @@ def _make_label_cache(vm) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def _make_patch_asio_cache(vm) -> dict:
|
def _make_patch_asio_cache(vm) -> dict:
|
||||||
|
params = {}
|
||||||
if vm.kind.name != "basic":
|
if vm.kind.name != "basic":
|
||||||
return {**{f"ASIO CHECKBOX||{i}": vm.patch.asio[i].get() for i in range(vm.kind.phys_out * 2)}}
|
params |= {**{f"ASIO INPUT SPINBOX||{i}": vm.patch.asio[i].get() for i in range(vm.kind.phys_out * 2)}}
|
||||||
|
for i in range(vm.kind.phys_out - 1):
|
||||||
|
target = getattr(vm.patch, f"A{i + 2}")
|
||||||
|
params |= {**{f"ASIO OUTPUT A{i + 2} SPINBOX||{j}": target[j].get() for j in range(vm.kind.num_bus)}}
|
||||||
|
return params
|
||||||
|
|
||||||
|
|
||||||
def _make_patch_insert_cache(vm) -> dict:
|
def _make_patch_insert_cache(vm) -> dict:
|
||||||
|
params = {}
|
||||||
if vm.kind.name != "basic":
|
if vm.kind.name != "basic":
|
||||||
return {**{f"INSERT CHECKBOX||{i}": vm.patch.insert[i].on for i in range(vm.kind.num_strip_levels)}}
|
params |= {**{f"INSERT CHECKBOX||{i}": vm.patch.insert[i].on for i in range(vm.kind.num_strip_levels)}}
|
||||||
|
return params
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from pathlib import Path
|
|||||||
import PySimpleGUI as psg
|
import PySimpleGUI as psg
|
||||||
|
|
||||||
from . import util
|
from . import util
|
||||||
|
from .compound import CompSlider, GateSlider, LabelSliderAdvanced
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -40,13 +41,37 @@ class Popup:
|
|||||||
filepath = values["Browse"]
|
filepath = values["Browse"]
|
||||||
break
|
break
|
||||||
self.window.nvda.speak(button)
|
self.window.nvda.speak(button)
|
||||||
case [[button], ["KEY", "ENTER"]]:
|
case [_, ["KEY", "ENTER"]]:
|
||||||
popup.find_element_with_focus().click()
|
popup.find_element_with_focus().click()
|
||||||
self.logger.debug(f"parsed::{parsed_cmd}")
|
self.logger.debug(f"parsed::{parsed_cmd}")
|
||||||
popup.close()
|
popup.close()
|
||||||
if filepath:
|
if filepath:
|
||||||
return Path(filepath)
|
return Path(filepath)
|
||||||
|
|
||||||
|
def on_pdirty(self):
|
||||||
|
if self.popup.Title == "Advanced Settings":
|
||||||
|
if self.kind.name != "basic":
|
||||||
|
for key, value in self.window.cache["asio"].items():
|
||||||
|
if "INPUT" in key:
|
||||||
|
identifier, i = key.split("||")
|
||||||
|
partial = util.get_channel_identifier_list(self.window.vm)[int(i)]
|
||||||
|
self.popup[f"{identifier}||{partial}"].update(value=value)
|
||||||
|
elif "OUTPUT" in key:
|
||||||
|
self.popup[key].update(value=value)
|
||||||
|
|
||||||
|
if self.popup.Title == "Advanced Compressor":
|
||||||
|
for param in ("RATIO", "THRESHOLD", "ATTACK", "RELEASE", "KNEE"):
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(
|
||||||
|
value=getattr(self.window.vm.strip[self.index].comp, param.lower())
|
||||||
|
)
|
||||||
|
self.popup["COMPRESSOR||SLIDER INPUT GAIN"].update(value=self.window.vm.strip[self.index].comp.gainin)
|
||||||
|
self.popup["COMPRESSOR||SLIDER OUTPUT GAIN"].update(value=self.window.vm.strip[self.index].comp.gainout)
|
||||||
|
elif self.popup.Title == "Advanced Gate":
|
||||||
|
for param in ("THRESHOLD", "DAMPING", "BPSIDECHAIN", "ATTACK", "HOLD", "RELEASE"):
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(
|
||||||
|
value=getattr(self.window.vm.strip[self.index].gate, param.lower())
|
||||||
|
)
|
||||||
|
|
||||||
def rename(self, message, index, title=None, tab=None):
|
def rename(self, message, index, title=None, tab=None):
|
||||||
if "Strip" in tab:
|
if "Strip" in tab:
|
||||||
if index < self.kind.phys_in:
|
if index < self.kind.phys_in:
|
||||||
@@ -83,7 +108,7 @@ class Popup:
|
|||||||
match parsed_cmd := self.window.parser.match.parseString(event):
|
match parsed_cmd := self.window.parser.match.parseString(event):
|
||||||
case [[button], ["FOCUS", "IN"]]:
|
case [[button], ["FOCUS", "IN"]]:
|
||||||
self.window.nvda.speak(button)
|
self.window.nvda.speak(button)
|
||||||
case [[button], ["KEY", "ENTER"]]:
|
case [_, ["KEY", "ENTER"]]:
|
||||||
popup.find_element_with_focus().click()
|
popup.find_element_with_focus().click()
|
||||||
case ["Ok"]:
|
case ["Ok"]:
|
||||||
data = values
|
data = values
|
||||||
@@ -93,6 +118,50 @@ class Popup:
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def advanced_settings(self, title):
|
def advanced_settings(self, title):
|
||||||
|
def add_patch_asio_input_to_strips(layout, i):
|
||||||
|
nums = list(range(99))
|
||||||
|
layout.append(
|
||||||
|
[
|
||||||
|
psg.Spin(
|
||||||
|
nums,
|
||||||
|
initial_value=self.window.cache["asio"][
|
||||||
|
f"ASIO INPUT SPINBOX||{util.get_asio_input_spinbox_index(0, i)}"
|
||||||
|
],
|
||||||
|
size=2,
|
||||||
|
enable_events=True,
|
||||||
|
key=f"ASIO INPUT SPINBOX||IN{i} 0",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
layout.append(
|
||||||
|
[
|
||||||
|
psg.Spin(
|
||||||
|
nums,
|
||||||
|
initial_value=self.window.cache["asio"][
|
||||||
|
f"ASIO INPUT SPINBOX||{util.get_asio_input_spinbox_index(1, i)}"
|
||||||
|
],
|
||||||
|
size=2,
|
||||||
|
enable_events=True,
|
||||||
|
key=f"ASIO INPUT SPINBOX||IN{i} 1",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_patch_bus_to_asio_outputs(layout, i):
|
||||||
|
nums = list(range(99))
|
||||||
|
layout.append(
|
||||||
|
[
|
||||||
|
psg.Spin(
|
||||||
|
nums,
|
||||||
|
initial_value=self.window.cache["asio"][f"ASIO OUTPUT A{i} SPINBOX||{j}"],
|
||||||
|
size=2,
|
||||||
|
enable_events=True,
|
||||||
|
key=f"ASIO OUTPUT A{i} SPINBOX||{j}",
|
||||||
|
)
|
||||||
|
for j in range(self.kind.num_bus)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def _make_buffering_frame() -> psg.Frame:
|
def _make_buffering_frame() -> psg.Frame:
|
||||||
buffer = [
|
buffer = [
|
||||||
[
|
[
|
||||||
@@ -108,27 +177,79 @@ class Popup:
|
|||||||
return psg.Frame("BUFFERING", buffer)
|
return psg.Frame("BUFFERING", buffer)
|
||||||
|
|
||||||
layout = []
|
layout = []
|
||||||
|
if self.kind.name != "basic":
|
||||||
|
inner = []
|
||||||
|
patch_input_to_strips = ([] for _ in range(self.kind.phys_in))
|
||||||
|
for i, checkbox_list in enumerate(patch_input_to_strips):
|
||||||
|
[step(checkbox_list, i + 1) for step in (add_patch_asio_input_to_strips,)]
|
||||||
|
inner.append(psg.Frame(f"In#{i + 1}", checkbox_list))
|
||||||
|
layout.append([psg.Frame("PATCH ASIO Inputs to Strips", [inner])])
|
||||||
|
|
||||||
|
inner_2 = []
|
||||||
|
patch_output_to_bus = ([] for _ in range(self.kind.phys_out - 1))
|
||||||
|
for i, checkbox_list in enumerate(patch_output_to_bus):
|
||||||
|
[step(checkbox_list, i + 2) for step in (add_patch_bus_to_asio_outputs,)]
|
||||||
|
inner_2.append([psg.Frame(f"OutA{i + 2}", checkbox_list)])
|
||||||
|
layout.append([psg.Frame("PATCH BUS to A1 ASIO Outputs", [*inner_2])])
|
||||||
|
|
||||||
steps = (_make_buffering_frame,)
|
steps = (_make_buffering_frame,)
|
||||||
for step in steps:
|
for step in steps:
|
||||||
layout.append([step()])
|
layout.append([step()])
|
||||||
layout.append([psg.Button("Exit", size=(8, 2))])
|
layout.append([psg.Button("Exit", size=(8, 2))])
|
||||||
|
|
||||||
popup = psg.Window(title, layout, finalize=True)
|
self.popup = psg.Window(title, layout, finalize=True)
|
||||||
|
if self.kind.name != "basic":
|
||||||
|
for i in range(self.kind.phys_out):
|
||||||
|
self.popup[f"ASIO INPUT SPINBOX||IN{i + 1} 0"].Widget.config(state="readonly")
|
||||||
|
self.popup[f"ASIO INPUT SPINBOX||IN{i + 1} 1"].Widget.config(state="readonly")
|
||||||
|
for i in range(self.kind.phys_out - 1):
|
||||||
|
for j in range(self.kind.num_bus):
|
||||||
|
self.popup[f"ASIO OUTPUT A{i + 2} SPINBOX||{j}"].Widget.config(state="readonly")
|
||||||
|
if self.kind.name != "basic":
|
||||||
|
for i in range(self.kind.phys_out):
|
||||||
|
self.popup[f"ASIO INPUT SPINBOX||IN{i + 1} 0"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup[f"ASIO INPUT SPINBOX||IN{i + 1} 1"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
for i in range(self.kind.phys_out - 1):
|
||||||
|
for j in range(self.kind.num_bus):
|
||||||
|
self.popup[f"ASIO OUTPUT A{i + 2} SPINBOX||{j}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
buttonmenu_opts = {"takefocus": 1, "highlightthickness": 1}
|
buttonmenu_opts = {"takefocus": 1, "highlightthickness": 1}
|
||||||
for driver in ("MME", "WDM", "KS", "ASIO"):
|
for driver in ("MME", "WDM", "KS", "ASIO"):
|
||||||
popup[f"BUFFER {driver}"].Widget.config(**buttonmenu_opts)
|
self.popup[f"BUFFER {driver}"].Widget.config(**buttonmenu_opts)
|
||||||
popup[f"BUFFER {driver}"].bind("<FocusIn>", "||FOCUS IN")
|
self.popup[f"BUFFER {driver}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
popup[f"BUFFER {driver}"].bind("<space>", "||KEY SPACE", propagate=False)
|
self.popup[f"BUFFER {driver}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
popup[f"BUFFER {driver}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
self.popup[f"BUFFER {driver}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
popup["Exit"].bind("<FocusIn>", "||FOCUS IN")
|
self.popup["Exit"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
popup["Exit"].bind("<Return>", "||KEY ENTER")
|
self.popup["Exit"].bind("<Return>", "||KEY ENTER")
|
||||||
|
self.window.vm.observer.add(self.on_pdirty)
|
||||||
while True:
|
while True:
|
||||||
event, values = popup.read()
|
event, values = self.popup.read()
|
||||||
self.logger.debug(f"event::{event}")
|
self.logger.debug(f"event::{event}")
|
||||||
self.logger.debug(f"values::{values}")
|
self.logger.debug(f"values::{values}")
|
||||||
if event in (psg.WIN_CLOSED, "Exit"):
|
if event in (psg.WIN_CLOSED, "Exit"):
|
||||||
break
|
break
|
||||||
match parsed_cmd := self.window.parser.match.parseString(event):
|
match parsed_cmd := self.window.parser.match.parseString(event):
|
||||||
|
case [["ASIO", "INPUT", "SPINBOX"], [in_num, channel]]:
|
||||||
|
index = util.get_asio_input_spinbox_index(int(channel), int(in_num[-1]))
|
||||||
|
val = values[f"ASIO INPUT SPINBOX||{in_num} {channel}"]
|
||||||
|
self.window.vm.patch.asio[index].set(val)
|
||||||
|
channel = ("left", "right")[int(channel)]
|
||||||
|
self.window.nvda.speak(str(val))
|
||||||
|
case [["ASIO", "INPUT", "SPINBOX"], [in_num, channel], ["FOCUS", "IN"]]:
|
||||||
|
if self.popup.find_element_with_focus() is not None:
|
||||||
|
val = values[f"ASIO INPUT SPINBOX||{in_num} {channel}"]
|
||||||
|
channel = ("left", "right")[int(channel)]
|
||||||
|
num = int(in_num[-1])
|
||||||
|
self.window.nvda.speak(f"Patch ASIO inputs to strips IN#{num} {channel} {val}")
|
||||||
|
case [["ASIO", "OUTPUT", param, "SPINBOX"], [index]]:
|
||||||
|
target = getattr(self.window.vm.patch, param)[int(index)]
|
||||||
|
target.set(values[event])
|
||||||
|
self.window.nvda.speak(str(values[event]))
|
||||||
|
case [["ASIO", "OUTPUT", param, "SPINBOX"], [index], ["FOCUS", "IN"]]:
|
||||||
|
if self.popup.find_element_with_focus() is not None:
|
||||||
|
val = values[f"ASIO OUTPUT {param} SPINBOX||{index}"]
|
||||||
|
self.window.nvda.speak(
|
||||||
|
f"Patch BUS to A1 ASIO Outputs OUT {param} channel {int(index) + 1} {val}"
|
||||||
|
)
|
||||||
case ["BUFFER MME" | "BUFFER WDM" | "BUFFER KS" | "BUFFER ASIO"]:
|
case ["BUFFER MME" | "BUFFER WDM" | "BUFFER KS" | "BUFFER ASIO"]:
|
||||||
if values[event] == "Default":
|
if values[event] == "Default":
|
||||||
if "MME" in event:
|
if "MME" in event:
|
||||||
@@ -148,10 +269,554 @@ class Popup:
|
|||||||
val = int(self.window.vm.get(f"option.buffer.{driver.lower()}"))
|
val = int(self.window.vm.get(f"option.buffer.{driver.lower()}"))
|
||||||
self.window.nvda.speak(f"{driver} BUFFER {val if val else 'default'}")
|
self.window.nvda.speak(f"{driver} BUFFER {val if val else 'default'}")
|
||||||
case [["BUFFER", driver], ["KEY", "SPACE" | "ENTER"]]:
|
case [["BUFFER", driver], ["KEY", "SPACE" | "ENTER"]]:
|
||||||
util.open_context_menu_for_buttonmenu(popup, f"BUFFER {driver}")
|
util.open_context_menu_for_buttonmenu(self.popup, f"BUFFER {driver}")
|
||||||
case [[button], ["FOCUS", "IN"]]:
|
case [[button], ["FOCUS", "IN"]]:
|
||||||
self.window.nvda.speak(button)
|
self.window.nvda.speak(button)
|
||||||
case [[button], ["KEY", "ENTER"]]:
|
case [_, ["KEY", "ENTER"]]:
|
||||||
popup.find_element_with_focus().click()
|
self.popup.find_element_with_focus().click()
|
||||||
self.logger.debug(f"parsed::{parsed_cmd}")
|
self.logger.debug(f"parsed::{parsed_cmd}")
|
||||||
popup.close()
|
self.window.vm.observer.remove(self.on_pdirty)
|
||||||
|
self.popup.close()
|
||||||
|
|
||||||
|
def compressor(self, index, title=None):
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
def _make_comp_frame() -> psg.Frame:
|
||||||
|
comp_layout = [
|
||||||
|
[LabelSliderAdvanced(self.window, index, param, CompSlider)]
|
||||||
|
for param in ("INPUT GAIN", "RATIO", "THRESHOLD", "ATTACK", "RELEASE", "KNEE", "OUTPUT GAIN")
|
||||||
|
]
|
||||||
|
return psg.Frame("ADVANCED COMPRESSOR", comp_layout)
|
||||||
|
|
||||||
|
layout = []
|
||||||
|
steps = (_make_comp_frame,)
|
||||||
|
for step in steps:
|
||||||
|
layout.append([step()])
|
||||||
|
layout.append([psg.Button("MAKEUP", size=(12, 1)), psg.Button("Exit", size=(8, 1))])
|
||||||
|
|
||||||
|
self.popup = psg.Window(title, layout, return_keyboard_events=False, finalize=True)
|
||||||
|
buttonmenu_opts = {"takefocus": 1, "highlightthickness": 1}
|
||||||
|
for param in ("INPUT GAIN", "RATIO", "THRESHOLD", "ATTACK", "RELEASE", "KNEE", "OUTPUT GAIN"):
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].Widget.config(**buttonmenu_opts)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind("<FocusOut>", "||FOCUS OUT")
|
||||||
|
for event in ("KeyPress", "KeyRelease"):
|
||||||
|
event_id = event.removeprefix("Key").upper()
|
||||||
|
for direction in ("Left", "Right", "Up", "Down"):
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind(
|
||||||
|
f"<{event}-{direction}>", f"||KEY {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind(
|
||||||
|
f"<Shift-{event}-{direction}>", f"||KEY SHIFT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind(
|
||||||
|
f"<Control-{event}-{direction}>", f"||KEY CTRL {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
if param == "RELEASE":
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind(
|
||||||
|
f"<Alt-{event}-{direction}>", f"||KEY ALT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind(
|
||||||
|
f"<Control-Alt-{event}-{direction}>", f"||KEY CTRL ALT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
|
||||||
|
self.popup["MAKEUP"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup["MAKEUP"].bind("<Return>", "||KEY ENTER")
|
||||||
|
self.popup["Exit"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup["Exit"].bind("<Return>", "||KEY ENTER")
|
||||||
|
self.window.vm.observer.add(self.on_pdirty)
|
||||||
|
while True:
|
||||||
|
event, values = self.popup.read()
|
||||||
|
self.logger.debug(f"event::{event}")
|
||||||
|
self.logger.debug(f"values::{values}")
|
||||||
|
if event in (psg.WIN_CLOSED, "Exit"):
|
||||||
|
break
|
||||||
|
match parsed_cmd := self.window.parser.match.parseString(event):
|
||||||
|
case [["COMPRESSOR"], ["SLIDER", param]]:
|
||||||
|
setattr(self.window.vm.strip[index].comp, param.lower(), values[event])
|
||||||
|
case [["COMPRESSOR"], ["SLIDER", param], ["FOCUS", "IN"]]:
|
||||||
|
self.window.nvda.speak(f"{param} {values[f'COMPRESSOR||SLIDER {param}']}")
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].comp, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
if param == "KNEE":
|
||||||
|
val += 0.1
|
||||||
|
else:
|
||||||
|
val += 1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
if param == "KNEE":
|
||||||
|
val -= 0.1
|
||||||
|
else:
|
||||||
|
val -= 1
|
||||||
|
|
||||||
|
val = CompSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].comp, param.lower(), val)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "KNEE":
|
||||||
|
self.window.nvda.speak(str(round(val, 2)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "CTRL", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].comp, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
if param == "KNEE":
|
||||||
|
val += 0.3
|
||||||
|
elif param == "RELEASE":
|
||||||
|
val += 5
|
||||||
|
else:
|
||||||
|
val += 3
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
if param == "KNEE":
|
||||||
|
val -= 0.3
|
||||||
|
elif param == "RELEASE":
|
||||||
|
val -= 5
|
||||||
|
else:
|
||||||
|
val -= 3
|
||||||
|
|
||||||
|
val = CompSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].comp, param.lower(), val)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "KNEE":
|
||||||
|
self.window.nvda.speak(str(round(val, 2)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "SHIFT", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].comp, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
if param == "KNEE":
|
||||||
|
val += 0.01
|
||||||
|
else:
|
||||||
|
val += 0.1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
if param == "KNEE":
|
||||||
|
val -= 0.01
|
||||||
|
else:
|
||||||
|
val -= 0.1
|
||||||
|
|
||||||
|
val = CompSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].comp, param.lower(), val)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "KNEE":
|
||||||
|
self.window.nvda.speak(str(round(val, 2)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "RELEASE"],
|
||||||
|
["KEY", "ALT", "LEFT" | "RIGHT" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = self.window.vm.strip[index].comp.release
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 10
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 10
|
||||||
|
|
||||||
|
val = util.check_bounds(val, (0, 5000))
|
||||||
|
self.window.vm.strip[index].comp.release = val
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "RELEASE"],
|
||||||
|
["KEY", "CTRL", "ALT", "LEFT" | "RIGHT" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = self.window.vm.strip[index].comp.release
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 50
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 50
|
||||||
|
|
||||||
|
val = util.check_bounds(val, (0, 5000))
|
||||||
|
self.window.vm.strip[index].comp.release = val
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
|
||||||
|
case [["COMPRESSOR"], ["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"]]:
|
||||||
|
if direction == "INPUT":
|
||||||
|
self.window.vm.strip[index].comp.gainin = values[event]
|
||||||
|
else:
|
||||||
|
self.window.vm.strip[index].comp.gainout = values[event]
|
||||||
|
case [["COMPRESSOR"], ["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"], ["FOCUS", "IN"]]:
|
||||||
|
label = f"{direction} GAIN"
|
||||||
|
self.window.nvda.speak(f"{label} {values[f'COMPRESSOR||SLIDER {label}']}")
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"],
|
||||||
|
["KEY", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
if direction == "INPUT":
|
||||||
|
val = self.window.vm.strip[index].comp.gainin
|
||||||
|
else:
|
||||||
|
val = self.window.vm.strip[index].comp.gainout
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 1
|
||||||
|
|
||||||
|
val = util.check_bounds(val, (-24, 24))
|
||||||
|
if direction == "INPUT":
|
||||||
|
self.window.vm.strip[index].comp.gainin = val
|
||||||
|
else:
|
||||||
|
self.window.vm.strip[index].comp.gainout = val
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {direction} GAIN"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"],
|
||||||
|
["KEY", "CTRL", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
if direction == "INPUT":
|
||||||
|
val = self.window.vm.strip[index].comp.gainin
|
||||||
|
else:
|
||||||
|
val = self.window.vm.strip[index].comp.gainout
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 3
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 3
|
||||||
|
|
||||||
|
val = util.check_bounds(val, (-24, 24))
|
||||||
|
if direction == "INPUT":
|
||||||
|
self.window.vm.strip[index].comp.gainin = val
|
||||||
|
else:
|
||||||
|
self.window.vm.strip[index].comp.gainout = val
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {direction} GAIN"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"],
|
||||||
|
["KEY", "SHIFT", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
if direction == "INPUT":
|
||||||
|
val = self.window.vm.strip[index].comp.gainin
|
||||||
|
else:
|
||||||
|
val = self.window.vm.strip[index].comp.gainout
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 0.1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 0.1
|
||||||
|
|
||||||
|
val = util.check_bounds(val, (-24, 24))
|
||||||
|
if direction == "INPUT":
|
||||||
|
self.window.vm.strip[index].comp.gainin = val
|
||||||
|
else:
|
||||||
|
self.window.vm.strip[index].comp.gainout = val
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {direction} GAIN"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
|
||||||
|
case [
|
||||||
|
["COMPRESSOR"],
|
||||||
|
["SLIDER", "INPUT" | "OUTPUT" as direction, "GAIN"],
|
||||||
|
["KEY", "CTRL", "SHIFT", "R"],
|
||||||
|
]:
|
||||||
|
if direction == "INPUT":
|
||||||
|
self.window.vm.strip[index].comp.gainin = 0
|
||||||
|
else:
|
||||||
|
self.window.vm.strip[index].comp.gainout = 0
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {direction} GAIN"].update(value=0)
|
||||||
|
self.window.nvda.speak(str(0))
|
||||||
|
case [["COMPRESSOR"], ["SLIDER", param], ["KEY", "CTRL", "SHIFT", "R"]]:
|
||||||
|
match param:
|
||||||
|
case "RATIO":
|
||||||
|
val = 1
|
||||||
|
case "THRESHOLD":
|
||||||
|
val = -20
|
||||||
|
case "ATTACK":
|
||||||
|
val = 10
|
||||||
|
case "RELEASE":
|
||||||
|
val = 50
|
||||||
|
case "KNEE":
|
||||||
|
val = 0.5
|
||||||
|
setattr(self.window.vm.strip[index].comp, param.lower(), val)
|
||||||
|
self.popup[f"COMPRESSOR||SLIDER {param}"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
|
||||||
|
case ["MAKEUP"]:
|
||||||
|
val = not self.window.vm.strip[index].comp.makeup
|
||||||
|
self.window.vm.strip[index].comp.makeup = val
|
||||||
|
self.window.nvda.speak("on" if val else "off")
|
||||||
|
case [[button], ["FOCUS", "IN"]]:
|
||||||
|
if button == "MAKEUP":
|
||||||
|
self.window.nvda.speak(
|
||||||
|
f"{button} {'on' if self.window.vm.strip[index].comp.makeup else 'off'}"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(button)
|
||||||
|
case [_, ["KEY", "ENTER"]]:
|
||||||
|
self.popup.find_element_with_focus().click()
|
||||||
|
self.logger.debug(f"parsed::{parsed_cmd}")
|
||||||
|
self.window.vm.observer.remove(self.on_pdirty)
|
||||||
|
self.popup.close()
|
||||||
|
|
||||||
|
def gate(self, index, title=None):
|
||||||
|
self.index = index
|
||||||
|
|
||||||
|
def _make_gate_frame() -> psg.Frame:
|
||||||
|
gate_layout = [
|
||||||
|
[LabelSliderAdvanced(self.window, index, param, GateSlider)]
|
||||||
|
for param in ("THRESHOLD", "DAMPING", "BPSIDECHAIN", "ATTACK", "HOLD", "RELEASE")
|
||||||
|
]
|
||||||
|
return psg.Frame("ADVANCED GATE", gate_layout)
|
||||||
|
|
||||||
|
layout = []
|
||||||
|
steps = (_make_gate_frame,)
|
||||||
|
for step in steps:
|
||||||
|
layout.append([step()])
|
||||||
|
layout.append([psg.Button("Exit", size=(8, 1))])
|
||||||
|
|
||||||
|
self.popup = psg.Window(title, layout, return_keyboard_events=False, finalize=True)
|
||||||
|
buttonmenu_opts = {"takefocus": 1, "highlightthickness": 1}
|
||||||
|
for param in ("THRESHOLD", "DAMPING", "BPSIDECHAIN", "ATTACK", "HOLD", "RELEASE"):
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].Widget.config(**buttonmenu_opts)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind("<FocusOut>", "||FOCUS OUT")
|
||||||
|
for event in ("KeyPress", "KeyRelease"):
|
||||||
|
event_id = event.removeprefix("Key").upper()
|
||||||
|
for direction in ("Left", "Right", "Up", "Down"):
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind(
|
||||||
|
f"<{event}-{direction}>", f"||KEY {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind(
|
||||||
|
f"<Shift-{event}-{direction}>", f"||KEY SHIFT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind(
|
||||||
|
f"<Control-{event}-{direction}>", f"||KEY CTRL {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
if param in ("BPSIDECHAIN", "ATTACK", "HOLD", "RELEASE"):
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind(
|
||||||
|
f"<Alt-{event}-{direction}>", f"||KEY ALT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind(
|
||||||
|
f"<Control-Alt-{event}-{direction}>", f"||KEY CTRL ALT {direction.upper()} {event_id}"
|
||||||
|
)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
|
||||||
|
self.popup["Exit"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self.popup["Exit"].bind("<Return>", "||KEY ENTER")
|
||||||
|
self.window.vm.observer.add(self.on_pdirty)
|
||||||
|
while True:
|
||||||
|
event, values = self.popup.read()
|
||||||
|
self.logger.debug(f"event::{event}")
|
||||||
|
self.logger.debug(f"values::{values}")
|
||||||
|
if event in (psg.WIN_CLOSED, "Exit"):
|
||||||
|
break
|
||||||
|
match parsed_cmd := self.window.parser.match.parseString(event):
|
||||||
|
case [["GATE"], ["SLIDER", param]]:
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), values[event])
|
||||||
|
case [["GATE"], ["SLIDER", param], ["FOCUS", "IN"]]:
|
||||||
|
label_map = {
|
||||||
|
"DAMPING": "Damping Max",
|
||||||
|
"BPSIDECHAIN": "BP Sidechain",
|
||||||
|
}
|
||||||
|
self.window.nvda.speak(f"{label_map.get(param, param)} {values[f'GATE||SLIDER {param}']}")
|
||||||
|
|
||||||
|
case [
|
||||||
|
["GATE"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].gate, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 1
|
||||||
|
|
||||||
|
val = GateSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "BPSIDECHAIN":
|
||||||
|
self.window.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["GATE"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "CTRL", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].gate, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 3
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 3
|
||||||
|
|
||||||
|
val = GateSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "BPSIDECHAIN":
|
||||||
|
self.window.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["GATE"],
|
||||||
|
["SLIDER", param],
|
||||||
|
["KEY", "SHIFT", "LEFT" | "RIGHT" | "UP" | "DOWN" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].gate, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 0.1
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 0.1
|
||||||
|
|
||||||
|
val = GateSlider.check_bounds(param, val)
|
||||||
|
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "BPSIDECHAIN":
|
||||||
|
self.window.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["GATE"],
|
||||||
|
["SLIDER", "BPSIDECHAIN" | "ATTACK" | "HOLD" | "RELEASE" as param],
|
||||||
|
["KEY", "ALT", "LEFT" | "RIGHT" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].gate, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 10
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 10
|
||||||
|
|
||||||
|
val = GateSlider.check_bounds(param, val)
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "BPSIDECHAIN":
|
||||||
|
self.window.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [
|
||||||
|
["GATE"],
|
||||||
|
["SLIDER", "BPSIDECHAIN" | "ATTACK" | "HOLD" | "RELEASE" as param],
|
||||||
|
["KEY", "CTRL", "ALT", "LEFT" | "RIGHT" as input_direction, "PRESS" | "RELEASE" as e],
|
||||||
|
]:
|
||||||
|
if e == "PRESS":
|
||||||
|
self.window.vm.event.pdirty = False
|
||||||
|
val = getattr(self.window.vm.strip[index].gate, param.lower())
|
||||||
|
|
||||||
|
match input_direction:
|
||||||
|
case "RIGHT" | "UP":
|
||||||
|
val += 50
|
||||||
|
case "LEFT" | "DOWN":
|
||||||
|
val -= 50
|
||||||
|
|
||||||
|
val = GateSlider.check_bounds(param, val)
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
if param == "BPSIDECHAIN":
|
||||||
|
self.window.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
else:
|
||||||
|
self.window.vm.event.pdirty = True
|
||||||
|
case [["GATE"], ["SLIDER", param], ["KEY", "CTRL", "SHIFT", "R"]]:
|
||||||
|
match param:
|
||||||
|
case "THRESHOLD":
|
||||||
|
val = -60
|
||||||
|
case "DAMPING":
|
||||||
|
val = -60
|
||||||
|
case "BPSIDECHAIN":
|
||||||
|
val = 100
|
||||||
|
case "ATTACK":
|
||||||
|
val = 0
|
||||||
|
case "HOLD":
|
||||||
|
val = 500
|
||||||
|
case "RELEASE":
|
||||||
|
val = 1000
|
||||||
|
setattr(self.window.vm.strip[index].gate, param.lower(), val)
|
||||||
|
self.popup[f"GATE||SLIDER {param}"].update(value=val)
|
||||||
|
self.window.nvda.speak(str(round(val, 1)))
|
||||||
|
|
||||||
|
case [[button], ["FOCUS", "IN"]]:
|
||||||
|
self.window.nvda.speak(button)
|
||||||
|
case [_, ["KEY", "ENTER"]]:
|
||||||
|
self.popup.find_element_with_focus().click()
|
||||||
|
|
||||||
|
self.logger.debug(f"parsed::{parsed_cmd}")
|
||||||
|
self.window.vm.observer.remove(self.on_pdirty)
|
||||||
|
self.popup.close()
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
|
import PySimpleGUI as psg
|
||||||
|
|
||||||
def get_asio_checkbox_index(channel, num) -> int:
|
|
||||||
|
def get_asio_input_spinbox_index(channel, num) -> int:
|
||||||
if channel == 0:
|
if channel == 0:
|
||||||
return 2 * num - 2
|
return 2 * num - 2
|
||||||
return 2 * num - 1
|
return 2 * num - 1
|
||||||
@@ -48,8 +50,11 @@ def get_patch_composite_list(kind) -> list:
|
|||||||
for i in range(kind.phys_out):
|
for i in range(kind.phys_out):
|
||||||
[temp.append(f"IN#{i + 1} {channel}") for channel in ("Left", "Right")]
|
[temp.append(f"IN#{i + 1} {channel}") for channel in ("Left", "Right")]
|
||||||
for i in range(kind.phys_out, kind.phys_out + kind.virt_out):
|
for i in range(kind.phys_out, kind.phys_out + kind.virt_out):
|
||||||
[temp.append(f"IN#{i + 1} {channel}") for channel in ("Left", "Right", "Center", "LFE", "SL", "SR", "BL", "BR")]
|
[
|
||||||
temp.append(f"BUS Channel")
|
temp.append(f"IN#{i + 1} {channel}")
|
||||||
|
for channel in ("Left", "Right", "Center", "LFE", "SL", "SR", "BL", "BR")
|
||||||
|
]
|
||||||
|
temp.append("BUS Channel")
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
|
|
||||||
@@ -108,6 +113,24 @@ def get_channel_identifier_list(vm) -> list:
|
|||||||
return identifiers
|
return identifiers
|
||||||
|
|
||||||
|
|
||||||
|
_bus_mode_map = {
|
||||||
|
"normal": "Normal",
|
||||||
|
"amix": "Mix Down A",
|
||||||
|
"bmix": "Mix Down B",
|
||||||
|
"repeat": "Stereo Repeat",
|
||||||
|
"composite": "Composite",
|
||||||
|
"tvmix": "Up Mix TV",
|
||||||
|
"upmix21": "Up Mix 2.1",
|
||||||
|
"upmix41": "Up Mix 4.1",
|
||||||
|
"upmix61": "Up Mix 6.1",
|
||||||
|
"centeronly": "Center Only",
|
||||||
|
"lfeonly": "Low Frequency Effect Only",
|
||||||
|
"rearonly": "Rear Only",
|
||||||
|
}
|
||||||
|
|
||||||
|
_bus_mode_map_reversed = dict((reversed(item) for item in _bus_mode_map.items()))
|
||||||
|
|
||||||
|
|
||||||
def get_bus_modes(vm) -> list:
|
def get_bus_modes(vm) -> list:
|
||||||
if vm.kind.name == "basic":
|
if vm.kind.name == "basic":
|
||||||
return [
|
return [
|
||||||
@@ -157,3 +180,63 @@ def get_full_slider_params(i, kind) -> Iterable:
|
|||||||
if kind.name == "basic":
|
if kind.name == "basic":
|
||||||
params.remove("LIMIT")
|
params.remove("LIMIT")
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
|
||||||
|
def get_slider_modes() -> Iterable:
|
||||||
|
return (
|
||||||
|
"GAIN MODE",
|
||||||
|
"BASS MODE",
|
||||||
|
"MID MODE",
|
||||||
|
"TREBLE MODE",
|
||||||
|
"AUDIBILITY MODE",
|
||||||
|
"COMP MODE",
|
||||||
|
"GATE MODE",
|
||||||
|
"DENOISER MODE",
|
||||||
|
"LIMIT MODE",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_bus_assignments(kind) -> list:
|
||||||
|
return [f"A{i}" for i in range(1, kind.phys_out + 1)] + [f"B{i}" for i in range(1, kind.virt_out + 1)]
|
||||||
|
|
||||||
|
|
||||||
|
psg.theme_add_new(
|
||||||
|
"HighContrast",
|
||||||
|
{
|
||||||
|
"BACKGROUND": "#FFFFFF",
|
||||||
|
"TEXT": "#000000",
|
||||||
|
"INPUT": "#FAF9F6",
|
||||||
|
"TEXT_INPUT": "#000000",
|
||||||
|
"SCROLL": "#FAF9F6",
|
||||||
|
"BUTTON": ("#000000", "#FFFFFF"),
|
||||||
|
"PROGRESS": ("#000000", "#FFFFFF"),
|
||||||
|
"BORDER": 2,
|
||||||
|
"SLIDER_DEPTH": 3,
|
||||||
|
"PROGRESS_DEPTH": 0,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_themes_list() -> list:
|
||||||
|
return [
|
||||||
|
"Bright Colors",
|
||||||
|
"Dark Blue 14",
|
||||||
|
"Dark Brown 2",
|
||||||
|
"Dark Brown 3",
|
||||||
|
"Dark Green 2",
|
||||||
|
"Dark Grey 2",
|
||||||
|
"Dark Teal1",
|
||||||
|
"Dark Teal6",
|
||||||
|
"Kayak",
|
||||||
|
"Light Blue 2",
|
||||||
|
"Light Brown 2",
|
||||||
|
"Light Brown 5",
|
||||||
|
"Light Green",
|
||||||
|
"Light Green 3",
|
||||||
|
"Light Grey 2",
|
||||||
|
"Light Purple",
|
||||||
|
"Neutral Blue",
|
||||||
|
"Reds",
|
||||||
|
"Sandy Beach",
|
||||||
|
"High Contrast",
|
||||||
|
]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import PySimpleGUI as psg
|
import PySimpleGUI as psg
|
||||||
|
|
||||||
from . import models, util
|
from . import configuration, models, util
|
||||||
from .builder import Builder
|
from .builder import Builder
|
||||||
from .nvda import Nvda
|
from .nvda import Nvda
|
||||||
from .parser import Parser
|
from .parser import Parser
|
||||||
@@ -12,18 +12,19 @@ from .popup import Popup
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
psg.theme("Dark Blue 3")
|
psg.theme(configuration.get("default_theme", "Dark Blue 3"))
|
||||||
|
if psg.theme() == "HighContrast":
|
||||||
|
psg.set_options(font=("Arial", 14))
|
||||||
|
|
||||||
|
|
||||||
class NVDAVMWindow(psg.Window):
|
class NVDAVMWindow(psg.Window):
|
||||||
"""Represents the main window of the Voicemeeter NVDA application"""
|
"""Represents the main window of the Voicemeeter NVDA application"""
|
||||||
|
|
||||||
SETTINGS = "settings.json"
|
|
||||||
|
|
||||||
def __init__(self, title, vm):
|
def __init__(self, title, vm):
|
||||||
self.vm = vm
|
self.vm = vm
|
||||||
self.kind = self.vm.kind
|
self.kind = self.vm.kind
|
||||||
self.logger = logger.getChild(type(self).__name__)
|
self.logger = logger.getChild(type(self).__name__)
|
||||||
|
self.logger.debug(f"loaded with theme: {psg.theme()}")
|
||||||
self.cache = {
|
self.cache = {
|
||||||
"hw_ins": models._make_hardware_ins_cache(self.vm),
|
"hw_ins": models._make_hardware_ins_cache(self.vm),
|
||||||
"hw_outs": models._make_hardware_outs_cache(self.vm),
|
"hw_outs": models._make_hardware_outs_cache(self.vm),
|
||||||
@@ -45,9 +46,9 @@ class NVDAVMWindow(psg.Window):
|
|||||||
for i in range(self.kind.phys_out):
|
for i in range(self.kind.phys_out):
|
||||||
self[f"HARDWARE OUT||A{i + 1}"].Widget.config(**buttonmenu_opts)
|
self[f"HARDWARE OUT||A{i + 1}"].Widget.config(**buttonmenu_opts)
|
||||||
if self.kind.name == "basic":
|
if self.kind.name == "basic":
|
||||||
self[f"HARDWARE OUT||A2"].Widget.config(**buttonmenu_opts)
|
self["HARDWARE OUT||A2"].Widget.config(**buttonmenu_opts)
|
||||||
if self.kind.name != "basic":
|
if self.kind.name != "basic":
|
||||||
[self[f"PATCH COMPOSITE||PC{i + 1}"].Widget.config(**buttonmenu_opts) for i in range(self.kind.phys_out)]
|
[self[f"PATCH COMPOSITE||PC{i + 1}"].Widget.config(**buttonmenu_opts) for i in range(self.kind.composite)]
|
||||||
slider_opts = {"takefocus": 1, "highlightthickness": 1}
|
slider_opts = {"takefocus": 1, "highlightthickness": 1}
|
||||||
for i in range(self.kind.num_strip):
|
for i in range(self.kind.num_strip):
|
||||||
for param in util.get_slider_params(i, self.kind):
|
for param in util.get_slider_params(i, self.kind):
|
||||||
@@ -57,22 +58,17 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts)
|
self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts)
|
||||||
for i in range(self.kind.num_bus):
|
for i in range(self.kind.num_bus):
|
||||||
self[f"BUS {i}||SLIDER GAIN"].Widget.config(**slider_opts)
|
self[f"BUS {i}||SLIDER GAIN"].Widget.config(**slider_opts)
|
||||||
if self.kind.name != "basic":
|
self[f"BUS {i}||MODE"].Widget.config(**buttonmenu_opts)
|
||||||
for i in range(self.kind.phys_out):
|
|
||||||
self[f"ASIO CHECKBOX||IN{i + 1} 0"].Widget.config(state="readonly")
|
|
||||||
self[f"ASIO CHECKBOX||IN{i + 1} 1"].Widget.config(state="readonly")
|
|
||||||
|
|
||||||
self.register_events()
|
self.register_events()
|
||||||
self["tabgroup"].set_focus()
|
self["tabgroup"].set_focus()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
settings_path = Path.cwd() / self.SETTINGS
|
settings_path = configuration.SETTINGS
|
||||||
if settings_path.exists():
|
if settings_path.exists():
|
||||||
try:
|
try:
|
||||||
with open(settings_path, "r") as f:
|
defaultconfig = Path(configuration.get("default_config", "")) # coerce the type
|
||||||
data = json.load(f)
|
if defaultconfig.is_file() and defaultconfig.exists():
|
||||||
defaultconfig = Path(data["default_config"])
|
|
||||||
if defaultconfig.exists():
|
|
||||||
self.vm.set("command.load", str(defaultconfig))
|
self.vm.set("command.load", str(defaultconfig))
|
||||||
self.logger.debug(f"config {defaultconfig} loaded")
|
self.logger.debug(f"config {defaultconfig} loaded")
|
||||||
self.TKroot.after(
|
self.TKroot.after(
|
||||||
@@ -81,7 +77,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
f"config {defaultconfig.stem} has been loaded",
|
f"config {defaultconfig.stem} has been loaded",
|
||||||
)
|
)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
self.logger.debug("no data in settings.json. silently continuing...")
|
self.logger.debug("no default_config in settings.json. silently continuing...")
|
||||||
|
|
||||||
self.vm.init_thread()
|
self.vm.init_thread()
|
||||||
self.vm.observer.add(self.on_pdirty)
|
self.vm.observer.add(self.on_pdirty)
|
||||||
@@ -123,10 +119,6 @@ class NVDAVMWindow(psg.Window):
|
|||||||
for i in range(self.kind.num_bus):
|
for i in range(self.kind.num_bus):
|
||||||
self[f"BUS {i}||SLIDER GAIN"].update(value=self.vm.bus[i].gain)
|
self[f"BUS {i}||SLIDER GAIN"].update(value=self.vm.bus[i].gain)
|
||||||
if self.kind.name != "basic":
|
if self.kind.name != "basic":
|
||||||
for key, value in self.cache["asio"].items():
|
|
||||||
identifier, i = key.split("||")
|
|
||||||
partial = util.get_channel_identifier_list(self.vm)[int(i)]
|
|
||||||
self[f"{identifier}||{partial}"].update(value=value)
|
|
||||||
for key, value in self.cache["insert"].items():
|
for key, value in self.cache["insert"].items():
|
||||||
identifier, i = key.split("||")
|
identifier, i = key.split("||")
|
||||||
partial = util.get_channel_identifier_list(self.vm)[int(i)]
|
partial = util.get_channel_identifier_list(self.vm)[int(i)]
|
||||||
@@ -179,30 +171,24 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self.bind(f"<Alt-Control-{event}-{direction}>", f"ALT CTRL {direction.upper()}||{event_id}")
|
self.bind(f"<Alt-Control-{event}-{direction}>", f"ALT CTRL {direction.upper()}||{event_id}")
|
||||||
|
|
||||||
# Hardware In
|
# Hardware In
|
||||||
for i in range(self.vm.kind.phys_in):
|
for i in range(self.kind.phys_in):
|
||||||
self[f"HARDWARE IN||{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"HARDWARE IN||{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"HARDWARE IN||{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
self[f"HARDWARE IN||{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
self[f"HARDWARE IN||{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
self[f"HARDWARE IN||{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
|
|
||||||
# Hardware Out
|
# Hardware Out
|
||||||
for i in range(self.vm.kind.phys_out):
|
for i in range(self.kind.phys_out):
|
||||||
self[f"HARDWARE OUT||A{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"HARDWARE OUT||A{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"HARDWARE OUT||A{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
self[f"HARDWARE OUT||A{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
self[f"HARDWARE OUT||A{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
self[f"HARDWARE OUT||A{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
if self.vm.kind.name == "basic":
|
if self.kind.name == "basic":
|
||||||
self[f"HARDWARE OUT||A2"].bind("<FocusIn>", "||FOCUS IN")
|
self["HARDWARE OUT||A2"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"HARDWARE OUT||A2"].bind("<space>", "||KEY SPACE", propagate=False)
|
self["HARDWARE OUT||A2"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
self[f"HARDWARE OUT||A2"].bind("<Return>", "||KEY ENTER", propagate=False)
|
self["HARDWARE OUT||A2"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
|
|
||||||
# Patch ASIO
|
|
||||||
if self.kind.name != "basic":
|
|
||||||
for i in range(self.kind.phys_out):
|
|
||||||
self[f"ASIO CHECKBOX||IN{i + 1} 0"].bind("<FocusIn>", "||FOCUS IN")
|
|
||||||
self[f"ASIO CHECKBOX||IN{i + 1} 1"].bind("<FocusIn>", "||FOCUS IN")
|
|
||||||
|
|
||||||
# Patch Composite
|
# Patch Composite
|
||||||
if self.kind.name != "basic":
|
if self.kind.name != "basic":
|
||||||
for i in range(self.vm.kind.phys_out):
|
for i in range(self.kind.composite):
|
||||||
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
@@ -236,7 +222,12 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
||||||
else:
|
else:
|
||||||
for param in ("MONO", "SOLO", "MUTE"):
|
if i == self.kind.phys_in + 1:
|
||||||
|
for param in ("KARAOKE", "SOLO", "MUTE"):
|
||||||
|
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
||||||
|
else:
|
||||||
|
for param in ("MC", "SOLO", "MUTE"):
|
||||||
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
||||||
|
|
||||||
@@ -260,13 +251,16 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self[f"STRIP {i}||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
|
self[f"STRIP {i}||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
|
||||||
|
|
||||||
# Bus Params
|
# Bus Params
|
||||||
params = ["MONO", "EQ", "MUTE", "MODE"]
|
params = ["MONO", "EQ", "MUTE"]
|
||||||
if self.vm.kind.name == "basic":
|
if self.kind.name == "basic":
|
||||||
params.remove("EQ")
|
params.remove("EQ")
|
||||||
for i in range(self.kind.num_bus):
|
for i in range(self.kind.num_bus):
|
||||||
for param in params:
|
for param in params:
|
||||||
self[f"BUS {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
self[f"BUS {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
self[f"BUS {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
self[f"BUS {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
||||||
|
self[f"BUS {i}||MODE"].bind("<FocusIn>", "||FOCUS IN")
|
||||||
|
self[f"BUS {i}||MODE"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||||
|
self[f"BUS {i}||MODE"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||||
|
|
||||||
# Bus Sliders
|
# Bus Sliders
|
||||||
for i in range(self.kind.num_bus):
|
for i in range(self.kind.num_bus):
|
||||||
@@ -300,18 +294,20 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self.logger.debug(f"values::{values}")
|
self.logger.debug(f"values::{values}")
|
||||||
if event in (psg.WIN_CLOSED, "Exit"):
|
if event in (psg.WIN_CLOSED, "Exit"):
|
||||||
break
|
break
|
||||||
elif event.endswith("MODE"):
|
elif event in util.get_slider_modes():
|
||||||
mode = event
|
mode = event
|
||||||
self.nvda.speak(f"{mode} enabled")
|
self.nvda.speak(f"{mode} enabled")
|
||||||
|
self.logger.debug(f"entered slider mode {mode}")
|
||||||
continue
|
continue
|
||||||
elif event == "ESCAPE":
|
elif event == "ESCAPE":
|
||||||
if mode:
|
if mode:
|
||||||
self.nvda.speak(f"{mode} disabled")
|
self.nvda.speak(f"{mode} disabled")
|
||||||
|
self.logger.debug(f"exited from slider mode {mode}")
|
||||||
mode = None
|
mode = None
|
||||||
continue
|
continue
|
||||||
|
|
||||||
match parsed_cmd := self.parser.match.parseString(event):
|
match parsed_cmd := self.parser.match.parseString(event):
|
||||||
# Slide mode
|
# Slider mode
|
||||||
case [["ALT", "LEFT" | "RIGHT" | "UP" | "DOWN" as direction], ["PRESS" | "RELEASE" as e]]:
|
case [["ALT", "LEFT" | "RIGHT" | "UP" | "DOWN" as direction], ["PRESS" | "RELEASE" as e]]:
|
||||||
if mode:
|
if mode:
|
||||||
self.write_event_value(f"SLIDER MODE {direction}||{e}", mode.split()[0])
|
self.write_event_value(f"SLIDER MODE {direction}||{e}", mode.split()[0])
|
||||||
@@ -423,7 +419,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
identifier, partial = focus.Key.split("||")
|
identifier, partial = focus.Key.split("||")
|
||||||
_, index = identifier.split()
|
_, index = identifier.split()
|
||||||
index = int(index)
|
index = int(index)
|
||||||
data = self.popup.rename("Label", index, title=f"Rename", tab=tab)
|
data = self.popup.rename("Label", index, title="Rename", tab=tab)
|
||||||
if not data: # cancel was pressed
|
if not data: # cancel was pressed
|
||||||
continue
|
continue
|
||||||
match tab:
|
match tab:
|
||||||
@@ -446,6 +442,23 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self[f"BUS {index}||LABEL"].update(value=label)
|
self[f"BUS {index}||LABEL"].update(value=label)
|
||||||
self.cache["labels"][f"BUS {index}||LABEL"] = label
|
self.cache["labels"][f"BUS {index}||LABEL"] = label
|
||||||
|
|
||||||
|
# Advanced popups (settings, comp, gate)
|
||||||
|
case ["CTRL-A"]:
|
||||||
|
match values["tabgroup"]:
|
||||||
|
case "tab||Settings":
|
||||||
|
self.write_event_value("ADVANCED SETTINGS", None)
|
||||||
|
case "tab||Physical Strip":
|
||||||
|
if values["tabgroup||Physical Strip"] == "tab||Physical Strip||sliders":
|
||||||
|
if focus := self.find_element_with_focus():
|
||||||
|
identifier, partial = focus.key.split("||")
|
||||||
|
_, index = identifier.split()
|
||||||
|
match self.kind.name:
|
||||||
|
case "potato":
|
||||||
|
if "SLIDER COMP" in partial:
|
||||||
|
self.popup.compressor(int(index), title="Advanced Compressor")
|
||||||
|
elif "SLIDER GATE" in partial:
|
||||||
|
self.popup.gate(int(index), title="Advanced Gate")
|
||||||
|
|
||||||
# Menus
|
# Menus
|
||||||
case [["Restart", "Audio", "Engine"], ["MENU"]]:
|
case [["Restart", "Audio", "Engine"], ["MENU"]]:
|
||||||
self.perform_long_operation(self.vm.command.restart, "ENGINE RESTART||END")
|
self.perform_long_operation(self.vm.command.restart, "ENGINE RESTART||END")
|
||||||
@@ -496,17 +509,27 @@ class NVDAVMWindow(psg.Window):
|
|||||||
file_types=(("XML", ".xml"),),
|
file_types=(("XML", ".xml"),),
|
||||||
):
|
):
|
||||||
filepath = Path(filepath)
|
filepath = Path(filepath)
|
||||||
with open(self.SETTINGS, "w") as f:
|
configuration.set("default_config", str(filepath))
|
||||||
json.dump({"default_config": str(filepath)}, f)
|
|
||||||
self.TKroot.after(
|
self.TKroot.after(
|
||||||
200,
|
200,
|
||||||
self.nvda.speak,
|
self.nvda.speak,
|
||||||
f"config {filepath.stem} set as default on startup",
|
f"config {filepath.stem} set as default on startup",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
with open(self.SETTINGS, "wb") as f:
|
configuration.delete("default_config")
|
||||||
f.truncate()
|
self.logger.debug("default_config removed from settings.json")
|
||||||
self.logger.debug("settings.json was truncated")
|
|
||||||
|
case [theme, ["MENU", "THEME"]]:
|
||||||
|
chosen = " ".join(theme)
|
||||||
|
if chosen == "Default":
|
||||||
|
chosen = "Dark Blue 3"
|
||||||
|
configuration.set("default_theme", chosen)
|
||||||
|
self.TKroot.after(
|
||||||
|
200,
|
||||||
|
self.nvda.speak,
|
||||||
|
f"theme {chosen} selected.",
|
||||||
|
)
|
||||||
|
self.logger.debug(f"theme {chosen} selected")
|
||||||
|
|
||||||
# Tabs
|
# Tabs
|
||||||
case ["tabgroup"] | [["tabgroup"], ["FOCUS", "IN"]]:
|
case ["tabgroup"] | [["tabgroup"], ["FOCUS", "IN"]]:
|
||||||
@@ -563,33 +586,25 @@ class NVDAVMWindow(psg.Window):
|
|||||||
case [["HARDWARE", "OUT"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
case [["HARDWARE", "OUT"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
||||||
util.open_context_menu_for_buttonmenu(self, f"HARDWARE OUT||{key}")
|
util.open_context_menu_for_buttonmenu(self, f"HARDWARE OUT||{key}")
|
||||||
|
|
||||||
# Patch ASIO
|
|
||||||
case [["ASIO", "CHECKBOX"], [in_num, channel]]:
|
|
||||||
index = util.get_asio_checkbox_index(int(channel), int(in_num[-1]))
|
|
||||||
val = values[f"ASIO CHECKBOX||{in_num} {channel}"]
|
|
||||||
self.vm.patch.asio[index].set(val)
|
|
||||||
channel = ("left", "right")[int(channel)]
|
|
||||||
self.nvda.speak(str(val))
|
|
||||||
case [["ASIO", "CHECKBOX"], [in_num, channel], ["FOCUS", "IN"]]:
|
|
||||||
if self.find_element_with_focus() is not None:
|
|
||||||
val = values[f"ASIO CHECKBOX||{in_num} {channel}"]
|
|
||||||
channel = ("left", "right")[int(channel)]
|
|
||||||
num = int(in_num[-1])
|
|
||||||
self.nvda.speak(f"Patch ASIO inputs to strips IN#{num} {channel} {val}")
|
|
||||||
|
|
||||||
# Patch COMPOSITE
|
# Patch COMPOSITE
|
||||||
case [["PATCH", "COMPOSITE"], [key]]:
|
case [["PATCH", "COMPOSITE"], [key]]:
|
||||||
val = values[f"PATCH COMPOSITE||{key}"]
|
val = values[f"PATCH COMPOSITE||{key}"]
|
||||||
index = int(key[-1]) - 1
|
index = int(key[-1]) - 1
|
||||||
self.vm.patch.composite[index].set(util.get_patch_composite_list(self.kind).index(val) + 1)
|
self.vm.patch.composite[index].set(util.get_patch_composite_list(self.kind).index(val) + 1)
|
||||||
self.TKroot.after(200, self.nvda.speak, f"PATCH COMPOSITE {key[-1]} set {val}")
|
self.TKroot.after(200, self.nvda.speak, val)
|
||||||
case [["PATCH", "COMPOSITE"], [key], ["FOCUS", "IN"]]:
|
case [["PATCH", "COMPOSITE"], [key], ["FOCUS", "IN"]]:
|
||||||
if self.find_element_with_focus() is not None:
|
if self.find_element_with_focus() is not None:
|
||||||
if values[f"PATCH COMPOSITE||{key}"]:
|
if values[f"PATCH COMPOSITE||{key}"]:
|
||||||
val = values[f"PATCH COMPOSITE||{key}"]
|
val = values[f"PATCH COMPOSITE||{key}"]
|
||||||
else:
|
else:
|
||||||
index = int(key[-1]) - 1
|
index = int(key[-1]) - 1
|
||||||
val = util.get_patch_composite_list(self.kind)[self.vm.patch.composite[index].get() - 1]
|
comp_index = self.vm.patch.composite[index].get()
|
||||||
|
comp_list = util.get_patch_composite_list(self.kind)
|
||||||
|
try:
|
||||||
|
val = comp_list[comp_index - 1]
|
||||||
|
except IndexError as e:
|
||||||
|
val = comp_list[-1]
|
||||||
|
self.logger.error(f"{type(e).__name__}: {e}")
|
||||||
self.nvda.speak(f"Patch COMPOSITE {key[-1]} {val}")
|
self.nvda.speak(f"Patch COMPOSITE {key[-1]} {val}")
|
||||||
case [["PATCH", "COMPOSITE"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
case [["PATCH", "COMPOSITE"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
||||||
util.open_context_menu_for_buttonmenu(self, f"PATCH COMPOSITE||{key}")
|
util.open_context_menu_for_buttonmenu(self, f"PATCH COMPOSITE||{key}")
|
||||||
@@ -620,7 +635,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self.write_event_value(f"INSERT CHECKBOX||{in_num} {channel}", val)
|
self.write_event_value(f"INSERT CHECKBOX||{in_num} {channel}", val)
|
||||||
|
|
||||||
# Advanced Settings
|
# Advanced Settings
|
||||||
case ["ADVANCED SETTINGS"] | ["CTRL-A"]:
|
case ["ADVANCED SETTINGS"]:
|
||||||
if values["tabgroup"] == "tab||Settings":
|
if values["tabgroup"] == "tab||Settings":
|
||||||
self.popup.advanced_settings(title="Advanced Settings")
|
self.popup.advanced_settings(title="Advanced Settings")
|
||||||
case [["ADVANCED", "SETTINGS"], ["FOCUS", "IN"]]:
|
case [["ADVANCED", "SETTINGS"], ["FOCUS", "IN"]]:
|
||||||
@@ -630,54 +645,36 @@ class NVDAVMWindow(psg.Window):
|
|||||||
|
|
||||||
# Strip Params
|
# Strip Params
|
||||||
case [["STRIP", index], [param]]:
|
case [["STRIP", index], [param]]:
|
||||||
label = self.cache["labels"][f"STRIP {index}||LABEL"]
|
|
||||||
match param:
|
match param:
|
||||||
case "MONO":
|
case "KARAOKE":
|
||||||
if int(index) < self.kind.phys_in:
|
opts = ["off", "k m", "k 1", "k 2", "k v"]
|
||||||
actual = param.lower()
|
|
||||||
elif int(index) == self.kind.phys_in + 1:
|
|
||||||
actual = "k"
|
|
||||||
else:
|
|
||||||
actual = "mc"
|
|
||||||
phonetic = {"k": "karaoke"}
|
|
||||||
if actual == "k":
|
|
||||||
next_val = self.vm.strip[int(index)].k + 1
|
next_val = self.vm.strip[int(index)].k + 1
|
||||||
if next_val == 4:
|
if next_val == len(opts):
|
||||||
next_val = 0
|
next_val = 0
|
||||||
setattr(self.vm.strip[int(index)], actual, next_val)
|
self.vm.strip[int(index)].k = next_val
|
||||||
self.cache["strip"][f"STRIP {index}||{param}"] = next_val
|
self.cache["strip"][f"STRIP {index}||{param}"] = next_val
|
||||||
self.nvda.speak(["off", "k m", "k 1", "k 2"][next_val])
|
self.nvda.speak(opts[next_val])
|
||||||
else:
|
case output if param in util._get_bus_assignments(self.kind):
|
||||||
val = not self.cache["strip"][f"STRIP {index}||{param}"]
|
val = not self.cache["strip"][f"STRIP {index}||{output}"]
|
||||||
setattr(self.vm.strip[int(index)], actual, val)
|
setattr(self.vm.strip[int(index)], output, val)
|
||||||
self.cache["strip"][f"STRIP {index}||{param}"] = val
|
self.cache["strip"][f"STRIP {index}||{output}"] = val
|
||||||
self.nvda.speak("on" if val else "off")
|
self.nvda.speak("on" if val else "off")
|
||||||
case _:
|
case _:
|
||||||
val = not self.cache["strip"][f"STRIP {index}||{param}"]
|
val = not self.cache["strip"][f"STRIP {index}||{param}"]
|
||||||
setattr(self.vm.strip[int(index)], param if param[0] in ("A", "B") else param.lower(), val)
|
setattr(self.vm.strip[int(index)], param.lower(), val)
|
||||||
self.cache["strip"][f"STRIP {index}||{param}"] = val
|
self.cache["strip"][f"STRIP {index}||{param}"] = val
|
||||||
self.nvda.speak("on" if val else "off")
|
self.nvda.speak("on" if val else "off")
|
||||||
case [["STRIP", index], [param], ["FOCUS", "IN"]]:
|
case [["STRIP", index], [param], ["FOCUS", "IN"]]:
|
||||||
if self.find_element_with_focus() is not None:
|
if self.find_element_with_focus() is not None:
|
||||||
val = self.cache["strip"][f"STRIP {index}||{param}"]
|
val = self.cache["strip"][f"STRIP {index}||{param}"]
|
||||||
match param:
|
phonetic = {"KARAOKE": "karaoke"}
|
||||||
case "MONO":
|
|
||||||
if int(index) < self.kind.phys_in:
|
|
||||||
actual = param.lower()
|
|
||||||
elif int(index) == self.kind.phys_in + 1:
|
|
||||||
actual = "k"
|
|
||||||
else:
|
|
||||||
actual = "mc"
|
|
||||||
case _:
|
|
||||||
actual = param
|
|
||||||
phonetic = {"k": "karaoke"}
|
|
||||||
label = self.cache["labels"][f"STRIP {index}||LABEL"]
|
label = self.cache["labels"][f"STRIP {index}||LABEL"]
|
||||||
if actual == "k":
|
if param == "KARAOKE":
|
||||||
self.nvda.speak(
|
self.nvda.speak(
|
||||||
f"{label} {phonetic.get(actual, actual)} {['off', 'k m', 'k 1', 'k 2'][self.cache['strip'][f'STRIP {int(index)}||{param}']]}"
|
f"{label} {phonetic.get(param, param)} {['off', 'k m', 'k 1', 'k 2', 'k v'][self.cache['strip'][f'STRIP {int(index)}||{param}']]}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.nvda.speak(f"{label} {phonetic.get(actual, actual)} {'on' if val else 'off'}")
|
self.nvda.speak(f"{label} {phonetic.get(param, param)} {'on' if val else 'off'}")
|
||||||
case [["STRIP", index], [param], ["KEY", "ENTER"]]:
|
case [["STRIP", index], [param], ["KEY", "ENTER"]]:
|
||||||
self.find_element_with_focus().click()
|
self.find_element_with_focus().click()
|
||||||
|
|
||||||
@@ -798,7 +795,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-40, 12))
|
val = util.check_bounds(val, (-40, 12))
|
||||||
self.vm.strip[int(index)].limit = val
|
self.vm.strip[int(index)].limit = val
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
||||||
self.nvda.speak(str(val))
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [
|
case [
|
||||||
@@ -865,7 +862,10 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-40, 12))
|
val = util.check_bounds(val, (-40, 12))
|
||||||
self.vm.strip[int(index)].limit = val
|
self.vm.strip[int(index)].limit = val
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
||||||
self.nvda.speak(f"{param} {val}")
|
if param == "LIMIT":
|
||||||
|
self.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [
|
case [
|
||||||
@@ -932,7 +932,10 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-40, 12))
|
val = util.check_bounds(val, (-40, 12))
|
||||||
self.vm.strip[int(index)].limit = val
|
self.vm.strip[int(index)].limit = val
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
|
||||||
self.nvda.speak(f"{param} {val}")
|
if param == "LIMIT":
|
||||||
|
self.nvda.speak(str(int(val)))
|
||||||
|
else:
|
||||||
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [["STRIP", index], ["SLIDER", param], ["KEY", "CTRL", "SHIFT", "R"]]:
|
case [["STRIP", index], ["SLIDER", param], ["KEY", "CTRL", "SHIFT", "R"]]:
|
||||||
@@ -941,6 +944,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
self.vm.strip[int(index)].gain = 0
|
self.vm.strip[int(index)].gain = 0
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
|
||||||
case "COMP" | "GATE" | "DENOISER":
|
case "COMP" | "GATE" | "DENOISER":
|
||||||
|
target = getattr(self.vm.strip[int(index)], param.lower())
|
||||||
setattr(target, "knob", 0)
|
setattr(target, "knob", 0)
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
|
||||||
case "AUDIBILITY":
|
case "AUDIBILITY":
|
||||||
@@ -952,7 +956,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
case "LIMIT":
|
case "LIMIT":
|
||||||
self.vm.strip[int(index)].limit = 12
|
self.vm.strip[int(index)].limit = 12
|
||||||
self[f"STRIP {index}||SLIDER {param}"].update(value=12)
|
self[f"STRIP {index}||SLIDER {param}"].update(value=12)
|
||||||
self.nvda.speak(f"{param} {12 if param == 'LABEL' else 0}")
|
self.nvda.speak(f"{12 if param == 'LIMIT' else 0}")
|
||||||
|
|
||||||
# Bus Params
|
# Bus Params
|
||||||
case [["BUS", index], [param]]:
|
case [["BUS", index], [param]]:
|
||||||
@@ -978,39 +982,26 @@ class NVDAVMWindow(psg.Window):
|
|||||||
"on" if val else "off",
|
"on" if val else "off",
|
||||||
)
|
)
|
||||||
case "MODE":
|
case "MODE":
|
||||||
bus_modes = util.get_bus_modes(self.vm)
|
chosen = util._bus_mode_map_reversed[values[event]]
|
||||||
next_index = bus_modes.index(val) + 1
|
setattr(self.vm.bus[int(index)].mode, chosen, True)
|
||||||
if next_index == len(bus_modes):
|
self.cache["bus"][event] = chosen
|
||||||
next_index = 0
|
|
||||||
next_bus = bus_modes[next_index]
|
|
||||||
phonetic = {
|
|
||||||
"amix": "Mix Down A",
|
|
||||||
"bmix": "Mix Down B",
|
|
||||||
"repeat": "Stereo Repeat",
|
|
||||||
"tvmix": "Up Mix TV",
|
|
||||||
"upmix21": "Up Mix 2.1",
|
|
||||||
"upmix41": "Up Mix 4.1",
|
|
||||||
"upmix61": "Up Mix 6.1",
|
|
||||||
"centeronly": "Center Only",
|
|
||||||
"lfeonly": "Low Frequency Effect Only",
|
|
||||||
"rearonly": "Rear Only",
|
|
||||||
}
|
|
||||||
setattr(self.vm.bus[int(index)].mode, next_bus, True)
|
|
||||||
self.cache["bus"][event] = next_bus
|
|
||||||
self.TKroot.after(
|
self.TKroot.after(
|
||||||
200,
|
200,
|
||||||
self.nvda.speak,
|
self.nvda.speak,
|
||||||
phonetic.get(next_bus, next_bus),
|
util._bus_mode_map[chosen],
|
||||||
)
|
)
|
||||||
case [["BUS", index], [param], ["FOCUS", "IN"]]:
|
case [["BUS", index], [param], ["FOCUS", "IN"]]:
|
||||||
if self.find_element_with_focus() is not None:
|
if self.find_element_with_focus() is not None:
|
||||||
label = self.cache["labels"][f"BUS {index}||LABEL"]
|
label = self.cache["labels"][f"BUS {index}||LABEL"]
|
||||||
val = self.cache["bus"][f"BUS {index}||{param}"]
|
val = self.cache["bus"][f"BUS {index}||{param}"]
|
||||||
if param == "MODE":
|
if param == "MODE":
|
||||||
self.nvda.speak(f"{label} bus {param} {val}")
|
self.nvda.speak(f"{label} bus {param} {util._bus_mode_map[val]}")
|
||||||
|
else:
|
||||||
|
self.nvda.speak(f"{label} {param} {'on' if val else 'off'}")
|
||||||
|
case [["BUS", index], [param], ["KEY", "SPACE" | "ENTER"]]:
|
||||||
|
if param == "MODE":
|
||||||
|
util.open_context_menu_for_buttonmenu(self, f"BUS {index}||MODE")
|
||||||
else:
|
else:
|
||||||
self.nvda.speak(f"{label} bus {param} {'on' if val else 'off'}")
|
|
||||||
case [["BUS", index], [param], ["KEY", "ENTER"]]:
|
|
||||||
self.find_element_with_focus().click()
|
self.find_element_with_focus().click()
|
||||||
|
|
||||||
# Bus Sliders
|
# Bus Sliders
|
||||||
@@ -1041,7 +1032,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-60, 12))
|
val = util.check_bounds(val, (-60, 12))
|
||||||
self.vm.bus[int(index)].gain = val
|
self.vm.bus[int(index)].gain = val
|
||||||
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
||||||
self.nvda.speak(str(val))
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [
|
case [
|
||||||
@@ -1060,7 +1051,7 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-60, 12))
|
val = util.check_bounds(val, (-60, 12))
|
||||||
self.vm.bus[int(index)].gain = val
|
self.vm.bus[int(index)].gain = val
|
||||||
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
||||||
self.nvda.speak(str(val))
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [
|
case [
|
||||||
@@ -1079,13 +1070,13 @@ class NVDAVMWindow(psg.Window):
|
|||||||
val = util.check_bounds(val, (-60, 12))
|
val = util.check_bounds(val, (-60, 12))
|
||||||
self.vm.bus[int(index)].gain = val
|
self.vm.bus[int(index)].gain = val
|
||||||
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
self[f"BUS {index}||SLIDER GAIN"].update(value=val)
|
||||||
self.nvda.speak(str(val))
|
self.nvda.speak(str(round(val, 1)))
|
||||||
else:
|
else:
|
||||||
self.vm.event.pdirty = True
|
self.vm.event.pdirty = True
|
||||||
case [["BUS", index], ["SLIDER", "GAIN"], ["KEY", "CTRL", "SHIFT", "R"]]:
|
case [["BUS", index], ["SLIDER", "GAIN"], ["KEY", "CTRL", "SHIFT", "R"]]:
|
||||||
self.vm.bus[int(index)].gain = 0
|
self.vm.bus[int(index)].gain = 0
|
||||||
self[f"BUS {index}||SLIDER GAIN"].update(value=0)
|
self[f"BUS {index}||SLIDER GAIN"].update(value=0)
|
||||||
self.nvda.speak(str(val))
|
self.nvda.speak(str(0))
|
||||||
|
|
||||||
# Unknown
|
# Unknown
|
||||||
case _:
|
case _:
|
||||||
|
|||||||
Reference in New Issue
Block a user