Created
December 11, 2024 18:49
-
-
Save ParticleG/45ba8bae68f6f5ae7beac1888c73ff16 to your computer and use it in GitHub Desktop.
Windows Volume Manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <mmdeviceapi.h> | |
#include <VolumeManager.h> | |
using namespace std; | |
namespace { | |
IMMDevice* getDefaultIMMDevice() { | |
IMMDevice* defaultDevice = nullptr; | |
IMMDeviceEnumerator* deviceEnumerator = nullptr; | |
if (const auto result = CoCreateInstance( | |
__uuidof(MMDeviceEnumerator), | |
nullptr, | |
CLSCTX_INPROC_SERVER, | |
__uuidof(IMMDeviceEnumerator), | |
reinterpret_cast<LPVOID *>(&deviceEnumerator) | |
); result != S_OK) { | |
switch (result) { | |
case E_NOINTERFACE: { | |
cout << "The specified class does not implement the requested interface." << endl; | |
break; | |
} | |
case E_POINTER: { | |
cout << "The ppv parameter is NULL." << endl; | |
break; | |
} | |
case CLASS_E_NOAGGREGATION: { | |
cout << "This class cannot be created as part of an aggregate." << endl; | |
break; | |
} | |
case REGDB_E_CLASSNOTREG: { | |
cout << "A specified class is not registered in the registration database." << endl; | |
break; | |
} | |
default: { | |
cout << "Unknown error." << endl; | |
break; | |
} | |
} | |
} | |
if (const auto result = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice); | |
result != S_OK) { | |
switch (result) { | |
case E_POINTER: { | |
cout << "The ppDevice parameter is NULL." << endl; | |
break; | |
} | |
case E_INVALIDARG: { | |
cout << "Parameter dataFlow or role is out of range." << endl; | |
break; | |
} | |
case E_NOTFOUND: { | |
cout << "No device is available." << endl; | |
break; | |
} | |
case E_OUTOFMEMORY: { | |
cout << "Out of memory." << endl; | |
break; | |
} | |
default: { | |
cout << "Unknown error." << endl; | |
break; | |
} | |
} | |
} | |
deviceEnumerator->Release(); | |
return defaultDevice; | |
} | |
IAudioEndpointVolume* getDefaultEndpointVolume() { | |
IMMDevice* defaultDevice = getDefaultIMMDevice(); | |
IAudioEndpointVolume* endpointVolume = nullptr; | |
if (const auto result = defaultDevice->Activate( | |
__uuidof(IAudioEndpointVolume), | |
CLSCTX_INPROC_SERVER, | |
nullptr, | |
reinterpret_cast<LPVOID *>(&endpointVolume) | |
); result != S_OK) { | |
switch (result) { | |
case E_NOINTERFACE: { | |
cout << "The object does not support the requested interface type." << endl; | |
break; | |
} | |
case E_POINTER: { | |
cout << "Parameter ppInterface is NULL." << endl; | |
break; | |
} | |
case E_INVALIDARG: { | |
cout << "The pActivationParams parameter must be NULL for the specified interface." << endl; | |
break; | |
} | |
case E_OUTOFMEMORY: { | |
cout << "Out of memory." << endl; | |
break; | |
} | |
default: { | |
cout << "Unknown error." << endl; | |
break; | |
} | |
} | |
} | |
defaultDevice->Release(); | |
return endpointVolume; | |
} | |
} | |
VolumeManager::VolumeManager() { | |
if (const auto result = CoInitializeEx(nullptr, COINIT_MULTITHREADED); result != S_OK) { | |
switch (result) { | |
case S_FALSE: { | |
cout << format("[{}] COM library is already initialized.", __FUNCTION__) << endl; | |
break; | |
} | |
case RPC_E_CHANGED_MODE: { | |
cout << format("[{}] This thread is an MTA.", __FUNCTION__) << endl; | |
} | |
case E_OUTOFMEMORY: { | |
throw runtime_error(format("[{}] Out of memory.", __FUNCTION__)); | |
} | |
case E_INVALIDARG: { | |
throw runtime_error(format("[{}] Parameter 'pvReserved' must be nullptr.", __FUNCTION__)); | |
} | |
case E_UNEXPECTED: { | |
throw runtime_error(format("[{}] An unexpected error occurred.", __FUNCTION__)); | |
} | |
default: { | |
throw runtime_error(format("[{}] An unknown error occurred.", __FUNCTION__)); | |
} | |
} | |
cout << "Failed to initialize COM library" << endl; | |
exit(1); | |
} | |
_endpointVolume.reset(getDefaultEndpointVolume()); | |
} | |
VolumeManager::~VolumeManager() { | |
_endpointVolume->Release(); | |
_endpointVolume.release(); | |
CoUninitialize(); | |
} | |
float VolumeManager::getMasterVolumeLevel(const VolumeType volumeType) const { | |
float volume{}; | |
switch (volumeType) { | |
case VolumeType::Decibels: { | |
if (const auto result = _endpointVolume->GetMasterVolumeLevel(&volume); | |
result != S_OK) { | |
switch (result) { | |
case E_POINTER: { | |
throw runtime_error(format("[{}] The pfLevelDB parameter is NULL.", __FUNCTION__)); | |
} | |
default: { | |
throw runtime_error(format("[{}] Unknown error.", __FUNCTION__)); | |
} | |
} | |
} | |
break; | |
} | |
case VolumeType::Scalar: { | |
if (const auto result = _endpointVolume->GetMasterVolumeLevelScalar(&volume); | |
result != S_OK) { | |
switch (result) { | |
case E_POINTER: { | |
throw runtime_error(format("[{}] The pfLevelDB parameter is NULL.", __FUNCTION__)); | |
} | |
default: { | |
throw runtime_error(format("[{}] Unknown error.", __FUNCTION__)); | |
} | |
} | |
} | |
break; | |
} | |
} | |
return volume; | |
} | |
void VolumeManager::setMasterVolumeLevel(const float volume, const VolumeType volumeType) const { | |
switch (volumeType) { | |
case VolumeType::Decibels: { | |
if (const auto result = _endpointVolume->SetMasterVolumeLevel(volume, nullptr); | |
result != S_OK) { | |
switch (result) { | |
case E_INVALIDARG: { | |
throw runtime_error(format( | |
"[{}] Parameter fLevelDB lies outside of the volume range supported by the device.", | |
__FUNCTION__)); | |
} | |
case E_OUTOFMEMORY: { | |
throw runtime_error(format("[{}] Out of memory.", __FUNCTION__)); | |
} | |
default: { | |
throw runtime_error(format("[{}] Unknown error.", __FUNCTION__)); | |
} | |
} | |
} | |
break; | |
} | |
case VolumeType::Scalar: { | |
if (const auto result = _endpointVolume->SetMasterVolumeLevelScalar(volume, nullptr); | |
result != S_OK) { | |
switch (result) { | |
case E_INVALIDARG: { | |
throw runtime_error(format("[{}] Parameter fLevel is outside the range from 0.0 to 1.0.", | |
__FUNCTION__)); | |
} | |
case E_OUTOFMEMORY: { | |
throw runtime_error(format("[{}] Out of memory.", __FUNCTION__)); | |
} | |
default: { | |
throw runtime_error(format("[{}] Unknown error.", __FUNCTION__)); | |
} | |
} | |
} | |
break; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <endpointvolume.h> | |
class VolumeManager { | |
public: | |
enum class VolumeType { | |
Decibels, | |
Scalar | |
}; | |
VolumeManager(); | |
~VolumeManager(); | |
float getMasterVolumeLevel(VolumeType volumeType = VolumeType::Scalar) const; | |
void setMasterVolumeLevel(float volume, VolumeType volumeType = VolumeType::Scalar) const; | |
private: | |
std::unique_ptr<IAudioEndpointVolume> _endpointVolume; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment