9 Commits
1.0 ... 1.0.4

Author SHA1 Message Date
5f5e24fa9c 1.0.4 2022-07-26 18:08:13 +02:00
cdffa1b50b per channel peak meters 2022-07-26 18:07:57 +02:00
eec9e25e12 1.0.3 2022-07-26 15:22:51 +02:00
3b4853acda Fix device add/remove handlers 2022-07-26 15:22:23 +02:00
cea13986f0 1.0.2 2022-07-24 02:09:58 +02:00
3641ffdad9 Set working dir on startup 2022-07-24 02:07:35 +02:00
7fa68792fb Use exe path to search for fonts 2022-07-22 21:48:04 +02:00
ebdd35b1e1 1.1 2022-07-22 01:49:30 +02:00
b986bfde39 fix missing tray icon bug 2022-07-22 01:48:26 +02:00
13 changed files with 217 additions and 105 deletions

View File

@@ -1,13 +1,44 @@
#include "ApplicationData.h" #include "ApplicationData.h"
#include "Util.h"
#include "AudioApi.h"
#include <functiondiscoverykeys.h>
AudioDevice::AudioDevice() AudioDevice::AudioDevice(IMMDevice* device, LPCWSTR deviceId)
{ {
id = std::wstring(deviceId);
IPropertyStore* propertyStore;
HRESULT err = device->OpenPropertyStore(STGM_READ, &propertyStore);
isError(err, "Failed to open prop store: ");
PROPVARIANT deviceNameProp;
const wchar_t* deviceName;
err = getDevicePropertyString(propertyStore, PKEY_Device_FriendlyName, &deviceNameProp, deviceName);
isError(err, "Failed to read name of device :");
name = utf8Encode(deviceName);
err = device->GetState(&state);
isError(err, "Failed to reat state of device: ");
err = device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&volumeInterface);
isError(err, "Failed to get audio endpoint volume interface: ");
err = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&meterInterface);
isError(err, "Failed to get audio meter interface: ");
getVolumeLimit(volumeInterface, &minVolumeDb, &maxVolumeDb);
if (propertyStore)
{
propertyStore->Release();
}
} }
AudioDevice::AudioDevice(AudioDevice&& other) noexcept AudioDevice::AudioDevice(AudioDevice&& other) noexcept
: device(other.device), volumeInterface(other.volumeInterface), meterInterface(other.meterInterface), : device(other.device), volumeInterface(other.volumeInterface), meterInterface(other.meterInterface),
id(other.id), name(other.name), state(other.state), id(other.id), name(other.name), state(other.state),
isDefaultConsole(other.isDefaultConsole), isDefaultMedia(other.isDefaultMedia), isDefaultCommunication(other.isDefaultCommunication) isDefaultConsole(other.isDefaultConsole), isDefaultMedia(other.isDefaultMedia), isDefaultCommunication(other.isDefaultCommunication),
minVolumeDb(other.minVolumeDb), maxVolumeDb(other.maxVolumeDb)
{ {
other.device = nullptr; other.device = nullptr;
other.volumeInterface = nullptr; other.volumeInterface = nullptr;
@@ -25,6 +56,8 @@ AudioDevice& AudioDevice::operator=(AudioDevice&& other) noexcept
this->isDefaultConsole = other.isDefaultConsole; this->isDefaultConsole = other.isDefaultConsole;
this->isDefaultMedia = other.isDefaultMedia; this->isDefaultMedia = other.isDefaultMedia;
this->isDefaultCommunication = other.isDefaultCommunication; this->isDefaultCommunication = other.isDefaultCommunication;
this->minVolumeDb = other.minVolumeDb;
this->maxVolumeDb = other.maxVolumeDb;
other.device = nullptr; other.device = nullptr;
other.volumeInterface = nullptr; other.volumeInterface = nullptr;

View File

@@ -14,11 +14,13 @@ public:
std::wstring id = {}; std::wstring id = {};
std::string name = {}; std::string name = {};
unsigned long state = {}; unsigned long state = {};
bool isDefaultConsole = {}; bool isDefaultConsole = false;
bool isDefaultMedia = {}; bool isDefaultMedia = false;
bool isDefaultCommunication = {}; bool isDefaultCommunication = false;
float minVolumeDb = 0.f;
float maxVolumeDb = 0.f;
AudioDevice(); AudioDevice(IMMDevice* device, LPCWSTR deviceId);
AudioDevice(AudioDevice&& other) noexcept; AudioDevice(AudioDevice&& other) noexcept;
AudioDevice& operator=(AudioDevice&& other) noexcept; AudioDevice& operator=(AudioDevice&& other) noexcept;
~AudioDevice(); ~AudioDevice();

View File

@@ -10,6 +10,7 @@
#include <windows.h> #include <windows.h>
#include <array>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@@ -23,10 +24,9 @@
#include "resource.h" #include "resource.h"
#include "AsuroTool.h" #include "AsuroTool.h"
class __declspec(uuid("3bc52579-15fd-43bb-9686-6273c238535e")) TrayGUID; const size_t MAX_FONT_PATH_LENGTH = 2048;
const UINT TRAY_ID = 420;
UINT const WMAPP_NOTIFYCALLBACK = WM_APP + 1; const UINT WMAPP_NOTIFYCALLBACK = WM_APP + 1;
UINT const WMAPP_HIDEFLYOUT = WM_APP + 2;
// Globals for use in callbacks // Globals for use in callbacks
DrawData* gDrawData; DrawData* gDrawData;
@@ -36,6 +36,13 @@ bool isHidden = false;
int main() int main()
{ {
std::wstring appDir;
getAppDir(appDir);
if (_wchdir(appDir.c_str()) != 0)
{
std::cout << "Failed to set working dir." << std::endl;
}
ApplicationData applicationData{}; ApplicationData applicationData{};
ImGuiCallbacks callbacks{}; ImGuiCallbacks callbacks{};
@@ -48,20 +55,30 @@ int main()
void init(DrawData& drawData, ApplicationData& appData) void init(DrawData& drawData, ApplicationData& appData)
{ {
std::wstring appPath;
getAppDir(appPath);
char appPathStr[MAX_FONT_PATH_LENGTH];
HRESULT convResult = WideCharToMultiByte(CP_UTF8, NULL, &appPath[0], -1, appPathStr, MAX_FONT_PATH_LENGTH, NULL, nullptr);
isError(convResult, "Failed to convert path: ");
// sad :( // sad :(
gDrawData = &drawData; gDrawData = &drawData;
gAppData = &appData; gAppData = &appData;
// Load text font // Load text font
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Montserrat-Regular.ttf", 18.0f); std::string fontPath = std::string(appPathStr);
fontPath.append("\\Montserrat-Regular.ttf");
io.Fonts->AddFontFromFileTTF(fontPath.c_str(), 18.0f);
// Load icon font // Load icon font
static const ImWchar icons_ranges[] = { 0xEA01, 0xF2DF, 0 }; static const ImWchar icons_ranges[] = { 0xEA01, 0xF2DF, 0 };
ImFontConfig icons_config; ImFontConfig icons_config;
icons_config.MergeMode = true; icons_config.MergeMode = true;
icons_config.PixelSnapH = true; icons_config.PixelSnapH = true;
io.Fonts->AddFontFromFileTTF("remixicon.ttf", 14.0f, &icons_config, icons_ranges); std::string iconFontPath = std::string(appPathStr);
iconFontPath.append("\\remixicon.ttf");
io.Fonts->AddFontFromFileTTF(iconFontPath.c_str(), 14.0f, &icons_config, icons_ranges);
// Set window icon // Set window icon
HINSTANCE instance = GetModuleHandle(NULL); HINSTANCE instance = GetModuleHandle(NULL);
@@ -75,8 +92,8 @@ void init(DrawData& drawData, ApplicationData& appData)
// Set tray icon // Set tray icon
NOTIFYICONDATA nid = { sizeof(nid) }; NOTIFYICONDATA nid = { sizeof(nid) };
nid.hWnd = drawData.window_handle; nid.hWnd = drawData.window_handle;
nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP | NIF_GUID; nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_SHOWTIP;
nid.guidItem = __uuidof(TrayGUID); nid.uID = TRAY_ID;
nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK; nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK;
HRESULT iconResult; HRESULT iconResult;
@@ -123,10 +140,6 @@ void draw(DrawData& drawData, ApplicationData& appData)
{ {
justDocked = false; justDocked = false;
// sad :(
gDrawData = &drawData;
gAppData = &appData;
// Actual Drawing // Actual Drawing
if (isHidden) if (isHidden)
{ {
@@ -167,9 +180,9 @@ void cleanup(DrawData& drawData, ApplicationData& appData)
{ {
// Remove tray icon // Remove tray icon
NOTIFYICONDATA nid = { sizeof(nid) }; NOTIFYICONDATA nid = { sizeof(nid) };
nid.uFlags = NIF_GUID; nid.hWnd = drawData.window_handle;
nid.guidItem = __uuidof(TrayGUID); nid.uID = TRAY_ID;
bool test = Shell_NotifyIcon(NIM_DELETE, &nid); Shell_NotifyIcon(NIM_DELETE, &nid);
} }
ImVec2 menuBar(DrawData& drawData, ApplicationData& appData) ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
@@ -181,9 +194,7 @@ ImVec2 menuBar(DrawData& drawData, ApplicationData& appData)
{ {
if (ImGui::BeginMenu("Settings")) if (ImGui::BeginMenu("Settings"))
{ {
bool prevDocked = appData.settings.docked; if (ImGui::Checkbox("Docked", &appData.settings.docked))
ImGui::Checkbox("Docked", &appData.settings.docked);
if (appData.settings.docked != prevDocked)
{ {
closeMenu = true; closeMenu = true;
updateDocked(drawData, appData); updateDocked(drawData, appData);
@@ -294,8 +305,8 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
ImGui::TableNextColumn(); ImGui::TableNextColumn();
if (dev.state == DEVICE_STATE_ACTIVE) if (dev.state == DEVICE_STATE_ACTIVE)
{ {
// Log scale because it looks better (no actual reason for these exact numbers) static std::array<float, 2> meterValues{};
float meterValue = log10f(getMeterValue(dev.meterInterface) * 9. + 1.); UINT channelCount = getMeterValues(dev.meterInterface, meterValues);
auto drawList = ImGui::GetWindowDrawList(); auto drawList = ImGui::GetWindowDrawList();
ImVec2 windowPos = ImGui::GetWindowPos(); ImVec2 windowPos = ImGui::GetWindowPos();
@@ -306,21 +317,32 @@ ImVec2 audioDeviceWindow(ApplicationData& appData, std::vector<AudioDevice>& dev
const float linePaddingX = 3.; const float linePaddingX = 3.;
cursorPos.x += linePaddingX; cursorPos.x += linePaddingX;
// BG line
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX), lineY), IM_COL32(120, 120, 120, 255), 2.); drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX), lineY), IM_COL32(120, 120, 120, 255), 2.);
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValue, lineY), IM_COL32(200, 200, 255, 255), 3.);
// Channel lines
if (channelCount == 1)
{
drawList->AddLine(ImVec2(cursorPos.x, lineY), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[0], lineY), IM_COL32(200, 200, 255, 255), 3.);
}
if (channelCount == 2)
{
drawList->AddLine(ImVec2(cursorPos.x, lineY - 2), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[0], lineY - 2), IM_COL32(200, 200, 255, 255), 3.);
drawList->AddLine(ImVec2(cursorPos.x, lineY + 2), ImVec2(cursorPos.x + (space.x - 2. * linePaddingX) * meterValues[1], lineY + 2), IM_COL32(200, 200, 255, 255), 3.);
}
ImGui::SetNextItemWidth(space.x);
ImGui::PushID(&dev.id);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
float volume = getVolume(dev.volumeInterface); float volume = getVolume(dev.volumeInterface);
float prevVolume = volume; if (ImGui::SliderFloat("", &volume, 0., 1., "", ImGuiSliderFlags_NoRoundToFormat | ImGuiSliderFlags_AlwaysClamp))
ImGui::SetNextItemWidth(space.x);
ImGui::PushID(dev.device);
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0, 0, 0, 0));
ImGui::SliderFloat("", &volume, 0., 1., "", ImGuiSliderFlags_NoRoundToFormat | ImGuiSliderFlags_AlwaysClamp);
ImGui::PopStyleColor();
ImGui::PopID();
if (prevVolume != volume)
{ {
setVolume(dev.volumeInterface, volume); setVolume(dev.volumeInterface, volume);
} }
ImGui::PopStyleColor();
ImGui::PopID();
} }
// Defaults // Defaults
@@ -378,12 +400,13 @@ LRESULT CALLBACK trayIconEventHandler(int code, WPARAM wParam, LPARAM lParam)
if (data->message == WMAPP_NOTIFYCALLBACK) if (data->message == WMAPP_NOTIFYCALLBACK)
{ {
switch (data->lParam) auto id = HIWORD(data->lParam);
auto trayEvent = LOWORD(data->lParam);
if (id == TRAY_ID && trayEvent == WM_LBUTTONUP)
{ {
case NIN_SELECT:
glfwShowWindow(gDrawData->window); glfwShowWindow(gDrawData->window);
glfwRestoreWindow(gDrawData->window); glfwRestoreWindow(gDrawData->window);
break;
} }
} }
} }

View File

@@ -71,8 +71,8 @@ IDI_ICON1 ICON "kaiju.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1 FILEVERSION 1,0,4,1
PRODUCTVERSION 1,0,0,1 PRODUCTVERSION 1,0,4,1
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@@ -89,12 +89,12 @@ BEGIN
BEGIN BEGIN
VALUE "CompanyName", "Asuro" VALUE "CompanyName", "Asuro"
VALUE "FileDescription", "Audio Thingy" VALUE "FileDescription", "Audio Thingy"
VALUE "FileVersion", "1.0.0.1" VALUE "FileVersion", "1.0.4.1"
VALUE "InternalName", "AsuroTool.exe" VALUE "InternalName", "AsuroTool.exe"
VALUE "LegalCopyright", "Copyright (C) 2022" VALUE "LegalCopyright", "Copyright (C) 2022"
VALUE "OriginalFilename", "AsuroTool.exe" VALUE "OriginalFilename", "AsuroTool.exe"
VALUE "ProductName", "Audio Thingy" VALUE "ProductName", "Audio Thingy"
VALUE "ProductVersion", "1.0.0.1" VALUE "ProductVersion", "1.0.4.1"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@@ -97,6 +97,9 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -114,6 +117,9 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -127,6 +133,9 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -144,6 +153,9 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,6 +3,7 @@
#include <functiondiscoverykeys.h> #include <functiondiscoverykeys.h>
#include <endpointvolume.h> #include <endpointvolume.h>
#include <array>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
@@ -31,7 +32,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
deviceList.clear(); deviceList.clear();
HRESULT err; HRESULT err;
IMMDeviceCollection* deviceCollection = NULL; IMMDeviceCollection* deviceCollection = nullptr;
err = audioData.deviceEnumerator->EnumAudioEndpoints(deviceType, DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED, &deviceCollection); err = audioData.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;
@@ -40,7 +41,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
err = deviceCollection->GetCount(&deviceCount); err = deviceCollection->GetCount(&deviceCount);
if (isError(err, "Failed to count audio devices: ")) return; if (isError(err, "Failed to count audio devices: ")) return;
IMMDevice* defaultConsoleDevice = NULL; IMMDevice* defaultConsoleDevice = nullptr;
LPWSTR defaultConsoleId = nullptr; LPWSTR defaultConsoleId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eConsole, &defaultConsoleDevice);
if (!FAILED(err)) if (!FAILED(err))
@@ -48,7 +49,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultConsoleDevice->GetId(&defaultConsoleId); defaultConsoleDevice->GetId(&defaultConsoleId);
} }
IMMDevice* defaultMediaOutput = NULL; IMMDevice* defaultMediaOutput = nullptr;
LPWSTR defaultMediaId = nullptr; LPWSTR defaultMediaId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eMultimedia, &defaultMediaOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -56,7 +57,7 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
defaultMediaOutput->GetId(&defaultMediaId); defaultMediaOutput->GetId(&defaultMediaId);
} }
IMMDevice* defaultCommunicationOutput = NULL; IMMDevice* defaultCommunicationOutput = nullptr;
LPWSTR defaultCommunicationId = nullptr; LPWSTR defaultCommunicationId = nullptr;
err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput); err = audioData.deviceEnumerator->GetDefaultAudioEndpoint(deviceType, ERole::eCommunications, &defaultCommunicationOutput);
if (!FAILED(err)) if (!FAILED(err))
@@ -66,57 +67,35 @@ void loadAudioDevices(AudioData& audioData, std::vector<AudioDevice>& deviceList
for (UINT i = 0; i < deviceCount; i += 1) for (UINT i = 0; i < deviceCount; i += 1)
{ {
AudioDevice deviceData{}; IMMDevice* device;
err = deviceCollection->Item(i, &device);
err = deviceCollection->Item(i, &deviceData.device);
if (isError(err, std::stringstream("Failed to get device ") << i << ": ")) if (isError(err, std::stringstream("Failed to get device ") << i << ": "))
{ {
continue; continue;
} }
LPWSTR deviceId; LPWSTR deviceId;
err = deviceData.device->GetId(&deviceId); err = device->GetId(&deviceId);
isError(err, std::stringstream("Failed to get device id ") << i << ": "); if (!isError(err, std::stringstream("Failed to get device id ") << i << ": "))
deviceData.id = std::wstring(deviceId); {
AudioDevice audioDevice(device, deviceId);
IPropertyStore* propertyStore;
err = deviceData.device->OpenPropertyStore(STGM_READ, &propertyStore);
isError(err, std::stringstream("Failed to open device ") << i << "prop store: ");
PROPVARIANT deviceNameProp;
const wchar_t* deviceName;
err = getDevicePropertyString(propertyStore, PKEY_Device_FriendlyName, &deviceNameProp, deviceName);
isError(err, std::stringstream("Failed to read name of device ") << i << ": ");
deviceData.name = utf8Encode(deviceName);
err = deviceData.device->GetState(&deviceData.state);
isError(err, std::stringstream("Failed to reat state of device ") << i << ": ");
err = deviceData.device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.volumeInterface);
isError(err, "Failed to get audio endpoint volume interface: ");
err = deviceData.device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID*)&deviceData.meterInterface);
isError(err, "Failed to get audio meter interface: ");
if (defaultConsoleId) if (defaultConsoleId)
{ {
deviceData.isDefaultConsole = wcscmp(defaultConsoleId, deviceId) == 0; audioDevice.isDefaultConsole = wcscmp(defaultConsoleId, deviceId) == 0;
} }
if (defaultMediaId) if (defaultMediaId)
{ {
deviceData.isDefaultMedia = wcscmp(defaultMediaId, deviceId) == 0; audioDevice.isDefaultMedia = wcscmp(defaultMediaId, deviceId) == 0;
} }
if (defaultCommunicationId) if (defaultCommunicationId)
{ {
deviceData.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0; audioDevice.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0;
} }
deviceList.push_back(std::move(deviceData)); deviceList.push_back(std::move(audioDevice));
if (propertyStore)
{
propertyStore->Release();
} }
CoTaskMemFree(deviceId); CoTaskMemFree(deviceId);
} }
@@ -150,7 +129,7 @@ HRESULT getDevicePropertyString(IPropertyStore* propertyStore, const PROPERTYKEY
void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role) void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole role)
{ {
IPolicyConfigVista* pPolicyConfig; IPolicyConfigVista* pPolicyConfig = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig); HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID*)&pPolicyConfig);
if (!isError(hr, "Failed to set default audio device: ")) if (!isError(hr, "Failed to set default audio device: "))
@@ -178,13 +157,18 @@ void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume)
isError(hr, "Failed to set volume level: "); isError(hr, "Failed to set volume level: ");
} }
float getMeterValue(IAudioMeterInformation* meterInterface) UINT getMeterValues(IAudioMeterInformation* meterInterface, std::array<float, 2>& outLevels)
{ {
float volume; UINT channelCount;
if (FAILED(meterInterface->GetPeakValue(&volume))) if (FAILED(meterInterface->GetMeteringChannelCount(&channelCount)) || channelCount > 2) return 0;
{
volume = 0.; if (FAILED(meterInterface->GetChannelsPeakValues(channelCount, &outLevels[0]))) return 0;
return channelCount;
} }
return volume; void getVolumeLimit(IAudioEndpointVolume* volumeInterface, float* outMin, float* outMax)
{
float dummy;
volumeInterface->GetVolumeRange(outMin, outMax, &dummy);
} }

