From 7446fa9e7eb81ae3476b70baea885518b6cc54a9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Feb 2019 11:21:06 -0800 Subject: [PATCH 01/71] allow mesh shapes to use MOTION_TYPE_KINEMATIC --- libraries/physics/src/EntityMotionState.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index ce9cb20c21..91c4c43c1d 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -205,6 +205,16 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { if (_entity->getShapeType() == SHAPE_TYPE_STATIC_MESH || (_body && _body->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)) { + if (_entity->isMoving()) { + // DANGER: Bullet doesn't support non-zero velocity for these shapes --> collision details may be wrong + // in ways that allow other DYNAMIC objects to tunnel/penetrate/snag. + // However, in practice low-velocity collisions work OK most of the time, and if we enforce these objects + // to be MOTION_TYPE_STATIC then some other bugs can be worse (e.g. when Grabbing --> Grab Action fails) + // so we're making a tradeoff here. + // TODO: The Correct Solution is to NOT use btBvhTriangleMesh shape for moving objects and instead compute the convex + // decomposition and build a btCompoundShape with convex sub-shapes. + return MOTION_TYPE_KINEMATIC; + } return MOTION_TYPE_STATIC; } From 304f993391b9f187e7b079b837fb570175485a62 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Feb 2019 16:50:45 -0800 Subject: [PATCH 02/71] remove enforcement of MOTION_TYPE_STATIC for mesh shapes --- libraries/physics/src/EntityMotionState.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 91c4c43c1d..4d210c96c5 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -203,21 +203,6 @@ PhysicsMotionType EntityMotionState::computePhysicsMotionType() const { } assert(entityTreeIsLocked()); - if (_entity->getShapeType() == SHAPE_TYPE_STATIC_MESH - || (_body && _body->getCollisionShape()->getShapeType() == TRIANGLE_MESH_SHAPE_PROXYTYPE)) { - if (_entity->isMoving()) { - // DANGER: Bullet doesn't support non-zero velocity for these shapes --> collision details may be wrong - // in ways that allow other DYNAMIC objects to tunnel/penetrate/snag. - // However, in practice low-velocity collisions work OK most of the time, and if we enforce these objects - // to be MOTION_TYPE_STATIC then some other bugs can be worse (e.g. when Grabbing --> Grab Action fails) - // so we're making a tradeoff here. - // TODO: The Correct Solution is to NOT use btBvhTriangleMesh shape for moving objects and instead compute the convex - // decomposition and build a btCompoundShape with convex sub-shapes. - return MOTION_TYPE_KINEMATIC; - } - return MOTION_TYPE_STATIC; - } - if (_entity->getLocked()) { if (_entity->isMoving()) { return MOTION_TYPE_KINEMATIC; From 2fdc9bce7734ff32a424576bac580a3638b4d8c9 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 15 Feb 2019 16:59:18 -0800 Subject: [PATCH 03/71] remove velocity restrictions on SHAPE_TYPE_STATIC_MESH --- libraries/entities/src/EntityItem.cpp | 78 +++++++++++---------------- 1 file changed, 32 insertions(+), 46 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2c6d679b46..049b46ec7e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1912,25 +1912,19 @@ void EntityItem::setRotation(glm::quat rotation) { void EntityItem::setVelocity(const glm::vec3& value) { glm::vec3 velocity = getLocalVelocity(); if (velocity != value) { - if (getShapeType() == SHAPE_TYPE_STATIC_MESH) { - if (velocity != Vectors::ZERO) { - setLocalVelocity(Vectors::ZERO); - } - } else { - float speed = glm::length(value); - if (!glm::isnan(speed)) { - const float MIN_LINEAR_SPEED = 0.001f; - const float MAX_LINEAR_SPEED = 270.0f; // 3m per step at 90Hz - if (speed < MIN_LINEAR_SPEED) { - velocity = ENTITY_ITEM_ZERO_VEC3; - } else if (speed > MAX_LINEAR_SPEED) { - velocity = (MAX_LINEAR_SPEED / speed) * value; - } else { - velocity = value; - } - setLocalVelocity(velocity); - _flags |= Simulation::DIRTY_LINEAR_VELOCITY; + float speed = glm::length(value); + if (!glm::isnan(speed)) { + const float MIN_LINEAR_SPEED = 0.001f; + const float MAX_LINEAR_SPEED = 270.0f; // 3m per step at 90Hz + if (speed < MIN_LINEAR_SPEED) { + velocity = ENTITY_ITEM_ZERO_VEC3; + } else if (speed > MAX_LINEAR_SPEED) { + velocity = (MAX_LINEAR_SPEED / speed) * value; + } else { + velocity = value; } + setLocalVelocity(velocity); + _flags |= Simulation::DIRTY_LINEAR_VELOCITY; } } } @@ -1948,19 +1942,15 @@ void EntityItem::setDamping(float value) { void EntityItem::setGravity(const glm::vec3& value) { withWriteLock([&] { if (_gravity != value) { - if (getShapeType() == SHAPE_TYPE_STATIC_MESH) { - _gravity = Vectors::ZERO; - } else { - float magnitude = glm::length(value); - if (!glm::isnan(magnitude)) { - const float MAX_ACCELERATION_OF_GRAVITY = 10.0f * 9.8f; // 10g - if (magnitude > MAX_ACCELERATION_OF_GRAVITY) { - _gravity = (MAX_ACCELERATION_OF_GRAVITY / magnitude) * value; - } else { - _gravity = value; - } - _flags |= Simulation::DIRTY_LINEAR_VELOCITY; + float magnitude = glm::length(value); + if (!glm::isnan(magnitude)) { + const float MAX_ACCELERATION_OF_GRAVITY = 10.0f * 9.8f; // 10g + if (magnitude > MAX_ACCELERATION_OF_GRAVITY) { + _gravity = (MAX_ACCELERATION_OF_GRAVITY / magnitude) * value; + } else { + _gravity = value; } + _flags |= Simulation::DIRTY_LINEAR_VELOCITY; } } }); @@ -1969,23 +1959,19 @@ void EntityItem::setGravity(const glm::vec3& value) { void EntityItem::setAngularVelocity(const glm::vec3& value) { glm::vec3 angularVelocity = getLocalAngularVelocity(); if (angularVelocity != value) { - if (getShapeType() == SHAPE_TYPE_STATIC_MESH) { - setLocalAngularVelocity(Vectors::ZERO); - } else { - float speed = glm::length(value); - if (!glm::isnan(speed)) { - const float MIN_ANGULAR_SPEED = 0.0002f; - const float MAX_ANGULAR_SPEED = 9.0f * TWO_PI; // 1/10 rotation per step at 90Hz - if (speed < MIN_ANGULAR_SPEED) { - angularVelocity = ENTITY_ITEM_ZERO_VEC3; - } else if (speed > MAX_ANGULAR_SPEED) { - angularVelocity = (MAX_ANGULAR_SPEED / speed) * value; - } else { - angularVelocity = value; - } - setLocalAngularVelocity(angularVelocity); - _flags |= Simulation::DIRTY_ANGULAR_VELOCITY; + float speed = glm::length(value); + if (!glm::isnan(speed)) { + const float MIN_ANGULAR_SPEED = 0.0002f; + const float MAX_ANGULAR_SPEED = 9.0f * TWO_PI; // 1/10 rotation per step at 90Hz + if (speed < MIN_ANGULAR_SPEED) { + angularVelocity = ENTITY_ITEM_ZERO_VEC3; + } else if (speed > MAX_ANGULAR_SPEED) { + angularVelocity = (MAX_ANGULAR_SPEED / speed) * value; + } else { + angularVelocity = value; } + setLocalAngularVelocity(angularVelocity); + _flags |= Simulation::DIRTY_ANGULAR_VELOCITY; } } } From 18325ef5e3771f8752f2b541a8e8455dd85e99e1 Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Mon, 16 Apr 2018 15:26:06 +0300 Subject: [PATCH 04/71] Save the "Mute Microphone" setting --- interface/src/scripting/Audio.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 2c4c29ff65..b4e3d7913b 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; +Setting::Handle mutedSetting { QStringList{ Audio::AUDIO, "MuteMicrophone" }, false }; + float Audio::loudnessToLevel(float loudness) { float level = loudness * (1/32768.0f); // level in [0, 1] @@ -42,6 +44,7 @@ Audio::Audio() : _devices(_contextIsHMD) { connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); enableNoiseReduction(enableNoiseReductionSetting.get()); onContextChanged(); + setMuted(mutedSetting.get()); } bool Audio::startRecording(const QString& filepath) { @@ -89,6 +92,15 @@ bool Audio::noiseReductionEnabled() const { }); } +void Audio::onMutedChanged() { + bool isMuted = DependencyManager::get()->isMuted(); + if (_isMuted != isMuted) { + _isMuted = isMuted; + mutedSetting.set(_isMuted); + emit mutedChanged(_isMuted); + } +} + void Audio::enableNoiseReduction(bool enable) { bool changed = false; withWriteLock([&] { From 3f523617535a991463334977b1c4c164dbcfb17d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sun, 17 Feb 2019 14:21:23 -0800 Subject: [PATCH 05/71] rework audioMuteOverlay.js --- interface/src/scripting/Audio.cpp | 10 +-- scripts/defaultScripts.js | 3 +- scripts/system/audioMuteOverlay.js | 140 +++++++++++++---------------- 3 files changed, 63 insertions(+), 90 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index b4e3d7913b..bb40f69b0b 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -76,6 +76,7 @@ void Audio::setMuted(bool isMuted) { withWriteLock([&] { if (_isMuted != isMuted) { _isMuted = isMuted; + mutedSetting.set(_isMuted); auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); changed = true; @@ -92,15 +93,6 @@ bool Audio::noiseReductionEnabled() const { }); } -void Audio::onMutedChanged() { - bool isMuted = DependencyManager::get()->isMuted(); - if (_isMuted != isMuted) { - _isMuted = isMuted; - mutedSetting.set(_isMuted); - emit mutedChanged(_isMuted); - } -} - void Audio::enableNoiseReduction(bool enable) { bool changed = false; withWriteLock([&] { diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js index bd7e79dffc..e392680df9 100644 --- a/scripts/defaultScripts.js +++ b/scripts/defaultScripts.js @@ -32,7 +32,8 @@ var DEFAULT_SCRIPTS_COMBINED = [ "system/firstPersonHMD.js", "system/tablet-ui/tabletUI.js", "system/emote.js", - "system/miniTablet.js" + "system/miniTablet.js", + "system/audioMuteOverlay.js" ]; var DEFAULT_SCRIPTS_SEPARATE = [ "system/controllers/controllerScripts.js", diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 731d62017d..14ac96c8c6 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -1,104 +1,84 @@ -"use strict"; -/* jslint vars: true, plusplus: true, forin: true*/ -/* globals Tablet, Script, AvatarList, Users, Entities, MyAvatar, Camera, Overlays, Vec3, Quat, Controller, print, getControllerWorldLocation */ -/* eslint indent: ["error", 4, { "outerIIFEBody": 0 }] */ // // audioMuteOverlay.js // // client script that creates an overlay to provide mute feedback // // Created by Triplelexx on 17/03/09 +// Reworked by Seth Alves on 2019-2-17 // 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 // +"use strict"; + +/* global Audio, Script, Overlays, Quat, MyAvatar */ + (function() { // BEGIN LOCAL_SCOPE - var utilsPath = Script.resolvePath('../developer/libraries/utils.js'); - Script.include(utilsPath); - var TWEEN_SPEED = 0.025; - var MIX_AMOUNT = 0.25; + var lastInputLoudness = 0.0; + var sampleRate = 8.0; // Hz + var attackTC = Math.exp(-1.0 / (sampleRate * 0.500)) // 500 milliseconds attack + var releaseTC = Math.exp(-1.0 / (sampleRate * 1.000)) // 1000 milliseconds release + var holdReset = 2.0 * sampleRate; // 2 seconds hold + var holdCount = 0; + var warningOverlayID = null; - var overlayPosition = Vec3.ZERO; - var tweenPosition = 0; - var startColor = { - red: 170, - green: 170, - blue: 170 - }; - var endColor = { - red: 255, - green: 0, - blue: 0 - }; - var overlayID; + function showWarning() { + if (warningOverlayID) { + return; + } + warningOverlayID = Overlays.addOverlay("text3d", { + name: "Muted-Warning", + localPosition: { x: 0.2, y: -0.35, z: -1.0 }, + localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }), + text: "Warning: you are muted", + textAlpha: 1, + color: { red: 226, green: 51, blue: 77 }, + backgroundAlpha: 0, + lineHeight: 0.042, + visible: true, + ignoreRayIntersection: true, + drawInFront: true, + grabbable: false, + parentID: MyAvatar.SELF_ID, + parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX") + }); + }; - Script.update.connect(update); - Script.scriptEnding.connect(cleanup); + function hideWarning() { + if (!warningOverlayID) { + return; + } + Overlays.deleteOverlay(warningOverlayID); + warningOverlayID = null; + } - function update(dt) { - if (!Audio.muted) { - if (hasOverlay()) { - deleteOverlay(); - } - } else if (!hasOverlay()) { - createOverlay(); - } else { - updateOverlay(); - } - } + function cleanup() { + Overlays.deleteOverlay(warningOverlayID); + } - function getOffsetPosition() { - return Vec3.sum(Camera.position, Quat.getFront(Camera.orientation)); - } + Script.scriptEnding.connect(cleanup); - function createOverlay() { - overlayPosition = getOffsetPosition(); - overlayID = Overlays.addOverlay("sphere", { - position: overlayPosition, - rotation: Camera.orientation, - alpha: 0.9, - dimensions: 0.1, - solid: true, - ignoreRayIntersection: true - }); - } + Script.setInterval(function() { - function hasOverlay() { - return Overlays.getProperty(overlayID, "position") !== undefined; - } + var inputLoudness = Audio.inputLevel; + var tc = (inputLoudness > lastInputLoudness) ? attackTC : releaseTC; + inputLoudness += tc * (lastInputLoudness - inputLoudness); + lastInputLoudness = inputLoudness; - function updateOverlay() { - // increase by TWEEN_SPEED until completion - if (tweenPosition < 1) { - tweenPosition += TWEEN_SPEED; - } else { - // after tween completion reset to zero and flip values to ping pong - tweenPosition = 0; - for (var component in startColor) { - var storedColor = startColor[component]; - startColor[component] = endColor[component]; - endColor[component] = storedColor; - } - } - // mix previous position with new and mix colors - overlayPosition = Vec3.mix(overlayPosition, getOffsetPosition(), MIX_AMOUNT); - Overlays.editOverlay(overlayID, { - color: colorMix(startColor, endColor, easeIn(tweenPosition)), - position: overlayPosition, - rotation: Camera.orientation - }); - } + if (Audio.muted && inputLoudness > 0.3) { + holdCount = holdReset; + } else { + holdCount = Math.max(holdCount - 1, 0); + } - function deleteOverlay() { - Overlays.deleteOverlay(overlayID); - } + if (holdCount > 0) { + showWarning(); + } else { + hideWarning(); + } + }, 1000.0 / sampleRate); - function cleanup() { - deleteOverlay(); - Audio.muted.disconnect(onMuteToggled); - Script.update.disconnect(update); - } }()); // END LOCAL_SCOPE From 38256df0f24cd710c2e8e32683fa73de9081361f Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 19 Feb 2019 09:32:41 -0800 Subject: [PATCH 06/71] add a way to disble muted warning from audio panel. fix positioning of warning. hide warning when removing timer. --- interface/resources/qml/hifi/audio/Audio.qml | 13 ++ interface/src/scripting/Audio.cpp | 25 ++++ interface/src/scripting/Audio.h | 14 +- libraries/audio-client/src/AudioClient.cpp | 8 + libraries/audio-client/src/AudioClient.h | 5 + scripts/system/audioMuteOverlay.js | 146 ++++++++++++------- 6 files changed, 155 insertions(+), 56 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index c8dd83cd62..34ae64aee8 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -159,6 +159,19 @@ Rectangle { onXChanged: rightMostInputLevelPos = x + width } } + + RowLayout { + spacing: muteMic.spacing*2; + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Warn when muted"); + checked: AudioScriptingInterface.warnWhenMuted; + onClicked: { + AudioScriptingInterface.warnWhenMuted = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.warnWhenMuted; }); // restore binding + } + } + } } Separator {} diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index bb40f69b0b..4a4b3c146b 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -25,6 +25,7 @@ QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; +Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle mutedSetting { QStringList{ Audio::AUDIO, "MuteMicrophone" }, false }; @@ -39,10 +40,12 @@ Audio::Audio() : _devices(_contextIsHMD) { auto client = DependencyManager::get().data(); connect(client, &AudioClient::muteToggled, this, &Audio::setMuted); connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction); + connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); enableNoiseReduction(enableNoiseReductionSetting.get()); + enableWarnWhenMuted(enableWarnWhenMutedSetting.get()); onContextChanged(); setMuted(mutedSetting.get()); } @@ -109,6 +112,28 @@ void Audio::enableNoiseReduction(bool enable) { } } +bool Audio::warnWhenMutedEnabled() const { + return resultWithReadLock([&] { + return _enableWarnWhenMuted; + }); +} + +void Audio::enableWarnWhenMuted(bool enable) { + bool changed = false; + withWriteLock([&] { + if (_enableWarnWhenMuted != enable) { + _enableWarnWhenMuted = enable; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setWarnWhenMuted", Q_ARG(bool, enable), Q_ARG(bool, false)); + enableWarnWhenMutedSetting.set(enable); + changed = true; + } + }); + if (changed) { + emit warnWhenMutedChanged(enable); + } +} + float Audio::getInputVolume() const { return resultWithReadLock([&] { return _inputVolume; diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index e4dcba9130..7e216eb0b2 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -58,6 +58,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) + Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged) Q_PROPERTY(float inputVolume READ getInputVolume WRITE setInputVolume NOTIFY inputVolumeChanged) Q_PROPERTY(float inputLevel READ getInputLevel NOTIFY inputLevelChanged) Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) @@ -75,6 +76,7 @@ public: bool isMuted() const; bool noiseReductionEnabled() const; + bool warnWhenMutedEnabled() const; float getInputVolume() const; float getInputLevel() const; bool isClipping() const; @@ -192,7 +194,7 @@ signals: * }); */ void mutedChanged(bool isMuted); - + /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged @@ -201,6 +203,14 @@ signals: */ void noiseReductionChanged(bool isEnabled); + /**jsdoc + * Triggered when "warn when muted" is enabled or disabled. + * @function Audio.warnWhenMutedChanged + * @param {boolean} isEnabled - true if "warn when muted" is enabled, otherwise false. + * @returns {Signal} + */ + void warnWhenMutedChanged(bool isEnabled); + /**jsdoc * Triggered when the input audio volume changes. * @function Audio.inputVolumeChanged @@ -248,6 +258,7 @@ public slots: private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); + void enableWarnWhenMuted(bool enable); void setInputVolume(float volume); void onInputLoudnessChanged(float loudness, bool isClipping); @@ -262,6 +273,7 @@ private: bool _isClipping { false }; bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. + bool _enableWarnWhenMuted { true }; bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 8c50a195ee..1c10d24f23 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -1531,6 +1531,14 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) { } } +void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) { + if (_warnWhenMuted != enable) { + _warnWhenMuted = enable; + if (emitSignal) { + emit warnWhenMutedChanged(_warnWhenMuted); + } + } +} bool AudioClient::setIsStereoInput(bool isStereoInput) { bool stereoInputChanged = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 29036b7c71..6d3483b0f8 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -210,6 +210,9 @@ public slots: void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true); bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } + void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); + bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } + bool getLocalEcho() { return _shouldEchoLocally; } void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } @@ -246,6 +249,7 @@ signals: void inputVolumeChanged(float volume); void muteToggled(bool muted); void noiseReductionChanged(bool noiseReductionEnabled); + void warnWhenMutedChanged(bool warnWhenMutedEnabled); void mutedByMixer(); void inputReceived(const QByteArray& inputSamples); void inputLoudnessChanged(float loudness, bool isClipping); @@ -365,6 +369,7 @@ private: bool _shouldEchoLocally; bool _shouldEchoToServer; bool _isNoiseGateEnabled; + bool _warnWhenMuted; bool _reverb; AudioEffectOptions _scriptReverbOptions; diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 14ac96c8c6..d759b7d885 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -17,68 +17,104 @@ (function() { // BEGIN LOCAL_SCOPE - var lastInputLoudness = 0.0; - var sampleRate = 8.0; // Hz - var attackTC = Math.exp(-1.0 / (sampleRate * 0.500)) // 500 milliseconds attack - var releaseTC = Math.exp(-1.0 / (sampleRate * 1.000)) // 1000 milliseconds release - var holdReset = 2.0 * sampleRate; // 2 seconds hold - var holdCount = 0; - var warningOverlayID = null; + var lastInputLoudness = 0.0; + var sampleRate = 8.0; // Hz + var attackTC = Math.exp(-1.0 / (sampleRate * 0.500)); // 500 milliseconds attack + var releaseTC = Math.exp(-1.0 / (sampleRate * 1.000)); // 1000 milliseconds release + var holdReset = 2.0 * sampleRate; // 2 seconds hold + var holdCount = 0; + var warningOverlayID = null; + var pollInterval = null; + var warningText = "Muted"; + var textDimensions = { x: 100, y: 50 }; - function showWarning() { - if (warningOverlayID) { - return; - } - warningOverlayID = Overlays.addOverlay("text3d", { - name: "Muted-Warning", - localPosition: { x: 0.2, y: -0.35, z: -1.0 }, - localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }), - text: "Warning: you are muted", - textAlpha: 1, - color: { red: 226, green: 51, blue: 77 }, - backgroundAlpha: 0, - lineHeight: 0.042, - visible: true, - ignoreRayIntersection: true, - drawInFront: true, - grabbable: false, - parentID: MyAvatar.SELF_ID, - parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX") - }); - }; + function showWarning() { + if (warningOverlayID) { + return; + } - function hideWarning() { - if (!warningOverlayID) { - return; - } - Overlays.deleteOverlay(warningOverlayID); - warningOverlayID = null; - } + var windowWidth; + var windowHeight; + if (HMD.active) { + var viewportDimension = Controller.getViewportDimensions(); + windowWidth = viewportDimension.x; + windowHeight = viewportDimension.y; + } else { + windowWidth = Window.innerWidth; + windowHeight = Window.innerHeight; + } - function cleanup() { - Overlays.deleteOverlay(warningOverlayID); - } + warningOverlayID = Overlays.addOverlay("text", { + name: "Muted-Warning", + font: { size: 36 }, + text: warningText, + x: windowWidth / 2 - textDimensions.x / 2, + y: windowHeight / 2 - textDimensions.y / 2, + width: textDimensions.x, + height: textDimensions.y, + textColor: { red: 226, green: 51, blue: 77 }, + backgroundAlpha: 0, + visible: true + }); + } - Script.scriptEnding.connect(cleanup); + function hideWarning() { + if (!warningOverlayID) { + return; + } + Overlays.deleteOverlay(warningOverlayID); + warningOverlayID = null; + } - Script.setInterval(function() { + function startPoll() { + if (pollInterval) { + return; + } + pollInterval = Script.setInterval(function() { + var inputLoudness = Audio.inputLevel; + var tc = (inputLoudness > lastInputLoudness) ? attackTC : releaseTC; + inputLoudness += tc * (lastInputLoudness - inputLoudness); + lastInputLoudness = inputLoudness; - var inputLoudness = Audio.inputLevel; - var tc = (inputLoudness > lastInputLoudness) ? attackTC : releaseTC; - inputLoudness += tc * (lastInputLoudness - inputLoudness); - lastInputLoudness = inputLoudness; + if (inputLoudness > 0.1) { + holdCount = holdReset; + } else { + holdCount = Math.max(holdCount - 1, 0); + } - if (Audio.muted && inputLoudness > 0.3) { - holdCount = holdReset; - } else { - holdCount = Math.max(holdCount - 1, 0); - } + if (holdCount > 0) { + showWarning(); + } else { + hideWarning(); + } + }, 1000.0 / sampleRate); + } - if (holdCount > 0) { - showWarning(); - } else { - hideWarning(); - } - }, 1000.0 / sampleRate); + function stopPoll() { + if (!pollInterval) { + return; + } + Script.clearInterval(pollInterval); + pollInterval = null; + hideWarning(); + } + + function startOrStopPoll() { + if (Audio.warnWhenMuted && Audio.muted) { + startPoll(); + } else { + stopPoll(); + } + } + + function cleanup() { + stopPoll(); + } + + Script.scriptEnding.connect(cleanup); + + startOrStopPoll(); + Audio.mutedChanged.connect(startOrStopPoll); + Audio.warnWhenMutedChanged.connect(startOrStopPoll); }()); // END LOCAL_SCOPE From 76aa6fb1b9a7416c6c8858c7fc9537a48ff6d14a Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 20 Feb 2019 12:53:43 -0800 Subject: [PATCH 07/71] keep muted warning in center of view for HMD --- scripts/system/audioMuteOverlay.js | 51 ++++++++++++++++++------------ 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index d759b7d885..65793d1d87 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -26,36 +26,45 @@ var warningOverlayID = null; var pollInterval = null; var warningText = "Muted"; - var textDimensions = { x: 100, y: 50 }; function showWarning() { if (warningOverlayID) { return; } - var windowWidth; - var windowHeight; if (HMD.active) { - var viewportDimension = Controller.getViewportDimensions(); - windowWidth = viewportDimension.x; - windowHeight = viewportDimension.y; + warningOverlayID = Overlays.addOverlay("text3d", { + name: "Muted-Warning", + localPosition: { x: 0, y: 0, z: -1.0 }, + localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }), + text: warningText, + textAlpha: 1, + textColor: { red: 226, green: 51, blue: 77 }, + backgroundAlpha: 0, + lineHeight: 0.042, + dimensions: { x: 0.11, y: 0.05 }, + visible: true, + ignoreRayIntersection: true, + drawInFront: true, + grabbable: false, + parentID: MyAvatar.SELF_ID, + parentJointIndex: MyAvatar.getJointIndex("_CAMERA_MATRIX") + }); } else { - windowWidth = Window.innerWidth; - windowHeight = Window.innerHeight; + var textDimensions = { x: 100, y: 50 }; + warningOverlayID = Overlays.addOverlay("text", { + name: "Muted-Warning", + font: { size: 36 }, + text: warningText, + x: Window.innerWidth / 2 - textDimensions.x / 2, + y: Window.innerHeight / 2 - textDimensions.y / 2, + width: textDimensions.x, + height: textDimensions.y, + textColor: { red: 226, green: 51, blue: 77 }, + backgroundAlpha: 0, + visible: true + }); } - - warningOverlayID = Overlays.addOverlay("text", { - name: "Muted-Warning", - font: { size: 36 }, - text: warningText, - x: windowWidth / 2 - textDimensions.x / 2, - y: windowHeight / 2 - textDimensions.y / 2, - width: textDimensions.x, - height: textDimensions.y, - textColor: { red: 226, green: 51, blue: 77 }, - backgroundAlpha: 0, - visible: true - }); } function hideWarning() { From bbad6af0d692b176bef20dc87866ccda8848d04c Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 20 Feb 2019 13:07:24 -0800 Subject: [PATCH 08/71] attempt to take background noise into account when triggering mute warning --- scripts/system/audioMuteOverlay.js | 33 ++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 65793d1d87..96f6d636dc 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -13,14 +13,22 @@ "use strict"; -/* global Audio, Script, Overlays, Quat, MyAvatar */ +/* global Audio, Script, Overlays, Quat, MyAvatar, HMD */ (function() { // BEGIN LOCAL_SCOPE - var lastInputLoudness = 0.0; + var lastShortTermInputLoudness = 0.0; + var lastLongTermInputLoudness = 0.0; var sampleRate = 8.0; // Hz - var attackTC = Math.exp(-1.0 / (sampleRate * 0.500)); // 500 milliseconds attack - var releaseTC = Math.exp(-1.0 / (sampleRate * 1.000)); // 1000 milliseconds release + + var shortTermAttackTC = Math.exp(-1.0 / (sampleRate * 0.500)); // 500 milliseconds attack + var shortTermReleaseTC = Math.exp(-1.0 / (sampleRate * 1.000)); // 1000 milliseconds release + + var longTermAttackTC = Math.exp(-1.0 / (sampleRate * 5.0)); // 5 second attack + var longTermReleaseTC = Math.exp(-1.0 / (sampleRate * 10.0)); // 10 seconds release + + var activationThreshold = 0.05; // how much louder short-term needs to be than long-term to trigger warning + var holdReset = 2.0 * sampleRate; // 2 seconds hold var holdCount = 0; var warningOverlayID = null; @@ -80,12 +88,19 @@ return; } pollInterval = Script.setInterval(function() { - var inputLoudness = Audio.inputLevel; - var tc = (inputLoudness > lastInputLoudness) ? attackTC : releaseTC; - inputLoudness += tc * (lastInputLoudness - inputLoudness); - lastInputLoudness = inputLoudness; + var shortTermInputLoudness = Audio.inputLevel; + var longTermInputLoudness = shortTermInputLoudness; - if (inputLoudness > 0.1) { + var shortTc = (shortTermInputLoudness > lastShortTermInputLoudness) ? shortTermAttackTC : shortTermReleaseTC; + var longTc = (longTermInputLoudness > lastLongTermInputLoudness) ? longTermAttackTC : longTermReleaseTC; + + shortTermInputLoudness += shortTc * (lastShortTermInputLoudness - shortTermInputLoudness); + longTermInputLoudness += longTc * (lastLongTermInputLoudness - longTermInputLoudness); + + lastShortTermInputLoudness = shortTermInputLoudness; + lastLongTermInputLoudness = longTermInputLoudness; + + if (shortTermInputLoudness > lastLongTermInputLoudness + activationThreshold) { holdCount = holdReset; } else { holdCount = Math.max(holdCount - 1, 0); From 6f400796214c1f26a15def2f98fe619806298649 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 21 Feb 2019 14:04:08 -0800 Subject: [PATCH 09/71] added a button to enable server loopback of audio --- interface/resources/qml/hifi/audio/Audio.qml | 7 ++ .../qml/hifi/audio/LoopbackAudio.qml | 75 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 interface/resources/qml/hifi/audio/LoopbackAudio.qml diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 34ae64aee8..e340ec5003 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -313,5 +313,12 @@ Rectangle { (bar.currentIndex === 0 && !isVR); anchors { left: parent.left; leftMargin: margins.paddings } } + LoopbackAudio { + x: margins.paddings + + visible: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR); + anchors { left: parent.left; leftMargin: margins.paddings } + } } } diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml new file mode 100644 index 0000000000..6d5f8d88fd --- /dev/null +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -0,0 +1,75 @@ +// +// LoopbackAudio.qml +// qml/hifi/audio +// +// Created by Seth Alves on 2019-2-18 +// Copyright 2019 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 + +import stylesUit 1.0 +import controlsUit 1.0 as HifiControls + +RowLayout { + property bool audioLoopedBack: false; + function startAudioLoopback() { + if (!audioLoopedBack) { + audioLoopedBack = true; + AudioScope.setServerEcho(true); + } + } + function stopAudioLoopback () { + if (audioLoopedBack) { + audioLoopedBack = false; + AudioScope.setServerEcho(false); + } + } + + Component.onDestruction: stopAudioLoopback(); + onVisibleChanged: { + if (!visible) { + stopAudioLoopback(); + } + } + + HifiConstants { id: hifi; } + + Button { + id: control + background: Rectangle { + implicitWidth: 20; + implicitHeight: 20; + radius: hifi.buttons.radius; + gradient: Gradient { + GradientStop { + position: 0.2; + color: audioLoopedBack ? hifi.buttons.colorStart[hifi.buttons.blue] : hifi.buttons.colorStart[hifi.buttons.black]; + } + GradientStop { + position: 1.0; + color: audioLoopedBack ? hifi.buttons.colorFinish[hifi.buttons.blue] : hifi.buttons.colorFinish[hifi.buttons.black]; + } + } + } + contentItem: HiFiGlyphs { + size: 14; + color: (control.pressed || control.hovered) ? (audioLoopedBack ? "black" : hifi.colors.primaryHighlight) : "white"; + text: audioLoopedBack ? hifi.glyphs.stop_square : hifi.glyphs.playback_play; + } + + onClicked: audioLoopedBack ? stopAudioLoopback() : startAudioLoopback(); + } + + RalewayRegular { + Layout.leftMargin: 2; + size: 14; + color: "white"; + text: audioLoopedBack ? qsTr("Disable Audio Loopback") : qsTr("Enable Audio Loopback"); + } +} From 9ff99c721386cf620dc6dd26d35138f87b295d44 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 22 Feb 2019 09:33:23 -0800 Subject: [PATCH 10/71] make server-audio-loopback button work in HMDs --- interface/resources/qml/hifi/audio/LoopbackAudio.qml | 4 ++-- libraries/audio-client/src/AudioClient.h | 12 ++++++------ libraries/audio/src/AbstractAudioInterface.h | 9 ++++++++- .../script-engine/src/AudioScriptingInterface.cpp | 12 ++++++++++++ .../script-engine/src/AudioScriptingInterface.h | 12 ++++++++++++ 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml index 6d5f8d88fd..2f0dbe5950 100644 --- a/interface/resources/qml/hifi/audio/LoopbackAudio.qml +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -21,13 +21,13 @@ RowLayout { function startAudioLoopback() { if (!audioLoopedBack) { audioLoopedBack = true; - AudioScope.setServerEcho(true); + AudioScriptingInterface.setServerEcho(true); } } function stopAudioLoopback () { if (audioLoopedBack) { audioLoopedBack = false; - AudioScope.setServerEcho(false); + AudioScriptingInterface.setServerEcho(false); } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 6d3483b0f8..b9648219a5 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -213,13 +213,13 @@ public slots: void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } - bool getLocalEcho() { return _shouldEchoLocally; } - void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; } - void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } + virtual bool getLocalEcho() override { return _shouldEchoLocally; } + virtual void setLocalEcho(bool localEcho) override { _shouldEchoLocally = localEcho; } + virtual void toggleLocalEcho() override { _shouldEchoLocally = !_shouldEchoLocally; } - bool getServerEcho() { return _shouldEchoToServer; } - void setServerEcho(bool serverEcho) { _shouldEchoToServer = serverEcho; } - void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } + virtual bool getServerEcho() override { return _shouldEchoToServer; } + virtual void setServerEcho(bool serverEcho) override { _shouldEchoToServer = serverEcho; } + virtual void toggleServerEcho() override { _shouldEchoToServer = !_shouldEchoToServer; } void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); void sendMuteEnvironmentPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 0f075ab224..e9e40e95f9 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -45,9 +45,16 @@ public slots: virtual bool shouldLoopbackInjectors() { return false; } virtual bool setIsStereoInput(bool stereo) = 0; - virtual bool isStereoInput() = 0; + virtual bool getLocalEcho() = 0; + virtual void setLocalEcho(bool localEcho) = 0; + virtual void toggleLocalEcho() = 0; + + virtual bool getServerEcho() = 0; + virtual void setServerEcho(bool serverEcho) = 0; + virtual void toggleServerEcho() = 0; + signals: void isStereoInputChanged(bool isStereo); }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 8e54d2d5de..b12b55c3f7 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -88,3 +88,15 @@ bool AudioScriptingInterface::isStereoInput() { } return stereoEnabled; } + +void AudioScriptingInterface::setServerEcho(bool serverEcho) { + if (_localAudioInterface) { + QMetaObject::invokeMethod(_localAudioInterface, "setServerEcho", Q_ARG(bool, serverEcho)); + } +} + +void AudioScriptingInterface::setLocalEcho(bool localEcho) { + if (_localAudioInterface) { + QMetaObject::invokeMethod(_localAudioInterface, "setLocalEcho", Q_ARG(bool, localEcho)); + } +} diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index d2f886d2dd..23cc02248d 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -66,6 +66,18 @@ public: _localAudioInterface->getAudioSolo().reset(); } + /**jsdoc + * @function Audio.setServerEcho + * @parm {boolean} serverEcho + */ + Q_INVOKABLE void setServerEcho(bool serverEcho); + + /**jsdoc + * @function Audio.setLocalEcho + * @parm {boolean} localEcho + */ + Q_INVOKABLE void setLocalEcho(bool localEcho); + protected: AudioScriptingInterface() = default; From 0523a0d06dc6fa751cfb7111c8f1730b96d548fa Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Sat, 23 Feb 2019 14:23:09 -0800 Subject: [PATCH 11/71] don't disable server echo when audio qml page is closed --- .../qml/hifi/audio/LoopbackAudio.qml | 9 +----- .../src/AudioScriptingInterface.cpp | 28 +++++++++++++++++++ .../src/AudioScriptingInterface.h | 21 ++++++++++++++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/hifi/audio/LoopbackAudio.qml b/interface/resources/qml/hifi/audio/LoopbackAudio.qml index 2f0dbe5950..3ecf09c948 100644 --- a/interface/resources/qml/hifi/audio/LoopbackAudio.qml +++ b/interface/resources/qml/hifi/audio/LoopbackAudio.qml @@ -17,7 +17,7 @@ import stylesUit 1.0 import controlsUit 1.0 as HifiControls RowLayout { - property bool audioLoopedBack: false; + property bool audioLoopedBack: AudioScriptingInterface.getServerEcho(); function startAudioLoopback() { if (!audioLoopedBack) { audioLoopedBack = true; @@ -31,13 +31,6 @@ RowLayout { } } - Component.onDestruction: stopAudioLoopback(); - onVisibleChanged: { - if (!visible) { - stopAudioLoopback(); - } - } - HifiConstants { id: hifi; } Button { diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index b12b55c3f7..65d71e46e6 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -89,14 +89,42 @@ bool AudioScriptingInterface::isStereoInput() { return stereoEnabled; } +bool AudioScriptingInterface::getServerEcho() { + bool serverEchoEnabled = false; + if (_localAudioInterface) { + serverEchoEnabled = _localAudioInterface->getServerEcho(); + } + return serverEchoEnabled; +} + void AudioScriptingInterface::setServerEcho(bool serverEcho) { if (_localAudioInterface) { QMetaObject::invokeMethod(_localAudioInterface, "setServerEcho", Q_ARG(bool, serverEcho)); } } +void AudioScriptingInterface::toggleServerEcho() { + if (_localAudioInterface) { + QMetaObject::invokeMethod(_localAudioInterface, "toggleServerEcho"); + } +} + +bool AudioScriptingInterface::getLocalEcho() { + bool localEchoEnabled = false; + if (_localAudioInterface) { + localEchoEnabled = _localAudioInterface->getLocalEcho(); + } + return localEchoEnabled; +} + void AudioScriptingInterface::setLocalEcho(bool localEcho) { if (_localAudioInterface) { QMetaObject::invokeMethod(_localAudioInterface, "setLocalEcho", Q_ARG(bool, localEcho)); } } + +void AudioScriptingInterface::toggleLocalEcho() { + if (_localAudioInterface) { + QMetaObject::invokeMethod(_localAudioInterface, "toggleLocalEcho"); + } +} diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 23cc02248d..a6801dcdcb 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -66,18 +66,39 @@ public: _localAudioInterface->getAudioSolo().reset(); } + /**jsdoc + * @function Audio.getServerEcho + */ + Q_INVOKABLE bool getServerEcho(); + /**jsdoc * @function Audio.setServerEcho * @parm {boolean} serverEcho */ Q_INVOKABLE void setServerEcho(bool serverEcho); + /**jsdoc + * @function Audio.toggleServerEcho + */ + Q_INVOKABLE void toggleServerEcho(); + + /**jsdoc + * @function Audio.getLocalEcho + */ + Q_INVOKABLE bool getLocalEcho(); + /**jsdoc * @function Audio.setLocalEcho * @parm {boolean} localEcho */ Q_INVOKABLE void setLocalEcho(bool localEcho); + /**jsdoc + * @function Audio.toggleLocalEcho + */ + Q_INVOKABLE void toggleLocalEcho(); + + protected: AudioScriptingInterface() = default; From 90a6e0d9b01446c4f4f0aa28be66c7961b8d3a24 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 4 Mar 2019 11:42:43 -0800 Subject: [PATCH 12/71] changing mute warning position --- scripts/system/audioMuteOverlay.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/audioMuteOverlay.js b/scripts/system/audioMuteOverlay.js index 96f6d636dc..cd0c99ab6e 100644 --- a/scripts/system/audioMuteOverlay.js +++ b/scripts/system/audioMuteOverlay.js @@ -43,7 +43,7 @@ if (HMD.active) { warningOverlayID = Overlays.addOverlay("text3d", { name: "Muted-Warning", - localPosition: { x: 0, y: 0, z: -1.0 }, + localPosition: { x: 0.0, y: -0.5, z: -1.0 }, localOrientation: Quat.fromVec3Degrees({ x: 0.0, y: 0.0, z: 0.0, w: 1.0 }), text: warningText, textAlpha: 1, @@ -64,8 +64,8 @@ name: "Muted-Warning", font: { size: 36 }, text: warningText, - x: Window.innerWidth / 2 - textDimensions.x / 2, - y: Window.innerHeight / 2 - textDimensions.y / 2, + x: (Window.innerWidth - textDimensions.x) / 2, + y: (Window.innerHeight - textDimensions.y), width: textDimensions.x, height: textDimensions.y, textColor: { red: 226, green: 51, blue: 77 }, From 7860db68eb6ca4f557582fc022f42873b16b4fda Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 4 Mar 2019 13:33:16 -0800 Subject: [PATCH 13/71] culling hearing oneself and testing sound --- interface/resources/qml/hifi/audio/Audio.qml | 7 ---- libraries/audio-client/src/AudioClient.h | 12 +++--- libraries/audio/src/AbstractAudioInterface.h | 8 ---- .../src/AudioScriptingInterface.cpp | 42 +------------------ .../src/AudioScriptingInterface.h | 33 --------------- 5 files changed, 7 insertions(+), 95 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index e340ec5003..34ae64aee8 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -313,12 +313,5 @@ Rectangle { (bar.currentIndex === 0 && !isVR); anchors { left: parent.left; leftMargin: margins.paddings } } - LoopbackAudio { - x: margins.paddings - - visible: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR); - anchors { left: parent.left; leftMargin: margins.paddings } - } } } diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index b9648219a5..6d3483b0f8 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -213,13 +213,13 @@ public slots: void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } - virtual bool getLocalEcho() override { return _shouldEchoLocally; } - virtual void setLocalEcho(bool localEcho) override { _shouldEchoLocally = localEcho; } - virtual void toggleLocalEcho() override { _shouldEchoLocally = !_shouldEchoLocally; } + bool getLocalEcho() { return _shouldEchoLocally; } + void setLocalEcho(bool localEcho) { _shouldEchoLocally = localEcho; } + void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } - virtual bool getServerEcho() override { return _shouldEchoToServer; } - virtual void setServerEcho(bool serverEcho) override { _shouldEchoToServer = serverEcho; } - virtual void toggleServerEcho() override { _shouldEchoToServer = !_shouldEchoToServer; } + bool getServerEcho() { return _shouldEchoToServer; } + void setServerEcho(bool serverEcho) { _shouldEchoToServer = serverEcho; } + void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); void sendMuteEnvironmentPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index e9e40e95f9..dc7d25fdc2 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -47,14 +47,6 @@ public slots: virtual bool setIsStereoInput(bool stereo) = 0; virtual bool isStereoInput() = 0; - virtual bool getLocalEcho() = 0; - virtual void setLocalEcho(bool localEcho) = 0; - virtual void toggleLocalEcho() = 0; - - virtual bool getServerEcho() = 0; - virtual void setServerEcho(bool serverEcho) = 0; - virtual void toggleServerEcho() = 0; - signals: void isStereoInputChanged(bool isStereo); }; diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 65d71e46e6..c695d67d91 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -87,44 +87,4 @@ bool AudioScriptingInterface::isStereoInput() { stereoEnabled = _localAudioInterface->isStereoInput(); } return stereoEnabled; -} - -bool AudioScriptingInterface::getServerEcho() { - bool serverEchoEnabled = false; - if (_localAudioInterface) { - serverEchoEnabled = _localAudioInterface->getServerEcho(); - } - return serverEchoEnabled; -} - -void AudioScriptingInterface::setServerEcho(bool serverEcho) { - if (_localAudioInterface) { - QMetaObject::invokeMethod(_localAudioInterface, "setServerEcho", Q_ARG(bool, serverEcho)); - } -} - -void AudioScriptingInterface::toggleServerEcho() { - if (_localAudioInterface) { - QMetaObject::invokeMethod(_localAudioInterface, "toggleServerEcho"); - } -} - -bool AudioScriptingInterface::getLocalEcho() { - bool localEchoEnabled = false; - if (_localAudioInterface) { - localEchoEnabled = _localAudioInterface->getLocalEcho(); - } - return localEchoEnabled; -} - -void AudioScriptingInterface::setLocalEcho(bool localEcho) { - if (_localAudioInterface) { - QMetaObject::invokeMethod(_localAudioInterface, "setLocalEcho", Q_ARG(bool, localEcho)); - } -} - -void AudioScriptingInterface::toggleLocalEcho() { - if (_localAudioInterface) { - QMetaObject::invokeMethod(_localAudioInterface, "toggleLocalEcho"); - } -} +} \ No newline at end of file diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index a6801dcdcb..d2f886d2dd 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -66,39 +66,6 @@ public: _localAudioInterface->getAudioSolo().reset(); } - /**jsdoc - * @function Audio.getServerEcho - */ - Q_INVOKABLE bool getServerEcho(); - - /**jsdoc - * @function Audio.setServerEcho - * @parm {boolean} serverEcho - */ - Q_INVOKABLE void setServerEcho(bool serverEcho); - - /**jsdoc - * @function Audio.toggleServerEcho - */ - Q_INVOKABLE void toggleServerEcho(); - - /**jsdoc - * @function Audio.getLocalEcho - */ - Q_INVOKABLE bool getLocalEcho(); - - /**jsdoc - * @function Audio.setLocalEcho - * @parm {boolean} localEcho - */ - Q_INVOKABLE void setLocalEcho(bool localEcho); - - /**jsdoc - * @function Audio.toggleLocalEcho - */ - Q_INVOKABLE void toggleLocalEcho(); - - protected: AudioScriptingInterface() = default; From 23e8a8bed4c6f086fa5718153cf6733ae218ae68 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 5 Mar 2019 17:09:54 -0800 Subject: [PATCH 14/71] Initial implementation (deadlocks still occurring in Audio.cpp) --- interface/resources/qml/hifi/audio/Audio.qml | 19 ++ interface/resources/qml/hifi/audio/MicBar.qml | 9 +- interface/src/Application.cpp | 18 ++ interface/src/Application.h | 2 + interface/src/scripting/Audio.cpp | 174 +++++++++++++++++- interface/src/scripting/Audio.h | 89 ++++++++- scripts/system/audio.js | 3 + 7 files changed, 302 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index c8dd83cd62..45358f59a2 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -148,6 +148,25 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk"); + checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; + onClicked: { + if (isVR) { + AudioScriptingInterface.pushToTalkHMD = checked; + } else { + AudioScriptingInterface.pushToTalkDesktop = checked; + } + checked = Qt.binding(function() { + if (isVR) { + return AudioScriptingInterface.pushToTalkHMD; + } else { + return AudioScriptingInterface.pushToTalkDesktop; + } + }); // restore binding + } + } AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Show audio level meter"); diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 39f75a9182..f91058bc3c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -11,10 +11,13 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 +import stylesUit 1.0 import TabletScriptingInterface 1.0 Rectangle { + HifiConstants { id: hifi; } + readonly property var level: AudioScriptingInterface.inputLevel; property bool gated: false; @@ -131,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.muted; + visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -152,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.muted ? "MUTED" : "MUTE"; + text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 2c8d71af00..bcd5367a89 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1431,6 +1431,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(this, &Application::activeDisplayPluginChanged, reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); + connect(this, &Application::pushedToTalk, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::handlePushedToTalk); } // Create the rendering engine. This can be slow on some machines due to lots of @@ -4195,6 +4197,10 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + case Qt::Key_T: + emit pushedToTalk(true); + break; + case Qt::Key_P: { if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { AudioInjectorOptions options; @@ -4300,6 +4306,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyReleaseEvent(event); } + + switch (event->key()) { + case Qt::Key_T: + emit pushedToTalk(false); + break; + } } void Application::focusOutEvent(QFocusEvent* event) { @@ -5243,6 +5255,9 @@ void Application::loadSettings() { } } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->loadData(); + getMyAvatar()->loadData(); _settingsLoaded = true; } @@ -5252,6 +5267,9 @@ void Application::saveSettings() const { DependencyManager::get()->saveSettings(); DependencyManager::get()->saveSettings(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->saveData(); + Menu::getInstance()->saveSettings(); getMyAvatar()->saveData(); PluginManager::getInstance()->saveSettings(); diff --git a/interface/src/Application.h b/interface/src/Application.h index a8cc9450c5..1c86326f90 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -358,6 +358,8 @@ signals: void miniTabletEnabledChanged(bool enabled); + void pushedToTalk(bool enabled); + public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 2c4c29ff65..fe04ce47ca 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -63,26 +63,163 @@ void Audio::stopRecording() { } bool Audio::isMuted() const { - return resultWithReadLock([&] { - return _isMuted; - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getMutedHMD(); + } + else { + return getMutedDesktop(); + } } void Audio::setMuted(bool isMuted) { + withWriteLock([&] { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } + else { + setMutedDesktop(isMuted); + } + }); +} + +void Audio::setMutedDesktop(bool isMuted) { + bool changed = false; + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit desktopMutedChanged(isMuted); + } +} + +bool Audio::getMutedDesktop() const { + return resultWithReadLock([&] { + return _desktopMuted; + }); +} + +void Audio::setMutedHMD(bool isMuted) { + bool changed = false; + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit hmdMutedChanged(isMuted); + } +} + +bool Audio::getMutedHMD() const { + return resultWithReadLock([&] { + return _hmdMuted; + }); +} + +bool Audio::getPTT() { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getPTTHMD(); + } + else { + return getPTTDesktop(); + } +} + +bool Audio::getPushingToTalk() const { + return resultWithReadLock([&] { + return _pushingToTalk; + }); +} + +void Audio::setPTT(bool enabled) { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setPTTHMD(enabled); + } + else { + setPTTDesktop(enabled); + } +} + +void Audio::setPTTDesktop(bool enabled) { bool changed = false; withWriteLock([&] { - if (_isMuted != isMuted) { - _isMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + if (_pttDesktop != enabled) { changed = true; + _pttDesktop = enabled; + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } } }); if (changed) { - emit mutedChanged(isMuted); + emit pushToTalkChanged(enabled); + emit pushToTalkDesktopChanged(enabled); } } +bool Audio::getPTTDesktop() const { + return resultWithReadLock([&] { + return _pttDesktop; + }); +} + +void Audio::setPTTHMD(bool enabled) { + bool changed = false; + withWriteLock([&] { + if (_pttHMD != enabled) { + changed = true; + _pttHMD = enabled; + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + } + }); + if (changed) { + emit pushToTalkChanged(enabled); + emit pushToTalkHMDChanged(enabled); + } +} + +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; @@ -179,11 +316,32 @@ void Audio::onContextChanged() { changed = true; } }); + if (isHMD) { + setMuted(getMutedHMD()); + } + else { + setMuted(getMutedDesktop()); + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } } +void Audio::handlePushedToTalk(bool enabled) { + if (getPTT()) { + if (enabled) { + setMuted(false); + } + else { + setMuted(true); + } + if (_pushingToTalk != enabled) { + _pushingToTalk = enabled; + emit pushingToTalkChanged(enabled); + } + } +} + void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index e4dcba9130..6aa589e399 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -12,6 +12,7 @@ #ifndef hifi_scripting_Audio_h #define hifi_scripting_Audio_h +#include #include "AudioScriptingInterface.h" #include "AudioDevices.h" #include "AudioEffectOptions.h" @@ -19,6 +20,9 @@ #include "AudioFileWav.h" #include +using MutedGetter = std::function; +using MutedSetter = std::function; + namespace scripting { class Audio : public AudioScriptingInterface, protected ReadWriteLockable { @@ -63,6 +67,12 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged) + Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged) + Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); + Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) + Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; @@ -82,6 +92,25 @@ public: void showMicMeter(bool show); + // Mute setting setters and getters + void setMutedDesktop(bool isMuted); + bool getMutedDesktop() const; + void setMutedHMD(bool isMuted); + bool getMutedHMD() const; + void setPTT(bool enabled); + bool getPTT(); + bool getPushingToTalk() const; + + // Push-To-Talk setters and getters + void setPTTDesktop(bool enabled); + bool getPTTDesktop() const; + void setPTTHMD(bool enabled); + bool getPTTHMD() const; + + // Settings handlers + void saveData(); + void loadData(); + /**jsdoc * @function Audio.setInputDevice * @param {object} device @@ -193,6 +222,46 @@ signals: */ void mutedChanged(bool isMuted); + /**jsdoc + * Triggered when desktop audio input is muted or unmuted. + * @function Audio.desktopMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. + * @returns {Signal} + */ + void desktopMutedChanged(bool isMuted); + + /**jsdoc + * Triggered when HMD audio input is muted or unmuted. + * @function Audio.hmdMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. + * @returns {Signal} + */ + void hmdMutedChanged(bool isMuted); + + /** + * Triggered when Push-to-Talk has been enabled or disabled. + * @function Audio.pushToTalkChanged + * @param {boolean} enabled - true if Push-to-Talk is enabled, otherwise false. + * @returns {Signal} + */ + void pushToTalkChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for desktop mode. + * @function Audio.pushToTalkDesktopChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for Desktop mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkDesktopChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for HMD mode. + * @function Audio.pushToTalkHMDChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for HMD mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkHMDChanged(bool enabled); + /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged @@ -237,6 +306,14 @@ signals: */ void contextChanged(const QString& context); + /**jsdoc + * Triggered when pushing to talk. + * @function Audio.pushingToTalkChanged + * @param {boolean} talking - true if broadcasting with PTT, false otherwise. + * @returns {Signal} + */ + void pushingToTalkChanged(bool talking); + public slots: /**jsdoc @@ -245,6 +322,8 @@ public slots: */ void onContextChanged(); + void handlePushedToTalk(bool enabled); + private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); @@ -260,11 +339,19 @@ private: float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; bool _isClipping { false }; - bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; + Setting::Handle _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true }; + Setting::Handle _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true }; + Setting::Handle _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false }; + Setting::Handle _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false }; + bool _desktopMuted{ true }; + bool _hmdMuted{ false }; + bool _pttDesktop{ false }; + bool _pttHMD{ false }; + bool _pushingToTalk{ false }; }; }; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index ee82c0c6ea..51d070d8cd 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -28,6 +28,9 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { + if (Audio.pushingToTalk) { + return; + } if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { From d88235a08f577bc4145bd6b8e518e02e256ae3ce Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 5 Mar 2019 17:09:54 -0800 Subject: [PATCH 15/71] Initial implementation (deadlocks still occurring in Audio.cpp) --- interface/resources/qml/hifi/audio/Audio.qml | 19 ++ interface/resources/qml/hifi/audio/MicBar.qml | 9 +- interface/src/Application.cpp | 18 ++ interface/src/Application.h | 2 + interface/src/scripting/Audio.cpp | 174 +++++++++++++++++- interface/src/scripting/Audio.h | 89 ++++++++- scripts/system/audio.js | 3 + 7 files changed, 302 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index c8dd83cd62..45358f59a2 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -148,6 +148,25 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk"); + checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; + onClicked: { + if (isVR) { + AudioScriptingInterface.pushToTalkHMD = checked; + } else { + AudioScriptingInterface.pushToTalkDesktop = checked; + } + checked = Qt.binding(function() { + if (isVR) { + return AudioScriptingInterface.pushToTalkHMD; + } else { + return AudioScriptingInterface.pushToTalkDesktop; + } + }); // restore binding + } + } AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Show audio level meter"); diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 39f75a9182..f91058bc3c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -11,10 +11,13 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 +import stylesUit 1.0 import TabletScriptingInterface 1.0 Rectangle { + HifiConstants { id: hifi; } + readonly property var level: AudioScriptingInterface.inputLevel; property bool gated: false; @@ -131,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.muted; + visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -152,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.muted ? "MUTED" : "MUTE"; + text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b8ab4d10db..fa63757560 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1435,6 +1435,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(this, &Application::activeDisplayPluginChanged, reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); + connect(this, &Application::pushedToTalk, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::handlePushedToTalk); } // Create the rendering engine. This can be slow on some machines due to lots of @@ -4199,6 +4201,10 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + case Qt::Key_T: + emit pushedToTalk(true); + break; + case Qt::Key_P: { if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { AudioInjectorOptions options; @@ -4304,6 +4310,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyReleaseEvent(event); } + + switch (event->key()) { + case Qt::Key_T: + emit pushedToTalk(false); + break; + } } void Application::focusOutEvent(QFocusEvent* event) { @@ -5235,6 +5247,9 @@ void Application::loadSettings() { } } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->loadData(); + getMyAvatar()->loadData(); _settingsLoaded = true; } @@ -5244,6 +5259,9 @@ void Application::saveSettings() const { DependencyManager::get()->saveSettings(); DependencyManager::get()->saveSettings(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->saveData(); + Menu::getInstance()->saveSettings(); getMyAvatar()->saveData(); PluginManager::getInstance()->saveSettings(); diff --git a/interface/src/Application.h b/interface/src/Application.h index a8cc9450c5..1c86326f90 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -358,6 +358,8 @@ signals: void miniTabletEnabledChanged(bool enabled); + void pushedToTalk(bool enabled); + public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 2c4c29ff65..fe04ce47ca 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -63,26 +63,163 @@ void Audio::stopRecording() { } bool Audio::isMuted() const { - return resultWithReadLock([&] { - return _isMuted; - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getMutedHMD(); + } + else { + return getMutedDesktop(); + } } void Audio::setMuted(bool isMuted) { + withWriteLock([&] { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } + else { + setMutedDesktop(isMuted); + } + }); +} + +void Audio::setMutedDesktop(bool isMuted) { + bool changed = false; + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit desktopMutedChanged(isMuted); + } +} + +bool Audio::getMutedDesktop() const { + return resultWithReadLock([&] { + return _desktopMuted; + }); +} + +void Audio::setMutedHMD(bool isMuted) { + bool changed = false; + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit hmdMutedChanged(isMuted); + } +} + +bool Audio::getMutedHMD() const { + return resultWithReadLock([&] { + return _hmdMuted; + }); +} + +bool Audio::getPTT() { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getPTTHMD(); + } + else { + return getPTTDesktop(); + } +} + +bool Audio::getPushingToTalk() const { + return resultWithReadLock([&] { + return _pushingToTalk; + }); +} + +void Audio::setPTT(bool enabled) { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setPTTHMD(enabled); + } + else { + setPTTDesktop(enabled); + } +} + +void Audio::setPTTDesktop(bool enabled) { bool changed = false; withWriteLock([&] { - if (_isMuted != isMuted) { - _isMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + if (_pttDesktop != enabled) { changed = true; + _pttDesktop = enabled; + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } } }); if (changed) { - emit mutedChanged(isMuted); + emit pushToTalkChanged(enabled); + emit pushToTalkDesktopChanged(enabled); } } +bool Audio::getPTTDesktop() const { + return resultWithReadLock([&] { + return _pttDesktop; + }); +} + +void Audio::setPTTHMD(bool enabled) { + bool changed = false; + withWriteLock([&] { + if (_pttHMD != enabled) { + changed = true; + _pttHMD = enabled; + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + } + }); + if (changed) { + emit pushToTalkChanged(enabled); + emit pushToTalkHMDChanged(enabled); + } +} + +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; @@ -179,11 +316,32 @@ void Audio::onContextChanged() { changed = true; } }); + if (isHMD) { + setMuted(getMutedHMD()); + } + else { + setMuted(getMutedDesktop()); + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } } +void Audio::handlePushedToTalk(bool enabled) { + if (getPTT()) { + if (enabled) { + setMuted(false); + } + else { + setMuted(true); + } + if (_pushingToTalk != enabled) { + _pushingToTalk = enabled; + emit pushingToTalkChanged(enabled); + } + } +} + void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index e4dcba9130..6aa589e399 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -12,6 +12,7 @@ #ifndef hifi_scripting_Audio_h #define hifi_scripting_Audio_h +#include #include "AudioScriptingInterface.h" #include "AudioDevices.h" #include "AudioEffectOptions.h" @@ -19,6 +20,9 @@ #include "AudioFileWav.h" #include +using MutedGetter = std::function; +using MutedSetter = std::function; + namespace scripting { class Audio : public AudioScriptingInterface, protected ReadWriteLockable { @@ -63,6 +67,12 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged) + Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged) + Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); + Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) + Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; @@ -82,6 +92,25 @@ public: void showMicMeter(bool show); + // Mute setting setters and getters + void setMutedDesktop(bool isMuted); + bool getMutedDesktop() const; + void setMutedHMD(bool isMuted); + bool getMutedHMD() const; + void setPTT(bool enabled); + bool getPTT(); + bool getPushingToTalk() const; + + // Push-To-Talk setters and getters + void setPTTDesktop(bool enabled); + bool getPTTDesktop() const; + void setPTTHMD(bool enabled); + bool getPTTHMD() const; + + // Settings handlers + void saveData(); + void loadData(); + /**jsdoc * @function Audio.setInputDevice * @param {object} device @@ -193,6 +222,46 @@ signals: */ void mutedChanged(bool isMuted); + /**jsdoc + * Triggered when desktop audio input is muted or unmuted. + * @function Audio.desktopMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. + * @returns {Signal} + */ + void desktopMutedChanged(bool isMuted); + + /**jsdoc + * Triggered when HMD audio input is muted or unmuted. + * @function Audio.hmdMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. + * @returns {Signal} + */ + void hmdMutedChanged(bool isMuted); + + /** + * Triggered when Push-to-Talk has been enabled or disabled. + * @function Audio.pushToTalkChanged + * @param {boolean} enabled - true if Push-to-Talk is enabled, otherwise false. + * @returns {Signal} + */ + void pushToTalkChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for desktop mode. + * @function Audio.pushToTalkDesktopChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for Desktop mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkDesktopChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for HMD mode. + * @function Audio.pushToTalkHMDChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for HMD mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkHMDChanged(bool enabled); + /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged @@ -237,6 +306,14 @@ signals: */ void contextChanged(const QString& context); + /**jsdoc + * Triggered when pushing to talk. + * @function Audio.pushingToTalkChanged + * @param {boolean} talking - true if broadcasting with PTT, false otherwise. + * @returns {Signal} + */ + void pushingToTalkChanged(bool talking); + public slots: /**jsdoc @@ -245,6 +322,8 @@ public slots: */ void onContextChanged(); + void handlePushedToTalk(bool enabled); + private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); @@ -260,11 +339,19 @@ private: float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; bool _isClipping { false }; - bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; + Setting::Handle _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true }; + Setting::Handle _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true }; + Setting::Handle _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false }; + Setting::Handle _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false }; + bool _desktopMuted{ true }; + bool _hmdMuted{ false }; + bool _pttDesktop{ false }; + bool _pttHMD{ false }; + bool _pushingToTalk{ false }; }; }; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index ee82c0c6ea..51d070d8cd 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -28,6 +28,9 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { + if (Audio.pushingToTalk) { + return; + } if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { From 8b4123b5dc7a92bedd7a533a2fd6f16b66a1ade5 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:00:09 -0800 Subject: [PATCH 16/71] laying groundwork for audio app + fixing deadlocks --- interface/resources/qml/hifi/audio/MicBar.qml | 6 +- interface/src/scripting/Audio.cpp | 108 +++++++++--------- interface/src/scripting/Audio.h | 1 + scripts/system/audio.js | 11 +- 4 files changed, 69 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index f91058bc3c..2ab1085408 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,7 +134,7 @@ Rectangle { Item { id: status; - readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index fe04ce47ca..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -66,32 +66,30 @@ bool Audio::isMuted() const { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getMutedHMD(); - } - else { + } else { return getMutedDesktop(); } } void Audio::setMuted(bool isMuted) { - withWriteLock([&] { - bool isHMD = qApp->isHMDMode(); - if (isHMD) { - setMutedHMD(isMuted); - } - else { - setMutedDesktop(isMuted); - } - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } else { + setMutedDesktop(isMuted); + } } void Audio::setMutedDesktop(bool isMuted) { bool changed = false; - if (_desktopMuted != isMuted) { - changed = true; - _desktopMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit desktopMutedChanged(isMuted); @@ -106,12 +104,14 @@ bool Audio::getMutedDesktop() const { void Audio::setMutedHMD(bool isMuted) { bool changed = false; - if (_hmdMuted != isMuted) { - changed = true; - _hmdMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit hmdMutedChanged(isMuted); @@ -128,12 +128,24 @@ bool Audio::getPTT() { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getPTTHMD(); - } - else { + } else { return getPTTDesktop(); } } +void scripting::Audio::setPushingToTalk(bool pushingToTalk) { + bool changed = false; + withWriteLock([&] { + if (_pushingToTalk != pushingToTalk) { + changed = true; + _pushingToTalk = pushingToTalk; + } + }); + if (changed) { + emit pushingToTalkChanged(pushingToTalk); + } +} + bool Audio::getPushingToTalk() const { return resultWithReadLock([&] { return _pushingToTalk; @@ -144,8 +156,7 @@ void Audio::setPTT(bool enabled) { bool isHMD = qApp->isHMDMode(); if (isHMD) { setPTTHMD(enabled); - } - else { + } else { setPTTDesktop(enabled); } } @@ -156,16 +167,16 @@ void Audio::setPTTDesktop(bool enabled) { if (_pttDesktop != enabled) { changed = true; _pttDesktop = enabled; - if (!enabled) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedDesktop(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkDesktopChanged(enabled); @@ -184,16 +195,16 @@ void Audio::setPTTHMD(bool enabled) { if (_pttHMD != enabled) { changed = true; _pttHMD = enabled; - if (!enabled) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedHMD(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkHMDChanged(enabled); @@ -318,8 +329,7 @@ void Audio::onContextChanged() { }); if (isHMD) { setMuted(getMutedHMD()); - } - else { + } else { setMuted(getMutedDesktop()); } if (changed) { @@ -331,14 +341,10 @@ void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { setMuted(false); - } - else { + } else { setMuted(true); } - if (_pushingToTalk != enabled) { - _pushingToTalk = enabled; - emit pushingToTalkChanged(enabled); - } + setPushingToTalk(enabled); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 6aa589e399..94f8a7bf54 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -99,6 +99,7 @@ public: bool getMutedHMD() const; void setPTT(bool enabled); bool getPTT(); + void setPushingToTalk(bool pushingToTalk); bool getPushingToTalk() const; // Push-To-Talk setters and getters diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 51d070d8cd..bf44cfa7cc 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -26,12 +26,15 @@ var UNMUTE_ICONS = { icon: "icons/tablet-icons/mic-unmute-i.svg", activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; +var PTT_ICONS = { + icon: "icons/tablet-icons/mic-unmute-i.svg", + activeIcon: "icons/tablet-icons/mic-unmute-a.svg" +}; function onMuteToggled() { if (Audio.pushingToTalk) { - return; - } - if (Audio.muted) { + button.editProperties(PTT_ICONS); + } else if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -71,6 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); +Audio.pushingToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -79,6 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); + Audio.pushingToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 7f585587ffb3afb0073855150fc5026ab0976aa1 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:11:47 -0800 Subject: [PATCH 17/71] Push to talk changes + rebased with master (#7) Push to talk changes + rebased with master --- interface/resources/qml/hifi/audio/MicBar.qml | 6 +- interface/src/AboutUtil.h | 14 +- interface/src/Application.cpp | 24 +- interface/src/commerce/Wallet.h | 8 +- .../AccountServicesScriptingInterface.cpp | 4 +- .../AccountServicesScriptingInterface.h | 34 +-- interface/src/scripting/Audio.cpp | 128 ++++++---- interface/src/scripting/Audio.h | 1 + .../src/scripting/WalletScriptingInterface.h | 22 +- interface/src/ui/overlays/Overlays.cpp | 239 ++++++------------ interface/src/ui/overlays/Overlays.h | 17 +- .../src/EntityTreeRenderer.cpp | 70 ++--- .../src/EntityTreeRenderer.h | 8 +- libraries/entities/src/EntityTree.cpp | 24 +- libraries/entities/src/EntityTree.h | 7 +- libraries/entities/src/EntityTreeElement.cpp | 4 +- libraries/entities/src/EntityTreeElement.h | 2 +- libraries/entities/src/ModelEntityItem.h | 2 +- libraries/fbx/src/FBXSerializer.cpp | 28 +- libraries/octree/src/Octree.h | 2 +- libraries/octree/src/OctreeProcessor.cpp | 4 +- libraries/octree/src/OctreeProcessor.h | 2 +- libraries/render-utils/src/Model.cpp | 19 +- libraries/script-engine/src/ScriptEngine.cpp | 4 +- scripts/system/audio.js | 11 +- 25 files changed, 331 insertions(+), 353 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index f91058bc3c..2ab1085408 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,7 +134,7 @@ Rectangle { Item { id: status; - readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } diff --git a/interface/src/AboutUtil.h b/interface/src/AboutUtil.h index c06255aaa5..4a5074857d 100644 --- a/interface/src/AboutUtil.h +++ b/interface/src/AboutUtil.h @@ -16,8 +16,8 @@ #include /**jsdoc - * The HifiAbout API provides information about the version of Interface that is currently running. It also - * provides the ability to open a Web page in an Interface browser window. + * The HifiAbout API provides information about the version of Interface that is currently running. It also + * has the functionality to open a web page in an Interface browser window. * * @namespace HifiAbout * @@ -30,9 +30,9 @@ * @property {string} qtVersion - The Qt version used in Interface that is currently running. Read-only. * * @example Report build information for the version of Interface currently running. - * print("HiFi build date: " + HifiAbout.buildDate); // 11 Feb 2019 - * print("HiFi version: " + HifiAbout.buildVersion); // 0.78.0 - * print("Qt version: " + HifiAbout.qtVersion); // 5.10.1 + * print("HiFi build date: " + HifiAbout.buildDate); // Returns the build date of the version of Interface currently running on your machine. + * print("HiFi version: " + HifiAbout.buildVersion); // Returns the build version of Interface currently running on your machine. + * print("Qt version: " + HifiAbout.qtVersion); // Returns the Qt version details of the version of Interface currently running on your machine. */ class AboutUtil : public QObject { @@ -52,9 +52,9 @@ public: public slots: /**jsdoc - * Display a Web page in an Interface browser window. + * Display a web page in an Interface browser window. * @function HifiAbout.openUrl - * @param {string} url - The URL of the Web page to display. + * @param {string} url - The URL of the web page you want to view in Interface. */ void openUrl(const QString &url) const; private: diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bcd5367a89..fa63757560 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1061,6 +1061,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(PluginManager::getInstance().data(), &PluginManager::inputDeviceRunningChanged, controllerScriptingInterface, &controller::ScriptingInterface::updateRunningInputDevices); + EntityTree::setEntityClicksCapturedOperator([this] { + return _controllerScriptingInterface->areEntityClicksCaptured(); + }); + _entityClipboard->createRootElement(); #ifdef Q_OS_WIN @@ -4404,7 +4408,6 @@ void Application::mouseMoveEvent(QMouseEvent* event) { if (compositor.getReticleVisible() || !isHMDMode() || !compositor.getReticleOverDesktop() || getOverlays().getOverlayAtPoint(glm::vec2(transformedPos.x(), transformedPos.y())) != UNKNOWN_ENTITY_ID) { getEntities()->mouseMoveEvent(&mappedEvent); - getOverlays().mouseMoveEvent(&mappedEvent); } _controllerScriptingInterface->emitMouseMoveEvent(&mappedEvent); // send events to any registered scripts @@ -4438,14 +4441,8 @@ void Application::mousePressEvent(QMouseEvent* event) { #endif QMouseEvent mappedEvent(event->type(), transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); - std::pair entityResult; - if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - entityResult = getEntities()->mousePressEvent(&mappedEvent); - } - std::pair overlayResult = getOverlays().mousePressEvent(&mappedEvent); - - QUuid focusedEntity = entityResult.first < overlayResult.first ? entityResult.second : overlayResult.second; - setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(focusedEntity) ? focusedEntity : UNKNOWN_ENTITY_ID); + QUuid result = getEntities()->mousePressEvent(&mappedEvent); + setKeyboardFocusEntity(getEntities()->wantsKeyboardFocus(result) ? result : UNKNOWN_ENTITY_ID); _controllerScriptingInterface->emitMousePressEvent(&mappedEvent); // send events to any registered scripts @@ -4484,11 +4481,7 @@ void Application::mouseDoublePressEvent(QMouseEvent* event) { transformedPos, event->screenPos(), event->button(), event->buttons(), event->modifiers()); - - if (!_controllerScriptingInterface->areEntityClicksCaptured()) { - getEntities()->mouseDoublePressEvent(&mappedEvent); - } - getOverlays().mouseDoublePressEvent(&mappedEvent); + getEntities()->mouseDoublePressEvent(&mappedEvent); // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isMouseCaptured()) { @@ -4513,7 +4506,6 @@ void Application::mouseReleaseEvent(QMouseEvent* event) { event->buttons(), event->modifiers()); getEntities()->mouseReleaseEvent(&mappedEvent); - getOverlays().mouseReleaseEvent(&mappedEvent); _controllerScriptingInterface->emitMouseReleaseEvent(&mappedEvent); // send events to any registered scripts @@ -6969,7 +6961,7 @@ void Application::clearDomainOctreeDetails(bool clearAll) { }); // reset the model renderer - clearAll ? getEntities()->clear() : getEntities()->clearNonLocalEntities(); + clearAll ? getEntities()->clear() : getEntities()->clearDomainAndNonOwnedEntities(); auto skyStage = DependencyManager::get()->getSkyStage(); diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 5e5e6c9b4f..fdd6b5e2a6 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -62,8 +62,8 @@ public: * ValueMeaningDescription * * - * 0Not logged inThe user isn't logged in. - * 1Not set upThe user's wallet isn't set up. + * 0Not logged inThe user is not logged in. + * 1Not set upThe user's wallet has not been set up. * 2Pre-existingThere is a wallet present on the server but not one * locally. * 3ConflictingThere is a wallet present on the server plus one present locally, @@ -73,8 +73,8 @@ public: * 5ReadyThe wallet is ready for use. * * - *

Wallets used to be stored locally but now they're stored on the server, unless the computer once had a wallet stored - * locally in which case the wallet may be present in both places.

+ *

Wallets used to be stored locally but now they're only stored on the server. A wallet is present in both places if + * your computer previously stored its information locally.

* @typedef {number} WalletScriptingInterface.WalletStatus */ enum WalletStatus { diff --git a/interface/src/scripting/AccountServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp index a3597886e9..5f8fb065ff 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -115,8 +115,8 @@ DownloadInfoResult::DownloadInfoResult() : /**jsdoc * Information on the assets currently being downloaded and pending download. * @typedef {object} AccountServices.DownloadInfoResult - * @property {number[]} downloading - The percentage complete for each asset currently being downloaded. - * @property {number} pending - The number of assets waiting to be download. + * @property {number[]} downloading - The download percentage remaining of each asset currently downloading. + * @property {number} pending - The number of assets pending download. */ QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result) { QScriptValue object = engine->newObject(); diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h index c08181d7c9..b188b4e63b 100644 --- a/interface/src/scripting/AccountServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -38,19 +38,19 @@ class AccountServicesScriptingInterface : public QObject { Q_OBJECT /**jsdoc - * The AccountServices API provides functions related to user connectivity, visibility, and asset download - * progress. + * The AccountServices API provides functions that give information on user connectivity, visibility, and + * asset download progress. * * @hifi-interface * @hifi-client-entity * @hifi-avatar * * @namespace AccountServices - * @property {string} username - The user name if the user is logged in, otherwise "Unknown user". - * Read-only. + * @property {string} username - The user name of the user logged in. If there is no user logged in, it is + * "Unknown user". Read-only. * @property {boolean} loggedIn - true if the user is logged in, otherwise false. * Read-only. - * @property {string} findableBy - The user's visibility to other people:
+ * @property {string} findableBy - The user's visibility to other users:
* "none" - user appears offline.
* "friends" - user is visible only to friends.
* "connections" - user is visible to friends and connections.
@@ -74,23 +74,23 @@ public: public slots: /**jsdoc - * Get information on the progress of downloading assets in the domain. + * Gets information on the download progress of assets in the domain. * @function AccountServices.getDownloadInfo - * @returns {AccountServices.DownloadInfoResult} Information on the progress of assets download. + * @returns {AccountServices.DownloadInfoResult} Information on the download progress of assets. */ DownloadInfoResult getDownloadInfo(); /**jsdoc - * Cause a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal to be triggered with information on the - * current progress of the download of assets in the domain. + * Triggers a {@link AccountServices.downloadInfoChanged|downloadInfoChanged} signal with information on the current + * download progress of the assets in the domain. * @function AccountServices.updateDownloadInfo */ void updateDownloadInfo(); /**jsdoc - * Check whether the user is logged in. + * Checks whether the user is logged in. * @function AccountServices.isLoggedIn - * @returns {boolean} true if the user is logged in, false otherwise. + * @returns {boolean} true if the user is logged in, false if not. * @example Report whether you are logged in. * var isLoggedIn = AccountServices.isLoggedIn(); * print("You are logged in: " + isLoggedIn); // true or false @@ -98,9 +98,9 @@ public slots: bool isLoggedIn(); /**jsdoc - * Prompts the user to log in (the login dialog is displayed) if they're not already logged in. + * The function returns the login status of the user and prompts the user to log in (with a login dialog) if they're not already logged in. * @function AccountServices.checkAndSignalForAccessToken - * @returns {boolean} true if the user is already logged in, false otherwise. + * @returns {boolean} true if the user is logged in, false if not. */ bool checkAndSignalForAccessToken(); @@ -140,7 +140,7 @@ signals: /**jsdoc * Triggered when the username logged in with changes, i.e., when the user logs in or out. * @function AccountServices.myUsernameChanged - * @param {string} username - The username logged in with if the user is logged in, otherwise "". + * @param {string} username - The user name of the user logged in. If there is no user logged in, it is "". * @returns {Signal} * @example Report when your username changes. * AccountServices.myUsernameChanged.connect(function (username) { @@ -150,9 +150,9 @@ signals: void myUsernameChanged(const QString& username); /**jsdoc - * Triggered when the progress of the download of assets for the domain changes. + * Triggered when the download progress of the assets in the domain changes. * @function AccountServices.downloadInfoChanged - * @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the progress of assets download. + * @param {AccountServices.DownloadInfoResult} downloadInfo - Information on the download progress of assets. * @returns {Signal} */ void downloadInfoChanged(DownloadInfoResult info); @@ -186,7 +186,7 @@ signals: /**jsdoc * Triggered when the login status of the user changes. * @function AccountServices.loggedInChanged - * @param {boolean} loggedIn - true if the user is logged in, otherwise false. + * @param {boolean} loggedIn - true if the user is logged in, false if not. * @returns {Signal} * @example Report when your login status changes. * AccountServices.loggedInChanged.connect(function(loggedIn) { diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index fe04ce47ca..45bb15f1a3 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -66,32 +66,30 @@ bool Audio::isMuted() const { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getMutedHMD(); - } - else { + } else { return getMutedDesktop(); } } void Audio::setMuted(bool isMuted) { - withWriteLock([&] { - bool isHMD = qApp->isHMDMode(); - if (isHMD) { - setMutedHMD(isMuted); - } - else { - setMutedDesktop(isMuted); - } - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } else { + setMutedDesktop(isMuted); + } } void Audio::setMutedDesktop(bool isMuted) { bool changed = false; - if (_desktopMuted != isMuted) { - changed = true; - _desktopMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit desktopMutedChanged(isMuted); @@ -106,12 +104,14 @@ bool Audio::getMutedDesktop() const { void Audio::setMutedHMD(bool isMuted) { bool changed = false; - if (_hmdMuted != isMuted) { - changed = true; - _hmdMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit hmdMutedChanged(isMuted); @@ -128,12 +128,24 @@ bool Audio::getPTT() { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getPTTHMD(); - } - else { + } else { return getPTTDesktop(); } } +void scripting::Audio::setPushingToTalk(bool pushingToTalk) { + bool changed = false; + withWriteLock([&] { + if (_pushingToTalk != pushingToTalk) { + changed = true; + _pushingToTalk = pushingToTalk; + } + }); + if (changed) { + emit pushingToTalkChanged(pushingToTalk); + } +} + bool Audio::getPushingToTalk() const { return resultWithReadLock([&] { return _pushingToTalk; @@ -144,8 +156,7 @@ void Audio::setPTT(bool enabled) { bool isHMD = qApp->isHMDMode(); if (isHMD) { setPTTHMD(enabled); - } - else { + } else { setPTTDesktop(enabled); } } @@ -156,16 +167,16 @@ void Audio::setPTTDesktop(bool enabled) { if (_pttDesktop != enabled) { changed = true; _pttDesktop = enabled; - if (!enabled) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedDesktop(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkDesktopChanged(enabled); @@ -184,16 +195,16 @@ void Audio::setPTTHMD(bool enabled) { if (_pttHMD != enabled) { changed = true; _pttHMD = enabled; - if (!enabled) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedHMD(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkHMDChanged(enabled); @@ -220,6 +231,26 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; @@ -318,8 +349,7 @@ void Audio::onContextChanged() { }); if (isHMD) { setMuted(getMutedHMD()); - } - else { + } else { setMuted(getMutedDesktop()); } if (changed) { @@ -331,14 +361,10 @@ void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { setMuted(false); - } - else { + } else { setMuted(true); } - if (_pushingToTalk != enabled) { - _pushingToTalk = enabled; - emit pushingToTalkChanged(enabled); - } + setPushingToTalk(enabled); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 6aa589e399..94f8a7bf54 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -99,6 +99,7 @@ public: bool getMutedHMD() const; void setPTT(bool enabled); bool getPTT(); + void setPushingToTalk(bool pushingToTalk); bool getPushingToTalk() const; // Push-To-Talk setters and getters diff --git a/interface/src/scripting/WalletScriptingInterface.h b/interface/src/scripting/WalletScriptingInterface.h index 7482b8be00..3ef9c7953a 100644 --- a/interface/src/scripting/WalletScriptingInterface.h +++ b/interface/src/scripting/WalletScriptingInterface.h @@ -41,8 +41,8 @@ public: * * @property {WalletScriptingInterface.WalletStatus} walletStatus - The status of the user's wallet. Read-only. * @property {boolean} limitedCommerce - true if Interface is running in limited commerce mode. In limited commerce - * mode, certain Interface functionality is disabled, e.g., users can't buy non-free items from the Marketplace. The Oculus - * Store version of Interface runs in limited commerce mode. Read-only. + * mode, certain Interface functionalities are disabled, e.g., users can't buy items that are not free from the Marketplace. + * The Oculus Store version of Interface runs in limited commerce mode. Read-only. */ class WalletScriptingInterface : public QObject, public Dependency { Q_OBJECT @@ -55,16 +55,16 @@ public: WalletScriptingInterface(); /**jsdoc - * Check and update the user's wallet status. + * Checks and updates the user's wallet status. * @function WalletScriptingInterface.refreshWalletStatus */ Q_INVOKABLE void refreshWalletStatus(); /**jsdoc - * Get the current status of the user's wallet. + * Gets the current status of the user's wallet. * @function WalletScriptingInterface.getWalletStatus * @returns {WalletScriptingInterface.WalletStatus} - * @example Two ways to report your wallet status. + * @example Use two methods to report your wallet's status. * print("Wallet status: " + WalletScriptingInterface.walletStatus); // Same value as next line. * print("Wallet status: " + WalletScriptingInterface.getWalletStatus()); */ @@ -74,11 +74,11 @@ public: * Check that a certified avatar entity is owned by the avatar whose entity it is. The result of the check is provided via * the {@link WalletScriptingInterface.ownershipVerificationSuccess|ownershipVerificationSuccess} and * {@link WalletScriptingInterface.ownershipVerificationFailed|ownershipVerificationFailed} signals.
- * Warning: Neither of these signals fire if the entity is not an avatar entity or it's not a certified - * entity. + * Warning: Neither of these signals are triggered if the entity is not an avatar entity or is not + * certified. * @function WalletScriptingInterface.proveAvatarEntityOwnershipVerification - * @param {Uuid} entityID - The ID of the avatar entity to check. - * @example Check ownership of all nearby certified avatar entities. + * @param {Uuid} entityID - The avatar entity's ID. + * @example Check the ownership of all nearby certified avatar entities. * // Set up response handling. * function ownershipSuccess(entityID) { * print("Ownership test succeeded for: " + entityID); @@ -118,7 +118,7 @@ public: signals: /**jsdoc - * Triggered when the status of the user's wallet changes. + * Triggered when the user's wallet status changes. * @function WalletScriptingInterface.walletStatusChanged * @returns {Signal} * @example Report when your wallet status changes, e.g., when you log in and out. @@ -136,7 +136,7 @@ signals: void limitedCommerceChanged(); /**jsdoc - * Triggered when the user rezzes a certified entity but the user's wallet is not ready and so the certified location of the + * Triggered when the user rezzes a certified entity but the user's wallet is not ready. So the certified location of the * entity cannot be updated in the metaverse. * @function WalletScriptingInterface.walletNotSetup * @returns {Signal} diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 5ae3f7d38e..dfd698f6c5 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -43,14 +43,6 @@ std::unordered_map Overlays::_entityToOverlayTypes; std::unordered_map Overlays::_overlayToEntityTypes; Overlays::Overlays() { - auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, this, &Overlays::hoverEnterPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, this, &Overlays::hoverOverPointerEvent); - connect(pointerManager.data(), &PointerManager::hoverEndOverlay, this, &Overlays::hoverLeavePointerEvent); - connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, this, &Overlays::mousePressPointerEvent); - connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, this, &Overlays::mouseMovePointerEvent); - connect(pointerManager.data(), &PointerManager::triggerEndOverlay, this, &Overlays::mouseReleasePointerEvent); - ADD_TYPE_MAP(Box, cube); ADD_TYPE_MAP(Sphere, sphere); _overlayToEntityTypes["rectangle3d"] = "Shape"; @@ -81,13 +73,23 @@ void Overlays::cleanupAllOverlays() { void Overlays::init() { auto entityScriptingInterface = DependencyManager::get().data(); - auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity); - connect(pointerManager.data(), &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); - connect(pointerManager.data(), &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); - connect(pointerManager.data(), &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); - connect(pointerManager.data(), &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); - connect(pointerManager.data(), &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); + auto pointerManager = DependencyManager::get().data(); + connect(pointerManager, &PointerManager::hoverBeginOverlay, entityScriptingInterface , &EntityScriptingInterface::hoverEnterEntity); + connect(pointerManager, &PointerManager::hoverContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(pointerManager, &PointerManager::hoverEndOverlay, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + connect(pointerManager, &PointerManager::triggerBeginOverlay, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(pointerManager, &PointerManager::triggerContinueOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(pointerManager, &PointerManager::triggerEndOverlay, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); + + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &Overlays::mousePressOnPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOffEntity, this, &Overlays::mousePressOffPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOnEntity, this, &Overlays::mouseDoublePressOnPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseDoublePressOffEntity, this, &Overlays::mouseDoublePressOffPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, &Overlays::mouseReleasePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, &Overlays::mouseMovePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity , this, &Overlays::hoverEnterPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, &Overlays::hoverOverPointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, &Overlays::hoverLeavePointerEvent); } void Overlays::update(float deltatime) { @@ -1159,7 +1161,7 @@ bool Overlays::isAddedOverlay(const QUuid& id) { } void Overlays::sendMousePressOnOverlay(const QUuid& id, const PointerEvent& event) { - mousePressPointerEvent(id, event); + mousePressOnPointerEvent(id, event); } void Overlays::sendMouseReleaseOnOverlay(const QUuid& id, const PointerEvent& event) { @@ -1206,57 +1208,66 @@ float Overlays::height() { return offscreenUi->getWindow()->size().height(); } -static uint32_t toPointerButtons(const QMouseEvent& event) { - uint32_t buttons = 0; - buttons |= event.buttons().testFlag(Qt::LeftButton) ? PointerEvent::PrimaryButton : 0; - buttons |= event.buttons().testFlag(Qt::RightButton) ? PointerEvent::SecondaryButton : 0; - buttons |= event.buttons().testFlag(Qt::MiddleButton) ? PointerEvent::TertiaryButton : 0; - return buttons; -} - -static PointerEvent::Button toPointerButton(const QMouseEvent& event) { - switch (event.button()) { - case Qt::LeftButton: - return PointerEvent::PrimaryButton; - case Qt::RightButton: - return PointerEvent::SecondaryButton; - case Qt::MiddleButton: - return PointerEvent::TertiaryButton; - default: - return PointerEvent::NoButtons; - } -} - -RayToOverlayIntersectionResult getPrevPickResult() { - RayToOverlayIntersectionResult overlayResult; - overlayResult.intersects = false; - auto pickResult = DependencyManager::get()->getPrevPickResultTyped(DependencyManager::get()->getMouseRayPickID()); - if (pickResult) { - overlayResult.intersects = pickResult->type == IntersectionType::LOCAL_ENTITY; - if (overlayResult.intersects) { - overlayResult.intersection = pickResult->intersection; - overlayResult.distance = pickResult->distance; - overlayResult.surfaceNormal = pickResult->surfaceNormal; - overlayResult.overlayID = pickResult->objectID; - overlayResult.extraInfo = pickResult->extraInfo; +void Overlays::mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mousePressOnOverlay(id, event); } } - return overlayResult; } -PointerEvent Overlays::calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, - const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, - PointerEvent::EventType eventType) { - glm::vec2 pos2D = RayPick::projectOntoEntityXYPlane(id, rayPickResult.intersection); - return PointerEvent(eventType, PointerManager::MOUSE_POINTER_ID, pos2D, rayPickResult.intersection, rayPickResult.surfaceNormal, - ray.direction, toPointerButton(*event), toPointerButtons(*event), event->modifiers()); +void Overlays::mousePressOffPointerEvent() { + emit mousePressOffOverlay(); +} + +void Overlays::mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseDoublePressOnOverlay(id, event); + } + } +} + +void Overlays::mouseDoublePressOffPointerEvent() { + emit mouseDoublePressOffOverlay(); +} + +void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseReleaseOnOverlay(id, event); + } + } +} + +void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) { + auto keyboard = DependencyManager::get(); + // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed + if (!keyboard->getKeyIDs().contains(id)) { + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit mouseMoveOnOverlay(id, event); + } + } } void Overlays::hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event) { auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverEnterOverlay(id, event); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverEnterOverlay(id, event); + } } } @@ -1264,7 +1275,10 @@ void Overlays::hoverOverPointerEvent(const QUuid& id, const PointerEvent& event) auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverOverOverlay(id, event); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverOverOverlay(id, event); + } } } @@ -1272,113 +1286,10 @@ void Overlays::hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event auto keyboard = DependencyManager::get(); // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed if (!keyboard->getKeyIDs().contains(id)) { - emit hoverLeaveOverlay(id, event); - } -} - -std::pair Overlays::mousePressEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mousePressEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - _currentClickingOnOverlayID = rayPickResult.overlayID; - - PointerEvent pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); - mousePressPointerEvent(_currentClickingOnOverlayID, pointerEvent); - return { rayPickResult.distance, rayPickResult.overlayID }; - } - emit mousePressOffOverlay(); - return { FLT_MAX, UNKNOWN_ENTITY_ID }; -} - -void Overlays::mousePressPointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mousePressOnOverlay(id, event); - } -} - -bool Overlays::mouseDoublePressEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - _currentClickingOnOverlayID = rayPickResult.overlayID; - - auto pointerEvent = calculateOverlayPointerEvent(_currentClickingOnOverlayID, ray, rayPickResult, event, PointerEvent::Press); - emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent); - return true; - } - emit mouseDoublePressOffOverlay(); - return false; -} - -bool Overlays::mouseReleaseEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseReleaseEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Release); - mouseReleasePointerEvent(rayPickResult.overlayID, pointerEvent); - } - - _currentClickingOnOverlayID = UNKNOWN_ENTITY_ID; - return false; -} - -void Overlays::mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mouseReleaseOnOverlay(id, event); - } -} - -bool Overlays::mouseMoveEvent(QMouseEvent* event) { - PerformanceTimer perfTimer("Overlays::mouseMoveEvent"); - - PickRay ray = qApp->computePickRay(event->x(), event->y()); - RayToOverlayIntersectionResult rayPickResult = getPrevPickResult(); - if (rayPickResult.intersects) { - auto pointerEvent = calculateOverlayPointerEvent(rayPickResult.overlayID, ray, rayPickResult, event, PointerEvent::Move); - mouseMovePointerEvent(rayPickResult.overlayID, pointerEvent); - - // If previously hovering over a different overlay then leave hover on that overlay. - if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID && rayPickResult.overlayID != _currentHoverOverOverlayID) { - auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); - hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); + auto entity = DependencyManager::get()->getEntity(id); + if (entity && entity->isLocalEntity()) { + emit hoverLeaveOverlay(id, event); } - - // If hovering over a new overlay then enter hover on that overlay. - if (rayPickResult.overlayID != _currentHoverOverOverlayID) { - hoverEnterPointerEvent(rayPickResult.overlayID, pointerEvent); - } - - // Hover over current overlay. - hoverOverPointerEvent(rayPickResult.overlayID, pointerEvent); - - _currentHoverOverOverlayID = rayPickResult.overlayID; - } else { - // If previously hovering an overlay then leave hover. - if (_currentHoverOverOverlayID != UNKNOWN_ENTITY_ID) { - auto pointerEvent = calculateOverlayPointerEvent(_currentHoverOverOverlayID, ray, rayPickResult, event, PointerEvent::Move); - hoverLeavePointerEvent(_currentHoverOverOverlayID, pointerEvent); - - _currentHoverOverOverlayID = UNKNOWN_ENTITY_ID; - } - } - return false; -} - -void Overlays::mouseMovePointerEvent(const QUuid& id, const PointerEvent& event) { - auto keyboard = DependencyManager::get(); - // Do not send keyboard key event to scripts to prevent malignant scripts from gathering what you typed - if (!keyboard->getKeyIDs().contains(id)) { - emit mouseMoveOnOverlay(id, event); } } diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 93efc2bc0b..0b2994b872 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -112,11 +112,6 @@ public: const QVector& discard, bool visibleOnly = false, bool collidableOnly = false); - std::pair mousePressEvent(QMouseEvent* event); - bool mouseDoublePressEvent(QMouseEvent* event); - bool mouseReleaseEvent(QMouseEvent* event); - bool mouseMoveEvent(QMouseEvent* event); - void cleanupAllOverlays(); mutable QScriptEngine _scriptEngine; @@ -719,9 +714,6 @@ private: PointerEvent calculateOverlayPointerEvent(const QUuid& id, const PickRay& ray, const RayToOverlayIntersectionResult& rayPickResult, QMouseEvent* event, PointerEvent::EventType eventType); - QUuid _currentClickingOnOverlayID; - QUuid _currentHoverOverOverlayID; - static QString entityToOverlayType(const QString& type); static QString overlayToEntityType(const QString& type); static std::unordered_map _entityToOverlayTypes; @@ -732,12 +724,17 @@ private: EntityItemProperties convertOverlayToEntityProperties(QVariantMap& overlayProps, std::pair& rotationToSave, const QString& type, bool add, const QUuid& id = QUuid()); private slots: - void mousePressPointerEvent(const QUuid& id, const PointerEvent& event); - void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event); + void mousePressOnPointerEvent(const QUuid& id, const PointerEvent& event); + void mousePressOffPointerEvent(); + void mouseDoublePressOnPointerEvent(const QUuid& id, const PointerEvent& event); + void mouseDoublePressOffPointerEvent(); void mouseReleasePointerEvent(const QUuid& id, const PointerEvent& event); + void mouseMovePointerEvent(const QUuid& id, const PointerEvent& event); void hoverEnterPointerEvent(const QUuid& id, const PointerEvent& event); void hoverOverPointerEvent(const QUuid& id, const PointerEvent& event); void hoverLeavePointerEvent(const QUuid& id, const PointerEvent& event); + + }; #define ADD_TYPE_MAP(entity, overlay) \ diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e54258fc3e..143c7fa377 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -73,14 +73,14 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf _currentHoverOverEntityID = UNKNOWN_ENTITY_ID; _currentClickingOnEntityID = UNKNOWN_ENTITY_ID; - auto entityScriptingInterface = DependencyManager::get(); + auto entityScriptingInterface = DependencyManager::get().data(); auto pointerManager = DependencyManager::get(); - connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity); - connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity); - connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity); - connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity); - connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity); - connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity); + connect(pointerManager.data(), &PointerManager::hoverBeginEntity, entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity); + connect(pointerManager.data(), &PointerManager::hoverContinueEntity, entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity); + connect(pointerManager.data(), &PointerManager::hoverEndEntity, entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity); + connect(pointerManager.data(), &PointerManager::triggerBeginEntity, entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity); + connect(pointerManager.data(), &PointerManager::triggerContinueEntity, entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity); + connect(pointerManager.data(), &PointerManager::triggerEndEntity, entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity); // Forward mouse events to web entities auto handlePointerEvent = [&](const QUuid& entityID, const PointerEvent& event) { @@ -93,10 +93,10 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf QMetaObject::invokeMethod(thisEntity.get(), "handlePointerEvent", Q_ARG(const PointerEvent&, event)); } }; - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { + connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseMoveOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::mouseReleaseOnEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverEnterEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) { @@ -106,8 +106,8 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf QMetaObject::invokeMethod(thisEntity.get(), "hoverEnterEntity", Q_ARG(const PointerEvent&, event)); } }); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); - connect(entityScriptingInterface.data(), &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { + connect(entityScriptingInterface, &EntityScriptingInterface::hoverOverEntity, this, handlePointerEvent); + connect(entityScriptingInterface, &EntityScriptingInterface::hoverLeaveEntity, this, [&](const QUuid& entityID, const PointerEvent& event) { std::shared_ptr thisEntity; auto entity = getEntity(entityID); if (entity && entity->isVisible() && entity->getType() == EntityTypes::Web) { @@ -196,8 +196,8 @@ void EntityTreeRenderer::resetEntitiesScriptEngine() { }); } -void EntityTreeRenderer::stopNonLocalEntityScripts() { - leaveNonLocalEntities(); +void EntityTreeRenderer::stopDomainAndNonOwnedEntities() { + leaveDomainAndNonOwnedEntities(); // unload and stop the engine if (_entitiesScriptEngine) { QList entitiesWithEntityScripts = _entitiesScriptEngine->getListOfEntityScriptIDs(); @@ -206,7 +206,7 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); if (entityItem) { - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { _entitiesScriptEngine->unloadEntityScript(entityID, true); } } @@ -214,8 +214,8 @@ void EntityTreeRenderer::stopNonLocalEntityScripts() { } } -void EntityTreeRenderer::clearNonLocalEntities() { - stopNonLocalEntityScripts(); +void EntityTreeRenderer::clearDomainAndNonOwnedEntities() { + stopDomainAndNonOwnedEntities(); std::unordered_map savedEntities; // remove all entities from the scene @@ -225,7 +225,7 @@ void EntityTreeRenderer::clearNonLocalEntities() { for (const auto& entry : _entitiesInScene) { const auto& renderer = entry.second; const EntityItemPointer& entityItem = renderer->getEntity(); - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { renderer->removeFromScene(scene, transaction); } else { savedEntities[entry.first] = entry.second; @@ -239,7 +239,7 @@ void EntityTreeRenderer::clearNonLocalEntities() { _layeredZones.clearNonLocalLayeredZones(); - OctreeProcessor::clearNonLocalEntities(); + OctreeProcessor::clearDomainAndNonOwnedEntities(); } void EntityTreeRenderer::clear() { @@ -655,22 +655,22 @@ bool EntityTreeRenderer::checkEnterLeaveEntities() { return didUpdate; } -void EntityTreeRenderer::leaveNonLocalEntities() { +void EntityTreeRenderer::leaveDomainAndNonOwnedEntities() { if (_tree && !_shuttingDown) { - QVector currentLocalEntitiesInside; + QVector currentEntitiesInsideToSave; foreach (const EntityItemID& entityID, _currentEntitiesInside) { EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); - if (!entityItem->isLocalEntity()) { + if (!(entityItem->isLocalEntity() || (entityItem->isAvatarEntity() && entityItem->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { emit leaveEntity(entityID); if (_entitiesScriptEngine) { _entitiesScriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); } } else { - currentLocalEntitiesInside.push_back(entityID); + currentEntitiesInsideToSave.push_back(entityID); } } - _currentEntitiesInside = currentLocalEntitiesInside; + _currentEntitiesInside = currentEntitiesInsideToSave; forceRecheckEntities(); } } @@ -792,11 +792,11 @@ static PointerEvent::Button toPointerButton(const QMouseEvent& event) { } } -std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { +QUuid EntityTreeRenderer::mousePressEvent(QMouseEvent* event) { // If we don't have a tree, or we're in the process of shutting down, then don't // process these events. if (!_tree || _shuttingDown) { - return { FLT_MAX, UNKNOWN_ENTITY_ID }; + return UNKNOWN_ENTITY_ID; } PerformanceTimer perfTimer("EntityTreeRenderer::mousePressEvent"); @@ -805,11 +805,13 @@ std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) RayToEntityIntersectionResult rayPickResult = _getPrevRayPickResultOperator(_mouseRayPickID); EntityItemPointer entity; if (rayPickResult.intersects && (entity = getTree()->findEntityByID(rayPickResult.entityID))) { - auto properties = entity->getProperties(); - QString urlString = properties.getHref(); - QUrl url = QUrl(urlString, QUrl::StrictMode); - if (url.isValid() && !url.isEmpty()){ - DependencyManager::get()->handleLookupString(urlString); + if (!EntityTree::areEntityClicksCaptured()) { + auto properties = entity->getProperties(); + QString urlString = properties.getHref(); + QUrl url = QUrl(urlString, QUrl::StrictMode); + if (url.isValid() && !url.isEmpty()) { + DependencyManager::get()->handleLookupString(urlString); + } } glm::vec2 pos2D = projectOntoEntityXYPlane(entity, ray, rayPickResult); @@ -827,10 +829,10 @@ std::pair EntityTreeRenderer::mousePressEvent(QMouseEvent* event) _lastPointerEvent = pointerEvent; _lastPointerEventValid = true; - return { rayPickResult.distance, rayPickResult.entityID }; + return rayPickResult.entityID; } emit entityScriptingInterface->mousePressOffEntity(); - return { FLT_MAX, UNKNOWN_ENTITY_ID }; + return UNKNOWN_ENTITY_ID; } void EntityTreeRenderer::mouseDoublePressEvent(QMouseEvent* event) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 51568ab744..a257951ba8 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -87,14 +87,14 @@ public: virtual void init() override; /// clears the tree - virtual void clearNonLocalEntities() override; + virtual void clearDomainAndNonOwnedEntities() override; virtual void clear() override; /// reloads the entity scripts, calling unload and preload void reloadEntityScripts(); // event handles which may generate entity related events - std::pair mousePressEvent(QMouseEvent* event); + QUuid mousePressEvent(QMouseEvent* event); void mouseReleaseEvent(QMouseEvent* event); void mouseDoublePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); @@ -170,7 +170,7 @@ private: bool findBestZoneAndMaybeContainingEntities(QVector* entitiesContainingAvatar = nullptr); bool applyLayeredZones(); - void stopNonLocalEntityScripts(); + void stopDomainAndNonOwnedEntities(); void checkAndCallPreload(const EntityItemID& entityID, bool reload = false, bool unloadFirst = false); @@ -179,7 +179,7 @@ private: QScriptValueList createEntityArgs(const EntityItemID& entityID); bool checkEnterLeaveEntities(); - void leaveNonLocalEntities(); + void leaveDomainAndNonOwnedEntities(); void leaveAllEntities(); void forceRecheckEntities(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 6e404ce690..d4f15fa8b2 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -78,13 +78,14 @@ OctreeElementPointer EntityTree::createNewElement(unsigned char* octalCode) { return std::static_pointer_cast(newElement); } -void EntityTree::eraseNonLocalEntities() { +void EntityTree::eraseDomainAndNonOwnedEntities() { emit clearingEntities(); if (_simulation) { // local entities are not in the simulation, so we clear ALL _simulation->clearEntities(); } + this->withWriteLock([&] { QHash savedEntities; // NOTE: lock the Tree first, then lock the _entityMap. @@ -93,10 +94,10 @@ void EntityTree::eraseNonLocalEntities() { foreach(EntityItemPointer entity, _entityMap) { EntityTreeElementPointer element = entity->getElement(); if (element) { - element->cleanupNonLocalEntities(); + element->cleanupDomainAndNonOwnedEntities(); } - if (entity->isLocalEntity()) { + if (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID())) { savedEntities[entity->getEntityItemID()] = entity; } else { int32_t spaceIndex = entity->getSpaceIndex(); @@ -114,15 +115,16 @@ void EntityTree::eraseNonLocalEntities() { { QWriteLocker locker(&_needsParentFixupLock); - QVector localEntitiesNeedsParentFixup; + QVector needParentFixup; foreach (EntityItemWeakPointer entityItem, _needsParentFixup) { - if (!entityItem.expired() && entityItem.lock()->isLocalEntity()) { - localEntitiesNeedsParentFixup.push_back(entityItem); + auto entity = entityItem.lock(); + if (entity && (entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getMyAvatarSessionUUID()))) { + needParentFixup.push_back(entityItem); } } - _needsParentFixup = localEntitiesNeedsParentFixup; + _needsParentFixup = needParentFixup; } } @@ -2972,6 +2974,7 @@ QStringList EntityTree::getJointNames(const QUuid& entityID) const { std::function EntityTree::_getEntityObjectOperator = nullptr; std::function EntityTree::_textSizeOperator = nullptr; +std::function EntityTree::_areEntityClicksCapturedOperator = nullptr; QObject* EntityTree::getEntityObject(const QUuid& id) { if (_getEntityObjectOperator) { @@ -2987,6 +2990,13 @@ QSizeF EntityTree::textSize(const QUuid& id, const QString& text) { return QSizeF(0.0f, 0.0f); } +bool EntityTree::areEntityClicksCaptured() { + if (_areEntityClicksCapturedOperator) { + return _areEntityClicksCapturedOperator(); + } + return false; +} + void EntityTree::updateEntityQueryAACubeWorker(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, MovingEntitiesOperator& moveOperator, bool force, bool tellServer) { // if the queryBox has changed, tell the entity-server diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index dcce0e4b99..39b3dc57c7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -75,7 +75,7 @@ public: } - virtual void eraseNonLocalEntities() override; + virtual void eraseDomainAndNonOwnedEntities() override; virtual void eraseAllOctreeElements(bool createNewRoot = true) override; virtual void readBitstreamToTree(const unsigned char* bitstream, @@ -255,6 +255,7 @@ public: QByteArray computeNonce(const QString& certID, const QString ownerKey); bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); + QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); } void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } void swapStaleProxies(std::vector& proxies) { proxies.swap(_staleProxies); } @@ -268,6 +269,9 @@ public: static void setTextSizeOperator(std::function textSizeOperator) { _textSizeOperator = textSizeOperator; } static QSizeF textSize(const QUuid& id, const QString& text); + static void setEntityClicksCapturedOperator(std::function areEntityClicksCapturedOperator) { _areEntityClicksCapturedOperator = areEntityClicksCapturedOperator; } + static bool areEntityClicksCaptured(); + std::map getNamedPaths() const { return _namedPaths; } void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender, @@ -378,6 +382,7 @@ private: static std::function _getEntityObjectOperator; static std::function _textSizeOperator; + static std::function _areEntityClicksCapturedOperator; std::vector _staleProxies; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index ce6f20262f..aab98adb52 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -697,11 +697,11 @@ EntityItemPointer EntityTreeElement::getEntityWithEntityItemID(const EntityItemI return foundEntity; } -void EntityTreeElement::cleanupNonLocalEntities() { +void EntityTreeElement::cleanupDomainAndNonOwnedEntities() { withWriteLock([&] { EntityItems savedEntities; foreach(EntityItemPointer entity, _entityItems) { - if (!entity->isLocalEntity()) { + if (!(entity->isLocalEntity() || (entity->isAvatarEntity() && entity->getOwningAvatarID() == getTree()->getMyAvatarSessionUUID()))) { entity->preDelete(); entity->_element = NULL; } else { diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index f82eaa7fb1..f94da44138 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -190,7 +190,7 @@ public: EntityItemPointer getEntityWithEntityItemID(const EntityItemID& id) const; void getEntitiesInside(const AACube& box, QVector& foundEntities); - void cleanupNonLocalEntities(); + void cleanupDomainAndNonOwnedEntities(); void cleanupEntities(); /// called by EntityTree on cleanup this will free all entities bool removeEntityItem(EntityItemPointer entity, bool deletion = false); diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 0efbbbb16c..08468617ba 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -164,7 +164,7 @@ protected: int _lastKnownCurrentFrame{-1}; glm::u8vec3 _color; - glm::vec3 _modelScale; + glm::vec3 _modelScale { 1.0f }; QString _modelURL; bool _relayParentJoints; bool _groupCulled { false }; diff --git a/libraries/fbx/src/FBXSerializer.cpp b/libraries/fbx/src/FBXSerializer.cpp index 9e7f422b40..5246242a1e 100644 --- a/libraries/fbx/src/FBXSerializer.cpp +++ b/libraries/fbx/src/FBXSerializer.cpp @@ -167,7 +167,6 @@ glm::mat4 getGlobalTransform(const QMultiMap& _connectionParen } } } - return globalTransform; } @@ -436,6 +435,8 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr hfmModel.originalURL = url; float unitScaleFactor = 1.0f; + glm::quat upAxisZRotation; + bool applyUpAxisZRotation = false; glm::vec3 ambientColor; QString hifiGlobalNodeID; unsigned int meshIndex = 0; @@ -473,11 +474,22 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr if (subobject.name == propertyName) { static const QVariant UNIT_SCALE_FACTOR = QByteArray("UnitScaleFactor"); static const QVariant AMBIENT_COLOR = QByteArray("AmbientColor"); + static const QVariant UP_AXIS = QByteArray("UpAxis"); const auto& subpropName = subobject.properties.at(0); if (subpropName == UNIT_SCALE_FACTOR) { unitScaleFactor = subobject.properties.at(index).toFloat(); } else if (subpropName == AMBIENT_COLOR) { ambientColor = getVec3(subobject.properties, index); + } else if (subpropName == UP_AXIS) { + constexpr int UP_AXIS_Y = 1; + constexpr int UP_AXIS_Z = 2; + int upAxis = subobject.properties.at(index).toInt(); + if (upAxis == UP_AXIS_Y) { + // No update necessary, y up is the default + } else if (upAxis == UP_AXIS_Z) { + upAxisZRotation = glm::angleAxis(glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)); + applyUpAxisZRotation = true; + } } } } @@ -1269,9 +1281,11 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr joint.geometricScaling = fbxModel.geometricScaling; joint.isSkeletonJoint = fbxModel.isLimbNode; hfmModel.hasSkeletonJoints = (hfmModel.hasSkeletonJoints || joint.isSkeletonJoint); - + if (applyUpAxisZRotation && joint.parentIndex == -1) { + joint.rotation *= upAxisZRotation; + joint.translation = upAxisZRotation * joint.translation; + } glm::quat combinedRotation = joint.preRotation * joint.rotation * joint.postRotation; - if (joint.parentIndex == -1) { joint.transform = hfmModel.offset * glm::translate(joint.translation) * joint.preTransform * glm::mat4_cast(combinedRotation) * joint.postTransform; @@ -1664,6 +1678,14 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr } } + if (applyUpAxisZRotation) { + hfmModelPtr->meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + hfmModelPtr->bindExtents.transform(glm::mat4_cast(upAxisZRotation)); + for (auto &mesh : hfmModelPtr->meshes) { + mesh.modelTransform *= glm::mat4_cast(upAxisZRotation); + mesh.meshExtents.transform(glm::mat4_cast(upAxisZRotation)); + } + } return hfmModelPtr; } diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index aac29201f1..82076f618b 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -149,7 +149,7 @@ public: OctreeElementPointer getRoot() { return _rootElement; } - virtual void eraseNonLocalEntities() { _isDirty = true; }; + virtual void eraseDomainAndNonOwnedEntities() { _isDirty = true; }; virtual void eraseAllOctreeElements(bool createNewRoot = true); virtual void readBitstreamToTree(const unsigned char* bitstream, uint64_t bufferSizeBytes, ReadBitstreamToTreeParams& args); diff --git a/libraries/octree/src/OctreeProcessor.cpp b/libraries/octree/src/OctreeProcessor.cpp index 18c8630391..03c8b9ca2f 100644 --- a/libraries/octree/src/OctreeProcessor.cpp +++ b/libraries/octree/src/OctreeProcessor.cpp @@ -198,10 +198,10 @@ void OctreeProcessor::processDatagram(ReceivedMessage& message, SharedNodePointe } -void OctreeProcessor::clearNonLocalEntities() { +void OctreeProcessor::clearDomainAndNonOwnedEntities() { if (_tree) { _tree->withWriteLock([&] { - _tree->eraseNonLocalEntities(); + _tree->eraseDomainAndNonOwnedEntities(); }); } } diff --git a/libraries/octree/src/OctreeProcessor.h b/libraries/octree/src/OctreeProcessor.h index bc5618e657..40af7a39f8 100644 --- a/libraries/octree/src/OctreeProcessor.h +++ b/libraries/octree/src/OctreeProcessor.h @@ -43,7 +43,7 @@ public: virtual void init(); /// clears the tree - virtual void clearNonLocalEntities(); + virtual void clearDomainAndNonOwnedEntities(); virtual void clear(); float getAverageElementsPerPacket() const { return _elementsPerPacket.getAverage(); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9bb3f72b31..dd9b0280ca 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -1346,14 +1346,19 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { } void Model::computeMeshPartLocalBounds() { - for (auto& part : _modelMeshRenderItems) { - const Model::MeshState& state = _meshStates.at(part->_meshIndex); - if (_useDualQuaternionSkinning) { - part->computeAdjustedLocalBound(state.clusterDualQuaternions); - } else { - part->computeAdjustedLocalBound(state.clusterMatrices); - } + render::Transaction transaction; + auto meshStates = _meshStates; + for (auto renderItem : _modelMeshRenderItemIDs) { + transaction.updateItem(renderItem, [this, meshStates](ModelMeshPartPayload& data) { + const Model::MeshState& state = meshStates.at(data._meshIndex); + if (_useDualQuaternionSkinning) { + data.computeAdjustedLocalBound(state.clusterDualQuaternions); + } else { + data.computeAdjustedLocalBound(state.clusterMatrices); + } + }); } + AbstractViewStateInterface::instance()->getMain3DScene()->enqueueTransaction(transaction); } // virtual diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 5d33a6a061..825017b1fe 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -976,7 +976,9 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString& using PointerHandler = std::function; auto makePointerHandler = [this](QString eventName) -> PointerHandler { return [this, eventName](const EntityItemID& entityItemID, const PointerEvent& event) { - forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); + if (!EntityTree::areEntityClicksCaptured()) { + forwardHandlerCall(entityItemID, eventName, { entityItemID.toScriptValue(this), event.toScriptValue(this) }); + } }; }; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 51d070d8cd..bf44cfa7cc 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -26,12 +26,15 @@ var UNMUTE_ICONS = { icon: "icons/tablet-icons/mic-unmute-i.svg", activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; +var PTT_ICONS = { + icon: "icons/tablet-icons/mic-unmute-i.svg", + activeIcon: "icons/tablet-icons/mic-unmute-a.svg" +}; function onMuteToggled() { if (Audio.pushingToTalk) { - return; - } - if (Audio.muted) { + button.editProperties(PTT_ICONS); + } else if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -71,6 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); +Audio.pushingToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -79,6 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); + Audio.pushingToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 4467567ee6b6dd478f3baef76dc8e089b760fba8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:18:57 -0800 Subject: [PATCH 18/71] changing text display --- interface/resources/qml/hifi/audio/MicBar.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 2ab1085408..9d1cbfbc6c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; + visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -155,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } From a318b715c3c12ab66cf8c977479c7b6ae1aaadde Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:45:43 -0800 Subject: [PATCH 19/71] exposing setting pushingToTalk --- interface/src/scripting/Audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 94f8a7bf54..9ad4aac9c1 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -72,7 +72,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) - Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; From e218e4bead36d28fa0e4d7de630f7acd72ec1851 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 17:29:32 -0800 Subject: [PATCH 20/71] updating compile failure + icons/settings update --- .../icons/tablet-icons/mic-ptt-a.svg | 1 + .../icons/tablet-icons/mic-ptt-i.svg | 24 +++++++ interface/resources/qml/hifi/audio/Audio.qml | 64 ++++++++++++++++--- interface/resources/qml/hifi/audio/MicBar.qml | 8 ++- interface/src/scripting/Audio.cpp | 20 ------ scripts/system/audio.js | 14 ++-- 6 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-a.svg create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-i.svg diff --git a/interface/resources/icons/tablet-icons/mic-ptt-a.svg b/interface/resources/icons/tablet-icons/mic-ptt-a.svg new file mode 100644 index 0000000000..e6df3c69d7 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-a.svg @@ -0,0 +1 @@ +mic-ptt-a \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/mic-ptt-i.svg b/interface/resources/icons/tablet-icons/mic-ptt-i.svg new file mode 100644 index 0000000000..2141ea5229 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-i.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 45358f59a2..d44a9c862e 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -120,6 +120,10 @@ Rectangle { isRedCheck: true; checked: AudioScriptingInterface.muted; onClicked: { + if (AudioScriptingInterface.pushToTalk && !checked) { + // disable push to talk if unmuting + AudioScriptingInterface.pushToTalk = false; + } AudioScriptingInterface.muted = checked; checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding } @@ -150,7 +154,23 @@ Rectangle { } AudioControls.CheckBox { spacing: muteMic.spacing - text: qsTr("Push To Talk"); + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + onXChanged: rightMostInputLevelPos = x + width + } + } + + Separator {} + + ColumnLayout { + spacing: muteMic.spacing; + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; onClicked: { if (isVR) { @@ -167,15 +187,41 @@ Rectangle { }); // restore binding } } - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Show audio level meter"); - checked: AvatarInputs.showAudioTools; - onClicked: { - AvatarInputs.showAudioTools = checked; - checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + Item { + id: pttTextContainer + x: margins.paddings; + width: rightMostInputLevelPos + height: pttTextMetrics.height + visible: true + TextMetrics { + id: pttTextMetrics + text: pttText.text + font: pttText.font + } + RalewayRegular { + id: pttText + wrapMode: Text.WordWrap + color: hifi.colors.white; + width: parent.width; + font.italic: true + size: 16; + text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : + qsTr("Press and hold the button \"T\" to unmute."); + onTextChanged: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } + } + } + Component.onCompleted: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } } - onXChanged: rightMostInputLevelPos = x + width } } } diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 9d1cbfbc6c..50477b82f8 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -67,6 +67,9 @@ Rectangle { hoverEnabled: true; scrollGestureEnabled: false; onClicked: { + if (AudioScriptingInterface.pushToTalk) { + return; + } AudioScriptingInterface.muted = !AudioScriptingInterface.muted; Tablet.playSound(TabletEnums.ButtonClick); } @@ -109,9 +112,10 @@ Rectangle { Image { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; + readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg"; id: image; - source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; + source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -155,7 +159,7 @@ Rectangle { color: parent.color; - text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED PTT-(T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 45bb15f1a3..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,26 +231,6 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } -bool Audio::getPTTHMD() const { - return resultWithReadLock([&] { - return _pttHMD; - }); -} - -void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); - _pttDesktopSetting.set(getPTTDesktop()); - _pttHMDSetting.set(getPTTHMD()); -} - -void Audio::loadData() { - _desktopMuted = _desktopMutedSetting.get(); - _hmdMuted = _hmdMutedSetting.get(); - _pttDesktop = _pttDesktopSetting.get(); - _pttHMD = _pttHMDSetting.get(); -} - bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index bf44cfa7cc..19ed3faef2 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -27,12 +27,12 @@ var UNMUTE_ICONS = { activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; var PTT_ICONS = { - icon: "icons/tablet-icons/mic-unmute-i.svg", - activeIcon: "icons/tablet-icons/mic-unmute-a.svg" + icon: "icons/tablet-icons/mic-ptt-i.svg", + activeIcon: "icons/tablet-icons/mic-ptt-a.svg" }; function onMuteToggled() { - if (Audio.pushingToTalk) { + if (Audio.pushToTalk) { button.editProperties(PTT_ICONS); } else if (Audio.muted) { button.editProperties(MUTE_ICONS); @@ -63,8 +63,8 @@ function onScreenChanged(type, url) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, - activeIcon: Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, + icon: Audio.pushToTalk ? PTT_ICONS.icon : Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: Audio.pushToTalk ? PTT_ICONS.activeIcon : Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -74,7 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); -Audio.pushingToTalkChanged.connect(onMuteToggled); +Audio.pushToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -83,7 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); - Audio.pushingToTalkChanged.disconnect(onMuteToggled); + Audio.pushToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 36f62f05d1b8d96199419aacab89c204bee6a58d Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:28 -0800 Subject: [PATCH 21/71] Add pushToTalk.js controllerModule. --- .../controllerModules/pushToTalk.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/system/controllers/controllerModules/pushToTalk.js diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js new file mode 100644 index 0000000000..e764b228c9 --- /dev/null +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -0,0 +1,75 @@ +"use strict"; + +// Created by Jason C. Najera on 3/7/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Handles Push-to-Talk functionality for HMD mode. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { // BEGIN LOCAL_SCOPE + function PushToTalkHandler() { + var _this = this; + this.active = false; + //var pttMapping, mappingName; + + this.setup = function() { + //mappingName = 'Hifi-PTT-Dev-' + Math.random(); + //pttMapping = Controller.newMapping(mappingName); + //pttMapping.enable(); + }; + + this.shouldTalk = function (controllerData) { + // Set up test against controllerData here... + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? true : false; + }; + + this.shouldStopTalking = function (controllerData) { + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? false : true; + }; + + this.isReady = function (controllerData, deltaTime) { + if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + Audio.pushingToTalk = true; + returnMakeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + if (this.shouldStopTalking(controllerData) || !Audio.pushToTalk) { + Audio.pushingToTalk = false; + return makeRunningValues(false, [], []); + } + + return makeRunningValues(true, [], []); + }; + + this.cleanup = function () { + //pttMapping.disable(); + }; + + this.parameters = makeDispatcherModuleParameters( + 950, + ["head"], + [], + 100); + } + + var pushToTalk = new PushToTalkHandler(); + enableDispatcherModule("PushToTalk", pushToTalk); + + function cleanup () { + pushToTalk.cleanup(); + disableDispatcherModule("PushToTalk"); + }; + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE \ No newline at end of file From f444f383eba17e47a39032411ce08b33d6df2b57 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:48 -0800 Subject: [PATCH 22/71] Enable pushToTalk.js controller module. --- scripts/system/controllers/controllerDispatcher.js | 1 + scripts/system/controllers/controllerScripts.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 28c3e2a299..f4c0cbb0d6 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -58,6 +58,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name // is stored as the value, rather than false. this.activitySlots = { + head: false, leftHand: false, rightHand: false, rightHandTrigger: false, diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 86ff7701c3..d236d6b12a 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -34,7 +34,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearGrabHyperLinkEntity.js", "controllerModules/nearTabletHighlight.js", "controllerModules/nearGrabEntity.js", - "controllerModules/farGrabEntity.js" + "controllerModules/farGrabEntity.js", + "controllerModules/pushToTalk.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From d8478e0b17d56500ff1fd4debdd322fbeec77a4c Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 5 Mar 2019 17:09:54 -0800 Subject: [PATCH 23/71] Initial implementation (deadlocks still occurring in Audio.cpp) --- interface/resources/qml/hifi/audio/Audio.qml | 19 ++ interface/resources/qml/hifi/audio/MicBar.qml | 9 +- interface/src/Application.cpp | 18 ++ interface/src/Application.h | 2 + interface/src/scripting/Audio.cpp | 174 +++++++++++++++++- interface/src/scripting/Audio.h | 89 ++++++++- scripts/system/audio.js | 3 + 7 files changed, 302 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index c8dd83cd62..45358f59a2 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -148,6 +148,25 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk"); + checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; + onClicked: { + if (isVR) { + AudioScriptingInterface.pushToTalkHMD = checked; + } else { + AudioScriptingInterface.pushToTalkDesktop = checked; + } + checked = Qt.binding(function() { + if (isVR) { + return AudioScriptingInterface.pushToTalkHMD; + } else { + return AudioScriptingInterface.pushToTalkDesktop; + } + }); // restore binding + } + } AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Show audio level meter"); diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 39f75a9182..f91058bc3c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -11,10 +11,13 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 +import stylesUit 1.0 import TabletScriptingInterface 1.0 Rectangle { + HifiConstants { id: hifi; } + readonly property var level: AudioScriptingInterface.inputLevel; property bool gated: false; @@ -131,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.muted; + visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -152,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.muted ? "MUTED" : "MUTE"; + text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b8ab4d10db..fa63757560 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1435,6 +1435,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(this, &Application::activeDisplayPluginChanged, reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); + connect(this, &Application::pushedToTalk, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::handlePushedToTalk); } // Create the rendering engine. This can be slow on some machines due to lots of @@ -4199,6 +4201,10 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + case Qt::Key_T: + emit pushedToTalk(true); + break; + case Qt::Key_P: { if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { AudioInjectorOptions options; @@ -4304,6 +4310,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyReleaseEvent(event); } + + switch (event->key()) { + case Qt::Key_T: + emit pushedToTalk(false); + break; + } } void Application::focusOutEvent(QFocusEvent* event) { @@ -5235,6 +5247,9 @@ void Application::loadSettings() { } } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->loadData(); + getMyAvatar()->loadData(); _settingsLoaded = true; } @@ -5244,6 +5259,9 @@ void Application::saveSettings() const { DependencyManager::get()->saveSettings(); DependencyManager::get()->saveSettings(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->saveData(); + Menu::getInstance()->saveSettings(); getMyAvatar()->saveData(); PluginManager::getInstance()->saveSettings(); diff --git a/interface/src/Application.h b/interface/src/Application.h index a8cc9450c5..1c86326f90 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -358,6 +358,8 @@ signals: void miniTabletEnabledChanged(bool enabled); + void pushedToTalk(bool enabled); + public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 2c4c29ff65..fe04ce47ca 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -63,26 +63,163 @@ void Audio::stopRecording() { } bool Audio::isMuted() const { - return resultWithReadLock([&] { - return _isMuted; - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getMutedHMD(); + } + else { + return getMutedDesktop(); + } } void Audio::setMuted(bool isMuted) { + withWriteLock([&] { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } + else { + setMutedDesktop(isMuted); + } + }); +} + +void Audio::setMutedDesktop(bool isMuted) { + bool changed = false; + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit desktopMutedChanged(isMuted); + } +} + +bool Audio::getMutedDesktop() const { + return resultWithReadLock([&] { + return _desktopMuted; + }); +} + +void Audio::setMutedHMD(bool isMuted) { + bool changed = false; + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit hmdMutedChanged(isMuted); + } +} + +bool Audio::getMutedHMD() const { + return resultWithReadLock([&] { + return _hmdMuted; + }); +} + +bool Audio::getPTT() { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getPTTHMD(); + } + else { + return getPTTDesktop(); + } +} + +bool Audio::getPushingToTalk() const { + return resultWithReadLock([&] { + return _pushingToTalk; + }); +} + +void Audio::setPTT(bool enabled) { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setPTTHMD(enabled); + } + else { + setPTTDesktop(enabled); + } +} + +void Audio::setPTTDesktop(bool enabled) { bool changed = false; withWriteLock([&] { - if (_isMuted != isMuted) { - _isMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + if (_pttDesktop != enabled) { changed = true; + _pttDesktop = enabled; + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } } }); if (changed) { - emit mutedChanged(isMuted); + emit pushToTalkChanged(enabled); + emit pushToTalkDesktopChanged(enabled); } } +bool Audio::getPTTDesktop() const { + return resultWithReadLock([&] { + return _pttDesktop; + }); +} + +void Audio::setPTTHMD(bool enabled) { + bool changed = false; + withWriteLock([&] { + if (_pttHMD != enabled) { + changed = true; + _pttHMD = enabled; + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + } + }); + if (changed) { + emit pushToTalkChanged(enabled); + emit pushToTalkHMDChanged(enabled); + } +} + +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; @@ -179,11 +316,32 @@ void Audio::onContextChanged() { changed = true; } }); + if (isHMD) { + setMuted(getMutedHMD()); + } + else { + setMuted(getMutedDesktop()); + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } } +void Audio::handlePushedToTalk(bool enabled) { + if (getPTT()) { + if (enabled) { + setMuted(false); + } + else { + setMuted(true); + } + if (_pushingToTalk != enabled) { + _pushingToTalk = enabled; + emit pushingToTalkChanged(enabled); + } + } +} + void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index e4dcba9130..6aa589e399 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -12,6 +12,7 @@ #ifndef hifi_scripting_Audio_h #define hifi_scripting_Audio_h +#include #include "AudioScriptingInterface.h" #include "AudioDevices.h" #include "AudioEffectOptions.h" @@ -19,6 +20,9 @@ #include "AudioFileWav.h" #include +using MutedGetter = std::function; +using MutedSetter = std::function; + namespace scripting { class Audio : public AudioScriptingInterface, protected ReadWriteLockable { @@ -63,6 +67,12 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged) + Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged) + Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); + Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) + Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; @@ -82,6 +92,25 @@ public: void showMicMeter(bool show); + // Mute setting setters and getters + void setMutedDesktop(bool isMuted); + bool getMutedDesktop() const; + void setMutedHMD(bool isMuted); + bool getMutedHMD() const; + void setPTT(bool enabled); + bool getPTT(); + bool getPushingToTalk() const; + + // Push-To-Talk setters and getters + void setPTTDesktop(bool enabled); + bool getPTTDesktop() const; + void setPTTHMD(bool enabled); + bool getPTTHMD() const; + + // Settings handlers + void saveData(); + void loadData(); + /**jsdoc * @function Audio.setInputDevice * @param {object} device @@ -193,6 +222,46 @@ signals: */ void mutedChanged(bool isMuted); + /**jsdoc + * Triggered when desktop audio input is muted or unmuted. + * @function Audio.desktopMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. + * @returns {Signal} + */ + void desktopMutedChanged(bool isMuted); + + /**jsdoc + * Triggered when HMD audio input is muted or unmuted. + * @function Audio.hmdMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. + * @returns {Signal} + */ + void hmdMutedChanged(bool isMuted); + + /** + * Triggered when Push-to-Talk has been enabled or disabled. + * @function Audio.pushToTalkChanged + * @param {boolean} enabled - true if Push-to-Talk is enabled, otherwise false. + * @returns {Signal} + */ + void pushToTalkChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for desktop mode. + * @function Audio.pushToTalkDesktopChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for Desktop mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkDesktopChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for HMD mode. + * @function Audio.pushToTalkHMDChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for HMD mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkHMDChanged(bool enabled); + /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged @@ -237,6 +306,14 @@ signals: */ void contextChanged(const QString& context); + /**jsdoc + * Triggered when pushing to talk. + * @function Audio.pushingToTalkChanged + * @param {boolean} talking - true if broadcasting with PTT, false otherwise. + * @returns {Signal} + */ + void pushingToTalkChanged(bool talking); + public slots: /**jsdoc @@ -245,6 +322,8 @@ public slots: */ void onContextChanged(); + void handlePushedToTalk(bool enabled); + private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); @@ -260,11 +339,19 @@ private: float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; bool _isClipping { false }; - bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; + Setting::Handle _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true }; + Setting::Handle _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true }; + Setting::Handle _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false }; + Setting::Handle _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false }; + bool _desktopMuted{ true }; + bool _hmdMuted{ false }; + bool _pttDesktop{ false }; + bool _pttHMD{ false }; + bool _pushingToTalk{ false }; }; }; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index ee82c0c6ea..51d070d8cd 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -28,6 +28,9 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { + if (Audio.pushingToTalk) { + return; + } if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { From eeb900b76165b0277ef17435d5e8c774ce85d9b5 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:00:09 -0800 Subject: [PATCH 24/71] laying groundwork for audio app + fixing deadlocks --- interface/resources/qml/hifi/audio/MicBar.qml | 6 +- interface/src/scripting/Audio.cpp | 108 +++++++++--------- interface/src/scripting/Audio.h | 1 + scripts/system/audio.js | 11 +- 4 files changed, 69 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index f91058bc3c..2ab1085408 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,7 +134,7 @@ Rectangle { Item { id: status; - readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index fe04ce47ca..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -66,32 +66,30 @@ bool Audio::isMuted() const { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getMutedHMD(); - } - else { + } else { return getMutedDesktop(); } } void Audio::setMuted(bool isMuted) { - withWriteLock([&] { - bool isHMD = qApp->isHMDMode(); - if (isHMD) { - setMutedHMD(isMuted); - } - else { - setMutedDesktop(isMuted); - } - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } else { + setMutedDesktop(isMuted); + } } void Audio::setMutedDesktop(bool isMuted) { bool changed = false; - if (_desktopMuted != isMuted) { - changed = true; - _desktopMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit desktopMutedChanged(isMuted); @@ -106,12 +104,14 @@ bool Audio::getMutedDesktop() const { void Audio::setMutedHMD(bool isMuted) { bool changed = false; - if (_hmdMuted != isMuted) { - changed = true; - _hmdMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit hmdMutedChanged(isMuted); @@ -128,12 +128,24 @@ bool Audio::getPTT() { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getPTTHMD(); - } - else { + } else { return getPTTDesktop(); } } +void scripting::Audio::setPushingToTalk(bool pushingToTalk) { + bool changed = false; + withWriteLock([&] { + if (_pushingToTalk != pushingToTalk) { + changed = true; + _pushingToTalk = pushingToTalk; + } + }); + if (changed) { + emit pushingToTalkChanged(pushingToTalk); + } +} + bool Audio::getPushingToTalk() const { return resultWithReadLock([&] { return _pushingToTalk; @@ -144,8 +156,7 @@ void Audio::setPTT(bool enabled) { bool isHMD = qApp->isHMDMode(); if (isHMD) { setPTTHMD(enabled); - } - else { + } else { setPTTDesktop(enabled); } } @@ -156,16 +167,16 @@ void Audio::setPTTDesktop(bool enabled) { if (_pttDesktop != enabled) { changed = true; _pttDesktop = enabled; - if (!enabled) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedDesktop(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkDesktopChanged(enabled); @@ -184,16 +195,16 @@ void Audio::setPTTHMD(bool enabled) { if (_pttHMD != enabled) { changed = true; _pttHMD = enabled; - if (!enabled) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedHMD(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkHMDChanged(enabled); @@ -318,8 +329,7 @@ void Audio::onContextChanged() { }); if (isHMD) { setMuted(getMutedHMD()); - } - else { + } else { setMuted(getMutedDesktop()); } if (changed) { @@ -331,14 +341,10 @@ void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { setMuted(false); - } - else { + } else { setMuted(true); } - if (_pushingToTalk != enabled) { - _pushingToTalk = enabled; - emit pushingToTalkChanged(enabled); - } + setPushingToTalk(enabled); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 6aa589e399..94f8a7bf54 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -99,6 +99,7 @@ public: bool getMutedHMD() const; void setPTT(bool enabled); bool getPTT(); + void setPushingToTalk(bool pushingToTalk); bool getPushingToTalk() const; // Push-To-Talk setters and getters diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 51d070d8cd..bf44cfa7cc 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -26,12 +26,15 @@ var UNMUTE_ICONS = { icon: "icons/tablet-icons/mic-unmute-i.svg", activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; +var PTT_ICONS = { + icon: "icons/tablet-icons/mic-unmute-i.svg", + activeIcon: "icons/tablet-icons/mic-unmute-a.svg" +}; function onMuteToggled() { if (Audio.pushingToTalk) { - return; - } - if (Audio.muted) { + button.editProperties(PTT_ICONS); + } else if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -71,6 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); +Audio.pushingToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -79,6 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); + Audio.pushingToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 066d1797cf6f96c722274b18b33d4010ed60405a Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:11:47 -0800 Subject: [PATCH 25/71] Push to talk changes + rebased with master (#7) Push to talk changes + rebased with master --- interface/src/scripting/Audio.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 63ce9d2b2e..45bb15f1a3 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,6 +231,26 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; From 9613cfb6b9da73248c95151ddc15d002b85a87b2 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:18:57 -0800 Subject: [PATCH 26/71] changing text display --- interface/resources/qml/hifi/audio/MicBar.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 2ab1085408..9d1cbfbc6c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; + visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -155,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } From 0cb9e7eb3609189d305df8cb700c5225e801b951 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:45:43 -0800 Subject: [PATCH 27/71] exposing setting pushingToTalk --- interface/src/scripting/Audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 94f8a7bf54..9ad4aac9c1 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -72,7 +72,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) - Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; From 7b197c975c9b5926e48cb2d2847e490d8f9ad0d8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 17:29:32 -0800 Subject: [PATCH 28/71] updating compile failure + icons/settings update --- .../icons/tablet-icons/mic-ptt-a.svg | 1 + .../icons/tablet-icons/mic-ptt-i.svg | 24 +++++++ interface/resources/qml/hifi/audio/Audio.qml | 64 ++++++++++++++++--- interface/resources/qml/hifi/audio/MicBar.qml | 8 ++- interface/src/scripting/Audio.cpp | 20 ------ scripts/system/audio.js | 14 ++-- 6 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-a.svg create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-i.svg diff --git a/interface/resources/icons/tablet-icons/mic-ptt-a.svg b/interface/resources/icons/tablet-icons/mic-ptt-a.svg new file mode 100644 index 0000000000..e6df3c69d7 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-a.svg @@ -0,0 +1 @@ +mic-ptt-a \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/mic-ptt-i.svg b/interface/resources/icons/tablet-icons/mic-ptt-i.svg new file mode 100644 index 0000000000..2141ea5229 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-i.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 45358f59a2..d44a9c862e 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -120,6 +120,10 @@ Rectangle { isRedCheck: true; checked: AudioScriptingInterface.muted; onClicked: { + if (AudioScriptingInterface.pushToTalk && !checked) { + // disable push to talk if unmuting + AudioScriptingInterface.pushToTalk = false; + } AudioScriptingInterface.muted = checked; checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding } @@ -150,7 +154,23 @@ Rectangle { } AudioControls.CheckBox { spacing: muteMic.spacing - text: qsTr("Push To Talk"); + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + onXChanged: rightMostInputLevelPos = x + width + } + } + + Separator {} + + ColumnLayout { + spacing: muteMic.spacing; + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; onClicked: { if (isVR) { @@ -167,15 +187,41 @@ Rectangle { }); // restore binding } } - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Show audio level meter"); - checked: AvatarInputs.showAudioTools; - onClicked: { - AvatarInputs.showAudioTools = checked; - checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + Item { + id: pttTextContainer + x: margins.paddings; + width: rightMostInputLevelPos + height: pttTextMetrics.height + visible: true + TextMetrics { + id: pttTextMetrics + text: pttText.text + font: pttText.font + } + RalewayRegular { + id: pttText + wrapMode: Text.WordWrap + color: hifi.colors.white; + width: parent.width; + font.italic: true + size: 16; + text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : + qsTr("Press and hold the button \"T\" to unmute."); + onTextChanged: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } + } + } + Component.onCompleted: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } } - onXChanged: rightMostInputLevelPos = x + width } } } diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 9d1cbfbc6c..50477b82f8 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -67,6 +67,9 @@ Rectangle { hoverEnabled: true; scrollGestureEnabled: false; onClicked: { + if (AudioScriptingInterface.pushToTalk) { + return; + } AudioScriptingInterface.muted = !AudioScriptingInterface.muted; Tablet.playSound(TabletEnums.ButtonClick); } @@ -109,9 +112,10 @@ Rectangle { Image { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; + readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg"; id: image; - source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; + source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -155,7 +159,7 @@ Rectangle { color: parent.color; - text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED PTT-(T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 45bb15f1a3..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,26 +231,6 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } -bool Audio::getPTTHMD() const { - return resultWithReadLock([&] { - return _pttHMD; - }); -} - -void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); - _pttDesktopSetting.set(getPTTDesktop()); - _pttHMDSetting.set(getPTTHMD()); -} - -void Audio::loadData() { - _desktopMuted = _desktopMutedSetting.get(); - _hmdMuted = _hmdMutedSetting.get(); - _pttDesktop = _pttDesktopSetting.get(); - _pttHMD = _pttHMDSetting.get(); -} - bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index bf44cfa7cc..19ed3faef2 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -27,12 +27,12 @@ var UNMUTE_ICONS = { activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; var PTT_ICONS = { - icon: "icons/tablet-icons/mic-unmute-i.svg", - activeIcon: "icons/tablet-icons/mic-unmute-a.svg" + icon: "icons/tablet-icons/mic-ptt-i.svg", + activeIcon: "icons/tablet-icons/mic-ptt-a.svg" }; function onMuteToggled() { - if (Audio.pushingToTalk) { + if (Audio.pushToTalk) { button.editProperties(PTT_ICONS); } else if (Audio.muted) { button.editProperties(MUTE_ICONS); @@ -63,8 +63,8 @@ function onScreenChanged(type, url) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, - activeIcon: Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, + icon: Audio.pushToTalk ? PTT_ICONS.icon : Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: Audio.pushToTalk ? PTT_ICONS.activeIcon : Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -74,7 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); -Audio.pushingToTalkChanged.connect(onMuteToggled); +Audio.pushToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -83,7 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); - Audio.pushingToTalkChanged.disconnect(onMuteToggled); + Audio.pushToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 2536fcaa1794c30813544e616f1ca4666c7a05a3 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:28 -0800 Subject: [PATCH 29/71] Add pushToTalk.js controllerModule. --- .../controllerModules/pushToTalk.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/system/controllers/controllerModules/pushToTalk.js diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js new file mode 100644 index 0000000000..e764b228c9 --- /dev/null +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -0,0 +1,75 @@ +"use strict"; + +// Created by Jason C. Najera on 3/7/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Handles Push-to-Talk functionality for HMD mode. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { // BEGIN LOCAL_SCOPE + function PushToTalkHandler() { + var _this = this; + this.active = false; + //var pttMapping, mappingName; + + this.setup = function() { + //mappingName = 'Hifi-PTT-Dev-' + Math.random(); + //pttMapping = Controller.newMapping(mappingName); + //pttMapping.enable(); + }; + + this.shouldTalk = function (controllerData) { + // Set up test against controllerData here... + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? true : false; + }; + + this.shouldStopTalking = function (controllerData) { + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? false : true; + }; + + this.isReady = function (controllerData, deltaTime) { + if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + Audio.pushingToTalk = true; + returnMakeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + if (this.shouldStopTalking(controllerData) || !Audio.pushToTalk) { + Audio.pushingToTalk = false; + return makeRunningValues(false, [], []); + } + + return makeRunningValues(true, [], []); + }; + + this.cleanup = function () { + //pttMapping.disable(); + }; + + this.parameters = makeDispatcherModuleParameters( + 950, + ["head"], + [], + 100); + } + + var pushToTalk = new PushToTalkHandler(); + enableDispatcherModule("PushToTalk", pushToTalk); + + function cleanup () { + pushToTalk.cleanup(); + disableDispatcherModule("PushToTalk"); + }; + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE \ No newline at end of file From 9d59e68d45d53e53d6471993dd34bca6695ddfab Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:48 -0800 Subject: [PATCH 30/71] Enable pushToTalk.js controller module. --- scripts/system/controllers/controllerDispatcher.js | 1 + scripts/system/controllers/controllerScripts.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 28c3e2a299..f4c0cbb0d6 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -58,6 +58,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name // is stored as the value, rather than false. this.activitySlots = { + head: false, leftHand: false, rightHand: false, rightHandTrigger: false, diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 86ff7701c3..d236d6b12a 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -34,7 +34,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearGrabHyperLinkEntity.js", "controllerModules/nearTabletHighlight.js", "controllerModules/nearGrabEntity.js", - "controllerModules/farGrabEntity.js" + "controllerModules/farGrabEntity.js", + "controllerModules/pushToTalk.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From 55efc315f10604c663b8df2fa6f65c7dce7d4f3f Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:45:59 -0800 Subject: [PATCH 31/71] Fix activation / deactivation criteria for PTT controller module. --- scripts/system/controllers/controllerModules/pushToTalk.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index e764b228c9..557476ccd7 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -25,12 +25,12 @@ Script.include("/~/system/libraries/controllers.js"); this.shouldTalk = function (controllerData) { // Set up test against controllerData here... - var gripVal = controllerData.secondaryValues[this.hand]; + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; return (gripVal) ? true : false; }; this.shouldStopTalking = function (controllerData) { - var gripVal = controllerData.secondaryValues[this.hand]; + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; return (gripVal) ? false : true; }; From a62cadc54145dcbc54bf17d6f13d1277a1832247 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 12:35:04 -0800 Subject: [PATCH 32/71] Fix typo. --- scripts/system/controllers/controllerModules/pushToTalk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index 557476ccd7..dd959ae6fb 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -35,7 +35,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData, deltaTime) { - if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + if (HMD.active && Audio.pushToTalk && this.shouldTalk(controllerData)) { Audio.pushingToTalk = true; returnMakeRunningValues(true, [], []); } From b83e9f70e64ff7717f1fda7a5ed7d7401cdf304b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 7 Mar 2019 12:36:56 -0800 Subject: [PATCH 33/71] adding PushToTalk action --- interface/src/Application.cpp | 11 +++++++++++ libraries/controllers/src/controllers/Actions.cpp | 4 ++++ libraries/controllers/src/controllers/Actions.h | 1 + .../controllers/controllerModules/pushToTalk.js | 4 ++-- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fa63757560..1582c69bc9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1601,12 +1601,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { using namespace controller; auto tabletScriptingInterface = DependencyManager::get(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); { auto actionEnum = static_cast(action); int key = Qt::Key_unknown; static int lastKey = Qt::Key_unknown; bool navAxis = false; switch (actionEnum) { + case Action::TOGGLE_PUSHTOTALK: + if (audioScriptingInterface->getPTT()) { + qDebug() << "State is " << state; + if (state > 0.0f) { + audioScriptingInterface->setPushingToTalk(false); + } else if (state < 0.0f) { + audioScriptingInterface->setPushingToTalk(true); + } + } + case Action::UI_NAV_VERTICAL: navAxis = true; if (state > 0.0f) { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 5a396231b6..57be2f788b 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -180,6 +180,7 @@ namespace controller { * third person, to full screen mirror, then back to first person and repeat. * ContextMenunumbernumberShow / hide the tablet. * ToggleMutenumbernumberToggle the microphone mute. + * TogglePushToTalknumbernumberToggle push to talk. * ToggleOverlaynumbernumberToggle the display of overlays. * SprintnumbernumberSet avatar sprint mode. * ReticleClicknumbernumberSet mouse-pressed. @@ -245,6 +246,8 @@ namespace controller { * ContextMenu instead. * TOGGLE_MUTEnumbernumberDeprecated: Use * ToggleMute instead. + * TOGGLE_PUSHTOTALKnumbernumberDeprecated: Use + * TogglePushToTalk instead. * SPRINTnumbernumberDeprecated: Use * Sprint instead. * LONGITUDINAL_BACKWARDnumbernumberDeprecated: Use @@ -411,6 +414,7 @@ namespace controller { makeButtonPair(Action::ACTION2, "SecondaryAction"), makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + makeButtonPair(Action::TOGGLE_PUSHTOTALK, "TogglePushToTalk"), makeButtonPair(Action::CYCLE_CAMERA, "CycleCamera"), makeButtonPair(Action::TOGGLE_OVERLAY, "ToggleOverlay"), makeButtonPair(Action::SPRINT, "Sprint"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index a12a3d60a9..3e99d8d147 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -60,6 +60,7 @@ enum class Action { CONTEXT_MENU, TOGGLE_MUTE, + TOGGLE_PUSHTOTALK, CYCLE_CAMERA, TOGGLE_OVERLAY, diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index e764b228c9..916769934d 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -35,7 +35,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData, deltaTime) { - if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + if (HMD.active && Audio.pushToTalk && this.shouldTalk(controllerData)) { Audio.pushingToTalk = true; returnMakeRunningValues(true, [], []); } @@ -72,4 +72,4 @@ Script.include("/~/system/libraries/controllers.js"); }; Script.scriptEnding.connect(cleanup); -}()); // END LOCAL_SCOPE \ No newline at end of file +}()); // END LOCAL_SCOPE From 472c7ffab443afbeaa31b090a07f1ddfd475e5ad Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 7 Mar 2019 12:54:32 -0800 Subject: [PATCH 34/71] removing debug statement --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1582c69bc9..16f4a9094f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1610,7 +1610,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo switch (actionEnum) { case Action::TOGGLE_PUSHTOTALK: if (audioScriptingInterface->getPTT()) { - qDebug() << "State is " << state; if (state > 0.0f) { audioScriptingInterface->setPushingToTalk(false); } else if (state < 0.0f) { From 15d49fd9a923ff3ddde21b15d2f1b9fe2e2ece65 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 5 Mar 2019 17:09:54 -0800 Subject: [PATCH 35/71] Initial implementation (deadlocks still occurring in Audio.cpp) --- interface/resources/qml/hifi/audio/Audio.qml | 19 ++ interface/resources/qml/hifi/audio/MicBar.qml | 9 +- interface/src/Application.cpp | 18 ++ interface/src/Application.h | 2 + interface/src/scripting/Audio.cpp | 174 +++++++++++++++++- interface/src/scripting/Audio.h | 89 ++++++++- scripts/system/audio.js | 3 + 7 files changed, 302 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index c8dd83cd62..45358f59a2 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -148,6 +148,25 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk"); + checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; + onClicked: { + if (isVR) { + AudioScriptingInterface.pushToTalkHMD = checked; + } else { + AudioScriptingInterface.pushToTalkDesktop = checked; + } + checked = Qt.binding(function() { + if (isVR) { + return AudioScriptingInterface.pushToTalkHMD; + } else { + return AudioScriptingInterface.pushToTalkDesktop; + } + }); // restore binding + } + } AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Show audio level meter"); diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 39f75a9182..f91058bc3c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -11,10 +11,13 @@ import QtQuick 2.5 import QtGraphicalEffects 1.0 +import stylesUit 1.0 import TabletScriptingInterface 1.0 Rectangle { + HifiConstants { id: hifi; } + readonly property var level: AudioScriptingInterface.inputLevel; property bool gated: false; @@ -131,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.muted; + visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -152,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.muted ? "MUTED" : "MUTE"; + text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6d9a1823a1..dad86e748e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1435,6 +1435,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(this, &Application::activeDisplayPluginChanged, reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); + connect(this, &Application::pushedToTalk, + reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::handlePushedToTalk); } // Create the rendering engine. This can be slow on some machines due to lots of @@ -4205,6 +4207,10 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; + case Qt::Key_T: + emit pushedToTalk(true); + break; + case Qt::Key_P: { if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { AudioInjectorOptions options; @@ -4310,6 +4316,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (_keyboardMouseDevice->isActive()) { _keyboardMouseDevice->keyReleaseEvent(event); } + + switch (event->key()) { + case Qt::Key_T: + emit pushedToTalk(false); + break; + } } void Application::focusOutEvent(QFocusEvent* event) { @@ -5241,6 +5253,9 @@ void Application::loadSettings() { } } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->loadData(); + getMyAvatar()->loadData(); _settingsLoaded = true; } @@ -5250,6 +5265,9 @@ void Application::saveSettings() const { DependencyManager::get()->saveSettings(); DependencyManager::get()->saveSettings(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->saveData(); + Menu::getInstance()->saveSettings(); getMyAvatar()->saveData(); PluginManager::getInstance()->saveSettings(); diff --git a/interface/src/Application.h b/interface/src/Application.h index a8cc9450c5..1c86326f90 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -358,6 +358,8 @@ signals: void miniTabletEnabledChanged(bool enabled); + void pushedToTalk(bool enabled); + public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 2c4c29ff65..fe04ce47ca 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -63,26 +63,163 @@ void Audio::stopRecording() { } bool Audio::isMuted() const { - return resultWithReadLock([&] { - return _isMuted; - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getMutedHMD(); + } + else { + return getMutedDesktop(); + } } void Audio::setMuted(bool isMuted) { + withWriteLock([&] { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } + else { + setMutedDesktop(isMuted); + } + }); +} + +void Audio::setMutedDesktop(bool isMuted) { + bool changed = false; + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit desktopMutedChanged(isMuted); + } +} + +bool Audio::getMutedDesktop() const { + return resultWithReadLock([&] { + return _desktopMuted; + }); +} + +void Audio::setMutedHMD(bool isMuted) { + bool changed = false; + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + if (changed) { + emit mutedChanged(isMuted); + emit hmdMutedChanged(isMuted); + } +} + +bool Audio::getMutedHMD() const { + return resultWithReadLock([&] { + return _hmdMuted; + }); +} + +bool Audio::getPTT() { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + return getPTTHMD(); + } + else { + return getPTTDesktop(); + } +} + +bool Audio::getPushingToTalk() const { + return resultWithReadLock([&] { + return _pushingToTalk; + }); +} + +void Audio::setPTT(bool enabled) { + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setPTTHMD(enabled); + } + else { + setPTTDesktop(enabled); + } +} + +void Audio::setPTTDesktop(bool enabled) { bool changed = false; withWriteLock([&] { - if (_isMuted != isMuted) { - _isMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + if (_pttDesktop != enabled) { changed = true; + _pttDesktop = enabled; + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } } }); if (changed) { - emit mutedChanged(isMuted); + emit pushToTalkChanged(enabled); + emit pushToTalkDesktopChanged(enabled); } } +bool Audio::getPTTDesktop() const { + return resultWithReadLock([&] { + return _pttDesktop; + }); +} + +void Audio::setPTTHMD(bool enabled) { + bool changed = false; + withWriteLock([&] { + if (_pttHMD != enabled) { + changed = true; + _pttHMD = enabled; + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } + else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + } + }); + if (changed) { + emit pushToTalkChanged(enabled); + emit pushToTalkHMDChanged(enabled); + } +} + +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; @@ -179,11 +316,32 @@ void Audio::onContextChanged() { changed = true; } }); + if (isHMD) { + setMuted(getMutedHMD()); + } + else { + setMuted(getMutedDesktop()); + } if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } } +void Audio::handlePushedToTalk(bool enabled) { + if (getPTT()) { + if (enabled) { + setMuted(false); + } + else { + setMuted(true); + } + if (_pushingToTalk != enabled) { + _pushingToTalk = enabled; + emit pushingToTalkChanged(enabled); + } + } +} + void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index e4dcba9130..6aa589e399 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -12,6 +12,7 @@ #ifndef hifi_scripting_Audio_h #define hifi_scripting_Audio_h +#include #include "AudioScriptingInterface.h" #include "AudioDevices.h" #include "AudioEffectOptions.h" @@ -19,6 +20,9 @@ #include "AudioFileWav.h" #include +using MutedGetter = std::function; +using MutedSetter = std::function; + namespace scripting { class Audio : public AudioScriptingInterface, protected ReadWriteLockable { @@ -63,6 +67,12 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool clipping READ isClipping NOTIFY clippingChanged) Q_PROPERTY(QString context READ getContext NOTIFY contextChanged) Q_PROPERTY(AudioDevices* devices READ getDevices NOTIFY nop) + Q_PROPERTY(bool desktopMuted READ getMutedDesktop WRITE setMutedDesktop NOTIFY desktopMutedChanged) + Q_PROPERTY(bool hmdMuted READ getMutedHMD WRITE setMutedHMD NOTIFY hmdMutedChanged) + Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); + Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) + Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; @@ -82,6 +92,25 @@ public: void showMicMeter(bool show); + // Mute setting setters and getters + void setMutedDesktop(bool isMuted); + bool getMutedDesktop() const; + void setMutedHMD(bool isMuted); + bool getMutedHMD() const; + void setPTT(bool enabled); + bool getPTT(); + bool getPushingToTalk() const; + + // Push-To-Talk setters and getters + void setPTTDesktop(bool enabled); + bool getPTTDesktop() const; + void setPTTHMD(bool enabled); + bool getPTTHMD() const; + + // Settings handlers + void saveData(); + void loadData(); + /**jsdoc * @function Audio.setInputDevice * @param {object} device @@ -193,6 +222,46 @@ signals: */ void mutedChanged(bool isMuted); + /**jsdoc + * Triggered when desktop audio input is muted or unmuted. + * @function Audio.desktopMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. + * @returns {Signal} + */ + void desktopMutedChanged(bool isMuted); + + /**jsdoc + * Triggered when HMD audio input is muted or unmuted. + * @function Audio.hmdMutedChanged + * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. + * @returns {Signal} + */ + void hmdMutedChanged(bool isMuted); + + /** + * Triggered when Push-to-Talk has been enabled or disabled. + * @function Audio.pushToTalkChanged + * @param {boolean} enabled - true if Push-to-Talk is enabled, otherwise false. + * @returns {Signal} + */ + void pushToTalkChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for desktop mode. + * @function Audio.pushToTalkDesktopChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for Desktop mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkDesktopChanged(bool enabled); + + /** + * Triggered when Push-to-Talk has been enabled or disabled for HMD mode. + * @function Audio.pushToTalkHMDChanged + * @param {boolean} enabled - true if Push-to-Talk is emabled for HMD mode, otherwise false. + * @returns {Signal} + */ + void pushToTalkHMDChanged(bool enabled); + /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged @@ -237,6 +306,14 @@ signals: */ void contextChanged(const QString& context); + /**jsdoc + * Triggered when pushing to talk. + * @function Audio.pushingToTalkChanged + * @param {boolean} talking - true if broadcasting with PTT, false otherwise. + * @returns {Signal} + */ + void pushingToTalkChanged(bool talking); + public slots: /**jsdoc @@ -245,6 +322,8 @@ public slots: */ void onContextChanged(); + void handlePushedToTalk(bool enabled); + private slots: void setMuted(bool muted); void enableNoiseReduction(bool enable); @@ -260,11 +339,19 @@ private: float _inputVolume { 1.0f }; float _inputLevel { 0.0f }; bool _isClipping { false }; - bool _isMuted { false }; bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _contextIsHMD { false }; AudioDevices* getDevices() { return &_devices; } AudioDevices _devices; + Setting::Handle _desktopMutedSetting{ QStringList { Audio::AUDIO, "desktopMuted" }, true }; + Setting::Handle _hmdMutedSetting{ QStringList { Audio::AUDIO, "hmdMuted" }, true }; + Setting::Handle _pttDesktopSetting{ QStringList { Audio::AUDIO, "pushToTalkDesktop" }, false }; + Setting::Handle _pttHMDSetting{ QStringList { Audio::AUDIO, "pushToTalkHMD" }, false }; + bool _desktopMuted{ true }; + bool _hmdMuted{ false }; + bool _pttDesktop{ false }; + bool _pttHMD{ false }; + bool _pushingToTalk{ false }; }; }; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index ee82c0c6ea..51d070d8cd 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -28,6 +28,9 @@ var UNMUTE_ICONS = { }; function onMuteToggled() { + if (Audio.pushingToTalk) { + return; + } if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { From 7cb17b2d6ea31da2aefb0f775569ca1abf43e637 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:00:09 -0800 Subject: [PATCH 36/71] laying groundwork for audio app + fixing deadlocks --- interface/resources/qml/hifi/audio/MicBar.qml | 6 +- interface/src/scripting/Audio.cpp | 108 +++++++++--------- interface/src/scripting/Audio.h | 1 + scripts/system/audio.js | 11 +- 4 files changed, 69 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index f91058bc3c..2ab1085408 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,7 +134,7 @@ Rectangle { Item { id: status; - readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: 50; + width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; height: 4; color: parent.color; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index fe04ce47ca..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -66,32 +66,30 @@ bool Audio::isMuted() const { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getMutedHMD(); - } - else { + } else { return getMutedDesktop(); } } void Audio::setMuted(bool isMuted) { - withWriteLock([&] { - bool isHMD = qApp->isHMDMode(); - if (isHMD) { - setMutedHMD(isMuted); - } - else { - setMutedDesktop(isMuted); - } - }); + bool isHMD = qApp->isHMDMode(); + if (isHMD) { + setMutedHMD(isMuted); + } else { + setMutedDesktop(isMuted); + } } void Audio::setMutedDesktop(bool isMuted) { bool changed = false; - if (_desktopMuted != isMuted) { - changed = true; - _desktopMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_desktopMuted != isMuted) { + changed = true; + _desktopMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit desktopMutedChanged(isMuted); @@ -106,12 +104,14 @@ bool Audio::getMutedDesktop() const { void Audio::setMutedHMD(bool isMuted) { bool changed = false; - if (_hmdMuted != isMuted) { - changed = true; - _hmdMuted = isMuted; - auto client = DependencyManager::get().data(); - QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); - } + withWriteLock([&] { + if (_hmdMuted != isMuted) { + changed = true; + _hmdMuted = isMuted; + auto client = DependencyManager::get().data(); + QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); + } + }); if (changed) { emit mutedChanged(isMuted); emit hmdMutedChanged(isMuted); @@ -128,12 +128,24 @@ bool Audio::getPTT() { bool isHMD = qApp->isHMDMode(); if (isHMD) { return getPTTHMD(); - } - else { + } else { return getPTTDesktop(); } } +void scripting::Audio::setPushingToTalk(bool pushingToTalk) { + bool changed = false; + withWriteLock([&] { + if (_pushingToTalk != pushingToTalk) { + changed = true; + _pushingToTalk = pushingToTalk; + } + }); + if (changed) { + emit pushingToTalkChanged(pushingToTalk); + } +} + bool Audio::getPushingToTalk() const { return resultWithReadLock([&] { return _pushingToTalk; @@ -144,8 +156,7 @@ void Audio::setPTT(bool enabled) { bool isHMD = qApp->isHMDMode(); if (isHMD) { setPTTHMD(enabled); - } - else { + } else { setPTTDesktop(enabled); } } @@ -156,16 +167,16 @@ void Audio::setPTTDesktop(bool enabled) { if (_pttDesktop != enabled) { changed = true; _pttDesktop = enabled; - if (!enabled) { - // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. - setMutedDesktop(true); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedDesktop(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for Desktop) on Push-To-Talk disable. + setMutedDesktop(true); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedDesktop(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkDesktopChanged(enabled); @@ -184,16 +195,16 @@ void Audio::setPTTHMD(bool enabled) { if (_pttHMD != enabled) { changed = true; _pttHMD = enabled; - if (!enabled) { - // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. - setMutedHMD(false); - } - else { - // Should be muted when not pushing to talk while PTT is enabled. - setMutedHMD(true); - } } }); + if (!enabled) { + // Set to default behavior (unmuted for HMD) on Push-To-Talk disable. + setMutedHMD(false); + } else { + // Should be muted when not pushing to talk while PTT is enabled. + setMutedHMD(true); + } + if (changed) { emit pushToTalkChanged(enabled); emit pushToTalkHMDChanged(enabled); @@ -318,8 +329,7 @@ void Audio::onContextChanged() { }); if (isHMD) { setMuted(getMutedHMD()); - } - else { + } else { setMuted(getMutedDesktop()); } if (changed) { @@ -331,14 +341,10 @@ void Audio::handlePushedToTalk(bool enabled) { if (getPTT()) { if (enabled) { setMuted(false); - } - else { + } else { setMuted(true); } - if (_pushingToTalk != enabled) { - _pushingToTalk = enabled; - emit pushingToTalkChanged(enabled); - } + setPushingToTalk(enabled); } } diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 6aa589e399..94f8a7bf54 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -99,6 +99,7 @@ public: bool getMutedHMD() const; void setPTT(bool enabled); bool getPTT(); + void setPushingToTalk(bool pushingToTalk); bool getPushingToTalk() const; // Push-To-Talk setters and getters diff --git a/scripts/system/audio.js b/scripts/system/audio.js index 51d070d8cd..bf44cfa7cc 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -26,12 +26,15 @@ var UNMUTE_ICONS = { icon: "icons/tablet-icons/mic-unmute-i.svg", activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; +var PTT_ICONS = { + icon: "icons/tablet-icons/mic-unmute-i.svg", + activeIcon: "icons/tablet-icons/mic-unmute-a.svg" +}; function onMuteToggled() { if (Audio.pushingToTalk) { - return; - } - if (Audio.muted) { + button.editProperties(PTT_ICONS); + } else if (Audio.muted) { button.editProperties(MUTE_ICONS); } else { button.editProperties(UNMUTE_ICONS); @@ -71,6 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); +Audio.pushingToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -79,6 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); + Audio.pushingToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From 60b98d2c70b2b9366d9d1d6abd18e9136d566a69 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:11:47 -0800 Subject: [PATCH 37/71] Push to talk changes + rebased with master (#7) Push to talk changes + rebased with master --- interface/src/scripting/Audio.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 63ce9d2b2e..45bb15f1a3 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,6 +231,26 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; From 8a5924077a24d0892ab402dceb0c1a258ce07860 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:18:57 -0800 Subject: [PATCH 38/71] changing text display --- interface/resources/qml/hifi/audio/MicBar.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 2ab1085408..9d1cbfbc6c 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -134,9 +134,9 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; - visible: AudioScriptingInterface.pushingToTalk || AudioScriptingInterface.muted; + visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; anchors { left: parent.left; @@ -155,7 +155,7 @@ Rectangle { color: parent.color; - text: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? "SPEAKING" : "PUSH TO TALK") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } @@ -165,7 +165,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } @@ -176,7 +176,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk ? (AudioScriptingInterface.pushingToTalk ? 45: 30) : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; height: 4; color: parent.color; } From a688c0a92b56062b46442f9e25d6d8b57f77294d Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:45:43 -0800 Subject: [PATCH 39/71] exposing setting pushingToTalk --- interface/src/scripting/Audio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 94f8a7bf54..9ad4aac9c1 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -72,7 +72,7 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { Q_PROPERTY(bool pushToTalk READ getPTT WRITE setPTT NOTIFY pushToTalkChanged); Q_PROPERTY(bool pushToTalkDesktop READ getPTTDesktop WRITE setPTTDesktop NOTIFY pushToTalkDesktopChanged) Q_PROPERTY(bool pushToTalkHMD READ getPTTHMD WRITE setPTTHMD NOTIFY pushToTalkHMDChanged) - Q_PROPERTY(bool pushingToTalk READ getPushingToTalk NOTIFY pushingToTalkChanged) + Q_PROPERTY(bool pushingToTalk READ getPushingToTalk WRITE setPushingToTalk NOTIFY pushingToTalkChanged) public: static QString AUDIO; From b7d44403e16e76e8d7b1c672c457946e1933a378 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 17:29:32 -0800 Subject: [PATCH 40/71] updating compile failure + icons/settings update --- .../icons/tablet-icons/mic-ptt-a.svg | 1 + .../icons/tablet-icons/mic-ptt-i.svg | 24 +++++++ interface/resources/qml/hifi/audio/Audio.qml | 64 ++++++++++++++++--- interface/resources/qml/hifi/audio/MicBar.qml | 8 ++- interface/src/scripting/Audio.cpp | 20 ------ scripts/system/audio.js | 14 ++-- 6 files changed, 93 insertions(+), 38 deletions(-) create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-a.svg create mode 100644 interface/resources/icons/tablet-icons/mic-ptt-i.svg diff --git a/interface/resources/icons/tablet-icons/mic-ptt-a.svg b/interface/resources/icons/tablet-icons/mic-ptt-a.svg new file mode 100644 index 0000000000..e6df3c69d7 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-a.svg @@ -0,0 +1 @@ +mic-ptt-a \ No newline at end of file diff --git a/interface/resources/icons/tablet-icons/mic-ptt-i.svg b/interface/resources/icons/tablet-icons/mic-ptt-i.svg new file mode 100644 index 0000000000..2141ea5229 --- /dev/null +++ b/interface/resources/icons/tablet-icons/mic-ptt-i.svg @@ -0,0 +1,24 @@ + + + + + + diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 45358f59a2..d44a9c862e 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -120,6 +120,10 @@ Rectangle { isRedCheck: true; checked: AudioScriptingInterface.muted; onClicked: { + if (AudioScriptingInterface.pushToTalk && !checked) { + // disable push to talk if unmuting + AudioScriptingInterface.pushToTalk = false; + } AudioScriptingInterface.muted = checked; checked = Qt.binding(function() { return AudioScriptingInterface.muted; }); // restore binding } @@ -150,7 +154,23 @@ Rectangle { } AudioControls.CheckBox { spacing: muteMic.spacing - text: qsTr("Push To Talk"); + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + onXChanged: rightMostInputLevelPos = x + width + } + } + + Separator {} + + ColumnLayout { + spacing: muteMic.spacing; + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; onClicked: { if (isVR) { @@ -167,15 +187,41 @@ Rectangle { }); // restore binding } } - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Show audio level meter"); - checked: AvatarInputs.showAudioTools; - onClicked: { - AvatarInputs.showAudioTools = checked; - checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + Item { + id: pttTextContainer + x: margins.paddings; + width: rightMostInputLevelPos + height: pttTextMetrics.height + visible: true + TextMetrics { + id: pttTextMetrics + text: pttText.text + font: pttText.font + } + RalewayRegular { + id: pttText + wrapMode: Text.WordWrap + color: hifi.colors.white; + width: parent.width; + font.italic: true + size: 16; + text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : + qsTr("Press and hold the button \"T\" to unmute."); + onTextChanged: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } + } + } + Component.onCompleted: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } } - onXChanged: rightMostInputLevelPos = x + width } } } diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 9d1cbfbc6c..50477b82f8 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -67,6 +67,9 @@ Rectangle { hoverEnabled: true; scrollGestureEnabled: false; onClicked: { + if (AudioScriptingInterface.pushToTalk) { + return; + } AudioScriptingInterface.muted = !AudioScriptingInterface.muted; Tablet.playSound(TabletEnums.ButtonClick); } @@ -109,9 +112,10 @@ Rectangle { Image { readonly property string unmutedIcon: "../../../icons/tablet-icons/mic-unmute-i.svg"; readonly property string mutedIcon: "../../../icons/tablet-icons/mic-mute-i.svg"; + readonly property string pushToTalkIcon: "../../../icons/tablet-icons/mic-ptt-i.svg"; id: image; - source: AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; + source: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? pushToTalkIcon : AudioScriptingInterface.muted ? mutedIcon : unmutedIcon; width: 30; height: 30; @@ -155,7 +159,7 @@ Rectangle { color: parent.color; - text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED-PTT (T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED PTT-(T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 45bb15f1a3..63ce9d2b2e 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,26 +231,6 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } -bool Audio::getPTTHMD() const { - return resultWithReadLock([&] { - return _pttHMD; - }); -} - -void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); - _pttDesktopSetting.set(getPTTDesktop()); - _pttHMDSetting.set(getPTTHMD()); -} - -void Audio::loadData() { - _desktopMuted = _desktopMutedSetting.get(); - _hmdMuted = _hmdMutedSetting.get(); - _pttDesktop = _pttDesktopSetting.get(); - _pttHMD = _pttHMDSetting.get(); -} - bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; diff --git a/scripts/system/audio.js b/scripts/system/audio.js index bf44cfa7cc..19ed3faef2 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -27,12 +27,12 @@ var UNMUTE_ICONS = { activeIcon: "icons/tablet-icons/mic-unmute-a.svg" }; var PTT_ICONS = { - icon: "icons/tablet-icons/mic-unmute-i.svg", - activeIcon: "icons/tablet-icons/mic-unmute-a.svg" + icon: "icons/tablet-icons/mic-ptt-i.svg", + activeIcon: "icons/tablet-icons/mic-ptt-a.svg" }; function onMuteToggled() { - if (Audio.pushingToTalk) { + if (Audio.pushToTalk) { button.editProperties(PTT_ICONS); } else if (Audio.muted) { button.editProperties(MUTE_ICONS); @@ -63,8 +63,8 @@ function onScreenChanged(type, url) { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, - activeIcon: Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, + icon: Audio.pushToTalk ? PTT_ICONS.icon : Audio.muted ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: Audio.pushToTalk ? PTT_ICONS.activeIcon : Audio.muted ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -74,7 +74,7 @@ onMuteToggled(); button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); Audio.mutedChanged.connect(onMuteToggled); -Audio.pushingToTalkChanged.connect(onMuteToggled); +Audio.pushToTalkChanged.connect(onMuteToggled); Script.scriptEnding.connect(function () { if (onAudioScreen) { @@ -83,7 +83,7 @@ Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.screenChanged.disconnect(onScreenChanged); Audio.mutedChanged.disconnect(onMuteToggled); - Audio.pushingToTalkChanged.disconnect(onMuteToggled); + Audio.pushToTalkChanged.disconnect(onMuteToggled); tablet.removeButton(button); }); From f6add9cafdd87c6a253c344f905c295cbaa617fc Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:28 -0800 Subject: [PATCH 41/71] Add pushToTalk.js controllerModule. --- .../controllerModules/pushToTalk.js | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 scripts/system/controllers/controllerModules/pushToTalk.js diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js new file mode 100644 index 0000000000..e764b228c9 --- /dev/null +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -0,0 +1,75 @@ +"use strict"; + +// Created by Jason C. Najera on 3/7/2019 +// Copyright 2019 High Fidelity, Inc. +// +// Handles Push-to-Talk functionality for HMD mode. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html + +Script.include("/~/system/libraries/controllerDispatcherUtils.js"); +Script.include("/~/system/libraries/controllers.js"); + +(function() { // BEGIN LOCAL_SCOPE + function PushToTalkHandler() { + var _this = this; + this.active = false; + //var pttMapping, mappingName; + + this.setup = function() { + //mappingName = 'Hifi-PTT-Dev-' + Math.random(); + //pttMapping = Controller.newMapping(mappingName); + //pttMapping.enable(); + }; + + this.shouldTalk = function (controllerData) { + // Set up test against controllerData here... + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? true : false; + }; + + this.shouldStopTalking = function (controllerData) { + var gripVal = controllerData.secondaryValues[this.hand]; + return (gripVal) ? false : true; + }; + + this.isReady = function (controllerData, deltaTime) { + if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + Audio.pushingToTalk = true; + returnMakeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + if (this.shouldStopTalking(controllerData) || !Audio.pushToTalk) { + Audio.pushingToTalk = false; + return makeRunningValues(false, [], []); + } + + return makeRunningValues(true, [], []); + }; + + this.cleanup = function () { + //pttMapping.disable(); + }; + + this.parameters = makeDispatcherModuleParameters( + 950, + ["head"], + [], + 100); + } + + var pushToTalk = new PushToTalkHandler(); + enableDispatcherModule("PushToTalk", pushToTalk); + + function cleanup () { + pushToTalk.cleanup(); + disableDispatcherModule("PushToTalk"); + }; + + Script.scriptEnding.connect(cleanup); +}()); // END LOCAL_SCOPE \ No newline at end of file From 3b274c2b6e7dbe77a9e89b255ae197c36e9184b8 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:31:48 -0800 Subject: [PATCH 42/71] Enable pushToTalk.js controller module. --- scripts/system/controllers/controllerDispatcher.js | 1 + scripts/system/controllers/controllerScripts.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index 28c3e2a299..f4c0cbb0d6 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -58,6 +58,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // not set to false (not in use), a module cannot start. When a module is using a slot, that module's name // is stored as the value, rather than false. this.activitySlots = { + head: false, leftHand: false, rightHand: false, rightHandTrigger: false, diff --git a/scripts/system/controllers/controllerScripts.js b/scripts/system/controllers/controllerScripts.js index 726e075fcc..ca7d041792 100644 --- a/scripts/system/controllers/controllerScripts.js +++ b/scripts/system/controllers/controllerScripts.js @@ -33,7 +33,8 @@ var CONTOLLER_SCRIPTS = [ "controllerModules/nearGrabHyperLinkEntity.js", "controllerModules/nearTabletHighlight.js", "controllerModules/nearGrabEntity.js", - "controllerModules/farGrabEntity.js" + "controllerModules/farGrabEntity.js", + "controllerModules/pushToTalk.js" ]; var DEBUG_MENU_ITEM = "Debug defaultScripts.js"; From 88a125aff0a5dd3de6d523e5325ddedc24dff30e Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Tue, 5 Mar 2019 17:09:54 -0800 Subject: [PATCH 43/71] Initial implementation (deadlocks still occurring in Audio.cpp) --- interface/resources/qml/hifi/audio/Audio.qml | 19 ++++++++ interface/resources/qml/hifi/audio/MicBar.qml | 18 ++++---- interface/src/scripting/Audio.cpp | 20 ++++++++ interface/src/scripting/Audio.h | 46 +++++++++---------- 4 files changed, 71 insertions(+), 32 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index d44a9c862e..9e9a8c0022 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -152,6 +152,25 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk"); + checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; + onClicked: { + if (isVR) { + AudioScriptingInterface.pushToTalkHMD = checked; + } else { + AudioScriptingInterface.pushToTalkDesktop = checked; + } + checked = Qt.binding(function() { + if (isVR) { + return AudioScriptingInterface.pushToTalkHMD; + } else { + return AudioScriptingInterface.pushToTalkDesktop; + } + }); // restore binding + } + } AudioControls.CheckBox { spacing: muteMic.spacing text: qsTr("Show audio level meter"); diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 50477b82f8..6cb45eaecb 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -19,13 +19,13 @@ Rectangle { HifiConstants { id: hifi; } readonly property var level: AudioScriptingInterface.inputLevel; - + property bool gated: false; Component.onCompleted: { AudioScriptingInterface.noiseGateOpened.connect(function() { gated = false; }); AudioScriptingInterface.noiseGateClosed.connect(function() { gated = true; }); } - + property bool standalone: false; property var dragTarget: null; @@ -138,7 +138,7 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; @@ -235,12 +235,12 @@ Rectangle { } } } - + Rectangle { id: gatedIndicator; visible: gated && !AudioScriptingInterface.clipping - - radius: 4; + + radius: 4; width: 2 * radius; height: 2 * radius; color: "#0080FF"; @@ -249,12 +249,12 @@ Rectangle { verticalCenter: parent.verticalCenter; } } - + Rectangle { id: clippingIndicator; visible: AudioScriptingInterface.clipping - - radius: 4; + + radius: 4; width: 2 * radius; height: 2 * radius; color: colors.red; diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 63ce9d2b2e..45bb15f1a3 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -231,6 +231,26 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index 9ad4aac9c1..10aceb02fb 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -41,16 +41,16 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable { * @hifi-assignment-client * * @property {boolean} muted - true if the audio input is muted, otherwise false. - * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When - * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just + * @property {boolean} noiseReduction - true if noise reduction is enabled, otherwise false. When + * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * above the noise floor. - * @property {number} inputLevel - The loudness of the audio input, range 0.0 (no sound) – + * @property {number} inputLevel - The loudness of the audio input, range 0.0 (no sound) – * 1.0 (the onset of clipping). Read-only. * @property {boolean} clipping - true if the audio input is clipping, otherwise false. - * @property {number} inputVolume - Adjusts the volume of the input audio; range 0.01.0. - * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some + * @property {number} inputVolume - Adjusts the volume of the input audio; range 0.01.0. + * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some * devices, and others might only support values of 0.0 and 1.0. - * @property {boolean} isStereoInput - true if the input audio is being used in stereo, otherwise + * @property {boolean} isStereoInput - true if the input audio is being used in stereo, otherwise * false. Some devices do not support stereo, in which case the value is always false. * @property {string} context - The current context of the audio: either "Desktop" or "HMD". * Read-only. @@ -115,7 +115,7 @@ public: /**jsdoc * @function Audio.setInputDevice * @param {object} device - * @param {boolean} isHMD + * @param {boolean} isHMD * @deprecated This function is deprecated and will be removed. */ Q_INVOKABLE void setInputDevice(const QAudioDeviceInfo& device, bool isHMD); @@ -129,8 +129,8 @@ public: Q_INVOKABLE void setOutputDevice(const QAudioDeviceInfo& device, bool isHMD); /**jsdoc - * Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options - * come from either the domain's audio zone if used — configured on the server — or as scripted by + * Enable or disable reverberation. Reverberation is done by the client, on the post-mix audio. The reverberation options + * come from either the domain's audio zone if used — configured on the server — or as scripted by * {@link Audio.setReverbOptions|setReverbOptions}. * @function Audio.setReverb * @param {boolean} enable - true to enable reverberation, false to disable. @@ -140,13 +140,13 @@ public: * var injectorOptions = { * position: MyAvatar.position * }; - * + * * Script.setTimeout(function () { * print("Reverb OFF"); * Audio.setReverb(false); * injector = Audio.playSound(sound, injectorOptions); * }, 1000); - * + * * Script.setTimeout(function () { * var reverbOptions = new AudioEffectOptions(); * reverbOptions.roomSize = 100; @@ -154,26 +154,26 @@ public: * print("Reverb ON"); * Audio.setReverb(true); * }, 4000); - * + * * Script.setTimeout(function () { * print("Reverb OFF"); * Audio.setReverb(false); * }, 8000); */ Q_INVOKABLE void setReverb(bool enable); - + /**jsdoc * Configure reverberation options. Use {@link Audio.setReverb|setReverb} to enable or disable reverberation. * @function Audio.setReverbOptions * @param {AudioEffectOptions} options - The reverberation options. */ Q_INVOKABLE void setReverbOptions(const AudioEffectOptions* options); - + /**jsdoc * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. * @function Audio.startRecording - * @param {string} filename - The path and name of the file to make the recording in. Should have a .wav + * @param {string} filename - The path and name of the file to make the recording in. Should have a .wav * extension. The file is overwritten if it already exists. - * @returns {boolean} true if the specified file could be opened and audio recording has started, otherwise + * @returns {boolean} true if the specified file could be opened and audio recording has started, otherwise * false. * @example Make a 10 second audio recording. * var filename = File.getTempDir() + "/audio.wav"; @@ -182,13 +182,13 @@ public: * Audio.stopRecording(); * print("Audio recording made in: " + filename); * }, 10000); - * + * * } else { * print("Could not make an audio recording in: " + filename); * } */ Q_INVOKABLE bool startRecording(const QString& filename); - + /**jsdoc * Finish making an audio recording started with {@link Audio.startRecording|startRecording}. * @function Audio.stopRecording @@ -222,7 +222,7 @@ signals: * }); */ void mutedChanged(bool isMuted); - + /**jsdoc * Triggered when desktop audio input is muted or unmuted. * @function Audio.desktopMutedChanged @@ -274,9 +274,9 @@ signals: /**jsdoc * Triggered when the input audio volume changes. * @function Audio.inputVolumeChanged - * @param {number} volume - The requested volume to be applied to the audio input, range 0.0 – - * 1.0. The resulting value of Audio.inputVolume depends on the capabilities of the device: - * for example, the volume can't be changed on some devices, and others might only support values of 0.0 + * @param {number} volume - The requested volume to be applied to the audio input, range 0.0 – + * 1.0. The resulting value of Audio.inputVolume depends on the capabilities of the device: + * for example, the volume can't be changed on some devices, and others might only support values of 0.0 * and 1.0. * @returns {Signal} */ @@ -285,7 +285,7 @@ signals: /**jsdoc * Triggered when the input audio level changes. * @function Audio.inputLevelChanged - * @param {number} level - The loudness of the input audio, range 0.0 (no sound) – 1.0 (the + * @param {number} level - The loudness of the input audio, range 0.0 (no sound) – 1.0 (the * onset of clipping). * @returns {Signal} */ From cb0fdd2ef2ff91854b0c4962bb2bd12bac4aa551 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:00:09 -0800 Subject: [PATCH 44/71] laying groundwork for audio app + fixing deadlocks --- interface/resources/qml/hifi/audio/MicBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 6cb45eaecb..41ab0b6e91 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -138,7 +138,7 @@ Rectangle { Item { id: status; - readonly property string color: (AudioScriptingInterface.pushingToTalk && AudioScriptingInterface.muted) ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; From f4db9fefd0d81e539491492981b32a8a75a51964 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:11:47 -0800 Subject: [PATCH 45/71] Push to talk changes + rebased with master (#7) Push to talk changes + rebased with master --- interface/src/scripting/Audio.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 45bb15f1a3..0a859c4dcc 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -251,6 +251,26 @@ void Audio::loadData() { _pttHMD = _pttHMDSetting.get(); } +bool Audio::getPTTHMD() const { + return resultWithReadLock([&] { + return _pttHMD; + }); +} + +void Audio::saveData() { + _desktopMutedSetting.set(getMutedDesktop()); + _hmdMutedSetting.set(getMutedHMD()); + _pttDesktopSetting.set(getPTTDesktop()); + _pttHMDSetting.set(getPTTHMD()); +} + +void Audio::loadData() { + _desktopMuted = _desktopMutedSetting.get(); + _hmdMuted = _hmdMutedSetting.get(); + _pttDesktop = _pttDesktopSetting.get(); + _pttHMD = _pttHMDSetting.get(); +} + bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; From 39db3979b9eb8e46b71623752ff4785fcb6f93dc Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 11:18:57 -0800 Subject: [PATCH 46/71] changing text display --- interface/resources/qml/hifi/audio/MicBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 41ab0b6e91..491b9f9554 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -138,7 +138,7 @@ Rectangle { Item { id: status; - readonly property string color: AudioScriptingInterface.pushToTalk ? hifi.colors.blueHighlight : AudioScriptingInterface.muted ? colors.muted : colors.unmuted; + readonly property string color: AudioScriptingInterface.muted ? colors.muted : colors.unmuted; visible: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) || AudioScriptingInterface.muted; From 88d8163b04e8b740c1433530a8821f94dae7ae3b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 6 Mar 2019 17:29:32 -0800 Subject: [PATCH 47/71] updating compile failure + icons/settings update --- interface/resources/qml/hifi/audio/Audio.qml | 60 +++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 9e9a8c0022..569cd23176 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -154,7 +154,23 @@ Rectangle { } AudioControls.CheckBox { spacing: muteMic.spacing - text: qsTr("Push To Talk"); + text: qsTr("Show audio level meter"); + checked: AvatarInputs.showAudioTools; + onClicked: { + AvatarInputs.showAudioTools = checked; + checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + } + onXChanged: rightMostInputLevelPos = x + width + } + } + + Separator {} + + ColumnLayout { + spacing: muteMic.spacing; + AudioControls.CheckBox { + spacing: muteMic.spacing + text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; onClicked: { if (isVR) { @@ -171,15 +187,41 @@ Rectangle { }); // restore binding } } - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Show audio level meter"); - checked: AvatarInputs.showAudioTools; - onClicked: { - AvatarInputs.showAudioTools = checked; - checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding + Item { + id: pttTextContainer + x: margins.paddings; + width: rightMostInputLevelPos + height: pttTextMetrics.height + visible: true + TextMetrics { + id: pttTextMetrics + text: pttText.text + font: pttText.font + } + RalewayRegular { + id: pttText + wrapMode: Text.WordWrap + color: hifi.colors.white; + width: parent.width; + font.italic: true + size: 16; + text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : + qsTr("Press and hold the button \"T\" to unmute."); + onTextChanged: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } + } + } + Component.onCompleted: { + if (pttTextMetrics.width > rightMostInputLevelPos) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } else { + pttTextContainer.height = pttTextMetrics.height; + } } - onXChanged: rightMostInputLevelPos = x + width } } From 24d6646e8d23f6aab6b441a7b097e17bd41ddbb4 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 11:45:59 -0800 Subject: [PATCH 48/71] Fix activation / deactivation criteria for PTT controller module. --- scripts/system/controllers/controllerModules/pushToTalk.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index e764b228c9..557476ccd7 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -25,12 +25,12 @@ Script.include("/~/system/libraries/controllers.js"); this.shouldTalk = function (controllerData) { // Set up test against controllerData here... - var gripVal = controllerData.secondaryValues[this.hand]; + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; return (gripVal) ? true : false; }; this.shouldStopTalking = function (controllerData) { - var gripVal = controllerData.secondaryValues[this.hand]; + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; return (gripVal) ? false : true; }; From 5f48a6d1044fd11cab6110db39eca367448ab0e4 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Thu, 7 Mar 2019 12:35:04 -0800 Subject: [PATCH 49/71] Fix typo. --- scripts/system/controllers/controllerModules/pushToTalk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index 557476ccd7..dd959ae6fb 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -35,7 +35,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.isReady = function (controllerData, deltaTime) { - if (HMD.active() && Audio.pushToTalk && this.shouldTalk(controllerData)) { + if (HMD.active && Audio.pushToTalk && this.shouldTalk(controllerData)) { Audio.pushingToTalk = true; returnMakeRunningValues(true, [], []); } From 18b86d550de3f71290b354e1e4ff602ba4dd03ff Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 7 Mar 2019 12:36:56 -0800 Subject: [PATCH 50/71] adding PushToTalk action --- interface/src/Application.cpp | 11 +++++++++++ libraries/controllers/src/controllers/Actions.cpp | 4 ++++ libraries/controllers/src/controllers/Actions.h | 1 + .../controllers/controllerModules/pushToTalk.js | 2 +- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index dad86e748e..c0cacd4e40 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1601,12 +1601,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { using namespace controller; auto tabletScriptingInterface = DependencyManager::get(); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); { auto actionEnum = static_cast(action); int key = Qt::Key_unknown; static int lastKey = Qt::Key_unknown; bool navAxis = false; switch (actionEnum) { + case Action::TOGGLE_PUSHTOTALK: + if (audioScriptingInterface->getPTT()) { + qDebug() << "State is " << state; + if (state > 0.0f) { + audioScriptingInterface->setPushingToTalk(false); + } else if (state < 0.0f) { + audioScriptingInterface->setPushingToTalk(true); + } + } + case Action::UI_NAV_VERTICAL: navAxis = true; if (state > 0.0f) { diff --git a/libraries/controllers/src/controllers/Actions.cpp b/libraries/controllers/src/controllers/Actions.cpp index 5a396231b6..57be2f788b 100644 --- a/libraries/controllers/src/controllers/Actions.cpp +++ b/libraries/controllers/src/controllers/Actions.cpp @@ -180,6 +180,7 @@ namespace controller { * third person, to full screen mirror, then back to first person and repeat. * ContextMenunumbernumberShow / hide the tablet. * ToggleMutenumbernumberToggle the microphone mute. + * TogglePushToTalknumbernumberToggle push to talk. * ToggleOverlaynumbernumberToggle the display of overlays. * SprintnumbernumberSet avatar sprint mode. * ReticleClicknumbernumberSet mouse-pressed. @@ -245,6 +246,8 @@ namespace controller { * ContextMenu instead. * TOGGLE_MUTEnumbernumberDeprecated: Use * ToggleMute instead. + * TOGGLE_PUSHTOTALKnumbernumberDeprecated: Use + * TogglePushToTalk instead. * SPRINTnumbernumberDeprecated: Use * Sprint instead. * LONGITUDINAL_BACKWARDnumbernumberDeprecated: Use @@ -411,6 +414,7 @@ namespace controller { makeButtonPair(Action::ACTION2, "SecondaryAction"), makeButtonPair(Action::CONTEXT_MENU, "ContextMenu"), makeButtonPair(Action::TOGGLE_MUTE, "ToggleMute"), + makeButtonPair(Action::TOGGLE_PUSHTOTALK, "TogglePushToTalk"), makeButtonPair(Action::CYCLE_CAMERA, "CycleCamera"), makeButtonPair(Action::TOGGLE_OVERLAY, "ToggleOverlay"), makeButtonPair(Action::SPRINT, "Sprint"), diff --git a/libraries/controllers/src/controllers/Actions.h b/libraries/controllers/src/controllers/Actions.h index a12a3d60a9..3e99d8d147 100644 --- a/libraries/controllers/src/controllers/Actions.h +++ b/libraries/controllers/src/controllers/Actions.h @@ -60,6 +60,7 @@ enum class Action { CONTEXT_MENU, TOGGLE_MUTE, + TOGGLE_PUSHTOTALK, CYCLE_CAMERA, TOGGLE_OVERLAY, diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index dd959ae6fb..9d6435f497 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -72,4 +72,4 @@ Script.include("/~/system/libraries/controllers.js"); }; Script.scriptEnding.connect(cleanup); -}()); // END LOCAL_SCOPE \ No newline at end of file +}()); // END LOCAL_SCOPE From 48d1ec850c64e16822ce495cf55d0f1c8a9b60cf Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Thu, 7 Mar 2019 12:54:32 -0800 Subject: [PATCH 51/71] removing debug statement --- interface/src/Application.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c0cacd4e40..fc9fcd1bbb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1610,7 +1610,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo switch (actionEnum) { case Action::TOGGLE_PUSHTOTALK: if (audioScriptingInterface->getPTT()) { - qDebug() << "State is " << state; if (state > 0.0f) { audioScriptingInterface->setPushingToTalk(false); } else if (state < 0.0f) { From e0fe11056e8e49e529eddc12930b50f737d55966 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 8 Mar 2019 16:37:41 -0800 Subject: [PATCH 52/71] fixing compile error --- interface/src/scripting/Audio.h | 40 --------------------------------- 1 file changed, 40 deletions(-) diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h index ce857211e0..10aceb02fb 100644 --- a/interface/src/scripting/Audio.h +++ b/interface/src/scripting/Audio.h @@ -263,46 +263,6 @@ signals: */ void pushToTalkHMDChanged(bool enabled); - /**jsdoc - * Triggered when desktop audio input is muted or unmuted. - * @function Audio.desktopMutedChanged - * @param {boolean} isMuted - true if the audio input is muted for desktop mode, otherwise false. - * @returns {Signal} - */ - void desktopMutedChanged(bool isMuted); - - /**jsdoc - * Triggered when HMD audio input is muted or unmuted. - * @function Audio.hmdMutedChanged - * @param {boolean} isMuted - true if the audio input is muted for HMD mode, otherwise false. - * @returns {Signal} - */ - void hmdMutedChanged(bool isMuted); - - /** - * Triggered when Push-to-Talk has been enabled or disabled. - * @function Audio.pushToTalkChanged - * @param {boolean} enabled - true if Push-to-Talk is enabled, otherwise false. - * @returns {Signal} - */ - void pushToTalkChanged(bool enabled); - - /** - * Triggered when Push-to-Talk has been enabled or disabled for desktop mode. - * @function Audio.pushToTalkDesktopChanged - * @param {boolean} enabled - true if Push-to-Talk is emabled for Desktop mode, otherwise false. - * @returns {Signal} - */ - void pushToTalkDesktopChanged(bool enabled); - - /** - * Triggered when Push-to-Talk has been enabled or disabled for HMD mode. - * @function Audio.pushToTalkHMDChanged - * @param {boolean} enabled - true if Push-to-Talk is emabled for HMD mode, otherwise false. - * @returns {Signal} - */ - void pushToTalkHMDChanged(bool enabled); - /**jsdoc * Triggered when the audio input noise reduction is enabled or disabled. * @function Audio.noiseReductionChanged From 3464fe09c1b0ea2b3671941e09bbe91dbe6f36bf Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Fri, 8 Mar 2019 17:39:11 -0800 Subject: [PATCH 53/71] Applying the hero changes to master soon to be rc81 --- .../src/avatars/AvatarMixerClientData.cpp | 4 + assignment-client/src/avatars/MixerAvatar.h | 3 - .../qml/+android_interface/Stats.qml | 4 + interface/resources/qml/Stats.qml | 4 + interface/src/avatar/AvatarManager.cpp | 183 +++++++++++------- interface/src/avatar/AvatarManager.h | 4 + interface/src/avatar/OtherAvatar.cpp | 12 -- interface/src/ui/Stats.cpp | 2 + interface/src/ui/Stats.h | 18 ++ libraries/avatars/src/AvatarData.cpp | 13 +- libraries/avatars/src/AvatarData.h | 18 +- 11 files changed, 179 insertions(+), 86 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index cef4383aee..557c5c9fe3 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -130,12 +130,16 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message, const SlaveShared } _lastReceivedSequenceNumber = sequenceNumber; glm::vec3 oldPosition = getPosition(); + bool oldHasPriority = _avatar->getHasPriority(); // compute the offset to the data payload if (!_avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead()))) { return false; } + // Regardless of what the client says, restore the priority as we know it without triggering any update. + _avatar->setHasPriorityWithoutTimestampReset(oldHasPriority); + auto newPosition = getPosition(); if (newPosition != oldPosition) { //#define AVATAR_HERO_TEST_HACK diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h index 4c3ded4582..3e80704495 100644 --- a/assignment-client/src/avatars/MixerAvatar.h +++ b/assignment-client/src/avatars/MixerAvatar.h @@ -19,11 +19,8 @@ class MixerAvatar : public AvatarData { public: - bool getHasPriority() const { return _hasPriority; } - void setHasPriority(bool hasPriority) { _hasPriority = hasPriority; } private: - bool _hasPriority { false }; }; using MixerAvatarSharedPointer = std::shared_ptr; diff --git a/interface/resources/qml/+android_interface/Stats.qml b/interface/resources/qml/+android_interface/Stats.qml index fe56f3797b..54f6086a86 100644 --- a/interface/resources/qml/+android_interface/Stats.qml +++ b/interface/resources/qml/+android_interface/Stats.qml @@ -113,6 +113,10 @@ Item { visible: root.expanded text: "Avatars Updated: " + root.updatedAvatarCount } + StatText { + visible: root.expanded + text: "Heroes Count/Updated: " + root.heroAvatarCount + "/" + root.updatedHeroAvatarCount + } StatText { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 3b703d72e6..6748418d19 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -115,6 +115,10 @@ Item { visible: root.expanded text: "Avatars Updated: " + root.updatedAvatarCount } + StatText { + visible: root.expanded + text: "Heroes Count/Updated: " + root.heroAvatarCount + "/" + root.updatedHeroAvatarCount + } StatText { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 55025b3b23..c66c0a30cb 100755 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -232,96 +232,142 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { auto avatarMap = getHashCopy(); const auto& views = qApp->getConicalViews(); - PrioritySortUtil::PriorityQueue sortedAvatars(views, - AvatarData::_avatarSortCoefficientSize, - AvatarData::_avatarSortCoefficientCenter, - AvatarData::_avatarSortCoefficientAge); - sortedAvatars.reserve(avatarMap.size() - 1); // don't include MyAvatar + // Prepare 2 queues for heros and for crowd avatars + using AvatarPriorityQueue = PrioritySortUtil::PriorityQueue; + // Keep two independent queues, one for heroes and one for the riff-raff. + enum PriorityVariants + { + kHero = 0, + kNonHero, + NumVariants + }; + AvatarPriorityQueue avatarPriorityQueues[NumVariants] = { + { views, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge }, + { views, + AvatarData::_avatarSortCoefficientSize, + AvatarData::_avatarSortCoefficientCenter, + AvatarData::_avatarSortCoefficientAge } }; + // Reserve space + //avatarPriorityQueues[kHero].reserve(10); // just few + avatarPriorityQueues[kNonHero].reserve(avatarMap.size() - 1); // don't include MyAvatar // Build vector and compute priorities auto nodeList = DependencyManager::get(); AvatarHash::iterator itr = avatarMap.begin(); while (itr != avatarMap.end()) { - const auto& avatar = std::static_pointer_cast(*itr); + auto avatar = std::static_pointer_cast(*itr); // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars if (avatar != _myAvatar && avatar->isInitialized() && !nodeList->isPersonalMutingNode(avatar->getID())) { - sortedAvatars.push(SortableAvatar(avatar)); + if (avatar->getHasPriority()) { + avatarPriorityQueues[kHero].push(SortableAvatar(avatar)); + } else { + avatarPriorityQueues[kNonHero].push(SortableAvatar(avatar)); + } } ++itr; } - // Sort - const auto& sortedAvatarVector = sortedAvatars.getSortedVector(); + + _numHeroAvatars = (int)avatarPriorityQueues[kHero].size(); // process in sorted order uint64_t startTime = usecTimestampNow(); - uint64_t updateExpiry = startTime + MAX_UPDATE_AVATARS_TIME_BUDGET; + + const uint64_t MAX_UPDATE_HEROS_TIME_BUDGET = uint64_t(0.8 * MAX_UPDATE_AVATARS_TIME_BUDGET); + + uint64_t updatePriorityExpiries[NumVariants] = { startTime + MAX_UPDATE_HEROS_TIME_BUDGET, startTime + MAX_UPDATE_AVATARS_TIME_BUDGET }; + int numHerosUpdated = 0; int numAvatarsUpdated = 0; - int numAVatarsNotUpdated = 0; + int numAvatarsNotUpdated = 0; render::Transaction renderTransaction; workload::Transaction workloadTransaction; - for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) { - const SortableAvatar& sortData = *it; - const auto avatar = std::static_pointer_cast(sortData.getAvatar()); - if (!avatar->_isClientAvatar) { - avatar->setIsClientAvatar(true); - } - // TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update - if (avatar->getSkeletonModel()->isLoaded()) { - // remove the orb if it is there - avatar->removeOrb(); - if (avatar->needsPhysicsUpdate()) { - _avatarsToChangeInPhysics.insert(avatar); - } - } else { - avatar->updateOrbPosition(); - } + + for (int p = kHero; p < NumVariants; p++) { + auto& priorityQueue = avatarPriorityQueues[p]; + // Sorting the current queue HERE as part of the measured timing. + const auto& sortedAvatarVector = priorityQueue.getSortedVector(); - // for ALL avatars... - if (_shouldRender) { - avatar->ensureInScene(avatar, qApp->getMain3DScene()); - } - avatar->animateScaleChanges(deltaTime); + auto passExpiry = updatePriorityExpiries[p]; - uint64_t now = usecTimestampNow(); - if (now < updateExpiry) { - // we're within budget - bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; - if (inView && avatar->hasNewJointData()) { - numAvatarsUpdated++; + for (auto it = sortedAvatarVector.begin(); it != sortedAvatarVector.end(); ++it) { + const SortableAvatar& sortData = *it; + const auto avatar = std::static_pointer_cast(sortData.getAvatar()); + if (!avatar->_isClientAvatar) { + avatar->setIsClientAvatar(true); } - auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig); - if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { - avatar->_transit.reset(); - avatar->setIsNewAvatar(false); - } - avatar->simulate(deltaTime, inView); - if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) { - _myAvatar->addAvatarHandsToFlow(avatar); - } - avatar->updateRenderItem(renderTransaction); - avatar->updateSpaceProxy(workloadTransaction); - avatar->setLastRenderUpdateTime(startTime); - } else { - // we've spent our full time budget --> bail on the rest of the avatar updates - // --> more avatars may freeze until their priority trickles up - // --> some scale animations may glitch - // --> some avatar velocity measurements may be a little off - - // no time to simulate, but we take the time to count how many were tragically missed - while (it != sortedAvatarVector.end()) { - const SortableAvatar& newSortData = *it; - const auto& newAvatar = newSortData.getAvatar(); - bool inView = newSortData.getPriority() > OUT_OF_VIEW_THRESHOLD; - // Once we reach an avatar that's not in view, all avatars after it will also be out of view - if (!inView) { - break; + // TODO: to help us scale to more avatars it would be nice to not have to poll this stuff every update + if (avatar->getSkeletonModel()->isLoaded()) { + // remove the orb if it is there + avatar->removeOrb(); + if (avatar->needsPhysicsUpdate()) { + _avatarsToChangeInPhysics.insert(avatar); } - numAVatarsNotUpdated += (int)(newAvatar->hasNewJointData()); - ++it; + } else { + avatar->updateOrbPosition(); } - break; + + // for ALL avatars... + if (_shouldRender) { + avatar->ensureInScene(avatar, qApp->getMain3DScene()); + } + + avatar->animateScaleChanges(deltaTime); + + uint64_t now = usecTimestampNow(); + if (now < passExpiry) { + // we're within budget + bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAvatarsUpdated++; + } + auto transitStatus = avatar->_transit.update(deltaTime, avatar->_serverPosition, _transitConfig); + if (avatar->getIsNewAvatar() && (transitStatus == AvatarTransit::Status::START_TRANSIT || + transitStatus == AvatarTransit::Status::ABORT_TRANSIT)) { + avatar->_transit.reset(); + avatar->setIsNewAvatar(false); + } + avatar->simulate(deltaTime, inView); + if (avatar->getSkeletonModel()->isLoaded() && avatar->getWorkloadRegion() == workload::Region::R1) { + _myAvatar->addAvatarHandsToFlow(avatar); + } + avatar->updateRenderItem(renderTransaction); + avatar->updateSpaceProxy(workloadTransaction); + avatar->setLastRenderUpdateTime(startTime); + } else { + // we've spent our time budget for this priority bucket + // let's deal with the reminding avatars if this pass and BREAK from the for loop + + if (p == kHero) { + // Hero, + // --> put them back in the non hero queue + + auto& crowdQueue = avatarPriorityQueues[kNonHero]; + while (it != sortedAvatarVector.end()) { + crowdQueue.push(SortableAvatar((*it).getAvatar())); + ++it; + } + } else { + // Non Hero + // --> bail on the rest of the avatar updates + // --> more avatars may freeze until their priority trickles up + // --> some scale animations may glitch + // --> some avatar velocity measurements may be a little off + + // no time to simulate, but we take the time to count how many were tragically missed + numAvatarsNotUpdated = sortedAvatarVector.end() - it; + } + + // We had to cut short this pass, we must break out of the for loop here + break; + } + } + + if (p == kHero) { + numHerosUpdated = numAvatarsUpdated; } } @@ -337,7 +383,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { _space->enqueueTransaction(workloadTransaction); _numAvatarsUpdated = numAvatarsUpdated; - _numAvatarsNotUpdated = numAVatarsNotUpdated; + _numAvatarsNotUpdated = numAvatarsNotUpdated; + _numHeroAvatarsUpdated = numHerosUpdated; simulateAvatarFades(deltaTime); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 51352ec861..2b58b14d11 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -90,6 +90,8 @@ public: int getNumAvatarsUpdated() const { return _numAvatarsUpdated; } int getNumAvatarsNotUpdated() const { return _numAvatarsNotUpdated; } + int getNumHeroAvatars() const { return _numHeroAvatars; } + int getNumHeroAvatarsUpdated() const { return _numHeroAvatarsUpdated; } float getAvatarSimulationTime() const { return _avatarSimulationTime; } void updateMyAvatar(float deltaTime); @@ -242,6 +244,8 @@ private: RateCounter<> _myAvatarSendRate; int _numAvatarsUpdated { 0 }; int _numAvatarsNotUpdated { 0 }; + int _numHeroAvatars{ 0 }; + int _numHeroAvatarsUpdated{ 0 }; float _avatarSimulationTime { 0.0f }; bool _shouldRender { true }; bool _myAvatarDataPacketsPaused { false }; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 7848c46eee..11eb6542c4 100755 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -200,17 +200,6 @@ void OtherAvatar::resetDetailedMotionStates() { void OtherAvatar::setWorkloadRegion(uint8_t region) { _workloadRegion = region; - QString printRegion = ""; - if (region == workload::Region::R1) { - printRegion = "R1"; - } else if (region == workload::Region::R2) { - printRegion = "R2"; - } else if (region == workload::Region::R3) { - printRegion = "R3"; - } else { - printRegion = "invalid"; - } - qCDebug(avatars) << "Setting workload region to " << printRegion; computeShapeLOD(); } @@ -235,7 +224,6 @@ void OtherAvatar::computeShapeLOD() { if (newLOD != _bodyLOD) { _bodyLOD = newLOD; if (isInPhysicsSimulation()) { - qCDebug(avatars) << "Changing to body LOD " << newLOD; _needsReinsertion = true; } } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e3697ee8ec..ecdae0b375 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -125,8 +125,10 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(heroAvatarCount, avatarManager->getNumHeroAvatars()); STAT_UPDATE(physicsObjectCount, qApp->getNumCollisionObjects()); STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); + STAT_UPDATE(updatedHeroAvatarCount, avatarManager->getNumHeroAvatarsUpdated()); STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE_FLOAT(renderrate, qApp->getRenderLoopRate(), 0.1f); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 36e92b00af..0f563a6935 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -49,8 +49,10 @@ private: \ * @property {number} presentdroprate - Read-only. * @property {number} gameLoopRate - Read-only. * @property {number} avatarCount - Read-only. + * @property {number} heroAvatarCount - Read-only. * @property {number} physicsObjectCount - Read-only. * @property {number} updatedAvatarCount - Read-only. + * @property {number} updatedHeroAvatarCount - Read-only. * @property {number} notUpdatedAvatarCount - Read-only. * @property {number} packetInCount - Read-only. * @property {number} packetOutCount - Read-only. @@ -203,8 +205,10 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, presentdroprate, 0) STATS_PROPERTY(int, gameLoopRate, 0) STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, heroAvatarCount, 0) STATS_PROPERTY(int, physicsObjectCount, 0) STATS_PROPERTY(int, updatedAvatarCount, 0) + STATS_PROPERTY(int, updatedHeroAvatarCount, 0) STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) @@ -436,6 +440,13 @@ signals: */ void avatarCountChanged(); + /**jsdoc + * Triggered when the value of the heroAvatarCount property changes. + * @function Stats.heroAvatarCountChanged + * @returns {Signal} + */ + void heroAvatarCountChanged(); + /**jsdoc * Triggered when the value of the updatedAvatarCount property changes. * @function Stats.updatedAvatarCountChanged @@ -443,6 +454,13 @@ signals: */ void updatedAvatarCountChanged(); + /**jsdoc + * Triggered when the value of the updatedHeroAvatarCount property changes. + * @function Stats.updatedHeroAvatarCountChanged + * @returns {Signal} + */ + void updatedHeroAvatarCountChanged(); + /**jsdoc * Triggered when the value of the notUpdatedAvatarCount property changes. * @function Stats.notUpdatedAvatarCountChanged diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c733cfa291..26407c3564 100755 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -564,6 +564,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent setAtBit16(flags, COLLIDE_WITH_OTHER_AVATARS); } + // Avatar has hero priority + if (getHasPriority()) { + setAtBit16(flags, HAS_HERO_PRIORITY); + } + data->flags = flags; destinationBuffer += sizeof(AvatarDataPacket::AdditionalFlags); @@ -1152,7 +1157,8 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { auto newHasProceduralEyeFaceMovement = oneAtBit16(bitItems, PROCEDURAL_EYE_FACE_MOVEMENT); auto newHasProceduralBlinkFaceMovement = oneAtBit16(bitItems, PROCEDURAL_BLINK_FACE_MOVEMENT); auto newCollideWithOtherAvatars = oneAtBit16(bitItems, COLLIDE_WITH_OTHER_AVATARS); - + auto newHasPriority = oneAtBit16(bitItems, HAS_HERO_PRIORITY); + bool keyStateChanged = (_keyState != newKeyState); bool handStateChanged = (_handState != newHandState); bool faceStateChanged = (_headData->_isFaceTrackerConnected != newFaceTrackerConnected); @@ -1161,8 +1167,10 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { bool proceduralEyeFaceMovementChanged = (_headData->getHasProceduralEyeFaceMovement() != newHasProceduralEyeFaceMovement); bool proceduralBlinkFaceMovementChanged = (_headData->getHasProceduralBlinkFaceMovement() != newHasProceduralBlinkFaceMovement); bool collideWithOtherAvatarsChanged = (_collideWithOtherAvatars != newCollideWithOtherAvatars); + bool hasPriorityChanged = (getHasPriority() != newHasPriority); bool somethingChanged = keyStateChanged || handStateChanged || faceStateChanged || eyeStateChanged || audioEnableFaceMovementChanged || - proceduralEyeFaceMovementChanged || proceduralBlinkFaceMovementChanged || collideWithOtherAvatarsChanged; + proceduralEyeFaceMovementChanged || + proceduralBlinkFaceMovementChanged || collideWithOtherAvatarsChanged || hasPriorityChanged; _keyState = newKeyState; _handState = newHandState; @@ -1172,6 +1180,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _headData->setHasProceduralEyeFaceMovement(newHasProceduralEyeFaceMovement); _headData->setHasProceduralBlinkFaceMovement(newHasProceduralBlinkFaceMovement); _collideWithOtherAvatars = newCollideWithOtherAvatars; + setHasPriorityWithoutTimestampReset(newHasPriority); sourceBuffer += sizeof(AvatarDataPacket::AdditionalFlags); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 63396a59ac..95bbcbeb16 100755 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -100,6 +100,9 @@ const quint32 AVATAR_MOTION_SCRIPTABLE_BITS = // Procedural audio to mouth movement is enabled 8th bit // Procedural Blink is enabled 9th bit // Procedural Eyelid is enabled 10th bit +// Procedural PROCEDURAL_BLINK_FACE_MOVEMENT is enabled 11th bit +// Procedural Collide with other avatars is enabled 12th bit +// Procedural Has Hero Priority is enabled 13th bit const int KEY_STATE_START_BIT = 0; // 1st and 2nd bits const int HAND_STATE_START_BIT = 2; // 3rd and 4th bits @@ -111,7 +114,7 @@ const int AUDIO_ENABLED_FACE_MOVEMENT = 8; // 9th bit const int PROCEDURAL_EYE_FACE_MOVEMENT = 9; // 10th bit const int PROCEDURAL_BLINK_FACE_MOVEMENT = 10; // 11th bit const int COLLIDE_WITH_OTHER_AVATARS = 11; // 12th bit - +const int HAS_HERO_PRIORITY = 12; // 13th bit (be scared) const char HAND_STATE_NULL = 0; const char LEFT_HAND_POINTING_FLAG = 1; @@ -1121,6 +1124,18 @@ public: int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; + // An Avatar can be set Priority from the AvatarMixer side. + bool getHasPriority() const { return _hasPriority; } + // regular setHasPriority does a check of state changed and if true reset 'additionalFlagsChanged' timestamp + void setHasPriority(bool hasPriority) { + if (_hasPriority != hasPriority) { + _additionalFlagsChanged = usecTimestampNow(); + _hasPriority = hasPriority; + } + } + // In some cases, we want to assign the hasPRiority flag without reseting timestamp + void setHasPriorityWithoutTimestampReset(bool hasPriority) { _hasPriority = hasPriority; } + const glm::vec3& getTargetVelocity() const { return _targetVelocity; } void clearRecordingBasis(); @@ -1498,6 +1513,7 @@ protected: bool _isNewAvatar { true }; bool _isClientAvatar { false }; bool _collideWithOtherAvatars { true }; + bool _hasPriority{ false }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; From 04c6c425125d4da61863662ac83f1242c1496705 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Sun, 10 Mar 2019 14:28:31 -0700 Subject: [PATCH 54/71] Fix build error from merge. --- interface/src/scripting/Audio.cpp | 40 ------------------------------- 1 file changed, 40 deletions(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 0a859c4dcc..669856198d 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -211,12 +211,6 @@ void Audio::setPTTHMD(bool enabled) { } } -bool Audio::getPTTHMD() const { - return resultWithReadLock([&] { - return _pttHMD; - }); -} - void Audio::saveData() { _desktopMutedSetting.set(getMutedDesktop()); _hmdMutedSetting.set(getMutedHMD()); @@ -237,40 +231,6 @@ bool Audio::getPTTHMD() const { }); } -void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); - _pttDesktopSetting.set(getPTTDesktop()); - _pttHMDSetting.set(getPTTHMD()); -} - -void Audio::loadData() { - _desktopMuted = _desktopMutedSetting.get(); - _hmdMuted = _hmdMutedSetting.get(); - _pttDesktop = _pttDesktopSetting.get(); - _pttHMD = _pttHMDSetting.get(); -} - -bool Audio::getPTTHMD() const { - return resultWithReadLock([&] { - return _pttHMD; - }); -} - -void Audio::saveData() { - _desktopMutedSetting.set(getMutedDesktop()); - _hmdMutedSetting.set(getMutedHMD()); - _pttDesktopSetting.set(getPTTDesktop()); - _pttHMDSetting.set(getPTTHMD()); -} - -void Audio::loadData() { - _desktopMuted = _desktopMutedSetting.get(); - _hmdMuted = _hmdMutedSetting.get(); - _pttDesktop = _pttDesktopSetting.get(); - _pttHMD = _pttHMDSetting.get(); -} - bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; From 2eaa1e63d333a2872683001d04cb24e724ac1f40 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Sun, 10 Mar 2019 17:07:02 -0700 Subject: [PATCH 55/71] switch from column layout to item --- interface/resources/qml/hifi/audio/Audio.qml | 74 +++----------------- 1 file changed, 8 insertions(+), 66 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 569cd23176..ce968090b4 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -166,10 +166,12 @@ Rectangle { Separator {} - ColumnLayout { - spacing: muteMic.spacing; + Item { + width: rightMostInputLevelPos + height: pttTextContainer.height + pttCheckBox.height + margins.paddings + 10 AudioControls.CheckBox { - spacing: muteMic.spacing + id: pttCheckBox + anchors.top: parent.top text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; onClicked: { @@ -189,69 +191,9 @@ Rectangle { } Item { id: pttTextContainer - x: margins.paddings; - width: rightMostInputLevelPos - height: pttTextMetrics.height - visible: true - TextMetrics { - id: pttTextMetrics - text: pttText.text - font: pttText.font - } - RalewayRegular { - id: pttText - wrapMode: Text.WordWrap - color: hifi.colors.white; - width: parent.width; - font.italic: true - size: 16; - text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : - qsTr("Press and hold the button \"T\" to unmute."); - onTextChanged: { - if (pttTextMetrics.width > rightMostInputLevelPos) { - pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; - } else { - pttTextContainer.height = pttTextMetrics.height; - } - } - } - Component.onCompleted: { - if (pttTextMetrics.width > rightMostInputLevelPos) { - pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; - } else { - pttTextContainer.height = pttTextMetrics.height; - } - } - } - } - - Separator {} - - ColumnLayout { - spacing: muteMic.spacing; - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Push To Talk (T)"); - checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; - onClicked: { - if (isVR) { - AudioScriptingInterface.pushToTalkHMD = checked; - } else { - AudioScriptingInterface.pushToTalkDesktop = checked; - } - checked = Qt.binding(function() { - if (isVR) { - return AudioScriptingInterface.pushToTalkHMD; - } else { - return AudioScriptingInterface.pushToTalkDesktop; - } - }); // restore binding - } - } - Item { - id: pttTextContainer - x: margins.paddings; - width: rightMostInputLevelPos + anchors.top: pttCheckBox.bottom + anchors.topMargin: 10 + width: parent.width height: pttTextMetrics.height visible: true TextMetrics { From 54973375a4f543557a7547828e3c8896db5bdb20 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Sun, 10 Mar 2019 17:18:18 -0700 Subject: [PATCH 56/71] fixng ptt checkbox --- interface/resources/qml/hifi/audio/Audio.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index ce968090b4..46ae937614 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -171,6 +171,8 @@ Rectangle { height: pttTextContainer.height + pttCheckBox.height + margins.paddings + 10 AudioControls.CheckBox { id: pttCheckBox + spacing: muteMic.spacing; + width: rightMostInputLevelPos anchors.top: parent.top text: qsTr("Push To Talk (T)"); checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; From cf3694e8e343f0dd39e8875c689214ea4611ed33 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Sun, 10 Mar 2019 19:00:41 -0700 Subject: [PATCH 57/71] fixing error in master + getting ptt to show up --- interface/resources/qml/hifi/audio/Audio.qml | 114 +++++++++---------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index a07fb1cb95..376aa39269 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -104,7 +104,6 @@ Rectangle { RowLayout { x: 2 * margins.paddings; - spacing: columnOne.width; width: parent.width; // mute is in its own row @@ -170,66 +169,66 @@ Rectangle { } } } + } - Separator {} + Separator {} - Item { - width: rightMostInputLevelPos - height: pttTextContainer.height + pttCheckBox.height + margins.paddings + 10 - AudioControls.CheckBox { - id: pttCheckBox - spacing: muteMic.spacing; - width: rightMostInputLevelPos - anchors.top: parent.top - text: qsTr("Push To Talk (T)"); - checked: isVR ? AudioScriptingInterface.pushToTalkHMD : AudioScriptingInterface.pushToTalkDesktop; - onClicked: { - if (isVR) { - AudioScriptingInterface.pushToTalkHMD = checked; + + ColumnLayout { + id: pttColumn + spacing: 24; + x: 2 * margins.paddings; + HifiControlsUit.Switch { + id: pttSwitch + height: root.switchHeight; + switchWidth: root.switchWidth; + labelTextOn: qsTr("Push To Talk (T)"); + backgroundOnColor: "#E3E3E3"; + checked: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; + onCheckedChanged: { + if ((bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR)) { + AudioScriptingInterface.pushToTalkDesktop = checked; + } else { + AudioScriptingInterface.pushToTalkHMD = checked; + } + checked = Qt.binding(function() { + if ((bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR)) { + return AudioScriptingInterface.pushToTalkDesktop; } else { - AudioScriptingInterface.pushToTalkDesktop = checked; + return AudioScriptingInterface.pushToTalkHMD; } - checked = Qt.binding(function() { - if (isVR) { - return AudioScriptingInterface.pushToTalkHMD; - } else { - return AudioScriptingInterface.pushToTalkDesktop; - } - }); // restore binding - } + }); // restore binding } - Item { - id: pttTextContainer - anchors.top: pttCheckBox.bottom - anchors.topMargin: 10 - width: parent.width - height: pttTextMetrics.height - visible: true - TextMetrics { - id: pttTextMetrics - text: pttText.text - font: pttText.font - } - RalewayRegular { - id: pttText - wrapMode: Text.WordWrap - color: hifi.colors.white; - width: parent.width; - font.italic: true - size: 16; - text: isVR ? qsTr("Press and hold grip triggers on both of your controllers to unmute.") : - qsTr("Press and hold the button \"T\" to unmute."); - onTextChanged: { - if (pttTextMetrics.width > rightMostInputLevelPos) { - pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; - } else { - pttTextContainer.height = pttTextMetrics.height; - } - } - } - Component.onCompleted: { - if (pttTextMetrics.width > rightMostInputLevelPos) { - pttTextContainer.height = Math.ceil(pttTextMetrics.width / rightMostInputLevelPos) * pttTextMetrics.height; + } + Item { + id: pttTextContainer + width: rightMostInputLevelPos + height: pttTextMetrics.height + anchors.left: parent.left + anchors.leftMargin: -margins.padding + TextMetrics { + id: pttTextMetrics + text: pttText.text + font: pttText.font + } + RalewayRegular { + id: pttText + color: hifi.colors.white; + width: parent.width; + wrapMode: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR) ? Text.NoWrap : Text.WordWrap; + font.italic: true + size: 16; + + text: (bar.currentIndex === 1 && isVR) || + (bar.currentIndex === 0 && !isVR) ? qsTr("Press and hold the button \"T\" to unmute.") : + qsTr("Press and hold grip triggers on both of your controllers to unmute."); + onTextChanged: { + if (pttTextMetrics.width > pttTextContainer.width) { + pttTextContainer.height = Math.ceil(pttTextMetrics.width / pttTextContainer.width) * pttTextMetrics.height; } else { pttTextContainer.height = pttTextMetrics.height; } @@ -240,6 +239,7 @@ Rectangle { Separator {} + Item { x: margins.paddings; width: parent.width - margins.paddings*2 @@ -293,7 +293,7 @@ Rectangle { text: devicename onPressed: { if (!checked) { - stereoMic.checked = false; + stereoInput.checked = false; AudioScriptingInterface.setStereoInput(false); // the next selected audio device might not support stereo AudioScriptingInterface.setInputDevice(info, bar.currentIndex === 1); } From 20487b2ad1adb1a10b16bac8cb48e3bf12e93e44 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Sun, 10 Mar 2019 19:39:45 -0700 Subject: [PATCH 58/71] connecting pushingToTalkChanged with handlePushedToTalk --- interface/src/Application.cpp | 19 +++++++++---------- interface/src/Application.h | 2 -- interface/src/scripting/Audio.cpp | 3 ++- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fc9fcd1bbb..215736001c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1435,8 +1435,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); connect(this, &Application::activeDisplayPluginChanged, reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::onContextChanged); - connect(this, &Application::pushedToTalk, - reinterpret_cast(audioScriptingInterface.data()), &scripting::Audio::handlePushedToTalk); } // Create the rendering engine. This can be slow on some machines due to lots of @@ -1609,13 +1607,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo bool navAxis = false; switch (actionEnum) { case Action::TOGGLE_PUSHTOTALK: - if (audioScriptingInterface->getPTT()) { - if (state > 0.0f) { - audioScriptingInterface->setPushingToTalk(false); - } else if (state < 0.0f) { - audioScriptingInterface->setPushingToTalk(true); - } + if (state > 0.0f) { + audioScriptingInterface->setPushingToTalk(false); + } else if (state < 0.0f) { + audioScriptingInterface->setPushingToTalk(true); } + break; case Action::UI_NAV_VERTICAL: navAxis = true; @@ -4218,7 +4215,8 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_T: - emit pushedToTalk(true); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->setPushingToTalk(true); break; case Qt::Key_P: { @@ -4329,7 +4327,8 @@ void Application::keyReleaseEvent(QKeyEvent* event) { switch (event->key()) { case Qt::Key_T: - emit pushedToTalk(false); + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); + audioScriptingInterface->setPushingToTalk(false); break; } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 1c86326f90..a8cc9450c5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -358,8 +358,6 @@ signals: void miniTabletEnabledChanged(bool enabled); - void pushedToTalk(bool enabled); - public slots: QVector pasteEntities(float x, float y, float z); bool exportEntities(const QString& filename, const QVector& entityIDs, const glm::vec3* givenOffset = nullptr); diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index 669856198d..c4dfcffb61 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -40,6 +40,8 @@ Audio::Audio() : _devices(_contextIsHMD) { connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); + // when pushing to talk changed, handle it. + connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk); enableNoiseReduction(enableNoiseReductionSetting.get()); onContextChanged(); } @@ -344,7 +346,6 @@ void Audio::handlePushedToTalk(bool enabled) { } else { setMuted(true); } - setPushingToTalk(enabled); } } From 7b51061b5f3eee889b6d5f7ea4cec5e502518650 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Sun, 10 Mar 2019 19:46:01 -0700 Subject: [PATCH 59/71] fixing initialization in switch statement --- interface/src/Application.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 215736001c..3230419816 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -4047,6 +4047,7 @@ void Application::keyPressEvent(QKeyEvent* event) { _keysPressed.insert(event->key(), *event); } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isKeyCaptured(event) || isInterstitialMode()) { @@ -4215,7 +4216,6 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_T: - auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); audioScriptingInterface->setPushingToTalk(true); break; @@ -4325,9 +4325,9 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _keyboardMouseDevice->keyReleaseEvent(event); } + auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); switch (event->key()) { case Qt::Key_T: - auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); audioScriptingInterface->setPushingToTalk(false); break; } From ec0cf3ee3ace28f9c2bc39fa350190238f15a46c Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Mon, 11 Mar 2019 09:58:20 -0700 Subject: [PATCH 60/71] Fix typo. --- .../controllerModules/pushToTalk.js | 123 +++++++++--------- 1 file changed, 62 insertions(+), 61 deletions(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index 9d6435f497..6b1bacc367 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -11,65 +11,66 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -(function() { // BEGIN LOCAL_SCOPE - function PushToTalkHandler() { - var _this = this; - this.active = false; - //var pttMapping, mappingName; - - this.setup = function() { - //mappingName = 'Hifi-PTT-Dev-' + Math.random(); - //pttMapping = Controller.newMapping(mappingName); - //pttMapping.enable(); - }; - - this.shouldTalk = function (controllerData) { - // Set up test against controllerData here... - var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; - return (gripVal) ? true : false; - }; - - this.shouldStopTalking = function (controllerData) { - var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; - return (gripVal) ? false : true; - }; - - this.isReady = function (controllerData, deltaTime) { - if (HMD.active && Audio.pushToTalk && this.shouldTalk(controllerData)) { - Audio.pushingToTalk = true; - returnMakeRunningValues(true, [], []); - } - - return makeRunningValues(false, [], []); - }; - - this.run = function (controllerData, deltaTime) { - if (this.shouldStopTalking(controllerData) || !Audio.pushToTalk) { - Audio.pushingToTalk = false; - return makeRunningValues(false, [], []); - } - - return makeRunningValues(true, [], []); - }; - - this.cleanup = function () { - //pttMapping.disable(); - }; - - this.parameters = makeDispatcherModuleParameters( - 950, - ["head"], - [], - 100); - } - - var pushToTalk = new PushToTalkHandler(); - enableDispatcherModule("PushToTalk", pushToTalk); - - function cleanup () { - pushToTalk.cleanup(); - disableDispatcherModule("PushToTalk"); - }; - - Script.scriptEnding.connect(cleanup); +(function () { // BEGIN LOCAL_SCOPE + function PushToTalkHandler() { + var _this = this; + this.active = false; + //var pttMapping, mappingName; + + this.setup = function () { + //mappingName = 'Hifi-PTT-Dev-' + Math.random(); + //pttMapping = Controller.newMapping(mappingName); + //pttMapping.enable(); + }; + + this.shouldTalk = function (controllerData) { + // Set up test against controllerData here... + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; + return (gripVal) ? true : false; + }; + + this.shouldStopTalking = function (controllerData) { + var gripVal = controllerData.secondaryValues[LEFT_HAND] && controllerData.secondaryValues[RIGHT_HAND]; + return (gripVal) ? false : true; + }; + + this.isReady = function (controllerData, deltaTime) { + if (HMD.active && Audio.pushToTalk && this.shouldTalk(controllerData)) { + Audio.pushingToTalk = true; + return makeRunningValues(true, [], []); + } + + return makeRunningValues(false, [], []); + }; + + this.run = function (controllerData, deltaTime) { + if (this.shouldStopTalking(controllerData) || !Audio.pushToTalk) { + Audio.pushingToTalk = false; + print("Stop pushing to talk."); + return makeRunningValues(false, [], []); + } + + return makeRunningValues(true, [], []); + }; + + this.cleanup = function () { + //pttMapping.disable(); + }; + + this.parameters = makeDispatcherModuleParameters( + 950, + ["head"], + [], + 100); + } + + var pushToTalk = new PushToTalkHandler(); + enableDispatcherModule("PushToTalk", pushToTalk); + + function cleanup() { + pushToTalk.cleanup(); + disableDispatcherModule("PushToTalk"); + }; + + Script.scriptEnding.connect(cleanup); }()); // END LOCAL_SCOPE From c1ed01115de29730e9d2132d5f63e13d143f04a3 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Mon, 11 Mar 2019 09:58:35 -0700 Subject: [PATCH 61/71] Fix logic for HMD vs Desktop. --- interface/resources/qml/hifi/audio/Audio.qml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 376aa39269..ecc3297d9f 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -184,18 +184,15 @@ Rectangle { switchWidth: root.switchWidth; labelTextOn: qsTr("Push To Talk (T)"); backgroundOnColor: "#E3E3E3"; - checked: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; + checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; onCheckedChanged: { - if ((bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR)) { + if (bar.currentIndex === 0) { AudioScriptingInterface.pushToTalkDesktop = checked; } else { AudioScriptingInterface.pushToTalkHMD = checked; } checked = Qt.binding(function() { - if ((bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR)) { + if (bar.currentIndex === 0) { return AudioScriptingInterface.pushToTalkDesktop; } else { return AudioScriptingInterface.pushToTalkHMD; @@ -218,13 +215,11 @@ Rectangle { id: pttText color: hifi.colors.white; width: parent.width; - wrapMode: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR) ? Text.NoWrap : Text.WordWrap; + wrapMode: (bar.currentIndex === 0) ? Text.NoWrap : Text.WordWrap; font.italic: true size: 16; - text: (bar.currentIndex === 1 && isVR) || - (bar.currentIndex === 0 && !isVR) ? qsTr("Press and hold the button \"T\" to unmute.") : + text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to unmute.") : qsTr("Press and hold grip triggers on both of your controllers to unmute."); onTextChanged: { if (pttTextMetrics.width > pttTextContainer.width) { From e4e8a61328d1a2ec9c70608e216b8e4dbf1625b2 Mon Sep 17 00:00:00 2001 From: Roxanne Skelly Date: Mon, 11 Mar 2019 10:46:51 -0700 Subject: [PATCH 62/71] Case 21326 - missing marketplaceInject.js The re-addition of marketplaceInject.js didn't merge from 80 for some reason. --- scripts/system/html/js/marketplacesInject.js | 744 +++++++++++++++++++ 1 file changed, 744 insertions(+) create mode 100644 scripts/system/html/js/marketplacesInject.js diff --git a/scripts/system/html/js/marketplacesInject.js b/scripts/system/html/js/marketplacesInject.js new file mode 100644 index 0000000000..8d408169ba --- /dev/null +++ b/scripts/system/html/js/marketplacesInject.js @@ -0,0 +1,744 @@ +/* global $, window, MutationObserver */ + +// +// marketplacesInject.js +// +// Created by David Rowe on 12 Nov 2016. +// Copyright 2016 High Fidelity, Inc. +// +// Injected into marketplace Web pages. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function () { + // Event bridge messages. + var CLARA_IO_DOWNLOAD = "CLARA.IO DOWNLOAD"; + var CLARA_IO_STATUS = "CLARA.IO STATUS"; + var CLARA_IO_CANCEL_DOWNLOAD = "CLARA.IO CANCEL DOWNLOAD"; + var CLARA_IO_CANCELLED_DOWNLOAD = "CLARA.IO CANCELLED DOWNLOAD"; + var GOTO_DIRECTORY = "GOTO_DIRECTORY"; + var GOTO_MARKETPLACE = "GOTO_MARKETPLACE"; + var QUERY_CAN_WRITE_ASSETS = "QUERY_CAN_WRITE_ASSETS"; + var CAN_WRITE_ASSETS = "CAN_WRITE_ASSETS"; + var WARN_USER_NO_PERMISSIONS = "WARN_USER_NO_PERMISSIONS"; + + var canWriteAssets = false; + var xmlHttpRequest = null; + var isPreparing = false; // Explicitly track download request status. + + var limitedCommerce = false; + var commerceMode = false; + var userIsLoggedIn = false; + var walletNeedsSetup = false; + var marketplaceBaseURL = "https://highfidelity.com"; + var messagesWaiting = false; + + function injectCommonCode(isDirectoryPage) { + // Supporting styles from marketplaces.css. + // Glyph font family, size, and spacing adjusted because HiFi-Glyphs cannot be used cross-domain. + $("head").append( + '' + ); + + // Supporting styles from edit-style.css. + // Font family, size, and position adjusted because Raleway-Bold cannot be used cross-domain. + $("head").append( + '' + ); + + // Footer. + var isInitialHiFiPage = location.href === (marketplaceBaseURL + "/marketplace?"); + $("body").append( + '
' + + (!isInitialHiFiPage ? '' : '') + + (isInitialHiFiPage ? '🛈 Get items from Clara.io!' : '') + + (!isDirectoryPage ? '' : '') + + (isDirectoryPage ? '🛈 Select a marketplace to explore.' : '') + + '
' + ); + + // Footer actions. + $("#back-button").on("click", function () { + if (document.referrer !== "") { + window.history.back(); + } else { + var params = { type: GOTO_MARKETPLACE }; + var itemIdMatch = location.search.match(/itemId=([^&]*)/); + if (itemIdMatch && itemIdMatch.length === 2) { + params.itemId = itemIdMatch[1]; + } + EventBridge.emitWebEvent(JSON.stringify(params)); + } + }); + $("#all-markets").on("click", function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: GOTO_DIRECTORY + })); + }); + } + + function injectDirectoryCode() { + + // Remove e-mail hyperlink. + var letUsKnow = $("#letUsKnow"); + letUsKnow.replaceWith(letUsKnow.html()); + + // Add button links. + + $('#exploreClaraMarketplace').on('click', function () { + window.location = "https://clara.io/library?gameCheck=true&public=true"; + }); + $('#exploreHifiMarketplace').on('click', function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: GOTO_MARKETPLACE + })); + }); + } + + emitWalletSetupEvent = function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: "WALLET_SETUP" + })); + }; + + function maybeAddSetupWalletButton() { + if (!$('body').hasClass("walletsetup-injected") && userIsLoggedIn && walletNeedsSetup) { + $('body').addClass("walletsetup-injected"); + + var resultsElement = document.getElementById('results'); + var setupWalletElement = document.createElement('div'); + setupWalletElement.classList.add("row"); + setupWalletElement.id = "setupWalletDiv"; + setupWalletElement.style = "height:60px;margin:20px 10px 10px 10px;padding:12px 5px;" + + "background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;"; + + var span = document.createElement('span'); + span.style = "margin:10px 5px;color:#1b6420;font-size:15px;"; + span.innerHTML = "Activate your Wallet to get money and shop in Marketplace."; + + var xButton = document.createElement('a'); + xButton.id = "xButton"; + xButton.setAttribute('href', "#"); + xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;"; + xButton.innerHTML = "X"; + xButton.onclick = function () { + setupWalletElement.remove(); + dummyRow.remove(); + }; + + setupWalletElement.appendChild(span); + setupWalletElement.appendChild(xButton); + + resultsElement.insertBefore(setupWalletElement, resultsElement.firstChild); + + // Dummy row for padding + var dummyRow = document.createElement('div'); + dummyRow.classList.add("row"); + dummyRow.style = "height:15px;"; + resultsElement.insertBefore(dummyRow, resultsElement.firstChild); + } + } + + function maybeAddLogInButton() { + if (!$('body').hasClass("login-injected") && !userIsLoggedIn) { + $('body').addClass("login-injected"); + var resultsElement = document.getElementById('results'); + if (!resultsElement) { // If we're on the main page, this will evaluate to `true` + resultsElement = document.getElementById('item-show'); + resultsElement.style = 'margin-top:0;'; + } + var logInElement = document.createElement('div'); + logInElement.classList.add("row"); + logInElement.id = "logInDiv"; + logInElement.style = "height:60px;margin:20px 10px 10px 10px;padding:5px;" + + "background-color:#D6F4D8;border-color:#aee9b2;border-width:2px;border-style:solid;border-radius:5px;"; + + var button = document.createElement('a'); + button.classList.add("btn"); + button.classList.add("btn-default"); + button.id = "logInButton"; + button.setAttribute('href', "#"); + button.innerHTML = "LOG IN"; + button.style = "width:80px;height:100%;margin-top:0;margin-left:10px;padding:13px;font-weight:bold;background:linear-gradient(white, #ccc);"; + button.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: "LOGIN" + })); + }; + + var span = document.createElement('span'); + span.style = "margin:10px;color:#1b6420;font-size:15px;"; + span.innerHTML = "to get items from the Marketplace."; + + var xButton = document.createElement('a'); + xButton.id = "xButton"; + xButton.setAttribute('href', "#"); + xButton.style = "width:50px;height:100%;margin:0;color:#ccc;font-size:20px;"; + xButton.innerHTML = "X"; + xButton.onclick = function () { + logInElement.remove(); + dummyRow.remove(); + }; + + logInElement.appendChild(button); + logInElement.appendChild(span); + logInElement.appendChild(xButton); + + resultsElement.insertBefore(logInElement, resultsElement.firstChild); + + // Dummy row for padding + var dummyRow = document.createElement('div'); + dummyRow.classList.add("row"); + dummyRow.style = "height:15px;"; + resultsElement.insertBefore(dummyRow, resultsElement.firstChild); + } + } + + function changeDropdownMenu() { + var logInOrOutButton = document.createElement('a'); + logInOrOutButton.id = "logInOrOutButton"; + logInOrOutButton.setAttribute('href', "#"); + logInOrOutButton.innerHTML = userIsLoggedIn ? "Log Out" : "Log In"; + logInOrOutButton.onclick = function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: "LOGIN" + })); + }; + + $($('.dropdown-menu').find('li')[0]).append(logInOrOutButton); + + $('a[href="/marketplace?view=mine"]').each(function () { + $(this).attr('href', '#'); + $(this).on('click', function () { + EventBridge.emitWebEvent(JSON.stringify({ + type: "MY_ITEMS" + })); + }); + }); + } + + function buyButtonClicked(id, referrer, edition) { + EventBridge.emitWebEvent(JSON.stringify({ + type: "CHECKOUT", + itemId: id, + referrer: referrer, + itemEdition: edition + })); + } + + function injectBuyButtonOnMainPage() { + var cost; + + // Unbind original mouseenter and mouseleave behavior + $('body').off('mouseenter', '#price-or-edit .price'); + $('body').off('mouseleave', '#price-or-edit .price'); + + $('.grid-item').find('#price-or-edit').each(function () { + $(this).css({ "margin-top": "0" }); + }); + + $('.grid-item').find('#price-or-edit').find('a').each(function() { + if ($(this).attr('href') !== '#') { // Guard necessary because of the AJAX nature of Marketplace site + $(this).attr('data-href', $(this).attr('href')); + $(this).attr('href', '#'); + } + cost = $(this).closest('.col-xs-3').find('.item-cost').text(); + var costInt = parseInt(cost, 10); + + $(this).closest('.col-xs-3').prev().attr("class", 'col-xs-6'); + $(this).closest('.col-xs-3').attr("class", 'col-xs-6'); + + var priceElement = $(this).find('.price'); + var available = true; + + if (priceElement.text() === 'invalidated' || + priceElement.text() === 'sold out' || + priceElement.text() === 'not for sale') { + available = false; + priceElement.css({ + "padding": "3px 5px 10px 5px", + "height": "40px", + "background": "linear-gradient(#a2a2a2, #fefefe)", + "color": "#000", + "font-weight": "600", + "line-height": "34px" + }); + } else { + priceElement.css({ + "padding": "3px 5px", + "height": "40px", + "background": "linear-gradient(#00b4ef, #0093C5)", + "color": "#FFF", + "font-weight": "600", + "line-height": "34px" + }); + } + + if (parseInt(cost) > 0) { + priceElement.css({ "width": "auto" }); + + if (available) { + priceElement.html(' ' + cost); + } + + priceElement.css({ "min-width": priceElement.width() + 30 }); + } + }); + + // change pricing to GET/BUY on button hover + $('body').on('mouseenter', '#price-or-edit .price', function () { + var $this = $(this); + var buyString = "BUY"; + var getString = "GET"; + // Protection against the button getting stuck in the "BUY"/"GET" state. + // That happens when the browser gets two MOUSEENTER events before getting a + // MOUSELEAVE event. Also, if not available for sale, just return. + if ($this.text() === buyString || + $this.text() === getString || + $this.text() === 'invalidated' || + $this.text() === 'sold out' || + $this.text() === 'not for sale' ) { + return; + } + $this.data('initialHtml', $this.html()); + + var cost = $(this).parent().siblings().text(); + if (parseInt(cost) > 0) { + $this.text(buyString); + } + if (parseInt(cost) == 0) { + $this.text(getString); + } + }); + + $('body').on('mouseleave', '#price-or-edit .price', function () { + var $this = $(this); + $this.html($this.data('initialHtml')); + }); + + + $('.grid-item').find('#price-or-edit').find('a').on('click', function () { + var price = $(this).closest('.grid-item').find('.price').text(); + if (price === 'invalidated' || + price === 'sold out' || + price === 'not for sale') { + return false; + } + buyButtonClicked($(this).closest('.grid-item').attr('data-item-id'), + "mainPage", + -1); + }); + } + + function injectUnfocusOnSearch() { + // unfocus input field on search, thus hiding virtual keyboard + $('#search-box').on('submit', function () { + if (document.activeElement) { + document.activeElement.blur(); + } + }); + } + + // fix for 10108 - marketplace category cannot scroll + function injectAddScrollbarToCategories() { + $('#categories-dropdown').on('show.bs.dropdown', function () { + $('body > div.container').css('display', 'none') + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': 'auto', 'height': 'calc(100vh - 110px)' }); + }); + + $('#categories-dropdown').on('hide.bs.dropdown', function () { + $('body > div.container').css('display', ''); + $('#categories-dropdown > ul.dropdown-menu').css({ 'overflow': '', 'height': '' }); + }); + } + + function injectHiFiCode() { + if (commerceMode) { + maybeAddLogInButton(); + maybeAddSetupWalletButton(); + + if (!$('body').hasClass("code-injected")) { + + $('body').addClass("code-injected"); + changeDropdownMenu(); + + var target = document.getElementById('templated-items'); + // MutationObserver is necessary because the DOM is populated after the page is loaded. + // We're searching for changes to the element whose ID is '#templated-items' - this is + // the element that gets filled in by the AJAX. + var observer = new MutationObserver(function (mutations) { + mutations.forEach(function (mutation) { + injectBuyButtonOnMainPage(); + }); + }); + var config = { attributes: true, childList: true, characterData: true }; + observer.observe(target, config); + + // Try this here in case it works (it will if the user just pressed the "back" button, + // since that doesn't trigger another AJAX request. + injectBuyButtonOnMainPage(); + } + } + + injectUnfocusOnSearch(); + injectAddScrollbarToCategories(); + } + + function injectHiFiItemPageCode() { + if (commerceMode) { + maybeAddLogInButton(); + + if (!$('body').hasClass("code-injected")) { + + $('body').addClass("code-injected"); + changeDropdownMenu(); + + var purchaseButton = $('#side-info').find('.btn').first(); + + var href = purchaseButton.attr('href'); + purchaseButton.attr('href', '#'); + var cost = $('.item-cost').text(); + var costInt = parseInt(cost, 10); + var availability = $.trim($('.item-availability').text()); + if (limitedCommerce && (costInt > 0)) { + availability = ''; + } + if (availability === 'available') { + purchaseButton.css({ + "background": "linear-gradient(#00b4ef, #0093C5)", + "color": "#FFF", + "font-weight": "600", + "padding-bottom": "10px" + }); + } else { + purchaseButton.css({ + "background": "linear-gradient(#a2a2a2, #fefefe)", + "color": "#000", + "font-weight": "600", + "padding-bottom": "10px" + }); + } + + var type = $('.item-type').text(); + var isUpdating = window.location.href.indexOf('edition=') > -1; + var urlParams = new URLSearchParams(window.location.search); + if (isUpdating) { + purchaseButton.html('UPDATE FOR FREE'); + } else if (availability !== 'available') { + purchaseButton.html('UNAVAILABLE ' + (availability ? ('(' + availability + ')') : '')); + } else if (parseInt(cost) > 0 && $('#side-info').find('#buyItemButton').size() === 0) { + purchaseButton.html('PURCHASE ' + cost); + } + + purchaseButton.on('click', function () { + if ('available' === availability || isUpdating) { + buyButtonClicked(window.location.pathname.split("/")[3], + "itemPage", + urlParams.get('edition')); + } + }); + } + } + + injectUnfocusOnSearch(); + } + + function updateClaraCode() { + // Have to repeatedly update Clara page because its content can change dynamically without location.href changing. + + // Clara library page. + if (location.href.indexOf("clara.io/library") !== -1) { + // Make entries navigate to "Image" view instead of default "Real Time" view. + var elements = $("a.thumbnail"); + for (var i = 0, length = elements.length; i < length; i++) { + var value = elements[i].getAttribute("href"); + if (value.slice(-6) !== "/image") { + elements[i].setAttribute("href", value + "/image"); + } + } + } + + // Clara item page. + if (location.href.indexOf("clara.io/view/") !== -1) { + // Make site navigation links retain gameCheck etc. parameters. + var element = $("a[href^=\'/library\']")[0]; + var parameters = "?gameCheck=true&public=true"; + var href = element.getAttribute("href"); + if (href.slice(-parameters.length) !== parameters) { + element.setAttribute("href", href + parameters); + } + + // Remove unwanted buttons and replace download options with a single "Download to High Fidelity" button. + var buttons = $("a.embed-button").parent("div"); + var downloadFBX; + if (buttons.find("div.btn-group").length > 0) { + buttons.children(".btn-primary, .btn-group , .embed-button").each(function () { this.remove(); }); + if ($("#hifi-download-container").length === 0) { // Button hasn't been moved already. + downloadFBX = $(' Download to High Fidelity'); + buttons.prepend(downloadFBX); + downloadFBX[0].addEventListener("click", startAutoDownload); + } + } + + // Move the "Download to High Fidelity" button to be more visible on tablet. + if ($("#hifi-download-container").length === 0 && window.innerWidth < 700) { + var downloadContainer = $('
'); + $(".top-title .col-sm-4").append(downloadContainer); + downloadContainer.append(downloadFBX); + } + } + } + + // Automatic download to High Fidelity. + function startAutoDownload() { + // One file request at a time. + if (isPreparing) { + console.log("WARNING: Clara.io FBX: Prepare only one download at a time"); + return; + } + + // User must be able to write to Asset Server. + if (!canWriteAssets) { + console.log("ERROR: Clara.io FBX: File download cancelled because no permissions to write to Asset Server"); + EventBridge.emitWebEvent(JSON.stringify({ + type: WARN_USER_NO_PERMISSIONS + })); + return; + } + + // User must be logged in. + var loginButton = $("#topnav a[href='/signup']"); + if (loginButton.length > 0) { + loginButton[0].click(); + return; + } + + // Obtain zip file to download for requested asset. + // Reference: https://clara.io/learn/sdk/api/export + + //var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?zip=true¢erScene=true&alignSceneGround=true&fbxUnit=Meter&fbxVersion=7&fbxEmbedTextures=true&imageFormat=WebGL"; + // 13 Jan 2017: Specify FBX version 5 and remove some options in order to make Clara.io site more likely to + // be successful in generating zip files. + var XMLHTTPREQUEST_URL = "https://clara.io/api/scenes/{uuid}/export/fbx?fbxUnit=Meter&fbxVersion=5&fbxEmbedTextures=true&imageFormat=WebGL"; + + var uuid = location.href.match(/\/view\/([a-z0-9\-]*)/)[1]; + var url = XMLHTTPREQUEST_URL.replace("{uuid}", uuid); + + xmlHttpRequest = new XMLHttpRequest(); + var responseTextIndex = 0; + var zipFileURL = ""; + + xmlHttpRequest.onreadystatechange = function () { + // Messages are appended to responseText; process the new ones. + var message = this.responseText.slice(responseTextIndex); + var statusMessage = ""; + + if (isPreparing) { // Ignore messages in flight after finished/cancelled. + var lines = message.split(/[\n\r]+/); + + for (var i = 0, length = lines.length; i < length; i++) { + if (lines[i].slice(0, 5) === "data:") { + // Parse line. + var data; + try { + data = JSON.parse(lines[i].slice(5)); + } + catch (e) { + data = {}; + } + + // Extract zip file URL. + if (data.hasOwnProperty("files") && data.files.length > 0) { + zipFileURL = data.files[0].url; + } + } + } + + if (statusMessage !== "") { + // Update the UI with the most recent status message. + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: statusMessage + })); + } + } + + responseTextIndex = this.responseText.length; + }; + + // Note: onprogress doesn't have computable total length so can't use it to determine % complete. + + xmlHttpRequest.onload = function () { + var statusMessage = ""; + + if (!isPreparing) { + return; + } + + isPreparing = false; + + var HTTP_OK = 200; + if (this.status !== HTTP_OK) { + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: statusMessage + })); + } else if (zipFileURL.slice(-4) !== ".zip") { + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: (statusMessage + ": " + zipFileURL) + })); + } else { + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_DOWNLOAD + })); + } + + xmlHttpRequest = null; + } + + isPreparing = true; + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_STATUS, + status: "Initiating download" + })); + + xmlHttpRequest.open("POST", url, true); + xmlHttpRequest.setRequestHeader("Accept", "text/event-stream"); + xmlHttpRequest.send(); + } + + function injectClaraCode() { + + // Make space for marketplaces footer in Clara pages. + $("head").append( + '' + ); + + // Condense space. + $("head").append( + '' + ); + + // Move "Download to High Fidelity" button. + $("head").append( + '' + ); + + // Update code injected per page displayed. + var updateClaraCodeInterval = undefined; + updateClaraCode(); + updateClaraCodeInterval = setInterval(function () { + updateClaraCode(); + }, 1000); + + window.addEventListener("unload", function () { + clearInterval(updateClaraCodeInterval); + updateClaraCodeInterval = undefined; + }); + + EventBridge.emitWebEvent(JSON.stringify({ + type: QUERY_CAN_WRITE_ASSETS + })); + } + + function cancelClaraDownload() { + isPreparing = false; + + if (xmlHttpRequest) { + xmlHttpRequest.abort(); + xmlHttpRequest = null; + console.log("Clara.io FBX: File download cancelled"); + EventBridge.emitWebEvent(JSON.stringify({ + type: CLARA_IO_CANCELLED_DOWNLOAD + })); + } + } + + function injectCode() { + var DIRECTORY = 0; + var HIFI = 1; + var CLARA = 2; + var HIFI_ITEM_PAGE = 3; + var pageType = DIRECTORY; + + if (location.href.indexOf(marketplaceBaseURL + "/") !== -1) { pageType = HIFI; } + if (location.href.indexOf("clara.io/") !== -1) { pageType = CLARA; } + if (location.href.indexOf(marketplaceBaseURL + "/marketplace/items/") !== -1) { pageType = HIFI_ITEM_PAGE; } + + injectCommonCode(pageType === DIRECTORY); + switch (pageType) { + case DIRECTORY: + injectDirectoryCode(); + break; + case HIFI: + injectHiFiCode(); + break; + case CLARA: + injectClaraCode(); + break; + case HIFI_ITEM_PAGE: + injectHiFiItemPageCode(); + break; + + } + } + + function onLoad() { + EventBridge.scriptEventReceived.connect(function (message) { + message = JSON.parse(message); + if (message.type === CAN_WRITE_ASSETS) { + canWriteAssets = message.canWriteAssets; + } else if (message.type === CLARA_IO_CANCEL_DOWNLOAD) { + cancelClaraDownload(); + } else if (message.type === "marketplaces") { + if (message.action === "commerceSetting") { + limitedCommerce = !!message.data.limitedCommerce; + commerceMode = !!message.data.commerceMode; + userIsLoggedIn = !!message.data.userIsLoggedIn; + walletNeedsSetup = !!message.data.walletNeedsSetup; + marketplaceBaseURL = message.data.metaverseServerURL; + if (marketplaceBaseURL.indexOf('metaverse.') !== -1) { + marketplaceBaseURL = marketplaceBaseURL.replace('metaverse.', ''); + } + messagesWaiting = message.data.messagesWaiting; + injectCode(); + } + } + }); + + // Request commerce setting + // Code is injected into the webpage after the setting comes back. + EventBridge.emitWebEvent(JSON.stringify({ + type: "REQUEST_SETTING" + })); + } + + // Load / unload. + window.addEventListener("load", onLoad); // More robust to Web site issues than using $(document).ready(). + window.addEventListener("page:change", onLoad); // Triggered after Marketplace HTML is changed +}()); From 8aedc98a584870156998ba615bd935511621e147 Mon Sep 17 00:00:00 2001 From: r3tk0n Date: Mon, 11 Mar 2019 10:54:48 -0700 Subject: [PATCH 63/71] Fix QML formatting issue. --- interface/resources/qml/hifi/audio/Audio.qml | 57 ++++++++------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index ecc3297d9f..5e849acedf 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -171,15 +171,14 @@ Rectangle { } } - Separator {} - - - ColumnLayout { - id: pttColumn - spacing: 24; - x: 2 * margins.paddings; + Separator { id: pttStartSeparator; } + Item { + width: rightMostInputLevelPos; + height: pttSwitch.height + pttText.height + 24; HifiControlsUit.Switch { id: pttSwitch + x: 2 * margins.paddings; + anchors.top: parent.top; height: root.switchHeight; switchWidth: root.switchWidth; labelTextOn: qsTr("Push To Talk (T)"); @@ -200,39 +199,25 @@ Rectangle { }); // restore binding } } - Item { - id: pttTextContainer - width: rightMostInputLevelPos - height: pttTextMetrics.height - anchors.left: parent.left - anchors.leftMargin: -margins.padding - TextMetrics { - id: pttTextMetrics - text: pttText.text - font: pttText.font - } - RalewayRegular { - id: pttText - color: hifi.colors.white; - width: parent.width; - wrapMode: (bar.currentIndex === 0) ? Text.NoWrap : Text.WordWrap; - font.italic: true - size: 16; + RalewayRegular { + id: pttText + x: 2 * margins.paddings; + color: hifi.colors.white; + anchors.bottom: parent.bottom; + width: rightMostInputLevelPos; + height: paintedHeight; + wrapMode: Text.WordWrap; + font.italic: true + size: 16; - text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to unmute.") : - qsTr("Press and hold grip triggers on both of your controllers to unmute."); - onTextChanged: { - if (pttTextMetrics.width > pttTextContainer.width) { - pttTextContainer.height = Math.ceil(pttTextMetrics.width / pttTextContainer.width) * pttTextMetrics.height; - } else { - pttTextContainer.height = pttTextMetrics.height; - } - } - } + text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to unmute.") : + qsTr("Press and hold grip triggers on both of your controllers to unmute."); } } - Separator {} + Separator { + id: pttEndSeparator; + } Item { From 4371723145a2c30d0e57ee2a3613d88fdb4a706a Mon Sep 17 00:00:00 2001 From: danteruiz Date: Mon, 11 Mar 2019 11:16:53 -0700 Subject: [PATCH 64/71] fix soft entity popping --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 03c50008a0..643e5afb70 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -181,9 +181,11 @@ void RenderableModelEntityItem::updateModelBounds() { updateRenderItems = true; } - if (model->getScaleToFitDimensions() != getScaledDimensions() || - model->getRegistrationPoint() != getRegistrationPoint() || - !model->getIsScaledToFit()) { + bool overridingModelTransform = model->isOverridingModelTransformAndOffset(); + if (!overridingModelTransform && + (model->getScaleToFitDimensions() != getScaledDimensions() || + model->getRegistrationPoint() != getRegistrationPoint() || + !model->getIsScaledToFit())) { // The machinery for updateModelBounds will give existing models the opportunity to fix their // translation/rotation/scale/registration. The first two are straightforward, but the latter two // have guards to make sure they don't happen after they've already been set. Here we reset those guards. From 2fb5e1ebc263efc96dedbe4fbf0ca7eba30f7c2d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Mar 2019 12:36:29 -0700 Subject: [PATCH 65/71] quiet some logging --- scripts/system/miniTablet.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/system/miniTablet.js b/scripts/system/miniTablet.js index 449921514c..91c8b1edcf 100644 --- a/scripts/system/miniTablet.js +++ b/scripts/system/miniTablet.js @@ -1048,6 +1048,7 @@ // Track grabbed state and item. switch (message.action) { case "grab": + case "equip": grabbingHand = HAND_NAMES.indexOf(message.joint); grabbedItem = message.grabbedEntity; break; @@ -1056,7 +1057,7 @@ grabbedItem = null; break; default: - error("Unexpected grab message!"); + error("Unexpected grab message: " + JSON.stringify(message)); return; } @@ -1144,4 +1145,4 @@ setUp(); Script.scriptEnding.connect(tearDown); -}()); \ No newline at end of file +}()); From e515e9cc66d627c543e3e50d8983ffdb38936703 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 11 Mar 2019 12:38:54 -0700 Subject: [PATCH 66/71] fix cloneEntity function --- scripts/system/libraries/cloneEntityUtils.js | 15 ++++++--------- .../system/libraries/controllerDispatcherUtils.js | 4 +++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index e0f4aba84a..f789e19cd8 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -5,8 +5,8 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global entityIsCloneable:true, getGrabbableData:true, cloneEntity:true, propsAreCloneDynamic:true, Script, - propsAreCloneDynamic:true, Entities*/ +/* global entityIsCloneable:true, cloneEntity:true, propsAreCloneDynamic:true, Script, + propsAreCloneDynamic:true, Entities, Uuid */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -47,13 +47,10 @@ propsAreCloneDynamic = function(props) { }; cloneEntity = function(props) { - var entityToClone = props.id; - var props = Entities.getEntityProperties(entityToClone, ['certificateID', 'certificateType']) - var certificateID = props.certificateID; - // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits - // will now be handled by the server where the entity add will fail if limit reached - if (entityIsCloneable(props) && (!!certificateID || props.certificateType.indexOf('domainUnlimited') >= 0)) { - var cloneID = Entities.cloneEntity(entityToClone); + var entityIDToClone = props.id; + if (entityIsCloneable(props) && + (Uuid.isNull(props.certificateID) || props.certificateType.indexOf('domainUnlimited') >= 0)) { + var cloneID = Entities.cloneEntity(entityIDToClone); return cloneID; } return null; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index 385ed954b0..5cb95f625d 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -156,7 +156,9 @@ DISPATCHER_PROPERTIES = [ "grab.equippableIndicatorOffset", "userData", "avatarEntity", - "owningAvatarID" + "owningAvatarID", + "certificateID", + "certificateType" ]; // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step From 07ddd4e1dd1d716d78c9c29db8b6d6ec880d7005 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 11 Mar 2019 14:27:47 -0700 Subject: [PATCH 67/71] moving key press detection to JSON --- .../resources/controllers/keyboardMouse.json | 1 + interface/src/Application.cpp | 15 ++------------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/interface/resources/controllers/keyboardMouse.json b/interface/resources/controllers/keyboardMouse.json index 74c11203ef..9b3c711c63 100644 --- a/interface/resources/controllers/keyboardMouse.json +++ b/interface/resources/controllers/keyboardMouse.json @@ -5,6 +5,7 @@ { "from": "Keyboard.D", "when": ["Keyboard.RightMouseButton", "!Keyboard.Control"], "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.E", "when": "!Keyboard.Control", "to": "Actions.LATERAL_RIGHT" }, { "from": "Keyboard.Q", "when": "!Keyboard.Control", "to": "Actions.LATERAL_LEFT" }, + { "from": "Keyboard.T", "when": "!Keyboard.Control", "to": "Actions.TogglePushToTalk" }, { "comment" : "Mouse turn need to be small continuous increments", "from": { "makeAxis" : [ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3230419816..de4a6bb167 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1608,9 +1608,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo switch (actionEnum) { case Action::TOGGLE_PUSHTOTALK: if (state > 0.0f) { - audioScriptingInterface->setPushingToTalk(false); - } else if (state < 0.0f) { audioScriptingInterface->setPushingToTalk(true); + } else if (state <= 0.0f) { + audioScriptingInterface->setPushingToTalk(false); } break; @@ -4047,7 +4047,6 @@ void Application::keyPressEvent(QKeyEvent* event) { _keysPressed.insert(event->key(), *event); } - auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); _controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts // if one of our scripts have asked to capture this event, then stop processing it if (_controllerScriptingInterface->isKeyCaptured(event) || isInterstitialMode()) { @@ -4215,10 +4214,6 @@ void Application::keyPressEvent(QKeyEvent* event) { } break; - case Qt::Key_T: - audioScriptingInterface->setPushingToTalk(true); - break; - case Qt::Key_P: { if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) { AudioInjectorOptions options; @@ -4325,12 +4320,6 @@ void Application::keyReleaseEvent(QKeyEvent* event) { _keyboardMouseDevice->keyReleaseEvent(event); } - auto audioScriptingInterface = reinterpret_cast(DependencyManager::get().data()); - switch (event->key()) { - case Qt::Key_T: - audioScriptingInterface->setPushingToTalk(false); - break; - } } void Application::focusOutEvent(QFocusEvent* event) { From ca5ff3381b154d7466a6010e9b344e80cb94e407 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 11 Mar 2019 16:02:18 -0700 Subject: [PATCH 68/71] changing position of the mute warning setting --- interface/resources/qml/hifi/audio/Audio.qml | 54 ++++++++++---------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index a5138b3dd9..1a0457fd0a 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -125,22 +125,6 @@ Rectangle { } } - HifiControlsUit.Switch { - id: stereoInput; - height: root.switchHeight; - switchWidth: root.switchWidth; - labelTextOn: qsTr("Stereo input"); - backgroundOnColor: "#E3E3E3"; - checked: AudioScriptingInterface.isStereoInput; - onCheckedChanged: { - AudioScriptingInterface.isStereoInput = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding - } - } - } - - ColumnLayout { - spacing: 24; HifiControlsUit.Switch { height: root.switchHeight; switchWidth: root.switchWidth; @@ -152,6 +136,23 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding } } + } + + ColumnLayout { + spacing: 24; + HifiControlsUit.Switch { + id: warnMutedSwitch + height: root.switchHeight; + switchWidth: root.switchWidth; + labelTextOn: qsTr("Warn when muted"); + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.warnWhenMuted; + onClicked: { + AudioScriptingInterface.warnWhenMuted = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.warnWhenMuted; }); // restore binding + } + } + HifiControlsUit.Switch { id: audioLevelSwitch @@ -165,19 +166,20 @@ Rectangle { checked = Qt.binding(function() { return AvatarInputs.showAudioTools; }); // restore binding } } - } - RowLayout { - spacing: muteMic.spacing*2; - AudioControls.CheckBox { - spacing: muteMic.spacing - text: qsTr("Warn when muted"); - checked: AudioScriptingInterface.warnWhenMuted; - onClicked: { - AudioScriptingInterface.warnWhenMuted = checked; - checked = Qt.binding(function() { return AudioScriptingInterface.warnWhenMuted; }); // restore binding + HifiControlsUit.Switch { + id: stereoInput; + height: root.switchHeight; + switchWidth: root.switchWidth; + labelTextOn: qsTr("Stereo input"); + backgroundOnColor: "#E3E3E3"; + checked: AudioScriptingInterface.isStereoInput; + onCheckedChanged: { + AudioScriptingInterface.isStereoInput = checked; + checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding } } + } } From 80821e8b7e354ebd1d77a737b74c1e009e942538 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 11 Mar 2019 16:15:47 -0700 Subject: [PATCH 69/71] changing mic bar indicator when muted in PTT --- interface/resources/qml/hifi/audio/Audio.qml | 62 +++++++++---------- interface/resources/qml/hifi/audio/MicBar.qml | 6 +- 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml index 5e849acedf..faa4f1de2f 100644 --- a/interface/resources/qml/hifi/audio/Audio.qml +++ b/interface/resources/qml/hifi/audio/Audio.qml @@ -140,6 +140,29 @@ Rectangle { checked = Qt.binding(function() { return AudioScriptingInterface.isStereoInput; }); // restore binding } } + + HifiControlsUit.Switch { + id: pttSwitch + height: root.switchHeight; + switchWidth: root.switchWidth; + labelTextOn: qsTr("Push To Talk (T)"); + backgroundOnColor: "#E3E3E3"; + checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; + onCheckedChanged: { + if (bar.currentIndex === 0) { + AudioScriptingInterface.pushToTalkDesktop = checked; + } else { + AudioScriptingInterface.pushToTalkHMD = checked; + } + checked = Qt.binding(function() { + if (bar.currentIndex === 0) { + return AudioScriptingInterface.pushToTalkDesktop; + } else { + return AudioScriptingInterface.pushToTalkHMD; + } + }); // restore binding + } + } } ColumnLayout { @@ -171,53 +194,26 @@ Rectangle { } } - Separator { id: pttStartSeparator; } Item { + anchors.left: parent.left width: rightMostInputLevelPos; - height: pttSwitch.height + pttText.height + 24; - HifiControlsUit.Switch { - id: pttSwitch - x: 2 * margins.paddings; - anchors.top: parent.top; - height: root.switchHeight; - switchWidth: root.switchWidth; - labelTextOn: qsTr("Push To Talk (T)"); - backgroundOnColor: "#E3E3E3"; - checked: (bar.currentIndex === 0) ? AudioScriptingInterface.pushToTalkDesktop : AudioScriptingInterface.pushToTalkHMD; - onCheckedChanged: { - if (bar.currentIndex === 0) { - AudioScriptingInterface.pushToTalkDesktop = checked; - } else { - AudioScriptingInterface.pushToTalkHMD = checked; - } - checked = Qt.binding(function() { - if (bar.currentIndex === 0) { - return AudioScriptingInterface.pushToTalkDesktop; - } else { - return AudioScriptingInterface.pushToTalkHMD; - } - }); // restore binding - } - } + height: pttText.height; RalewayRegular { id: pttText - x: 2 * margins.paddings; + x: margins.paddings; color: hifi.colors.white; - anchors.bottom: parent.bottom; width: rightMostInputLevelPos; height: paintedHeight; wrapMode: Text.WordWrap; font.italic: true size: 16; - text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to unmute.") : - qsTr("Press and hold grip triggers on both of your controllers to unmute."); + text: (bar.currentIndex === 0) ? qsTr("Press and hold the button \"T\" to talk.") : + qsTr("Press and hold grip triggers on both of your controllers to talk."); } } - Separator { - id: pttEndSeparator; - } + Separator { } Item { diff --git a/interface/resources/qml/hifi/audio/MicBar.qml b/interface/resources/qml/hifi/audio/MicBar.qml index 491b9f9554..f51da9c381 100644 --- a/interface/resources/qml/hifi/audio/MicBar.qml +++ b/interface/resources/qml/hifi/audio/MicBar.qml @@ -159,7 +159,7 @@ Rectangle { color: parent.color; - text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? "MUTED PTT-(T)" : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); + text: (AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk) ? (HMD.active ? "MUTED PTT" : "MUTED PTT-(T)") : (AudioScriptingInterface.muted ? "MUTED" : "MUTE"); font.pointSize: 12; } @@ -169,7 +169,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50; height: 4; color: parent.color; } @@ -180,7 +180,7 @@ Rectangle { verticalCenter: parent.verticalCenter; } - width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? 25 : 50; + width: AudioScriptingInterface.pushToTalk && !AudioScriptingInterface.pushingToTalk ? (HMD.active ? 27 : 25) : 50; height: 4; color: parent.color; } From 3c5fd069595dec2595dd74fbf4e78dee30f5a0a7 Mon Sep 17 00:00:00 2001 From: Jason Najera <39922250+r3tk0n@users.noreply.github.com> Date: Mon, 11 Mar 2019 16:16:58 -0700 Subject: [PATCH 70/71] Remove useless comment. Comment was not helpful. --- interface/src/scripting/Audio.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/scripting/Audio.cpp b/interface/src/scripting/Audio.cpp index c4dfcffb61..6b4ad80231 100644 --- a/interface/src/scripting/Audio.cpp +++ b/interface/src/scripting/Audio.cpp @@ -40,7 +40,6 @@ Audio::Audio() : _devices(_contextIsHMD) { connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); - // when pushing to talk changed, handle it. connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk); enableNoiseReduction(enableNoiseReductionSetting.get()); onContextChanged(); From 84b177996b346deaf82712c4ff74f0ebfa8c608b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 11 Mar 2019 16:43:26 -0700 Subject: [PATCH 71/71] removing dead code --- .../controllers/controllerModules/pushToTalk.js | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/scripts/system/controllers/controllerModules/pushToTalk.js b/scripts/system/controllers/controllerModules/pushToTalk.js index 6b1bacc367..11335ba2f5 100644 --- a/scripts/system/controllers/controllerModules/pushToTalk.js +++ b/scripts/system/controllers/controllerModules/pushToTalk.js @@ -11,17 +11,10 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); Script.include("/~/system/libraries/controllers.js"); -(function () { // BEGIN LOCAL_SCOPE +(function() { // BEGIN LOCAL_SCOPE function PushToTalkHandler() { var _this = this; this.active = false; - //var pttMapping, mappingName; - - this.setup = function () { - //mappingName = 'Hifi-PTT-Dev-' + Math.random(); - //pttMapping = Controller.newMapping(mappingName); - //pttMapping.enable(); - }; this.shouldTalk = function (controllerData) { // Set up test against controllerData here... @@ -53,10 +46,6 @@ Script.include("/~/system/libraries/controllers.js"); return makeRunningValues(true, [], []); }; - this.cleanup = function () { - //pttMapping.disable(); - }; - this.parameters = makeDispatcherModuleParameters( 950, ["head"], @@ -68,9 +57,8 @@ Script.include("/~/system/libraries/controllers.js"); enableDispatcherModule("PushToTalk", pushToTalk); function cleanup() { - pushToTalk.cleanup(); disableDispatcherModule("PushToTalk"); }; Script.scriptEnding.connect(cleanup); -}()); // END LOCAL_SCOPE +}()); // END LOCAL_SCOPE