diff --git a/interface/resources/qml/hifi/Audio.qml b/interface/resources/qml/hifi/Audio.qml new file mode 100644 index 0000000000..deb44b9bd5 --- /dev/null +++ b/interface/resources/qml/hifi/Audio.qml @@ -0,0 +1,253 @@ +// +// 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 var eventBridge; + property string title: "Audio Options" + signal sendToScript(var message); + + //set models after Components is shown + Component.onCompleted: { + refreshTimer.start() + refreshTimerOutput.start() + } + + 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 + 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.showAudioTools + text.text: qsTr("Show audio level meter") + onCheckBoxClicked: { + 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 + font.pointSize: 27 + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter + size: 16 + color: "#AFAFAF" + text: qsTr("CHOOSE INPUT DEVICE") + } + } + + ListView { + Timer { + id: refreshTimer + interval: 1 + repeat: false + onTriggered: { + //refresh model + inputAudioListView.model = undefined + inputAudioListView.model = AudioDevice.inputAudioDevices + } + } + id: inputAudioListView + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 125 + spacing: 16 + clip: true + snapMode: ListView.SnapToItem + delegate: AudioCheckbox { + width: parent.width + checkbox.checked: (modelData === AudioDevice.getInputDevice()) + text.text: modelData + onCheckBoxClicked: { + if (checked) { + AudioDevice.setInputDevice(modelData) + refreshTimer.start() + } + } + } + } + + 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 + font.pointSize: 27 + } + RalewayRegular { + anchors.verticalCenter: parent.verticalCenter + size: 16 + color: "#AFAFAF" + text: qsTr("CHOOSE OUTPUT DEVICE") + } + } + ListView { + id: outputAudioListView + Timer { + id: refreshTimerOutput + interval: 1 + repeat: false + onTriggered: { + //refresh model + outputAudioListView.model = undefined + outputAudioListView.model = AudioDevice.outputAudioDevices + } + } + anchors { left: parent.left; right: parent.right; leftMargin: 70 } + height: 250 + spacing: 16 + clip: true + snapMode: ListView.SnapToItem + delegate: AudioCheckbox { + width: parent.width + checkbox.checked: (modelData === AudioDevice.getOutputDevice()) + text.text: modelData + onCheckBoxClicked: { + if (checked) { + AudioDevice.setOutputDevice(modelData) + refreshTimerOutput.start() + } + } + } + } + + 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/components/AudioCheckbox.qml b/interface/resources/qml/hifi/components/AudioCheckbox.qml new file mode 100644 index 0000000000..a8e0441e0a --- /dev/null +++ b/interface/resources/qml/hifi/components/AudioCheckbox.qml @@ -0,0 +1,29 @@ +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 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/src/scripting/AudioDeviceScriptingInterface.cpp b/interface/src/scripting/AudioDeviceScriptingInterface.cpp index c4dc58f16b..cbb08c0af0 100644 --- a/interface/src/scripting/AudioDeviceScriptingInterface.cpp +++ b/interface/src/scripting/AudioDeviceScriptingInterface.cpp @@ -18,6 +18,21 @@ AudioDeviceScriptingInterface* AudioDeviceScriptingInterface::getInstance() { return &sharedInstance; } +QStringList AudioDeviceScriptingInterface::inputAudioDevices() const +{ + return DependencyManager::get()->getDeviceNames(QAudio::AudioInput).toList();; +} + +QStringList AudioDeviceScriptingInterface::outputAudioDevices() const +{ + return DependencyManager::get()->getDeviceNames(QAudio::AudioOutput).toList();; +} + +bool AudioDeviceScriptingInterface::muted() +{ + return getMuted(); +} + AudioDeviceScriptingInterface::AudioDeviceScriptingInterface() { connect(DependencyManager::get().data(), &AudioClient::muteToggled, this, &AudioDeviceScriptingInterface::muteToggled); @@ -31,7 +46,6 @@ bool AudioDeviceScriptingInterface::setInputDevice(const QString& deviceName) { Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const QString&, deviceName)); - return result; } @@ -41,7 +55,6 @@ bool AudioDeviceScriptingInterface::setOutputDevice(const QString& deviceName) { Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const QString&, deviceName)); - return result; } @@ -69,7 +82,6 @@ QVector AudioDeviceScriptingInterface::getOutputDevices() { return DependencyManager::get()->getDeviceNames(QAudio::AudioOutput); } - float AudioDeviceScriptingInterface::getInputVolume() { return DependencyManager::get()->getInputVolume(); } @@ -90,6 +102,17 @@ 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(); } diff --git a/interface/src/scripting/AudioDeviceScriptingInterface.h b/interface/src/scripting/AudioDeviceScriptingInterface.h index 149de9bf56..4d1d47dcba 100644 --- a/interface/src/scripting/AudioDeviceScriptingInterface.h +++ b/interface/src/scripting/AudioDeviceScriptingInterface.h @@ -20,9 +20,18 @@ class AudioEffectOptions; class AudioDeviceScriptingInterface : public QObject { 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(); + public slots: bool setInputDevice(const QString& deviceName); bool setOutputDevice(const QString& deviceName); @@ -44,12 +53,17 @@ public slots: bool getMuted(); void toggleMute(); + void setMuted(bool muted); + private: AudioDeviceScriptingInterface(); signals: void muteToggled(); void deviceChanged(); + void mutedChanged(bool muted); + void inputAudioDevicesChanged(QStringList inputAudioDevices); + void outputAudioDevicesChanged(QStringList outputAudioDevices); }; #endif // hifi_AudioDeviceScriptingInterface_h diff --git a/interface/src/ui/AvatarInputs.cpp b/interface/src/ui/AvatarInputs.cpp index 944be4bf9e..37d7a30938 100644 --- a/interface/src/ui/AvatarInputs.cpp +++ b/interface/src/ui/AvatarInputs.cpp @@ -43,6 +43,16 @@ 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; \ @@ -59,7 +69,8 @@ void AvatarInputs::update() { AI_UPDATE(cameraEnabled, !Menu::getInstance()->isOptionChecked(MenuOption::NoFaceTracking)); AI_UPDATE(cameraMuted, Menu::getInstance()->isOptionChecked(MenuOption::MuteFaceTracking)); AI_UPDATE(isHMD, qApp->isHMDMode()); - AI_UPDATE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); + + AI_UPDATE_WRITABLE(showAudioTools, Menu::getInstance()->isOptionChecked(MenuOption::AudioTools)); auto audioIO = DependencyManager::get(); const float AUDIO_METER_AVERAGING = 0.5; @@ -100,6 +111,14 @@ void AvatarInputs::update() { //iconColor = PULSE_MIN + (PULSE_MAX - PULSE_MIN) * pulseFactor; } +void AvatarInputs::setShowAudioTools(bool showAudioTools) { + if (_showAudioTools == showAudioTools) + return; + + Menu::getInstance()->setIsOptionChecked(MenuOption::AudioTools, showAudioTools); + update(); +} + void AvatarInputs::toggleCameraMute() { FaceTracker* faceTracker = qApp->getSelectedFaceTracker(); if (faceTracker) { diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h index 5535469445..0c4fc0f23c 100644 --- a/interface/src/ui/AvatarInputs.h +++ b/interface/src/ui/AvatarInputs.h @@ -29,12 +29,17 @@ class AvatarInputs : public QQuickItem { AI_PROPERTY(bool, audioClipping, false) AI_PROPERTY(float, audioLevel, 0) AI_PROPERTY(bool, isHMD, false) - AI_PROPERTY(bool, showAudioTools, true) + + Q_PROPERTY(bool showAudioTools READ showAudioTools WRITE setShowAudioTools NOTIFY showAudioToolsChanged) public: static AvatarInputs* getInstance(); AvatarInputs(QQuickItem* parent = nullptr); void update(); + bool showAudioTools() const { return _showAudioTools; } + +public slots: + void setShowAudioTools(bool showAudioTools); signals: void cameraEnabledChanged(); @@ -43,7 +48,7 @@ signals: void audioClippingChanged(); void audioLevelChanged(); void isHMDChanged(); - void showAudioToolsChanged(); + void showAudioToolsChanged(bool showAudioTools); protected: Q_INVOKABLE void resetSensors(); @@ -52,6 +57,7 @@ protected: private: float _trailingAudioLoudness{ 0 }; + bool _showAudioTools { false }; }; #endif // hifi_AvatarInputs_h diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index ede630d4ad..ae8755910b 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -46,6 +46,8 @@ #include "LODManager.h" #include "ui/OctreeStatsProvider.h" #include "ui/DomainConnectionModel.h" +#include "scripting/AudioDeviceScriptingInterface.h" +#include "ui/AvatarInputs.h" static const float DPI = 30.47f; static const float INCHES_TO_METERS = 1.0f / 39.3701f; @@ -189,6 +191,9 @@ void Web3DOverlay::loadSourceURL() { _webSurface->getRootContext()->setContextProperty("LODManager", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); _webSurface->getRootContext()->setContextProperty("DCModel", DependencyManager::get().data()); + _webSurface->getRootContext()->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); + _webSurface->getRootContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); + _webSurface->getRootContext()->setContextProperty("pathToFonts", "../../"); tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data()); diff --git a/scripts/system/audio.js b/scripts/system/audio.js index beeb8609d8..7bc8676a2e 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -45,7 +45,8 @@ function onClicked() { var entity = HMD.tabletID; Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); shouldActivateButton = true; - tablet.gotoMenuScreen("Audio"); + shouldActivateButton = true; + tablet.loadQMLSource("../Audio.qml"); onAudioScreen = true; } }