[VCM] Track newly added microphones (#16199)

* [VCM] Track newly added microphones when [All] is selected in the settings

* [VCM] handle case when no mics are left

* fixup: fix crashes onNotify

* fixup: fix build
This commit is contained in:
Andrey Nekrasov
2022-03-21 12:48:11 +03:00
committed by GitHub
parent 2e3a2b3f96
commit 176f2c2870
13 changed files with 224 additions and 33 deletions

View File

@@ -0,0 +1,78 @@
#include "pch.h"
#include "AudioDeviceNotificationClient.h"
AudioDeviceNotificationClient::AudioDeviceNotificationClient()
{
(void)CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&_deviceEnumerator));
if (!_deviceEnumerator)
{
return;
}
if (FAILED(_deviceEnumerator->RegisterEndpointNotificationCallback(this)))
{
_deviceEnumerator->Release();
_deviceEnumerator = nullptr;
}
}
AudioDeviceNotificationClient::~AudioDeviceNotificationClient()
{
if (!_deviceEnumerator)
{
return;
}
_deviceEnumerator->UnregisterEndpointNotificationCallback(this);
_deviceEnumerator->Release();
}
ULONG AudioDeviceNotificationClient::AddRef()
{
return 1;
}
ULONG AudioDeviceNotificationClient::Release()
{
return 1;
}
HRESULT AudioDeviceNotificationClient::QueryInterface(REFIID, void**)
{
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceAdded(LPCWSTR)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceRemoved(LPCWSTR)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceStateChanged(LPCWSTR, DWORD)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR)
{
if (role == eConsole && (flow == eCapture || flow == eAll))
{
_deviceConfigurationChanged = true;
}
return S_OK;
}

View File

@@ -0,0 +1,30 @@
#pragma once
#include <MMDeviceAPI.h>
struct AudioDeviceNotificationClient : IMMNotificationClient
{
AudioDeviceNotificationClient();
~AudioDeviceNotificationClient();
bool PullPendingNotifications()
{
const bool result = _deviceConfigurationChanged;
_deviceConfigurationChanged = false;
return result;
}
private:
ULONG AddRef() override;
ULONG Release() override;
HRESULT QueryInterface(REFIID, void**) override;
HRESULT OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override;
HRESULT OnDeviceAdded(LPCWSTR) override;
HRESULT OnDeviceRemoved(LPCWSTR) override;
HRESULT OnDeviceStateChanged(LPCWSTR, DWORD) override;
HRESULT OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override;
IMMDeviceEnumerator* _deviceEnumerator = nullptr;
bool _deviceConfigurationChanged = false;
};

View File

