WinRT: Wait until audio device activation is complete and PrepDevice during OpenAudio
authorEthan Lee <flibitijibibo@flibitijibibo.com>
Tue, 25 Sep 2018 01:45:12 -0400
changeset 1222209e314140a28
parent 12221 e50b720b73e9
child 12223 90162273424f
WinRT: Wait until audio device activation is complete and PrepDevice during OpenAudio
src/audio/wasapi/SDL_wasapi.c
src/audio/wasapi/SDL_wasapi_win32.c
src/audio/wasapi/SDL_wasapi_winrt.cpp
     1.1 --- a/src/audio/wasapi/SDL_wasapi.c	Tue Sep 25 19:41:33 2018 -0700
     1.2 +++ b/src/audio/wasapi/SDL_wasapi.c	Tue Sep 25 01:45:12 2018 -0400
     1.3 @@ -725,6 +725,12 @@
     1.4      WASAPI_PlatformThreadDeinit(this);
     1.5  }
     1.6  
     1.7 +void
     1.8 +WASAPI_BeginLoopIteration(_THIS)
     1.9 +{
    1.10 +	/* no-op. */
    1.11 +}
    1.12 +
    1.13  static void
    1.14  WASAPI_Deinitialize(void)
    1.15  {
     2.1 --- a/src/audio/wasapi/SDL_wasapi_win32.c	Tue Sep 25 19:41:33 2018 -0700
     2.2 +++ b/src/audio/wasapi/SDL_wasapi_win32.c	Tue Sep 25 01:45:12 2018 -0400
     2.3 @@ -405,12 +405,6 @@
     2.4      SDL_assert(!"This function should have only been called on WinRT.");
     2.5  }
     2.6  
     2.7 -void
     2.8 -WASAPI_BeginLoopIteration(_THIS)
     2.9 -{
    2.10 -    /* no-op. */
    2.11 -}
    2.12 -
    2.13  #endif  /* SDL_AUDIO_DRIVER_WASAPI && !defined(__WINRT__) */
    2.14  
    2.15  /* vi: set ts=4 sw=4 expandtab: */
     3.1 --- a/src/audio/wasapi/SDL_wasapi_winrt.cpp	Tue Sep 25 19:41:33 2018 -0700
     3.2 +++ b/src/audio/wasapi/SDL_wasapi_winrt.cpp	Tue Sep 25 01:45:12 2018 -0400
     3.3 @@ -185,20 +185,9 @@
     3.4  HRESULT
     3.5  SDL_WasapiActivationHandler::ActivateCompleted(IActivateAudioInterfaceAsyncOperation *async)
     3.6  {
     3.7 -    HRESULT result = S_OK;
     3.8 -    IUnknown *iunknown = nullptr;
     3.9 -    const HRESULT ret = async->GetActivateResult(&result, &iunknown);
    3.10 -
    3.11 -    if (SUCCEEDED(ret) && SUCCEEDED(result)) {
    3.12 -        iunknown->QueryInterface(IID_PPV_ARGS(&device->hidden->client));
    3.13 -        if (device->hidden->client) {
    3.14 -            // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
    3.15 -            SDL_AtomicSet(&device->hidden->just_activated, 1);
    3.16 -        }
    3.17 -    }
    3.18 -
    3.19 +    // Just set a flag, since we're probably in a different thread. We'll pick it up and init everything on our own thread to prevent races.
    3.20 +    SDL_AtomicSet(&device->hidden->just_activated, 1);
    3.21      WASAPI_UnrefDevice(device);
    3.22 -
    3.23      return S_OK;
    3.24  }
    3.25  
    3.26 @@ -236,30 +225,50 @@
    3.27      IActivateAudioInterfaceAsyncOperation *async = nullptr;
    3.28      const HRESULT ret = ActivateAudioInterfaceAsync(devid, __uuidof(IAudioClient), nullptr, handler.Get(), &async);
    3.29  
    3.30 -    if (async != nullptr) {
    3.31 -        async->Release();
    3.32 -    }
    3.33 -
    3.34 -    if (FAILED(ret)) {
    3.35 +    if (FAILED(ret) || async == nullptr) {
    3.36 +        if (async != nullptr) {
    3.37 +            async->Release();
    3.38 +        }
    3.39          handler.Get()->Release();
    3.40          WASAPI_UnrefDevice(_this);
    3.41          return WIN_SetErrorFromHRESULT("WASAPI can't activate requested audio endpoint", ret);
    3.42      }
    3.43  
    3.44 +    /* Spin until the async operation is complete.
    3.45 +     * If we don't PrepDevice before leaving this function, the bug list gets LONG:
    3.46 +     * - device.spec is not filled with the correct information
    3.47 +     * - The 'obtained' spec will be wrong for ALLOW_CHANGE properties
    3.48 +     * - SDL_AudioStreams will/will not be allocated at the right time
    3.49 +     * - SDL_assert(device->callbackspec.size == device->spec.size) will fail
    3.50 +     * - When the assert is ignored, skipping or a buffer overflow will occur
    3.51 +     */
    3.52 +    while (!SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
    3.53 +        SDL_Delay(1);
    3.54 +    }
    3.55 +
    3.56 +    HRESULT activateRes = S_OK;
    3.57 +    IUnknown *iunknown = nullptr;
    3.58 +    const HRESULT getActivateRes = async->GetActivateResult(&activateRes, &iunknown);
    3.59 +    async->Release();
    3.60 +    if (FAILED(getActivateRes)) {
    3.61 +        return WIN_SetErrorFromHRESULT("Failed to get WASAPI activate result", getActivateRes);
    3.62 +    } else if (FAILED(activateRes)) {
    3.63 +        return WIN_SetErrorFromHRESULT("Failed to activate WASAPI device", activateRes);
    3.64 +    }
    3.65 +
    3.66 +    iunknown->QueryInterface(IID_PPV_ARGS(&_this->hidden->client));
    3.67 +    if (!_this->hidden->client) {
    3.68 +        return SDL_SetError("Failed to query WASAPI client interface");
    3.69 +    }
    3.70 +
    3.71 +    if (WASAPI_PrepDevice(_this, isrecovery) == -1) {
    3.72 +        return -1;
    3.73 +    }
    3.74 +
    3.75      return 0;
    3.76  }
    3.77  
    3.78  void
    3.79 -WASAPI_BeginLoopIteration(_THIS)
    3.80 -{
    3.81 -    if (SDL_AtomicCAS(&_this->hidden->just_activated, 1, 0)) {
    3.82 -        if (WASAPI_PrepDevice(_this, SDL_TRUE) == -1) {
    3.83 -            SDL_OpenedAudioDeviceDisconnected(_this);
    3.84 -        } 
    3.85 -    }
    3.86 -}
    3.87 -
    3.88 -void
    3.89  WASAPI_PlatformThreadInit(_THIS)
    3.90  {
    3.91      // !!! FIXME: set this thread to "Pro Audio" priority.