View File

@@ -14,4 +14,5 @@ void setDefaultAudioDevice(AudioData& audioData, const wchar_t* deviceId, ERole
float getVolume(IAudioEndpointVolume* volumeInterface); float getVolume(IAudioEndpointVolume* volumeInterface);
void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume); void setVolume(IAudioEndpointVolume* volumeInterface, float newVolume);
float getMeterValue(IAudioMeterInformation* meterInterface); UINT getMeterValues(IAudioMeterInformation* meterInterface, std::array<float, 2>& levels);
void getVolumeLimit(IAudioEndpointVolume* volumeInterface, float* outMin, float* outMax);

View File

@@ -34,7 +34,25 @@ HRESULT __stdcall AudioNotificationListener::OnDeviceAdded(LPCWSTR pwstrDeviceId
HRESULT result = audioData->deviceEnumerator->GetDevice(pwstrDeviceId, &newDevice); HRESULT result = audioData->deviceEnumerator->GetDevice(pwstrDeviceId, &newDevice);
if (SUCCEEDED(result) && newDevice != nullptr) if (SUCCEEDED(result) && newDevice != nullptr)
{ {
// TODO: add to device list IMMEndpoint* endpoint;
result = newDevice->QueryInterface(&endpoint);
if (SUCCEEDED(result) && endpoint != nullptr)
{
EDataFlow dataFlow;
result = endpoint->GetDataFlow(&dataFlow);
if (SUCCEEDED(result))
{
AudioDevice audioDevice(newDevice, pwstrDeviceId);
if (dataFlow == EDataFlow::eCapture)
{
audioData->recordingDevices.push_back(std::move(audioDevice));
}
else
{
audioData->playbackDevices.push_back(std::move(audioDevice));
}
}
}
} }
return result; return result;
@@ -50,7 +68,9 @@ HRESULT __stdcall AudioNotificationListener::OnDeviceRemoved(LPCWSTR pwstrDevice
if (wcscmp(deviceListIterator->id.c_str(), pwstrDeviceId) == 0) if (wcscmp(deviceListIterator->id.c_str(), pwstrDeviceId) == 0)
{ {
deviceListIterator = deviceList.erase(deviceListIterator); deviceListIterator = deviceList.erase(deviceListIterator);
continue;
} }
deviceListIterator++;
} }
}; };
@@ -126,7 +146,7 @@ HRESULT __stdcall AudioNotificationListener::QueryInterface(REFIID riid, void**
} }
else else
{ {
*ppvObject = NULL; *ppvObject = nullptr;
return E_NOINTERFACE; return E_NOINTERFACE;
} }

