mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added WASAPI device change detection.
This commit is contained in:
parent
754ba64afc
commit
a99b309d3c
1 changed files with 105 additions and 27 deletions
|
|
@ -67,6 +67,10 @@ void logFailure (HRESULT hr)
|
|||
case AUDCLNT_E_EVENTHANDLE_NOT_SET: m = "AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
|
||||
case AUDCLNT_E_INCORRECT_BUFFER_SIZE: m = "AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
|
||||
case AUDCLNT_E_BUFFER_SIZE_ERROR: m = "AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
|
||||
case AUDCLNT_E_CPUUSAGE_EXCEEDED: m = "AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
|
||||
case AUDCLNT_E_BUFFER_ERROR: m = "AUDCLNT_E_BUFFER_ERROR"; break;
|
||||
case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: m = "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
|
||||
case AUDCLNT_E_INVALID_DEVICE_PERIOD: m = "AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
|
||||
case AUDCLNT_S_BUFFER_EMPTY: m = "AUDCLNT_S_BUFFER_EMPTY"; break;
|
||||
case AUDCLNT_S_THREAD_ALREADY_REGISTERED: m = "AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
|
||||
default: break;
|
||||
|
|
@ -124,7 +128,7 @@ namespace
|
|||
eCommunications = (eMultimedia + 1)
|
||||
};
|
||||
|
||||
struct IMMNotificationClient : public IUnknown
|
||||
struct __declspec (uuid ("7991EEC9-7E89-4D85-8390-6C703CEC60C0")) IMMNotificationClient : public IUnknown
|
||||
{
|
||||
virtual HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR, DWORD) = 0;
|
||||
virtual HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR) = 0;
|
||||
|
|
@ -184,8 +188,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* const src) n
|
|||
class WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIDeviceBase (const ComSmartPtr <IMMDevice>& device_, const bool useExclusiveMode_)
|
||||
: device (device_),
|
||||
WASAPIDeviceBase (const ComSmartPtr <IMMDevice>& d, const bool exclusiveMode)
|
||||
: device (d),
|
||||
sampleRate (0),
|
||||
defaultSampleRate (0),
|
||||
numChannels (0),
|
||||
|
|
@ -193,7 +197,7 @@ public:
|
|||
minBufferSize (0),
|
||||
defaultBufferSize (0),
|
||||
latencySamples (0),
|
||||
useExclusiveMode (useExclusiveMode_),
|
||||
useExclusiveMode (exclusiveMode),
|
||||
sampleRateHasChanged (false)
|
||||
{
|
||||
clientEvent = CreateEvent (0, false, false, _T("JuceWASAPI"));
|
||||
|
|
@ -221,14 +225,14 @@ public:
|
|||
|
||||
rates.addUsingDefaultSort (defaultSampleRate);
|
||||
|
||||
static const double ratesToTest[] = { 44100.0, 48000.0, 88200.0, 96000.0 };
|
||||
static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (ratesToTest); ++i)
|
||||
{
|
||||
if (ratesToTest[i] == defaultSampleRate)
|
||||
continue;
|
||||
|
||||
format.Format.nSamplesPerSec = (DWORD) roundDoubleToInt (ratesToTest[i]);
|
||||
format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
|
||||
|
||||
if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
|
||||
(WAVEFORMATEX*) &format, 0)))
|
||||
|
|
@ -375,7 +379,8 @@ private:
|
|||
|
||||
if (device != nullptr)
|
||||
{
|
||||
HRESULT hr = device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER, 0, (void**) client.resetAndGetPointerAddress());
|
||||
HRESULT hr = device->Activate (__uuidof (IAudioClient), CLSCTX_INPROC_SERVER,
|
||||
nullptr, (void**) client.resetAndGetPointerAddress());
|
||||
logFailure (hr);
|
||||
}
|
||||
|
||||
|
|
@ -456,8 +461,8 @@ private:
|
|||
class WASAPIInputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIInputDevice (const ComSmartPtr <IMMDevice>& device_, const bool useExclusiveMode_)
|
||||
: WASAPIDeviceBase (device_, useExclusiveMode_),
|
||||
WASAPIInputDevice (const ComSmartPtr <IMMDevice>& d, const bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode),
|
||||
reservoir (1, 1)
|
||||
{
|
||||
}
|
||||
|
|
@ -574,8 +579,8 @@ private:
|
|||
class WASAPIOutputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIOutputDevice (const ComSmartPtr <IMMDevice>& device_, const bool useExclusiveMode_)
|
||||
: WASAPIDeviceBase (device_, useExclusiveMode_)
|
||||
WASAPIOutputDevice (const ComSmartPtr <IMMDevice>& d, const bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -659,18 +664,19 @@ private:
|
|||
|
||||
//==============================================================================
|
||||
class WASAPIAudioIODevice : public AudioIODevice,
|
||||
public Thread
|
||||
public Thread,
|
||||
private AsyncUpdater
|
||||
{
|
||||
public:
|
||||
WASAPIAudioIODevice (const String& deviceName,
|
||||
const String& outputDeviceId_,
|
||||
const String& inputDeviceId_,
|
||||
const bool useExclusiveMode_)
|
||||
const bool exclusiveMode)
|
||||
: AudioIODevice (deviceName, "Windows Audio"),
|
||||
Thread ("Juce WASAPI"),
|
||||
outputDeviceId (outputDeviceId_),
|
||||
inputDeviceId (inputDeviceId_),
|
||||
useExclusiveMode (useExclusiveMode_),
|
||||
useExclusiveMode (exclusiveMode),
|
||||
isOpen_ (false),
|
||||
isStarted (false),
|
||||
currentBufferSizeSamples (0),
|
||||
|
|
@ -780,8 +786,10 @@ public:
|
|||
return lastError;
|
||||
}
|
||||
|
||||
currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize);
|
||||
currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate;
|
||||
currentBufferSizeSamples = bufferSizeSamples <= 0 ? defaultBufferSize : jmax (bufferSizeSamples, minBufferSize);
|
||||
currentSampleRate = sampleRate > 0 ? sampleRate : defaultSampleRate;
|
||||
lastKnownInputChannels = inputChannels;
|
||||
lastKnownOutputChannels = outputChannels;
|
||||
|
||||
if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels))
|
||||
{
|
||||
|
|
@ -904,10 +912,10 @@ public:
|
|||
{
|
||||
setMMThreadPriority();
|
||||
|
||||
const int bufferSize = currentBufferSizeSamples;
|
||||
const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
|
||||
const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
|
||||
bool sampleRateChanged = false;
|
||||
const int bufferSize = currentBufferSizeSamples;
|
||||
const int numInputBuffers = getActiveInputChannels().countNumberOfSetBits();
|
||||
const int numOutputBuffers = getActiveOutputChannels().countNumberOfSetBits();
|
||||
bool sampleRateChanged = false;
|
||||
|
||||
AudioSampleBuffer ins (jmax (1, numInputBuffers), bufferSize + 32);
|
||||
AudioSampleBuffer outs (jmax (1, numOutputBuffers), bufferSize + 32);
|
||||
|
|
@ -925,7 +933,10 @@ public:
|
|||
break;
|
||||
|
||||
if (inputDevice->sampleRateHasChanged)
|
||||
{
|
||||
sampleRateChanged = true;
|
||||
sampleRateChangedByOutput = false;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -943,13 +954,16 @@ public:
|
|||
outputDevice->copyBuffers (const_cast <const float**> (outputBuffers), numOutputBuffers, bufferSize, *this);
|
||||
|
||||
if (outputDevice->sampleRateHasChanged)
|
||||
{
|
||||
sampleRateChanged = true;
|
||||
sampleRateChangedByOutput = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sampleRateChanged)
|
||||
{
|
||||
// xxx one of the devices has had its sample rate changed externally.. not 100% sure how
|
||||
// to handle this..
|
||||
triggerAsyncUpdate();
|
||||
break; //Quit the thread... will restart it later!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -973,10 +987,13 @@ private:
|
|||
bool isOpen_, isStarted;
|
||||
int currentBufferSizeSamples;
|
||||
double currentSampleRate;
|
||||
bool sampleRateChangedByOutput;
|
||||
|
||||
AudioIODeviceCallback* callback;
|
||||
CriticalSection startStopLock;
|
||||
|
||||
BigInteger lastKnownInputChannels, lastKnownOutputChannels;
|
||||
|
||||
//==============================================================================
|
||||
bool createDevices()
|
||||
{
|
||||
|
|
@ -1014,6 +1031,32 @@ private:
|
|||
&& (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate()
|
||||
{
|
||||
stop();
|
||||
|
||||
outputDevice = nullptr;
|
||||
inputDevice = nullptr;
|
||||
initialise();
|
||||
|
||||
open (lastKnownInputChannels, lastKnownOutputChannels,
|
||||
getChangedSampleRate(), currentBufferSizeSamples);
|
||||
|
||||
start (callback);
|
||||
}
|
||||
|
||||
double getChangedSampleRate() const
|
||||
{
|
||||
if (outputDevice != nullptr && sampleRateChangedByOutput)
|
||||
return outputDevice->defaultSampleRate;
|
||||
|
||||
if (inputDevice != nullptr && ! sampleRateChangedByOutput)
|
||||
return inputDevice->defaultSampleRate;
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODevice)
|
||||
};
|
||||
|
|
@ -1031,6 +1074,12 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
~WASAPIAudioIODeviceType()
|
||||
{
|
||||
if (notifyClient != nullptr)
|
||||
enumerator->UnregisterEndpointNotificationCallback (notifyClient);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void scanForDevices()
|
||||
{
|
||||
|
|
@ -1101,6 +1150,30 @@ public:
|
|||
|
||||
private:
|
||||
bool hasScanned;
|
||||
ComSmartPtr<IMMDeviceEnumerator> enumerator;
|
||||
|
||||
//==============================================================================
|
||||
class ChangeNotificationClient : public ComBaseClassHelper<IMMNotificationClient>
|
||||
{
|
||||
public:
|
||||
ChangeNotificationClient (WASAPIAudioIODeviceType& d)
|
||||
: ComBaseClassHelper <IMMNotificationClient> (0), device (d) {}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved (LPCWSTR) { return notify(); }
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged (LPCWSTR, DWORD) { return notify(); }
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged (EDataFlow, ERole, LPCWSTR) { return notify(); }
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
|
||||
|
||||
private:
|
||||
WASAPIAudioIODeviceType& device;
|
||||
|
||||
HRESULT notify() { device.systemDeviceChanged(); return S_OK; }
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient)
|
||||
};
|
||||
|
||||
ComSmartPtr<ChangeNotificationClient> notifyClient;
|
||||
|
||||
//==============================================================================
|
||||
static String getDefaultEndpoint (IMMDeviceEnumerator* const enumerator, const bool forCapture)
|
||||
|
|
@ -1129,9 +1202,14 @@ private:
|
|||
StringArray& outputDeviceIds,
|
||||
StringArray& inputDeviceIds)
|
||||
{
|
||||
ComSmartPtr <IMMDeviceEnumerator> enumerator;
|
||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
return;
|
||||
if (enumerator == nullptr)
|
||||
{
|
||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
return;
|
||||
|
||||
notifyClient = new ChangeNotificationClient (*this);
|
||||
enumerator->RegisterEndpointNotificationCallback (notifyClient);
|
||||
}
|
||||
|
||||
const String defaultRenderer (getDefaultEndpoint (enumerator, false));
|
||||
const String defaultCapture (getDefaultEndpoint (enumerator, true));
|
||||
|
|
@ -1205,9 +1283,9 @@ private:
|
|||
inputDeviceNames = newInNames;
|
||||
outputDeviceIds = newOutIds;
|
||||
inputDeviceIds = newInIds;
|
||||
|
||||
callDeviceChangeListeners();
|
||||
}
|
||||
|
||||
callDeviceChangeListeners();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue