diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index 916a7d4889..d6dc5d2736 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -18,6 +18,7 @@ Original.CheckBox { id: checkBox property int colorScheme: hifi.colorSchemes.light + property string color: hifi.colors.lightGray readonly property bool isLightColorScheme: colorScheme == hifi.colorSchemes.light property bool isRedCheck: false property int boxSize: 14 @@ -89,7 +90,7 @@ Original.CheckBox { label: Label { text: control.text - colorScheme: checkBox.colorScheme + color: control.color x: 2 wrapMode: Text.Wrap enabled: checkBox.enabled diff --git a/interface/resources/qml/desktop/Desktop.qml b/interface/resources/qml/desktop/Desktop.qml index 42db16aa72..3dcf747113 100644 --- a/interface/resources/qml/desktop/Desktop.qml +++ b/interface/resources/qml/desktop/Desktop.qml @@ -353,6 +353,14 @@ FocusScope { showDesktop(); } + function ensureTitleBarVisible(targetWindow) { + // Reposition window to ensure that title bar is vertically inside window. + if (targetWindow.frame && targetWindow.frame.decoration) { + var topMargin = -targetWindow.frame.decoration.anchors.topMargin; // Frame's topMargin is a negative value. + targetWindow.y = Math.max(targetWindow.y, topMargin); + } + } + function centerOnVisible(item) { var targetWindow = d.getDesktopWindow(item); if (!targetWindow) { @@ -375,11 +383,12 @@ FocusScope { targetWindow.x = newX; targetWindow.y = newY; + ensureTitleBarVisible(targetWindow); + // If we've noticed that our recommended desktop rect has changed, record that change here. if (recommendedRect != newRecommendedRect) { recommendedRect = newRecommendedRect; } - } function repositionOnVisible(item) { @@ -394,7 +403,6 @@ FocusScope { return; } - var oldRecommendedRect = recommendedRect; var oldRecommendedDimmensions = { x: oldRecommendedRect.width, y: oldRecommendedRect.height }; var newRecommendedRect = Controller.getRecommendedOverlayRect(); @@ -426,7 +434,6 @@ FocusScope { newPosition.y = -1 } - if (newPosition.x === -1 && newPosition.y === -1) { var originRelativeX = (targetWindow.x - oldRecommendedRect.x); var originRelativeY = (targetWindow.y - oldRecommendedRect.y); @@ -444,6 +451,8 @@ FocusScope { } targetWindow.x = newPosition.x; targetWindow.y = newPosition.y; + + ensureTitleBarVisible(targetWindow); } Component { id: messageDialogBuilder; MessageDialog { } } diff --git a/interface/resources/qml/hifi/Audio.qml b/interface/resources/qml/hifi/Audio.qml deleted file mode 100644 index 48de891733..0000000000 --- a/interface/resources/qml/hifi/Audio.qml +++ /dev/null @@ -1,266 +0,0 @@ -// -// Audio.qml -// qml/hifi -// -// Audio setup -// -// Created by Vlad Stelmahovsky on 03/22/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.5 -import QtQuick.Controls 1.4 -import QtGraphicalEffects 1.0 - -import "../styles-uit" -import "../controls-uit" as HifiControls - -import "components" - -Rectangle { - id: audio; - - //put info text here - property alias infoText: infoArea.text - - color: "#404040"; - - HifiConstants { id: hifi; } - objectName: "AudioWindow" - - property string title: "Audio Options" - signal sendToScript(var message); - - - Component { - id: separator - LinearGradient { - start: Qt.point(0, 0) - end: Qt.point(0, 4) - gradient: Gradient { - GradientStop { position: 0.0; color: "#303030" } - GradientStop { position: 0.33; color: "#252525" } // Equivalent of darkGray0 over baseGray background. - GradientStop { position: 0.5; color: "#303030" } - GradientStop { position: 0.6; color: "#454a49" } - GradientStop { position: 1.0; color: "#454a49" } - } - cached: true - } - } - - Column { - anchors { left: parent.left; right: parent.right } - spacing: 8 - - RalewayRegular { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 45 - size: 20 - color: "white" - text: audio.title - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - //connections required to syncronize with Menu - Connections { - target: AudioDevice - onMuteToggled: { - audioMute.checkbox.checked = AudioDevice.getMuted() - } - } - - Connections { - target: AvatarInputs !== undefined ? AvatarInputs : null - onShowAudioToolsChanged: { - audioTools.checkbox.checked = showAudioTools - } - } - - AudioCheckbox { - id: audioMute - width: parent.width - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - checkbox.checked: AudioDevice.muted - text.text: qsTr("Mute microphone") - onCheckBoxClicked: { - AudioDevice.muted = checked - } - } - - AudioCheckbox { - id: audioTools - width: parent.width - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - checkbox.checked: AvatarInputs !== undefined ? AvatarInputs.showAudioTools : false - text.text: qsTr("Show audio level meter") - onCheckBoxClicked: { - if (AvatarInputs !== undefined) { - AvatarInputs.showAudioTools = checked - } - } - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - text: hifi.glyphs.mic - color: hifi.colors.primaryHighlight - anchors.verticalCenter: parent.verticalCenter - size: 32 - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "#AFAFAF" - text: qsTr("CHOOSE INPUT DEVICE") - } - } - - ListView { - id: inputAudioListView - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: 125 - spacing: 0 - clip: true - snapMode: ListView.SnapToItem - model: AudioDevice - delegate: Item { - width: parent.width - visible: devicemode === 0 - height: visible ? 36 : 0 - - AudioCheckbox { - id: cbin - anchors.verticalCenter: parent.verticalCenter - Binding { - target: cbin.checkbox - property: 'checked' - value: devicechecked - } - - width: parent.width - cbchecked: devicechecked - text.text: devicename - onCheckBoxClicked: { - if (checked) { - if (devicename.length > 0) { - console.log("Audio.qml about to call AudioDevice.setInputDeviceAsync().devicename:" + devicename); - AudioDevice.setInputDeviceAsync(devicename); - } else { - console.log("Audio.qml attempted to set input device to empty device name."); - } - } - } - } - } - } - - Loader { - width: parent.width - height: 5 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - text: hifi.glyphs.unmuted - color: hifi.colors.primaryHighlight - anchors.verticalCenter: parent.verticalCenter - size: 32 - } - RalewayRegular { - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "#AFAFAF" - text: qsTr("CHOOSE OUTPUT DEVICE") - } - } - - ListView { - id: outputAudioListView - anchors { left: parent.left; right: parent.right; leftMargin: 70 } - height: 250 - spacing: 0 - clip: true - snapMode: ListView.SnapToItem - model: AudioDevice - delegate: Item { - width: parent.width - visible: devicemode === 1 - height: visible ? 36 : 0 - AudioCheckbox { - id: cbout - width: parent.width - anchors.verticalCenter: parent.verticalCenter - Binding { - target: cbout.checkbox - property: 'checked' - value: devicechecked - } - text.text: devicename - onCheckBoxClicked: { - if (checked) { - if (devicename.length > 0) { - console.log("Audio.qml about to call AudioDevice.setOutputDeviceAsync().devicename:" + devicename); - AudioDevice.setOutputDeviceAsync(devicename); - } else { - console.log("Audio.qml attempted to set output device to empty device name."); - } - - } - } - } - } - } - - Loader { - id: lastSeparator - width: parent.width - height: 6 - sourceComponent: separator - } - - Row { - anchors { left: parent.left; right: parent.right; leftMargin: 30 } - height: 40 - spacing: 8 - - HiFiGlyphs { - id: infoSign - text: hifi.glyphs.info - color: "#AFAFAF" - anchors.verticalCenter: parent.verticalCenter - size: 60 - } - RalewayRegular { - id: infoArea - width: parent.width - infoSign.implicitWidth - parent.spacing - 10 - wrapMode: Text.WordWrap - anchors.verticalCenter: parent.verticalCenter - size: 12 - color: hifi.colors.baseGrayHighlight - } - } - } -} diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index a86defdfd7..91c1d99cf5 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -590,14 +590,11 @@ Item { console.log("This avatar is no longer present. goToUserInDomain() failed."); return; } - var vector = Vec3.subtract(avatar.position, MyAvatar.position); - var distance = Vec3.length(vector); - var target = Vec3.multiply(Vec3.normalize(vector), distance - 2.0); // FIXME: We would like the avatar to recompute the avatar's "maybe fly" test at the new position, so that if high enough up, // the avatar goes into fly mode rather than falling. However, that is not exposed to Javascript right now. // FIXME: it would be nice if this used the same teleport steps and smoothing as in the teleport.js script. // Note, however, that this script allows teleporting to a person in the air, while teleport.js is going to a grounded target. - MyAvatar.orientation = Quat.lookAtSimple(MyAvatar.position, avatar.position); - MyAvatar.position = Vec3.sum(MyAvatar.position, target); + MyAvatar.position = Vec3.sum(avatar.position, Vec3.multiplyQbyV(avatar.orientation, {x: 0, y: 0, z: -2})); + MyAvatar.orientation = Quat.multiply(avatar.orientation, {y: 1}); } } diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml new file mode 100644 index 0000000000..a30aba2a6b --- /dev/null +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -0,0 +1,162 @@ +// +// Audio.qml +// qml/hifi/audio +// +// Audio setup +// +// Created by Vlad Stelmahovsky on 03/22/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.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import "../../styles-uit" +import "../../controls-uit" as HifiControls +import "../../windows" +import "./" as Audio + +Rectangle { + id: root; + + HifiConstants { id: hifi; } + + property var eventBridge; + property string title: "Audio Settings - " + Audio.context; + signal sendToScript(var message); + + color: hifi.colors.baseGray; + + // only show the title if loaded through a "loader" + function showTitle() { + return root.parent.objectName == "loader"; + } + + Column { + y: 16; // padding does not work + spacing: 16; + width: parent.width; + + RalewayRegular { + x: 16; // padding does not work + size: 16; + color: "white"; + text: root.title; + + visible: root.showTitle(); + } + + Separator { visible: root.showTitle() } + + Grid { + columns: 2; + x: 16; // padding does not work + spacing: 16; + + Audio.CheckBox { + text: qsTr("Mute microphone"); + checked: Audio.muted; + onClicked: { + Audio.muted = checked; + checked = Qt.binding(function() { return Audio.muted; }); // restore binding + } + } + Audio.CheckBox { + text: qsTr("Enable noise reduction"); + checked: Audio.noiseReduction; + onClicked: { + Audio.noiseReduction = checked; + checked = Qt.binding(function() { return Audio.noiseReduction; }); // restore binding + } + } + Audio.CheckBox { + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + } + } + + Separator {} + + RowLayout { + HiFiGlyphs { + text: hifi.glyphs.mic; + color: hifi.colors.primaryHighlight; + anchors.verticalCenter: parent.verticalCenter; + size: 28; + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter; + size: 16; + color: hifi.colors.lightGrayText; + text: qsTr("CHOOSE INPUT DEVICE"); + } + } + + ListView { + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 125; + spacing: 0; + snapMode: ListView.SnapToItem; + clip: true; + model: Audio.devices.input; + delegate: Item { + width: parent.width; + height: 36; + Audio.CheckBox { + text: display; + checked: selected; + onClicked: { + selected = checked; + checked = Qt.binding(function() { return selected; }); // restore binding + } + } + } + } + + Separator {} + + 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"); + } + } + + ListView { + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 125; + spacing: 0; + snapMode: ListView.SnapToItem; + clip: true; + model: Audio.devices.output; + delegate: Item { + width: parent.width; + height: 36; + Audio.CheckBox { + text: display; + checked: selected; + onClicked: { + selected = checked; + checked = Qt.binding(function() { return selected; }); // restore binding + } + } + } + } + } +} diff --git a/interface/resources/qml/hifi/audio/CheckBox.qml b/interface/resources/qml/hifi/audio/CheckBox.qml new file mode 100644 index 0000000000..1f632ac479 --- /dev/null +++ b/interface/resources/qml/hifi/audio/CheckBox.qml @@ -0,0 +1,18 @@ +// +// CheckBox.qml +// qml/hifi/audio +// +// Created by Zach Pomerantz on 6/12/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.5 + +import "../../controls-uit" as HifiControls + +HifiControls.CheckBox { + color: "white" +} diff --git a/interface/resources/qml/hifi/components/AudioCheckbox.qml b/interface/resources/qml/hifi/components/AudioCheckbox.qml deleted file mode 100644 index b037fe4c7d..0000000000 --- a/interface/resources/qml/hifi/components/AudioCheckbox.qml +++ /dev/null @@ -1,30 +0,0 @@ -import QtQuick 2.5 -import QtQuick.Controls 1.4 - -import "../../styles-uit" -import "../../controls-uit" as HifiControls - -Row { - id: row - spacing: 16 - property alias checkbox: cb - property alias cbchecked: cb.checked - property alias text: txt - signal checkBoxClicked(bool checked) - - HifiControls.CheckBox { - id: cb - boxSize: 20 - colorScheme: hifi.colorSchemes.dark - anchors.verticalCenter: parent.verticalCenter - onClicked: checkBoxClicked(cb.checked) - } - RalewayBold { - id: txt - wrapMode: Text.WordWrap - width: parent.width - cb.boxSize - 30 - anchors.verticalCenter: parent.verticalCenter - size: 16 - color: "white" - } -} diff --git a/interface/resources/qml/hifi/dialogs/Audio.qml b/interface/resources/qml/hifi/dialogs/Audio.qml new file mode 100644 index 0000000000..4ce9e14c42 --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/Audio.qml @@ -0,0 +1,27 @@ +// +// Audio.qml +// +// Created by Zach Pomerantz on 6/12/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 "../../windows" +import "../audio" + +ScrollingWindow { + id: root; + + resizable: true; + destroyOnHidden: true; + width: 400; + height: 577; + minSize: Qt.vector2d(400, 500); + + Audio { id: audio; width: root.width } + + objectName: "AudioDialog"; + title: audio.title; +} diff --git a/interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml b/interface/resources/qml/hifi/dialogs/AudioBuffers.qml similarity index 82% rename from interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml rename to interface/resources/qml/hifi/dialogs/AudioBuffers.qml index 0c7c881bbf..42e0781adc 100644 --- a/interface/resources/qml/hifi/dialogs/AudioPreferencesDialog.qml +++ b/interface/resources/qml/hifi/dialogs/AudioBuffers.qml @@ -5,9 +5,9 @@ import "../../dialogs" PreferencesDialog { id: root - objectName: "AudioPreferencesDialog" + objectName: "AudioBuffersDialog" title: "Audio Settings" - showCategories: ["Audio"] + showCategories: ["Audio Buffers"] property var settings: Settings { category: root.objectName property alias x: root.x @@ -16,4 +16,3 @@ PreferencesDialog { property alias height: root.height } } - diff --git a/interface/resources/qml/hifi/tablet/Tablet.qml b/interface/resources/qml/hifi/tablet/Tablet.qml index 18f88b7718..25cbd02426 100644 --- a/interface/resources/qml/hifi/tablet/Tablet.qml +++ b/interface/resources/qml/hifi/tablet/Tablet.qml @@ -6,16 +6,10 @@ Item { id: tablet objectName: "tablet" property double micLevel: 0.8 - property bool micEnabled: true property int rowIndex: 0 property int columnIndex: 0 property int count: (flowMain.children.length - 1) - // called by C++ code to keep mic state updated - function setMicEnabled(newMicEnabled) { - tablet.micEnabled = newMicEnabled; - } - // called by C++ code to keep audio bar updated function setMicLevel(newMicLevel) { tablet.micLevel = newMicLevel; @@ -121,8 +115,8 @@ Item { } Item { - visible: (!tablet.micEnabled && !toggleMuteMouseArea.containsMouse) - || (tablet.micEnabled && toggleMuteMouseArea.containsMouse) + visible: (Audio.muted && !toggleMuteMouseArea.containsMouse) + || (!Audio.muted && toggleMuteMouseArea.containsMouse) Image { id: muteIcon @@ -201,7 +195,7 @@ Item { preventStealing: true propagateComposedEvents: false scrollGestureEnabled: false - onClicked: tabletRoot.toggleMicEnabled() + onClicked: { Audio.muted = !Audio.muted } } RalewaySemiBold { @@ -271,7 +265,7 @@ Item { PropertyChanges { target: muteIcon - visible: micEnabled + visible: !Audio.muted } PropertyChanges { diff --git a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml similarity index 67% rename from interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml rename to interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml index b30e741be9..1b4d0feaca 100644 --- a/interface/resources/qml/hifi/tablet/TabletAudioPreferences.qml +++ b/interface/resources/qml/hifi/tablet/TabletAudioBuffers.qml @@ -1,7 +1,7 @@ // -// TabletAudioPreferences.qml +// TabletAudioBuffers.qml // -// Created by Davd Rowe on 7 Mar 2017. +// Created by Zach Pomerantz on 6/5/2017. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -17,7 +17,9 @@ StackView { id: profileRoot initialItem: root objectName: "stack" - property string title: "Audio Settings" + property string title: "Audio Buffers" + property alias gotoPreviousApp: root.gotoPreviousApp; + property var eventBridge; signal sendToScript(var message); @@ -31,7 +33,7 @@ StackView { TabletPreferencesDialog { id: root - objectName: "TabletAudioPreferences" - showCategories: ["Audio"] + objectName: "TabletAudioBuffersDialog" + showCategories: ["Audio Buffers"] } } diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index b6cdce0853..faa4013bce 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -147,10 +147,6 @@ Item { } } - function toggleMicEnabled() { - ApplicationInterface.toggleMuteAudio(); - } - function setUsername(newUsername) { username = newUsername; } diff --git a/interface/resources/qml/hifi/tablet/WindowRoot.qml b/interface/resources/qml/hifi/tablet/WindowRoot.qml index 12f302d60a..ea0c2844a1 100644 --- a/interface/resources/qml/hifi/tablet/WindowRoot.qml +++ b/interface/resources/qml/hifi/tablet/WindowRoot.qml @@ -71,10 +71,6 @@ Windows.ScrollingWindow { } } - function toggleMicEnabled() { - ApplicationInterface.toggleMuteAudio(); - } - function setUsername(newUsername) { username = newUsername; } diff --git a/interface/resources/qml/styles-uit/Separator.qml b/interface/resources/qml/styles-uit/Separator.qml new file mode 100644 index 0000000000..4134b928a7 --- /dev/null +++ b/interface/resources/qml/styles-uit/Separator.qml @@ -0,0 +1,41 @@ +// +// Separator.qml +// +// Created by Zach Fox on 2017-06-06 +// 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.5 +import "../styles-uit" + +Item { + // Size + height: 2; + width: parent.width; + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + anchors.bottomMargin: height; + // Style + color: hifi.colors.baseGrayShadow; + } + + Rectangle { + // Size + width: parent.width; + height: 1; + // Anchors + anchors.left: parent.left; + anchors.bottom: parent.bottom; + // Style + color: hifi.colors.baseGrayHighlight; + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 287ac2931e..65f15af550 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -150,11 +150,11 @@ #include "InterfaceLogging.h" #include "LODManager.h" #include "ModelPackager.h" +#include "scripting/Audio.h" #include "networking/CloseEventSender.h" #include "scripting/TestScriptingInterface.h" #include "scripting/AccountScriptingInterface.h" #include "scripting/AssetMappingsScriptingInterface.h" -#include "scripting/AudioDeviceScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" #include "scripting/GlobalServicesScriptingInterface.h" @@ -251,6 +251,8 @@ static const QString MARKETPLACE_CDN_HOSTNAME = "mpassets.highfidelity.com"; static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; +static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; + const QHash Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, { SVO_JSON_EXTENSION, &Application::importSVOFromURL }, @@ -711,9 +713,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo recorder->recordFrame(AUDIO_FRAME_TYPE, audio); } }); + audioIO->startThread(); - auto audioScriptingInterface = DependencyManager::set(); - connect(audioIO.data(), &AudioClient::muteToggled, this, &Application::audioMuteToggled); + auto audioScriptingInterface = DependencyManager::set(); connect(audioIO.data(), &AudioClient::mutedByMixer, audioScriptingInterface.data(), &AudioScriptingInterface::mutedByMixer); connect(audioIO.data(), &AudioClient::receivedFirstPacket, audioScriptingInterface.data(), &AudioScriptingInterface::receivedFirstPacket); connect(audioIO.data(), &AudioClient::disconnected, audioScriptingInterface.data(), &AudioScriptingInterface::disconnected); @@ -729,8 +731,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo audioScriptingInterface->environmentMuted(); } }); - - audioIO->startThread(); + connect(this, &Application::activeDisplayPluginChanged, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); ResourceManager::init(); // Make sure we don't time out during slow operations at startup @@ -1604,12 +1606,12 @@ QString Application::getUserAgent() { return userAgent; } -void Application::toggleTabletUI() const { +void Application::toggleTabletUI(bool shouldOpen) const { auto tabletScriptingInterface = DependencyManager::get(); auto hmd = DependencyManager::get(); - TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); bool messageOpen = tablet->isMessageDialogOpen(); - if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) { + if ((!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) && !(shouldOpen && hmd->getShouldShowTablet())) { auto HMD = DependencyManager::get(); HMD->toggleShouldShowTablet(); } @@ -1986,7 +1988,6 @@ void Application::initializeUi() { surfaceContext->setContextProperty("Stats", Stats::getInstance()); surfaceContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); surfaceContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); - surfaceContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); surfaceContext->setContextProperty("AvatarBookmarks", DependencyManager::get().data()); surfaceContext->setContextProperty("LocationBookmarks", DependencyManager::get().data()); @@ -2315,12 +2316,6 @@ void Application::runTests() { runUnitTests(); } -void Application::audioMuteToggled() const { - QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio); - Q_CHECK_PTR(muteAction); - muteAction->setChecked(DependencyManager::get()->isMuted()); -} - void Application::faceTrackerMuteToggled() { QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteFaceTracking); @@ -2390,7 +2385,7 @@ void Application::showHelp() { queryString.addQueryItem("handControllerName", handControllerName); queryString.addQueryItem("defaultTab", defaultTab); auto tabletScriptingInterface = DependencyManager::get(); - TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); tablet->gotoWebScreen(INFO_HELP_PATH + "?" + queryString.toString()); //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } @@ -2846,7 +2841,7 @@ void Application::keyPressEvent(QKeyEvent* event) { if (isShifted && isMeta) { auto offscreenUi = DependencyManager::get(); offscreenUi->togglePinned(); - //offscreenUi->getRootContext()->engine()->clearComponentCache(); + //offscreenUi->getSurfaceContext()->engine()->clearComponentCache(); //OffscreenUi::information("Debugging", "Component cache cleared"); // placeholder for dialogs being converted to QML. } @@ -2887,6 +2882,12 @@ void Application::keyPressEvent(QKeyEvent* event) { Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox); break; + case Qt::Key_M: + if (isMeta) { + DependencyManager::get()->toggleMute(); + } + break; + case Qt::Key_N: if (!isOption && !isShifted && isMeta) { DependencyManager::get()->toggleIgnoreRadius(); @@ -4471,10 +4472,11 @@ void Application::update(float deltaTime) { } else { const quint64 MUTE_MICROPHONE_AFTER_USECS = 5000000; //5 secs Menu* menu = Menu::getInstance(); - if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !menu->isOptionChecked(MenuOption::MuteAudio)) { + auto audioClient = DependencyManager::get(); + if (menu->isOptionChecked(MenuOption::AutoMuteAudio) && !audioClient->isMuted()) { if (_lastFaceTrackerUpdate > 0 && ((usecTimestampNow() - _lastFaceTrackerUpdate) > MUTE_MICROPHONE_AFTER_USECS)) { - menu->triggerOption(MenuOption::MuteAudio); + audioClient->toggleMute(); _lastFaceTrackerUpdate = 0; } } else { @@ -5520,7 +5522,6 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get()->getStats().data()); scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get().data()); scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get().data()); @@ -5799,38 +5800,24 @@ bool Application::displayAvatarAttachmentConfirmationDialog(const QString& name) } } -void Application::toggleRunningScriptsWidget() const { - auto scriptEngines = DependencyManager::get(); - bool scriptsRunning = !scriptEngines->getRunningScripts().isEmpty(); - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); +void Application::showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const { + auto tablet = DependencyManager::get()->getTablet(SYSTEM_TABLET); + auto hmd = DependencyManager::get(); + bool onTablet = false; - if (tablet->getToolbarMode() || false == scriptsRunning) { - static const QUrl url("hifi/dialogs/RunningScripts.qml"); - DependencyManager::get()->show(url, "RunningScripts"); - } else { - auto hmd = DependencyManager::get(); - if (!hmd->getShouldShowTablet() && !isHMDMode()) { - static const QUrl url("hifi/dialogs/RunningScripts.qml"); - DependencyManager::get()->show(url, "RunningScripts"); - } else { - static const QUrl url("../../hifi/dialogs/TabletRunningScripts.qml"); - tablet->pushOntoStack(url); + if (!tablet->getToolbarMode()) { + onTablet = tablet->pushOntoStack(tabletUrl); + if (onTablet) { + toggleTabletUI(true); } } - //DependencyManager::get()->show(url, "RunningScripts"); - //if (_runningScriptsWidget->isVisible()) { - // if (_runningScriptsWidget->hasFocus()) { - // _runningScriptsWidget->hide(); - // } else { - // _runningScriptsWidget->raise(); - // setActiveWindow(_runningScriptsWidget); - // _runningScriptsWidget->setFocus(); - // } - //} else { - // _runningScriptsWidget->show(); - // _runningScriptsWidget->setFocus(); - //} + + if (!onTablet) { + DependencyManager::get()->show(widgetUrl, name); + } + if (tablet->getToolbarMode()) { + DependencyManager::get()->show(widgetUrl, name); + } } void Application::showScriptLogs() { @@ -5852,7 +5839,7 @@ void Application::showAssetServerWidget(QString filePath) { } }; auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); auto hmd = DependencyManager::get(); if (tablet->getToolbarMode()) { DependencyManager::get()->show(url, "AssetServer", startUpload); @@ -5887,21 +5874,6 @@ void Application::addAssetToWorldFromURL(QString url) { request->send(); } -void Application::showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const { - auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); - auto hmd = DependencyManager::get(); - if (tablet->getToolbarMode()) { - DependencyManager::get()->show(desktopURL, name); - } else { - tablet->pushOntoStack(tabletURL); - if (!hmd->getShouldShowTablet() && !isHMDMode()) { - hmd->openTablet(); - } - - } -} - void Application::addAssetToWorldFromURLRequestFinished() { auto request = qobject_cast(sender()); auto url = request->getUrl().toString(); @@ -6385,7 +6357,7 @@ void Application::loadScriptURLDialog() const { void Application::loadLODToolsDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->lodTools(); @@ -6397,7 +6369,7 @@ void Application::loadLODToolsDialog() { void Application::loadEntityStatisticsDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->octreeStatsDetails(); @@ -6408,7 +6380,7 @@ void Application::loadEntityStatisticsDialog() { void Application::loadDomainConnectionDialog() { auto tabletScriptingInterface = DependencyManager::get(); - auto tablet = dynamic_cast(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); + auto tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) { auto dialogsManager = DependencyManager::get(); dialogsManager->showDomainConnectionDialog(); @@ -7065,11 +7037,6 @@ void Application::updateSystemTabletMode() { } } -void Application::toggleMuteAudio() { - auto menu = Menu::getInstance(); - menu->setIsOptionChecked(MenuOption::MuteAudio, !menu->isOptionChecked(MenuOption::MuteAudio)); -} - OverlayID Application::getTabletScreenID() const { auto HMD = DependencyManager::get(); return HMD->getCurrentTabletScreenID(); diff --git a/interface/src/Application.h b/interface/src/Application.h index bcb5e2bc10..adcdbe7b60 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -318,11 +318,10 @@ public slots: Q_INVOKABLE void loadScriptURLDialog() const; void toggleLogDialog(); void toggleEntityScriptServerLogDialog(); - void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; - void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const; + void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); @@ -391,7 +390,6 @@ public slots: void addAssetToWorldMessageClose(); - Q_INVOKABLE void toggleMuteAudio(); void loadLODToolsDialog(); void loadEntityStatisticsDialog(); void loadDomainConnectionDialog(); @@ -405,7 +403,6 @@ private slots: void resettingDomain(); - void audioMuteToggled() const; void faceTrackerMuteToggled(); void activeChanged(Qt::ApplicationState state); @@ -503,7 +500,7 @@ private: static void dragEnterEvent(QDragEnterEvent* event); void maybeToggleMenuVisible(QMouseEvent* event) const; - void toggleTabletUI() const; + void toggleTabletUI(bool shouldOpen = false) const; MainWindow* _window; QElapsedTimer& _sessionRunTimer; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 463069430d..bc771594a9 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -94,8 +94,13 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, redoAction); // Edit > Running Scripts - addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, - qApp, SLOT(toggleRunningScriptsWidget())); + auto action = addActionToQMenuAndActionHash(editMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J); + connect(action, &QAction::triggered, [] { + static const QUrl widgetUrl("hifi/dialogs/RunningScripts.qml"); + static const QUrl tabletUrl("../../hifi/dialogs/TabletRunningScripts.qml"); + static const QString name("RunningScripts"); + qApp->showDialog(widgetUrl, tabletUrl, name); + }); // Edit > Open and Run Script from File... [advanced] addActionToQMenuAndActionHash(editMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, @@ -143,28 +148,13 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, MenuOption::ReloadContent, 0, qApp, SLOT(reloadResourceCaches()), QAction::NoRole, UNSPECIFIED_POSITION, "Advanced"); - - // Audio menu ---------------------------------- - MenuWrapper* audioMenu = addMenu("Audio"); - auto audioIO = DependencyManager::get(); - - // Audio > Mute - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, - audioIO.data(), SLOT(toggleMute())); - - // Audio > Show Level Meter - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioTools, 0, false); - - addCheckableActionToQMenuAndActionHash(audioMenu, MenuOption::AudioNoiseReduction, 0, true, - audioIO.data(), SLOT(toggleAudioNoiseReduction())); - // Avatar menu ---------------------------------- MenuWrapper* avatarMenu = addMenu("Avatar"); auto avatarManager = DependencyManager::get(); auto avatar = avatarManager->getMyAvatar(); // Avatar > Attachments... - auto action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments); + action = addActionToQMenuAndActionHash(avatarMenu, MenuOption::Attachments); connect(action, &QAction::triggered, [] { qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"), QString("../../hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog"); @@ -297,6 +287,14 @@ Menu::Menu() { QString("../../hifi/tablet/TabletGeneralPreferences.qml"), "GeneralPreferencesDialog"); }); + action = addActionToQMenuAndActionHash(settingsMenu, "Audio..."); + connect(action, &QAction::triggered, [] { + static const QUrl widgetUrl("hifi/dialogs/Audio.qml"); + static const QUrl tabletUrl("../../hifi/audio/Audio.qml"); + static const QString name("AudioDialog"); + qApp->showDialog(widgetUrl, tabletUrl, name); + }); + // Settings > Avatar... action = addActionToQMenuAndActionHash(settingsMenu, "Avatar..."); connect(action, &QAction::triggered, [] { @@ -622,10 +620,11 @@ Menu::Menu() { action = addActionToQMenuAndActionHash(audioDebugMenu, "Buffers..."); connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/AudioPreferencesDialog.qml"), - QString("../../hifi/tablet/TabletAudioPreferences.qml"), "AudioPreferencesDialog"); + qApp->showDialog(QString("hifi/dialogs/AudioBuffers.qml"), + QString("../../hifi/tablet/TabletAudioBuffers.qml"), "AudioBuffersDialog"); }); + auto audioIO = DependencyManager::get(); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, audioIO.data(), SLOT(toggleServerEcho())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 41bf89a20a..f69860099d 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -36,7 +36,6 @@ namespace MenuOption { const QString AssetMigration = "ATP Asset Migration"; const QString AssetServer = "Asset Browser"; const QString Attachments = "Attachments..."; - const QString AudioNoiseReduction = "Noise Reduction"; const QString AudioScope = "Show Scope"; const QString AudioScopeFiftyFrames = "Fifty"; const QString AudioScopeFiveFrames = "Five"; @@ -44,7 +43,6 @@ namespace MenuOption { const QString AudioScopePause = "Pause Scope"; const QString AudioScopeTwentyFrames = "Twenty"; const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; - const QString AudioTools = "Show Level Meter"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; const QString AvatarBookmarks = "Avatar Bookmarks"; @@ -124,7 +122,6 @@ namespace MenuOption { const QString LogExtraTimings = "Log Extra Timing Details"; const QString LowVelocityFilter = "Low Velocity Filter"; const QString MeshVisible = "Draw Mesh"; - const QString MuteAudio = "Mute Microphone"; const QString MuteEnvironment = "Mute Environment"; const QString MuteFaceTracking = "Mute Face Tracking"; const QString NamesAboveHeads = "Names Above Heads"; diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp new file mode 100644 index 0000000000..0b99e60c89 --- /dev/null +++ b/interface/src/scripting/Audio.cpp @@ -0,0 +1,109 @@ +// +// Audio.cpp +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/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 +// + +#include "Audio.h" + +#include "Application.h" +#include "AudioClient.h" +#include "ui/AvatarInputs.h" + +using namespace scripting; + +QString Audio::AUDIO { "Audio" }; +QString Audio::DESKTOP { "Desktop" }; +QString Audio::HMD { "VR" }; + +Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; + +Audio::Audio() : _devices(_contextIsHMD) { + auto client = DependencyManager::get(); + connect(client.data(), &AudioClient::muteToggled, this, &Audio::onMutedChanged); + connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); + connect(&_devices._inputs, &AudioDeviceList::deviceChanged, this, &Audio::onInputChanged); + enableNoiseReduction(enableNoiseReductionSetting.get()); +} + +void Audio::setMuted(bool isMuted) { + if (_isMuted != isMuted) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "toggleMute", Qt::BlockingQueuedConnection); + + _isMuted = isMuted; + emit mutedChanged(_isMuted); + } +} + +void Audio::onMutedChanged() { + auto client = DependencyManager::get().data(); + bool isMuted; + QMetaObject::invokeMethod(client, "isMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isMuted)); + + if (_isMuted != isMuted) { + _isMuted = isMuted; + emit mutedChanged(_isMuted); + } +} + +void Audio::enableNoiseReduction(bool enable) { + if (_enableNoiseReduction != enable) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setNoiseReduction", Qt::BlockingQueuedConnection, Q_ARG(bool, enable)); + + enableNoiseReductionSetting.set(enable); + _enableNoiseReduction = enable; + emit noiseReductionChanged(enable); + } +} + +void Audio::setInputVolume(float volume) { + // getInputVolume will not reflect changes synchronously, so clamp beforehand + volume = glm::clamp(volume, 0.0f, 1.0f); + + if (_inputVolume != volume) { + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setInputVolume", Qt::BlockingQueuedConnection, Q_ARG(float, volume)); + + _inputVolume = volume; + emit inputVolumeChanged(_inputVolume); + } +} + +// different audio input devices may have different volumes +void Audio::onInputChanged() { + auto client = DependencyManager::get().data(); + float volume; + QMetaObject::invokeMethod(client, "getInputVolume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(float, volume)); + + if (_inputVolume != volume) { + _inputVolume = volume; + emit inputVolumeChanged(_inputVolume); + } +} + +QString Audio::getContext() const { + return _contextIsHMD ? Audio::HMD : Audio::DESKTOP; +} + +void Audio::onContextChanged() { + bool isHMD = qApp->isHMDMode(); + if (_contextIsHMD != isHMD) { + _contextIsHMD = isHMD; + emit contextChanged(getContext()); + } +} + +void Audio::setReverb(bool enable) { + DependencyManager::get()->setReverb(enable); +} + +void Audio::setReverbOptions(const AudioEffectOptions* options) { + DependencyManager::get()->setReverbOptions(options); +} \ No newline at end of file diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h new file mode 100644 index 0000000000..d438df41ca --- /dev/null +++ b/interface/src/scripting/Audio.h @@ -0,0 +1,81 @@ +// +// Audio.h +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/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 +// + +#ifndef hifi_scripting_Audio_h +#define hifi_scripting_Audio_h + +#include "AudioScriptingInterface.h" +#include "AudioDevices.h" +#include "AudioEffectOptions.h" +#include "SettingHandle.h" + +namespace scripting { + +class Audio : public AudioScriptingInterface { + Q_OBJECT + SINGLETON_DEPENDENCY + + Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) + Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) + Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged) + Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) + Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + +public: + static QString AUDIO; + static QString HMD; + static QString DESKTOP; + + virtual ~Audio() {} + + bool isMuted() const { return _isMuted; } + bool noiseReductionEnabled() const { return _enableNoiseReduction; } + float getInputVolume() const { return _inputVolume; } + QString getContext() const; + + void setMuted(bool muted); + void enableNoiseReduction(bool enable); + void showMicMeter(bool show); + void setInputVolume(float volume); + + Q_INVOKABLE void setReverb(bool enable); + Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); + +signals: + void nop(); + void mutedChanged(bool isMuted); + void noiseReductionChanged(bool isEnabled); + void inputVolumeChanged(float volume); + void contextChanged(const QString& context); + +public slots: + void onMutedChanged(); + void onContextChanged(); + void onInputChanged(); + +protected: + // Audio must live on a separate thread from AudioClient to avoid deadlocks + Audio(); + +private: + + float _inputVolume { 1.0f }; + bool _isMuted { false }; + bool _enableNoiseReduction; + bool _contextIsHMD { false }; + + AudioDevices* getDevices() { return &_devices; } + AudioDevices _devices; +}; + +}; + +#endif // hifi_scripting_Audio_h diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.cpp b/interface/src/scripting/AudioDeviceScriptingInterface.cpp deleted file mode 100644 index 1fd30af0e3..0000000000 --- a/interface/src/scripting/AudioDeviceScriptingInterface.cpp +++ /dev/null @@ -1,317 +0,0 @@ -// -// AudioDeviceScriptingInterface.cpp -// interface/src/scripting -// -// Created by Brad Hefta-Gaub on 3/23/14. -// Copyright 2014 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 -// - -#include -#include - -#include "AudioDeviceScriptingInterface.h" -#include "SettingsScriptingInterface.h" - -AudioDeviceScriptingInterface* AudioDeviceScriptingInterface::getInstance() { - static AudioDeviceScriptingInterface sharedInstance; - return &sharedInstance; -} - -QStringList AudioDeviceScriptingInterface::inputAudioDevices() const { - return _inputAudioDevices; -} - -QStringList AudioDeviceScriptingInterface::outputAudioDevices() const { - return _outputAudioDevices; -} - -bool AudioDeviceScriptingInterface::muted() -{ - return getMuted(); -} - -AudioDeviceScriptingInterface::AudioDeviceScriptingInterface(): QAbstractListModel(nullptr) { - connect(DependencyManager::get().data(), &AudioClient::muteToggled, - this, &AudioDeviceScriptingInterface::muteToggled); - connect(DependencyManager::get().data(), &AudioClient::deviceChanged, - this, &AudioDeviceScriptingInterface::onDeviceChanged, Qt::QueuedConnection); - connect(DependencyManager::get().data(), &AudioClient::currentInputDeviceChanged, - this, &AudioDeviceScriptingInterface::onCurrentInputDeviceChanged, Qt::QueuedConnection); - connect(DependencyManager::get().data(), &AudioClient::currentOutputDeviceChanged, - this, &AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged, Qt::QueuedConnection); - //fill up model - onDeviceChanged(); - //set up previously saved device - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - const QString inDevice = settings->getValue("audio_input_device", _currentInputDevice).toString(); - if (inDevice != _currentInputDevice) { - // before using the old setting, check to make sure the device still exists.... - bool inDeviceExists = DependencyManager::get()->getNamedAudioDeviceForModeExists(QAudio::AudioInput, inDevice); - - if (inDeviceExists) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << inDevice << "] _currentInputDevice:" << _currentInputDevice; - setInputDeviceAsync(inDevice); - } else { - qCDebug(audioclient) << __FUNCTION__ << "previously selected device no longer exists inDevice: [" << inDevice << "] keeping device _currentInputDevice:" << _currentInputDevice; - } - } - - // If the audio_output_device setting is not available, use the _currentOutputDevice - auto outDevice = settings->getValue("audio_output_device", _currentOutputDevice).toString(); - - if (outDevice != _currentOutputDevice) { - // before using the old setting, check to make sure the device still exists.... - bool outDeviceExists = DependencyManager::get()->getNamedAudioDeviceForModeExists(QAudio::AudioOutput, outDevice); - - if (outDeviceExists) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() outDevice: [" << outDevice << "] _currentOutputDevice:" << _currentOutputDevice; - setOutputDeviceAsync(outDevice); - } else { - qCDebug(audioclient) << __FUNCTION__ << "previously selected device no longer exists outDevice: [" << outDevice << "] keeping device _currentOutputDevice:" << _currentOutputDevice; - } - - } -} - -bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - bool result; - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, deviceName)); - return result; -} - -bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) { - - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - bool result; - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", - Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, deviceName)); - return result; -} - -bool AudioDeviceScriptingInterface::setDeviceFromMenu(const QString& deviceMenuName) { - QAudio::Mode mode; - - if (deviceMenuName.indexOf("for Output") != -1) { - mode = QAudio::AudioOutput; - } else if (deviceMenuName.indexOf("for Input") != -1) { - mode = QAudio::AudioInput; - } else { - return false; - } - - for (ScriptingAudioDeviceInfo di: _devices) { - if (mode == di.mode && deviceMenuName.contains(di.name)) { - if (mode == QAudio::AudioOutput) { - qCDebug(audioclient) << __FUNCTION__ << "about to call setOutputDeviceAsync() device: [" << di.name << "]"; - setOutputDeviceAsync(di.name); - } else { - qCDebug(audioclient) << __FUNCTION__ << "about to call setInputDeviceAsync() device: [" << di.name << "]"; - setInputDeviceAsync(di.name); - } - return true; - } - } - - return false; -} - -void AudioDeviceScriptingInterface::setInputDeviceAsync(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - if (deviceName.isEmpty()) { - qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; - return; - } - - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchInputToAudioDevice", - Qt::QueuedConnection, - Q_ARG(const QString&, deviceName)); -} - -void AudioDeviceScriptingInterface::setOutputDeviceAsync(const QString& deviceName) { - qCDebug(audioclient) << __FUNCTION__ << "deviceName:" << deviceName; - - if (deviceName.isEmpty()) { - qCDebug(audioclient) << __FUNCTION__ << "attempt to set empty deviceName:" << deviceName << "... ignoring!"; - return; - } - - QMetaObject::invokeMethod(DependencyManager::get().data(), "switchOutputToAudioDevice", - Qt::QueuedConnection, - Q_ARG(const QString&, deviceName)); -} - -QString AudioDeviceScriptingInterface::getInputDevice() { - return DependencyManager::get()->getDeviceName(QAudio::AudioInput); -} - -QString AudioDeviceScriptingInterface::getOutputDevice() { - return DependencyManager::get()->getDeviceName(QAudio::AudioOutput); -} - -QString AudioDeviceScriptingInterface::getDefaultInputDevice() { - return DependencyManager::get()->getDefaultDeviceName(QAudio::AudioInput); -} - -QString AudioDeviceScriptingInterface::getDefaultOutputDevice() { - return DependencyManager::get()->getDefaultDeviceName(QAudio::AudioOutput); -} - -QVector AudioDeviceScriptingInterface::getInputDevices() { - return DependencyManager::get()->getDeviceNames(QAudio::AudioInput); -} - -QVector AudioDeviceScriptingInterface::getOutputDevices() { - return DependencyManager::get()->getDeviceNames(QAudio::AudioOutput); -} - -float AudioDeviceScriptingInterface::getInputVolume() { - return DependencyManager::get()->getInputVolume(); -} - -void AudioDeviceScriptingInterface::setInputVolume(float volume) { - DependencyManager::get()->setInputVolume(volume); -} - -void AudioDeviceScriptingInterface::setReverb(bool reverb) { - DependencyManager::get()->setReverb(reverb); -} - -void AudioDeviceScriptingInterface::setReverbOptions(const AudioEffectOptions* options) { - DependencyManager::get()->setReverbOptions(options); -} - -void AudioDeviceScriptingInterface::toggleMute() { - DependencyManager::get()->toggleMute(); -} - -void AudioDeviceScriptingInterface::setMuted(bool muted) -{ - bool lMuted = getMuted(); - if (lMuted == muted) - return; - - toggleMute(); - lMuted = getMuted(); - emit mutedChanged(lMuted); -} - -bool AudioDeviceScriptingInterface::getMuted() { - return DependencyManager::get()->isMuted(); -} - -QVariant AudioDeviceScriptingInterface::data(const QModelIndex& index, int role) const { - //sanity - if (!index.isValid() || index.row() >= _devices.size()) - return QVariant(); - - - if (role == Qt::DisplayRole || role == DisplayNameRole) { - return _devices.at(index.row()).name; - } else if (role == SelectedRole) { - return _devices.at(index.row()).selected; - } else if (role == AudioModeRole) { - return (int)_devices.at(index.row()).mode; - } - return QVariant(); -} - -int AudioDeviceScriptingInterface::rowCount(const QModelIndex& parent) const { - Q_UNUSED(parent) - return _devices.size(); -} - -QHash AudioDeviceScriptingInterface::roleNames() const { - QHash roles; - roles.insert(DisplayNameRole, "devicename"); - roles.insert(SelectedRole, "devicechecked"); - roles.insert(AudioModeRole, "devicemode"); - return roles; -} - -void AudioDeviceScriptingInterface::onDeviceChanged() -{ - beginResetModel(); - _outputAudioDevices.clear(); - _devices.clear(); - _currentOutputDevice = getOutputDevice(); - for (QString name: getOutputDevices()) { - ScriptingAudioDeviceInfo di; - di.name = name; - di.selected = (name == _currentOutputDevice); - di.mode = QAudio::AudioOutput; - _devices.append(di); - _outputAudioDevices.append(name); - } - emit outputAudioDevicesChanged(_outputAudioDevices); - - _inputAudioDevices.clear(); - _currentInputDevice = getInputDevice(); - for (QString name: getInputDevices()) { - ScriptingAudioDeviceInfo di; - di.name = name; - di.selected = (name == _currentInputDevice); - di.mode = QAudio::AudioInput; - _devices.append(di); - _inputAudioDevices.append(name); - } - emit inputAudioDevicesChanged(_inputAudioDevices); - - endResetModel(); - emit deviceChanged(); -} - -void AudioDeviceScriptingInterface::onCurrentInputDeviceChanged(const QString& name) -{ - currentDeviceUpdate(name, QAudio::AudioInput); - //we got a signal that device changed. Save it now - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - settings->setValue("audio_input_device", name); - emit currentInputDeviceChanged(name); -} - -void AudioDeviceScriptingInterface::onCurrentOutputDeviceChanged(const QString& name) -{ - currentDeviceUpdate(name, QAudio::AudioOutput); - - // FIXME - this is kinda janky to set the setting on the result of a signal - //we got a signal that device changed. Save it now - SettingsScriptingInterface* settings = SettingsScriptingInterface::getInstance(); - qCDebug(audioclient) << __FUNCTION__ << "about to call settings->setValue('audio_output_device', name); name:" << name; - settings->setValue("audio_output_device", name); - emit currentOutputDeviceChanged(name); -} - -void AudioDeviceScriptingInterface::currentDeviceUpdate(const QString& name, QAudio::Mode mode) -{ - QVector role; - role.append(SelectedRole); - - for (int i = 0; i < _devices.size(); i++) { - ScriptingAudioDeviceInfo di = _devices.at(i); - if (di.mode != mode) { - continue; - } - if (di.selected && di.name != name ) { - di.selected = false; - _devices[i] = di; - emit dataChanged(index(i, 0), index(i, 0), role); - } - if (di.name == name) { - di.selected = true; - _devices[i] = di; - emit dataChanged(index(i, 0), index(i, 0), role); - } - } -} diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.h b/interface/src/scripting/AudioDeviceScriptingInterface.h deleted file mode 100644 index f912c35288..0000000000 --- a/interface/src/scripting/AudioDeviceScriptingInterface.h +++ /dev/null @@ -1,107 +0,0 @@ -// -// AudioDeviceScriptingInterface.h -// interface/src/scripting -// -// Created by Brad Hefta-Gaub on 3/22/14. -// Copyright 2014 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 -// - -#ifndef hifi_AudioDeviceScriptingInterface_h -#define hifi_AudioDeviceScriptingInterface_h - -#include -#include -#include -#include -#include - -class AudioEffectOptions; - -struct ScriptingAudioDeviceInfo { - QString name; - bool selected; - QAudio::Mode mode; -}; - -class AudioDeviceScriptingInterface : public QAbstractListModel { - Q_OBJECT - - Q_PROPERTY(QStringList inputAudioDevices READ inputAudioDevices NOTIFY inputAudioDevicesChanged) - Q_PROPERTY(QStringList outputAudioDevices READ outputAudioDevices NOTIFY outputAudioDevicesChanged) - Q_PROPERTY(bool muted READ muted WRITE setMuted NOTIFY mutedChanged) - -public: - static AudioDeviceScriptingInterface* getInstance(); - - QStringList inputAudioDevices() const; - QStringList outputAudioDevices() const; - bool muted(); - - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex& parent = QModelIndex()) const override; - QHash roleNames() const override; - - enum Roles { - DisplayNameRole = Qt::UserRole, - SelectedRole, - AudioModeRole - }; - -private slots: - void onDeviceChanged(); - void onCurrentInputDeviceChanged(const QString& name); - void onCurrentOutputDeviceChanged(const QString& name); - void currentDeviceUpdate(const QString& name, QAudio::Mode mode); - -public slots: - bool setInputDevice(const QString& deviceName); - bool setOutputDevice(const QString& deviceName); - bool setDeviceFromMenu(const QString& deviceMenuName); - - QString getInputDevice(); - QString getOutputDevice(); - - QString getDefaultInputDevice(); - QString getDefaultOutputDevice(); - - QVector getInputDevices(); - QVector getOutputDevices(); - - float getInputVolume(); - void setInputVolume(float volume); - void setReverb(bool reverb); - void setReverbOptions(const AudioEffectOptions* options); - - bool getMuted(); - void toggleMute(); - - void setMuted(bool muted); - - void setInputDeviceAsync(const QString& deviceName); - void setOutputDeviceAsync(const QString& deviceName); -private: - AudioDeviceScriptingInterface(); - -signals: - void muteToggled(); - void deviceChanged(); - void currentInputDeviceChanged(const QString& name); - void currentOutputDeviceChanged(const QString& name); - void mutedChanged(bool muted); - void inputAudioDevicesChanged(QStringList inputAudioDevices); - void outputAudioDevicesChanged(QStringList outputAudioDevices); - -private: - QVector _devices; - - QStringList _inputAudioDevices; - QStringList _outputAudioDevices; - - QString _currentInputDevice; - QString _currentOutputDevice; -}; - -#endif // hifi_AudioDeviceScriptingInterface_h diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp new file mode 100644 index 0000000000..055d59efe3 --- /dev/null +++ b/interface/src/scripting/AudioDevices.cpp @@ -0,0 +1,242 @@ +// +// AudioDevices.cpp +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/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 +// + +#include "AudioDevices.h" + +#include "Application.h" +#include "AudioClient.h" +#include "Audio.h" + +using namespace scripting; + +Setting::Handle inputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "INPUT" }}; +Setting::Handle outputDeviceDesktop { QStringList { Audio::AUDIO, Audio::DESKTOP, "OUTPUT" }}; +Setting::Handle inputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "INPUT" }}; +Setting::Handle outputDeviceHMD { QStringList { Audio::AUDIO, Audio::HMD, "OUTPUT" }}; + +QHash AudioDeviceList::_roles { + { Qt::DisplayRole, "display" }, + { Qt::CheckStateRole, "selected" } +}; +Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled }; + +QVariant AudioDeviceList::data(const QModelIndex& index, int role) const { + if (!index.isValid() || index.row() >= _devices.size()) { + return QVariant(); + } + + if (role == Qt::DisplayRole) { + return _devices.at(index.row()).display; + } else if (role == Qt::CheckStateRole) { + return _devices.at(index.row()).selected; + } else { + return QVariant(); + } +} + +bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) { + if (!index.isValid() || index.row() >= _devices.size()) { + return false; + } + + bool success = false; + + if (role == Qt::CheckStateRole) { + auto selected = value.toBool(); + auto& device = _devices[index.row()]; + + // only allow switching to a new device, not deactivating an in-use device + if (selected + // skip if already selected + && selected != device.selected) { + + auto client = DependencyManager::get(); + QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, success), + Q_ARG(QAudio::Mode, _mode), + Q_ARG(const QAudioDeviceInfo&, device.info)); + + if (success) { + device.selected = true; + emit deviceSelected(device.info); + emit deviceChanged(device.info); + } + } + } + + emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); + return success; +} + +void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) { + bool success { false }; + + // try to set the last selected device + if (!device.isNull()) { + auto i = 0; + for (; i < rowCount(); ++i) { + if (device == _devices[i].info.deviceName()) { + break; + } + } + if (i < rowCount()) { + success = setData(createIndex(i, 0), true, Qt::CheckStateRole); + } + + // the selection failed - reset it + if (!success) { + emit deviceSelected(QAudioDeviceInfo()); + } + } + + // try to set to the default device for this mode + if (!success) { + auto client = DependencyManager::get().data(); + if (contextIsHMD) { + QString deviceName; + if (_mode == QAudio::AudioInput) { + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioInDevice(); + } else { // if (_mode == QAudio::AudioOutput) + deviceName = qApp->getActiveDisplayPlugin()->getPreferredAudioOutDevice(); + } + if (!deviceName.isNull()) { + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode), Q_ARG(QString, deviceName)); + } + } else { + // use the system default + QMetaObject::invokeMethod(client, "switchAudioDevice", Q_ARG(QAudio::Mode, _mode)); + } + } +} + +void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) { + _selectedDevice = device; + QModelIndex index; + + for (auto i = 0; i < _devices.size(); ++i) { + AudioDevice& device = _devices[i]; + + if (device.selected && device.info != _selectedDevice) { + device.selected = false; + } else if (device.info == _selectedDevice) { + device.selected = true; + index = createIndex(i, 0); + } + } + + emit deviceChanged(_selectedDevice); + emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0)); +} + +void AudioDeviceList::onDevicesChanged(const QList& devices) { + beginResetModel(); + + _devices.clear(); + + foreach(const QAudioDeviceInfo& deviceInfo, devices) { + AudioDevice device; + device.info = deviceInfo; + device.display = device.info.deviceName() + .replace("High Definition", "HD") + .remove("Device") + .replace(" )", ")"); + device.selected = (device.info == _selectedDevice); + _devices.push_back(device); + } + + endResetModel(); +} + +AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) { + auto client = DependencyManager::get(); + + connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection); + connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection); + + // 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)); + + connect(&_inputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onInputDeviceSelected); + connect(&_outputs, &AudioDeviceList::deviceSelected, this, &AudioDevices::onOutputDeviceSelected); +} + +void AudioDevices::onContextChanged(const QString& context) { + QString input; + QString output; + if (_contextIsHMD) { + input = inputDeviceHMD.get(); + output = outputDeviceHMD.get(); + } else { + input = inputDeviceDesktop.get(); + output = outputDeviceDesktop.get(); + } + + _inputs.resetDevice(_contextIsHMD, input); + _outputs.resetDevice(_contextIsHMD, output); +} + +void AudioDevices::onInputDeviceSelected(const QAudioDeviceInfo& device) { + QString deviceName; + if (!device.isNull()) { + deviceName = device.deviceName(); + } + + if (_contextIsHMD) { + inputDeviceHMD.set(deviceName); + } else { + inputDeviceDesktop.set(deviceName); + } +} + +void AudioDevices::onOutputDeviceSelected(const QAudioDeviceInfo& device) { + QString deviceName; + if (!device.isNull()) { + deviceName = device.deviceName(); + } + + if (_contextIsHMD) { + outputDeviceHMD.set(deviceName); + } else { + outputDeviceDesktop.set(deviceName); + } +} + +void AudioDevices::onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device) { + if (mode == QAudio::AudioInput) { + _inputs.onDeviceChanged(device); + } else { // if (mode == QAudio::AudioOutput) + _outputs.onDeviceChanged(device); + } +} + +void AudioDevices::onDevicesChanged(QAudio::Mode mode, const QList& devices) { + static bool initialized { false }; + auto initialize = [&]{ + if (initialized) { + onContextChanged(QString()); + } else { + initialized = true; + } + }; + + if (mode == QAudio::AudioInput) { + _inputs.onDevicesChanged(devices); + static std::once_flag inputFlag; + std::call_once(inputFlag, initialize); + } else { // if (mode == QAudio::AudioOutput) + _outputs.onDevicesChanged(devices); + static std::once_flag outputFlag; + std::call_once(outputFlag, initialize); + } +} \ No newline at end of file diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h new file mode 100644 index 0000000000..ec7022e721 --- /dev/null +++ b/interface/src/scripting/AudioDevices.h @@ -0,0 +1,98 @@ +// +// AudioDevices.h +// interface/src/scripting +// +// Created by Zach Pomerantz on 28/5/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 +// + +#ifndef hifi_scripting_AudioDevices_h +#define hifi_scripting_AudioDevices_h + +#include +#include +#include + +namespace scripting { + +class AudioDevice { +public: + QAudioDeviceInfo info; + QString display; + bool selected { false }; +}; + +class AudioDeviceList : public QAbstractListModel { + Q_OBJECT + +public: + AudioDeviceList(QAudio::Mode mode) : _mode(mode) {} + + int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); } + QHash roleNames() const override { return _roles; } + Qt::ItemFlags flags(const QModelIndex& index) const override { return _flags; } + + // get/set devices through a QML ListView + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant &value, int role) override; + + // reset device to the last selected device in this context, or the default + void resetDevice(bool contextIsHMD, const QString& device); + +signals: + void deviceSelected(const QAudioDeviceInfo& device); + void deviceChanged(const QAudioDeviceInfo& device); + +private slots: + void onDeviceChanged(const QAudioDeviceInfo& device); + void onDevicesChanged(const QList& devices); + +private: + friend class AudioDevices; + + static QHash _roles; + static Qt::ItemFlags _flags; + + QAudio::Mode _mode; + QAudioDeviceInfo _selectedDevice; + QList _devices; +}; + +class Audio; + +class AudioDevices : public QObject { + Q_OBJECT + Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop) + Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop) + +public: + AudioDevices(bool& contextIsHMD); + +signals: + void nop(); + +private slots: + void onContextChanged(const QString& context); + void onInputDeviceSelected(const QAudioDeviceInfo& device); + void onOutputDeviceSelected(const QAudioDeviceInfo& device); + void onDeviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); + void onDevicesChanged(QAudio::Mode mode, const QList& devices); + +private: + friend class Audio; + + AudioDeviceList* getInputList() { return &_inputs; } + AudioDeviceList* getOutputList() { return &_outputs; } + + AudioDeviceList _inputs { QAudio::AudioInput }; + AudioDeviceList _outputs { QAudio::AudioOutput }; + + bool& _contextIsHMD; +}; + +}; + +#endif // hifi_scripting_AudioDevices_h diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 2b715eac9d..7a46aded77 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -18,9 +18,10 @@ HIFI_QML_DEF(AvatarInputs) - static AvatarInputs* INSTANCE{ nullptr }; +Setting::Handle showAudioToolsSetting { QStringList { "AvatarInputs", "showAudioTools" }, false }; + AvatarInputs* AvatarInputs::getInstance() { if (!INSTANCE) { AvatarInputs::registerType(); @@ -32,6 +33,7 @@ AvatarInputs* AvatarInputs::getInstance() { AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { INSTANCE = this; + _showAudioTools = showAudioToolsSetting.get(); } #define AI_UPDATE(name, src) \ @@ -43,16 +45,6 @@ AvatarInputs::AvatarInputs(QQuickItem* parent) : QQuickItem(parent) { } \ } -#define AI_UPDATE_WRITABLE(name, src) \ - { \ - auto val = src; \ - if (_##name != val) { \ - _##name = val; \ - qDebug() << "AvatarInputs" << val; \ - emit name##Changed(val); \ - } \ - } - #define AI_UPDATE_FLOAT(name, src, epsilon) \ { \ float val = src; \ @@ -94,8 +86,6 @@ void AvatarInputs::update() { AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); - AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); - auto audioIO = DependencyManager::get(); const float audioLevel = loudnessToAudioLevel(DependencyManager::get()->getLastInputLoudness()); @@ -122,8 +112,9 @@ void AvatarInputs::setShowAudioTools(bool showAudioTools) { if (_showAudioTools == showAudioTools) return; - Menu::getInstance()->setIsOptionChecked(MenuOption::AudioTools, showAudioTools); - update(); + _showAudioTools = showAudioTools; + showAudioToolsSetting.set(_showAudioTools); + emit showAudioToolsChanged(_showAudioTools); } void AvatarInputs::toggleCameraMute() { diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index bc9955a24f..247cc54995 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -49,7 +49,7 @@ signals: void audioClippingChanged(); void audioLevelChanged(); void isHMDChanged(); - void showAudioToolsChanged(bool showAudioTools); + void showAudioToolsChanged(bool show); protected: Q_INVOKABLE void resetSensors(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 767c122bb6..41a4ebdf68 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -227,17 +227,17 @@ void setupPreferences() { preferences->addPreference(preference); } - static const QString AUDIO("Audio"); + static const QString AUDIO_BUFFERS("Audio Buffers"); { auto getter = []()->bool { return !DependencyManager::get()->getReceivedAudioStream().dynamicJitterBufferEnabled(); }; auto setter = [](bool value) { DependencyManager::get()->getReceivedAudioStream().setDynamicJitterBufferEnabled(!value); }; - auto preference = new CheckPreference(AUDIO, "Disable dynamic jitter buffer", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable dynamic jitter buffer", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getReceivedAudioStream().getStaticJitterBufferFrames(); }; auto setter = [](float value) { DependencyManager::get()->getReceivedAudioStream().setStaticJitterBufferFrames(value); }; - auto preference = new SpinnerPreference(AUDIO, "Static jitter buffer frames", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Static jitter buffer frames", getter, setter); preference->setMin(0); preference->setMax(2000); preference->setStep(1); @@ -246,13 +246,13 @@ void setupPreferences() { { auto getter = []()->bool { return !DependencyManager::get()->getOutputStarveDetectionEnabled(); }; auto setter = [](bool value) { DependencyManager::get()->setOutputStarveDetectionEnabled(!value); }; - auto preference = new CheckPreference(AUDIO, "Disable output starve detection", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Disable output starve detection", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getOutputBufferSize(); }; auto setter = [](float value) { DependencyManager::get()->setOutputBufferSize(value); }; - auto preference = new SpinnerPreference(AUDIO, "Output buffer initial frames", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Output buffer initial frames", getter, setter); preference->setMin(AudioClient::MIN_BUFFER_FRAMES); preference->setMax(AudioClient::MAX_BUFFER_FRAMES); preference->setStep(1); @@ -262,13 +262,13 @@ void setupPreferences() { { auto getter = []()->bool { return DependencyManager::get()->isSimulatingJitter(); }; auto setter = [](bool value) { return DependencyManager::get()->setIsSimulatingJitter(value); }; - auto preference = new CheckPreference(AUDIO, "Packet jitter simulator", getter, setter); + auto preference = new CheckPreference(AUDIO_BUFFERS, "Packet jitter simulator", getter, setter); preferences->addPreference(preference); } { auto getter = []()->float { return DependencyManager::get()->getGateThreshold(); }; auto setter = [](float value) { return DependencyManager::get()->setGateThreshold(value); }; - auto preference = new SpinnerPreference(AUDIO, "Packet throttle threshold", getter, setter); + auto preference = new SpinnerPreference(AUDIO_BUFFERS, "Packet throttle threshold", getter, setter); preference->setMin(1); preference->setMax(200); preference->setStep(1); diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a688e90e69..6d4de9e181 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -47,7 +47,6 @@ #include "LODManager.h" #include "ui/OctreeStatsProvider.h" #include "ui/DomainConnectionModel.h" -#include "scripting/AudioDeviceScriptingInterface.h" #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" #include "scripting/GlobalServicesScriptingInterface.h" @@ -185,6 +184,7 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("Account", AccountScriptingInterface::getInstance()); + _webSurface->getSurfaceContext()->setContextProperty("Audio", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("AudioStats", DependencyManager::get()->getStats().data()); _webSurface->getSurfaceContext()->setContextProperty("HMD", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("fileDialogHelper", new FileDialogHelper()); @@ -195,7 +195,6 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getSurfaceContext()->setContextProperty("LODManager", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5297426840..f32d37562a 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -79,6 +79,49 @@ using Mutex = std::mutex; using Lock = std::unique_lock; static Mutex _deviceMutex; +// thread-safe +QList getAvailableDevices(QAudio::Mode mode) { + // NOTE: availableDevices() clobbers the Qt internal device list + Lock lock(_deviceMutex); + return QAudioDeviceInfo::availableDevices(mode); +} + +// now called from a background thread, to keep blocking operations off the audio thread +void AudioClient::checkDevices() { + auto inputDevices = getAvailableDevices(QAudio::AudioInput); + auto outputDevices = getAvailableDevices(QAudio::AudioOutput); + + if (inputDevices != _inputDevices) { + _inputDevices.swap(inputDevices); + emit devicesChanged(QAudio::AudioInput, _inputDevices); + } + + if (outputDevices != _outputDevices) { + _outputDevices.swap(outputDevices); + emit devicesChanged(QAudio::AudioOutput, _outputDevices); + } +} + +QAudioDeviceInfo AudioClient::getActiveAudioDevice(QAudio::Mode mode) const { + Lock lock(_deviceMutex); + + if (mode == QAudio::AudioInput) { + return _inputDeviceInfo; + } else { // if (mode == QAudio::AudioOutput) + return _outputDeviceInfo; + } +} + +QList AudioClient::getAudioDevices(QAudio::Mode mode) const { + Lock lock(_deviceMutex); + + if (mode == QAudio::AudioInput) { + return _inputDevices; + } else { // if (mode == QAudio::AudioOutput) + return _outputDevices; + } +} + static void channelUpmix(int16_t* source, int16_t* dest, int numSamples, int numExtraChannels) { for (int i = 0; i < numSamples/2; i++) { @@ -173,8 +216,9 @@ AudioClient::AudioClient() : connect(&_receivedAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioClient::handleMismatchAudioFormat); - _inputDevices = getDeviceNames(QAudio::AudioInput); - _outputDevices = getDeviceNames(QAudio::AudioOutput); + // initialize wasapi; if getAvailableDevices is called from the CheckDevicesThread before this, it will crash + getAvailableDevices(QAudio::AudioInput); + getAvailableDevices(QAudio::AudioOutput); // start a thread to detect any device changes @@ -241,13 +285,6 @@ void AudioClient::audioMixerKilled() { emit disconnected(); } -// thread-safe -QList getAvailableDevices(QAudio::Mode mode) { - // NOTE: availableDevices() clobbers the Qt internal device list - Lock lock(_deviceMutex); - return QAudioDeviceInfo::availableDevices(mode); -} - QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& deviceName) { QAudioDeviceInfo result; foreach(QAudioDeviceInfo audioDevice, getAvailableDevices(mode)) { @@ -261,7 +298,7 @@ QAudioDeviceInfo getNamedAudioDeviceForMode(QAudio::Mode mode, const QString& de } #ifdef Q_OS_WIN -QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { +QString getWinDeviceName(IMMDevice* pEndpoint) { QString deviceName; IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); @@ -282,7 +319,7 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { return deviceName; } -QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { +QString AudioClient::getWinDeviceName(wchar_t* guid) { QString deviceName; HRESULT hr = S_OK; CoInitialize(nullptr); @@ -294,7 +331,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); } else { - deviceName = ::friendlyNameForAudioDevice(pEndpoint); + deviceName = ::getWinDeviceName(pEndpoint); } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = nullptr; @@ -377,7 +414,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { printf("Audio Error: device not found\n"); deviceName = QString("NONE"); } else { - deviceName = friendlyNameForAudioDevice(pEndpoint); + deviceName = getWinDeviceName(pEndpoint); } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = NULL; @@ -747,29 +784,22 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { } +bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo) { + auto device = deviceInfo; -QString AudioClient::getDefaultDeviceName(QAudio::Mode mode) { - QAudioDeviceInfo deviceInfo = defaultAudioDeviceForMode(mode); - return deviceInfo.deviceName(); -} - -QVector AudioClient::getDeviceNames(QAudio::Mode mode) { - QVector deviceNames; - const QList &availableDevice = getAvailableDevices(mode); - foreach(const QAudioDeviceInfo &audioDevice, availableDevice) { - deviceNames << audioDevice.deviceName().trimmed(); + if (device.isNull()) { + device = defaultAudioDeviceForMode(mode); + } + + if (mode == QAudio::AudioInput) { + return switchInputToAudioDevice(device); + } else { // if (mode == QAudio::AudioOutput) + return switchOutputToAudioDevice(device); } - return deviceNames; } -bool AudioClient::switchInputToAudioDevice(const QString& inputDeviceName) { - qCDebug(audioclient) << "switchInputToAudioDevice [" << inputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName).deviceName() << "]"; - return switchInputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioInput, inputDeviceName)); -} - -bool AudioClient::switchOutputToAudioDevice(const QString& outputDeviceName) { - qCDebug(audioclient) << "switchOutputToAudioDevice [" << outputDeviceName << "] [" << getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName).deviceName() << "]"; - return switchOutputToAudioDevice(getNamedAudioDeviceForMode(QAudio::AudioOutput, outputDeviceName)); +bool AudioClient::switchAudioDevice(QAudio::Mode mode, const QString& deviceName) { + return switchAudioDevice(mode, getNamedAudioDeviceForMode(mode, deviceName)); } void AudioClient::configureReverb() { @@ -1313,8 +1343,7 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { } // change in channel count for desired input format, restart the input device - qCDebug(audioclient) << __FUNCTION__ << "about to call switchInputToAudioDevice:" << _inputAudioDeviceName; - switchInputToAudioDevice(_inputAudioDeviceName); + switchInputToAudioDevice(_inputDeviceInfo); } } @@ -1353,6 +1382,9 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); + // cleanup any previously initialized device if (_audioInput) { // The call to stop() causes _inputDevice to be destructed. @@ -1365,7 +1397,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn _audioInput = NULL; _numInputCallbackBytes = 0; - _inputAudioDeviceName = ""; + _inputDeviceInfo = QAudioDeviceInfo(); } if (_inputToNetworkResampler) { @@ -1380,8 +1412,8 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (!inputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; - _inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed(); - emit currentInputDeviceChanged(_inputAudioDeviceName); + _inputDeviceInfo = inputDeviceInfo; + emit deviceChanged(QAudio::AudioInput, inputDeviceInfo); if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; @@ -1416,10 +1448,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); _inputDevice = _audioInput->start(); - lock.unlock(); if (_inputDevice) { connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleMicAudioInput())); @@ -1469,6 +1498,9 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice bool supportedFormat = false; + // NOTE: device start() uses the Qt internal device list + Lock lock(_deviceMutex); + Lock localAudioLock(_localAudioMutex); _localSamplesAvailable.exchange(0, std::memory_order_release); @@ -1494,6 +1526,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice delete[] _localOutputMixBuffer; _localOutputMixBuffer = NULL; + + _outputDeviceInfo = QAudioDeviceInfo(); } if (_networkToOutputResampler) { @@ -1507,8 +1541,8 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice if (!outputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; - _outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed(); - emit currentOutputDeviceChanged(_outputAudioDeviceName); + _outputDeviceInfo = outputDeviceInfo; + emit deviceChanged(QAudio::AudioOutput, outputDeviceInfo); if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; @@ -1583,10 +1617,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutputIODevice.start(); - // NOTE: device start() uses the Qt internal device list - Lock lock(_deviceMutex); _audioOutput->start(&_audioOutputIODevice); - lock.unlock(); // setup a loopback audio output device _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); @@ -1786,19 +1817,6 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { return bytesWritten; } -// now called from a background thread, to keep blocking operations off the audio thread -void AudioClient::checkDevices() { - QVector inputDevices = getDeviceNames(QAudio::AudioInput); - QVector outputDevices = getDeviceNames(QAudio::AudioOutput); - - if (inputDevices != _inputDevices || outputDevices != _outputDevices) { - _inputDevices = inputDevices; - _outputDevices = outputDevices; - - emit deviceChanged(); - } -} - void AudioClient::loadSettings() { _receivedAudioStream.setDynamicJitterBufferEnabled(dynamicJitterBufferEnabled.get()); _receivedAudioStream.setStaticJitterBufferFrames(staticJitterBufferFrames.get()); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index f3e7e418b2..01241938d9 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -123,8 +123,6 @@ public: float getTimeSinceLastClip() const { return _timeSinceLastClip; } float getAudioAverageInputLoudness() const { return _lastInputLoudness; } - bool isMuted() { return _muted; } - const AudioIOStats& getStats() const { return _stats; } int getOutputBufferSize() { return _outputBufferSizeFrames.get(); } @@ -147,12 +145,15 @@ public: bool outputLocalInjector(AudioInjector* injector) override; + QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const; + QList getAudioDevices(QAudio::Mode mode) const; + static const float CALLBACK_ACCELERATOR_RATIO; bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName); #ifdef Q_OS_WIN - static QString friendlyNameForAudioDevice(wchar_t* guid); + static QString getWinDeviceName(wchar_t* guid); #endif public slots: @@ -172,11 +173,14 @@ public slots: void handleRecordedAudioInput(const QByteArray& audio); void reset(); void audioMixerKilled(); + void toggleMute(); + bool isMuted() { return _muted; } + virtual void setIsStereoInput(bool stereo) override; - void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } + void setNoiseReduction(bool isNoiseGateEnabled) { _isNoiseGateEnabled = isNoiseGateEnabled; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } @@ -188,12 +192,9 @@ public slots: bool shouldLoopbackInjectors() override { return _shouldEchoToServer; } - bool switchInputToAudioDevice(const QString& inputDeviceName); - bool switchOutputToAudioDevice(const QString& outputDeviceName); - QString getDeviceName(QAudio::Mode mode) const { return (mode == QAudio::AudioInput) ? - _inputAudioDeviceName : _outputAudioDeviceName; } - QString getDefaultDeviceName(QAudio::Mode mode); - QVector getDeviceNames(QAudio::Mode mode); + // calling with a null QAudioDevice will use the system default + bool switchAudioDevice(QAudio::Mode mode, const QAudioDeviceInfo& deviceInfo = QAudioDeviceInfo()); + bool switchAudioDevice(QAudio::Mode mode, const QString& deviceName); float getInputVolume() const { return (_audioInput) ? (float)_audioInput->volume() : 0.0f; } void setInputVolume(float volume) { if (_audioInput) _audioInput->setVolume(volume); } @@ -215,7 +216,9 @@ signals: void noiseGateClosed(); void changeDevice(const QAudioDeviceInfo& outputDeviceInfo); - void deviceChanged(); + + void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device); + void devicesChanged(QAudio::Mode mode, const QList& devices); void receivedFirstPacket(); void disconnected(); @@ -224,9 +227,6 @@ signals: void muteEnvironmentRequested(glm::vec3 position, float radius); - void currentOutputDeviceChanged(const QString& name); - void currentInputDeviceChanged(const QString& name); - protected: AudioClient(); ~AudioClient(); @@ -293,9 +293,6 @@ private: MixedProcessedAudioStream _receivedAudioStream; bool _isStereoInput; - QString _inputAudioDeviceName; - QString _outputAudioDeviceName; - quint64 _outputStarveDetectionStartTimeMsec; int _outputStarveDetectionCount; @@ -371,8 +368,11 @@ private: glm::vec3 avatarBoundingBoxCorner; glm::vec3 avatarBoundingBoxScale; - QVector _inputDevices; - QVector _outputDevices; + QAudioDeviceInfo _inputDeviceInfo; + QAudioDeviceInfo _outputDeviceInfo; + + QList _inputDevices; + QList _outputDevices; bool _hasReceivedFirstPacket { false }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 8452494d95..b5dd60d03b 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -19,12 +19,6 @@ void registerAudioMetaTypes(QScriptEngine* engine) { qScriptRegisterMetaType(engine, soundSharedPointerToScriptValue, soundSharedPointerFromScriptValue); } -AudioScriptingInterface::AudioScriptingInterface() : - _localAudioInterface(NULL) -{ - -} - ScriptAudioInjector* AudioScriptingInterface::playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions) { if (QThread::currentThread() != thread()) { ScriptAudioInjector* injector = NULL; diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 5ec8ce4b12..9ee2af7738 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -28,6 +28,7 @@ public: void setLocalAudioInterface(AbstractAudioInterface* audioInterface) { _localAudioInterface = audioInterface; } protected: + AudioScriptingInterface() {} // this method is protected to stop C++ callers from calling, but invokable from script Q_INVOKABLE ScriptAudioInjector* playSound(SharedSoundPointer sound, const AudioInjectorOptions& injectorOptions = AudioInjectorOptions()); @@ -44,9 +45,7 @@ signals: void inputReceived(const QByteArray& inputSamples); /// a frame of mic input audio has been received and processed private: - AudioScriptingInterface(); - - AbstractAudioInterface* _localAudioInterface; + AbstractAudioInterface* _localAudioInterface { nullptr }; }; void registerAudioMetaTypes(QScriptEngine* engine); diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index dadf436ea5..b41c548e57 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -439,7 +439,7 @@ void TabletProxy::loadQMLSource(const QVariant& path) { } } -void TabletProxy::pushOntoStack(const QVariant& path) { +bool TabletProxy::pushOntoStack(const QVariant& path) { QObject* root = nullptr; if (!_toolbarMode && _qmlTabletRoot) { root = _qmlTabletRoot; @@ -457,6 +457,8 @@ void TabletProxy::pushOntoStack(const QVariant& path) { } else { qCDebug(scriptengine) << "tablet cannot push QML because _qmlTabletRoot or _desktopWindow is null"; } + + return root; } void TabletProxy::popFromStack() { @@ -612,15 +614,6 @@ void TabletProxy::removeButton(QObject* tabletButtonProxy) { } } -void TabletProxy::updateMicEnabled(const bool micOn) { - auto tablet = getQmlTablet(); - if (!tablet) { - //qCCritical(scriptengine) << "Could not find tablet in TabletRoot.qml"; - } else { - QMetaObject::invokeMethod(tablet, "setMicEnabled", Qt::AutoConnection, Q_ARG(QVariant, QVariant(micOn))); - } -} - void TabletProxy::updateAudioBar(const double micLevel) { auto tablet = getQmlTablet(); if (!tablet) { diff --git a/libraries/script-engine/src/TabletScriptingInterface.h b/libraries/script-engine/src/TabletScriptingInterface.h index 85c1fdaf9a..2ae9e70846 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.h +++ b/libraries/script-engine/src/TabletScriptingInterface.h @@ -119,7 +119,9 @@ public: Q_INVOKABLE void gotoWebScreen(const QString& url, const QString& injectedJavaScriptUrl); Q_INVOKABLE void loadQMLSource(const QVariant& path); - Q_INVOKABLE void pushOntoStack(const QVariant& path); + // FIXME: This currently relies on a script initializing the tablet (hence the bool denoting success); + // it should be initialized internally so it cannot fail + Q_INVOKABLE bool pushOntoStack(const QVariant& path); Q_INVOKABLE void popFromStack(); Q_INVOKABLE void loadQMLOnTop(const QVariant& path); @@ -150,13 +152,6 @@ public: */ Q_INVOKABLE void removeButton(QObject* tabletButtonProxy); - /**jsdoc - * Updates the tablet's mic enabled state - * @function TabletProxy#updateMicEnabled - * @param micEnabled {bool} mic enabled state - */ - Q_INVOKABLE void updateMicEnabled(const bool micEnabled); - /**jsdoc * Updates the audio bar in tablet to reflect latest mic level * @function TabletProxy#updateAudioBar diff --git a/plugins/oculus/src/OculusDisplayPlugin.cpp b/plugins/oculus/src/OculusDisplayPlugin.cpp index db8c92ac23..0fd8467e38 100644 --- a/plugins/oculus/src/OculusDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusDisplayPlugin.cpp @@ -244,7 +244,7 @@ QString OculusDisplayPlugin::getPreferredAudioInDevice() const { if (!OVR_SUCCESS(ovr_GetAudioDeviceInGuidStr(buffer))) { return QString(); } - return AudioClient::friendlyNameForAudioDevice(buffer); + return AudioClient::getWinDeviceName(buffer); } QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { @@ -252,7 +252,7 @@ QString OculusDisplayPlugin::getPreferredAudioOutDevice() const { if (!OVR_SUCCESS(ovr_GetAudioDeviceOutGuidStr(buffer))) { return QString(); } - return AudioClient::friendlyNameForAudioDevice(buffer); + return AudioClient::getWinDeviceName(buffer); } OculusDisplayPlugin::~OculusDisplayPlugin() { diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 15fb7d72c9..7a73c91c7d 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -725,7 +725,7 @@ QString OpenVrDisplayPlugin::getPreferredAudioInDevice() const { std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); - device = AudioClient::friendlyNameForAudioDevice(deviceW.data()); + device = AudioClient::getWinDeviceName(deviceW.data()); } return device; } @@ -738,7 +738,7 @@ QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const { std::vector deviceW; deviceW.assign(size, INIT); device.toWCharArray(deviceW.data()); - device = AudioClient::friendlyNameForAudioDevice(deviceW.data()); + device = AudioClient::getWinDeviceName(deviceW.data()); } return device; } diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index 319a821eb6..2270118861 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -25,7 +25,6 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/tablet-goto.js", "system/marketplaces/marketplaces.js", "system/edit.js", - "system/selectAudioDevice.js", "system/notifications.js", "system/dialTone.js", "system/firstPersonHMD.js", diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 7bc8676a2e..cb9589ae9e 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -27,7 +27,7 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { - if (AudioDevice.getMuted()) { + if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -46,7 +46,7 @@ function onClicked() { Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); shouldActivateButton = true; shouldActivateButton = true; - tablet.loadQMLSource("../Audio.qml"); + tablet.loadQMLSource("../audio/Audio.qml"); onAudioScreen = true; } } @@ -60,8 +60,8 @@ function onScreenChanged(type, url) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: AudioDevice.getMuted() ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, - activeIcon: AudioDevice.getMuted() ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, + icon: Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -70,7 +70,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); -AudioDevice.muteToggled.connect(onMuteToggled); +Audio.mutedChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -78,7 +78,7 @@ Script.scriptEnding.connect(function () { } button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); - AudioDevice.muteToggled.disconnect(onMuteToggled); + Audio.mutedChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index cf07402d64..731d62017d 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -39,7 +39,7 @@ Script.scriptEnding.connect(cleanup); function update(dt) { - if (!AudioDevice.getMuted()) { + if (!Audio.muted) { if (hasOverlay()) { deleteOverlay(); } @@ -47,7 +47,7 @@ createOverlay(); } else { updateOverlay(); - } + } } function getOffsetPosition() { @@ -98,7 +98,7 @@ function cleanup() { deleteOverlay(); - AudioDevice.muteToggled.disconnect(onMuteToggled); + Audio.muted.disconnect(onMuteToggled); Script.update.disconnect(update); } }()); // END LOCAL_SCOPE diff --git a/scripts/system/away.js b/scripts/system/away.js index 4ca938d492..f98408376d 100644 --- a/scripts/system/away.js +++ b/scripts/system/away.js @@ -196,9 +196,9 @@ MyAvatar.wentActive.connect(setActiveProperties) function setAwayProperties() { isAway = true; - wasMuted = AudioDevice.getMuted(); + wasMuted = Audio.muted; if (!wasMuted) { - AudioDevice.toggleMute(); + Audio.muted = !Audio.muted; } MyAvatar.setEnableMeshVisible(false); // just for our own display, without changing point of view playAwayAnimation(); // animation is still seen by others @@ -221,7 +221,7 @@ function setAwayProperties() { function setActiveProperties() { isAway = false; if (!wasMuted) { - AudioDevice.toggleMute(); + Audio.muted = !Audio.muted; } MyAvatar.setEnableMeshVisible(true); // IWBNI we respected Developer->Avatar->Draw Mesh setting. stopAwayAnimation(); diff --git a/scripts/system/dialTone.js b/scripts/system/dialTone.js index 02624645d5..7b693aa2de 100644 --- a/scripts/system/dialTone.js +++ b/scripts/system/dialTone.js @@ -33,8 +33,8 @@ Audio.disconnected.connect(function(){ Audio.playSound(disconnectSound, soundOptions); }); -AudioDevice.muteToggled.connect(function () { - if (AudioDevice.getMuted()) { +Audio.mutedChanged.connect(function () { + if (Audio.muted) { Audio.playSound(micMutedSound, soundOptions); } }); diff --git a/scripts/system/notifications.js b/scripts/system/notifications.js index 6429d6e0c6..b34630f3f8 100644 --- a/scripts/system/notifications.js +++ b/scripts/system/notifications.js @@ -26,7 +26,7 @@ // // 1. Set the Event Connector at the bottom of the script. // example: -// AudioDevice.muteToggled.connect(onMuteStateChanged); +// Audio.mutedChanged.connect(onMuteStateChanged); // // 2. Create a new function to produce a text string, do not include new line returns. // example: @@ -34,7 +34,7 @@ // var muteState, // muteString; // -// muteState = AudioDevice.getMuted() ? "muted" : "unmuted"; +// muteState = Audio.muted ? "muted" : "unmuted"; // muteString = "Microphone is now " + muteState; // createNotification(muteString, NotificationType.MUTE_TOGGLE); // } diff --git a/scripts/system/selectAudioDevice.js b/scripts/system/selectAudioDevice.js deleted file mode 100644 index 2d40795692..0000000000 --- a/scripts/system/selectAudioDevice.js +++ /dev/null @@ -1,215 +0,0 @@ -"use strict"; - -// -// audioDeviceExample.js -// examples -// -// Created by Brad Hefta-Gaub on 3/22/14 -// Copyright 2013 High Fidelity, Inc. -// -// This is an example script that demonstrates use of the Menu object -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -(function() { // BEGIN LOCAL_SCOPE - -const INPUT = "Input"; -const OUTPUT = "Output"; -const SELECT_AUDIO_SCRIPT_STARTUP_TIMEOUT = 300; -// -// VAR DEFINITIONS -// -var debugPrintStatements = true; -const INPUT_DEVICE_SETTING = "audio_input_device"; -const OUTPUT_DEVICE_SETTING = "audio_output_device"; -var audioDevicesList = []; // placeholder for menu items -var wasHmdActive = false; // assume it's not active to start -var switchedAudioInputToHMD = false; -var switchedAudioOutputToHMD = false; -var previousSelectedInputAudioDevice = ""; -var previousSelectedOutputAudioDevice = ""; - -var interfaceInputDevice = ""; -var interfaceOutputDevice = ""; - -// -// BEGIN FUNCTION DEFINITIONS -// -function debug() { - if (debugPrintStatements) { - print.apply(null, [].concat.apply(["selectAudioDevice.js:"], [].map.call(arguments, JSON.stringify))); - } -} - - -function setupAudioMenus() { - // menu events can be triggered asynchronously; skip them for 200ms to avoid recursion and false switches - removeAudioMenus(); - - // Setup audio input devices - Menu.addSeparator("Audio", "Input Audio Device"); - var currentInputDevice = AudioDevice.getInputDevice() - for (var i = 0; i < AudioDevice.inputAudioDevices.length; i++) { - var audioDeviceMenuString = "Use " + AudioDevice.inputAudioDevices[i] + " for Input"; - Menu.addMenuItem({ - menuName: "Audio", - menuItemName: audioDeviceMenuString, - isCheckable: true, - isChecked: AudioDevice.inputAudioDevices[i] == currentInputDevice - }); - audioDevicesList.push(audioDeviceMenuString); - } - - // Setup audio output devices - Menu.addSeparator("Audio", "Output Audio Device"); - var currentOutputDevice = AudioDevice.getOutputDevice() - for (var i = 0; i < AudioDevice.outputAudioDevices.length; i++) { - var audioDeviceMenuString = "Use " + AudioDevice.outputAudioDevices[i] + " for Output"; - Menu.addMenuItem({ - menuName: "Audio", - menuItemName: audioDeviceMenuString, - isCheckable: true, - isChecked: AudioDevice.outputAudioDevices[i] == currentOutputDevice - }); - audioDevicesList.push(audioDeviceMenuString); - } -} - -function removeAudioMenus() { - Menu.removeSeparator("Audio", "Input Audio Device"); - Menu.removeSeparator("Audio", "Output Audio Device"); - - for (var index = 0; index < audioDevicesList.length; index++) { - if (Menu.menuItemExists("Audio", audioDevicesList[index])) { - Menu.removeMenuItem("Audio", audioDevicesList[index]); - } - } - - Menu.removeMenu("Audio > Devices"); - - audioDevicesList = []; -} - -function onDevicechanged() { - debug("System audio devices changed. Removing and replacing Audio Menus..."); - setupAudioMenus(); -} - -function onMenuEvent(audioDeviceMenuString) { - if (Menu.isOptionChecked(audioDeviceMenuString) && - (audioDeviceMenuString !== interfaceInputDevice && - audioDeviceMenuString !== interfaceOutputDevice)) { - AudioDevice.setDeviceFromMenu(audioDeviceMenuString) - } -} - -function onCurrentDeviceChanged() { - debug("System audio device switched. "); - interfaceInputDevice = "Use " + AudioDevice.getInputDevice() + " for Input"; - interfaceOutputDevice = "Use " + AudioDevice.getOutputDevice() + " for Output"; - for (var index = 0; index < audioDevicesList.length; index++) { - if (audioDevicesList[index] === interfaceInputDevice || - audioDevicesList[index] === interfaceOutputDevice) { - if (Menu.isOptionChecked(audioDevicesList[index]) === false) - Menu.setIsOptionChecked(audioDevicesList[index], true); - } else { - if (Menu.isOptionChecked(audioDevicesList[index]) === true) - Menu.setIsOptionChecked(audioDevicesList[index], false); - } - } -} - -function restoreAudio() { - if (switchedAudioInputToHMD) { - debug("Switching back from HMD preferred audio input to: " + previousSelectedInputAudioDevice); - AudioDevice.setInputDeviceAsync(previousSelectedInputAudioDevice) - switchedAudioInputToHMD = false; - } - if (switchedAudioOutputToHMD) { - debug("Switching back from HMD preferred audio output to: " + previousSelectedOutputAudioDevice); - AudioDevice.setOutputDeviceAsync(previousSelectedOutputAudioDevice) - switchedAudioOutputToHMD = false; - } -} - -// Some HMDs (like Oculus CV1) have a built in audio device. If they -// do, then this function will handle switching to that device automatically -// when you goActive with the HMD active. -function checkHMDAudio() { - // HMD Active state is changing; handle switching - if (HMD.active != wasHmdActive) { - debug("HMD Active state changed!"); - - // We're putting the HMD on; switch to those devices - if (HMD.active) { - debug("HMD is now Active."); - var hmdPreferredAudioInput = HMD.preferredAudioInput(); - var hmdPreferredAudioOutput = HMD.preferredAudioOutput(); - debug("hmdPreferredAudioInput: " + hmdPreferredAudioInput); - debug("hmdPreferredAudioOutput: " + hmdPreferredAudioOutput); - - if (hmdPreferredAudioInput !== "") { - debug("HMD has preferred audio input device."); - previousSelectedInputAudioDevice = Settings.getValue(INPUT_DEVICE_SETTING); - debug("previousSelectedInputAudioDevice: " + previousSelectedInputAudioDevice); - if (hmdPreferredAudioInput != previousSelectedInputAudioDevice) { - switchedAudioInputToHMD = true; - AudioDevice.setInputDeviceAsync(hmdPreferredAudioInput) - } - } - if (hmdPreferredAudioOutput !== "") { - debug("HMD has preferred audio output device."); - previousSelectedOutputAudioDevice = Settings.getValue(OUTPUT_DEVICE_SETTING); - debug("previousSelectedOutputAudioDevice: " + previousSelectedOutputAudioDevice); - if (hmdPreferredAudioOutput != previousSelectedOutputAudioDevice) { - switchedAudioOutputToHMD = true; - AudioDevice.setOutputDeviceAsync(hmdPreferredAudioOutput) - } - } - } else { - debug("HMD no longer active. Restoring audio I/O devices..."); - restoreAudio(); - } - } - wasHmdActive = HMD.active; -} - -// -// END FUNCTION DEFINITIONS -// - -// -// BEGIN SCRIPT BODY -// -// Wait for the C++ systems to fire up before trying to do anything with audio devices -Script.setTimeout(function () { - debug("Connecting deviceChanged(), displayModeChanged(), and switchAudioDevice()..."); - AudioDevice.deviceChanged.connect(onDevicechanged); - AudioDevice.currentInputDeviceChanged.connect(onCurrentDeviceChanged); - AudioDevice.currentOutputDeviceChanged.connect(onCurrentDeviceChanged); - HMD.displayModeChanged.connect(checkHMDAudio); - Menu.menuItemEvent.connect(onMenuEvent); - debug("Setting up Audio I/O menu for the first time..."); - setupAudioMenus(); - debug("Checking HMD audio status...") - checkHMDAudio(); -}, SELECT_AUDIO_SCRIPT_STARTUP_TIMEOUT); - -debug("Connecting scriptEnding()"); -Script.scriptEnding.connect(function () { - restoreAudio(); - removeAudioMenus(); - Menu.menuItemEvent.disconnect(onMenuEvent); - HMD.displayModeChanged.disconnect(checkHMDAudio); - AudioDevice.currentInputDeviceChanged.disconnect(onCurrentDeviceChanged); - AudioDevice.currentOutputDeviceChanged.disconnect(onCurrentDeviceChanged); - AudioDevice.deviceChanged.disconnect(onDevicechanged); -}); - -// -// END SCRIPT BODY -// - -}()); // END LOCAL_SCOPE diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 4e3fb95140..b045e64c6a 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -144,7 +144,9 @@ function onMessage(message) { isDomainOpen(Settings.getValue("previousSnapshotDomainID"), function (canShare) { var isGif = fileExtensionMatches(message.data, "gif"); isLoggedIn = Account.isLoggedIn(); - isUploadingPrintableStill = canShare && Account.isLoggedIn() && !isGif; + if (!isGif) { + isUploadingPrintableStill = canShare && Account.isLoggedIn(); + } if (canShare) { if (isLoggedIn) { print('Sharing snapshot with audience "for_url":', message.data); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index e45fc8d87b..b7324ed28c 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -185,10 +185,7 @@ //TODO: move to tablet qml? if (tabletShown) { - var currentMicEnabled = !Menu.isOptionChecked(MUTE_MICROPHONE_MENU_ITEM); - var currentMicLevel = getMicLevel(); - gTablet.updateMicEnabled(currentMicEnabled); - gTablet.updateAudioBar(currentMicLevel); + gTablet.updateAudioBar(getMicLevel()); } if (now - validCheckTime > MSECS_PER_SEC) {