Compare commits

...

11 Commits

Author SHA1 Message Date
09f052b4a3 defines slider mode nav events 2023-09-20 10:40:09 +01:00
577f1fbdbb fixes limit resolution 2023-09-20 10:38:30 +01:00
f9614f44ac popups moved into popup.py 2023-09-20 10:01:48 +01:00
9b757b743b shortens the spoken feedback
for most of the controls.

needs more testing...
2023-09-19 23:19:39 +01:00
7f8f6f2b6c adds more navigation binds.
adds slider modes.
2023-09-19 22:22:44 +01:00
0c806b8e4c add guard clauses for nav binds 2023-09-18 23:07:09 +01:00
645361c731 bump to pre-release a1 2023-09-18 09:38:37 +01:00
6baa0000c8 Merge remote-tracking branch 'origin/dev' into add-nav-binds 2023-09-18 09:18:44 +01:00
d722154325 adds ctrl+shift+r slider reset bind
closes #9

patch bump
2023-09-18 09:12:12 +01:00
c94d763569 adds some quick navigation binds 2023-09-18 00:07:35 +01:00
ac14b7ad0d adds sliders to on_pdirty
bump to b2
2023-09-17 14:49:42 +01:00
5 changed files with 424 additions and 217 deletions

View File

@@ -1,6 +1,6 @@
[project] [project]
name = "nvda_voicemeeter" name = "nvda_voicemeeter"
version = "0.2.1b1" version = "0.2.2a1"
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" },

View File

