diff --git a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index c461847b32..76ed55b715 100644 --- a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -352,8 +352,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcep class WASAPIDeviceBase { public: - WASAPIDeviceBase (const ComSmartPtr& d, bool exclusiveMode, std::function&& cb) - : device (d), useExclusiveMode (exclusiveMode), reopenCallback (cb) + WASAPIDeviceBase (const ComSmartPtr& d, bool exclusiveMode) + : device (d), useExclusiveMode (exclusiveMode) { clientEvent = CreateEvent (nullptr, false, false, nullptr); @@ -429,7 +429,8 @@ public: && tryInitialisingWithBufferSize (bufferSizeSamples)) { sampleRateHasChanged = false; - shouldClose = false; + shouldShutdown = false; + channelMaps.clear(); for (int i = 0; i <= channels.getHighestBit(); ++i) @@ -468,9 +469,19 @@ public: sampleRateHasChanged = true; } - void deviceBecameInactive() + void deviceSessionBecameInactive() { - shouldClose = true; + isActive = false; + } + + void deviceSessionExpired() + { + shouldShutdown = true; + } + + void deviceSessionBecameActive() + { + isActive = true; } //============================================================================== @@ -487,8 +498,7 @@ public: Array channelMaps; UINT32 actualBufferSize = 0; int bytesPerSample = 0, bytesPerFrame = 0; - bool sampleRateHasChanged = false, shouldClose = false; - std::function reopenCallback; + std::atomic sampleRateHasChanged { false }, shouldShutdown { false }, isActive { true }; virtual void updateFormat (bool isFloat) = 0; @@ -504,13 +514,20 @@ private: JUCE_COMRESULT OnChannelVolumeChanged (DWORD, float*, DWORD, LPCGUID) { return S_OK; } JUCE_COMRESULT OnGroupingParamChanged (LPCGUID, LPCGUID) { return S_OK; } - JUCE_COMRESULT OnStateChanged(AudioSessionState state) + JUCE_COMRESULT OnStateChanged (AudioSessionState state) { - if (state == AudioSessionStateActive) - owner.reopenCallback(); - - if (state == AudioSessionStateInactive || state == AudioSessionStateExpired) - owner.deviceBecameInactive(); + switch (state) + { + case AudioSessionStateInactive: + owner.deviceSessionBecameInactive(); + break; + case AudioSessionStateExpired: + owner.deviceSessionExpired(); + break; + case AudioSessionStateActive: + owner.deviceSessionBecameActive(); + break; + } return S_OK; } @@ -692,8 +709,8 @@ private: class WASAPIInputDevice : public WASAPIDeviceBase { public: - WASAPIInputDevice (const ComSmartPtr& d, bool exclusiveMode, std::function&& reopenCallback) - : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) + WASAPIInputDevice (const ComSmartPtr& d, bool exclusiveMode) + : WASAPIDeviceBase (d, exclusiveMode) { } @@ -746,6 +763,8 @@ public: return false; purgeInputBuffers(); + isActive = true; + return true; } @@ -853,8 +872,8 @@ private: class WASAPIOutputDevice : public WASAPIDeviceBase { public: - WASAPIOutputDevice (const ComSmartPtr& d, bool exclusiveMode, std::function&& reopenCallback) - : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) + WASAPIOutputDevice (const ComSmartPtr& d, bool exclusiveMode) + : WASAPIDeviceBase (d, exclusiveMode) { } @@ -899,7 +918,12 @@ public: if (check (renderClient->GetBuffer (samplesToDo, &outputData))) renderClient->ReleaseBuffer (samplesToDo, AUDCLNT_BUFFERFLAGS_SILENT); - return check (client->Start()); + if (! check (client->Start())) + return false; + + isActive = true; + + return true; } int getNumSamplesAvailableToCopy() const @@ -1124,7 +1148,8 @@ public: if (inputDevice != nullptr) ResetEvent (inputDevice->clientEvent); if (outputDevice != nullptr) ResetEvent (outputDevice->clientEvent); - deviceBecameInactive = false; + shouldShutdown = false; + deviceSampleRateChanged = false; startThread (8); Thread::sleep (5); @@ -1233,7 +1258,6 @@ public: auto bufferSize = currentBufferSizeSamples; auto numInputBuffers = getActiveInputChannels().countNumberOfSetBits(); auto numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits(); - bool sampleRateHasChanged = false; AudioBuffer ins (jmax (1, numInputBuffers), bufferSize + 32); AudioBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32); @@ -1244,13 +1268,22 @@ public: while (! threadShouldExit()) { - if ((outputDevice != nullptr && outputDevice->shouldClose) - || (inputDevice != nullptr && inputDevice->shouldClose)) + if ((outputDevice != nullptr && outputDevice->shouldShutdown) + || (inputDevice != nullptr && inputDevice->shouldShutdown)) { - deviceBecameInactive = true; + shouldShutdown = true; + triggerAsyncUpdate(); + + break; } - if (inputDevice != nullptr && ! deviceBecameInactive) + auto inputDeviceActive = (inputDevice != nullptr && inputDevice->isActive); + auto outputDeviceActive = (outputDevice != nullptr && outputDevice->isActive); + + if (! inputDeviceActive && ! outputDeviceActive) + continue; + + if (inputDeviceActive) { if (outputDevice == nullptr) { @@ -1272,12 +1305,13 @@ public: if (inputDevice->sampleRateHasChanged) { - sampleRateHasChanged = true; - sampleRateChangedByOutput = false; + deviceSampleRateChanged = true; + triggerAsyncUpdate(); + + break; } } - if (! deviceBecameInactive) { const ScopedTryLock sl (startStopLock); @@ -1288,7 +1322,7 @@ public: outs.clear(); } - if (outputDevice != nullptr && ! deviceBecameInactive) + if (outputDeviceActive) { // Note that this function is handed the input device so it can check for the event and make sure // the input reservoir is filled up correctly even when bufferSize > device actualBufferSize @@ -1296,15 +1330,11 @@ public: if (outputDevice->sampleRateHasChanged) { - sampleRateHasChanged = true; - sampleRateChangedByOutput = true; - } - } + deviceSampleRateChanged = true; + triggerAsyncUpdate(); - if (sampleRateHasChanged || deviceBecameInactive) - { - triggerAsyncUpdate(); - break; // Quit the thread... will restart it later! + break; + } } } } @@ -1332,7 +1362,7 @@ private: AudioIODeviceCallback* callback = {}; CriticalSection startStopLock; - bool sampleRateChangedByOutput = false, deviceBecameInactive = false; + std::atomic shouldShutdown { false }, deviceSampleRateChanged { false }; BigInteger lastKnownInputChannels, lastKnownOutputChannels; @@ -1368,60 +1398,54 @@ private: auto flow = getDataFlow (device); - auto deviceReopenCallback = [this] - { - if (deviceBecameInactive) - { - MessageManager::callAsync ([this] - { - close(); - reopenDevices(); - }); - } - }; - if (deviceId == inputDeviceId && flow == eCapture) - inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode, deviceReopenCallback)); + inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode)); else if (deviceId == outputDeviceId && flow == eRender) - outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode, deviceReopenCallback)); + outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode)); } return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk())) && (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk())); } - void reopenDevices() - { - outputDevice = nullptr; - inputDevice = nullptr; - - initialise(); - - open (lastKnownInputChannels, lastKnownOutputChannels, - getChangedSampleRate(), currentBufferSizeSamples); - - start (callback); - } - //============================================================================== void handleAsyncUpdate() override { - stop(); + auto closeDevices = [this] + { + close(); - // sample rate change - if (! deviceBecameInactive) - reopenDevices(); - } + outputDevice = nullptr; + inputDevice = nullptr; + }; - double getChangedSampleRate() const - { - if (outputDevice != nullptr && sampleRateChangedByOutput) - return outputDevice->defaultSampleRate; + if (shouldShutdown) + { + closeDevices(); + } + else if (deviceSampleRateChanged) + { + auto sampleRateChangedByInput = (inputDevice != nullptr && inputDevice->sampleRateHasChanged); - if (inputDevice != nullptr && ! sampleRateChangedByOutput) - return inputDevice->defaultSampleRate; + closeDevices(); + initialise(); - return 0.0; + auto changedSampleRate = [this, sampleRateChangedByInput] () + { + if (inputDevice != nullptr && sampleRateChangedByInput) + return inputDevice->defaultSampleRate; + + if (outputDevice != nullptr && ! sampleRateChangedByInput) + return outputDevice->defaultSampleRate; + + return 0.0; + }(); + + open (lastKnownInputChannels, lastKnownOutputChannels, + changedSampleRate, currentBufferSizeSamples); + + start (callback); + } } //============================================================================== @@ -1542,7 +1566,7 @@ private: HRESULT notify() { if (device != nullptr) - device->triggerAsyncDeviceChangeCallback(); + device->triggerAsyncDeviceChangeCallback(); return S_OK; }