Merge pull request #11182 from vladest/audio_devices_split

Audio devices split
This commit is contained in:
Dante Ruiz 2017-09-20 16:43:30 -07:00 committed by GitHub
commit 4f328524b1
11 changed files with 484 additions and 143 deletions

View file

@ -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
}
}

View file

@ -12,7 +12,7 @@
// //
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import "../../styles-uit" import "../../styles-uit"
@ -36,7 +36,41 @@ Rectangle {
return (root.parent !== null) && root.parent.objectName == "loader"; 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; property bool showPeaks: true;
function enablePeakValues() { function enablePeakValues() {
Audio.devices.input.peakValuesEnabled = true; Audio.devices.input.peakValuesEnabled = true;
Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) { Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
@ -45,6 +79,7 @@ Rectangle {
} }
}); });
} }
function disablePeakValues() { function disablePeakValues() {
root.showPeaks = false; root.showPeaks = false;
Audio.devices.input.peakValuesEnabled = false; Audio.devices.input.peakValuesEnabled = false;
@ -55,29 +90,32 @@ Rectangle {
onVisibleChanged: visible ? enablePeakValues() : disablePeakValues(); onVisibleChanged: visible ? enablePeakValues() : disablePeakValues();
Column { Column {
y: 16; // padding does not work spacing: 12;
spacing: 16; anchors.top: bar.bottom
anchors.bottom: parent.bottom
anchors.bottomMargin: 5
width: parent.width; width: parent.width;
Separator { }
RalewayRegular { RalewayRegular {
x: 16; // padding does not work x: margins.paddings + muteMic.boxSize + muteMic.spacing;
size: 16; size: 16;
color: "white"; color: "white";
text: root.title; text: qsTr("Input Device Settings")
visible: root.showTitle();
} }
Separator { visible: root.showTitle() }
ColumnLayout { ColumnLayout {
x: 16; // padding does not work x: margins.paddings;
spacing: 16; spacing: 16;
width: parent.width;
// mute is in its own row // mute is in its own row
RowLayout { RowLayout {
AudioControls.CheckBox { AudioControls.CheckBox {
id: muteMic
text: qsTr("Mute microphone"); text: qsTr("Mute microphone");
spacing: margins.sizeCheckBox - boxSize
isRedCheck: true; isRedCheck: true;
checked: Audio.muted; checked: Audio.muted;
onClicked: { onClicked: {
@ -88,8 +126,9 @@ Rectangle {
} }
RowLayout { RowLayout {
spacing: 16; spacing: muteMic.spacing*2; //make it visually distinguish
AudioControls.CheckBox { AudioControls.CheckBox {
spacing: muteMic.spacing
text: qsTr("Enable noise reduction"); text: qsTr("Enable noise reduction");
checked: Audio.noiseReduction; checked: Audio.noiseReduction;
onClicked: { onClicked: {
@ -98,24 +137,33 @@ Rectangle {
} }
} }
AudioControls.CheckBox { AudioControls.CheckBox {
spacing: muteMic.spacing
text: qsTr("Show audio level meter"); text: qsTr("Show audio level meter");
checked: AvatarInputs.showAudioTools; checked: AvatarInputs.showAudioTools;
onClicked: { onClicked: {
AvatarInputs.showAudioTools = checked; AvatarInputs.showAudioTools = checked;
checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding
} }
onXChanged: rightMostInputLevelPos = x + width
} }
} }
} }
Separator {} Separator {}
RowLayout { Item {
x: margins.paddings;
width: parent.width - margins.paddings*2
height: 36
HiFiGlyphs { HiFiGlyphs {
width: margins.sizeCheckBox
text: hifi.glyphs.mic; text: hifi.glyphs.mic;
color: hifi.colors.primaryHighlight; 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; anchors.verticalCenter: parent.verticalCenter;
size: 28; size: 30;
} }
RalewayRegular { RalewayRegular {
anchors.verticalCenter: parent.verticalCenter; anchors.verticalCenter: parent.verticalCenter;
@ -126,90 +174,114 @@ Rectangle {
} }
ListView { ListView {
anchors { left: parent.left; right: parent.right; leftMargin: 70 } id: inputView
height: 125; width: parent.width - margins.paddings*2
spacing: 0; x: margins.paddings
height: Math.min(150, contentHeight);
spacing: 4;
snapMode: ListView.SnapToItem; snapMode: ListView.SnapToItem;
clip: true; clip: true;
model: Audio.devices.input; model: Audio.devices.input;
delegate: Item { delegate: Item {
width: parent.width; width: rightMostInputLevelPos
height: 36; height: margins.sizeCheckBox > checkBoxInput.implicitHeight ?
margins.sizeCheckBox : checkBoxInput.implicitHeight
AudioControls.CheckBox { AudioControls.CheckBox {
id: checkbox id: checkBoxInput
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
text: display; spacing: margins.sizeCheckBox - boxSize
wrap: false; anchors.verticalCenter: parent.verticalCenter
checked: selected; width: parent.width - inputLevel.width
enabled: false; 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 { InputPeak {
id: inputPeak; id: inputLevel
visible: Audio.devices.input.peakValuesAvailable; anchors.right: parent.right
peak: model.peak; peak: model.peak;
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right visible: (bar.currentIndex === 1 && selectedHMD && isVR) ||
anchors.rightMargin: 30 (bar.currentIndex === 0 && selectedDesktop && !isVR) &&
Audio.devices.input.peakValuesAvailable;
} }
} }
} }
Separator {} Separator {}
RowLayout { Item {
Column { x: margins.paddings;
RowLayout { width: parent.width - margins.paddings*2
HiFiGlyphs { height: 36
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");
}
}
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 { ListView {
anchors { left: parent.left; right: parent.right; leftMargin: 70 } id: outputView
height: Math.min(250, contentHeight); width: parent.width - margins.paddings*2
spacing: 0; x: margins.paddings
height: Math.min(360 - inputView.height, contentHeight);
spacing: 4;
snapMode: ListView.SnapToItem; snapMode: ListView.SnapToItem;
clip: true; clip: true;
model: Audio.devices.output; model: Audio.devices.output;
delegate: Item { delegate: Item {
width: parent.width; width: rightMostInputLevelPos
height: 36; height: margins.sizeCheckBox > checkBoxOutput.implicitHeight ?
margins.sizeCheckBox : checkBoxOutput.implicitHeight
AudioControls.CheckBox { AudioControls.CheckBox {
id: checkbox id: checkBoxOutput
anchors.verticalCenter: parent.verticalCenter width: parent.width
anchors.left: parent.left spacing: margins.sizeCheckBox - boxSize
text: display; boxSize: margins.sizeCheckBox / 2
checked: selected; isRound: true
enabled: false; checked: bar.currentIndex === 0 ? selectedDesktop : selectedHMD;
} checkable: !checked
text: devicename
MouseArea { onPressed: {
anchors.fill: checkbox if (!checked) {
onClicked: Audio.setOutputDevice(info); 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 }
}
} }
} }

View file

@ -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"
}
}

View file

@ -9,10 +9,10 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // 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 import "../../controls-uit" as HifiControls
HifiControls.CheckBox { HifiControls.CheckBoxQQC2 {
color: "white" color: "white"
} }

View file

@ -9,8 +9,6 @@
// //
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
Text { Text {
id: root id: root

View file

@ -2057,6 +2057,7 @@ void Application::cleanupBeforeQuit() {
// this must happen after QML, as there are unexplained audio crashes originating in qtwebengine // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine
DependencyManager::destroy<AudioClient>(); DependencyManager::destroy<AudioClient>();
DependencyManager::destroy<AudioInjectorManager>(); DependencyManager::destroy<AudioInjectorManager>();
DependencyManager::destroy<AudioScriptingInterface>();
qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete";
} }

View file

@ -135,10 +135,10 @@ void Audio::setReverbOptions(const AudioEffectOptions* options) {
DependencyManager::get<AudioClient>()->setReverbOptions(options); DependencyManager::get<AudioClient>()->setReverbOptions(options);
} }
void Audio::setInputDevice(const QAudioDeviceInfo& device) { void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
_devices.chooseInputDevice(device); _devices.chooseInputDevice(device, isHMD);
} }
void Audio::setOutputDevice(const QAudioDeviceInfo& device) { void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
_devices.chooseOutputDevice(device); _devices.chooseOutputDevice(device, isHMD);
} }

View file

@ -50,8 +50,8 @@ public:
void showMicMeter(bool show); void showMicMeter(bool show);
void setInputVolume(float volume); void setInputVolume(float volume);
Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device); Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD);
Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device); Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
Q_INVOKABLE void setReverb(bool enable); Q_INVOKABLE void setReverb(bool enable);
Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options);

View file

@ -38,15 +38,17 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
} }
enum AudioDeviceRole { enum AudioDeviceRole {
DisplayRole = Qt::DisplayRole, DeviceNameRole = Qt::UserRole,
CheckStateRole = Qt::CheckStateRole, SelectedDesktopRole,
PeakRole = Qt::UserRole, SelectedHMDRole,
InfoRole = Qt::UserRole + 1 PeakRole,
InfoRole
}; };
QHash<int, QByteArray> AudioDeviceList::_roles { QHash<int, QByteArray> AudioDeviceList::_roles {
{ DisplayRole, "display" }, { DeviceNameRole, "devicename" },
{ CheckStateRole, "selected" }, { SelectedDesktopRole, "selectedDesktop" },
{ SelectedHMDRole, "selectedHMD" },
{ PeakRole, "peak" }, { PeakRole, "peak" },
{ InfoRole, "info" } { InfoRole, "info" }
}; };
@ -68,15 +70,64 @@ static QString getTargetDevice(bool hmd, QAudio::Mode mode) {
Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; 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<AudioDevice> 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 { QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
if (!index.isValid() || index.row() >= rowCount()) { if (!index.isValid() || index.row() >= rowCount()) {
return QVariant(); return QVariant();
} }
if (role == DisplayRole) { if (role == DeviceNameRole) {
return _devices.at(index.row())->display; return _devices.at(index.row())->display;
} else if (role == CheckStateRole) { } else if (role == SelectedDesktopRole) {
return _devices.at(index.row())->selected; return _devices.at(index.row())->selectedDesktop;
} else if (role == SelectedHMDRole) {
return _devices.at(index.row())->selectedHMD;
} else if (role == InfoRole) { } else if (role == InfoRole) {
return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info); return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info);
} else { } else {
@ -130,37 +181,48 @@ void AudioDeviceList::resetDevice(bool contextIsHMD) {
#endif #endif
} }
void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD) {
auto oldDevice = _selectedDevice; auto oldDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
_selectedDevice = device; QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
selectedDevice = device;
for (auto i = 0; i < rowCount(); ++i) { for (auto i = 0; i < _devices.size(); ++i) {
AudioDevice& device = *_devices[i]; std::shared_ptr<AudioDevice> device = _devices[i];
bool &isSelected = isHMD ? device->selectedHMD : device->selectedDesktop;
if (device.selected && device.info != _selectedDevice) { if (isSelected && device->info != selectedDevice) {
device.selected = false; isSelected = false;
} else if (device.info == _selectedDevice) { } else if (device->info == selectedDevice) {
device.selected = true; isSelected = true;
} }
} }
emit deviceChanged(_selectedDevice); emit deviceChanged(selectedDevice);
emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0));
} }
void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) { void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD) {
QAudioDeviceInfo& selectedDevice = isHMD ? _selectedHMDDevice : _selectedDesktopDevice;
const QString& savedDeviceName = isHMD ? _hmdSavedDeviceName : _desktopSavedDeviceName;
beginResetModel(); beginResetModel();
_devices.clear(); _devices.clear();
foreach(const QAudioDeviceInfo& deviceInfo, devices) { foreach(const QAudioDeviceInfo& deviceInfo, devices) {
AudioDevice device; AudioDevice device;
bool &isSelected = isHMD ? device.selectedHMD : device.selectedDesktop;
device.info = deviceInfo; device.info = deviceInfo;
device.display = device.info.deviceName() device.display = device.info.deviceName()
.replace("High Definition", "HD") .replace("High Definition", "HD")
.remove("Device") .remove("Device")
.replace(" )", ")"); .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)); _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::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, 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 // connections are made after client is initialized, so we must also fetch the devices
_inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput)); const QList<QAudioDeviceInfo>& devicesInput = client->getAudioDevices(QAudio::AudioInput);
_outputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioOutput)); const QList<QAudioDeviceInfo>& devicesOutput = client->getAudioDevices(QAudio::AudioOutput);
_inputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioInput)); //setup HMD devices
_outputs.onDevicesChanged(client->getAudioDevices(QAudio::AudioOutput)); _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) { void AudioDevices::onContextChanged(const QString& context) {
_inputs.resetDevice(_contextIsHMD); _inputs.resetDevice(_contextIsHMD);
_outputs.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(); QString deviceName = device.isNull() ? QString() : device.deviceName();
auto& setting = getSetting(_contextIsHMD, mode); auto& setting = getSetting(isHMD, mode);
// check for a previous device // check for a previous device
auto wasDefault = setting.get().isNull(); 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) { void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) {
if (mode == QAudio::AudioInput) { if (mode == QAudio::AudioInput) {
if (_requestedInputDevice == device) { if (_requestedInputDevice == device) {
onDeviceSelected(QAudio::AudioInput, device, _inputs._selectedDevice); onDeviceSelected(QAudio::AudioInput, device,
_contextIsHMD ? _inputs._selectedHMDDevice : _inputs._selectedDesktopDevice,
_contextIsHMD);
_requestedInputDevice = QAudioDeviceInfo(); _requestedInputDevice = QAudioDeviceInfo();
} }
_inputs.onDeviceChanged(device); _inputs.onDeviceChanged(device, _contextIsHMD);
} else { // if (mode == QAudio::AudioOutput) } else { // if (mode == QAudio::AudioOutput)
if (_requestedOutputDevice == device) { if (_requestedOutputDevice == device) {
onDeviceSelected(QAudio::AudioOutput, device, _outputs._selectedDevice); onDeviceSelected(QAudio::AudioOutput, device,
_contextIsHMD ? _outputs._selectedHMDDevice : _outputs._selectedDesktopDevice,
_contextIsHMD);
_requestedOutputDevice = QAudioDeviceInfo(); _requestedOutputDevice = QAudioDeviceInfo();
} }
_outputs.onDeviceChanged(device); _outputs.onDeviceChanged(device, _contextIsHMD);
} }
} }
void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices) { void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices) {
static std::once_flag once; static std::once_flag once;
std::call_once(once, [&] {
//readout settings
auto client = DependencyManager::get<AudioClient>();
_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) { if (mode == QAudio::AudioInput) {
_inputs.onDevicesChanged(devices); _inputs.onDevicesChanged(devices, _contextIsHMD);
_inputs.onDevicesChanged(devices, !_contextIsHMD);
} else { // if (mode == QAudio::AudioOutput) } 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) { void AudioDevices::chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD) {
auto client = DependencyManager::get<AudioClient>(); //check if current context equals device to change
_requestedInputDevice = device; if (_contextIsHMD == isHMD) {
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", auto client = DependencyManager::get<AudioClient>();
Q_ARG(QAudio::Mode, QAudio::AudioInput), _requestedInputDevice = device;
Q_ARG(const QAudioDeviceInfo&, 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) { void AudioDevices::chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD) {
auto client = DependencyManager::get<AudioClient>(); //check if current context equals device to change
_requestedOutputDevice = device; if (_contextIsHMD == isHMD) {
QMetaObject::invokeMethod(client.data(), "switchAudioDevice", auto client = DependencyManager::get<AudioClient>();
Q_ARG(QAudio::Mode, QAudio::AudioOutput), _requestedOutputDevice = device;
Q_ARG(const QAudioDeviceInfo&, 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);
}
} }

View file

@ -25,15 +25,16 @@ class AudioDevice {
public: public:
QAudioDeviceInfo info; QAudioDeviceInfo info;
QString display; QString display;
bool selected { false }; bool selectedDesktop { false };
bool selectedHMD { false };
}; };
class AudioDeviceList : public QAbstractListModel { class AudioDeviceList : public QAbstractListModel {
Q_OBJECT Q_OBJECT
public: public:
AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {} AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput);
~AudioDeviceList() = default; virtual ~AudioDeviceList();
virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device) virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
{ return std::make_shared<AudioDevice>(device); } { return std::make_shared<AudioDevice>(device); }
@ -52,8 +53,8 @@ signals:
void deviceChanged(const QAudioDeviceInfo& device); void deviceChanged(const QAudioDeviceInfo& device);
protected slots: protected slots:
void onDeviceChanged(const QAudioDeviceInfo& device); void onDeviceChanged(const QAudioDeviceInfo& device, bool isHMD);
void onDevicesChanged(const QList<QAudioDeviceInfo>& devices); void onDevicesChanged(const QList<QAudioDeviceInfo>& devices, bool isHMD);
protected: protected:
friend class AudioDevices; friend class AudioDevices;
@ -61,8 +62,11 @@ protected:
static QHash<int, QByteArray> _roles; static QHash<int, QByteArray> _roles;
static Qt::ItemFlags _flags; static Qt::ItemFlags _flags;
const QAudio::Mode _mode; const QAudio::Mode _mode;
QAudioDeviceInfo _selectedDevice; QAudioDeviceInfo _selectedDesktopDevice;
QAudioDeviceInfo _selectedHMDDevice;
QList<std::shared_ptr<AudioDevice>> _devices; QList<std::shared_ptr<AudioDevice>> _devices;
QString _hmdSavedDeviceName;
QString _desktopSavedDeviceName;
}; };
class AudioInputDevice : public AudioDevice { class AudioInputDevice : public AudioDevice {
@ -102,7 +106,6 @@ protected:
void setPeakValuesEnabled(bool enable); void setPeakValuesEnabled(bool enable);
bool _peakValuesEnabled { false }; bool _peakValuesEnabled { false };
}; };
class Audio; class Audio;
class AudioDevices : public QObject { class AudioDevices : public QObject {
@ -112,15 +115,18 @@ class AudioDevices : public QObject {
public: public:
AudioDevices(bool& contextIsHMD); AudioDevices(bool& contextIsHMD);
void chooseInputDevice(const QAudioDeviceInfo& device); virtual ~AudioDevices();
void chooseOutputDevice(const QAudioDeviceInfo& device);
void chooseInputDevice(const QAudioDeviceInfo& device, bool isHMD);
void chooseOutputDevice(const QAudioDeviceInfo& device, bool isHMD);
signals: signals:
void nop(); void nop();
private slots: private slots:
void onContextChanged(const QString& context); 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 onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices); void onDevicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);

View file

@ -1725,14 +1725,6 @@ int AudioClient::setOutputBufferSize(int numFrames, bool persist) {
if (persist) { if (persist) {
_outputBufferSizeFrames.set(numFrames); _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; return numFrames;
} }