Skip to content

Commit

Permalink
WASAPI Driver: don't crash (or rather hang) when starting client fails
Browse files Browse the repository at this point in the history
We were waiting infinitely for the WASAPI_Client_Started event, but when there was an error while starting the client, that event would never be signaled. So MuseScore would be stuck infinitely on launch.
  • Loading branch information
cbjeukendrup authored and RomanPudashkin committed Jan 12, 2023
1 parent 715509e commit f5af414
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 5 deletions.
12 changes: 10 additions & 2 deletions src/framework/audio/internal/platform/win/wasapiaudioclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Media::Devices;
using namespace winrt::Windows::Devices::Enumeration;

WasapiAudioClient::WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientStoppedEvent)
: m_clientStartedEvent(clientStartedEvent), m_clientStoppedEvent(clientStoppedEvent)
WasapiAudioClient::WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientFailedToStartEvent, HANDLE clientStoppedEvent)
: m_clientStartedEvent(clientStartedEvent), m_clientFailedToStartEvent(clientFailedToStartEvent), m_clientStoppedEvent(
clientStoppedEvent)
{
check_hresult(MFStartup(MF_VERSION, MFSTARTUP_LITE));
}
Expand Down Expand Up @@ -218,6 +219,8 @@ HRESULT WasapiAudioClient::ActivateCompleted(IActivateAudioInterfaceAsyncOperati
m_audioRenderClient = nullptr;
m_sampleReadyAsyncResult = nullptr;

SetEvent(m_clientFailedToStartEvent);

// Must return S_OK even on failure.
return S_OK;
}
Expand Down Expand Up @@ -370,6 +373,8 @@ void WasapiAudioClient::startPlayback() noexcept
} catch (...) {
hresult error = to_hresult();
setStateAndNotify(DeviceState::Error, error);

SetEvent(m_clientFailedToStartEvent);
}
}

Expand Down Expand Up @@ -398,6 +403,9 @@ HRESULT WasapiAudioClient::onStartPlayback(IMFAsyncResult*) noexcept
return S_OK;
} catch (...) {
setStateAndNotify(DeviceState::Error, to_hresult());

SetEvent(m_clientFailedToStartEvent);

// Must return S_OK.
return S_OK;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ namespace winrt {
struct WasapiAudioClient : implements<WasapiAudioClient, IActivateAudioInterfaceCompletionHandler>
{
public:
WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientStoppedEvent);
WasapiAudioClient(HANDLE clientStartedEvent, HANDLE clientFailedToStartEvent, HANDLE clientStoppedEvent);
~WasapiAudioClient();

void setHardWareOffload(bool value);
Expand Down Expand Up @@ -99,6 +99,7 @@ struct WasapiAudioClient : implements<WasapiAudioClient, IActivateAudioInterface
DeviceState m_deviceState = DeviceState::Uninitialized;
SampleRequestCallback m_sampleRequestCallback;
HANDLE m_clientStartedEvent;
HANDLE m_clientFailedToStartEvent;
HANDLE m_clientStoppedEvent;
};
}
Expand Down
15 changes: 13 additions & 2 deletions src/framework/audio/internal/platform/win/wasapiaudiodriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ inline REFERENCE_TIME samplesToRefTime(int numSamples, double sampleRate) noexce

struct WasapiData {
HANDLE clientStartedEvent;
HANDLE clientFailedToStartEvent;
HANDLE clientStoppedEvent;

winrt::com_ptr<winrt::WasapiAudioClient> wasapiClient;
Expand All @@ -54,6 +55,7 @@ static WasapiData s_data;
WasapiAudioDriver::WasapiAudioDriver()
{
s_data.clientStartedEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Started");
s_data.clientFailedToStartEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Failed_To_Start");
s_data.clientStoppedEvent = CreateEvent(NULL, FALSE, FALSE, L"WASAPI_Client_Stopped");

m_devicesListener.startWithCallback([this]() {
Expand All @@ -77,7 +79,8 @@ std::string WasapiAudioDriver::name() const
bool WasapiAudioDriver::open(const Spec& spec, Spec* activeSpec)
{
if (!s_data.wasapiClient.get()) {
s_data.wasapiClient = make_self<WasapiAudioClient>(s_data.clientStartedEvent, s_data.clientStoppedEvent);
s_data.wasapiClient
= make_self<WasapiAudioClient>(s_data.clientStartedEvent, s_data.clientFailedToStartEvent, s_data.clientStoppedEvent);
}

m_desiredSpec = spec;
Expand All @@ -102,7 +105,15 @@ bool WasapiAudioDriver::open(const Spec& spec, Spec* activeSpec)

s_data.wasapiClient->asyncInitializeAudioDevice(deviceId);

WaitForSingleObject(s_data.clientStartedEvent, INFINITE);
static constexpr DWORD handleCount = 2;
const HANDLE handles[handleCount] = { s_data.clientStartedEvent, s_data.clientFailedToStartEvent };

DWORD waitResult = WaitForMultipleObjects(handleCount, handles, false, INFINITE);
if (waitResult != WAIT_OBJECT_0) {
// Either the event was the second event (namely s_data.clientFailedToStartEvent)
// Or some wait error occurred
return false;
}

m_activeSpec = m_desiredSpec;
m_activeSpec.sampleRate = s_data.wasapiClient->sampleRate();
Expand Down

0 comments on commit f5af414

Please sign in to comment.