View File

@@ -74,19 +74,16 @@ void setAutostart(bool newValue)
HRESULT hr; HRESULT hr;
HKEY runKey = NULL; HKEY runKey = nullptr;
hr = RegCreateKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &runKey); hr = RegCreateKey(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", &runKey);
if (isError(hr, "Failed to find/create autostart run key: ")) return; if (isError(hr, "Failed to find/create autostart run key: ")) return;
if (newValue) if (newValue)
{ {
std::wstring appPath; std::wstring appPath;
appPath.resize(MAX_PATH_LENGTH); if (FAILED(getAppPath(appPath))) return;
hr = GetModuleFileName(NULL, &appPath[0], static_cast<DWORD>(appPath.size()));
if (isError(hr, "Failed to get executable name: ")) return;
appPath.resize(wcslen(appPath.data()));
hr = RegSetValueEx(runKey, KEY_APP_NAME, 0, REG_SZ, (BYTE*)appPath.c_str(), (appPath.size() + 1) * sizeof(wchar_t)); hr = RegSetValueEx(runKey, KEY_APP_NAME, 0, REG_SZ, (BYTE*)appPath.c_str(), static_cast<DWORD>((appPath.size() + 1) * sizeof(wchar_t)));
if (isError(hr, "Failed to write autostart key: ")) return; if (isError(hr, "Failed to write autostart key: ")) return;
} }
else else

View File

@@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include "Util.h" #include "Util.h"
#include "pathcch.h"
bool isError(const HRESULT result, const std::stringstream message) bool isError(const HRESULT result, const std::stringstream message)
{ {
@@ -29,3 +30,24 @@ std::string utf8Encode(const std::wstring& wstr)
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &resultString[0], sizeNeeded, NULL, NULL); WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &resultString[0], sizeNeeded, NULL, NULL);
return resultString; return resultString;
} }
HRESULT getAppPath(std::wstring& outPath)
{
const size_t MAX_PATH_LENGTH = 32767;
outPath.resize(MAX_PATH_LENGTH);
HRESULT hr = GetModuleFileName(NULL, &outPath[0], static_cast<DWORD>(outPath.size()));
if (isError(hr, "Failed to get executable name: ")) return hr;
outPath.resize(wcslen(outPath.data()));
return hr;
}
HRESULT getAppDir(std::wstring& outPath)
{
HRESULT hr = getAppPath(outPath);
if (FAILED(hr)) return hr;
hr = PathCchRemoveFileSpec(&outPath[0], static_cast<DWORD>(outPath.size()));
if (isError(hr, "Failed to get executable dir: ")) return hr;
outPath.resize(wcslen(outPath.data()));
return hr;
}

