Compare commits

...

7 Commits

Author SHA1 Message Date
bdd570738a fix version bump... 2023-09-29 01:18:57 +01:00
71b137a9c2 use self.kind 2023-09-29 01:16:48 +01:00
912eb8c14d number of patch composite buttons fixed
for banana and potato kinds

voicemeeter-api dependency bump

patch bump
2023-09-29 01:09:18 +01:00
9cd65737e5 bump to version 0.5.1
closes #16
2023-09-28 23:35:02 +01:00
4a6ca2a353 check against slider modes explicitly
add enter/exit slider mode debug logging
2023-09-28 22:43:06 +01:00
cc99b14e89 define space bind event for bus buttons 2023-09-28 21:35:05 +01:00
23458debaa initial busmode buttonmenu implementation
bump to 0.5.1a1
Issue #16
2023-09-28 19:44:42 +01:00
5 changed files with 87 additions and 50 deletions

6
pdm.lock generated
View File

@ -251,13 +251,13 @@ files = [
[[package]] [[package]]
name = "voicemeeter-api" name = "voicemeeter-api"
version = "2.4.10" version = "2.4.11"
requires_python = ">=3.10,<4.0" requires_python = ">=3.10,<4.0"
summary = "A Python wrapper for the Voiceemeter API" summary = "A Python wrapper for the Voiceemeter API"
dependencies = [ dependencies = [
"tomli<3.0.0,>=2.0.1; python_version < \"3.11\"", "tomli<3.0.0,>=2.0.1; python_version < \"3.11\"",
] ]
files = [ files = [
{file = "voicemeeter_api-2.4.10-py3-none-any.whl", hash = "sha256:2f75acb7b472e56b6bd8d4f1141f32d948c55ef9b30d5a08e085a1c8e76e2464"}, {file = "voicemeeter_api-2.4.11-py3-none-any.whl", hash = "sha256:7a1b290d90c851204438c18e2d343e568c242fcd1e664c5b88d4019a553d44e1"},
{file = "voicemeeter_api-2.4.10.tar.gz", hash = "sha256:1d8dfc1e8922179f8b97c90b90b9ed051082018c6af5feb1d48250140a02d40c"}, {file = "voicemeeter_api-2.4.11.tar.gz", hash = "sha256:875591ad326a7a13ef141536cca83953edcda81da256191bab3844bac46a0e70"},
] ]

View File

@ -1,6 +1,6 @@
[project] [project]
name = "nvda_voicemeeter" name = "nvda_voicemeeter"
version = "0.5.0" version = "0.5.2"
description = "A Voicemeeter app compatible with NVDA" description = "A Voicemeeter app compatible with NVDA"
authors = [ authors = [
{ name = "onyx-and-iris", email = "code@onyxandiris.online" }, { name = "onyx-and-iris", email = "code@onyxandiris.online" },
@ -8,7 +8,7 @@ authors = [
dependencies = [ dependencies = [
"pysimplegui>=4.60.5", "pysimplegui>=4.60.5",
"pyparsing>=3.1.1", "pyparsing>=3.1.1",
"voicemeeter-api>=2.4.10", "voicemeeter-api>=2.4.11",
] ]
requires-python = ">=3.10,<3.12" requires-python = ">=3.10,<3.12"
readme = "README.md" readme = "README.md"

View File

@ -197,7 +197,7 @@ class Builder:
"""tab0 row3 represents patch composite""" """tab0 row3 represents patch composite"""
def add_physical_device_opts(layout): def add_physical_device_opts(layout):
outputs = util.get_patch_composite_list(self.vm.kind) outputs = util.get_patch_composite_list(self.kind)
layout.append( layout.append(
[ [
psg.ButtonMenu( psg.ButtonMenu(
@ -206,7 +206,7 @@ class Builder:
menu_def=["", outputs], menu_def=["", outputs],
key=f"PATCH COMPOSITE||PC{i + 1}", key=f"PATCH COMPOSITE||PC{i + 1}",
) )
for i in range(self.kind.phys_out) for i in range(self.kind.composite)
] ]
) )
@ -478,18 +478,26 @@ class Builder:
"""tab3 row represents bus composite toggle""" """tab3 row represents bus composite toggle"""
def add_strip_outputs(layout): def add_strip_outputs(layout):
params = ["MONO", "EQ", "MUTE", "MODE"] params = ["MONO", "EQ", "MUTE"]
if self.vm.kind.name == "basic": if self.kind.name == "basic":
params.remove("EQ") params.remove("EQ")
label = {"MODE": "BUSMODE"} busmodes = [util._bus_mode_map[mode] for mode in util.get_bus_modes(self.vm)]
layout.append( layout.append(
[ [
psg.Button( *[
label.get(param, param.capitalize()), psg.Button(
size=(12 if param == "MODE" else 6, 2), param.capitalize(),
key=f"BUS {i}||{param}", size=(6, 2),
) key=f"BUS {i}||{param}",
for param in params )
for param in params
],
psg.ButtonMenu(
"BUSMODE",
size=(12, 2),
menu_def=["", busmodes],
key=f"BUS {i}||MODE",
),
] ]
) )

View File

@ -111,6 +111,24 @@ def get_channel_identifier_list(vm) -> list:
return identifiers return identifiers
_bus_mode_map = {
"normal": "Normal",
"amix": "Mix Down A",
"bmix": "Mix Down B",
"repeat": "Stereo Repeat",
"composite": "Composite",
"tvmix": "Up Mix TV",
"upmix21": "Up Mix 2.1",
"upmix41": "Up Mix 4.1",
"upmix61": "Up Mix 6.1",
"centeronly": "Center Only",
"lfeonly": "Low Frequency Effect Only",
"rearonly": "Rear Only",
}
_bus_mode_map_reversed = dict((reversed(item) for item in _bus_mode_map.items()))
def get_bus_modes(vm) -> list: def get_bus_modes(vm) -> list:
if vm.kind.name == "basic": if vm.kind.name == "basic":
return [ return [
@ -160,3 +178,17 @@ def get_full_slider_params(i, kind) -> Iterable:
if kind.name == "basic": if kind.name == "basic":
params.remove("LIMIT") params.remove("LIMIT")
return params return params
def get_slider_modes() -> Iterable:
return (
"GAIN MODE",
"BASS MODE",
"MID MODE",
"TREBLE MODE",
"AUDIBILITY MODE",
"COMP MODE",
"GATE MODE",
"DENOISER MODE",
"LIMIT MODE",
)

View File

@ -47,7 +47,7 @@ class NVDAVMWindow(psg.Window):
if self.kind.name == "basic": if self.kind.name == "basic":
self["HARDWARE OUT||A2"].Widget.config(**buttonmenu_opts) self["HARDWARE OUT||A2"].Widget.config(**buttonmenu_opts)
if self.kind.name != "basic": if self.kind.name != "basic":
[self[f"PATCH COMPOSITE||PC{i + 1}"].Widget.config(**buttonmenu_opts) for i in range(self.kind.phys_out)] [self[f"PATCH COMPOSITE||PC{i + 1}"].Widget.config(**buttonmenu_opts) for i in range(self.kind.composite)]
slider_opts = {"takefocus": 1, "highlightthickness": 1} slider_opts = {"takefocus": 1, "highlightthickness": 1}
for i in range(self.kind.num_strip): for i in range(self.kind.num_strip):
for param in util.get_slider_params(i, self.kind): for param in util.get_slider_params(i, self.kind):
@ -57,6 +57,7 @@ class NVDAVMWindow(psg.Window):
self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts) self[f"STRIP {i}||SLIDER LIMIT"].Widget.config(**slider_opts)
for i in range(self.kind.num_bus): for i in range(self.kind.num_bus):
self[f"BUS {i}||SLIDER GAIN"].Widget.config(**slider_opts) self[f"BUS {i}||SLIDER GAIN"].Widget.config(**slider_opts)
self[f"BUS {i}||MODE"].Widget.config(**buttonmenu_opts)
if self.kind.name != "basic": if self.kind.name != "basic":
for i in range(self.kind.phys_out): 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} 0"].Widget.config(state="readonly")
@ -179,17 +180,17 @@ class NVDAVMWindow(psg.Window):
self.bind(f"<Alt-Control-{event}-{direction}>", f"ALT CTRL {direction.upper()}||{event_id}") self.bind(f"<Alt-Control-{event}-{direction}>", f"ALT CTRL {direction.upper()}||{event_id}")
# Hardware In # Hardware In
for i in range(self.vm.kind.phys_in): for i in range(self.kind.phys_in):
self[f"HARDWARE IN||{i + 1}"].bind("<FocusIn>", "||FOCUS IN") self[f"HARDWARE IN||{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
self[f"HARDWARE IN||{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False) self[f"HARDWARE IN||{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
self[f"HARDWARE IN||{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False) self[f"HARDWARE IN||{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
# Hardware Out # Hardware Out
for i in range(self.vm.kind.phys_out): for i in range(self.kind.phys_out):
self[f"HARDWARE OUT||A{i + 1}"].bind("<FocusIn>", "||FOCUS IN") self[f"HARDWARE OUT||A{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
self[f"HARDWARE OUT||A{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False) self[f"HARDWARE OUT||A{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
self[f"HARDWARE OUT||A{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False) self[f"HARDWARE OUT||A{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
if self.vm.kind.name == "basic": if self.kind.name == "basic":
self["HARDWARE OUT||A2"].bind("<FocusIn>", "||FOCUS IN") self["HARDWARE OUT||A2"].bind("<FocusIn>", "||FOCUS IN")
self["HARDWARE OUT||A2"].bind("<space>", "||KEY SPACE", propagate=False) self["HARDWARE OUT||A2"].bind("<space>", "||KEY SPACE", propagate=False)
self["HARDWARE OUT||A2"].bind("<Return>", "||KEY ENTER", propagate=False) self["HARDWARE OUT||A2"].bind("<Return>", "||KEY ENTER", propagate=False)
@ -202,7 +203,7 @@ class NVDAVMWindow(psg.Window):
# Patch Composite # Patch Composite
if self.kind.name != "basic": if self.kind.name != "basic":
for i in range(self.vm.kind.phys_out): for i in range(self.kind.composite):
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<FocusIn>", "||FOCUS IN") self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<FocusIn>", "||FOCUS IN")
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False) self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<space>", "||KEY SPACE", propagate=False)
self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False) self[f"PATCH COMPOSITE||PC{i + 1}"].bind("<Return>", "||KEY ENTER", propagate=False)
@ -260,13 +261,16 @@ class NVDAVMWindow(psg.Window):
self[f"STRIP {i}||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R") self[f"STRIP {i}||SLIDER {param}"].bind("<Control-Shift-KeyPress-R>", "||KEY CTRL SHIFT R")
# Bus Params # Bus Params
params = ["MONO", "EQ", "MUTE", "MODE"] params = ["MONO", "EQ", "MUTE"]
if self.vm.kind.name == "basic": if self.kind.name == "basic":
params.remove("EQ") params.remove("EQ")
for i in range(self.kind.num_bus): for i in range(self.kind.num_bus):
for param in params: for param in params:
self[f"BUS {i}||{param}"].bind("<FocusIn>", "||FOCUS IN") self[f"BUS {i}||{param}"].bind("<FocusIn>", "||FOCUS IN")
self[f"BUS {i}||{param}"].bind("<Return>", "||KEY ENTER") self[f"BUS {i}||{param}"].bind("<Return>", "||KEY ENTER")
self[f"BUS {i}||MODE"].bind("<FocusIn>", "||FOCUS IN")
self[f"BUS {i}||MODE"].bind("<space>", "||KEY SPACE", propagate=False)
self[f"BUS {i}||MODE"].bind("<Return>", "||KEY ENTER", propagate=False)
# Bus Sliders # Bus Sliders
for i in range(self.kind.num_bus): for i in range(self.kind.num_bus):
@ -300,13 +304,15 @@ class NVDAVMWindow(psg.Window):
self.logger.debug(f"values::{values}") self.logger.debug(f"values::{values}")
if event in (psg.WIN_CLOSED, "Exit"): if event in (psg.WIN_CLOSED, "Exit"):
break break
elif event.endswith("MODE"): elif event in util.get_slider_modes():
mode = event mode = event
self.nvda.speak(f"{mode} enabled") self.nvda.speak(f"{mode} enabled")
self.logger.debug(f"entered slider mode {mode}")
continue continue
elif event == "ESCAPE": elif event == "ESCAPE":
if mode: if mode:
self.nvda.speak(f"{mode} disabled") self.nvda.speak(f"{mode} disabled")
self.logger.debug(f"exited from slider mode {mode}")
mode = None mode = None
continue continue
@ -599,14 +605,18 @@ class NVDAVMWindow(psg.Window):
val = values[f"PATCH COMPOSITE||{key}"] val = values[f"PATCH COMPOSITE||{key}"]
index = int(key[-1]) - 1 index = int(key[-1]) - 1
self.vm.patch.composite[index].set(util.get_patch_composite_list(self.kind).index(val) + 1) self.vm.patch.composite[index].set(util.get_patch_composite_list(self.kind).index(val) + 1)
self.TKroot.after(200, self.nvda.speak, f"PATCH COMPOSITE {key[-1]} set {val}") self.TKroot.after(200, self.nvda.speak, val)
case [["PATCH", "COMPOSITE"], [key], ["FOCUS", "IN"]]: case [["PATCH", "COMPOSITE"], [key], ["FOCUS", "IN"]]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
if values[f"PATCH COMPOSITE||{key}"]: if values[f"PATCH COMPOSITE||{key}"]:
val = values[f"PATCH COMPOSITE||{key}"] val = values[f"PATCH COMPOSITE||{key}"]
else: else:
index = int(key[-1]) - 1 index = int(key[-1]) - 1
val = util.get_patch_composite_list(self.kind)[self.vm.patch.composite[index].get() - 1] comp_index = self.vm.patch.composite[index].get()
if self.kind.name == "banana":
if comp_index == 64: # bus channel
comp_index = 0
val = util.get_patch_composite_list(self.kind)[comp_index - 1]
self.nvda.speak(f"Patch COMPOSITE {key[-1]} {val}") self.nvda.speak(f"Patch COMPOSITE {key[-1]} {val}")
case [["PATCH", "COMPOSITE"], [key], ["KEY", "SPACE" | "ENTER"]]: case [["PATCH", "COMPOSITE"], [key], ["KEY", "SPACE" | "ENTER"]]:
util.open_context_menu_for_buttonmenu(self, f"PATCH COMPOSITE||{key}") util.open_context_menu_for_buttonmenu(self, f"PATCH COMPOSITE||{key}")
@ -1002,40 +1012,27 @@ class NVDAVMWindow(psg.Window):
"on" if val else "off", "on" if val else "off",
) )
case "MODE": case "MODE":
bus_modes = util.get_bus_modes(self.vm) chosen = util._bus_mode_map_reversed[values[event]]
next_index = bus_modes.index(val) + 1 setattr(self.vm.bus[int(index)].mode, chosen, True)
if next_index == len(bus_modes): self.cache["bus"][event] = chosen
next_index = 0
next_bus = bus_modes[next_index]
phonetic = {
"amix": "Mix Down A",
"bmix": "Mix Down B",
"repeat": "Stereo Repeat",
"tvmix": "Up Mix TV",
"upmix21": "Up Mix 2.1",
"upmix41": "Up Mix 4.1",
"upmix61": "Up Mix 6.1",
"centeronly": "Center Only",
"lfeonly": "Low Frequency Effect Only",
"rearonly": "Rear Only",
}
setattr(self.vm.bus[int(index)].mode, next_bus, True)
self.cache["bus"][event] = next_bus
self.TKroot.after( self.TKroot.after(
200, 200,
self.nvda.speak, self.nvda.speak,
phonetic.get(next_bus, next_bus), util._bus_mode_map[chosen],
) )
case [["BUS", index], [param], ["FOCUS", "IN"]]: case [["BUS", index], [param], ["FOCUS", "IN"]]:
if self.find_element_with_focus() is not None: if self.find_element_with_focus() is not None:
label = self.cache["labels"][f"BUS {index}||LABEL"] label = self.cache["labels"][f"BUS {index}||LABEL"]
val = self.cache["bus"][f"BUS {index}||{param}"] val = self.cache["bus"][f"BUS {index}||{param}"]
if param == "MODE": if param == "MODE":
self.nvda.speak(f"{label} bus {param} {val}") self.nvda.speak(f"{label} bus {param} {util._bus_mode_map[val]}")
else: else:
self.nvda.speak(f"{label} bus {param} {'on' if val else 'off'}") self.nvda.speak(f"{label} {param} {'on' if val else 'off'}")
case [["BUS", index], [param], ["KEY", "ENTER"]]: case [["BUS", index], [param], ["KEY", "SPACE" | "ENTER"]]:
self.find_element_with_focus().click() if param == "MODE":
util.open_context_menu_for_buttonmenu(self, f"BUS {index}||MODE")
else:
self.find_element_with_focus().click()
# Bus Sliders # Bus Sliders
case [["BUS", index], ["SLIDER", "GAIN"]]: case [["BUS", index], ["SLIDER", "GAIN"]]: