Skip to content

Commit

Permalink
Fix audio output device selection (#17532)
Browse files Browse the repository at this point in the history
Fixes #17530

Summary of the issue:
With the switch to exclusively WASAPI, selecting among more than 2 output devices did not work properly.

Description of user facing changes
Selection of output devices should now be more reliable.

Description of development approach
Fixed up the check for whether the selected device is the default output device. Also removed some unneeded initialisation parameters and constants.

Testing strategy:
Manually tested switching between 3 output devices (plus default), with various devices set as the Windows and NVDA default.

Known issues with pull request:
Some legacy winmm code remains, but this will be removed once SAPI4 support has been fixed.
  • Loading branch information
SaschaCowley authored Dec 18, 2024
1 parent ada7908 commit bcccf14
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 30 deletions.
31 changes: 4 additions & 27 deletions source/nvwave.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
)
from enum import Enum, auto
from ctypes import (
POINTER,
Structure,
c_uint,
byref,
Expand Down Expand Up @@ -76,17 +75,7 @@ class WAVEFORMATEX(Structure):
]


LPWAVEFORMATEX = POINTER(WAVEFORMATEX)


WAVE_FORMAT_PCM = 1
WAVE_MAPPER = -1

CALLBACK_NULL = 0
# CALLBACK_FUNCTION = 0x30000
CALLBACK_EVENT = 0x50000
# waveOutProc = CFUNCTYPE(HANDLE, UINT, DWORD, DWORD, DWORD)
# WOM_DONE = 0x3bd


def _isDebugForNvWave():
Expand Down Expand Up @@ -258,7 +247,7 @@ class WasapiWavePlayer(garbageHandler.TrackedObject):
#: Whether there is a pending stream idle check.
_isIdleCheckPending: bool = False
#: Use the default device, this is the configSpec default value.
DEFAULT_DEVICE_KEY = "default"
DEFAULT_DEVICE_KEY = typing.cast(str, config.conf.getConfigValidation(("speech", "outputDevice")).default)
#: The silence output device, None if not initialized.
_silenceDevice: typing.Optional[str] = None

Expand All @@ -267,21 +256,16 @@ def __init__(
channels: int,
samplesPerSec: int,
bitsPerSample: int,
outputDevice: typing.Union[str, int] = WAVE_MAPPER,
closeWhenIdle: bool = False,
outputDevice: str = DEFAULT_DEVICE_KEY,
wantDucking: bool = True,
buffered: bool = False,
purpose: AudioPurpose = AudioPurpose.SPEECH,
):
"""Constructor.
@param channels: The number of channels of audio; e.g. 2 for stereo, 1 for mono.
@param samplesPerSec: Samples per second (hz).
@param bitsPerSample: The number of bits per sample.
@param outputDevice: The name of the audio output device to use,
WAVE_MAPPER for default.
@param closeWhenIdle: Deprecated; ignored.
@param outputDevice: The name of the audio output device to use, defaults to WasapiWavePlayer.DEFAULT_DEVICE_KEY
@param wantDucking: if true then background audio will be ducked on Windows 8 and higher
@param buffered: Whether to buffer small chunks of audio to prevent audio glitches.
@param purpose: The purpose of this audio.
@note: If C{outputDevice} is a name and no such device exists, the default device will be used.
@raise WindowsError: If there was an error opening the audio output device.
Expand All @@ -303,7 +287,7 @@ def __init__(
if audioDucking.isAudioDuckingSupported():
self._audioDucker = audioDucking.AudioDucker()
self._purpose = purpose
if self._isDefaultDevice(outputDevice):
if outputDevice == self.DEFAULT_DEVICE_KEY:
outputDevice = ""
self._player = NVDAHelper.localLib.wasPlay_create(
outputDevice,
Expand Down Expand Up @@ -557,13 +541,6 @@ def _idleCheck(cls):
# Schedule another check here in case feed isn't called for a while.
cls._scheduleIdleCheck()

@classmethod
def _isDefaultDevice(cls, name):
if name in (WAVE_MAPPER, cls.DEFAULT_DEVICE_KEY):
return True
# Check if this is the WinMM sound mapper device, which means default.
return name == next(_getOutputDevices())[1]


WavePlayer = WasapiWavePlayer
fileWavePlayer: Optional[WavePlayer] = None
Expand Down
1 change: 0 additions & 1 deletion source/synthDrivers/_espeak.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,6 @@ def initialize(indexCallback=None):
samplesPerSec=sampleRate,
bitsPerSample=16,
outputDevice=config.conf["speech"]["outputDevice"],
buffered=True,
)
onIndexReached = indexCallback
espeakDLL.espeak_SetSynthCallback(callback)
Expand Down
6 changes: 4 additions & 2 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,11 +141,13 @@ As the NVDA update check URL is now configurable directly within NVDA, no replac
* `SymphonyDocument.script_toggleTextAttribute` to `SymphonyDocument.script_changeTextFormatting`
* The `space` keyword argument for `brailleDisplayDrivers.seikantk.InputGesture` now expects an `int` rather than a `bool`. (#17047, @school510587)
* The `[upgrade]` configuration section including `[upgrade][newLaptopKeyboardLayout]` has been removed. (#17191)
* Due to the retirement of NVDA's winmm support:
* The following symbols have been removed from `nvwave`: `HWAVEOUT`, `LPHWAVEOUT`, `LPWAVEHDR`, `MAXPNAMELEN`, `MMSYSERR_NOERROR`, `usingWasapiWavePlayer`, `WAVEHDR`, `WAVEOUTCAPS`, `WHDR_DONE`, `WinmmWavePlayer`, and `winmm`.
* Due to the retirement of NVDA's winmm support (#17496, #17532):
* The following symbols have been removed from `nvwave`: `CALLBACK_EVENT`, `CALLBACK_FUNCTION`, `CALLBACK_NULL`, `HWAVEOUT`, `LPHWAVEOUT`, `LPWAVEFORMATEX`, `LPWAVEHDR`, `MAXPNAMELEN`, `MMSYSERR_NOERROR`, `usingWasapiWavePlayer`, `WAVEHDR`, `WAVEOUTCAPS`, `waveOutProc`, `WAVE_MAPPER`, `WHDR_DONE`, `WinmmWavePlayer`, and `winmm`.
* `gui.settingsDialogs.AdvancedPanelControls.wasapiComboBox` has been removed.
* The `WASAPI` key has been removed from the `audio` section of the config spec.
* The output from `nvwave.outputDeviceNameToID`, and input to `nvwave.outputDeviceIDToName` are now string identifiers.
* The `outputDevice` parameter to `WasapiWavePlayer.__init__` should now only be passed string arguments.
* The deprecated `closeWhenIdle` and `buffered` parameters to `WasapiWavePlayer.__init__` have been removed.
* In `NVDAObjects.window.scintilla.ScintillaTextInfo`, if no text is selected, the `collapse` method is overriden to expand to line if the `end` parameter is set to `True` (#17431, @nvdaes)
* The following symbols have been removed with no replacement: `languageHandler.getLanguageCliArgs`, `__main__.quitGroup` and `__main__.installGroup` . (#17486, @CyrilleB79)
* Prefix matching on command line flags, e.g. using `--di` for `--disable-addons` is no longer supported. (#11644, @CyrilleB79)
Expand Down

0 comments on commit bcccf14

Please sign in to comment.