diff --git a/interface/resources/qml/controls-uit/CheckBoxQQC2.qml b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml new file mode 100644 index 0000000000..92bad04d01 --- /dev/null +++ b/interface/resources/qml/controls-uit/CheckBoxQQC2.qml @@ -0,0 +1,113 @@ +// +// CheckBox2.qml +// +// Created by Vlad Stelmahovsky on 10 Aug 2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 + +import "../styles-uit" +import "../controls-uit" as HiFiControls + +CheckBox { + id: checkBox + + HifiConstants { id: hifi; } + + padding: 0 + leftPadding: 0 + property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGrayText + readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light + property bool isRedCheck: false + property bool isRound: false + property int boxSize: 14 + property int boxRadius: isRound ? boxSize : 3 + property bool wrap: true; + readonly property int checkSize: Math.max(boxSize - 8, 10) + readonly property int checkRadius: isRound ? checkSize / 2 : 2 + focusPolicy: Qt.ClickFocus + + indicator: Rectangle { + id: box + implicitWidth: boxSize + implicitHeight: boxSize + radius: boxRadius + x: checkBox.leftPadding + y: parent.height / 2 - height / 2 + border.width: 1 + border.color: pressed || hovered + ? hifi.colors.checkboxCheckedBorder + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + + gradient: Gradient { + GradientStop { + position: 0.2 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightStart) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightStart : hifi.colors.checkboxDarkStart) + } + GradientStop { + position: 1.0 + color: pressed || hovered + ? (checkBox.isLightColorScheme ? hifi.colors.checkboxChecked : hifi.colors.checkboxLightFinish) + : (checkBox.isLightColorScheme ? hifi.colors.checkboxLightFinish : hifi.colors.checkboxDarkFinish) + } + } + + Rectangle { + visible: pressed || hovered + anchors.centerIn: parent + id: innerBox + width: checkSize - 4 + height: width + radius: checkRadius + color: hifi.colors.checkboxCheckedBorder + } + + Rectangle { + id: check + width: checkSize + height: checkSize + radius: checkRadius + anchors.centerIn: parent + color: isRedCheck ? hifi.colors.checkboxCheckedRed : hifi.colors.checkboxChecked + border.width: 2 + border.color: isRedCheck? hifi.colors.checkboxCheckedBorderRed : hifi.colors.checkboxCheckedBorder + visible: checked && !pressed || !checked && pressed + } + + Rectangle { + id: disabledOverlay + visible: !enabled + width: boxSize + height: boxSize + radius: boxRadius + border.width: 1 + border.color: hifi.colors.baseGrayHighlight + color: hifi.colors.baseGrayHighlight + opacity: 0.5 + } + } + + contentItem: Text { + id: root + FontLoader { id: ralewaySemiBold; source: pathToFonts + "fonts/Raleway-SemiBold.ttf"; } + font.pixelSize: hifi.fontSizes.inputLabel + font.family: ralewaySemiBold.name + text: checkBox.text + color: checkBox.color + x: 2 + wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap + elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight + enabled: checkBox.enabled + verticalAlignment: Text.AlignVCenter + leftPadding: checkBox.indicator.width + checkBox.spacing + } +} + diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index a819e032eb..b1f80ac5e8 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -12,7 +12,7 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 +import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import "../../styles-uit" @@ -36,7 +36,41 @@ Rectangle { return (root.parent !== null) && root.parent.objectName == "loader"; } + + property bool isVR: Audio.context === "VR" + property real rightMostInputLevelPos: 0 + //placeholder for control sizes and paddings + //recalculates dynamically in case of UI size is changed + QtObject { + id: margins + property real paddings: root.width / 20.25 + + property real sizeCheckBox: root.width / 13.5 + property real sizeText: root.width / 2.5 + property real sizeLevel: root.width / 5.8 + property real sizeDesktop: root.width / 5.8 + property real sizeVR: root.width / 13.5 + } + + TabBar { + id: bar + spacing: 0 + width: parent.width + height: 42 + currentIndex: isVR ? 1 : 0 + + AudioControls.AudioTabButton { + height: parent.height + text: qsTr("Desktop") + } + AudioControls.AudioTabButton { + height: parent.height + text: qsTr("VR") + } + } + property bool showPeaks: true; + function enablePeakValues() { Audio.devices.input.peakValuesEnabled = true; Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) { @@ -45,6 +79,7 @@ Rectangle { } }); } + function disablePeakValues() { root.showPeaks = false; Audio.devices.input.peakValuesEnabled = false; @@ -55,29 +90,32 @@ Rectangle { onVisibleChanged: visible ? enablePeakValues() : disablePeakValues(); Column { - y: 16; // padding does not work - spacing: 16; + spacing: 12; + anchors.top: bar.bottom + anchors.bottom: parent.bottom + anchors.bottomMargin: 5 width: parent.width; + Separator { } + RalewayRegular { - x: 16; // padding does not work + x: margins.paddings + muteMic.boxSize + muteMic.spacing; size: 16; color: "white"; - text: root.title; - - visible: root.showTitle(); + text: qsTr("Input Device Settings") } - Separator { visible: root.showTitle() } - ColumnLayout { - x: 16; // padding does not work + x: margins.paddings; spacing: 16; + width: parent.width; // mute is in its own row RowLayout { AudioControls.CheckBox { + id: muteMic text: qsTr("Mute microphone"); + spacing: margins.sizeCheckBox - boxSize isRedCheck: true; checked: Audio.muted; onClicked: { @@ -88,8 +126,9 @@ Rectangle { } RowLayout { - spacing: 16; + spacing: muteMic.spacing*2; //make it visually distinguish AudioControls.CheckBox { + spacing: muteMic.spacing text: qsTr("Enable noise reduction"); checked: Audio.noiseReduction; onClicked: { @@ -98,24 +137,33 @@ Rectangle { } } AudioControls.CheckBox { + spacing: muteMic.spacing text: qsTr("Show audio level meter"); checked: AvatarInputs.showAudioTools; onClicked: { AvatarInputs.showAudioTools = checked; checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding } + onXChanged: rightMostInputLevelPos = x + width } } } Separator {} - RowLayout { + Item { + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: 36 + HiFiGlyphs { + width: margins.sizeCheckBox text: hifi.glyphs.mic; color: hifi.colors.primaryHighlight; + anchors.left: parent.left + anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% anchors.verticalCenter: parent.verticalCenter; - size: 28; + size: 30; } RalewayRegular { anchors.verticalCenter: parent.verticalCenter; @@ -126,90 +174,114 @@ Rectangle { } ListView { - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: 125; - spacing: 0; + id: inputView + width: parent.width - margins.paddings*2 + x: margins.paddings + height: Math.min(150, contentHeight); + spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.input; delegate: Item { - width: parent.width; - height: 36; - + width: rightMostInputLevelPos + height: margins.sizeCheckBox > checkBoxInput.implicitHeight ? + margins.sizeCheckBox : checkBoxInput.implicitHeight + AudioControls.CheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter + id: checkBoxInput anchors.left: parent.left - text: display; - wrap: false; - checked: selected; - enabled: false; + spacing: margins.sizeCheckBox - boxSize + anchors.verticalCenter: parent.verticalCenter + width: parent.width - inputLevel.width + clip: true + checkable: !checked + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + boxSize: margins.sizeCheckBox / 2 + isRound: true + text: devicename + onPressed: { + if (!checked) { + Audio.setInputDevice(info, bar.currentIndex === 1); + } + } } - - MouseArea { - anchors.fill: checkbox - onClicked: Audio.setInputDevice(info); - } - InputPeak { - id: inputPeak; - visible: Audio.devices.input.peakValuesAvailable; + id: inputLevel + anchors.right: parent.right peak: model.peak; anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: 30 + visible: (bar.currentIndex === 1 && selectedHMD && isVR) || + (bar.currentIndex === 0 && selectedDesktop && !isVR) && + Audio.devices.input.peakValuesAvailable; } } } Separator {} - RowLayout { - Column { - RowLayout { - HiFiGlyphs { - text: hifi.glyphs.unmuted; - color: hifi.colors.primaryHighlight; - anchors.verticalCenter: parent.verticalCenter; - size: 36; - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter; - size: 16; - color: hifi.colors.lightGrayText; - text: qsTr("CHOOSE OUTPUT DEVICE"); - } - } + Item { + x: margins.paddings; + width: parent.width - margins.paddings*2 + height: 36 - PlaySampleSound { anchors { left: parent.left; leftMargin: 60 }} + HiFiGlyphs { + anchors.left: parent.left + anchors.leftMargin: -size/4 //the glyph has empty space at left about 25% + anchors.verticalCenter: parent.verticalCenter; + width: margins.sizeCheckBox + text: hifi.glyphs.unmuted; + color: hifi.colors.primaryHighlight; + size: 36; + } + + RalewayRegular { + width: margins.sizeText + margins.sizeLevel + anchors.left: parent.left + anchors.leftMargin: margins.sizeCheckBox + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE OUTPUT DEVICE"); } } ListView { - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: Math.min(250, contentHeight); - spacing: 0; + id: outputView + width: parent.width - margins.paddings*2 + x: margins.paddings + height: Math.min(360 - inputView.height, contentHeight); + spacing: 4; snapMode: ListView.SnapToItem; clip: true; model: Audio.devices.output; delegate: Item { - width: parent.width; - height: 36; + width: rightMostInputLevelPos + height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ? + margins.sizeCheckBox : checkBoxOutput.implicitHeight AudioControls.CheckBox { - id: checkbox - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - text: display; - checked: selected; - enabled: false; - } - - MouseArea { - anchors.fill: checkbox - onClicked: Audio.setOutputDevice(info); + id: checkBoxOutput + width: parent.width + spacing: margins.sizeCheckBox - boxSize + boxSize: margins.sizeCheckBox / 2 + isRound: true + checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD; + checkable: !checked + text: devicename + onPressed: { + if (!checked) { + Audio.setOutputDevice(info, bar.currentIndex === 1); + } + } } } } + PlaySampleSound { + x: margins.paddings + + visible: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR); + anchors { left: parent.left; leftMargin: margins.paddings } + } } } diff --git a/interface/resources/qml/hifi/audio/AudioTabButton.qml b/interface/resources/qml/hifi/audio/AudioTabButton.qml new file mode 100644 index 0000000000..3a3ed90f5e --- /dev/null +++ b/interface/resources/qml/hifi/audio/AudioTabButton.qml @@ -0,0 +1,35 @@ +// +// AudioTabButton.qml +// qml/hifi/audio +// +// Created by Vlad Stelmahovsky on 8/16/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import "../../controls-uit" as HifiControls +import "../../styles-uit" + +TabButton { + id: control + font.pixelSize: height / 2 + + HifiConstants { id: hifi; } + + contentItem: RalewaySemiBold { + text: control.text + font: control.font + color: "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: control.checked ? hifi.colors.baseGray : "black" + } +} diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml index 1f632ac479..3a954d4004 100644 --- a/interface/resources/qml/hifi/audio/CheckBox.qml +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -import QtQuick 2.5 +import QtQuick 2.7 import "../../controls-uit" as HifiControls -HifiControls.CheckBox { +HifiControls.CheckBoxQQC2 { color: "white" } diff --git a/interface/resources/qml/styles-uit/HiFiGlyphs.qml b/interface/resources/qml/styles-uit/HiFiGlyphs.qml index cbd6fa1d68..f78d6c6f59 100644 --- a/interface/resources/qml/styles-uit/HiFiGlyphs.qml +++ b/interface/resources/qml/styles-uit/HiFiGlyphs.qml @@ -9,8 +9,6 @@ // import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 Text { id: root diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5700bb6a72..2ae142b0e4 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2057,6 +2057,7 @@ void Application::cleanupBeforeQuit() { // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 9719c23885..f9c1a95fb5 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -135,10 +135,10 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) { DependencyManager::get()->setReverbOptions(options); } -void Audio::setInputDevice(const QAudioDeviceInfo& device) { - _devices.chooseInputDevice(device); +void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { + _devices.chooseInputDevice(device, isHMD); } -void Audio::setOutputDevice(const QAudioDeviceInfo& device) { - _devices.chooseOutputDevice(device); +void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { + _devices.chooseOutputDevice(device, isHMD); } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index bd40de4303..abd2312cf0 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -50,8 +50,8 @@ public: void showMicMeter(bool show); void setInputVolume(float volume); - Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device); - Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device); + Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD); + Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); Q_INVOKABLE void setReverb(bool enable); Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp index e5cc43f9df..a130b46877 100644 --- a/interface/src/scripting/AudioDevices.cpp +++ b/interface/src/scripting/AudioDevices.cpp @@ -38,15 +38,17 @@ Setting::Handle& getSetting(bool contextIsHMD, QAudio::Mode mode) { } enum AudioDeviceRole { - DisplayRole = Qt::DisplayRole, - CheckStateRole = Qt::CheckStateRole, - PeakRole = Qt::UserRole, - InfoRole = Qt::UserRole + 1 + DeviceNameRole = Qt::UserRole, + SelectedDesktopRole, + SelectedHMDRole, + PeakRole, + InfoRole }; QHash AudioDeviceList::_roles { - { DisplayRole, "display" }, - { CheckStateRole, "selected" }, + { DeviceNameRole, "devicename" }, + { SelectedDesktopRole, "selectedDesktop" }, + { SelectedHMDRole, "selectedHMD" }, { PeakRole, "peak" }, { InfoRole, "info" } }; @@ -68,15 +70,64 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) { Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; +AudioDeviceList::AudioDeviceList(QAudio::Mode mode) : _mode(mode) { + auto& setting1 = getSetting(true, QAudio::AudioInput); + if (setting1.isSet()) { + qDebug() << "Device name in settings for HMD, Input" << setting1.get(); + } else { + qDebug() << "Device name in settings for HMD, Input not set"; + } + + auto& setting2 = getSetting(true, QAudio::AudioOutput); + if (setting2.isSet()) { + qDebug() << "Device name in settings for HMD, Output" << setting2.get(); + } else { + qDebug() << "Device name in settings for HMD, Output not set"; + } + + auto& setting3 = getSetting(false, QAudio::AudioInput); + if (setting3.isSet()) { + qDebug() << "Device name in settings for Desktop, Input" << setting3.get(); + } else { + qDebug() << "Device name in settings for Desktop, Input not set"; + } + + auto& setting4 = getSetting(false, QAudio::AudioOutput); + if (setting4.isSet()) { + qDebug() << "Device name in settings for Desktop, Output" << setting4.get(); + } else { + qDebug() << "Device name in settings for Desktop, Output not set"; + } +} + +AudioDeviceList::~AudioDeviceList() { + //save all selected devices + auto& settingHMD = getSetting(true, _mode); + auto& settingDesktop = getSetting(false, _mode); + // store the selected device + foreach(std::shared_ptr adevice, _devices) { + if (adevice->selectedDesktop) { + qDebug() << "Saving Desktop for" << _mode << "name" << adevice->info.deviceName(); + settingDesktop.set(adevice->info.deviceName()); + } + if (adevice->selectedHMD) { + qDebug() << "Saving HMD for" << _mode << "name" << adevice->info.deviceName(); + settingHMD.set(adevice->info.deviceName()); + } + } +} + QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.row() >= rowCount()) { return QVariant(); } - if (role == DisplayRole) { + if (role == DeviceNameRole) { return _devices.at(index.row())->display; - } else if (role == CheckStateRole) { - return _devices.at(index.row())->selected; + } else if (role == SelectedDesktopRole) { + return _devices.at(index.row())->selectedDesktop; + } else if (role == SelectedHMDRole) { + return _devices.at(index.row())->selectedHMD; } else if (role == InfoRole) { return QVariant::fromValue(_devices.at(index.row())->info); } else { @@ -130,37 +181,48 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) { #endif } -void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { - auto oldDevice = _selectedDevice; - _selectedDevice = device; +void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) { + auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + selectedDevice = device; - for (auto i = 0; i < rowCount(); ++i) { - AudioDevice& device = *_devices[i]; - - if (device.selected && device.info != _selectedDevice) { - device.selected = false; - } else if (device.info == _selectedDevice) { - device.selected = true; + for (auto i = 0; i < _devices.size(); ++i) { + std::shared_ptr device = _devices[i]; + bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop; + if (isSelected && device->info != selectedDevice) { + isSelected = false; + } else if (device->info == selectedDevice) { + isSelected = true; } } - emit deviceChanged(_selectedDevice); + emit deviceChanged(selectedDevice); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); } -void AudioDeviceList::onDevicesChanged(const QList& devices) { +void AudioDeviceList::onDevicesChanged(const QList& devices, bool isHMD) { + QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice; + + const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName; beginResetModel(); _devices.clear(); foreach(const QAudioDeviceInfo& deviceInfo, devices) { AudioDevice device; + bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop; device.info = deviceInfo; device.display = device.info.deviceName() .replace("High Definition", "HD") .remove("Device") .replace(" )", ")"); - device.selected = (device.info == _selectedDevice); + if (!selectedDevice.isNull()) { + isSelected = (device.info == selectedDevice); + } else { + //no selected device for context. fallback to saved + isSelected = (device.info.deviceName() == savedDeviceName); + } + qDebug() << "adding audio device:" << device.display << device.selectedDesktop << device.selectedHMD << _mode; _devices.push_back(newDevice(device)); } @@ -203,22 +265,32 @@ AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection); + _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput), contextIsHMD); + _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput), contextIsHMD); + // connections are made after client is initialized, so we must also fetch the devices - _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput)); - _outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput)); - _inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); - _outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); + const QList& devicesInput = client->getAudioDevices(QAudio::AudioInput); + const QList& devicesOutput = client->getAudioDevices(QAudio::AudioOutput); + //setup HMD devices + _inputs.onDevicesChanged(devicesInput, true); + _outputs.onDevicesChanged(devicesOutput, true); + //setup Desktop devices + _inputs.onDevicesChanged(devicesInput, false); + _outputs.onDevicesChanged(devicesOutput, false); } +AudioDevices::~AudioDevices() {} + void AudioDevices::onContextChanged(const QString& context) { _inputs.resetDevice(_contextIsHMD); _outputs.resetDevice(_contextIsHMD); } -void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice) { +void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, + const QAudioDeviceInfo& previousDevice, bool isHMD) { QString deviceName = device.isNull() ? QString() : device.deviceName(); - auto& setting = getSetting(_contextIsHMD, mode); + auto& setting = getSetting(isHMD, mode); // check for a previous device auto wasDefault = setting.get().isNull(); @@ -254,42 +326,94 @@ void AudioDevices::onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& d void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) { if (mode == QAudio::AudioInput) { if (_requestedInputDevice == device) { - onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); + onDeviceSelected(QAudio::AudioInput, device, + _contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, + _contextIsHMD); _requestedInputDevice = QAudioDeviceInfo(); } - _inputs.onDeviceChanged(device); + _inputs.onDeviceChanged(device, _contextIsHMD); } else { // if (mode == QAudio::AudioOutput) if (_requestedOutputDevice == device) { - onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); + onDeviceSelected(QAudio::AudioOutput, device, + _contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, + _contextIsHMD); _requestedOutputDevice = QAudioDeviceInfo(); } - _outputs.onDeviceChanged(device); + _outputs.onDeviceChanged(device, _contextIsHMD); } } void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { static std::once_flag once; + std::call_once(once, [&] { + //readout settings + auto client = DependencyManager::get(); + + _inputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioInput); + _inputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioInput); + + //fallback to default device + if (_inputs._desktopSavedDeviceName.isEmpty()) { + _inputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioInput).deviceName(); + } + //fallback to desktop device + if (_inputs._hmdSavedDeviceName.isEmpty()) { + _inputs._hmdSavedDeviceName = _inputs._desktopSavedDeviceName; + } + + _outputs._hmdSavedDeviceName = getTargetDevice(true, QAudio::AudioOutput); + _outputs._desktopSavedDeviceName = getTargetDevice(false, QAudio::AudioOutput); + + if (_outputs._desktopSavedDeviceName.isEmpty()) { + _outputs._desktopSavedDeviceName = client->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); + } + if (_outputs._hmdSavedDeviceName.isEmpty()) { + _outputs._hmdSavedDeviceName = _outputs._desktopSavedDeviceName; + } + onContextChanged(QString()); + }); + + //set devices for both contexts if (mode == QAudio::AudioInput) { - _inputs.onDevicesChanged(devices); + _inputs.onDevicesChanged(devices, _contextIsHMD); + _inputs.onDevicesChanged(devices, !_contextIsHMD); } else { // if (mode == QAudio::AudioOutput) - _outputs.onDevicesChanged(devices); + _outputs.onDevicesChanged(devices, _contextIsHMD); + _outputs.onDevicesChanged(devices, !_contextIsHMD); } - std::call_once(once, [&] { onContextChanged(QString()); }); } -void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - _requestedInputDevice = device; - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioInput), - Q_ARG(const QAudioDeviceInfo&, device)); +void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) { + //check if current context equals device to change + if (_contextIsHMD == isHMD) { + auto client = DependencyManager::get(); + _requestedInputDevice = device; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioInput), + Q_ARG(const QAudioDeviceInfo&, device)); + } else { + //context is different. just save device in settings + onDeviceSelected(QAudio::AudioInput, device, + isHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice, + isHMD); + _inputs.onDeviceChanged(device, isHMD); + } } -void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device) { - auto client = DependencyManager::get(); - _requestedOutputDevice = device; - QMetaObject::invokeMethod(client.data(), "switchAudioDevice", - Q_ARG(QAudio::Mode, QAudio::AudioOutput), - Q_ARG(const QAudioDeviceInfo&, device)); +void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { + //check if current context equals device to change + if (_contextIsHMD == isHMD) { + auto client = DependencyManager::get(); + _requestedOutputDevice = device; + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", + Q_ARG(QAudio::Mode, QAudio::AudioOutput), + Q_ARG(const QAudioDeviceInfo&, device)); + } else { + //context is different. just save device in settings + onDeviceSelected(QAudio::AudioOutput, device, + isHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice, + isHMD); + _outputs.onDeviceChanged(device, isHMD); + } } diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h index 4c1820e9c8..36f1653e38 100644 --- a/interface/src/scripting/AudioDevices.h +++ b/interface/src/scripting/AudioDevices.h @@ -25,15 +25,16 @@ class AudioDevice { public: QAudioDeviceInfo info; QString display; - bool selected { false }; + bool selectedDesktop { false }; + bool selectedHMD { false }; }; class AudioDeviceList : public QAbstractListModel { Q_OBJECT public: - AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {} - ~AudioDeviceList() = default; + AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput); + virtual ~AudioDeviceList(); virtual std::shared_ptr newDevice(const AudioDevice& device) { return std::make_shared(device); } @@ -52,8 +53,8 @@ signals: void deviceChanged(const QAudioDeviceInfo& device); protected slots: - void onDeviceChanged(const QAudioDeviceInfo& device); - void onDevicesChanged(const QList& devices); + void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD); + void onDevicesChanged(const QList& devices, bool isHMD); protected: friend class AudioDevices; @@ -61,8 +62,11 @@ protected: static QHash _roles; static Qt::ItemFlags _flags; const QAudio::Mode _mode; - QAudioDeviceInfo _selectedDevice; + QAudioDeviceInfo _selectedDesktopDevice; + QAudioDeviceInfo _selectedHMDDevice; QList> _devices; + QString _hmdSavedDeviceName; + QString _desktopSavedDeviceName; }; class AudioInputDevice : public AudioDevice { @@ -102,7 +106,6 @@ protected: void setPeakValuesEnabled(bool enable); bool _peakValuesEnabled { false }; }; - class Audio; class AudioDevices : public QObject { @@ -112,15 +115,18 @@ class AudioDevices : public QObject { public: AudioDevices(bool& contextIsHMD); - void chooseInputDevice(const QAudioDeviceInfo& device); - void chooseOutputDevice(const QAudioDeviceInfo& device); + virtual ~AudioDevices(); + + void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD); + void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD); signals: void nop(); private slots: void onContextChanged(const QString& context); - void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, const QAudioDeviceInfo& previousDevice); + void onDeviceSelected(QAudio::Mode mode, const QAudioDeviceInfo& device, + const QAudioDeviceInfo& previousDevice, bool isHMD); void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); void onDevicesChanged(QAudio::Mode mode, const QList& devices); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 4e7cc08919..cbfcd473dc 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1725,14 +1725,6 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) { if (persist) { _outputBufferSizeFrames.set(numFrames); } - - if (_audioOutput) { - // The buffer size can't be adjusted after QAudioOutput::start() has been called, so - // recreate the device by switching to the default. - QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); - qCDebug(audioclient) << __FUNCTION__ << "about to send changeDevice signal outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; - emit changeDevice(outputDeviceInfo); // On correct thread, please, as setOutputBufferSize can be called from main thread. - } } return numFrames; }