Skip to content

Commit 2bcbc8b

Browse files
authored
Merge d6bfdb8 into bcc7f25
2 parents bcc7f25 + d6bfdb8 commit 2bcbc8b

5 files changed

Lines changed: 61 additions & 0 deletions

File tree

nvdaHelper/local/wasapi.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,16 @@ HRESULT WasapiPlayer::resume() {
513513
HRESULT WasapiPlayer::setSessionVolume(float level) {
514514
CComPtr<ISimpleAudioVolume> volume;
515515
HRESULT hr = client->GetService(IID_ISimpleAudioVolume, (void**)&volume);
516+
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
517+
// If we're using a specific device, it's just been invalidated. Fall back
518+
// to the default device.
519+
deviceId.clear();
520+
hr = open(true);
521+
if (FAILED(hr)) {
522+
return hr;
523+
}
524+
hr = client->GetService(IID_ISimpleAudioVolume, (void**)&volume);
525+
}
516526
if (FAILED(hr)) {
517527
return hr;
518528
}

source/config/configSpec.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
[audio]
5959
audioDuckingMode = integer(default=0)
6060
wasapi = boolean(default=true)
61+
soundVolumeFollowsVoice = boolean(default=false)
6162
6263
# Braille settings
6364
[braille]

source/gui/settingsDialogs.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3062,11 +3062,26 @@ def __init__(self, parent):
30623062
wx.CheckBox(audioBox, label=label)
30633063
)
30643064
self.bindHelpEvent("WASAPI", self.wasapiCheckBox)
3065+
self.wasapiCheckBox.Bind(wx.EVT_CHECKBOX, self.onWasapiChange)
30653066
self.wasapiCheckBox.SetValue(
30663067
config.conf["audio"]["wasapi"]
30673068
)
30683069
self.wasapiCheckBox.defaultValue = self._getDefaultValue(
30693070
["audio", "wasapi"])
3071+
# Translators: This is the label for a checkbox control in the
3072+
# Advanced settings panel.
3073+
label = _("Volume of NVDA sounds follows voice volume (requires WASAPI)")
3074+
self.soundVolFollowCheckBox: wx.CheckBox = audioGroup.addItem(
3075+
wx.CheckBox(audioBox, label=label)
3076+
)
3077+
self.bindHelpEvent("SoundVolumeFollowsVoice", self.soundVolFollowCheckBox)
3078+
self.soundVolFollowCheckBox.SetValue(
3079+
config.conf["audio"]["soundVolumeFollowsVoice"]
3080+
)
3081+
self.soundVolFollowCheckBox.defaultValue = self._getDefaultValue(
3082+
["audio", "soundVolumeFollowsVoice"])
3083+
if not self.wasapiCheckBox.GetValue():
3084+
self.soundVolFollowCheckBox.Disable()
30703085

30713086
# Translators: This is the label for a group of advanced options in the
30723087
# Advanced settings panel
@@ -3129,6 +3144,9 @@ def onOpenScratchpadDir(self,evt):
31293144
path=config.getScratchpadDir(ensureExists=True)
31303145
os.startfile(path)
31313146

3147+
def onWasapiChange(self, evt):
3148+
self.soundVolFollowCheckBox.Enable(evt.IsChecked())
3149+
31323150
def _getDefaultValue(self, configPath):
31333151
return config.conf.getConfigValidation(configPath).default
31343152

@@ -3156,6 +3174,7 @@ def haveConfigDefaultsBeenRestored(self):
31563174
and self.caretMoveTimeoutSpinControl.GetValue() == self.caretMoveTimeoutSpinControl.defaultValue
31573175
and self.reportTransparentColorCheckBox.GetValue() == self.reportTransparentColorCheckBox.defaultValue
31583176
and self.wasapiCheckBox.GetValue() == self.wasapiCheckBox.defaultValue
3177+
and self.soundVolFollowCheckBox.GetValue() == self.soundVolFollowCheckBox.defaultValue
31593178
and set(self.logCategoriesList.CheckedItems) == set(self.logCategoriesList.defaultCheckedItems)
31603179
and self.playErrorSoundCombo.GetSelection() == self.playErrorSoundCombo.defaultValue
31613180
and True # reduce noise in diff when the list is extended.
@@ -3182,6 +3201,7 @@ def restoreToDefaults(self):
31823201
self.caretMoveTimeoutSpinControl.SetValue(self.caretMoveTimeoutSpinControl.defaultValue)
31833202
self.reportTransparentColorCheckBox.SetValue(self.reportTransparentColorCheckBox.defaultValue)
31843203
self.wasapiCheckBox.SetValue(self.wasapiCheckBox.defaultValue)
3204+
self.soundVolFollowCheckBox.SetValue(self.soundVolFollowCheckBox.defaultValue)
31853205
self.logCategoriesList.CheckedItems = self.logCategoriesList.defaultCheckedItems
31863206
self.playErrorSoundCombo.SetSelection(self.playErrorSoundCombo.defaultValue)
31873207
self._defaultsRestored = True
@@ -3213,6 +3233,7 @@ def onSave(self):
32133233
self.reportTransparentColorCheckBox.IsChecked()
32143234
)
32153235
config.conf["audio"]["wasapi"] = self.wasapiCheckBox.IsChecked()
3236+
config.conf["audio"]["soundVolumeFollowsVoice"] = self.soundVolFollowCheckBox.IsChecked()
32163237
config.conf["annotations"]["reportDetails"] = self.annotationsDetailsCheckBox.IsChecked()
32173238
config.conf["annotations"]["reportAriaDescription"] = self.ariaDescCheckBox.IsChecked()
32183239
config.conf["braille"]["enableHidBrailleSupport"] = self.supportHidBrailleCombo.GetSelection()

