| @@ -352,8 +352,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcep | |||
| class WASAPIDeviceBase | |||
| { | |||
| public: | |||
| WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& cb) | |||
| : device (d), useExclusiveMode (exclusiveMode), reopenCallback (cb) | |||
| WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& 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() | |||
| { | |||
| isActive = false; | |||
| } | |||
| void deviceSessionExpired() | |||
| { | |||
| shouldShutdown = true; | |||
| } | |||
| void deviceSessionBecameActive() | |||
| { | |||
| shouldClose = true; | |||
| isActive = true; | |||
| } | |||
| //============================================================================== | |||
| @@ -487,8 +498,7 @@ public: | |||
| Array<int> channelMaps; | |||
| UINT32 actualBufferSize = 0; | |||
| int bytesPerSample = 0, bytesPerFrame = 0; | |||
| bool sampleRateHasChanged = false, shouldClose = false; | |||
| std::function<void()> reopenCallback; | |||
| std::atomic<bool> 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<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& reopenCallback) | |||
| : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) | |||
| WASAPIInputDevice (const ComSmartPtr<IMMDevice>& 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<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& reopenCallback) | |||
| : WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback)) | |||
| WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& 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<float> ins (jmax (1, numInputBuffers), bufferSize + 32); | |||
| AudioBuffer<float> 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<bool> 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() | |||
| //============================================================================== | |||
| void handleAsyncUpdate() override | |||
| { | |||
| outputDevice = nullptr; | |||
| inputDevice = nullptr; | |||
| auto closeDevices = [this] | |||
| { | |||
| close(); | |||
| initialise(); | |||
| outputDevice = nullptr; | |||
| inputDevice = nullptr; | |||
| }; | |||
| open (lastKnownInputChannels, lastKnownOutputChannels, | |||
| getChangedSampleRate(), currentBufferSizeSamples); | |||
| if (shouldShutdown) | |||
| { | |||
| closeDevices(); | |||
| } | |||
| else if (deviceSampleRateChanged) | |||
| { | |||
| auto sampleRateChangedByInput = (inputDevice != nullptr && inputDevice->sampleRateHasChanged); | |||
| start (callback); | |||
| } | |||
| closeDevices(); | |||
| initialise(); | |||
| //============================================================================== | |||
| void handleAsyncUpdate() override | |||
| { | |||
| stop(); | |||
| auto changedSampleRate = [this, sampleRateChangedByInput] () | |||
| { | |||
| if (inputDevice != nullptr && sampleRateChangedByInput) | |||
| return inputDevice->defaultSampleRate; | |||
| // sample rate change | |||
| if (! deviceBecameInactive) | |||
| reopenDevices(); | |||
| } | |||
| if (outputDevice != nullptr && ! sampleRateChangedByInput) | |||
| return outputDevice->defaultSampleRate; | |||
| double getChangedSampleRate() const | |||
| { | |||
| if (outputDevice != nullptr && sampleRateChangedByOutput) | |||
| return outputDevice->defaultSampleRate; | |||
| return 0.0; | |||
| }(); | |||
| if (inputDevice != nullptr && ! sampleRateChangedByOutput) | |||
| return inputDevice->defaultSampleRate; | |||
| open (lastKnownInputChannels, lastKnownOutputChannels, | |||
| changedSampleRate, currentBufferSizeSamples); | |||
| return 0.0; | |||
| start (callback); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| @@ -1542,7 +1566,7 @@ private: | |||
| HRESULT notify() | |||
| { | |||
| if (device != nullptr) | |||
| device->triggerAsyncDeviceChangeCallback(); | |||
| device->triggerAsyncDeviceChangeCallback(); | |||
| return S_OK; | |||
| } | |||