View File

@@ -6,3 +6,5 @@
bool isError(const HRESULT result, const std::stringstream message); bool isError(const HRESULT result, const std::stringstream message);
bool isError(const HRESULT result, const char* message); bool isError(const HRESULT result, const char* message);
std::string utf8Encode(const std::wstring& wstr); std::string utf8Encode(const std::wstring& wstr);
HRESULT getAppPath(std::wstring& outPath);
HRESULT getAppDir(std::wstring& outPath);

View File

@@ -107,6 +107,8 @@
<Lib> <Lib>
<AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -130,6 +132,8 @@
<Lib> <Lib>
<AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -149,6 +153,8 @@
<Lib> <Lib>
<AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -172,6 +178,8 @@
<Lib> <Lib>
<AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories> <AdditionalLibraryDirectories>E:\Code\glfw-3.3.7.bin.WIN64\lib-vc2022;D:\Applications\VulkanSDK\1.2.182.0\Lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>vulkan-1.lib;glfw3.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Lib> </Lib>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>

View File

@@ -96,6 +96,8 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@@ -112,6 +114,8 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@@ -124,6 +128,8 @@
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -140,6 +146,8 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding> <EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences> <OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
<IgnoreSpecificDefaultLibraries>
</IgnoreSpecificDefaultLibraries>
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>