@@ -126,6 +126,12 @@ LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARA
}
case WM_TIMER:
{
if (toolbar->audioConfChangesNotifier.PullPendingNotifications())
{
instance->onMicrophoneConfigurationChanged();
}
toolbar->microphoneMuted = instance->getMicrophoneMuteState();
if (toolbar->generalSettingsUpdateScheduled)
{
instance->onGeneralSettingsChanged();

View File

@@ -6,6 +6,8 @@
#include <common/Display/monitors.h>
#include "AudioDeviceNotificationClient.h"
struct ToolbarImages
{
Gdiplus::Image* camOnMicOn = nullptr;
@@ -43,6 +45,7 @@ private:
ToolbarImages darkImages;
ToolbarImages lightImages;
AudioDeviceNotificationClient audioConfChangesNotifier;
bool cameraMuted = false;
bool cameraInUse = false;

View File

@@ -111,6 +111,7 @@ xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AudioDeviceNotificationClient.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
@@ -118,6 +119,7 @@ xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
<ClInclude Include="VideoConferenceModule.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AudioDeviceNotificationClient.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="Toolbar.cpp" />

View File

@@ -6,6 +6,7 @@
<ClCompile Include="pch.cpp" />
<ClCompile Include="VideoConferenceModule.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="AudioDeviceNotificationClient.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
@@ -13,6 +14,7 @@
<ClInclude Include="pch.h" />
<ClInclude Include="VideoConferenceModule.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="AudioDeviceNotificationClient.h" />
</ItemGroup>
<ItemGroup>
<Image Include="black.bmp" />

View File

@@ -8,6 +8,7 @@
#include <shellapi.h>
#include <filesystem>
#include <unordered_set>
#include <common/debug_control.h>
#include <common/SettingsAPI/settings_helpers.h>
@@ -51,20 +52,22 @@ void VideoConferenceModule::reverseMicrophoneMute()
bool muted = false;
for (auto& controlledMic : instance->_controlledMicrophones)
{
const bool was_muted = controlledMic.muted();
controlledMic.toggle_muted();
const bool was_muted = controlledMic->muted();
controlledMic->toggle_muted();
muted = muted || !was_muted;
}
if (muted)
{
Trace::MicrophoneMuted();
}
instance->_mic_muted_state_during_disconnect = !instance->_mic_muted_state_during_disconnect;
toolbar.setMicrophoneMute(muted);
}
bool VideoConferenceModule::getMicrophoneMuteState()
{
return instance->_microphoneTrackedInUI ? instance->_microphoneTrackedInUI->muted() : false;
return instance->_microphoneTrackedInUI ? instance->_microphoneTrackedInUI->muted() : instance->_mic_muted_state_during_disconnect;
}
void VideoConferenceModule::reverseVirtualCameraMuteState()
@@ -268,6 +271,46 @@ void VideoConferenceModule::onModuleSettingsChanged()
}
}
void VideoConferenceModule::onMicrophoneConfigurationChanged()
{
if (!_controllingAllMics)
{
// Don't care if we don't control all the mics
return;
}
const bool mutedStateForNewMics = _microphoneTrackedInUI ? _microphoneTrackedInUI->muted() : _mic_muted_state_during_disconnect;
std::unordered_set<std::wstring_view> currentlyTrackedMicsIds;
for (const auto& controlledMic : _controlledMicrophones)
{
currentlyTrackedMicsIds.emplace(controlledMic->id());
}
auto allMics = MicrophoneDevice::getAllActive();
for (auto& newMic : allMics)
{
if (currentlyTrackedMicsIds.contains(newMic->id()))
{
continue;
}
if (mutedStateForNewMics)
{
newMic->set_muted(true);
}
_controlledMicrophones.emplace_back(std::move(newMic));
}
// Restore invalidated pointer
_microphoneTrackedInUI = controlledDefaultMic();
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
}
}
VideoConferenceModule::VideoConferenceModule() :
_generalSettingsWatcher{ PTSettingsHelper::get_powertoys_general_save_file_location(), [this] {
toolbar.scheduleGeneralSettingsUpdate();
@@ -388,47 +431,56 @@ void VideoConferenceModule::updateControlledMicrophones(const std::wstring_view
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic.set_muted(false);
controlledMic->set_muted(false);
}
_controlledMicrophones.clear();
_microphoneTrackedInUI = nullptr;
auto allMics = MicrophoneDevice::getAllActive();
if (new_mic == L"[All]")
{
_controllingAllMics = true;
_controlledMicrophones = std::move(allMics);
if (auto defaultMic = MicrophoneDevice::getDefault())
{
for (auto& controlledMic : _controlledMicrophones)
{
if (controlledMic.id() == defaultMic->id())
{
_microphoneTrackedInUI = &controlledMic;
break;
}
}
}
_microphoneTrackedInUI = controlledDefaultMic();
}
else
{
_controllingAllMics = false;
for (auto& controlledMic : allMics)
{
if (controlledMic.name() == new_mic)
if (controlledMic->name() == new_mic)
{
_controlledMicrophones.emplace_back(std::move(controlledMic));
_microphoneTrackedInUI = &_controlledMicrophones[0];
_microphoneTrackedInUI = _controlledMicrophones[0].get();
break;
}
}
}
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([&](const bool muted) {
_microphoneTrackedInUI->set_mute_changed_callback([](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
toolbar.setMicrophoneMute(_microphoneTrackedInUI->muted());
}
}
MicrophoneDevice* VideoConferenceModule::controlledDefaultMic()
{
if (auto defaultMic = MicrophoneDevice::getDefault())
{
for (auto& controlledMic : _controlledMicrophones)
{
if (controlledMic->id() == defaultMic->id())
{
return controlledMic.get();
}
}
}
return nullptr;
}
void toggleProxyCamRegistration(const bool enable)
{
if (!is_process_elevated())

View File

@@ -61,10 +61,14 @@ public:
void onGeneralSettingsChanged();
void onModuleSettingsChanged();
void onMicrophoneConfigurationChanged();
private:
void init_settings();
void updateControlledMicrophones(const std::wstring_view new_mic);
MicrophoneDevice* controlledDefaultMic();
// all callback methods and used by callback have to be static
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
static bool isKeyPressed(unsigned int keyCode);
@@ -73,7 +77,9 @@ private:
static HHOOK hook_handle;
bool _enabled = false;
std::vector<MicrophoneDevice> _controlledMicrophones;
bool _mic_muted_state_during_disconnect = false;
bool _controllingAllMics = false;
std::vector<std::unique_ptr<MicrophoneDevice>> _controlledMicrophones;
MicrophoneDevice* _microphoneTrackedInUI = nullptr;
std::optional<SerializedSharedMemory> _imageOverlayChannel;