mirror of
https://github.com/onyx-and-iris/nvda-voicemeeter.git
synced 2026-04-07 18:03:35 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c5c8893ad | |||
| e645437d1d | |||
| 9969506698 | |||
| 3a1143199a | |||
| 79f739f250 | |||
| b45b2da4aa | |||
| 0adfec2e63 | |||
| dacc972b17 | |||
| 2ed1cad666 | |||
| 64361b2011 | |||
| 36003fe73f | |||
| cb82033e1c | |||
| bde9020471 | |||
| 364b0deeb4 | |||
| a80e4e2d83 | |||
| 6e146bac50 | |||
| c385476cc4 | |||
| 1c09556c61 | |||
| 421688eff8 | |||
| bdd570738a | |||
| 71b137a9c2 | |||
| 912eb8c14d |
14
README.md
14
README.md
@@ -131,9 +131,11 @@ For Gate BP Sidechain, Attack, Hold, Release you may use:
|
||||
|
||||
To reset a slider back to its default value you may use `Control + Shift + R`.
|
||||
|
||||
#### `Menu`
|
||||
### Menu
|
||||
|
||||
A single menu item `Voicemeeter` can be opened using `Alt` and then `v`. The menu allows you to:
|
||||
#### `Voicemeeter`
|
||||
|
||||
The `Voicemeeter` menu can be opened using `Alt` and then `v`. It offers the following options:
|
||||
|
||||
- Restart Voicemeeter audio engine
|
||||
- Save/Load current settings (as an xml file)
|
||||
@@ -143,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.
|
||||
|
||||
### `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.
|
||||
|
||||
@@ -182,7 +188,7 @@ If you have any questions/suggestions feel free to raise an issue or open a new
|
||||
|
||||
[Vincent Burel](https://github.com/vburel2018) for creating Voicemeeter and its SDK.
|
||||
|
||||
[PySimpleGUI](https://github.com/PySimpleGUI) team for creating an awesome GUI framework.
|
||||
[FreeSimpleGUI](https://github.com/spyoungtech/FreeSimpleGUI) a fork of the now closed source PySimpleGUI project.
|
||||
|
||||
[spec]: ./SPECIFICATION.md
|
||||
[voicemeeter]: https://voicemeeter.com/
|
||||
|
||||
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 |
258
pdm.lock
generated
258
pdm.lock
generated
@@ -2,26 +2,27 @@
|
||||
# It is not intended for manual editing.
|
||||
|
||||
[metadata]
|
||||
groups = ["default", "build", "lint", "test"]
|
||||
cross_platform = true
|
||||
static_urls = false
|
||||
lock_version = "4.3"
|
||||
content_hash = "sha256:680eff1b532e55860290380d4e2f331dc29af6fb898a0df16fdb033843bf15a4"
|
||||
groups = ["default", "build", "lint"]
|
||||
strategy = ["cross_platform", "inherit_metadata"]
|
||||
lock_version = "4.4.2"
|
||||
content_hash = "sha256:24da3456e709ca07cb83968d98b9e0b1ce533bf153076cc16bc34c66e047a70c"
|
||||
|
||||
[[package]]
|
||||
name = "altgraph"
|
||||
version = "0.17.3"
|
||||
version = "0.17.4"
|
||||
summary = "Python graph (network) package"
|
||||
groups = ["build"]
|
||||
files = [
|
||||
{file = "altgraph-0.17.3-py2.py3-none-any.whl", hash = "sha256:c8ac1ca6772207179ed8003ce7687757c04b0b71536f81e2ac5755c6226458fe"},
|
||||
{file = "altgraph-0.17.3.tar.gz", hash = "sha256:ad33358114df7c9416cdb8fa1eaa5852166c505118717021c6a8c7c7abbd03dd"},
|
||||
{file = "altgraph-0.17.4-py2.py3-none-any.whl", hash = "sha256:642743b4750de17e655e6711601b077bc6598dbfa3ba5fa2b2a35ce12b508dff"},
|
||||
{file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "23.7.0"
|
||||
version = "24.4.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "The uncompromising code formatter."
|
||||
groups = ["lint"]
|
||||
dependencies = [
|
||||
"click>=8.0.0",
|
||||
"mypy-extensions>=0.4.3",
|
||||
@@ -29,15 +30,19 @@ dependencies = [
|
||||
"pathspec>=0.9.0",
|
||||
"platformdirs>=2",
|
||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||
"typing-extensions>=4.0.1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:5c4bc552ab52f6c1c506ccae05681fab58c3f72d59ae6e6639e8885e94fe2587"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:552513d5cd5694590d7ef6f46e1767a4df9af168d449ff767b13b084c020e63f"},
|
||||
{file = "black-23.7.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:86cee259349b4448adb4ef9b204bb4467aae74a386bce85d56ba4f5dc0da27be"},
|
||||
{file = "black-23.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:501387a9edcb75d7ae8a4412bb8749900386eaef258f1aefab18adddea1936bc"},
|
||||
{file = "black-23.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:fb074d8b213749fa1d077d630db0d5f8cc3b2ae63587ad4116e8a436e9bbe995"},
|
||||
{file = "black-23.7.0-py3-none-any.whl", hash = "sha256:9fd59d418c60c0348505f2ddf9609c1e1de8e7493eab96198fc89d9f865e7a96"},
|
||||
{file = "black-23.7.0.tar.gz", hash = "sha256:022a582720b0d9480ed82576c920a8c1dde97cc38ff11d8d8859b3bd6ca9eedb"},
|
||||
{file = "black-24.4.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce"},
|
||||
{file = "black-24.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021"},
|
||||
{file = "black-24.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063"},
|
||||
{file = "black-24.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96"},
|
||||
{file = "black-24.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474"},
|
||||
{file = "black-24.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c"},
|
||||
{file = "black-24.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb"},
|
||||
{file = "black-24.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1"},
|
||||
{file = "black-24.4.2-py3-none-any.whl", hash = "sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c"},
|
||||
{file = "black-24.4.2.tar.gz", hash = "sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -45,6 +50,7 @@ name = "click"
|
||||
version = "8.1.7"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Composable command line interface toolkit"
|
||||
groups = ["lint"]
|
||||
dependencies = [
|
||||
"colorama; platform_system == \"Windows\"",
|
||||
]
|
||||
@@ -58,21 +64,61 @@ name = "colorama"
|
||||
version = "0.4.6"
|
||||
requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
|
||||
summary = "Cross-platform colored terminal text."
|
||||
groups = ["lint"]
|
||||
marker = "platform_system == \"Windows\""
|
||||
files = [
|
||||
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
|
||||
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "freesimplegui"
|
||||
version = "5.1.0"
|
||||
summary = "The free-forever Python GUI framework."
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "FreeSimpleGUI-5.1.0-py3-none-any.whl", hash = "sha256:9dee6408bc0df2d30327f775e5e61160085f63d0d1c2f41f83d00cb250e507c2"},
|
||||
{file = "freesimplegui-5.1.0.tar.gz", hash = "sha256:2be084404bac9675a3082df8eb8cea4872121f166fc37298081203961618268f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macholib"
|
||||
version = "1.16.2"
|
||||
version = "1.16.3"
|
||||
summary = "Mach-O header analysis and editing"
|
||||
groups = ["build"]
|
||||
marker = "sys_platform == \"darwin\""
|
||||
dependencies = [
|
||||
"altgraph>=0.17",
|
||||
]
|
||||
files = [
|
||||
{file = "macholib-1.16.2-py2.py3-none-any.whl", hash = "sha256:44c40f2cd7d6726af8fa6fe22549178d3a4dfecc35a9cd15ea916d9c83a688e0"},
|
||||
{file = "macholib-1.16.2.tar.gz", hash = "sha256:557bbfa1bb255c20e9abafe7ed6cd8046b48d9525db2f9b77d3122a63a2a8bf8"},
|
||||
{file = "macholib-1.16.3-py2.py3-none-any.whl", hash = "sha256:0e315d7583d38b8c77e815b1ecbdbf504a8258d8b3e17b61165c6feb60d18f2c"},
|
||||
{file = "macholib-1.16.3.tar.gz", hash = "sha256:07ae9e15e8e4cd9a788013d81f5908b3609aa76f9b1421bae9c4d7606ec86a30"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "1.10.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Optional static typing for Python"
|
||||
groups = ["lint"]
|
||||
dependencies = [
|
||||
"mypy-extensions>=1.0.0",
|
||||
"tomli>=1.1.0; python_version < \"3.11\"",
|
||||
"typing-extensions>=4.1.0",
|
||||
]
|
||||
files = [
|
||||
{file = "mypy-1.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e36f229acfe250dc660790840916eb49726c928e8ce10fbdf90715090fe4ae02"},
|
||||
{file = "mypy-1.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:51a46974340baaa4145363b9e051812a2446cf583dfaeba124af966fa44593f7"},
|
||||
{file = "mypy-1.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:901c89c2d67bba57aaaca91ccdb659aa3a312de67f23b9dfb059727cce2e2e0a"},
|
||||
{file = "mypy-1.10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0cd62192a4a32b77ceb31272d9e74d23cd88c8060c34d1d3622db3267679a5d9"},
|
||||
{file = "mypy-1.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:a2cbc68cb9e943ac0814c13e2452d2046c2f2b23ff0278e26599224cf164e78d"},
|
||||
{file = "mypy-1.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bd6f629b67bb43dc0d9211ee98b96d8dabc97b1ad38b9b25f5e4c4d7569a0c6a"},
|
||||
{file = "mypy-1.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a1bbb3a6f5ff319d2b9d40b4080d46cd639abe3516d5a62c070cf0114a457d84"},
|
||||
{file = "mypy-1.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8edd4e9bbbc9d7b79502eb9592cab808585516ae1bcc1446eb9122656c6066f"},
|
||||
{file = "mypy-1.10.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6166a88b15f1759f94a46fa474c7b1b05d134b1b61fca627dd7335454cc9aa6b"},
|
||||
{file = "mypy-1.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:5bb9cd11c01c8606a9d0b83ffa91d0b236a0e91bc4126d9ba9ce62906ada868e"},
|
||||
{file = "mypy-1.10.1-py3-none-any.whl", hash = "sha256:71d8ac0b906354ebda8ef1673e5fde785936ac1f29ff6987c7483cfbd5a4235a"},
|
||||
{file = "mypy-1.10.1.tar.gz", hash = "sha256:1f8f492d7db9e3593ef42d4f115f04e556130f2819ad33ab84551403e97dd4c0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -80,6 +126,7 @@ name = "mypy-extensions"
|
||||
version = "1.0.0"
|
||||
requires_python = ">=3.5"
|
||||
summary = "Type system extensions for programs checked with the mypy type checker."
|
||||
groups = ["lint"]
|
||||
files = [
|
||||
{file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
|
||||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
@@ -87,22 +134,24 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.1"
|
||||
requires_python = ">=3.7"
|
||||
version = "24.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Core utilities for Python packages"
|
||||
groups = ["build", "lint"]
|
||||
files = [
|
||||
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
|
||||
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
|
||||
{file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"},
|
||||
{file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.11.2"
|
||||
requires_python = ">=3.7"
|
||||
version = "0.12.1"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Utility library for gitignore style pattern matching of file paths."
|
||||
groups = ["lint"]
|
||||
files = [
|
||||
{file = "pathspec-0.11.2-py3-none-any.whl", hash = "sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20"},
|
||||
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
|
||||
{file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"},
|
||||
{file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -110,6 +159,8 @@ name = "pefile"
|
||||
version = "2023.2.7"
|
||||
requires_python = ">=3.6.0"
|
||||
summary = "Python PE parsing module"
|
||||
groups = ["build"]
|
||||
marker = "sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "pefile-2023.2.7-py3-none-any.whl", hash = "sha256:da185cd2af68c08a6cd4481f7325ed600a88f6a813bad9dea07ab3ef73d8d8d6"},
|
||||
{file = "pefile-2023.2.7.tar.gz", hash = "sha256:82e6114004b3d6911c77c3953e3838654b04511b8b66e8583db70c65998017dc"},
|
||||
@@ -117,81 +168,69 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "3.10.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
|
||||
version = "4.2.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
|
||||
groups = ["lint"]
|
||||
files = [
|
||||
{file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"},
|
||||
{file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psgdemos"
|
||||
version = "1.12.1"
|
||||
summary = "Installs the full set of PySimpleGUI Demo Programs and the Demo Browser."
|
||||
dependencies = [
|
||||
"PySimpleGUI",
|
||||
]
|
||||
files = [
|
||||
{file = "psgdemos-1.12.1-py3-none-any.whl", hash = "sha256:a035540dd0ff92f410aed9b7af8d5a641d9d5a9eac3e0072ef115adf06abb447"},
|
||||
{file = "psgdemos-1.12.1.tar.gz", hash = "sha256:4108af795477531a9b1c8675b9aa9b6628c109e660f6954baf8ba2dc3b5806e9"},
|
||||
{file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"},
|
||||
{file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller"
|
||||
version = "5.13.0"
|
||||
requires_python = "<3.13,>=3.7"
|
||||
version = "6.8.0"
|
||||
requires_python = "<3.13,>=3.8"
|
||||
summary = "PyInstaller bundles a Python application and all its dependencies into a single package."
|
||||
groups = ["build"]
|
||||
dependencies = [
|
||||
"altgraph",
|
||||
"macholib>=1.8; sys_platform == \"darwin\"",
|
||||
"packaging>=22.0",
|
||||
"pefile>=2022.5.30; sys_platform == \"win32\"",
|
||||
"pyinstaller-hooks-contrib>=2021.4",
|
||||
"pyinstaller-hooks-contrib>=2024.6",
|
||||
"pywin32-ctypes>=0.2.1; sys_platform == \"win32\"",
|
||||
"setuptools>=42.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pyinstaller-5.13.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:7fdd319828de679f9c5e381eff998ee9b4164bf4457e7fca56946701cf002c3f"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0df43697c4914285ecd333be968d2cd042ab9b2670124879ee87931d2344eaf5"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_i686.whl", hash = "sha256:28d9742c37e9fb518444b12f8c8ab3cb4ba212d752693c34475c08009aa21ccf"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e5fb17de6c325d3b2b4ceaeb55130ad7100a79096490e4c5b890224406fa42f4"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:78975043edeb628e23a73fb3ef0a273cda50e765f1716f75212ea3e91b09dede"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:cd7d5c06f2847195a23d72ede17c60857d6f495d6f0727dc6c9bc1235f2eb79c"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:24009eba63cfdbcde6d2634e9c87f545eb67249ddf3b514e0cd3b2cdaa595828"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:1fde4381155f21d6354dc450dcaa338cd8a40aaacf6bd22b987b0f3e1f96f3ee"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-win32.whl", hash = "sha256:2d03419904d1c25c8968b0ad21da0e0f33d8d65716e29481b5bd83f7f342b0c5"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-win_amd64.whl", hash = "sha256:9fc27c5a853b14a90d39c252707673c7a0efec921cd817169aff3af0fca8c127"},
|
||||
{file = "pyinstaller-5.13.0-py3-none-win_arm64.whl", hash = "sha256:3a331951f9744bc2379ea5d65d36f3c828eaefe2785f15039592cdc08560b262"},
|
||||
{file = "pyinstaller-5.13.0.tar.gz", hash = "sha256:5e446df41255e815017d96318e39f65a3eb807e74a796c7e7ff7f13b6366a2e9"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-macosx_10_13_universal2.whl", hash = "sha256:5ff6bc2784c1026f8e2f04aa3760cbed41408e108a9d4cf1dd52ee8351a3f6e1"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:39ac424d2ee2457d2ab11a5091436e75a0cccae207d460d180aa1fcbbafdd528"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_i686.whl", hash = "sha256:355832a3acc7de90a255ecacd4b9f9e166a547a79c8905d49f14e3a75c1acdb9"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:6303c7a009f47e6a96ef65aed49f41e36ece8d079b9193ca92fe807403e5fe80"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2b71509468c811968c0b5decb5bbe85b6292ea52d7b1f26313d2aabb673fa9a5"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ff31c5b99e05a4384bbe2071df67ec8b2b347640a375eae9b40218be2f1754c6"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:000c36b13fe4cd8d0d8c2bc855b1ddcf39867b5adf389e6b5ca45b25fa3e619d"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:fe0af018d7d5077180e3144ada89a4da5df8d07716eb7e9482834a56dc57a4e8"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-win32.whl", hash = "sha256:d257f6645c7334cbd66f38a4fac62c3ad614cc46302b2b5d9f8cc48c563bce0e"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-win_amd64.whl", hash = "sha256:81cccfa9b16699b457f4788c5cc119b50f3cd4d0db924955f15c33f2ad27a50d"},
|
||||
{file = "pyinstaller-6.8.0-py3-none-win_arm64.whl", hash = "sha256:1c3060a263758cf7f0144ab4c016097b20451b2469d468763414665db1bb743d"},
|
||||
{file = "pyinstaller-6.8.0.tar.gz", hash = "sha256:3f4b6520f4423fe19bcc2fd63ab7238851ae2bdcbc98f25bc5d2f97cc62012e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyinstaller-hooks-contrib"
|
||||
version = "2023.7"
|
||||
version = "2024.7"
|
||||
requires_python = ">=3.7"
|
||||
summary = "Community maintained hooks for PyInstaller"
|
||||
groups = ["build"]
|
||||
dependencies = [
|
||||
"packaging>=22.0",
|
||||
"setuptools>=42.0.0",
|
||||
]
|
||||
files = [
|
||||
{file = "pyinstaller-hooks-contrib-2023.7.tar.gz", hash = "sha256:0c436a4c3506020e34116a8a7ddfd854c1ad6ddca9a8cd84500bd6e69c9e68f9"},
|
||||
{file = "pyinstaller_hooks_contrib-2023.7-py2.py3-none-any.whl", hash = "sha256:3c10df14c0f71ab388dfbf1625375b087e7330d9444cbfd2b310ba027fa0cff0"},
|
||||
{file = "pyinstaller_hooks_contrib-2024.7-py2.py3-none-any.whl", hash = "sha256:8bf0775771fbaf96bcd2f4dfd6f7ae6c1dd1b1efe254c7e50477b3c08e7841d8"},
|
||||
{file = "pyinstaller_hooks_contrib-2024.7.tar.gz", hash = "sha256:fd5f37dcf99bece184e40642af88be16a9b89613ecb958a8bd1136634fc9fac5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyparsing"
|
||||
version = "3.1.1"
|
||||
version = "3.1.2"
|
||||
requires_python = ">=3.6.8"
|
||||
summary = "pyparsing module - Classes and methods to define and execute parsing grammars"
|
||||
groups = ["default"]
|
||||
files = [
|
||||
{file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"},
|
||||
{file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pysimplegui"
|
||||
version = "4.60.5"
|
||||
summary = "Python GUIs for Humans. Launched in 2018. It's 2022 & PySimpleGUI is an ACTIVE & supported project. Super-simple to create custom GUI's. 325+ Demo programs & Cookbook for rapid start. Extensive documentation. Main docs at www.PySimpleGUI.org. Fun & your success are the focus. Examples using Machine Learning (GUI, OpenCV Integration), Rainmeter Style Desktop Widgets, Matplotlib + Pyplot, PIL support, add GUI to command line scripts, PDF & Image Viewers. Great for beginners & advanced GUI programmers."
|
||||
files = [
|
||||
{file = "PySimpleGUI-4.60.5-py3-none-any.whl", hash = "sha256:004f33311ee685a5287fad54f500f7290b40d7c806044e478b1384f85dedce64"},
|
||||
{file = "PySimpleGUI-4.60.5.tar.gz", hash = "sha256:31014d1cc5eef1373d7e93564ff2604662645cc774a939b1f01aa253e7f9d78b"},
|
||||
{file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"},
|
||||
{file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -199,6 +238,8 @@ name = "pywin32-ctypes"
|
||||
version = "0.2.2"
|
||||
requires_python = ">=3.6"
|
||||
summary = "A (partial) reimplementation of pywin32 using ctypes/cffi"
|
||||
groups = ["build"]
|
||||
marker = "sys_platform == \"win32\""
|
||||
files = [
|
||||
{file = "pywin32-ctypes-0.2.2.tar.gz", hash = "sha256:3426e063bdd5fd4df74a14fa3cf80a0b42845a87e1d1e81f6549f9daec593a60"},
|
||||
{file = "pywin32_ctypes-0.2.2-py3-none-any.whl", hash = "sha256:bf490a1a709baf35d688fe0ecf980ed4de11d2b3e37b51e5442587a75d9957e7"},
|
||||
@@ -206,37 +247,40 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.291"
|
||||
version = "0.5.0"
|
||||
requires_python = ">=3.7"
|
||||
summary = "An extremely fast Python linter, written in Rust."
|
||||
summary = "An extremely fast Python linter and code formatter, written in Rust."
|
||||
groups = ["lint"]
|
||||
files = [
|
||||
{file = "ruff-0.0.291-py3-none-macosx_10_7_x86_64.whl", hash = "sha256:b97d0d7c136a85badbc7fd8397fdbb336e9409b01c07027622f28dcd7db366f2"},
|
||||
{file = "ruff-0.0.291-py3-none-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:6ab44ea607967171e18aa5c80335237be12f3a1523375fa0cede83c5cf77feb4"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a04b384f2d36f00d5fb55313d52a7d66236531195ef08157a09c4728090f2ef0"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b727c219b43f903875b7503a76c86237a00d1a39579bb3e21ce027eec9534051"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87671e33175ae949702774071b35ed4937da06f11851af75cd087e1b5a488ac4"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:b75f5801547f79b7541d72a211949754c21dc0705c70eddf7f21c88a64de8b97"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b09b94efdcd162fe32b472b2dd5bf1c969fcc15b8ff52f478b048f41d4590e09"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d5b56bc3a2f83a7a1d7f4447c54d8d3db52021f726fdd55d549ca87bca5d747"},
|
||||
{file = "ruff-0.0.291-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13f0d88e5f367b2dc8c7d90a8afdcfff9dd7d174e324fd3ed8e0b5cb5dc9b7f6"},
|
||||
{file = "ruff-0.0.291-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b3eeee1b1a45a247758ecdc3ab26c307336d157aafc61edb98b825cadb153df3"},
|
||||
{file = "ruff-0.0.291-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6c06006350c3bb689765d71f810128c9cdf4a1121fd01afc655c87bab4fb4f83"},
|
||||
{file = "ruff-0.0.291-py3-none-musllinux_1_2_i686.whl", hash = "sha256:fd17220611047de247b635596e3174f3d7f2becf63bd56301fc758778df9b629"},
|
||||
{file = "ruff-0.0.291-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:5383ba67ad360caf6060d09012f1fb2ab8bd605ab766d10ca4427a28ab106e0b"},
|
||||
{file = "ruff-0.0.291-py3-none-win32.whl", hash = "sha256:1d5f0616ae4cdc7a938b493b6a1a71c8a47d0300c0d65f6e41c281c2f7490ad3"},
|
||||
{file = "ruff-0.0.291-py3-none-win_amd64.whl", hash = "sha256:8a69bfbde72db8ca1c43ee3570f59daad155196c3fbe357047cd9b77de65f15b"},
|
||||
{file = "ruff-0.0.291-py3-none-win_arm64.whl", hash = "sha256:d867384a4615b7f30b223a849b52104214442b5ba79b473d7edd18da3cde22d6"},
|
||||
{file = "ruff-0.0.291.tar.gz", hash = "sha256:c61109661dde9db73469d14a82b42a88c7164f731e6a3b0042e71394c1c7ceed"},
|
||||
{file = "ruff-0.5.0-py3-none-linux_armv6l.whl", hash = "sha256:ee770ea8ab38918f34e7560a597cc0a8c9a193aaa01bfbd879ef43cb06bd9c4c"},
|
||||
{file = "ruff-0.5.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38f3b8327b3cb43474559d435f5fa65dacf723351c159ed0dc567f7ab735d1b6"},
|
||||
{file = "ruff-0.5.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7594f8df5404a5c5c8f64b8311169879f6cf42142da644c7e0ba3c3f14130370"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc7012d6ec85032bc4e9065110df205752d64010bed5f958d25dbee9ce35de3"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d505fb93b0fabef974b168d9b27c3960714d2ecda24b6ffa6a87ac432905ea38"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dc5cfd3558f14513ed0d5b70ce531e28ea81a8a3b1b07f0f48421a3d9e7d80a"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:db3ca35265de239a1176d56a464b51557fce41095c37d6c406e658cf80bbb362"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1a321c4f68809fddd9b282fab6a8d8db796b270fff44722589a8b946925a2a8"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c4dfcd8d34b143916994b3876b63d53f56724c03f8c1a33a253b7b1e6bf2a7d"},
|
||||
{file = "ruff-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81e5facfc9f4a674c6a78c64d38becfbd5e4f739c31fcd9ce44c849f1fad9e4c"},
|
||||
{file = "ruff-0.5.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e589e27971c2a3efff3fadafb16e5aef7ff93250f0134ec4b52052b673cf988d"},
|
||||
{file = "ruff-0.5.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2ffbc3715a52b037bcb0f6ff524a9367f642cdc5817944f6af5479bbb2eb50e"},
|
||||
{file = "ruff-0.5.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd096e23c6a4f9c819525a437fa0a99d1c67a1b6bb30948d46f33afbc53596cf"},
|
||||
{file = "ruff-0.5.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:46e193b36f2255729ad34a49c9a997d506e58f08555366b2108783b3064a0e1e"},
|
||||
{file = "ruff-0.5.0-py3-none-win32.whl", hash = "sha256:49141d267100f5ceff541b4e06552e98527870eafa1acc9dec9139c9ec5af64c"},
|
||||
{file = "ruff-0.5.0-py3-none-win_amd64.whl", hash = "sha256:e9118f60091047444c1b90952736ee7b1792910cab56e9b9a9ac20af94cd0440"},
|
||||
{file = "ruff-0.5.0-py3-none-win_arm64.whl", hash = "sha256:ed5c4df5c1fb4518abcb57725b576659542bdbe93366f4f329e8f398c4b71178"},
|
||||
{file = "ruff-0.5.0.tar.gz", hash = "sha256:eb641b5873492cf9bd45bc9c5ae5320648218e04386a5f0c264ad6ccce8226a1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "68.1.2"
|
||||
version = "70.2.0"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
groups = ["build"]
|
||||
files = [
|
||||
{file = "setuptools-68.1.2-py3-none-any.whl", hash = "sha256:3d8083eed2d13afc9426f227b24fd1659489ec107c0e86cec2ffdde5c92e790b"},
|
||||
{file = "setuptools-68.1.2.tar.gz", hash = "sha256:3d4dfa6d95f1b101d695a6160a7626e15583af71a5f52176efa5d39a054d475d"},
|
||||
{file = "setuptools-70.2.0-py3-none-any.whl", hash = "sha256:b8b8060bb426838fbe942479c90296ce976249451118ef566a5a0b7d8b78fb05"},
|
||||
{file = "setuptools-70.2.0.tar.gz", hash = "sha256:bd63e505105011b25c3c11f753f7e3b8465ea739efddaccef8f0efac2137bac1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -244,20 +288,34 @@ name = "tomli"
|
||||
version = "2.0.1"
|
||||
requires_python = ">=3.7"
|
||||
summary = "A lil' TOML parser"
|
||||
groups = ["default", "lint"]
|
||||
marker = "python_version < \"3.11\""
|
||||
files = [
|
||||
{file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"},
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.12.2"
|
||||
requires_python = ">=3.8"
|
||||
summary = "Backported and Experimental Type Hints for Python 3.8+"
|
||||
groups = ["lint"]
|
||||
files = [
|
||||
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
|
||||
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "voicemeeter-api"
|
||||
version = "2.4.10"
|
||||
requires_python = ">=3.10,<4.0"
|
||||
version = "2.6.0"
|
||||
requires_python = "<4.0,>=3.10"
|
||||
summary = "A Python wrapper for the Voiceemeter API"
|
||||
groups = ["default"]
|
||||
dependencies = [
|
||||
"tomli<3.0.0,>=2.0.1; python_version < \"3.11\"",
|
||||
]
|
||||
files = [
|
||||
{file = "voicemeeter_api-2.4.10-py3-none-any.whl", hash = "sha256:2f75acb7b472e56b6bd8d4f1141f32d948c55ef9b30d5a08e085a1c8e76e2464"},
|
||||
{file = "voicemeeter_api-2.4.10.tar.gz", hash = "sha256:1d8dfc1e8922179f8b97c90b90b9ed051082018c6af5feb1d48250140a02d40c"},
|
||||
{file = "voicemeeter_api-2.6.0-py3-none-any.whl", hash = "sha256:c2ef8eb063ce3aeac4827ad7883150c407a0effb0fde3778782cd3024a295255"},
|
||||
{file = "voicemeeter_api-2.6.0.tar.gz", hash = "sha256:db93f27b58ce927c7d56084b224e1d0cdd6406ab7f26e4f99a9a873495a3d2e7"},
|
||||
]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[project]
|
||||
name = "nvda_voicemeeter"
|
||||
version = "0.5.1"
|
||||
version = "0.6.4"
|
||||
description = "A Voicemeeter app compatible with NVDA"
|
||||
authors = [
|
||||
{ name = "onyx-and-iris", email = "code@onyxandiris.online" },
|
||||
]
|
||||
authors = [{ name = "onyx-and-iris", email = "code@onyxandiris.online" }]
|
||||
dependencies = [
|
||||
"pysimplegui>=4.60.5",
|
||||
"pyparsing>=3.1.1",
|
||||
"voicemeeter-api>=2.4.10",
|
||||
"voicemeeter-api>=2.6.0",
|
||||
"freesimplegui>=5.1.0",
|
||||
]
|
||||
requires-python = ">=3.10,<3.12"
|
||||
readme = "README.md"
|
||||
@@ -17,16 +15,8 @@ readme = "README.md"
|
||||
text = "MIT"
|
||||
|
||||
[tool.pdm.dev-dependencies]
|
||||
build = [
|
||||
"pyinstaller>=5.1",
|
||||
]
|
||||
lint = [
|
||||
"black>=23.7.0",
|
||||
"ruff>=0.0.291",
|
||||
]
|
||||
test = [
|
||||
"psgdemos>=1.12.1",
|
||||
]
|
||||
lint = ["black>=23.7.0", "ruff>=0.0.291", "mypy>=1.7.0"]
|
||||
build = ["pyinstaller>=6.3.0"]
|
||||
|
||||
[tool.pdm.scripts.build]
|
||||
shell = "build.ps1"
|
||||
@@ -35,13 +25,8 @@ shell = "build.ps1"
|
||||
line-length = 119
|
||||
|
||||
[tool.ruff]
|
||||
select = [
|
||||
"E",
|
||||
"F",
|
||||
]
|
||||
ignore = [
|
||||
"E501",
|
||||
]
|
||||
select = ["E", "F"]
|
||||
ignore = ["E501"]
|
||||
fixable = [
|
||||
"A",
|
||||
"B",
|
||||
@@ -120,7 +105,4 @@ target-version = "py310"
|
||||
max-complexity = 10
|
||||
|
||||
[tool.ruff.per-file-ignores]
|
||||
"__init__.py" = [
|
||||
"E402",
|
||||
"F401",
|
||||
]
|
||||
"__init__.py" = ["E402", "F401"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import PySimpleGUI as psg
|
||||
import FreeSimpleGUI as psg
|
||||
|
||||
from . import util
|
||||
from .compound import LabelSlider
|
||||
@@ -26,7 +26,6 @@ class Builder:
|
||||
steps = (
|
||||
self.make_tab0_row0,
|
||||
self.make_tab0_row1,
|
||||
self.make_tab0_row2,
|
||||
self.make_tab0_row3,
|
||||
self.make_tab0_row4,
|
||||
self.make_tab0_row5,
|
||||
@@ -93,6 +92,8 @@ class Builder:
|
||||
return [[menu], [tab_group]]
|
||||
|
||||
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 = [
|
||||
[
|
||||
"&Voicemeeter",
|
||||
@@ -103,6 +104,7 @@ class Builder:
|
||||
"Load Settings on Startup ::MENU",
|
||||
],
|
||||
],
|
||||
["&Theme", themes],
|
||||
]
|
||||
return psg.Menu(menu_def, key="menus")
|
||||
|
||||
@@ -152,61 +154,20 @@ class Builder:
|
||||
[step(hardware_out) for step in (add_physical_device_opts,)]
|
||||
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:
|
||||
"""tab0 row3 represents patch composite"""
|
||||
|
||||
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(
|
||||
[
|
||||
psg.ButtonMenu(
|
||||
f"PC{i + 1}",
|
||||
size=(6, 2),
|
||||
size=(5, 2),
|
||||
menu_def=["", outputs],
|
||||
key=f"PATCH COMPOSITE||PC{i + 1}",
|
||||
)
|
||||
for i in range(self.kind.phys_out)
|
||||
for i in range(self.kind.composite)
|
||||
]
|
||||
)
|
||||
|
||||
@@ -381,7 +342,7 @@ class Builder:
|
||||
if i == self.kind.phys_in + 1:
|
||||
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("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
||||
],
|
||||
@@ -389,7 +350,7 @@ class Builder:
|
||||
else:
|
||||
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("Mute", size=(6, 2), key=f"STRIP {i}||MUTE"),
|
||||
],
|
||||
@@ -479,7 +440,7 @@ class Builder:
|
||||
|
||||
def add_strip_outputs(layout):
|
||||
params = ["MONO", "EQ", "MUTE"]
|
||||
if self.vm.kind.name == "basic":
|
||||
if self.kind.name == "basic":
|
||||
params.remove("EQ")
|
||||
busmodes = [util._bus_mode_map[mode] for mode in util.get_bus_modes(self.vm)]
|
||||
layout.append(
|
||||
|
||||
@@ -5,7 +5,7 @@ from pathlib import Path
|
||||
|
||||
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":
|
||||
raise NVDAVMError("Only Windows OS supported")
|
||||
@@ -15,7 +15,7 @@ REG_KEY = "\\".join(
|
||||
None,
|
||||
(
|
||||
"SOFTWARE",
|
||||
"WOW6432Node" if bits == 64 else "",
|
||||
"WOW6432Node" if BITS == 64 else "",
|
||||
"Microsoft",
|
||||
"Windows",
|
||||
"CurrentVersion",
|
||||
@@ -39,8 +39,8 @@ except FileNotFoundError:
|
||||
|
||||
controller_path = Path(__file__).parents[2].resolve() / "controllerClient"
|
||||
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))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Union
|
||||
|
||||
import PySimpleGUI as psg
|
||||
import FreeSimpleGUI as psg
|
||||
|
||||
from . import util
|
||||
|
||||
@@ -12,6 +12,9 @@ class LabelSlider(psg.Frame):
|
||||
self.parent = parent
|
||||
if param in ("AUDIBILITY", "DENOISER"):
|
||||
size = 7
|
||||
else:
|
||||
if psg.theme() == "HighContrast":
|
||||
size = 5
|
||||
else:
|
||||
size = 4
|
||||
layout = [
|
||||
|
||||
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)
|
||||
@@ -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)},
|
||||
}
|
||||
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}||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:
|
||||
params |= {
|
||||
**{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:
|
||||
params = {}
|
||||
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:
|
||||
params = {}
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import PySimpleGUI as psg
|
||||
import FreeSimpleGUI as psg
|
||||
|
||||
from . import util
|
||||
from .compound import CompSlider, GateSlider, LabelSliderAdvanced
|
||||
@@ -48,6 +48,30 @@ class Popup:
|
||||
if 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):
|
||||
if "Strip" in tab:
|
||||
if index < self.kind.phys_in:
|
||||
@@ -94,6 +118,50 @@ class Popup:
|
||||
return data
|
||||
|
||||
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:
|
||||
buffer = [
|
||||
[
|
||||
@@ -109,27 +177,79 @@ class Popup:
|
||||
return psg.Frame("BUFFERING", buffer)
|
||||
|
||||
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,)
|
||||
for step in steps:
|
||||
layout.append([step()])
|
||||
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}
|
||||
for driver in ("MME", "WDM", "KS", "ASIO"):
|
||||
popup[f"BUFFER {driver}"].Widget.config(**buttonmenu_opts)
|
||||
popup[f"BUFFER {driver}"].bind("<FocusIn>", "||FOCUS IN")
|
||||
popup[f"BUFFER {driver}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||
popup[f"BUFFER {driver}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||
popup["Exit"].bind("<FocusIn>", "||FOCUS IN")
|
||||
popup["Exit"].bind("<Return>", "||KEY ENTER")
|
||||
self.popup[f"BUFFER {driver}"].Widget.config(**buttonmenu_opts)
|
||||
self.popup[f"BUFFER {driver}"].bind("<FocusIn>", "||FOCUS IN")
|
||||
self.popup[f"BUFFER {driver}"].bind("<space>", "||KEY SPACE", propagate=False)
|
||||
self.popup[f"BUFFER {driver}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||
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 = popup.read()
|
||||
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 [["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"]:
|
||||
if values[event] == "Default":
|
||||
if "MME" in event:
|
||||
@@ -149,27 +269,14 @@ class Popup:
|
||||
val = int(self.window.vm.get(f"option.buffer.{driver.lower()}"))
|
||||
self.window.nvda.speak(f"{driver} BUFFER {val if val else 'default'}")
|
||||
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"]]:
|
||||
self.window.nvda.speak(button)
|
||||
case [_, ["KEY", "ENTER"]]:
|
||||
popup.find_element_with_focus().click()
|
||||
self.popup.find_element_with_focus().click()
|
||||
self.logger.debug(f"parsed::{parsed_cmd}")
|
||||
popup.close()
|
||||
|
||||
def on_pdirty(self):
|
||||
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())
|
||||
)
|
||||
self.window.vm.observer.remove(self.on_pdirty)
|
||||
self.popup.close()
|
||||
|
||||
def compressor(self, index, title=None):
|
||||
self.index = index
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from typing import Iterable
|
||||
|
||||
import FreeSimpleGUI as psg
|
||||
|
||||
def get_asio_checkbox_index(channel, num) -> int:
|
||||
|
||||
def get_asio_input_spinbox_index(channel, num) -> int:
|
||||
if channel == 0:
|
||||
return 2 * num - 2
|
||||
return 2 * num - 1
|
||||
@@ -192,3 +194,49 @@ def get_slider_modes() -> Iterable:
|
||||
"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",
|
||||
]
|
||||
|
||||
@@ -2,9 +2,9 @@ import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
import PySimpleGUI as psg
|
||||
import FreeSimpleGUI as psg
|
||||
|
||||
from . import models, util
|
||||
from . import configuration, models, util
|
||||
from .builder import Builder
|
||||
from .nvda import Nvda
|
||||
from .parser import Parser
|
||||
@@ -12,18 +12,19 @@ from .popup import Popup
|
||||
|
||||
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):
|
||||
"""Represents the main window of the Voicemeeter NVDA application"""
|
||||
|
||||
SETTINGS = "settings.json"
|
||||
|
||||
def __init__(self, title, vm):
|
||||
self.vm = vm
|
||||
self.kind = self.vm.kind
|
||||
self.logger = logger.getChild(type(self).__name__)
|
||||
self.logger.debug(f"loaded with theme: {psg.theme()}")
|
||||
self.cache = {
|
||||
"hw_ins": models._make_hardware_ins_cache(self.vm),
|
||||
"hw_outs": models._make_hardware_outs_cache(self.vm),
|
||||
@@ -47,7 +48,7 @@ class NVDAVMWindow(psg.Window):
|
||||
if self.kind.name == "basic":
|
||||
self["HARDWARE OUT||A2"].Widget.config(**buttonmenu_opts)
|
||||
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}
|
||||
for i in range(self.kind.num_strip):
|
||||
for param in util.get_slider_params(i, self.kind):
|
||||
@@ -58,22 +59,16 @@ class NVDAVMWindow(psg.Window):
|
||||
for i in range(self.kind.num_bus):
|
||||
self[f"BUS {i}||SLIDER GAIN"].Widget.config(**slider_opts)
|
||||
self[f"BUS {i}||MODE"].Widget.config(**buttonmenu_opts)
|
||||
if self.kind.name != "basic":
|
||||
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["tabgroup"].set_focus()
|
||||
|
||||
def __enter__(self):
|
||||
settings_path = Path.cwd() / self.SETTINGS
|
||||
settings_path = configuration.SETTINGS
|
||||
if settings_path.exists():
|
||||
try:
|
||||
with open(settings_path, "r") as f:
|
||||
data = json.load(f)
|
||||
defaultconfig = Path(data["default_config"])
|
||||
if defaultconfig.exists():
|
||||
defaultconfig = Path(configuration.get("default_config", "")) # coerce the type
|
||||
if defaultconfig.is_file() and defaultconfig.exists():
|
||||
self.vm.set("command.load", str(defaultconfig))
|
||||
self.logger.debug(f"config {defaultconfig} loaded")
|
||||
self.TKroot.after(
|
||||
@@ -82,7 +77,7 @@ class NVDAVMWindow(psg.Window):
|
||||
f"config {defaultconfig.stem} has been loaded",
|
||||
)
|
||||
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.observer.add(self.on_pdirty)
|
||||
@@ -124,10 +119,6 @@ class NVDAVMWindow(psg.Window):
|
||||
for i in range(self.kind.num_bus):
|
||||
self[f"BUS {i}||SLIDER GAIN"].update(value=self.vm.bus[i].gain)
|
||||
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():
|
||||
identifier, i = key.split("||")
|
||||
partial = util.get_channel_identifier_list(self.vm)[int(i)]
|
||||
@@ -180,30 +171,24 @@ class NVDAVMWindow(psg.Window):
|
||||
self.bind(f"<Alt-Control-{event}-{direction}>", f"ALT CTRL {direction.upper()}||{event_id}")
|
||||
|
||||
# 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("<space>", "||KEY SPACE", propagate=False)
|
||||
self[f"HARDWARE IN||{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||
|
||||
# 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("<space>", "||KEY SPACE", 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["HARDWARE OUT||A2"].bind("<FocusIn>", "||FOCUS IN")
|
||||
self["HARDWARE OUT||A2"].bind("<space>", "||KEY SPACE", 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
|
||||
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("<space>", "||KEY SPACE", propagate=False)
|
||||
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
|
||||
@@ -237,7 +222,12 @@ class NVDAVMWindow(psg.Window):
|
||||
self[f"STRIP {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
|
||||
self[f"STRIP {i}||{param}"].bind("<Return>", "||KEY ENTER")
|
||||
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("<Return>", "||KEY ENTER")
|
||||
|
||||
@@ -262,7 +252,7 @@ class NVDAVMWindow(psg.Window):
|
||||
|
||||
# Bus Params
|
||||
params = ["MONO", "EQ", "MUTE"]
|
||||
if self.vm.kind.name == "basic":
|
||||
if self.kind.name == "basic":
|
||||
params.remove("EQ")
|
||||
for i in range(self.kind.num_bus):
|
||||
for param in params:
|
||||
@@ -519,17 +509,27 @@ class NVDAVMWindow(psg.Window):
|
||||
file_types=(("XML", ".xml"),),
|
||||
):
|
||||
filepath = Path(filepath)
|
||||
with open(self.SETTINGS, "w") as f:
|
||||
json.dump({"default_config": str(filepath)}, f)
|
||||
configuration.set("default_config", str(filepath))
|
||||
self.TKroot.after(
|
||||
200,
|
||||
self.nvda.speak,
|
||||
f"config {filepath.stem} set as default on startup",
|
||||
)
|
||||
else:
|
||||
with open(self.SETTINGS, "wb") as f:
|
||||
f.truncate()
|
||||
self.logger.debug("settings.json was truncated")
|
||||
configuration.delete("default_config")
|
||||
self.logger.debug("default_config removed from settings.json")
|
||||
|
||||
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
|
||||
case ["tabgroup"] | [["tabgroup"], ["FOCUS", "IN"]]:
|
||||
@@ -586,33 +586,25 @@ class NVDAVMWindow(psg.Window):
|
||||
case [["HARDWARE", "OUT"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
||||
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
|
||||
case [["PATCH", "COMPOSITE"], [key]]:
|
||||
val = values[f"PATCH COMPOSITE||{key}"]
|
||||
index = int(key[-1]) - 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"]]:
|
||||
if self.find_element_with_focus() is not None:
|
||||
if values[f"PATCH COMPOSITE||{key}"]:
|
||||
val = values[f"PATCH COMPOSITE||{key}"]
|
||||
else:
|
||||
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}")
|
||||
case [["PATCH", "COMPOSITE"], [key], ["KEY", "SPACE" | "ENTER"]]:
|
||||
util.open_context_menu_for_buttonmenu(self, f"PATCH COMPOSITE||{key}")
|
||||
@@ -653,54 +645,36 @@ class NVDAVMWindow(psg.Window):
|
||||
|
||||
# Strip Params
|
||||
case [["STRIP", index], [param]]:
|
||||
label = self.cache["labels"][f"STRIP {index}||LABEL"]
|
||||
match param:
|
||||
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"
|
||||
phonetic = {"k": "karaoke"}
|
||||
if actual == "k":
|
||||
case "KARAOKE":
|
||||
opts = ["off", "k m", "k 1", "k 2", "k v"]
|
||||
next_val = self.vm.strip[int(index)].k + 1
|
||||
if next_val == 4:
|
||||
if next_val == len(opts):
|
||||
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.nvda.speak(["off", "k m", "k 1", "k 2"][next_val])
|
||||
else:
|
||||
val = not self.cache["strip"][f"STRIP {index}||{param}"]
|
||||
setattr(self.vm.strip[int(index)], actual, val)
|
||||
self.cache["strip"][f"STRIP {index}||{param}"] = val
|
||||
self.nvda.speak(opts[next_val])
|
||||
case output if param in util._get_bus_assignments(self.kind):
|
||||
val = not self.cache["strip"][f"STRIP {index}||{output}"]
|
||||
setattr(self.vm.strip[int(index)], output, val)
|
||||
self.cache["strip"][f"STRIP {index}||{output}"] = val
|
||||
self.nvda.speak("on" if val else "off")
|
||||
case _:
|
||||
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.nvda.speak("on" if val else "off")
|
||||
case [["STRIP", index], [param], ["FOCUS", "IN"]]:
|
||||
if self.find_element_with_focus() is not None:
|
||||
val = self.cache["strip"][f"STRIP {index}||{param}"]
|
||||
match param:
|
||||
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"}
|
||||
phonetic = {"KARAOKE": "karaoke"}
|
||||
label = self.cache["labels"][f"STRIP {index}||LABEL"]
|
||||
if actual == "k":
|
||||
if param == "KARAOKE":
|
||||
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:
|
||||
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"]]:
|
||||
self.find_element_with_focus().click()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user