Compare commits
5 Commits
1.0.2
...
5f5e24fa9c
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f5e24fa9c | |||
| cdffa1b50b | |||
| eec9e25e12 | |||
| 3b4853acda | |||
| cea13986f0 |
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -304,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();
|
||||||
@@ -316,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
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ IDI_ICON1 ICON "kaiju.ico"
|
|||||||
//
|
//
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
VS_VERSION_INFO VERSIONINFO
|
||||||
FILEVERSION 1,0,1,1
|
FILEVERSION 1,0,4,1
|
||||||
PRODUCTVERSION 1,0,1,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.1.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.1.1"
|
VALUE "ProductVersion", "1.0.4.1"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
BLOCK "VarFileInfo"
|
BLOCK "VarFileInfo"
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
deviceData.isDefaultConsole = wcscmp(defaultConsoleId, deviceId) == 0;
|
AudioDevice audioDevice(device, deviceId);
|
||||||
}
|
|
||||||
if (defaultMediaId)
|
if (defaultConsoleId)
|
||||||
{
|
{
|
||||||
deviceData.isDefaultMedia = wcscmp(defaultMediaId, deviceId) == 0;
|
audioDevice.isDefaultConsole = wcscmp(defaultConsoleId, deviceId) == 0;
|
||||||
}
|
}
|
||||||
if (defaultCommunicationId)
|
if (defaultMediaId)
|
||||||
{
|
{
|
||||||
deviceData.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0;
|
audioDevice.isDefaultMedia = wcscmp(defaultMediaId, deviceId) == 0;
|
||||||
|
}
|
||||||
|
if (defaultCommunicationId)
|
||||||
|
{
|
||||||
|
audioDevice.isDefaultCommunication = wcscmp(defaultCommunicationId, deviceId) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceList.push_back(std::move(audioDevice));
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceList.push_back(std::move(deviceData));
|
|
||||||
|
|
||||||
if (propertyStore)
|
|
||||||
{
|
|
||||||
propertyStore->Release();
|
|
||||||
}
|
|
||||||
CoTaskMemFree(deviceId);
|
CoTaskMemFree(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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.;
|
|
||||||
}
|
|
||||||
|
|
||||||
return volume;
|
if (FAILED(meterInterface->GetChannelsPeakValues(channelCount, &outLevels[0]))) return 0;
|
||||||
|
|
||||||
|
return channelCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void getVolumeLimit(IAudioEndpointVolume* volumeInterface, float* outMin, float* outMax)
|
||||||
|
{
|
||||||
|
float dummy;
|
||||||
|
volumeInterface->GetVolumeRange(outMin, outMax, &dummy);
|
||||||
}
|
}
|
||||||
@@ -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);
|
||||||
@@ -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++;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user