@@ -202,7 +202,8 @@ class WasapiPlayer {
202202
203203 // Reset our state due to being stopped. This runs on the feeder thread
204204 // rather than on the thread which called stop() because writing to a vector
205- // isn't thread safe.
205+ // isn't thread safe. We also reset the stream here because this can't be done
206+ // in stop() if the feeder thread is currently writing to the buffer.
206207 void completeStop ();
207208
208209 // Convert frames into ms.
@@ -586,22 +587,21 @@ bool WasapiPlayer::didPreferredDeviceBecomeAvailable() {
586587}
587588
588589HRESULT WasapiPlayer::stop () {
589- playState = PlayState::stopping;
590590 HRESULT hr = client->Stop ();
591+ // It's important that we set playState *after*
592+ // calling client->Stop() because otherwise, the feeder thread might see the
593+ // playState change and call client->Reset() before client->Stop() runs,
594+ // causing AUDCLNT_E_NOT_STOPPED.
595+ playState = PlayState::stopping;
591596 // If the device has been invalidated, it has already stopped. Just ignore
592597 // this and behave as if we were successful to avoid a cascade of breakage.
593598 // feed() will attempt to reopen the device next time it is called.
594599 if (
595600 hr != AUDCLNT_E_DEVICE_INVALIDATED
596601 && hr != AUDCLNT_E_NOT_INITIALIZED
602+ && FAILED (hr)
597603 ) {
598- if (FAILED (hr)) {
599- return hr;
600- }
601- hr = client->Reset ();
602- if (FAILED (hr)) {
603- return hr;
604- }
604+ return hr;
605605 }
606606 // If there is a feed/sync call waiting, wake it up so it can immediately
607607 // return to the caller.
@@ -610,6 +610,14 @@ HRESULT WasapiPlayer::stop() {
610610}
611611
612612void WasapiPlayer::completeStop () {
613+ HRESULT hr = client->Reset ();
614+ if (FAILED (hr)) {
615+ // We must not use LOG_ERROR here because that plays a sound and we might be
616+ // in the middle of stopping our sound player.
617+ LOG_DEBUGWARNING (L" Couldn't reset stream: " << hr);
618+ // We deliberately continue here. If Reset failed, the stream is probably
619+ // already cleared or unusable anyway. We should always reset our state.
620+ }
613621 nextFeedId = 0 ;
614622 sentFrames = 0 ;
615623 feedEnds.clear ();
0 commit comments