live update
This commit is contained in:
@@ -1,9 +1,49 @@
|
|||||||
#include "ApplicationData.h"
|
#include "ApplicationData.h"
|
||||||
|
|
||||||
|
AudioDevice::AudioDevice()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioDevice::AudioDevice(AudioDevice&& other) noexcept
|
||||||
|
: device(other.device), volumeInterface(other.volumeInterface), meterInterface(other.meterInterface),
|
||||||
|
id(other.id), name(other.name), state(other.state),
|
||||||
|
isDefaultConsole(other.isDefaultConsole), isDefaultMedia(other.isDefaultMedia), isDefaultCommunication(other.isDefaultCommunication)
|
||||||
|
{
|
||||||
|
other.device = nullptr;
|
||||||
|
other.volumeInterface = nullptr;
|
||||||
|
other.meterInterface = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioDevice& AudioDevice::operator=(AudioDevice&& other) noexcept
|
||||||
|
{
|
||||||
|
this->device = other.device;
|
||||||
|
this->volumeInterface = other.volumeInterface;
|
||||||
|
this->meterInterface = other.meterInterface;
|
||||||
|
this->id = other.id;
|
||||||
|
this->name = other.name;
|
||||||
|
this->state = other.state;
|
||||||
|
this->isDefaultConsole = other.isDefaultConsole;
|
||||||
|
this->isDefaultMedia = other.isDefaultMedia;
|
||||||
|
this->isDefaultCommunication = other.isDefaultCommunication;
|
||||||
|
|
||||||
|
other.device = nullptr;
|
||||||
|
other.volumeInterface = nullptr;
|
||||||
|
other.meterInterface = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
AudioDevice::~AudioDevice()
|
AudioDevice::~AudioDevice()
|
||||||
{
|
{
|
||||||
|
if (volumeInterface)
|
||||||
|
{
|
||||||
|
volumeInterface->Release();
|
||||||
|
}
|
||||||
|
if (meterInterface)
|
||||||
|
{
|
||||||
|
meterInterface->Release();
|
||||||
|
}
|
||||||
if (device)
|
if (device)
|
||||||
{
|
{
|
||||||
//device->Release();
|
device->Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <endpointvolume.h>
|
#include <endpointvolume.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "AudioNotificationListener.h"
|
||||||
|
|
||||||
class AudioDevice {
|
class AudioDevice {
|
||||||
public:
|
public:
|
||||||
@@ -17,6 +18,9 @@ public:
|
|||||||
bool isDefaultMedia = {};
|
bool isDefaultMedia = {};
|
||||||
bool isDefaultCommunication = {};
|
bool isDefaultCommunication = {};
|
||||||
|
|
||||||
|
AudioDevice();
|
||||||
|
AudioDevice(AudioDevice&& other) noexcept;
|
||||||
|
AudioDevice& operator=(AudioDevice&& other) noexcept;
|
||||||
~AudioDevice();
|
~AudioDevice();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -31,4 +35,6 @@ public:
|
|||||||
ApplicationSettings settings = {};
|
ApplicationSettings settings = {};
|
||||||
std::vector<AudioDevice> playbackDevices = {};
|
std::vector<AudioDevice> playbackDevices = {};
|
||||||
std::vector<AudioDevice> recordingDevices = {};
|
std::vector<AudioDevice> recordingDevices = {};
|
||||||
|
IMMDeviceEnumerator* deviceEnumerator = nullptr;
|
||||||
|
AudioNotificationListener* audioNotificationListener = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -36,8 +36,16 @@ void init(DrawData& drawData, void* customData)
|
|||||||
io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges);
|
io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges);
|
||||||
|
|
||||||
// Set up audio device api
|
// Set up audio device api
|
||||||
HRESULT initResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
HRESULT audioResult;
|
||||||
isError(initResult, "Failed to initialize COM: ");
|
audioResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
isError(audioResult, "Failed to initialize COM: ");
|
||||||
|
|
||||||
|
audioResult = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&appData->deviceEnumerator));
|
||||||
|
isError(audioResult, "Failed to set up audio device enumerator: ");
|
||||||
|
|
||||||
|
appData->audioNotificationListener = new AudioNotificationListener(appData);
|
||||||
|
audioResult = appData->deviceEnumerator->RegisterEndpointNotificationCallback(appData->audioNotificationListener);
|
||||||
|
isError(audioResult, "Failed to register audio notification listener: ");
|
||||||
|
|
||||||
reloadDeviceLists(appData);
|
reloadDeviceLists(appData);
|
||||||
|
|
||||||
@@ -54,20 +62,24 @@ void draw(DrawData& drawData, void* customData)
|
|||||||
{
|
{
|
||||||
ApplicationData* appData = static_cast<ApplicationData*>(customData);
|
ApplicationData* appData = static_cast<ApplicationData*>(customData);
|
||||||
float customYCursor = 0;
|
float customYCursor = 0;
|
||||||
|
ImVec2 viewportSize = ImGui::GetMainViewport()->Size;
|
||||||
|
|
||||||
|
// Menu Bar
|
||||||
customYCursor += menuBar(appData).y;
|
customYCursor += menuBar(appData).y;
|
||||||
|
|
||||||
ImVec2 containingSize = ImGui::GetMainViewport()->Size;
|
// Playback Devices
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
||||||
ImGui::SetNextWindowSize(ImVec2(containingSize.x, 0));
|
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
|
||||||
customYCursor += audioDeviceWindow(appData, appData->playbackDevices, " \xEE\xB8\x84 Playback").y;
|
customYCursor += audioDeviceWindow(appData, appData->playbackDevices, " \xEE\xB8\x84 Playback").y;
|
||||||
|
|
||||||
customYCursor += 5.;
|
customYCursor += 5.;
|
||||||
|
|
||||||
|
// Recording devices
|
||||||
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
ImGui::SetNextWindowPos(ImVec2(0, customYCursor));
|
||||||
ImGui::SetNextWindowSize(ImVec2(containingSize.x, 0));
|
ImGui::SetNextWindowSize(ImVec2(viewportSize.x, 0));
|
||||||
customYCursor += audioDeviceWindow(appData, appData->recordingDevices, " \xEE\xBD\x8F Recording").y;
|
customYCursor += audioDeviceWindow(appData, appData->recordingDevices, " \xEE\xBD\x8F Recording").y;
|
||||||
|
|
||||||
|
// Resize viewport
|
||||||
if (appData->settings.fitWindowHeight)
|
if (appData->settings.fitWindowHeight)
|
||||||
{
|
{
|
||||||
drawData.window_size.y = customYCursor;
|
drawData.window_size.y = customYCursor;
|
||||||
@@ -150,14 +162,18 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
|
|||||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.));
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(.7, .7, .7, 1.));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Device Name
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
|
|
||||||
ImGui::Text(dev.name.c_str());
|
ImGui::Text(dev.name.c_str());
|
||||||
|
|
||||||
|
// Volume
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (dev.state == DEVICE_STATE_ACTIVE)
|
if (dev.state == DEVICE_STATE_ACTIVE)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
float volume = log10f(getMeterValue(dev.meterInterface) * 9. + 1.);
|
float volume = log10f(getMeterValue(dev.meterInterface) * 9. + 1.);
|
||||||
|
|
||||||
auto drawList = ImGui::GetWindowDrawList();
|
auto drawList = ImGui::GetWindowDrawList();
|
||||||
@@ -170,6 +186,7 @@ ImVec2 audioDeviceWindow(ApplicationData* appData, std::vector<AudioDevice>& dev
|
|||||||
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x * volume, lineY), IM_COL32(200, 200, 255, 255), 3.);
|
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + space.x * volume, lineY), IM_COL32(200, 200, 255, 255), 3.);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defaults
|
||||||
ImGui::TableNextColumn();
|
ImGui::TableNextColumn();
|
||||||
if (dev.state == DEVICE_STATE_ACTIVE)
|
if (dev.state == DEVICE_STATE_ACTIVE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -145,6 +145,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="ApplicationData.cpp" />
|
<ClCompile Include="ApplicationData.cpp" />
|
||||||
<ClCompile Include="AsuroTool.cpp" />
|
<ClCompile Include="AsuroTool.cpp" />
|
||||||
|
<ClCompile Include="AudioNotificationListener.cpp" />
|
||||||
<ClCompile Include="Util.cpp" />
|
<ClCompile Include="Util.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -152,6 +153,7 @@
|
|||||||
<ClInclude Include="AsuroTool.h" />
|
<ClInclude Include="AsuroTool.h" />
|
||||||
<ClCompile Include="AudioApi.cpp" />
|
<ClCompile Include="AudioApi.cpp" />
|
||||||
<ClInclude Include="AudioApi.h" />
|
<ClInclude Include="AudioApi.h" />
|
||||||
|
<ClInclude Include="AudioNotificationListener.h" />
|
||||||
<ClInclude Include="PolicyConfig.h" />
|
<ClInclude Include="PolicyConfig.h" />
|
||||||
<ClInclude Include="resource.h" />
|
<ClInclude Include="resource.h" />
|
||||||
<ClInclude Include="Util.h" />
|
<ClInclude Include="Util.h" />
|
||||||
|
|||||||
@@ -13,20 +13,29 @@
|
|||||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
</Filter>
|
</Filter>
|
||||||
|
<Filter Include="Source Files\Audio">
|
||||||
|
<UniqueIdentifier>{37b6f2f7-f9d0-428e-9a8f-4b034610a39a}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files\Audio">
|
||||||
|
<UniqueIdentifier>{b65d213d-ddf6-4816-90d1-bf0811a51abf}</UniqueIdentifier>
|
||||||
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AsuroTool.cpp">
|
<ClCompile Include="AsuroTool.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="AudioApi.cpp">
|
|
||||||
<Filter>Source Files</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Util.cpp">
|
<ClCompile Include="Util.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ApplicationData.cpp">
|
<ClCompile Include="ApplicationData.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="AudioApi.cpp">
|
||||||
|
<Filter>Source Files\Audio</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="AudioNotificationListener.cpp">
|
||||||
|
<Filter>Source Files\Audio</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="AsuroTool.h">
|
<ClInclude Include="AsuroTool.h">
|
||||||
@@ -38,15 +47,18 @@
|
|||||||
<ClInclude Include="PolicyConfig.h">
|
<ClInclude Include="PolicyConfig.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="AudioApi.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="ApplicationData.h">
|
<ClInclude Include="ApplicationData.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="resource.h">
|
<ClInclude Include="resource.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="AudioApi.h">
|
||||||
|
<Filter>Header Files\Audio</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="AudioNotificationListener.h">
|
||||||
|
<Filter>Header Files\Audio</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<CopyFileToFolders Include="Montserrat-Regular.ttf">
|
<CopyFileToFolders Include="Montserrat-Regular.ttf">
|
||||||
|
|||||||
@@ -38,18 +38,14 @@ void setDefaultAudioDevice(ApplicationData* appData, const wchar_t* deviceId, ER
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType)
|
void loadAudioDevices(ApplicationData* appData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType)
|
||||||
{
|
{
|
||||||
deviceList.clear();
|
deviceList.clear();
|
||||||
|
|
||||||
HRESULT err;
|
HRESULT err;
|
||||||
IMMDeviceEnumerator* deviceEnumerator = NULL;
|
|
||||||
IMMDeviceCollection* deviceCollection = NULL;
|
IMMDeviceCollection* deviceCollection = NULL;
|
||||||
|
|
||||||
err = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator));
|
err = appData->deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection);
|
||||||
if (isError(err, "Failed to set up audio device enumerator: ")) return;
|
|
||||||
|
|
||||||
err = deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection);
|
|
||||||
if (isError(err, "Failed to enumerate audio devices: ")) return;
|
if (isError(err, "Failed to enumerate audio devices: ")) return;
|
||||||
|
|
||||||
UINT deviceCount;
|
UINT deviceCount;
|
||||||
@@ -58,7 +54,7 @@ void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType
|
|||||||
|
|
||||||
IMMDevice* defaultConsoleDevice = NULL;
|
IMMDevice* defaultConsoleDevice = NULL;
|
||||||
LPWSTR defaultConsoleId = nullptr;
|
LPWSTR defaultConsoleId = nullptr;
|
||||||
err = deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice);
|
err = appData->deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice);
|
||||||
if (!FAILED(err))
|
if (!FAILED(err))
|
||||||
{
|
{
|
||||||
defaultConsoleDevice->GetId(&defaultConsoleId);
|
defaultConsoleDevice->GetId(&defaultConsoleId);
|
||||||
@@ -66,7 +62,7 @@ void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType
|
|||||||
|
|
||||||
IMMDevice* defaultMediaOutput = NULL;
|
IMMDevice* defaultMediaOutput = NULL;
|
||||||
LPWSTR defaultMediaId = nullptr;
|
LPWSTR defaultMediaId = nullptr;
|
||||||
err = deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput);
|
err = appData->deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput);
|
||||||
if (!FAILED(err))
|
if (!FAILED(err))
|
||||||
{
|
{
|
||||||
defaultMediaOutput->GetId(&defaultMediaId);
|
defaultMediaOutput->GetId(&defaultMediaId);
|
||||||
@@ -74,7 +70,7 @@ void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType
|
|||||||
|
|
||||||
IMMDevice* defaultCommunicationOutput = NULL;
|
IMMDevice* defaultCommunicationOutput = NULL;
|
||||||
LPWSTR defaultCommunicationId = nullptr;
|
LPWSTR defaultCommunicationId = nullptr;
|
||||||
err = deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput);
|
err = appData->deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput);
|
||||||
if (!FAILED(err))
|
if (!FAILED(err))
|
||||||
{
|
{
|
||||||
defaultCommunicationOutput->GetId(&defaultCommunicationId);
|
defaultCommunicationOutput->GetId(&defaultCommunicationId);
|
||||||
@@ -82,48 +78,54 @@ void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType
|
|||||||
|
|
||||||
for (UINT i = 0; i < deviceCount; i += 1)
|
for (UINT i = 0; i < deviceCount; i += 1)
|
||||||
{
|
{
|
||||||
IMMDevice* device;
|
AudioDevice deviceData{};
|
||||||
err = deviceCollection->Item(i, &device);
|
|
||||||
isError(err, std::stringstream("Failed to get device ") << i << ": ");
|
err = deviceCollection->Item(i, &deviceData.device);
|
||||||
|
if (isError(err, std::stringstream("Failed to get device ") << i << ": "))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
LPWSTR deviceId;
|
LPWSTR deviceId;
|
||||||
err = device->GetId(&deviceId);
|
err = deviceData.device->GetId(&deviceId);
|
||||||
isError(err, std::stringstream("Failed to get device id ") << i << ": ");
|
isError(err, std::stringstream("Failed to get device id ") << i << ": ");
|
||||||
|
deviceData.id = std::wstring(deviceId);
|
||||||
|
|
||||||
IPropertyStore* propertyStore;
|
IPropertyStore* propertyStore;
|
||||||
err = device->OpenPropertyStore(STGM_READ, &propertyStore);
|
err = deviceData.device->OpenPropertyStore(STGM_READ, &propertyStore);
|
||||||
isError(err, std::stringstream("Failed to open device ") << i << "prop store: ");
|
isError(err, std::stringstream("Failed to open device ") << i << "prop store: ");
|
||||||
|
|
||||||
PROPVARIANT deviceNameProp;
|
PROPVARIANT deviceNameProp;
|
||||||
const wchar_t* deviceName;
|
const wchar_t* deviceName;
|
||||||
err = getDevicePropertyString(propertyStore, PKEY_Device_FriendlyName, &deviceNameProp, deviceName);
|
err = getDevicePropertyString(propertyStore, PKEY_Device_FriendlyName, &deviceNameProp, deviceName);
|
||||||
isError(err, std::stringstream("Failed to read name of device ") << i << ": ");
|
isError(err, std::stringstream("Failed to read name of device ") << i << ": ");
|
||||||
|
deviceData.name = utf8Encode(deviceName);
|
||||||
|
|
||||||
DWORD deviceState;
|
err = deviceData.device->GetState(&deviceData.state);
|
||||||
err = device->GetState(&deviceState);
|
|
||||||
isError(err, std::stringstream("Failed to reat state of device ") << i << ": ");
|
isError(err, std::stringstream("Failed to reat state of device ") << i << ": ");
|
||||||
|
|
||||||
IAudioEndpointVolume* volumeInterface;
|
err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface);
|
||||||
err = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&volumeInterface);
|
|
||||||
isError(err, "Failed to get audio endpoint volume interface: ");
|
isError(err, "Failed to get audio endpoint volume interface: ");
|
||||||
|
|
||||||
IAudioMeterInformation* meterInterface;
|
IAudioMeterInformation* meterInterface;
|
||||||
err = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&meterInterface);
|
err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface);
|
||||||
isError(err, "Failed to get audio meter interface: ");
|
isError(err, "Failed to get audio meter interface: ");
|
||||||
|
|
||||||
deviceList.push_back({
|
if (defaultConsoleId)
|
||||||
device,
|
{
|
||||||
volumeInterface,
|
deviceData.isDefaultConsole = wcscmp(defaultConsoleId, deviceId) == 0;
|
||||||
meterInterface,
|
}
|
||||||
std::wstring(deviceId),
|
if (defaultMediaId)
|
||||||
utf8Encode(deviceName),
|
{
|
||||||
deviceState,
|
deviceData.isDefaultMedia = wcscmp(defaultMediaId, deviceId) == 0;
|
||||||
utf8Encode(defaultConsoleId) == utf8Encode(deviceId),
|
}
|
||||||
utf8Encode(defaultMediaId) == utf8Encode(deviceId),
|
if (defaultCommunicationId)
|
||||||
utf8Encode(defaultCommunicationId) == utf8Encode(deviceId),
|
{
|
||||||
});
|
deviceData.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceList.push_back(std::move(deviceData));
|
||||||
|
|
||||||
// Free stuff
|
|
||||||
if (propertyStore)
|
if (propertyStore)
|
||||||
{
|
{
|
||||||
propertyStore->Release();
|
propertyStore->Release();
|
||||||
@@ -131,22 +133,18 @@ void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType
|
|||||||
CoTaskMemFree(deviceId);
|
CoTaskMemFree(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceEnumerator)
|
std::sort(deviceList.begin(), deviceList.end(), [](AudioDevice& a, AudioDevice& b) { return a.state < b.state; });
|
||||||
{
|
|
||||||
deviceEnumerator->Release();
|
|
||||||
}
|
|
||||||
if (deviceCollection)
|
if (deviceCollection)
|
||||||
{
|
{
|
||||||
deviceCollection->Release();
|
deviceCollection->Release();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(deviceList.begin(), deviceList.end(), [](AudioDevice& a, AudioDevice& b) { return a.state < b.state; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void reloadDeviceLists(ApplicationData* appData)
|
void reloadDeviceLists(ApplicationData* appData)
|
||||||
{
|
{
|
||||||
loadAudioDevices(appData->playbackDevices, EDataFlow::eRender);
|
loadAudioDevices(appData, appData->playbackDevices, EDataFlow::eRender);
|
||||||
loadAudioDevices(appData->recordingDevices, EDataFlow::eCapture);
|
loadAudioDevices(appData, appData->recordingDevices, EDataFlow::eCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
float getVolume(IAudioEndpointVolume* volumeInterface)
|
float getVolume(IAudioEndpointVolume* volumeInterface)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData);
|
HRESULT getDeviceProperty(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData);
|
||||||
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown");
|
HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY propertyKey, PROPVARIANT* outData, const wchar_t*& outString, const wchar_t* defaultStr = L"Unknown");
|
||||||
void setDefaultAudioDevice(ApplicationData* appData, const wchar_t* deviceId, ERole role);
|
void setDefaultAudioDevice(ApplicationData* appData, const wchar_t* deviceId, ERole role);
|
||||||
void loadAudioDevices(std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
|
void loadAudioDevices(ApplicationData* appData, std::vector<AudioDevice>& deviceList, EDataFlow deviceType);
|
||||||
void reloadDeviceLists(ApplicationData* appData);
|
void reloadDeviceLists(ApplicationData* appData);
|
||||||
float getVolume(IAudioEndpointVolume* volumeInterface);
|
float getVolume(IAudioEndpointVolume* volumeInterface);
|
||||||
float getMeterValue(IAudioMeterInformation* meterInterface);
|
float getMeterValue(IAudioMeterInformation* meterInterface);
|
||||||
|
|||||||
150
AsuroTool/AudioNotificationListener.cpp
Normal file
150
AsuroTool/AudioNotificationListener.cpp
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "AudioApi.h"
|
||||||
|
#include "AudioNotificationListener.h"
|
||||||
|
|
||||||
|
|
||||||
|
AudioNotificationListener::AudioNotificationListener(ApplicationData* appData) : appData(appData)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT __stdcall AudioNotificationListener::OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
|
||||||
|
{
|
||||||
|
auto updateDevice = [pwstrDeviceId, dwNewState](std::vector<AudioDevice>& deviceList)
|
||||||
|
{
|
||||||
|
for (auto& audioDevice : deviceList)
|
||||||
|
{
|
||||||
|
if (wcscmp(audioDevice.id.c_str(), pwstrDeviceId) == 0)
|
||||||
|
{
|
||||||
|
audioDevice.state = dwNewState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateDevice(appData->playbackDevices);
|
||||||
|
updateDevice(appData->recordingDevices);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT __stdcall AudioNotificationListener::OnDeviceAdded(LPCWSTR pwstrDeviceId)
|
||||||
|
{
|
||||||
|
IMMDevice* newDevice;
|
||||||
|
HRESULT result = appData->deviceEnumerator->GetDevice(pwstrDeviceId, &newDevice);
|
||||||
|
if (SUCCEEDED(result) && newDevice != nullptr)
|
||||||
|
{
|
||||||
|
// TODO: add to device list
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT __stdcall AudioNotificationListener::OnDeviceRemoved(LPCWSTR pwstrDeviceId)
|
||||||
|
{
|
||||||
|
auto deleteDevice = [pwstrDeviceId](std::vector<AudioDevice>& deviceList)
|
||||||
|
{
|
||||||
|
auto deviceListIterator = deviceList.begin();
|
||||||
|
while (deviceListIterator != deviceList.end())
|
||||||
|
{
|
||||||
|
if (wcscmp(deviceListIterator->id.c_str(), pwstrDeviceId) == 0)
|
||||||
|
{
|
||||||
|
deviceListIterator = deviceList.erase(deviceListIterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
deleteDevice(appData->playbackDevices);
|
||||||
|
deleteDevice(appData->recordingDevices);
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT __stdcall AudioNotificationListener::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId)
|
||||||
|
{
|
||||||
|
std::vector<AudioDevice>* deviceList;
|
||||||
|
if (flow == EDataFlow::eRender)
|
||||||
|
{
|
||||||
|
deviceList = &appData->playbackDevices;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deviceList = &appData->recordingDevices;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (AudioDevice& audioDevice : *deviceList)
|
||||||
|
{
|
||||||
|
if (wcscmp(audioDevice.id.c_str(), pwstrDefaultDeviceId) == 0)
|
||||||
|
{
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case ERole::eCommunications:
|
||||||
|
audioDevice.isDefaultCommunication = true;
|
||||||
|
break;
|
||||||
|
case ERole::eConsole:
|
||||||
|
audioDevice.isDefaultConsole = true;
|
||||||
|
break;
|
||||||
|
case ERole::eMultimedia:
|
||||||
|
audioDevice.isDefaultMedia = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (role)
|
||||||
|
{
|
||||||
|
case ERole::eCommunications:
|
||||||
|
audioDevice.isDefaultCommunication = false;
|
||||||
|
break;
|
||||||
|
case ERole::eConsole:
|
||||||
|
audioDevice.isDefaultConsole = false;
|
||||||
|
break;
|
||||||
|
case ERole::eMultimedia:
|
||||||
|
audioDevice.isDefaultMedia = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT __stdcall AudioNotificationListener::OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
|
||||||
|
{
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy pasted stuff from Windows SDK Example
|
||||||
|
// No clue what it does lol
|
||||||
|
// https://github.com/pauldotknopf/WindowsSDK7-Samples/blob/master/multimedia/audio/osd/endpointMonitor.cpp
|
||||||
|
HRESULT __stdcall AudioNotificationListener::QueryInterface(REFIID riid, void** ppvObject)
|
||||||
|
{
|
||||||
|
if ((riid == __uuidof(IUnknown)) ||
|
||||||
|
(riid == __uuidof(IMMNotificationClient)))
|
||||||
|
{
|
||||||
|
*ppvObject = static_cast<IMMNotificationClient*>(this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ppvObject = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AddRef();
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG __stdcall AudioNotificationListener::AddRef(void)
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&refCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG __stdcall AudioNotificationListener::Release(void)
|
||||||
|
{
|
||||||
|
long refCountResult = InterlockedDecrement(&refCount);
|
||||||
|
if (refCountResult == 0)
|
||||||
|
{
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
return refCountResult;
|
||||||
|
}
|
||||||
23
AsuroTool/AudioNotificationListener.h
Normal file
23
AsuroTool/AudioNotificationListener.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
|
||||||
|
class ApplicationData;
|
||||||
|
class AudioNotificationListener : public IMMNotificationClient {
|
||||||
|
public:
|
||||||
|
ApplicationData* appData;
|
||||||
|
long refCount = 1;
|
||||||
|
|
||||||
|
AudioNotificationListener(ApplicationData* appData);
|
||||||
|
|
||||||
|
virtual HRESULT __stdcall OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) override;
|
||||||
|
virtual HRESULT __stdcall OnDeviceAdded(LPCWSTR pwstrDeviceId) override;
|
||||||
|
virtual HRESULT __stdcall OnDeviceRemoved(LPCWSTR pwstrDeviceId) override;
|
||||||
|
virtual HRESULT __stdcall OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDefaultDeviceId) override;
|
||||||
|
virtual HRESULT __stdcall OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key) override;
|
||||||
|
|
||||||
|
virtual HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) override;
|
||||||
|
virtual ULONG __stdcall AddRef(void) override;
|
||||||
|
virtual ULONG __stdcall Release(void) override;
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user