From c8d089957d643526461616f556c1dab60ceb2e04 Mon Sep 17 00:00:00 2001 From: matzman666 Date: Mon, 13 Feb 2017 22:19:42 +0100 Subject: [PATCH] Added combo-boxes to allow audio device selection. --- bin/win64/res/qml/AudioPage.qml | 158 +++++++----- src/tabcontrollers/AudioManager.h | 14 +- src/tabcontrollers/AudioTabController.cpp | 234 ++++++++++++++---- src/tabcontrollers/AudioTabController.h | 60 +++-- .../audiomanager/AudioManagerWindows.cpp | 168 ++++++++++++- .../audiomanager/AudioManagerWindows.h | 22 +- .../audiomanager/IPolicyConfig.h | 177 +++++++++++++ 7 files changed, 699 insertions(+), 134 deletions(-) create mode 100644 src/tabcontrollers/audiomanager/IPolicyConfig.h diff --git a/bin/win64/res/qml/AudioPage.qml b/bin/win64/res/qml/AudioPage.qml index 80b3bd4..635da21 100644 --- a/bin/win64/res/qml/AudioPage.qml +++ b/bin/win64/res/qml/AudioPage.qml @@ -8,6 +8,8 @@ MyStackViewPage { width: 1200 headerText: "Audio Settings" + property bool componentCompleted: false + MyDialogOkPopup { id: audioMessageDialog @@ -77,17 +79,43 @@ MyStackViewPage { } content: ColumnLayout { - spacing: 92 + spacing: 68 + + RowLayout { + MyText { + text: "Playback Device: " + Layout.preferredWidth: 210 + } + MyComboBox { + id: audioPlaybackNameCombo + Layout.maximumWidth: 900 + Layout.minimumWidth: 900 + Layout.preferredWidth: 900 + onCurrentIndexChanged: { + if (componentCompleted) { + AudioTabController.setPlaybackDeviceIndex(currentIndex) + } + } + } + } ColumnLayout { spacing: 18 RowLayout { MyText { text: "Mirror Device: " + Layout.preferredWidth: 210 } - MyText { - id: audioMirrorNameText - text: "" + MyComboBox { + id: audioMirrorNameCombo + Layout.maximumWidth: 900 + Layout.minimumWidth: 900 + Layout.preferredWidth: 900 + onCurrentIndexChanged: { + if (componentCompleted) { + AudioTabController.setMirrorDeviceIndex(currentIndex - 1) + } + } } } RowLayout { @@ -159,10 +187,18 @@ MyStackViewPage { RowLayout { MyText { text: "Microphone: " + Layout.preferredWidth: 210 } - MyText { - id: audioMicNameText - text: "" + MyComboBox { + id: audioMicNameCombo + Layout.maximumWidth: 900 + Layout.minimumWidth: 900 + Layout.preferredWidth: 900 + onCurrentIndexChanged: { + if (componentCompleted) { + AudioTabController.setMicDeviceIndex(currentIndex) + } + } } } RowLayout { @@ -338,64 +374,80 @@ MyStackViewPage { } Component.onCompleted: { - reloadPttProfiles() - if (AudioTabController.mirrorPresent) { - audioMirrorVolumeMinusButton.enabled = true - audioMirrorVolumeSlider.enabled = true - audioMirrorVolumePlusButton.enabled = true - audioMirrorMuteToggle.enabled = true - audioMirrorNameText.text = AudioTabController.mirrorDevName - audioMirrorVolumeSlider.value = AudioTabController.mirrorVolume - audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted - } else { + var devs1 = [] + var devs2 = [""] + var playbackDeviceCount = AudioTabController.getPlaybackDeviceCount() + for (var i = 0; i < playbackDeviceCount; i++) { + var name = AudioTabController.getPlaybackDeviceName(i) + devs1.push(name) + devs2.push(name) + } + audioPlaybackNameCombo.model = devs1 + audioPlaybackNameCombo.currentIndex = AudioTabController.playbackDeviceIndex + audioMirrorNameCombo.model = devs2 + if (AudioTabController.mirrorDeviceIndex < 0) { + audioMirrorNameCombo.currentIndex = 0 audioMirrorVolumeMinusButton.enabled = false audioMirrorVolumeSlider.enabled = false audioMirrorVolumePlusButton.enabled = false audioMirrorMuteToggle.enabled = false - audioMirrorNameText.text = "" - } - if (AudioTabController.micPresent) { - audioMicVolumeMinusButton.enabled = true - audioMicVolumeSlider.enabled = true - audioMicVolumePlusButton.enabled = true - audioMicMuteToggle.enabled = true - audioMicNameText.text = AudioTabController.micDevName - audioMicVolumeSlider.value = AudioTabController.micVolume - audioMicMuteToggle.checked = AudioTabController.micMuted } else { + audioMirrorVolumeMinusButton.enabled = true + audioMirrorVolumeSlider.enabled = true + audioMirrorVolumePlusButton.enabled = true + audioMirrorMuteToggle.enabled = true + audioMirrorVolumeSlider.value = AudioTabController.mirrorVolume + audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted + audioMirrorNameCombo.currentIndex = AudioTabController.mirrorDeviceIndex + 1 + } + var devs3 = [] + var micDeviceCount = AudioTabController.getRecordingDeviceCount() + for (var i = 0; i < micDeviceCount; i++) { + var name = AudioTabController.getRecordingDeviceName(i) + devs3.push(name) + } + audioMicNameCombo.model = devs3 + if (AudioTabController.micDeviceIndex < 0) { + audioMicNameCombo.currentIndex = 0 audioMicVolumeMinusButton.enabled = false audioMicVolumeSlider.enabled = false audioMicVolumePlusButton.enabled = false audioMicMuteToggle.enabled = false - audioMicNameText.text = "" + } else { + audioMicVolumeMinusButton.enabled = true + audioMicVolumeSlider.enabled = true + audioMicVolumePlusButton.enabled = true + audioMicMuteToggle.enabled = true + audioMicVolumeSlider.value = AudioTabController.micVolume + audioMicMuteToggle.checked = AudioTabController.micMuted + audioMicNameCombo.currentIndex = AudioTabController.micDeviceIndex } + reloadPttProfiles() audioPttEnabledToggle.checked = AudioTabController.pttEnabled audioPttLeftControllerToggle.checked = AudioTabController.pttLeftControllerEnabled audioPttRightControllerToggle.checked = AudioTabController.pttRightControllerEnabled audioPttShowNotificationToggle.checked = AudioTabController.pttShowNotification + componentCompleted = true } Connections { target: AudioTabController - onMirrorPresentChanged: { - if (value) { - audioMirrorVolumeMinusButton.enabled = true - audioMirrorVolumeSlider.enabled = true - audioMirrorVolumePlusButton.enabled = true - audioMirrorMuteToggle.enabled = true - } else { + onPlaybackDeviceIndexChanged: { + audioPlaybackNameCombo.currentIndex = index + } + onMirrorDeviceIndexChanged: { + if (index < 0) { + audioMirrorNameCombo.currentIndex = 0 audioMirrorVolumeMinusButton.enabled = false audioMirrorVolumeSlider.enabled = false audioMirrorVolumePlusButton.enabled = false audioMirrorMuteToggle.enabled = false - audioMirrorNameText.text = "" - } - } - onMirrorDevNameChanged: { - if (AudioTabController.mirrorPresent) { - audioMirrorNameText.text = AudioTabController.mirrorDevName } else { - audioMirrorNameText.text = "" + audioMirrorVolumeMinusButton.enabled = true + audioMirrorVolumeSlider.enabled = true + audioMirrorVolumePlusButton.enabled = true + audioMirrorMuteToggle.enabled = true + audioMirrorNameCombo.currentIndex = index + 1 } } onMirrorVolumeChanged: { @@ -404,25 +456,19 @@ MyStackViewPage { onMirrorMutedChanged: { audioMirrorMuteToggle.checked = AudioTabController.mirrorMuted } - onMicPresentChanged: { - if (value) { - audioMicVolumeMinusButton.enabled = true - audioMicVolumeSlider.enabled = true - audioMicVolumePlusButton.enabled = true - audioMicMuteToggle.enabled = true - } else { + onMicDeviceIndexChanged: { + if (index < 0) { + audioMicNameCombo.currentIndex = 0 audioMicVolumeMinusButton.enabled = false audioMicVolumeSlider.enabled = false audioMicVolumePlusButton.enabled = false audioMicMuteToggle.enabled = false - audioMicNameText.text = "" - } - } - onMicDevNameChanged: { - if (AudioTabController.micPresent) { - audioMicNameText.text = AudioTabController.micDevName } else { - audioMicNameText.text = "" + audioMicVolumeMinusButton.enabled = true + audioMicVolumeSlider.enabled = true + audioMicVolumePlusButton.enabled = true + audioMicMuteToggle.enabled = true + audioMicNameCombo.currentIndex = index } } onMicVolumeChanged: { diff --git a/src/tabcontrollers/AudioManager.h b/src/tabcontrollers/AudioManager.h index 41e7d5f..9cc9dda 100644 --- a/src/tabcontrollers/AudioManager.h +++ b/src/tabcontrollers/AudioManager.h @@ -1,6 +1,7 @@ #pragma once #include +#include // application namespace namespace advsettings { @@ -14,21 +15,30 @@ public: virtual void init(AudioTabController* controller) = 0; - virtual void setMirrorDevice(const std::string& id) = 0; - virtual void deleteMirrorDevice() = 0; + virtual void setPlaybackDevice(const std::string& id, bool notify = true) = 0; + virtual std::string getPlaybackDevName() = 0; + virtual std::string getPlaybackDevId() = 0; + + virtual void setMirrorDevice(const std::string& id, bool notify = true) = 0; virtual bool isMirrorValid() = 0; virtual std::string getMirrorDevName() = 0; + virtual std::string getMirrorDevId() = 0; virtual float getMirrorVolume() = 0; virtual bool setMirrorVolume(float value) = 0; virtual bool getMirrorMuted() = 0; virtual bool setMirrorMuted(bool value) = 0; + virtual void setMicDevice(const std::string& id, bool notify = true) = 0; virtual bool isMicValid() = 0; virtual std::string getMicDevName() = 0; + virtual std::string getMicDevId() = 0; virtual float getMicVolume() = 0; virtual bool setMicVolume(float value) = 0; virtual bool getMicMuted() = 0; virtual bool setMicMuted(bool value) = 0; + + virtual std::vector> getRecordingDevices() = 0; + virtual std::vector> getPlaybackDevices() = 0; }; } diff --git a/src/tabcontrollers/AudioTabController.cpp b/src/tabcontrollers/AudioTabController.cpp index c437c6c..e393970 100644 --- a/src/tabcontrollers/AudioTabController.cpp +++ b/src/tabcontrollers/AudioTabController.cpp @@ -11,21 +11,23 @@ namespace advsettings { vr::EVRSettingsError vrSettingsError; audioManager.reset(new AudioManagerWindows()); audioManager->init(this); - char mirrorDeviceId[1024]; - vr::VRSettings()->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackMirrorDevice_String, mirrorDeviceId, 1024, &vrSettingsError); + m_playbackDevices = audioManager->getPlaybackDevices(); + m_recordingDevices = audioManager->getRecordingDevices(); + findPlaybackDeviceIndex(audioManager->getPlaybackDevId(), false); + char deviceId[1024]; + vr::VRSettings()->GetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackMirrorDevice_String, deviceId, 1024, &vrSettingsError); if (vrSettingsError != vr::VRSettingsError_None) { LOG(WARNING) << "Could not read \"" << vr::k_pch_audio_OnPlaybackMirrorDevice_String << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + audioManager->setMirrorDevice(deviceId); + findMirrorDeviceIndex(audioManager->getMirrorDevId(), false); + lastMirrorDevId = deviceId; + m_mirrorVolume = audioManager->getMirrorVolume(); + m_mirrorMuted = audioManager->getMirrorMuted(); } - audioManager->setMirrorDevice(mirrorDeviceId); - lastMirrorDevId = mirrorDeviceId; - m_micPresent = audioManager->isMicValid(); + findMicDeviceIndex(audioManager->getMicDevId(), false); m_micVolume = audioManager->getMicVolume(); m_micMuted = audioManager->getMicMuted(); - m_micDevName = QString::fromStdString(audioManager->getMicDevName()); - m_mirrorPresent = audioManager->isMirrorValid(); - m_mirrorVolume = audioManager->getMirrorVolume(); - m_mirrorMuted = audioManager->getMirrorMuted(); - m_mirrorDevName = QString::fromStdString(audioManager->getMirrorDevName()); reloadPttProfiles(); reloadPttConfig(); eventLoopTick(); @@ -58,16 +60,6 @@ namespace advsettings { } - bool AudioTabController::mirrorPresent() const { - return m_mirrorPresent; - } - - - QString AudioTabController::mirrorDevName() const { - return m_mirrorDevName; - } - - float AudioTabController::mirrorVolume() const { return m_mirrorVolume; } @@ -78,16 +70,6 @@ namespace advsettings { } - bool AudioTabController::micPresent() const { - return m_micPresent; - } - - - QString AudioTabController::micDevName() const { - return m_micDevName; - } - - float AudioTabController::micVolume() const { return m_micVolume; } @@ -110,24 +92,14 @@ namespace advsettings { } if (lastMirrorDevId.compare(mirrorDeviceId) != 0) { audioManager->setMirrorDevice(mirrorDeviceId); - if (audioManager->isMirrorValid()) { - if (!m_mirrorPresent) { - m_mirrorPresent = true; - emit mirrorPresentChanged(m_mirrorPresent); - } - m_mirrorDevName = QString::fromStdString(audioManager->getMirrorDevName()); - emit mirrorDevNameChanged(m_mirrorDevName); - } else if (m_mirrorPresent) { - m_mirrorPresent = false; - emit mirrorPresentChanged(m_mirrorPresent); - } + findMirrorDeviceIndex(audioManager->getMirrorDevId()); lastMirrorDevId = mirrorDeviceId; } - if (m_mirrorPresent) { + if (m_mirrorDeviceIndex >= 0) { setMirrorVolume(audioManager->getMirrorVolume()); setMirrorMuted(audioManager->getMirrorMuted()); } - if (m_micPresent) { + if (m_recordingDeviceIndex >= 0) { setMicVolume(audioManager->getMicVolume()); setMicMuted(audioManager->getMicMuted()); } @@ -212,20 +184,172 @@ namespace advsettings { } void AudioTabController::onNewRecordingDevice() { - if (audioManager->isMicValid()) { - if (!m_micPresent) { - m_micPresent = true; - emit micPresentChanged(m_micPresent); - } - setMicVolume(audioManager->getMicVolume()); - setMicMuted(audioManager->getMicMuted()); - m_micDevName = QString::fromStdString(audioManager->getMicDevName()); + findMicDeviceIndex(audioManager->getMicDevId()); + } - emit micDevNameChanged(m_micDevName); + void AudioTabController::onNewPlaybackDevice() { + findPlaybackDeviceIndex(audioManager->getPlaybackDevId()); + } + + void AudioTabController::onNewMirrorDevice() { + } + + void AudioTabController::onDeviceAdded() { + } + + void AudioTabController::onDeviceRemoved() { + } + + int AudioTabController::getPlaybackDeviceCount() { + return (int)m_playbackDevices.size(); + } + + QString AudioTabController::getPlaybackDeviceName(int index) { + if (index < m_playbackDevices.size()) { + return QString::fromStdString(m_playbackDevices[index].second); } else { - if (m_micPresent) { - m_micPresent = false; - emit micPresentChanged(m_micPresent); + return ""; + } + } + + int AudioTabController::getRecordingDeviceCount() { + return (int)m_recordingDevices.size(); + } + + QString AudioTabController::getRecordingDeviceName(int index) { + if (index < m_recordingDevices.size()) { + return QString::fromStdString(m_recordingDevices[index].second); + } else { + return ""; + } + } + + + int AudioTabController::playbackDeviceIndex() const { + return m_playbackDeviceIndex; + } + + int AudioTabController::mirrorDeviceIndex() const { + return m_mirrorDeviceIndex; + } + + int AudioTabController::micDeviceIndex() const { + return m_recordingDeviceIndex; + } + + void AudioTabController::setPlaybackDeviceIndex(int index, bool notify) { + if (index != m_playbackDeviceIndex) { + if (index < m_playbackDevices.size() && index != m_mirrorDeviceIndex) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackDevice_String, m_playbackDevices[index].first.c_str(), &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not write \"" << vr::k_pch_audio_OnPlaybackDevice_String << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(); + audioManager->setPlaybackDevice(m_playbackDevices[index].first, notify); + } + } else if (notify) { + emit playbackDeviceIndexChanged(m_playbackDeviceIndex); + } + } + } + + void AudioTabController::setMirrorDeviceIndex(int index, bool notify) { + if (index != m_mirrorDeviceIndex) { + if (index == -1) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->RemoveKeyInSection(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackMirrorDevice_String, &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not remove \"" << vr::k_pch_audio_OnPlaybackMirrorDevice_String << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(); + audioManager->setMirrorDevice("", notify); + } + } else if (index < m_playbackDevices.size() && index != m_playbackDeviceIndex && index != m_mirrorDeviceIndex) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnPlaybackMirrorDevice_String, m_playbackDevices[index].first.c_str(), &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not write \"" << vr::k_pch_audio_OnPlaybackMirrorDevice_String << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(); + audioManager->setMirrorDevice(m_playbackDevices[index].first, notify); + } + } else if (notify) { + emit mirrorDeviceIndexChanged(index); + } + } + } + + void AudioTabController::setMicDeviceIndex(int index, bool notify) { + if (index != m_recordingDeviceIndex) { + if (index < m_recordingDevices.size()) { + vr::EVRSettingsError vrSettingsError; + vr::VRSettings()->SetString(vr::k_pch_audio_Section, vr::k_pch_audio_OnRecordDevice_String, m_recordingDevices[index].first.c_str(), &vrSettingsError); + if (vrSettingsError != vr::VRSettingsError_None) { + LOG(WARNING) << "Could not write \"" << vr::k_pch_audio_OnRecordDevice_String << "\" setting: " << vr::VRSettings()->GetSettingsErrorNameFromEnum(vrSettingsError); + } else { + vr::VRSettings()->Sync(); + audioManager->setMicDevice(m_recordingDevices[index].first, notify); + } + } else if (notify) { + emit micDeviceIndexChanged(index); + } + } + } + + void AudioTabController::findPlaybackDeviceIndex(std::string id, bool notify) { + int i = 0; + bool deviceFound = false; + for (auto d : m_playbackDevices) { + if (d.first.compare(id) == 0) { + deviceFound = true; + break; + } else { + ++i; + } + } + if (deviceFound) { + m_playbackDeviceIndex = i; + if (notify) { + emit playbackDeviceIndexChanged(i); + } + } + } + + void AudioTabController::findMirrorDeviceIndex(std::string id, bool notify) { + int i = 0; + bool deviceFound = false; + for (auto d : m_playbackDevices) { + if (d.first.compare(id) == 0) { + deviceFound = true; + break; + } else { + ++i; + } + } + if (deviceFound && m_mirrorDeviceIndex != i) { + m_mirrorDeviceIndex = i; + if (notify) { + emit mirrorDeviceIndexChanged(i); + } + } + } + + void AudioTabController::findMicDeviceIndex(std::string id, bool notify) { + int i = 0; + bool deviceFound = false; + for (auto d : m_recordingDevices) { + if (d.first.compare(id) == 0) { + deviceFound = true; + break; + } else { + ++i; + } + } + if (deviceFound) { + m_recordingDeviceIndex = i; + if (notify) { + emit micDeviceIndexChanged(i); } } } diff --git a/src/tabcontrollers/AudioTabController.h b/src/tabcontrollers/AudioTabController.h index 6eab364..91ef998 100644 --- a/src/tabcontrollers/AudioTabController.h +++ b/src/tabcontrollers/AudioTabController.h @@ -15,12 +15,15 @@ namespace advsettings { class AudioTabController : public PttController { Q_OBJECT - Q_PROPERTY(bool mirrorPresent READ mirrorPresent NOTIFY mirrorPresentChanged) - Q_PROPERTY(QString mirrorDevName READ mirrorDevName NOTIFY mirrorDevNameChanged) + Q_PROPERTY(int playbackDeviceIndex READ playbackDeviceIndex WRITE setPlaybackDeviceIndex NOTIFY playbackDeviceIndexChanged) + Q_PROPERTY(int mirrorDeviceIndex READ mirrorDeviceIndex WRITE setMirrorDeviceIndex NOTIFY mirrorDeviceIndexChanged) + //Q_PROPERTY(bool mirrorPresent READ mirrorPresent NOTIFY mirrorPresentChanged) + //Q_PROPERTY(QString mirrorDevName READ mirrorDevName NOTIFY mirrorDevNameChanged) Q_PROPERTY(float mirrorVolume READ mirrorVolume WRITE setMirrorVolume NOTIFY mirrorVolumeChanged) Q_PROPERTY(bool mirrorMuted READ mirrorMuted WRITE setMirrorMuted NOTIFY mirrorMutedChanged) - Q_PROPERTY(bool micPresent READ micPresent NOTIFY micPresentChanged) - Q_PROPERTY(QString micDevName READ micDevName NOTIFY micDevNameChanged) + Q_PROPERTY(int micDeviceIndex READ micDeviceIndex WRITE setMicDeviceIndex NOTIFY micDeviceIndexChanged) + //Q_PROPERTY(bool micPresent READ micPresent NOTIFY micPresentChanged) + //Q_PROPERTY(QString micDevName READ micDevName NOTIFY micDevNameChanged) Q_PROPERTY(float micVolume READ micVolume WRITE setMicVolume NOTIFY micVolumeChanged) Q_PROPERTY(bool micMuted READ micMuted WRITE setMicMuted NOTIFY micMutedChanged) @@ -30,19 +33,25 @@ namespace advsettings { vr::VROverlayHandle_t m_ulNotificationOverlayHandle = vr::k_ulOverlayHandleInvalid; - bool m_mirrorPresent = false; - QString m_mirrorDevName = ""; + int m_playbackDeviceIndex = -1; + + int m_mirrorDeviceIndex = -1; + //bool m_mirrorPresent = false; + //QString m_mirrorDevName = ""; float m_mirrorVolume = 1.0; bool m_mirrorMuted = false; - bool m_micPresent = false; - QString m_micDevName = ""; + int m_recordingDeviceIndex = -1; + //bool m_micPresent = false; + //QString m_micDevName = ""; float m_micVolume = 1.0; bool m_micMuted = false; unsigned settingsUpdateCounter = 0; std::unique_ptr audioManager; + std::vector> m_recordingDevices; + std::vector> m_playbackDevices; std::string lastMirrorDevId; QString getSettingsName() override { return "audioSettings"; } @@ -53,23 +62,37 @@ namespace advsettings { bool pttChangeValid() override; virtual vr::VROverlayHandle_t getNotificationOverlayHandle() override { return m_ulNotificationOverlayHandle; } + void findPlaybackDeviceIndex(std::string id, bool notify = true); + void findMirrorDeviceIndex(std::string id, bool notify = true); + void findMicDeviceIndex(std::string id, bool notify = true); + public: void initStage1(); void initStage2(OverlayController* parent, QQuickWindow* widget); void eventLoopTick(); - bool mirrorPresent() const; - QString mirrorDevName() const; + int playbackDeviceIndex() const; + + int mirrorDeviceIndex() const; float mirrorVolume() const; bool mirrorMuted() const; - bool micPresent() const; - QString micDevName() const; + int micDeviceIndex() const; float micVolume() const; bool micMuted() const; + Q_INVOKABLE int getPlaybackDeviceCount(); + Q_INVOKABLE QString getPlaybackDeviceName(int index); + + Q_INVOKABLE int getRecordingDeviceCount(); + Q_INVOKABLE QString getRecordingDeviceName(int index); + void onNewRecordingDevice(); + void onNewPlaybackDevice(); + void onNewMirrorDevice(); + void onDeviceAdded(); + void onDeviceRemoved(); public slots: void setMirrorVolume(float value, bool notify = true); @@ -78,16 +101,21 @@ namespace advsettings { void setMicVolume(float value, bool notify = true); void setMicMuted(bool value, bool notify = true); + void setPlaybackDeviceIndex(int value, bool notify = true); + void setMirrorDeviceIndex(int value, bool notify = true); + void setMicDeviceIndex(int value, bool notify = true); + signals: - void mirrorPresentChanged(bool value); - void mirrorDevNameChanged(QString value); + void playbackDeviceIndexChanged(int index); + + void mirrorDeviceIndexChanged(int index); void mirrorVolumeChanged(float value); void mirrorMutedChanged(bool value); - void micPresentChanged(bool value); - void micDevNameChanged(QString value); + void micDeviceIndexChanged(int index); void micVolumeChanged(float value); void micMutedChanged(bool value); + }; } // namespace advsettings diff --git a/src/tabcontrollers/audiomanager/AudioManagerWindows.cpp b/src/tabcontrollers/audiomanager/AudioManagerWindows.cpp index fd4d7fb..4e62fb8 100644 --- a/src/tabcontrollers/audiomanager/AudioManagerWindows.cpp +++ b/src/tabcontrollers/audiomanager/AudioManagerWindows.cpp @@ -24,6 +24,9 @@ AudioManagerWindows::~AudioManagerWindows() { if (micAudioDevice) { micAudioDevice->Release(); } + if (playbackAudioDevice) { + playbackAudioDevice->Release(); + } audioDeviceEnumerator->Release(); } @@ -32,6 +35,10 @@ void AudioManagerWindows::init(AudioTabController* controller) { if (!audioDeviceEnumerator) { throw std::exception("Could not create audio device enumerator"); } + playbackAudioDevice = getDefaultPlaybackDevice(audioDeviceEnumerator); + if (!playbackAudioDevice) { + LOG(WARNING) << "Could not find a default recording device."; + } micAudioDevice = getDefaultRecordingDevice(audioDeviceEnumerator); if (micAudioDevice) { micAudioEndpointVolume = getAudioEndpointVolume(micAudioDevice); @@ -40,20 +47,62 @@ void AudioManagerWindows::init(AudioTabController* controller) { } this->controller = controller; audioDeviceEnumerator->RegisterEndpointNotificationCallback((IMMNotificationClient*)this); + policyConfig = getPolicyConfig(); + if (!policyConfig) { + LOG(ERROR) << "Could not find PolicyConfig interface"; + } +} + +void AudioManagerWindows::setPlaybackDevice(const std::string & id, bool notify) { + if (!id.empty()) { + auto dev = getDevice(audioDeviceEnumerator, id); + if (!dev) { + LOG(WARNING) << "Could not find playback device \"" << id << "\"."; + } else { + playbackAudioDevice = dev; + if (policyConfig) { + LPWSTR devId; + if (dev->GetId(&devId) >= 0) { + policyConfig->SetDefaultEndpoint(devId, eConsole); + } + } + } + } + if (notify) { + controller->onNewPlaybackDevice(); + } +} + +std::string AudioManagerWindows::getPlaybackDevName() { + if (playbackAudioDevice) { + return getDeviceName(playbackAudioDevice); + } + return ""; +} + +std::string AudioManagerWindows::getPlaybackDevId() { + if (playbackAudioDevice) { + return getDeviceId(playbackAudioDevice); + } + return ""; } -void AudioManagerWindows::setMirrorDevice(const std::string& id) { +void AudioManagerWindows::setMirrorDevice(const std::string& id, bool notify) { if (id.empty()) { deleteMirrorDevice(); } else { - mirrorAudioDevice = getDevice(audioDeviceEnumerator, id); - if (mirrorAudioDevice) { + auto dev = getDevice(audioDeviceEnumerator, id); + if (dev) { + mirrorAudioDevice = dev; mirrorAudioEndpointVolume = getAudioEndpointVolume(mirrorAudioDevice); } else { LOG(WARNING) << "Could not find mirror device \"" << id << "\"."; } } + if (notify) { + controller->onNewMirrorDevice(); + } } void AudioManagerWindows::deleteMirrorDevice() { @@ -78,6 +127,13 @@ std::string AudioManagerWindows::getMirrorDevName() { return ""; } +std::string AudioManagerWindows::getMirrorDevId() { + if (mirrorAudioDevice) { + return getDeviceId(mirrorAudioDevice); + } + return ""; +} + float AudioManagerWindows::getMirrorVolume() { float value; if (mirrorAudioEndpointVolume && mirrorAudioEndpointVolume->GetMasterVolumeLevelScalar(&value) >= 0) { @@ -119,6 +175,26 @@ bool AudioManagerWindows::isMicValid() { return micAudioEndpointVolume != nullptr; } +void AudioManagerWindows::setMicDevice(const std::string & id, bool notify) { + if (!id.empty()) { + auto dev = getDevice(audioDeviceEnumerator, id); + if (!dev) { + LOG(WARNING) << "Could not find recording device \"" << id << "\"."; + } else { + micAudioDevice = dev; + if (policyConfig) { + LPWSTR devId; + if (dev->GetId(&devId) >= 0) { + policyConfig->SetDefaultEndpoint(devId, eConsole); + } + } + } + } + if (notify) { + controller->onNewRecordingDevice(); + } +} + std::string AudioManagerWindows::getMicDevName() { if (micAudioDevice) { return getDeviceName(micAudioDevice); @@ -126,6 +202,13 @@ std::string AudioManagerWindows::getMicDevName() { return ""; } +std::string AudioManagerWindows::getMicDevId() { + if (micAudioDevice) { + return getDeviceId(micAudioDevice); + } + return ""; +} + float AudioManagerWindows::getMicVolume() { float value; if (micAudioEndpointVolume && micAudioEndpointVolume->GetMasterVolumeLevelScalar(&value) >= 0) { @@ -162,6 +245,14 @@ bool AudioManagerWindows::setMicMuted(bool value) { return false; } +std::vector> AudioManagerWindows::getRecordingDevices() { + return getDevices(audioDeviceEnumerator, eCapture); +} + +std::vector> AudioManagerWindows::getPlaybackDevices() { + return getDevices(audioDeviceEnumerator, eRender); +} + IMMDeviceEnumerator* AudioManagerWindows::getAudioDeviceEnumerator() { IMMDeviceEnumerator* pEnumerator; @@ -171,6 +262,24 @@ IMMDeviceEnumerator* AudioManagerWindows::getAudioDeviceEnumerator() { return pEnumerator; } +IPolicyConfig * AudioManagerWindows::getPolicyConfig() { + IPolicyConfig* policyConfig = nullptr; + // for Win 10 + auto hr = CoCreateInstance(__uuidof(CPolicyConfigClient), NULL, CLSCTX_INPROC, IID_IPolicyConfig2, (LPVOID *)&policyConfig); + if (hr != S_OK) { + hr = CoCreateInstance(__uuidof(CPolicyConfigClient), NULL, CLSCTX_INPROC, IID_IPolicyConfig1, (LPVOID *)&policyConfig); + } + // for Win Vista, 7, 8, 8.1 + if (hr != S_OK) { + hr = CoCreateInstance(__uuidof(CPolicyConfigClient), NULL, CLSCTX_INPROC, IID_IPolicyConfig0, (LPVOID *)&policyConfig); + } + if (hr != S_OK) { + return nullptr; + } else { + return policyConfig; + } +} + IMMDevice* AudioManagerWindows::getDefaultRecordingDevice(IMMDeviceEnumerator* deviceEnumerator) { IMMDevice* pDevice; if (deviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &pDevice) < 0) { @@ -179,6 +288,14 @@ IMMDevice* AudioManagerWindows::getDefaultRecordingDevice(IMMDeviceEnumerator* d return pDevice; } +IMMDevice * AudioManagerWindows::getDefaultPlaybackDevice(IMMDeviceEnumerator * deviceEnumerator) { + IMMDevice* pDevice; + if (deviceEnumerator->GetDefaultAudioEndpoint(eRender, eCommunications, &pDevice) < 0) { + return nullptr; + } + return pDevice; +} + IMMDevice* AudioManagerWindows::getDevice(IMMDeviceEnumerator* deviceEnumerator, const std::string& id) { std::wstring_convert> converter; std::wstring wid = converter.from_bytes(id); @@ -212,6 +329,33 @@ std::string AudioManagerWindows::getDeviceName(IMMDevice* device) { return ""; } +std::string AudioManagerWindows::getDeviceId(IMMDevice* device) { + LPWSTR ppstrId; + if (device->GetId(&ppstrId) >= 0) { + std::wstring_convert> converter; + return converter.to_bytes(ppstrId); + } + return ""; +} + +std::vector> AudioManagerWindows::getDevices(IMMDeviceEnumerator * deviceEnumerator, EDataFlow dataFlow) { + std::vector> retval; + IMMDeviceCollection *ppDevices = nullptr; + if (deviceEnumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATE_ACTIVE, &ppDevices) >= 0) { + UINT count; + ppDevices->GetCount(&count); + for (UINT i = 0; i < count; ++i) { + IMMDevice *device = nullptr; + ppDevices->Item(i, &device); + if (device) { + retval.emplace_back(getDeviceId(device), getDeviceName(device)); + } + } + ppDevices->Release(); + } + return retval; +} + HRESULT AudioManagerWindows::QueryInterface(REFIID riid, void ** ppvObject) { if (IID_IUnknown == riid) { @@ -240,10 +384,12 @@ HRESULT AudioManagerWindows::OnDeviceStateChanged(LPCWSTR, DWORD) { } HRESULT AudioManagerWindows::OnDeviceAdded(LPCWSTR) { + controller->onDeviceAdded(); return S_OK; } HRESULT AudioManagerWindows::OnDeviceRemoved(LPCWSTR) { + controller->onDeviceRemoved(); return S_OK; } @@ -266,6 +412,22 @@ HRESULT AudioManagerWindows::OnDefaultDeviceChanged(EDataFlow flow, ERole role, controller->onNewRecordingDevice(); } } + } else if (flow == eRender && role == eMultimedia) { + if (audioDeviceEnumerator) { + auto device = getDevice(audioDeviceEnumerator, pwstrDefaultDeviceId); + if (device != playbackAudioDevice) { + if (playbackAudioDevice) { + playbackAudioDevice->Release(); + } + playbackAudioDevice = device; + if (!playbackAudioDevice) { + std::wstring_convert> converter; + std::string name = converter.to_bytes(pwstrDefaultDeviceId); + LOG(WARNING) << "Could not find playback device \"" << name << "\"."; + } + controller->onNewPlaybackDevice(); + } + } } /*char* pszFlow = (char*)"?????"; char* pszRole = (char*)"?????"; diff --git a/src/tabcontrollers/audiomanager/AudioManagerWindows.h b/src/tabcontrollers/audiomanager/AudioManagerWindows.h index 094628b..b380fd5 100644 --- a/src/tabcontrollers/audiomanager/AudioManagerWindows.h +++ b/src/tabcontrollers/audiomanager/AudioManagerWindows.h @@ -5,6 +5,7 @@ #include #include #include +#include "IPolicyConfig.h" // application namespace @@ -15,32 +16,45 @@ friend class AudioNotificationClient; private: AudioTabController* controller = nullptr; IMMDeviceEnumerator* audioDeviceEnumerator = nullptr; + IMMDevice* playbackAudioDevice = nullptr; IMMDevice* mirrorAudioDevice = nullptr; IAudioEndpointVolume* mirrorAudioEndpointVolume = nullptr; IMMDevice* micAudioDevice = nullptr; IAudioEndpointVolume* micAudioEndpointVolume = nullptr; + IPolicyConfig* policyConfig = nullptr; public: ~AudioManagerWindows(); virtual void init(AudioTabController* controller) override; - virtual void setMirrorDevice(const std::string& id) override; - virtual void deleteMirrorDevice() override; + virtual void setPlaybackDevice(const std::string& id, bool notify = true) override; + virtual std::string getPlaybackDevName() override; + virtual std::string getPlaybackDevId() override; + + virtual void setMirrorDevice(const std::string& id, bool notify = true) override; virtual bool isMirrorValid() override; virtual std::string getMirrorDevName() override; + virtual std::string getMirrorDevId() override; virtual float getMirrorVolume() override; virtual bool setMirrorVolume(float value) override; virtual bool getMirrorMuted() override; virtual bool setMirrorMuted(bool value) override; virtual bool isMicValid() override; + virtual void setMicDevice(const std::string& id, bool notify = true) override; virtual std::string getMicDevName() override; + virtual std::string getMicDevId() override; virtual float getMicVolume() override; virtual bool setMicVolume(float value) override; virtual bool getMicMuted() override; virtual bool setMicMuted(bool value) override; + virtual std::vector> getRecordingDevices() override; + virtual std::vector> getPlaybackDevices() override; + + void deleteMirrorDevice(); + // from IMMNotificationClient virtual HRESULT QueryInterface(REFIID riid, void ** ppvObject) override; virtual ULONG AddRef(void) override; @@ -53,11 +67,15 @@ public: private: IMMDeviceEnumerator* getAudioDeviceEnumerator(); + IPolicyConfig* getPolicyConfig(); IMMDevice* getDefaultRecordingDevice(IMMDeviceEnumerator* deviceEnumerator); + IMMDevice* getDefaultPlaybackDevice(IMMDeviceEnumerator* deviceEnumerator); IMMDevice* getDevice(IMMDeviceEnumerator* deviceEnumerator, const std::string& id); IMMDevice* getDevice(IMMDeviceEnumerator* deviceEnumerator, LPCWSTR id); IAudioEndpointVolume* getAudioEndpointVolume(IMMDevice* device); std::string getDeviceName(IMMDevice* device); + std::string getDeviceId(IMMDevice* device); + std::vector> getDevices(IMMDeviceEnumerator* deviceEnumerator, EDataFlow dataFlow); }; } diff --git a/src/tabcontrollers/audiomanager/IPolicyConfig.h b/src/tabcontrollers/audiomanager/IPolicyConfig.h new file mode 100644 index 0000000..dd8d1bd --- /dev/null +++ b/src/tabcontrollers/audiomanager/IPolicyConfig.h @@ -0,0 +1,177 @@ +// ---------------------------------------------------------------------------- +// PolicyConfig.h +// Undocumented COM-interface IPolicyConfig. +// Use for set default audio render endpoint +// @author EreTIk +// ---------------------------------------------------------------------------- + + +#pragma once + +// ca286fc3-91fd-42c3-8e9b-caafa66242e3 +static const GUID IID_IPolicyConfig2 = { 0xCA286FC3, 0x91FD, 0x42C3, { 0x8E, 0x9B, 0xCA, 0xAF, 0xA6, 0x62, 0x42, 0xE3 } }; + +// 6be54be8-a068-4875-a49d-0c2966473b11 +static const GUID IID_IPolicyConfig1 = { 0x6BE54BE8, 0xA068, 0x4875, { 0xA4, 0x9D, 0x0C, 0x29, 0x66, 0x47, 0x3B, 0x11 } }; + +// f8679f50-850a-41cf-9c72-430f290290c8 +static const GUID IID_IPolicyConfig0 = { 0xF8679F50, 0x850A, 0x41CF, { 0x9C, 0x72, 0x43, 0x0F, 0x29, 0x02, 0x90, 0xC8 } }; + +class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9") CPolicyConfigClient; + +interface IPolicyConfig : public IUnknown +{ +public: + + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX ** + ); + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX ** + ); + + virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat( + PCWSTR + ); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX * + ); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64 + ); + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64 + ); + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode * + ); + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode * + ); + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + int, + const PROPERTYKEY &, + const PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + int, + const PROPERTYKEY &, + const PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + __in PCWSTR wszDeviceId, + __in ERole eRole + ); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT + ); +}; + +interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620") +IPolicyConfigVista; +class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862") +CPolicyConfigVistaClient; +// ---------------------------------------------------------------------------- +// class CPolicyConfigVistaClient +// {294935CE-F637-4E7C-A41B-AB255460B862} +// +// interface IPolicyConfigVista +// {568b9108-44bf-40b4-9006-86afe5b5a620} +// +// Query interface: +// CComPtr PolicyConfig; +// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient)); +// +// @compatible: Windows Vista and Later +// ---------------------------------------------------------------------------- +interface IPolicyConfigVista : public IUnknown +{ +public: + + virtual HRESULT GetMixFormat( + PCWSTR, + WAVEFORMATEX ** + ); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat( + PCWSTR, + INT, + WAVEFORMATEX ** + ); + + virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat( + PCWSTR, + WAVEFORMATEX *, + WAVEFORMATEX * + ); + + virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod( + PCWSTR, + INT, + PINT64, + PINT64 + ); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod( + PCWSTR, + PINT64 + ); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetShareMode( + PCWSTR, + struct DeviceShareMode * + ); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE SetShareMode( + PCWSTR, + struct DeviceShareMode * + ); // not available on Windows 7, use method from IPolicyConfig + + virtual HRESULT STDMETHODCALLTYPE GetPropertyValue( + PCWSTR, + int, + const PROPERTYKEY &, + const PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetPropertyValue( + PCWSTR, + int, + const PROPERTYKEY &, + const PROPVARIANT * + ); + + virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint( + __in PCWSTR wszDeviceId, + __in ERole eRole + ); + + virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility( + PCWSTR, + INT + ); // not available on Windows 7, use method from IPolicyConfig +};