@@ -332,7 +332,7 @@ class Builder:
psg.Slider( psg.Slider(
range=(-40, 12), range=(-40, 12),
default_value=self.vm.strip[i].limit, default_value=self.vm.strip[i].limit,
resolution=0.1, resolution=1,
disable_number_display=True, disable_number_display=True,
expand_x=True, expand_x=True,
enable_events=True, enable_events=True,
@@ -433,7 +433,7 @@ class Builder:
psg.Slider( psg.Slider(
range=(-40, 12), range=(-40, 12),
default_value=self.vm.strip[i].limit, default_value=self.vm.strip[i].limit,
resolution=0.1, resolution=1,
disable_number_display=True, disable_number_display=True,
expand_x=True, expand_x=True,
enable_events=True, enable_events=True,

View File

@@ -10,17 +10,13 @@ class LabelSlider(psg.Frame):
size = 7 size = 7
else: else:
size = 4 size = 4
if param == "LIMIT":
resolution = 1
else:
resolution = 0.1
layout = [ layout = [
[ [
psg.Text(param.capitalize(), size=size), psg.Text(param.capitalize(), size=size),
psg.Slider( psg.Slider(
range=range_, range=range_,
default_value=self.default_value(i, param), default_value=self.default_value(i, param),
resolution=resolution, resolution=0.1,
disable_number_display=True, disable_number_display=True,
size=(12, 16), size=(12, 16),
expand_x=True, expand_x=True,

View File

@@ -0,0 +1,162 @@
import logging
from pathlib import Path
import PySimpleGUI as psg
from . import util
logger = logging.getLogger(__name__)
class Popup:
def __init__(self, window):
self.window = window
self.logger = logger.getChild(type(self).__name__)
def save_as(self, message, title=None, initial_folder=None):
layout = [
[psg.Text(message)],
[
psg.FileSaveAs("Browse", initial_folder=str(initial_folder), file_types=(("XML", ".xml"),)),
psg.Button("Cancel"),
],
]
popup = psg.Window(title, layout, finalize=True)
popup["Browse"].bind("<FocusIn>", "||FOCUS IN")
popup["Browse"].bind("<Return>", "||KEY ENTER")
popup["Cancel"].bind("<FocusIn>", "||FOCUS IN")
popup["Cancel"].bind("<Return>", "||KEY ENTER")
filepath = None
while True:
event, values = popup.read()
self.logger.debug(f"event::{event}")
self.logger.debug(f"values::{values}")
if event in (psg.WIN_CLOSED, "Cancel"):
break
match parsed_cmd := self.window.parser.match.parseString(event):
case [[button], ["FOCUS", "IN"]]:
if values["Browse"]:
filepath = values["Browse"]
break
self.window.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
popup.find_element_with_focus().click()
self.logger.debug(f"parsed::{parsed_cmd}")
popup.close()
if filepath:
return Path(filepath)
def rename(self, message, title=None, tab=None):
if tab == "Physical Strip":
upper = self.window.kind.phys_in + 1
elif tab == "Virtual Strip":
upper = self.window.kind.virt_in + 1
elif tab == "Buses":
upper = self.window.kind.num_bus + 1
layout = [
[psg.Text(message)],
[
[
psg.Spin(
list(range(1, upper)), initial_value=1, size=2, enable_events=True, key=f"Index", readonly=True
),
psg.Input(key="Edit"),
],
[psg.Button("Ok"), psg.Button("Cancel")],
],
]
popup = psg.Window(title, layout, finalize=True)
popup["Index"].bind("<FocusIn>", "||FOCUS IN")
popup["Edit"].bind("<FocusIn>", "||FOCUS IN")
popup["Ok"].bind("<FocusIn>", "||FOCUS IN")
popup["Ok"].bind("<Return>", "||KEY ENTER")
popup["Cancel"].bind("<FocusIn>", "||FOCUS IN")
popup["Cancel"].bind("<Return>", "||KEY ENTER")
data = {}
while True:
event, values = popup.read()
self.logger.debug(f"event::{event}")
self.logger.debug(f"values::{values}")
if event in (psg.WIN_CLOSED, "Cancel"):
break
match parsed_cmd := self.window.parser.match.parseString(event):
case ["Index"]:
val = values["Index"]
self.window.nvda.speak(f"Index {val}")
case [[button], ["FOCUS", "IN"]]:
if button == "Index":
val = values["Index"]
self.window.nvda.speak(f"Index {val}")
else:
self.window.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
popup.find_element_with_focus().click()
case ["Ok"]:
data = values
break
self.logger.debug(f"parsed::{parsed_cmd}")
popup.close()
return data
def advanced_settings(self, title):
def _make_buffering_frame() -> psg.Frame:
buffer = [
[
psg.ButtonMenu(
driver,
size=(14, 2),
menu_def=["", util.get_asio_samples_list(driver)],
key=f"BUFFER {driver}",
)
for driver in ("MME", "WDM", "KS", "ASIO")
],
]
return psg.Frame("BUFFERING", buffer)
layout = []
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)
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")
while True:
event, values = 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.parser.match.parseString(event):
case ["BUFFER MME" | "BUFFER WDM" | "BUFFER KS" | "BUFFER ASIO"]:
if values[event] == "Default":
if "MME" in event:
val = 1024
elif "WDM" in event or "KS" in event:
val = 512
else:
val = 0
else:
val = int(values[event])
driver = event.split()[1]
self.vm.set(f"option.buffer.{driver.lower()}", val)
self.TKroot.after(200, self.window.nvda.speak, f"{driver} BUFFER {val if val else 'default'}")
case [["BUFFER", driver], ["FOCUS", "IN"]]:
val = int(self.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}")
case [[button], ["FOCUS", "IN"]]:
self.window.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
popup.find_element_with_focus().click()
self.logger.debug(f"parsed::{parsed_cmd}")
popup.close()

View File

@@ -8,6 +8,7 @@ from . import 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
from .popup import Popup
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -34,6 +35,7 @@ class NVDAVMWindow(psg.Window):
} }
self.nvda = Nvda() self.nvda = Nvda()
self.parser = Parser() self.parser = Parser()
self.popup = Popup(self)
self.builder = Builder(self) self.builder = Builder(self)
layout = self.builder.run() layout = self.builder.run()
super().__init__(title, layout, return_keyboard_events=True, finalize=True) super().__init__(title, layout, return_keyboard_events=True, finalize=True)
@@ -48,12 +50,8 @@ class NVDAVMWindow(psg.Window):
[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.phys_out)]
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):
if i < self.kind.phys_in: for param in util.get_slider_params(i, self.vm):
for param in util.get_slider_params(i, self.vm): self[f"STRIP {i}||SLIDER {param}"].Widget.config(**slider_opts)
self[f"STRIP {i}||SLIDER {param}"].Widget.config(**slider_opts)
else:
for param in ("BASS", "MID", "TREBLE"):
self[f"STRIP {i}||SLIDER {param}"].Widget.config(**slider_opts)
self[f"STRIP {i}||SLIDER GAIN"].Widget.config(**slider_opts) self[f"STRIP {i}||SLIDER GAIN"].Widget.config(**slider_opts)
if self.kind.name != "basic": if self.kind.name != "basic":
self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts) self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts)
@@ -109,6 +107,15 @@ class NVDAVMWindow(psg.Window):
self[f"{key}||SLIDER"].update(value=value) self[f"{key}||SLIDER"].update(value=value)
for i in range(self.kind.num_strip): for i in range(self.kind.num_strip):
self[f"STRIP {i}||SLIDER GAIN"].update(value=self.vm.strip[i].gain) self[f"STRIP {i}||SLIDER GAIN"].update(value=self.vm.strip[i].gain)
if self.kind.name != "basic":
self[f"STRIP {i}||SLIDER LIMIT"].update(value=self.vm.strip[i].limit)
for param in util.get_slider_params(i, self.vm):
if param in ("AUDIBILITY", "BASS", "MID", "TREBLE"):
val = getattr(self.vm.strip[i], param.lower())
else:
target = getattr(self.vm.strip[i], param.lower())
val = target.knob
self[f"STRIP {i}||SLIDER {param}"].update(value=val)
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":
@@ -131,7 +138,37 @@ class NVDAVMWindow(psg.Window):
self[f"tabgroup||{tabname}"].bind("<Shift-KeyPress-Tab>", "||KEY SHIFT TAB") self[f"tabgroup||{tabname}"].bind("<Shift-KeyPress-Tab>", "||KEY SHIFT TAB")
self.bind("<Control-KeyPress-Tab>", "CTRL-TAB") self.bind("<Control-KeyPress-Tab>", "CTRL-TAB")
self.bind("<Control-Shift-KeyPress-Tab>", "CTRL-SHIFT-TAB") self.bind("<Control-Shift-KeyPress-Tab>", "CTRL-SHIFT-TAB")
# NAV
self.bind("<Control-a>", "CTRL-A") self.bind("<Control-a>", "CTRL-A")
for i in range(1, 10):
self.bind(f"<Control-Key-{i}>", f"CTRL-{i}")
for i in range(1, 10):
self.bind(f"<Alt-Key-{i}>", f"ALT-{i}")
self.bind("<Control-o>", "CTRL-O")
self.bind("<Control-s>", "CTRL-S")
self.bind("<Control-m>", "CTRL-M")
if self.kind.name == "basic":
self.bind("<Control-u>", "AUDIBILITY MODE")
self.bind("<Control-g>", "GAIN MODE")
elif self.kind.name == "banana":
self.bind("<Control-g>", "GAIN MODE")
self.bind("<Control-c>", "COMP MODE")
self.bind("<Control-t>", "GATE MODE")
self.bind("<Control-l>", "LIMIT MODE")
else:
self.bind("<Control-g>", "GAIN MODE")
self.bind("<Control-c>", "COMP MODE")
self.bind("<Control-t>", "GATE MODE")
self.bind("<Control-d>", "DENOISER MODE")
self.bind("<Control-l>", "LIMIT MODE")
self.bind("<Alt-Left>", "LEFT")
self.bind("<Alt-Right>", "RIGHT")
self.bind("<Alt-Shift-KeyPress-Left>", "SHIFT-LEFT")
self.bind("<Alt-Shift-KeyPress-Right>", "SHIFT-RIGHT")
self.bind("<Alt-Control-KeyPress-Left>", "CTRL-LEFT")
self.bind("<Alt-Control-KeyPress-Right>", "CTRL-RIGHT")
# Hardware In # Hardware In
for i in range(self.vm.kind.phys_in): for i in range(self.vm.kind.phys_in):
@@ -211,6 +248,7 @@ class NVDAVMWindow(psg.Window):
self[f"STRIP {i}||SLIDER {param}"].bind("<Shift-KeyPress-Down>", "||KEY SHIFT DOWN") self[f"STRIP {i}||SLIDER {param}"].bind("<Shift-KeyPress-Down>", "||KEY SHIFT DOWN")
self[f"STRIP {i}||SLIDER {param}"].bind("<Control-KeyPress-Up>", "||KEY CTRL UP") self[f"STRIP {i}||SLIDER {param}"].bind("<Control-KeyPress-Up>", "||KEY CTRL UP")
self[f"STRIP {i}||SLIDER {param}"].bind("<Control-KeyPress-Down>", "||KEY CTRL DOWN") self[f"STRIP {i}||SLIDER {param}"].bind("<Control-KeyPress-Down>", "||KEY CTRL DOWN")
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", "MODE"]
@@ -237,154 +275,7 @@ class NVDAVMWindow(psg.Window):
self[f"BUS {i}||SLIDER GAIN"].bind("<Shift-KeyPress-Down>", "||KEY SHIFT DOWN") self[f"BUS {i}||SLIDER GAIN"].bind("<Shift-KeyPress-Down>", "||KEY SHIFT DOWN")
self[f"BUS {i}||SLIDER GAIN"].bind("<Control-KeyPress-Up>", "||KEY CTRL UP") self[f"BUS {i}||SLIDER GAIN"].bind("<Control-KeyPress-Up>", "||KEY CTRL UP")
self[f"BUS {i}||SLIDER GAIN"].bind("<Control-KeyPress-Down>", "||KEY CTRL DOWN") self[f"BUS {i}||SLIDER GAIN"].bind("<Control-KeyPress-Down>", "||KEY CTRL DOWN")
self[f"BUS {i}||SLIDER GAIN"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
def popup_save_as(self, message, title=None, initial_folder=None):
layout = [
[psg.Text(message)],
[
psg.FileSaveAs("Browse", initial_folder=str(initial_folder), file_types=(("XML", ".xml"),)),
psg.Button("Cancel"),
],
]
window = psg.Window(title, layout, finalize=True)
window["Browse"].bind("<FocusIn>", "||FOCUS IN")
window["Browse"].bind("<Return>", "||KEY ENTER")
window["Cancel"].bind("<FocusIn>", "||FOCUS IN")
window["Cancel"].bind("<Return>", "||KEY ENTER")
filepath = None
while True:
event, values = window.read()
self.logger.debug(f"event::{event}")
self.logger.debug(f"values::{values}")
if event in (psg.WIN_CLOSED, "Cancel"):
break
match parsed_cmd := self.parser.match.parseString(event):
case [[button], ["FOCUS", "IN"]]:
if values["Browse"]:
filepath = values["Browse"]
break
self.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
window.find_element_with_focus().click()
self.logger.debug(f"parsed::{parsed_cmd}")
window.close()
if filepath:
return Path(filepath)
def popup_rename(self, message, title=None, tab=None):
if tab == "Physical Strip":
upper = self.kind.phys_in + 1
elif tab == "Virtual Strip":
upper = self.kind.virt_in + 1
elif tab == "Buses":
upper = self.kind.num_bus + 1
layout = [
[psg.Text(message)],
[
[
psg.Spin(
list(range(1, upper)), initial_value=1, size=2, enable_events=True, key=f"Index", readonly=True
),
psg.Input(key="Edit"),
],
[psg.Button("Ok"), psg.Button("Cancel")],
],
]
window = psg.Window(title, layout, finalize=True)
window["Index"].bind("<FocusIn>", "||FOCUS IN")
window["Edit"].bind("<FocusIn>", "||FOCUS IN")
window["Ok"].bind("<FocusIn>", "||FOCUS IN")
window["Ok"].bind("<Return>", "||KEY ENTER")
window["Cancel"].bind("<FocusIn>", "||FOCUS IN")
window["Cancel"].bind("<Return>", "||KEY ENTER")
data = {}
while True:
event, values = window.read()
self.logger.debug(f"event::{event}")
self.logger.debug(f"values::{values}")
if event in (psg.WIN_CLOSED, "Cancel"):
break
match parsed_cmd := self.parser.match.parseString(event):
case ["Index"]:
val = values["Index"]
self.nvda.speak(f"Index {val}")
case [[button], ["FOCUS", "IN"]]:
if button == "Index":
val = values["Index"]
self.nvda.speak(f"Index {val}")
else:
self.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
window.find_element_with_focus().click()
case ["Ok"]:
data = values
break
self.logger.debug(f"parsed::{parsed_cmd}")
window.close()
return data
def popup_advanced_settings(self, title):
def _make_buffering_frame() -> psg.Frame:
buffer = [
[
psg.ButtonMenu(
driver,
size=(14, 2),
menu_def=["", util.get_asio_samples_list(driver)],
key=f"BUFFER {driver}",
)
for driver in ("MME", "WDM", "KS", "ASIO")
],
]
return psg.Frame("BUFFERING", buffer)
layout = []
steps = (_make_buffering_frame,)
for step in steps:
layout.append([step()])
layout.append([psg.Button("Exit", size=(8, 2))])
window = psg.Window(title, layout, finalize=True)
buttonmenu_opts = {"takefocus": 1, "highlightthickness": 1}
for driver in ("MME", "WDM", "KS", "ASIO"):
window[f"BUFFER {driver}"].Widget.config(**buttonmenu_opts)
window[f"BUFFER {driver}"].bind("<FocusIn>", "||FOCUS IN")
window[f"BUFFER {driver}"].bind("<space>", "||KEY SPACE", propagate=False)
window[f"BUFFER {driver}"].bind("<Return>", "||KEY ENTER", propagate=False)
window["Exit"].bind("<FocusIn>", "||FOCUS IN")
window["Exit"].bind("<Return>", "||KEY ENTER")
while True:
event, values = window.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.parser.match.parseString(event):
case ["BUFFER MME" | "BUFFER WDM" | "BUFFER KS" | "BUFFER ASIO"]:
if values[event] == "Default":
if "MME" in event:
val = 1024
elif "WDM" in event or "KS" in event:
val = 512
else:
val = 0
else:
val = int(values[event])
driver = event.split()[1]
self.vm.set(f"option.buffer.{driver.lower()}", val)
self.TKroot.after(200, self.nvda.speak, f"{driver} BUFFER {val if val else 'default'}")
case [["BUFFER", driver], ["FOCUS", "IN"]]:
val = int(self.vm.get(f"option.buffer.{driver.lower()}"))
self.nvda.speak(f"{driver} BUFFER {val if val else 'default'}")
case [["BUFFER", driver], ["KEY", "SPACE" | "ENTER"]]:
util.open_context_menu_for_buttonmenu(window, f"BUFFER {driver}")
case [[button], ["FOCUS", "IN"]]:
self.nvda.speak(button)
case [[button], ["KEY", "ENTER"]]:
window.find_element_with_focus().click()
self.logger.debug(f"parsed::{parsed_cmd}")
window.close()
def run(self): def run(self):
""" """
@@ -392,6 +283,7 @@ class NVDAVMWindow(psg.Window):
Main thread will shutdown once a close or exit event occurs Main thread will shutdown once a close or exit event occurs
""" """
mode = None
while True: while True:
event, values = self.read() event, values = self.read()
@@ -399,6 +291,18 @@ 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 in ("GAIN MODE", "COMP MODE", "GATE MODE", "DENOISER MODE", "LIMIT MODE"):
mode = event
self.nvda.speak(f"{mode} enabled")
elif event == "Escape:27":
if mode:
self.nvda.speak(f"{mode.split()[0]} mode disabled")
mode = None
if mode:
if event in ("LEFT", "RIGHT", "SHIFT-LEFT", "SHIFT-RIGHT", "CTRL-LEFT", "CTRL-RIGHT"):
self.write_event_value(f"SLIDER-MODE-{event}", mode.split()[0])
continue
match parsed_cmd := self.parser.match.parseString(event): match parsed_cmd := self.parser.match.parseString(event):
# Focus tabgroup # Focus tabgroup
@@ -406,11 +310,122 @@ class NVDAVMWindow(psg.Window):
self["tabgroup"].set_focus() self["tabgroup"].set_focus()
self.nvda.speak(f"{values['tabgroup']}") self.nvda.speak(f"{values['tabgroup']}")
# Quick Navigation
case ["CTRL-1" | "CTRL-2" | "CTRL-3" | "CTRL-4" | "CTRL-5" | "CTRL-6" | "CTRL-7" | "CTRL-8" as bind]:
key, index = bind.split("-")
match values["tabgroup"]:
case "tab||Physical Strip":
if int(index) > self.kind.phys_in:
continue
self[f"STRIP {int(index) - 1}||A1"].set_focus()
if (
self.find_element_with_focus() is None
or self.find_element_with_focus().Key != f"STRIP {int(index) - 1}||A1"
):
self[f"STRIP {int(index) - 1}||SLIDER GAIN"].set_focus()
case "tab||Virtual Strip":
index = int(index) + self.kind.phys_in
if index > self.kind.num_strip:
continue
self[f"STRIP {index - 1}||A1"].set_focus()
if (
self.find_element_with_focus() is None
or self.find_element_with_focus().Key != f"STRIP {int(index) - 1}||A1"
):
self[f"STRIP {int(index) - 1}||SLIDER GAIN"].set_focus()
case "tab||Buses":
if int(index) > self.kind.num_bus:
continue
self[f"BUS {int(index) - 1}||MONO"].set_focus()
if (
self.find_element_with_focus() is None
or self.find_element_with_focus().Key != f"BUS {int(index) - 1}||MONO"
):
self[f"BUS {int(index) - 1}||SLIDER GAIN"].set_focus()
case ["ALT-1" | "ALT-2" | "ALT-3" | "ALT-4" | "ALT-5" | "ALT-6" | "ALT-7" | "ALT-8" as bind]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
key, index = bind.split("-")
if int(index) > self.kind.phys_out + self.kind.virt_out:
continue
if focus := self.find_element_with_focus():
identifier, param = focus.Key.split("||")
if int(index) <= self.kind.phys_out:
self.write_event_value(f"{identifier}||A{int(index)}", None)
else:
self.write_event_value(f"{identifier}||B{int(index) - self.kind.phys_out}", None)
case ["CTRL-O"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
if focus := self.find_element_with_focus():
identifier, param = focus.Key.split("||")
self.write_event_value(f"{identifier}||MONO", None)
case ["CTRL-S"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip"):
continue
if focus := self.find_element_with_focus():
identifier, param = focus.Key.split("||")
self.write_event_value(f"{identifier}||SOLO", None)
case ["CTRL-M"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
if focus := self.find_element_with_focus():
identifier, param = focus.Key.split("||")
self.write_event_value(f"{identifier}||MUTE", None)
case ["SLIDER-MODE-LEFT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY LEFT", None)
case ["SLIDER-MODE-RIGHT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY RIGHT", None)
case ["SLIDER-MODE-SHIFT-LEFT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY SHIFT LEFT", None)
case ["SLIDER-MODE-SHIFT-RIGHT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY SHIFT RIGHT", None)
case ["SLIDER-MODE-CTRL-LEFT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY CTRL LEFT", None)
case ["SLIDER-MODE-CTRL-RIGHT"]:
if values["tabgroup"] not in ("tab||Physical Strip", "tab||Virtual Strip", "tab||Buses"):
continue
param = values[event]
if focus := self.find_element_with_focus():
identifier, partial = focus.Key.split("||")
if "SLIDER" not in partial:
self.write_event_value(f"{identifier}||SLIDER {param}||KEY CTRL RIGHT", None)
# Rename popups # Rename popups
case ["F2:113"]: case ["F2:113"]:
tab = values["tabgroup"] tab = values["tabgroup"].split("||")[1]
if tab in ("Physical Strip", "Virtual Strip", "Buses"): if tab in ("Physical Strip", "Virtual Strip", "Buses"):
data = self.popup_rename("Label", title=f"Rename {tab}", tab=tab) data = self.popup.rename("Label", title=f"Rename {tab}", tab=tab)
if not data: # cancel was pressed if not data: # cancel was pressed
continue continue
index = int(data["Index"]) - 1 index = int(data["Index"]) - 1
@@ -446,7 +461,7 @@ class NVDAVMWindow(psg.Window):
) )
case [["Save", "Settings"], ["MENU"]]: case [["Save", "Settings"], ["MENU"]]:
initial_folder = Path.home() / "Documents" / "Voicemeeter" initial_folder = Path.home() / "Documents" / "Voicemeeter"
if filepath := self.popup_save_as( if filepath := self.popup.save_as(
"Open the file browser", title="Save As", initial_folder=initial_folder "Open the file browser", title="Save As", initial_folder=initial_folder
): ):
self.vm.set("command.save", str(filepath)) self.vm.set("command.save", str(filepath))
@@ -558,7 +573,7 @@ class NVDAVMWindow(psg.Window):
val = values[f"ASIO CHECKBOX||{in_num} {channel}"] val = values[f"ASIO CHECKBOX||{in_num} {channel}"]
self.vm.patch.asio[index].set(val) self.vm.patch.asio[index].set(val)
channel = ("left", "right")[int(channel)] channel = ("left", "right")[int(channel)]
self.nvda.speak(f"Patch ASIO {in_num} {channel} set to {val}") self.nvda.speak(str(val))
case [["ASIO", "CHECKBOX"], [in_num, channel], ["FOCUS", "IN"]]: case [["ASIO", "CHECKBOX"], [in_num, channel], ["FOCUS", "IN"]]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
val = values[f"ASIO CHECKBOX||{in_num} {channel}"] val = values[f"ASIO CHECKBOX||{in_num} {channel}"]
@@ -592,9 +607,7 @@ class NVDAVMWindow(psg.Window):
) )
val = values[f"INSERT CHECKBOX||{in_num} {channel}"] val = values[f"INSERT CHECKBOX||{in_num} {channel}"]
self.vm.patch.insert[index].on = val self.vm.patch.insert[index].on = val
self.nvda.speak( self.nvda.speak(f"{'on' if val else 'off'}")
f"PATCH INSERT {in_num} {util._patch_insert_channels[int(channel)]} set to {'on' if val else 'off'}"
)
case [["INSERT", "CHECKBOX"], [in_num, channel], ["FOCUS", "IN"]]: case [["INSERT", "CHECKBOX"], [in_num, channel], ["FOCUS", "IN"]]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
index = util.get_insert_checkbox_index( index = util.get_insert_checkbox_index(
@@ -610,7 +623,7 @@ class NVDAVMWindow(psg.Window):
# Advanced Settings # Advanced Settings
case ["ADVANCED SETTINGS"] | ["CTRL-A"]: case ["ADVANCED SETTINGS"] | ["CTRL-A"]:
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"]]:
self.nvda.speak("ADVANCED SETTINGS") self.nvda.speak("ADVANCED SETTINGS")
case [["ADVANCED", "SETTINGS"], ["KEY", "ENTER"]]: case [["ADVANCED", "SETTINGS"], ["KEY", "ENTER"]]:
@@ -634,19 +647,17 @@ class NVDAVMWindow(psg.Window):
next_val = 0 next_val = 0
setattr(self.vm.strip[int(index)], actual, next_val) setattr(self.vm.strip[int(index)], actual, next_val)
self.cache["strip"][f"STRIP {index}||{param}"] = next_val self.cache["strip"][f"STRIP {index}||{param}"] = next_val
self.nvda.speak( self.nvda.speak(["off", "k m", "k 1", "k 2"][next_val])
f"{label} {phonetic.get(actual, actual)} {['off', 'k m', 'k 1', 'k 2'][next_val]}"
)
else: else:
val = not self.cache["strip"][f"STRIP {index}||{param}"] val = not self.cache["strip"][f"STRIP {index}||{param}"]
setattr(self.vm.strip[int(index)], actual, val) setattr(self.vm.strip[int(index)], actual, val)
self.cache["strip"][f"STRIP {index}||{param}"] = val self.cache["strip"][f"STRIP {index}||{param}"] = val
self.nvda.speak(f"{label} {phonetic.get(actual, actual)} {'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 if param[0] in ("A", "B") else param.lower(), val)
self.cache["strip"][f"STRIP {index}||{param}"] = val self.cache["strip"][f"STRIP {index}||{param}"] = val
self.nvda.speak(f"{label} {param} {'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}"]
@@ -687,7 +698,6 @@ class NVDAVMWindow(psg.Window):
| "TREBLE" as param, | "TREBLE" as param,
], ],
]: ]:
label = self.cache["labels"][f"STRIP {index}||LABEL"]
val = values[event] val = values[event]
match param: match param:
case "GAIN": case "GAIN":
@@ -702,7 +712,6 @@ class NVDAVMWindow(psg.Window):
self.vm.strip[int(index)].limit = val self.vm.strip[int(index)].limit = val
case "BASS" | "MID" | "TREBLE": case "BASS" | "MID" | "TREBLE":
setattr(self.vm.strip[int(index)], param.lower(), val) setattr(self.vm.strip[int(index)], param.lower(), val)
self.nvda.speak(f"{label} {param} slider {val}")
case [ case [
["STRIP", index], ["STRIP", index],
[ [
@@ -721,11 +730,9 @@ class NVDAVMWindow(psg.Window):
]: ]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
self.vm.event.pdirty = False self.vm.event.pdirty = False
label = self.cache["labels"][f"STRIP {index}||LABEL"]
val = values[f"STRIP {index}||SLIDER {param}"] val = values[f"STRIP {index}||SLIDER {param}"]
if param == "LIMIT": label = self.cache["labels"][f"STRIP {index}||LABEL"]
val = int(val) self.nvda.speak(f"{label} {param} {int(val) if param == 'LIMIT' else val}")
self.nvda.speak(f"{label} {param} slider {val}")
case [ case [
["STRIP", index], ["STRIP", index],
[ [
@@ -772,20 +779,26 @@ class NVDAVMWindow(psg.Window):
match param: match param:
case "GAIN": case "GAIN":
self.vm.strip[int(index)].gain = util.check_bounds(val, (-60, 12)) val = util.check_bounds(val, (-60, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-60, 12))) self.vm.strip[int(index)].gain = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "COMP" | "GATE" | "DENOISER": case "COMP" | "GATE" | "DENOISER":
setattr(target, "knob", util.check_bounds(val, (0, 10))) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) setattr(target, "knob", val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "AUDIBILITY": case "AUDIBILITY":
self.vm.strip[int(index)].audibility = util.check_bounds(val, (0, 10)) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) self.vm.strip[int(index)].audibility = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "BASS" | "MID" | "TREBLE": case "BASS" | "MID" | "TREBLE":
setattr(self.vm.strip[int(index)], param.lower(), util.check_bounds(val, (-12, 12))) val = util.check_bounds(val, (-12, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-12, 12))) setattr(self.vm.strip[int(index)], param.lower(), val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "LIMIT": case "LIMIT":
self.vm.strip[int(index)].limit = util.check_bounds(val, (-40, 12)) val = util.check_bounds(val, (-40, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-40, 12))) self.vm.strip[int(index)].limit = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
self.nvda.speak(str(val))
case [ case [
["STRIP", index], ["STRIP", index],
[ [
@@ -829,20 +842,26 @@ class NVDAVMWindow(psg.Window):
match param: match param:
case "GAIN": case "GAIN":
self.vm.strip[int(index)].gain = util.check_bounds(val, (-60, 12)) val = util.check_bounds(val, (-60, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-60, 12))) self.vm.strip[int(index)].gain = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "COMP" | "GATE" | "DENOISER": case "COMP" | "GATE" | "DENOISER":
setattr(target, "knob", util.check_bounds(val, (0, 10))) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) setattr(target, "knob", val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "AUDIBILITY": case "AUDIBILITY":
self.vm.strip[int(index)].audibility = util.check_bounds(val, (0, 10)) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) self.vm.strip[int(index)].audibility = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "BASS" | "MID" | "TREBLE": case "BASS" | "MID" | "TREBLE":
setattr(self.vm.strip[int(index)], param.lower(), util.check_bounds(val, (-12, 12))) val = util.check_bounds(val, (-12, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-12, 12))) setattr(self.vm.strip[int(index)], param.lower(), val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "LIMIT": case "LIMIT":
self.vm.strip[int(index)].limit = util.check_bounds(val, (-40, 12)) val = util.check_bounds(val, (-40, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-40, 12))) self.vm.strip[int(index)].limit = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
self.nvda.speak(f"{param} {val}")
case [ case [
["STRIP", index], ["STRIP", index],
[ [
@@ -886,20 +905,44 @@ class NVDAVMWindow(psg.Window):
match param: match param:
case "GAIN": case "GAIN":
self.vm.strip[int(index)].gain = util.check_bounds(val, (-60, 12)) val = util.check_bounds(val, (-60, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-60, 12))) self.vm.strip[int(index)].gain = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "COMP" | "GATE" | "DENOISER": case "COMP" | "GATE" | "DENOISER":
setattr(target, "knob", util.check_bounds(val, (0, 10))) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) setattr(target, "knob", val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "AUDIBILITY": case "AUDIBILITY":
self.vm.strip[int(index)].audibility = util.check_bounds(val, (0, 10)) val = util.check_bounds(val, (0, 10))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (0, 10))) self.vm.strip[int(index)].audibility = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "BASS" | "MID" | "TREBLE": case "BASS" | "MID" | "TREBLE":
setattr(self.vm.strip[int(index)], param.lower(), util.check_bounds(val, (-12, 12))) val = util.check_bounds(val, (-12, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-12, 12))) setattr(self.vm.strip[int(index)], param.lower(), val)
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
case "LIMIT": case "LIMIT":
self.vm.strip[int(index)].limit = util.check_bounds(val, (-40, 12)) val = util.check_bounds(val, (-40, 12))
self[f"STRIP {index}||SLIDER {param}"].update(value=util.check_bounds(val, (-40, 12))) self.vm.strip[int(index)].limit = val
self[f"STRIP {index}||SLIDER {param}"].update(value=val)
self.nvda.speak(f"{param} {val}")
case [["STRIP", index], ["SLIDER", param], ["KEY", "CTRL", "SHIFT", "R"]]:
match param:
case "GAIN":
self.vm.strip[int(index)].gain = 0
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
case "COMP" | "GATE" | "DENOISER":
setattr(target, "knob", 0)
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
case "AUDIBILITY":
self.vm.strip[int(index)].audibility = 0
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
case "BASS" | "MID" | "TREBLE":
setattr(self.vm.strip[int(index)], param.lower(), 0)
self[f"STRIP {index}||SLIDER {param}"].update(value=0)
case "LIMIT":
self.vm.strip[int(index)].limit = 12
self[f"STRIP {index}||SLIDER {param}"].update(value=12)
self.nvda.speak(f"{param} {12 if param == 'LABEL' else 0}")
# Bus Params # Bus Params
case [["BUS", index], [param]]: case [["BUS", index], [param]]:
@@ -913,7 +956,7 @@ class NVDAVMWindow(psg.Window):
self.TKroot.after( self.TKroot.after(
200, 200,
self.nvda.speak, self.nvda.speak,
f"{label} bus {param} {'on' if val else 'off'}", "on" if val else "off",
) )
case "MONO" | "MUTE": case "MONO" | "MUTE":
val = not val val = not val
@@ -922,7 +965,7 @@ class NVDAVMWindow(psg.Window):
self.TKroot.after( self.TKroot.after(
200, 200,
self.nvda.speak, self.nvda.speak,
f"{label} bus {param} {'on' if val else 'off'}", "on" if val else "off",
) )
case "MODE": case "MODE":
bus_modes = util.get_bus_modes(self.vm) bus_modes = util.get_bus_modes(self.vm)
@@ -947,7 +990,7 @@ class NVDAVMWindow(psg.Window):
self.TKroot.after( self.TKroot.after(
200, 200,
self.nvda.speak, self.nvda.speak,
f"{label} bus mode {phonetic.get(next_bus, next_bus)}", phonetic.get(next_bus, next_bus),
) )
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:
@@ -965,13 +1008,12 @@ class NVDAVMWindow(psg.Window):
label = self.cache["labels"][f"BUS {index}||LABEL"] label = self.cache["labels"][f"BUS {index}||LABEL"]
val = values[event] val = values[event]
self.vm.bus[int(index)].gain = val self.vm.bus[int(index)].gain = val
self.nvda.speak(f"{label} gain slider {val}")
case [["BUS", index], ["SLIDER", "GAIN"], ["FOCUS", "IN"]]: case [["BUS", index], ["SLIDER", "GAIN"], ["FOCUS", "IN"]]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
self.vm.event.pdirty = False self.vm.event.pdirty = False
label = self.cache["labels"][f"BUS {index}||LABEL"] label = self.cache["labels"][f"BUS {index}||LABEL"]
val = values[f"BUS {index}||SLIDER GAIN"] val = values[f"BUS {index}||SLIDER GAIN"]
self.nvda.speak(f"{label} gain slider {val}") self.nvda.speak(f"{label} gain {val}")
case [["BUS", index], ["SLIDER", "GAIN"], ["FOCUS", "OUT"]]: case [["BUS", index], ["SLIDER", "GAIN"], ["FOCUS", "OUT"]]:
self.vm.event.pdirty = True self.vm.event.pdirty = True
case [["BUS", index], ["SLIDER", "GAIN"], ["KEY", "LEFT" | "RIGHT" | "UP" | "DOWN" as direction]]: case [["BUS", index], ["SLIDER", "GAIN"], ["KEY", "LEFT" | "RIGHT" | "UP" | "DOWN" as direction]]:
@@ -994,8 +1036,10 @@ class NVDAVMWindow(psg.Window):
val += 3 val += 3
case "LEFT" | "DOWN": case "LEFT" | "DOWN":
val -= 3 val -= 3
self.vm.bus[int(index)].gain = util.check_bounds(val, (-60, 12)) val = util.check_bounds(val, (-60, 12))
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))
case [ case [
["BUS", index], ["BUS", index],
["SLIDER", "GAIN"], ["SLIDER", "GAIN"],
@@ -1009,6 +1053,11 @@ class NVDAVMWindow(psg.Window):
val -= 0.1 val -= 0.1
self.vm.bus[int(index)].gain = util.check_bounds(val, (-60, 12)) self.vm.bus[int(index)].gain = util.check_bounds(val, (-60, 12))
self[f"BUS {index}||SLIDER GAIN"].update(value=val) self[f"BUS {index}||SLIDER GAIN"].update(value=val)
self.nvda.speak(str(val))
case [["BUS", index], ["SLIDER", "GAIN"], ["KEY", "CTRL", "SHIFT", "R"]]:
self.vm.bus[int(index)].gain = 0
self[f"BUS {index}||SLIDER GAIN"].update(value=0)
self.nvda.speak(str(val))
# Unknown # Unknown
case _: case _: