Handle default output device change by capturing from new device

This commit is contained in:
Quantum 2020-01-08 23:12:06 -08:00
parent df1299f5de
commit e8be8a4a14

View file

@ -32,6 +32,44 @@ _COM_SMARTPTR_TYPEDEF(IMMDevice, __uuidof(IMMDevice));
_COM_SMARTPTR_TYPEDEF(IAudioClient, __uuidof(IAudioClient));
_COM_SMARTPTR_TYPEDEF(IAudioCaptureClient, __uuidof(IAudioCaptureClient));
class DeviceChangeNotification : public IMMNotificationClient {
volatile ULONG ref;
volatile bool &changed;
public:
DeviceChangeNotification(volatile bool &changed) : changed(changed) {}
// This is meant to be allocated on stack, so we don't actually free.
STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&ref); }
STDMETHODIMP_(ULONG) Release() { return InterlockedDecrement(&ref); }
STDMETHODIMP QueryInterface(REFIID iid, void **ppv) {
if (iid == IID_IUnknown) {
AddRef();
*ppv = (IUnknown *)this;
} else if (iid == __uuidof(IMMNotificationClient)) {
AddRef();
*ppv = (IMMNotificationClient *)this;
} else {
*ppv = nullptr;
return E_NOINTERFACE;
}
return S_OK;
}
STDMETHODIMP OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) {
if (flow == eRender && role == eConsole) {
changed = true;
}
return S_OK;
}
STDMETHODIMP OnDeviceAdded(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceRemoved(LPCWSTR) { return S_OK; }
STDMETHODIMP OnDeviceStateChanged(LPCWSTR, DWORD) { return S_OK; }
STDMETHODIMP OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) { return S_OK; }
};
class EnsureCaptureStop {
IAudioClientPtr m_client;
@ -99,15 +137,6 @@ int main(int argc, char *argv[]) {
CCoInitialize comInit;
ensure(comInit);
IMMDeviceEnumeratorPtr pEnumerator;
ensure(pEnumerator.CreateInstance(__uuidof(MMDeviceEnumerator)));
IMMDevicePtr pDevice;
ensure(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice));
IAudioClientPtr pClient;
ensure(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void **)&pClient));
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)channels;
@ -116,6 +145,20 @@ int main(int argc, char *argv[]) {
wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
IMMDeviceEnumeratorPtr pEnumerator;
ensure(pEnumerator.CreateInstance(__uuidof(MMDeviceEnumerator)));
volatile bool deviceChanged = false;
DeviceChangeNotification deviceChangeNotification(deviceChanged);
pEnumerator->RegisterEndpointNotificationCallback(&deviceChangeNotification);
for (;;) {
IMMDevicePtr pDevice;
ensure(pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice));
IAudioClientPtr pClient;
ensure(pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void **)&pClient));
ensure(pClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK,
16 * REFTIMES_PER_MILLISEC, 0, &wfx, nullptr));
@ -135,11 +178,18 @@ int main(int argc, char *argv[]) {
ensure(pClient->Start());
EnsureCaptureStop autoStop(pClient);
for (;;) {
while (!deviceChanged) {
Sleep(dwDelay);
UINT32 packetLength;
ensure(pCapture->GetNextPacketSize(&packetLength));
HRESULT hrNext = pCapture->GetNextPacketSize(&packetLength);
if (hrNext == AUDCLNT_E_DEVICE_INVALIDATED) {
while (!deviceChanged)
;
break;
} else {
ensure(hrNext);
}
while (packetLength) {
LPBYTE pData;
@ -156,4 +206,6 @@ int main(int argc, char *argv[]) {
ensure(pCapture->GetNextPacketSize(&packetLength));
}
}
deviceChanged = false;
}
}