source/nvwave.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,10 @@ class WasapiWavePlayer(garbageHandler.TrackedObject):
774774
#: This allows us to have a single callback in the class rather than on
775775
#: each instance, which prevents reference cycles.
776776
_instances = weakref.WeakValueDictionary()
777+
#: The previous value of the soundVolumeFollowsVoice setting. This is used to
778+
#: determine when this setting has been disabled when it was previously
779+
#: enabled.
780+
_prevSoundVolFollow: bool = False
777781

778782
def __init__(
779783
self,
@@ -814,6 +818,7 @@ def __init__(
814818
import audioDucking
815819
if audioDucking.isAudioDuckingSupported():
816820
self._audioDucker = audioDucking.AudioDucker()
821+
self._session = session
817822
self._player = NVDAHelper.localLib.wasPlay_create(
818823
self._deviceNameToId(outputDevice),
819824
format,
@@ -857,6 +862,7 @@ def open(self):
857862
WavePlayer.audioDeviceError_static = True
858863
raise
859864
WasapiWavePlayer.audioDeviceError_static = False
865+
self._sessionVolumeFollow()
860866

861867
def close(self):
862868
"""For WASAPI, this just stops playback.
@@ -913,6 +919,7 @@ def stop(self):
913919
self._audioDucker.disable()
914920
NVDAHelper.localLib.wasPlay_stop(self._player)
915921
self._doneCallbacks = {}
922+
self._sessionVolumeFollow()
916923

917924
def pause(self, switch: bool):
918925
"""Pause or unpause playback.
@@ -935,6 +942,21 @@ def setSessionVolume(self, level: float):
935942
"""
936943
NVDAHelper.localLib.wasPlay_setSessionVolume(self._player, c_float(level))
937944

945+
def _sessionVolumeFollow(self):
946+
if self._session is not soundsSession:
947+
return
948+
follow = config.conf["audio"]["soundVolumeFollowsVoice"]
949+
if not follow and WavePlayer._prevSoundVolFollow:
950+
# Following was disabled. Reset the sound volume to maximum.
951+
self.setSessionVolume(1.0)
952+
WavePlayer._prevSoundVolFollow = follow
953+
if not follow:
954+
return
955+
import synthDriverHandler
956+
synth = synthDriverHandler.getSynth()
957+
if synth and synth.isSupported("volume"):
958+
self.setSessionVolume(synth.volume / 100)
959+
938960
@staticmethod
939961
def _getDevices():
940962
rawDevs = BSTR()

user_docs/en/userGuide.t2t

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2324,6 +2324,13 @@ WASAPI is a more modern audio framework which may improve the responsiveness, pe
23242324
This option is enabled by default.
23252325
After changing this option, you will need to restart NVDA for the change to take effect.
23262326

2327+
==== Volume of NVDA sounds follows voice volume ====[SoundVolumeFollowsVoice]
2328+
When this option is enabled, the volume of NVDA sounds and beeps will follow the volume setting of the voice you are using.
2329+
If you decrease the volume of the voice, the volume of sounds will decrease.
2330+
Similarly, if you increase the volume of the voice, the volume of sounds will increase.
2331+
This option only takes effect when "Use WASAPI for audio output" is enabled.
2332+
This option is disabled by default.
2333+
23272334
==== Debug logging categories ====[AdvancedSettingsDebugLoggingCategories]
23282335
The checkboxes in this list allow you to enable specific categories of debug messages in NVDA's log.
23292336
Logging these messages can result in decreased performance and large log files.

0 commit comments

Comments
 (0)