WasapiPlayer: Call IAudioClient::Reset in completeStop() rather than in stop().#18434
Conversation
…in the middle of stopping our sound player.
|
From a user point of view, the try build with LOG_DEBUGWARNING is working fine. Even when reset fails.
Here is a log fragment showing the reset error: Error 0x88890005 | AUDCLNT_E_NOT_STOPPED |
|
@michaelDCurran I think what is happening is that on one thread, |
|
Nah, just my incompetence. Sorry. I'll fix it. |
|
Please don't beat yourself up. It does no-one any good. |
|
Thread safety is such fun, innit? |
|
I'll push another try build and verify this still fixes the remote problem just in case. If so, we can mark this as ready for review. |
See test results for failed build of commit 8a335bfc0b |
|
@jcsteh or @michaelDCurran have you had a chance to test the latest try build yet? I have been unable to reduce the bug regardless of NVDA version |
|
I tested the latest build and can no longer reproduce the remote problem. I'll mark this as ready for review. |
|
I've been running the latest try build remotely for quite some time, successfully bringing up UAC dialogs remotely with out issue. I think it is good to go. |
Link to issue number:
Fixes #18101
May address #17131, though I'm not sure and I think there's still a thread safety bug in that third party synth driver even if it does.
Summary of the issue:
Sometimes, speech is silent on the local computer when using NVDA Remote Access and a UAC screen appears on the remote computer. This occurs because NVDA tries to play a sound, which calls WavePlayer.stop, which throws an AUDCLNT_E_BUFFER_OPERATION_PENDING exception. That exception isn't caught, so it prevents speech handlers from being registered.
Description of user facing changes:
When using NVDA Remote Access, speech from User Account Control screens on the remote computer now works reliably.
Description of development approach:
AUDCLNT_E_BUFFER_OPERATION_PENDING is being returned from IAudioClient::Reset() called by WasapiPlayer::stop(). Per the documentation, Reset can return AUDCLNT_E_BUFFER_OPERATION_PENDING if "The client is currently writing to or reading from the buffer."
It's indeed slightly possible that the feeder thread is still in WasapiPlayer::feed() when we call WasapiPlayer::stop() on the main thread. This is why stopping is split into two phases: the stop() call itself, generally called on the main thread, and completeStop(), called by the feeder thread when it sees that stop() has been called.
Thus, we fix this problem by moving the IAudioClient::Reset() call from WasapiPlayer::stop() to WasapiPlayer::completeStop(), ensuring that it is called on the feeder thread and therefore not while the buffer is being written to. Audio still stops immediately when WasapiPlayer::stop() is called due to the call to IAudioClient::Stop(), which does not care about whether another thread is writing to the buffer.
We also call IAudioClient::Stop() before setting playState. Otherwise, the feeder thread might see the playState change and call client->Reset() before client->Stop() runs, causing AUDCLNT_E_NOT_STOPPED.
Testing strategy:
Followed steps in #18101. Verified the expected result 30 times.
Known issues with pull request:
None.
Code Review Checklist:
@coderabbitai summary