nvWave: Keep audio device open (PR #11531)
Fixes #5172
Fixes #10721
Fixes #11482
Fixes #11490
### History
#5172
Some audio drivers/hardware take a long time to open the device and/or truncate the start/end of the audio.
#10721
Calling WaveOutOpen in the OneCore synth callback mysteriously blocks (and thus lags) for ~100 ms. Because we close the audio device on idle, we can trigger this problem. Although PR #11023 mostly fixed this, it's impossible (or at least very difficult) to resolve this completely from within the OneCore driver.
PR #11024 attempted to fix these issues by waiting 10 seconds before closing the audio device"
### Problem to solve
Subsequent to #11024, there are occasional exceptions from nvwave, particularly when switching synthesisers. In particular the following cases need to be handled smoothly:
- When using Microsoft Sound Mapper, NVDA should use the Windows default device (even if it changes)
- When the NVDA configured devices becomes invalid, nvWave should fall back to Microsoft Sound Mapper
- When the NVDA configured device becomes available again, NVDA should switch back to using it.
- Handle no audio device at all.
Since these issues needed to be fixed, and also:
- Closing and opening the audio device was originally introduced to support Remote Desktop audio, this is now better served by other solutions.
- Performance is improved by keeping it open, using a timeout means that the lag is more rare, but will still occur.
### How it is solved
Instead, now don't close the device at all. As per the discussion:
https://github.com/nvaccess/nvda/pull/11505#issuecomment-674879807
To fix issues with current / preferred device:
- nvWave now saves the preferred device when it is constructed.
- If using a device fails, it is considered unavailable, nvWave attempts to fall back to "Microsoft Sound Mapper".
- From my testing, using "Microsoft Sound Mapper" correctly handled changing the default device (Win 10 2004). It would be helpful if others could confirm, especially on different OS versions.
- During `_idleUnbuffered`
- The current device is checked to see if it matches the preferred device.
- The available devices are polled to see if the preferred device is available, if so it switches.