Browse Source

Added WASAPI device change detection.

tags/2021-05-28
jules 12 years ago
parent
commit
a99b309d3c
1 changed files with 105 additions and 27 deletions
  1. +105
    -27
      modules/juce_audio_devices/native/juce_win32_WASAPI.cpp

+ 105
- 27
modules/juce_audio_devices/native/juce_win32_WASAPI.cpp View File

@@ -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…
Cancel
Save