mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-07 04:10:08 +00:00
WASAPI: Suspend processing when AudioSessionStateInactive session event is received and restart on AudioSessionStateActive
This commit is contained in:
parent
9d9d26c85a
commit
fa0f3af034
1 changed files with 101 additions and 77 deletions
|
|
@ -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()
|
||||
{
|
||||
shouldClose = true;
|
||||
isActive = false;
|
||||
}
|
||||
|
||||
void deviceSessionExpired()
|
||||
{
|
||||
shouldShutdown = true;
|
||||
}
|
||||
|
||||
void deviceSessionBecameActive()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue