Skip to content

Commit

Permalink
Use windows sdk to set audio device, dropped AudioDeviceCMDlets
Browse files Browse the repository at this point in the history
  • Loading branch information
Odizinne committed Nov 8, 2024
1 parent 4b0b89c commit 170616d
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 177 deletions.
99 changes: 67 additions & 32 deletions AudioManager/AudioManager.cpp
Original file line number Diff line number Diff line change
@@ -1,39 +1,88 @@
#include "AudioManager.h"
#include <QProcess>
#include <chrono>
#include <stdexcept>
#include <thread>
#include <QDebug>
#include <mmdeviceapi.h>
#include <Functiondiscoverykeys_devpkey.h>
#include <comdef.h>
#include <atlbase.h>
#include "PolicyConfig.h"

std::string executeCommand(QString command)
bool setDefaultAudioOutputDevice(const QString &deviceId)
{
QProcess process;
process.setProgram("powershell.exe");
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = nullptr;
IMMDevice *defaultDevice = nullptr;
IPolicyConfig *policyConfig = nullptr;
IPolicyConfigVista *policyConfigVista = nullptr;

// Initialize COM library
hr = CoInitialize(nullptr);
if (FAILED(hr)) {
qDebug() << "Failed to initialize COM library";
return false;
}

// Create device enumerator
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&deviceEnumerator));
if (FAILED(hr)) {
qDebug() << "Failed to create device enumerator";
CoUninitialize();
return false;
}

QStringList arguments;
arguments << "-NoProfile" << "-Command" << command;
process.setArguments(arguments);
// Get the device by ID
hr = deviceEnumerator->GetDevice(reinterpret_cast<LPCWSTR>(deviceId.utf16()), &defaultDevice);
if (FAILED(hr)) {
qDebug() << "Failed to get device by ID";
deviceEnumerator->Release();
CoUninitialize();
return false;
}

process.start();
if (!process.waitForStarted()) {
throw std::runtime_error("Failed to start PowerShell process!");
// Attempt to get IPolicyConfig interface
hr = CoCreateInstance(__uuidof(CPolicyConfigClient), nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&policyConfig));
if (FAILED(hr)) {
// If IPolicyConfig is unavailable, try IPolicyConfigVista
hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient), nullptr, CLSCTX_ALL,
IID_PPV_ARGS(&policyConfigVista));
if (FAILED(hr)) {
qDebug() << "Failed to create PolicyConfig interface";
defaultDevice->Release();
deviceEnumerator->Release();
CoUninitialize();
return false;
}
}
if (!process.waitForFinished()) {
throw std::runtime_error("PowerShell process did not finish!");

// Set as default device for all audio roles (Console, Multimedia, Communications)
if (policyConfig) {
hr = policyConfig->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eConsole);
policyConfig->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eMultimedia);
policyConfig->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eCommunications);
} else if (policyConfigVista) {
hr = policyConfigVista->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eConsole);
policyConfigVista->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eMultimedia);
policyConfigVista->SetDefaultEndpoint(reinterpret_cast<LPCWSTR>(deviceId.utf16()), eCommunications);
}

QByteArray output = process.readAllStandardOutput();
QByteArray error = process.readAllStandardError();
// Release resources
if (policyConfig) policyConfig->Release();
if (policyConfigVista) policyConfigVista->Release();
defaultDevice->Release();
deviceEnumerator->Release();
CoUninitialize();

if (!error.isEmpty()) {
return error.toStdString();
if (FAILED(hr)) {
qDebug() << "Failed to set default audio output device";
return false;
}

return output.toStdString();
qDebug() << "Default audio output device set to:" << deviceId;
return true;
}


Expand All @@ -42,15 +91,11 @@ void AudioManager::setAudioDevice(QString ID)
bool deviceFound = false;
int maxRetries = 10;
int retryCount = 0;
std::string result;
qDebug() << "Will try to set output to:" << ID;

while (retryCount < maxRetries) {
// Correctly add double quotes around the ID, without adding escape characters
QString setCommand = QString("Set-AudioDevice -ID \"") + ID + "\"";
result = executeCommand(setCommand);

if (result.find("Error") == std::string::npos) {
if (setDefaultAudioOutputDevice(ID)) {
deviceFound = true;
qDebug() << "Switched audio output to:" << ID;
break;
Expand All @@ -70,7 +115,6 @@ void AudioManager::setAudioDevice(QString ID)
}
}

// AI generated
QList<Device> AudioManager::ListAudioOutputDevices()
{
QList<Device> devices;
Expand Down Expand Up @@ -168,12 +212,3 @@ QList<Device> AudioManager::ListAudioOutputDevices()
return devices;
}

void AudioManager::PrintAudioOutputDevices()
{
QList<Device> devices = ListAudioOutputDevices();

for (const Device& device : devices) {
qDebug() << "Device Name: " << device.name;
qDebug() << "Device ID: " << device.ID;
}
}
1 change: 0 additions & 1 deletion AudioManager/AudioManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ namespace AudioManager
{
void setAudioDevice(QString ID);
QList<Device> ListAudioOutputDevices();
void PrintAudioOutputDevices();
};

#endif // AUDIOMANAGER_H
149 changes: 149 additions & 0 deletions AudioManager/PolicyConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#pragma once

#include <Unknwn.h> // For IUnknown
#include <mmdeviceapi.h> // For ERole
#include <propidl.h> // For PROPVARIANT
#include <Functiondiscoverykeys_devpkey.h> // For PROPERTYKEY
#include <audioclient.h> // For WAVEFORMATEX

// Define IPolicyConfig interface
struct DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8") IPolicyConfig : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetMixFormat(
PCWSTR,
WAVEFORMATEX**
) = 0;

virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
PCWSTR,
INT,
WAVEFORMATEX**
) = 0;

virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
PCWSTR
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
PCWSTR,
WAVEFORMATEX*,
WAVEFORMATEX*
) = 0;

virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
PCWSTR,
INT,
PINT64,
PINT64
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
PCWSTR,
PINT64
) = 0;

virtual HRESULT STDMETHODCALLTYPE GetShareMode(
PCWSTR,
struct DeviceShareMode*
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetShareMode(
PCWSTR,
struct DeviceShareMode*
) = 0;

virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
PCWSTR,
const PROPERTYKEY&,
PROPVARIANT*
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
PCWSTR,
const PROPERTYKEY&,
const PROPVARIANT&
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
PCWSTR wszDeviceId,
ERole eRole
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
PCWSTR,
INT
) = 0;
};

// CLSID for IPolicyConfig
class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient;

// Define IPolicyConfigVista interface
struct DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") IPolicyConfigVista : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE GetMixFormat(
PCWSTR,
WAVEFORMATEX**
) = 0; // Not available on Windows 7, use method from IPolicyConfig

virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
PCWSTR,
INT,
WAVEFORMATEX**
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
PCWSTR,
WAVEFORMATEX*,
WAVEFORMATEX*
) = 0;

virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
PCWSTR,
INT,
PINT64,
PINT64
) = 0; // Not available on Windows 7, use method from IPolicyConfig

virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
PCWSTR,
PINT64
) = 0; // Not available on Windows 7, use method from IPolicyConfig

virtual HRESULT STDMETHODCALLTYPE GetShareMode(
PCWSTR,
struct DeviceShareMode*
) = 0; // Not available on Windows 7, use method from IPolicyConfig

virtual HRESULT STDMETHODCALLTYPE SetShareMode(
PCWSTR,
struct DeviceShareMode*
) = 0; // Not available on Windows 7, use method from IPolicyConfig

virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
PCWSTR,
const PROPERTYKEY&,
PROPVARIANT*
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
PCWSTR,
const PROPERTYKEY&,
const PROPVARIANT&
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
PCWSTR wszDeviceId,
ERole eRole
) = 0;

virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
PCWSTR,
INT
) = 0; // Not available on Windows 7, use method from IPolicyConfig
};

// CLSID for IPolicyConfigVista
class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") CPolicyConfigVistaClient;
1 change: 1 addition & 0 deletions BigPictureTV.pro
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ SOURCES += \

HEADERS += \
AudioManager/AudioManager.h \
AudioManager/PolicyConfig.h \
BigPictureTV/BigPictureTV.h \
Configurator/Configurator.h \
NightLightSwitcher/NightLightSwitcher.h \
Expand Down
Loading

0 comments on commit 170616d

Please sign in to comment.