Merge branch 'tablet-ui' of github.com:highfidelity/hifi into tablet-notifcation

This commit is contained in:
Dante Ruiz 2017-03-15 19:27:11 +01:00
commit dac16af1df
37 changed files with 1759 additions and 340 deletions

View file

@ -37,7 +37,6 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer";
// FIXME - what we'd actually like to do is send to users at ~50% of their present rate down to 30hz. Assume 90 for now.
const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45;
const unsigned int AVATAR_DATA_SEND_INTERVAL_MSECS = (1.0f / (float) AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) * 1000;
AvatarMixer::AvatarMixer(ReceivedMessage& message) :
ThreadedAssignment(message)

View file

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<g>
<path class="st0" d="M25.3,23.7c-0.6,0-1.2,0.2-1.6,0.7S23,25.4,23,26c0,0.6,0.2,1.1,0.7,1.6c0.5,0.5,1,0.7,1.6,0.7
c0.6,0,1.2-0.2,1.6-0.7c0.5-0.5,0.7-1,0.7-1.6c0-0.6-0.2-1.2-0.7-1.6C26.5,23.9,26,23.7,25.3,23.7z"/>
<path class="st0" d="M25.3,17.2c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21.2,30.3,17.2,25.3,17.2z M31.5,26c0,0.3-0.1,0.6-0.3,0.8
c-0.2,0.2-0.5,0.3-0.8,0.3c-0.2,0-0.3,0-0.5-0.1c-0.1,0-0.3,0-0.5-0.1c-0.2,0.6-0.3,1.1-0.6,1.4c0.2,0.1,0.3,0.2,0.4,0.3h0.1
c0.2,0.1,0.4,0.2,0.4,0.2c0.5,0.5,0.5,1.1,0,1.6c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0-0.6-0.1-0.8-0.3c0,0-0.1-0.2-0.2-0.4
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.4c-0.4,0.3-0.9,0.5-1.4,0.6c0.1,0.2,0.1,0.4,0.1,0.5c0.1,0.2,0.1,0.3,0.1,0.5
c0,0.3-0.1,0.6-0.3,0.8c-0.2,0.2-0.5,0.3-0.8,0.3c-0.3,0-0.6-0.1-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.2,0-0.3,0.1-0.5
c0-0.1,0-0.3,0.1-0.5c-0.5-0.1-1-0.3-1.4-0.6c-0.1,0.2-0.2,0.3-0.2,0.4L22.8,30c0,0.1-0.1,0.2-0.2,0.4c-0.2,0.2-0.5,0.3-0.8,0.3
c-0.3,0-0.6-0.1-0.8-0.3c-0.5-0.5-0.5-1.1,0-1.6c0.1-0.1,0.2-0.2,0.5-0.2c0-0.1,0.2-0.2,0.4-0.3c-0.2-0.3-0.4-0.8-0.6-1.4
C21.1,27,20.9,27,20.7,27V27c-0.1,0.1-0.2,0.1-0.5,0.1c-0.3,0-0.6-0.1-0.8-0.3c-0.2-0.2-0.3-0.5-0.3-0.8c0-0.3,0.1-0.6,0.3-0.8
c0.2-0.2,0.5-0.3,0.8-0.3c0.2,0,0.3,0,0.5,0.1c0.1,0,0.3,0,0.5,0.1c0.1-0.5,0.3-1,0.6-1.4c-0.1,0-0.2-0.1-0.4-0.2h-0.1
c-0.2-0.1-0.3-0.2-0.4-0.3c-0.5-0.5-0.5-1,0-1.5c0.5-0.5,1.1-0.5,1.6,0c0.1,0.1,0.2,0.2,0.2,0.4c0.1,0.1,0.2,0.2,0.3,0.5
c0.4-0.3,0.9-0.5,1.4-0.6c-0.1-0.2-0.1-0.3-0.1-0.5v-0.1c-0.1-0.2-0.1-0.3-0.1-0.5c0-0.3,0.1-0.6,0.3-0.8c0.2-0.2,0.5-0.3,0.8-0.3
c0.3,0,0.6,0.1,0.8,0.3c0.2,0.2,0.3,0.5,0.3,0.8c0,0.2,0,0.3-0.1,0.5v0.1c0,0.2,0,0.3-0.1,0.5c0.4,0.1,0.9,0.3,1.4,0.6
c0-0.2,0.1-0.3,0.2-0.5c0-0.1,0.1-0.2,0.3-0.4c0.2-0.2,0.4-0.3,0.8-0.3c0.3,0,0.6,0.1,0.8,0.3c0.5,0.5,0.5,1.1,0,1.6
c-0.2,0.2-0.3,0.2-0.4,0.3c-0.1,0.1-0.2,0.2-0.5,0.2c0.3,0.5,0.5,1,0.6,1.4c0.2-0.1,0.3-0.1,0.5-0.1H30c0.2-0.1,0.3-0.1,0.5-0.1
c0.3,0,0.6,0.1,0.8,0.3C31.4,25.5,31.5,25.7,31.5,26L31.5,26z"/>
</g>
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
S48.6,24.9,48,24.9z"/>
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
<rect x="12" y="24.6" class="st0" width="6.9" height="3"/>
<rect x="32.9" y="24.6" class="st0" width="5.1" height="3"/>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
S48.6,24.9,48,24.9z"/>
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
<rect x="12" y="24.6" class="st0" width="7.3" height="3"/>
<rect x="32.9" y="24.6" class="st0" width="5.3" height="3"/>
<path class="st0" d="M25.3,17c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21,30.3,17,25.3,17z M24.1,29.7c0,0.5-0.6,1-1.4,1
s-1.4-0.4-1.4-1v-7.3c0-0.5,0.6-1,1.4-1s1.4,0.4,1.4,1V29.7z M29.3,29.7c0,0.5-0.6,1-1.4,1c-0.8,0-1.4-0.4-1.4-1v-7.3
c0-0.5,0.6-1,1.4-1c0.8,0,1.4,0.4,1.4,1V29.7z"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="Layer_2">
</g>
<path class="st0" d="M22.3,15.4v-2.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v3.5C20.7,15.9,21.5,15.6,22.3,15.4z"/>
<path class="st0" d="M25.3,15c0.4,0,0.8,0,1.1,0.1V8.6c0-0.6-0.5-1.2-1.2-1.2S24.1,8,24.1,8.6V15C24.5,15,24.9,15,25.3,15z"/>
<path class="st0" d="M30.6,16.3V3.6c0-0.6-0.5-1.2-1.2-1.2S28.3,3,28.3,3.6v11.7C29.1,15.6,29.9,15.9,30.6,16.3z"/>
<path class="st0" d="M48,24.9h-0.6v-2.1c0-0.6-0.5-1.2-1.2-1.2S45,22.2,45,22.9v2.1h-1.8V12c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2
v12.9H39v-9.6c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2V36c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-8.7h1.8v12.9
c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V27.3H45v2.9c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.9H48c0.6,0,1.2-0.5,1.2-1.2
S48.6,24.9,48,24.9z"/>
<path class="st0" d="M13.9,12c0-0.6-0.5-1.2-1.2-1.2c-0.6,0-1.2,0.5-1.2,1.2v12.9H9.7v-4.6c0-0.6-0.5-1.2-1.2-1.2
c-0.6,0-1.2,0.5-1.2,1.2v4.6H5.5v-2.1c0-0.6-0.5-1.2-1.2-1.2s-1.2,0.5-1.2,1.2v2.1H2.6c-0.6,0-1.2,0.5-1.2,1.2s0.5,1.2,1.2,1.2h0.6
v2.1c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-2.1h1.8v6.2c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2v-6.2h1.8v12.1
c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V12z"/>
<path class="st0" d="M28.3,37v9.8c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2V36.1C29.9,36.5,29.1,36.8,28.3,37z"/>
<path class="st0" d="M25.3,37.5c-0.4,0-0.8,0-1.2-0.1v4.5c0,0.6,0.5,1.2,1.2,1.2s1.2-0.5,1.2-1.2v-4.5
C26.1,37.4,25.7,37.5,25.3,37.5z"/>
<path class="st0" d="M19.9,36.1v3.3c0,0.6,0.5,1.2,1.2,1.2c0.6,0,1.2-0.5,1.2-1.2V37C21.5,36.8,20.7,36.5,19.9,36.1z"/>
<path class="st0" d="M25.3,17.2c-5,0-9,4-9,9c0,5,4,9,9,9s9-4,9-9C34.4,21.2,30.3,17.2,25.3,17.2z M30.3,26.1l-6,5.4
c-0.1,0.1-0.2,0.1-0.4,0.1c-0.1,0-0.3,0-0.3-0.1c-0.3-0.1-0.5-0.4-0.5-0.5V20.9c0-0.1,0.2-0.4,0.4-0.5c0.1,0,0.2-0.1,0.3-0.1
c0.2,0,0.3,0,0.4,0.1l6,4.8c0.1,0.1,0.2,0.3,0.2,0.4C30.5,25.8,30.4,26,30.3,26.1z"/>
<rect x="12" y="24.6" class="st0" width="7.3" height="3"/>
<rect x="32.9" y="24.6" class="st0" width="7.3" height="3"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -181,6 +181,31 @@ Item {
root.avatarMixerOutPps + "pps, " +
root.myAvatarSendRate.toFixed(2) + "hz";
}
StatText {
visible: root.expanded;
text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " +
root.audioMixerInPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " +
"Silent: " + root.audioSilentInboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " +
root.audioMixerOutPps + "pps";
}
StatText {
visible: root.expanded;
text: "Audio Out Mic: " + root.audioMicOutboundPPS + " pps, " +
"Silent: " + root.audioSilentOutboundPPS + " pps";
}
StatText {
visible: root.expanded;
text: "Audio Codec: " + root.audioCodec + " Noise Gate: " +
root.audioNoiseGate;
}
StatText {
visible: root.expanded;
text: "Downloads: " + root.downloads + "/" + root.downloadLimit +

View file

@ -0,0 +1,244 @@
//
// TabletEntityStatistics.qml
//
// Created by Vlad Stelmahovsky on 3/11/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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
import "../../windows"
Rectangle {
id: root
objectName: "EntityStatistics"
property var eventBridge;
signal sendToScript(var message);
property bool isHMD: false
color: hifi.colors.baseGray
property int colorScheme: hifi.colorSchemes.dark
HifiConstants { id: hifi }
Component.onCompleted: {
OctreeStats.startUpdates()
}
Component.onDestruction: {
OctreeStats.stopUpdates()
}
Flickable {
id: scrollView
width: parent.width
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.bottomMargin: hifi.dimensions.tabletMenuHeader
contentWidth: column.implicitWidth
contentHeight: column.implicitHeight
boundsBehavior: Flickable.StopAtBounds
Column {
id: column
anchors.margins: 10
anchors.left: parent.left
anchors.right: parent.right
y: hifi.dimensions.tabletMenuHeader //-bgNavBar
spacing: 20
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Elements on Servers:")
text: OctreeStats.serverElements
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Local Elements:")
text: OctreeStats.localElements
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Elements Memory:")
text: OctreeStats.localElementsMemory
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Sending Mode:")
text: OctreeStats.sendingMode
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Incoming Entity Packets:")
text: OctreeStats.processedPackets
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Processed Packets Elements:")
text: OctreeStats.processedPacketsElements
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Processed Packets Entities:")
text: OctreeStats.processedPacketsEntities
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Processed Packets Timing:")
text: OctreeStats.processedPacketsTiming
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Outbound Entity Packets:")
text: OctreeStats.outboundEditPackets
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Entity Update Time:")
text: OctreeStats.entityUpdateTime
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
TabletEntityStatisticsItem {
anchors.left: parent.left
anchors.right: parent.right
titleText: qsTr("Entity Updates:")
text: OctreeStats.entityUpdates
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
Repeater {
model: OctreeStats.serversNum
delegate: Column {
id: serverColumn
width: scrollView.width - 10
x: 5
spacing: 5
state: "less"
TabletEntityStatisticsItem {
id: serverStats
width: parent.width
titleText: qsTr("Entity Server ") + (index+1) + ":"
colorScheme: root.colorScheme
color: OctreeStats.getColor()
}
Row {
id: buttonsRow
width: parent.width
height: 30
spacing: 10
HifiControls.Button {
id: moreButton
color: hifi.buttons.blue
colorScheme: root.colorScheme
width: parent.width / 2 - 10
height: 30
onClicked: {
if (serverColumn.state === "less") {
serverColumn.state = "more"
} else if (serverColumn.state === "more") {
serverColumn.state = "most"
} else {
serverColumn.state = "more"
}
}
}
HifiControls.Button {
id: mostButton
color: hifi.buttons.blue
colorScheme: root.colorScheme
height: 30
width: parent.width / 2 - 10
onClicked: {
if (serverColumn.state === "less") {
serverColumn.state = "most"
} else if (serverColumn.state === "more") {
serverColumn.state = "less"
} else {
serverColumn.state = "less"
}
}
}
}
states: [
State {
name: "less"
PropertyChanges { target: moreButton; text: qsTr("more..."); }
PropertyChanges { target: mostButton; text: qsTr("most..."); }
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3]; }
},
State {
name: "more"
PropertyChanges { target: moreButton; text: qsTr("most..."); }
PropertyChanges { target: mostButton; text: qsTr("less..."); }
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] +
OctreeStats.servers[index*3 + 1]; }
},
State {
name: "most"
PropertyChanges { target: moreButton; text: qsTr("less..."); }
PropertyChanges { target: mostButton; text: qsTr("least..."); }
PropertyChanges { target: serverStats; text: OctreeStats.servers[index*3] +
OctreeStats.servers[index*3 + 1] +
OctreeStats.servers[index*3 + 2]; }
}
]
} //servers column
} //repeater
} //column
} //flickable
}

View file

@ -0,0 +1,49 @@
//
// TabletEntityStatistics.qml
//
// Created by Vlad Stelmahovsky on 3/11/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
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import Qt.labs.settings 1.0
import "../../styles-uit"
import "../../controls-uit" as HifiControls
Column {
id: root
property int colorScheme: hifi.colorSchemes.dark
property alias titleText: titleLabel.text
property alias text: valueLabel.text
property alias color: valueLabel.color
HifiConstants { id: hifi }
anchors.left: parent.left
anchors.right: parent.right
spacing: 10
HifiControls.Label {
id: titleLabel
size: 20
anchors.left: parent.left
anchors.right: parent.right
colorScheme: root.colorScheme
}
RalewaySemiBold {
id: valueLabel
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.WordWrap
size: 16
color: enabled ? (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGray : hifi.colors.lightGrayText)
: (root.colorScheme == hifi.colorSchemes.light ? hifi.colors.lightGrayText : hifi.colors.baseGrayHighlight);
}
}

View file

@ -79,266 +79,297 @@ Rectangle {
scripts.stopAllScripts();
}
Column {
Flickable {
id: flickable
width: parent.width
HifiControls.TabletContentSection {
name: "Currently Running"
isFirst: true
height: parent.height - (keyboard.raised ? keyboard.raisedHeight : 0)
contentWidth: parent.width
contentHeight: column.childrenRect.height
clip: true
HifiControls.VerticalSpacer {}
Column {
id: column
width: parent.width
HifiControls.TabletContentSection {
id: firstSection
name: "Currently Running"
isFirst: true
Row {
spacing: hifi.dimensions.contentSpacing.x
HifiControls.VerticalSpacer {}
HifiControls.Button {
text: "Reload All"
color: hifi.buttons.black
onClicked: reloadAll()
}
Row {
spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button {
text: "Remove All"
color: hifi.buttons.red
onClicked: stopAll()
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.Table {
model: runningScriptsModel
id: table
height: 185
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.right: parent.right
expandSelectedRow: true
itemDelegate: Item {
anchors {
left: parent ? parent.left : undefined
leftMargin: hifi.dimensions.tablePadding
right: parent ? parent.right : undefined
rightMargin: hifi.dimensions.tablePadding
HifiControls.Button {
text: "Reload All"
color: hifi.buttons.black
onClicked: reloadAll()
}
FiraSansSemiBold {
id: textItem
text: styleData.value
size: hifi.fontSizes.tableText
color: table.colorScheme == hifi.colorSchemes.light
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
HifiControls.Button {
text: "Remove All"
color: hifi.buttons.red
onClicked: stopAll()
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.Table {
model: runningScriptsModel
id: table
height: 185
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.right: parent.right
expandSelectedRow: true
itemDelegate: Item {
anchors {
left: parent.left
right: parent.right
top: parent.top
topMargin: 3
left: parent ? parent.left : undefined
leftMargin: hifi.dimensions.tablePadding
right: parent ? parent.right : undefined
rightMargin: hifi.dimensions.tablePadding
}
HiFiGlyphs {
id: reloadButton
text: hifi.glyphs.reloadSmall
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
FiraSansSemiBold {
id: textItem
text: styleData.value
size: hifi.fontSizes.tableText
color: table.colorScheme == hifi.colorSchemes.light
? (styleData.selected ? hifi.colors.black : hifi.colors.baseGrayHighlight)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
anchors {
top: parent.top
right: stopButton.left
verticalCenter: parent.verticalCenter
}
MouseArea {
id: reloadButtonArea
anchors { fill: parent; margins: -2 }
onClicked: reloadScript(model.url)
}
}
HiFiGlyphs {
id: stopButton
text: hifi.glyphs.closeSmall
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
anchors {
top: parent.top
left: parent.left
right: parent.right
verticalCenter: parent.verticalCenter
top: parent.top
topMargin: 3
}
MouseArea {
id: stopButtonArea
anchors { fill: parent; margins: -2 }
onClicked: stopScript(model.url)
HiFiGlyphs {
id: reloadButton
text: hifi.glyphs.reloadSmall
color: reloadButtonArea.pressed ? hifi.colors.white : parent.color
anchors {
top: parent.top
right: stopButton.left
verticalCenter: parent.verticalCenter
}
MouseArea {
id: reloadButtonArea
anchors { fill: parent; margins: -2 }
onClicked: reloadScript(model.url)
}
}
HiFiGlyphs {
id: stopButton
text: hifi.glyphs.closeSmall
color: stopButtonArea.pressed ? hifi.colors.white : parent.color
anchors {
top: parent.top
right: parent.right
verticalCenter: parent.verticalCenter
}
MouseArea {
id: stopButtonArea
anchors { fill: parent; margins: -2 }
onClicked: stopScript(model.url)
}
}
}
FiraSansSemiBold {
text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : ""
elide: Text.ElideMiddle
size: hifi.fontSizes.tableText
color: table.colorScheme == hifi.colorSchemes.light
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
anchors {
top: textItem.bottom
left: parent.left
right: parent.right
}
visible: styleData.selected
}
}
TableViewColumn {
role: "name"
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
}
HifiControls.TabletContentSection {
name: "Load Scripts"
HifiControls.VerticalSpacer {}
Row {
spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button {
text: "from URL"
color: hifi.buttons.black
height: 26
onClicked: fromUrlTimer.running = true
// For some reason trigginer an API that enters
// an internal event loop directly from the button clicked
// trigger below causes the appliction to behave oddly.
// Most likely because the button onClicked handling is never
// completed until the function returns.
// FIXME find a better way of handling the input dialogs that
// doesn't trigger this.
Timer {
id: fromUrlTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadScriptURLDialog();
}
}
HifiControls.Button {
text: "from Disk"
color: hifi.buttons.black
height: 26
onClicked: fromDiskTimer.running = true
Timer {
id: fromDiskTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadDialog();
}
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {}
HifiControls.TextField {
id: filterEdit
isSearchField: true
anchors.left: parent.left
anchors.right: parent.right
colorScheme: hifi.colorSchemes.dark
placeholderText: "Filter"
onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i")
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
onActiveFocusChanged: {
// raise the keyboard
keyboard.raised = activeFocus;
// scroll to the bottom of the content area.
if (activeFocus) {
flickable.contentY = (flickable.contentHeight - flickable.height);
}
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.Tree {
id: treeView
height: 155
treeModel: scriptsModel
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.right: parent.right
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.TextField {
id: selectedScript
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x
colorScheme: hifi.colorSchemes.dark
readOnly: true
Connections {
target: treeView
onCurrentIndexChanged: {
var path = scriptsModel.data(treeView.currentIndex, 0x100)
if (path) {
selectedScript.text = path
} else {
selectedScript.text = ""
}
}
}
FiraSansSemiBold {
text: runningScriptsModel.get(styleData.row) ? runningScriptsModel.get(styleData.row).url : ""
elide: Text.ElideMiddle
size: hifi.fontSizes.tableText
color: table.colorScheme == hifi.colorSchemes.light
? (styleData.selected ? hifi.colors.black : hifi.colors.lightGray)
: (styleData.selected ? hifi.colors.black : hifi.colors.lightGrayText)
anchors {
top: textItem.bottom
left: parent.left
right: parent.right
}
visible: styleData.selected
}
}
TableViewColumn {
role: "name"
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
}
HifiControls.TabletContentSection {
name: "Load Scripts"
HifiControls.VerticalSpacer {}
Row {
spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button {
text: "from URL"
color: hifi.buttons.black
height: 26
onClicked: fromUrlTimer.running = true
// For some reason trigginer an API that enters
// an internal event loop directly from the button clicked
// trigger below causes the appliction to behave oddly.
// Most likely because the button onClicked handling is never
// completed until the function returns.
// FIXME find a better way of handling the input dialogs that
// doesn't trigger this.
Timer {
id: fromUrlTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadScriptURLDialog();
}
}
HifiControls.Button {
text: "from Disk"
color: hifi.buttons.black
height: 26
onClicked: fromDiskTimer.running = true
Timer {
id: fromDiskTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadDialog();
}
}
HifiControls.Button {
text: "Load Defaults"
color: hifi.buttons.black
height: 26
onClicked: loadDefaults()
}
}
HifiControls.VerticalSpacer {}
HifiControls.TextField {
id: filterEdit
isSearchField: true
anchors.left: parent.left
anchors.right: parent.right
colorScheme: hifi.colorSchemes.dark
placeholderText: "Filter"
onTextChanged: scriptsModel.filterRegExp = new RegExp("^.*" + text + ".*$", "i")
Component.onCompleted: scriptsModel.filterRegExp = new RegExp("^.*$", "i")
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.Tree {
id: treeView
height: 155
treeModel: scriptsModel
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
anchors.right: parent.right
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight + 2 // Add space for border
}
HifiControls.TextField {
id: selectedScript
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: loadButton.width + hifi.dimensions.contentSpacing.x
colorScheme: hifi.colorSchemes.dark
readOnly: true
Connections {
target: treeView
onCurrentIndexChanged: {
var path = scriptsModel.data(treeView.currentIndex, 0x100)
if (path) {
selectedScript.text = path
} else {
selectedScript.text = ""
}
}
}
}
Item {
// Take the loadButton out of the column flow.
id: loadButtonContainer
anchors.top: selectedScript.top
anchors.right: parent.right
HifiControls.Button {
id: loadButton
Item {
// Take the loadButton out of the column flow.
id: loadButtonContainer
anchors.top: selectedScript.top
anchors.right: parent.right
text: "Load"
color: hifi.buttons.blue
enabled: selectedScript.text != ""
onClicked: root.loadScript(selectedScript.text)
HifiControls.Button {
id: loadButton
anchors.right: parent.right
text: "Load"
color: hifi.buttons.blue
enabled: selectedScript.text != ""
onClicked: root.loadScript(selectedScript.text)
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0)
}
HifiControls.TextAction {
id: directoryButton
icon: hifi.glyphs.script
iconSize: 24
text: "Reveal Scripts Folder"
onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath)
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
visible: !isHMD
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight - 3
visible: !isHMD
}
}
}
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight - (!isHMD ? 3 : 0)
}
HifiControls.TextAction {
id: directoryButton
icon: hifi.glyphs.script
iconSize: 24
text: "Reveal Scripts Folder"
onClicked: fileDialogHelper.openDirectory(scripts.defaultScriptsPath)
colorScheme: hifi.colorSchemes.dark
anchors.left: parent.left
visible: !isHMD
}
HifiControls.VerticalSpacer {
height: hifi.dimensions.controlInterlineHeight - 3
visible: !isHMD
}
HifiControls.Keyboard {
id: keyboard
raised: false
numeric: false
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
}
}

View file

@ -17,7 +17,7 @@ Item {
id: root
anchors.fill: parent
objectName: "tabletMenuHandlerItem"
StackView {
anchors.fill: parent
id: d
@ -54,6 +54,10 @@ Item {
d.currentItem.focus = true;
d.currentItem.forceActiveFocus();
breadcrumbText.text = d.currentItem.title;
if (typeof bgNavBar !== "undefined") {
d.currentItem.y = bgNavBar.height;
d.currentItem.height -= bgNavBar.height;
}
}
function popSource() {

View file

@ -173,6 +173,7 @@
#include "ui/overlays/Overlays.h"
#include "Util.h"
#include "InterfaceParentFinder.h"
#include "ui/OctreeStatsProvider.h"
#include "FrameTimingsScriptingInterface.h"
#include <GPUIdent.h>
@ -523,6 +524,7 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<OffscreenQmlSurfaceCache>();
DependencyManager::set<EntityScriptClient>();
DependencyManager::set<EntityScriptServerLogClient>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
return previousSessionCrashed;
}
@ -550,7 +552,7 @@ const float DEFAULT_DESKTOP_TABLET_SCALE_PERCENT = 75.0f;
const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
const bool DEFAULT_TABLET_VISIBLE_TO_OTHERS = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = true;
Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bool runServer, QString runServerPathOption) :
QApplication(argc, argv),
@ -1188,6 +1190,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// set the local loopback interface for local sounds
AudioInjector::setLocalAudioInterface(audioIO.data());
AudioScriptingInterface::getInstance().setLocalAudioInterface(audioIO.data());
connect(audioIO.data(), &AudioClient::noiseGateOpened, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateOpened);
connect(audioIO.data(), &AudioClient::noiseGateClosed, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::noiseGateClosed);
connect(audioIO.data(), &AudioClient::inputReceived, &AudioScriptingInterface::getInstance(), &AudioScriptingInterface::inputReceived);
this->installEventFilter(this);
@ -1625,9 +1631,10 @@ QString Application::getUserAgent() {
void Application::toggleTabletUI() const {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto hmd = DependencyManager::get<HMDScriptingInterface>();
TabletProxy* tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
bool messageOpen = tablet->isMessageDialogOpen();
if (!messageOpen) {
if (!messageOpen || (messageOpen && !hmd->getShouldShowTablet())) {
auto HMD = DependencyManager::get<HMDScriptingInterface>();
HMD->toggleShouldShowTablet();
}
@ -1803,6 +1810,7 @@ Application::~Application() {
DependencyManager::destroy<GeometryCache>();
DependencyManager::destroy<ScriptCache>();
DependencyManager::destroy<SoundCache>();
DependencyManager::destroy<OctreeStatsProvider>();
ResourceManager::cleanup();
@ -1950,6 +1958,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("ApplicationInterface", this);
rootContext->setContextProperty("Audio", &AudioScriptingInterface::getInstance());
rootContext->setContextProperty("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
rootContext->setContextProperty("AudioScope", DependencyManager::get<AudioScope>().data());
rootContext->setContextProperty("Controller", DependencyManager::get<controller::ScriptingInterface>().data());
rootContext->setContextProperty("Entities", DependencyManager::get<EntityScriptingInterface>().data());
_fileDownload = new FileScriptingInterface(engine);
@ -3178,7 +3188,23 @@ void Application::mousePressEvent(QMouseEvent* event) {
}
}
void Application::mouseDoublePressEvent(QMouseEvent* event) const {
void Application::mouseDoublePressEvent(QMouseEvent* event) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
auto eventPosition = getApplicationCompositor().getMouseEventPosition(event);
QPointF transformedPos = offscreenUi->mapToVirtualScreen(eventPosition, _glWidget);
QMouseEvent mappedEvent(event->type(),
transformedPos,
event->screenPos(), event->button(),
event->buttons(), event->modifiers());
if (!_aboutToQuit) {
getOverlays().mouseDoublePressEvent(&mappedEvent);
if (!_controllerScriptingInterface->areEntityClicksCaptured()) {
getEntities()->mouseDoublePressEvent(&mappedEvent);
}
}
// if one of our scripts have asked to capture this event, then stop processing it
if (_controllerScriptingInterface->isMouseCaptured()) {
return;
@ -5525,6 +5551,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
// Caches
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
@ -6375,6 +6402,18 @@ void Application::loadLODToolsDialog() {
}
void Application::loadEntityStatisticsDialog() {
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
if (tablet->getToolbarMode() || (!tablet->getTabletRoot() && !isHMDMode())) {
auto dialogsManager = DependencyManager::get<DialogsManager>();
dialogsManager->octreeStatsDetails();
} else {
tablet->pushOntoStack("../../hifi/dialogs/TabletEntityStatistics.qml");
}
}
void Application::toggleLogDialog() {
if (! _logDialog) {
_logDialog = new LogDialog(nullptr, getLogger());

View file

@ -404,6 +404,7 @@ public slots:
Q_INVOKABLE void toggleMuteAudio();
void loadLODToolsDialog();
void loadEntityStatisticsDialog();
private slots:
void showDesktop();
@ -497,7 +498,7 @@ private:
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseDoublePressEvent(QMouseEvent* event) const;
void mouseDoublePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void touchBeginEvent(QTouchEvent* event);

View file

@ -547,8 +547,10 @@ Menu::Menu() {
// Developer > Entities >>>
MenuWrapper* entitiesOptionsMenu = developerMenu->addMenu("Entities");
addActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::OctreeStats, 0,
dialogsManager.data(), SLOT(octreeStatsDetails()));
qApp, SLOT(loadEntityStatisticsDialog()));
addCheckableActionToQMenuAndActionHash(entitiesOptionsMenu, MenuOption::ShowRealtimeEntityStats);
// Developer > Network >>>

View file

@ -52,12 +52,14 @@ AudioScope::AudioScope() :
connect(audioIO.data(), &AudioClient::inputReceived, this, &AudioScope::addInputToScope);
}
void AudioScope::toggle() {
_isEnabled = !_isEnabled;
if (_isEnabled) {
allocateScope();
} else {
freeScope();
void AudioScope::setVisible(bool visible) {
if (_isEnabled != visible) {
_isEnabled = visible;
if (_isEnabled) {
allocateScope();
} else {
freeScope();
}
}
}

View file

@ -34,8 +34,14 @@ public:
void render(RenderArgs* renderArgs, int width, int height);
public slots:
void toggle();
void toggle() { setVisible(!_isEnabled); }
void setVisible(bool visible);
bool getVisible() const { return _isEnabled; }
void togglePause() { _isPaused = !_isPaused; }
void setPause(bool paused) { _isPaused = paused; }
bool getPause() { return _isPaused; }
void selectAudioScopeFiveFrames();
void selectAudioScopeTwentyFrames();
void selectAudioScopeFiftyFrames();
@ -74,7 +80,6 @@ private:
int _inputID;
int _outputLeftID;
int _outputRightD;
};
#endif // hifi_AudioScope_h

View file

@ -0,0 +1,377 @@
//
// OctreeStatsProvider.cpp
// interface/src/ui
//
// Created by Vlad Stelmahovsky on 3/12/17.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Application.h"
#include "../octree/OctreePacketProcessor.h"
#include "ui/OctreeStatsProvider.h"
OctreeStatsProvider::OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model) :
QObject(parent),
_model(model)
, _statCount(0)
, _averageUpdatesPerSecond(SAMPLES_PER_SECOND)
{
//schedule updates
connect(&_updateTimer, &QTimer::timeout, this, &OctreeStatsProvider::updateOctreeStatsData);
_updateTimer.setInterval(100);
//timer will be rescheduled on each new timeout
_updateTimer.setSingleShot(true);
}
/*
* Start updates statistics
*/
void OctreeStatsProvider::startUpdates() {
_updateTimer.start();
}
/*
* Stop updates statistics
*/
void OctreeStatsProvider::stopUpdates() {
_updateTimer.stop();
}
QColor OctreeStatsProvider::getColor() const {
static int statIndex = 1;
static quint32 rotatingColors[] = { GREENISH, YELLOWISH, GREYISH };
quint32 colorRGBA = rotatingColors[statIndex % (sizeof(rotatingColors)/sizeof(rotatingColors[0]))];
quint32 rgb = colorRGBA >> 8;
const quint32 colorpart1 = 0xfefefeu;
const quint32 colorpart2 = 0xf8f8f8;
rgb = ((rgb & colorpart1) >> 1) + ((rgb & colorpart2) >> 3);
statIndex++;
return QColor::fromRgb(rgb);
}
OctreeStatsProvider::~OctreeStatsProvider() {
_updateTimer.stop();
}
int OctreeStatsProvider::serversNum() const {
return m_serversNum;
}
void OctreeStatsProvider::updateOctreeStatsData() {
// Processed Entities Related stats
auto entities = qApp->getEntities();
auto entitiesTree = entities->getTree();
// Do this ever paint event... even if we don't update
auto totalTrackedEdits = entitiesTree->getTotalTrackedEdits();
const quint64 SAMPLING_WINDOW = USECS_PER_SECOND / SAMPLES_PER_SECOND;
quint64 now = usecTimestampNow();
quint64 sinceLastWindow = now - _lastWindowAt;
auto editsInLastWindow = totalTrackedEdits - _lastKnownTrackedEdits;
float sinceLastWindowInSeconds = (float)sinceLastWindow / (float)USECS_PER_SECOND;
float recentUpdatesPerSecond = (float)editsInLastWindow / sinceLastWindowInSeconds;
if (sinceLastWindow > SAMPLING_WINDOW) {
_averageUpdatesPerSecond.updateAverage(recentUpdatesPerSecond);
_lastWindowAt = now;
_lastKnownTrackedEdits = totalTrackedEdits;
}
// Only refresh our stats every once in a while, unless asked for realtime
quint64 REFRESH_AFTER = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 0 : USECS_PER_SECOND;
quint64 sinceLastRefresh = now - _lastRefresh;
if (sinceLastRefresh < REFRESH_AFTER) {
_updateTimer.start((REFRESH_AFTER - sinceLastRefresh)/1000);
return;
}
// Only refresh our stats every once in a while, unless asked for realtime
//if no realtime, then update once per second. Otherwise consider 60FPS update, ie 16ms interval
//int updateinterval = Menu::getInstance()->isOptionChecked(MenuOption::ShowRealtimeEntityStats) ? 16 : 1000;
_updateTimer.start(REFRESH_AFTER/1000);
const int FLOATING_POINT_PRECISION = 3;
m_localElementsMemory = QString("Elements RAM: %1MB").arg(OctreeElement::getTotalMemoryUsage() / 1000000.0f, 5, 'f', 4);
emit localElementsMemoryChanged(m_localElementsMemory);
// Local Elements
m_localElements = QString("Total: %1 / Internal: %2 / Leaves: %3").
arg(OctreeElement::getNodeCount()).
arg(OctreeElement::getInternalNodeCount()).
arg(OctreeElement::getLeafNodeCount());
emit localElementsChanged(m_localElements);
// iterate all the current octree stats, and list their sending modes, total their octree elements, etc...
int serverCount = 0;
int movingServerCount = 0;
unsigned long totalNodes = 0;
unsigned long totalInternal = 0;
unsigned long totalLeaves = 0;
m_sendingMode.clear();
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
sceneStats->withReadLock([&] {
for (NodeToOctreeSceneStatsIterator i = sceneStats->begin(); i != sceneStats->end(); i++) {
//const QUuid& uuid = i->first;
OctreeSceneStats& stats = i->second;
serverCount++;
// calculate server node totals
totalNodes += stats.getTotalElements();
totalInternal += stats.getTotalInternal();
totalLeaves += stats.getTotalLeaves();
// Sending mode
if (serverCount > 1) {
m_sendingMode += ",";
}
if (stats.isMoving()) {
m_sendingMode += "M";
movingServerCount++;
} else {
m_sendingMode += "S";
}
if (stats.isFullScene()) {
m_sendingMode += "F";
} else {
m_sendingMode += "p";
}
}
});
m_sendingMode += QString(" - %1 servers").arg(serverCount);
if (movingServerCount > 0) {
m_sendingMode += " <SCENE NOT STABLE> ";
} else {
m_sendingMode += " <SCENE STABLE> ";
}
emit sendingModeChanged(m_sendingMode);
// Server Elements
m_serverElements = QString("Total: %1 / Internal: %2 / Leaves: %3").
arg(totalNodes).arg(totalInternal).arg(totalLeaves);
emit serverElementsChanged(m_serverElements);
// Processed Packets Elements
auto averageElementsPerPacket = entities->getAverageElementsPerPacket();
auto averageEntitiesPerPacket = entities->getAverageEntitiesPerPacket();
auto averageElementsPerSecond = entities->getAverageElementsPerSecond();
auto averageEntitiesPerSecond = entities->getAverageEntitiesPerSecond();
auto averageWaitLockPerPacket = entities->getAverageWaitLockPerPacket();
auto averageUncompressPerPacket = entities->getAverageUncompressPerPacket();
auto averageReadBitstreamPerPacket = entities->getAverageReadBitstreamPerPacket();
const OctreePacketProcessor& entitiesPacketProcessor = qApp->getOctreePacketProcessor();
auto incomingPacketsDepth = entitiesPacketProcessor.packetsToProcessCount();
auto incomingPPS = entitiesPacketProcessor.getIncomingPPS();
auto processedPPS = entitiesPacketProcessor.getProcessedPPS();
auto treeProcessedPPS = entities->getAveragePacketsPerSecond();
m_processedPackets = QString("Queue Size: %1 Packets / Network IN: %2 PPS / Queue OUT: %3 PPS / Tree IN: %4 PPS")
.arg(incomingPacketsDepth)
.arg(incomingPPS, 5, 'f', FLOATING_POINT_PRECISION)
.arg(processedPPS, 5, 'f', FLOATING_POINT_PRECISION)
.arg(treeProcessedPPS, 5, 'f', FLOATING_POINT_PRECISION);
emit processedPacketsChanged(m_processedPackets);
m_processedPacketsElements = QString("%1 per packet / %2 per second")
.arg(averageElementsPerPacket, 5, 'f', FLOATING_POINT_PRECISION)
.arg(averageElementsPerSecond, 5, 'f', FLOATING_POINT_PRECISION);
emit processedPacketsElementsChanged(m_processedPacketsElements);
m_processedPacketsEntities = QString("%1 per packet / %2 per second")
.arg(averageEntitiesPerPacket, 5, 'f', FLOATING_POINT_PRECISION)
.arg(averageEntitiesPerSecond, 5, 'f', FLOATING_POINT_PRECISION);
emit processedPacketsEntitiesChanged(m_processedPacketsEntities);
m_processedPacketsTiming = QString("Lock Wait: %1 (usecs) / Uncompress: %2 (usecs) / Process: %3 (usecs)")
.arg(averageWaitLockPerPacket)
.arg(averageUncompressPerPacket)
.arg(averageReadBitstreamPerPacket);
emit processedPacketsTimingChanged(m_processedPacketsTiming);
auto entitiesEditPacketSender = qApp->getEntityEditPacketSender();
auto outboundPacketsDepth = entitiesEditPacketSender->packetsToSendCount();
auto outboundQueuedPPS = entitiesEditPacketSender->getLifetimePPSQueued();
auto outboundSentPPS = entitiesEditPacketSender->getLifetimePPS();
m_outboundEditPackets = QString("Queue Size: %1 packets / Queued IN: %2 PPS / Sent OUT: %3 PPS")
.arg(outboundPacketsDepth)
.arg(outboundQueuedPPS, 5, 'f', FLOATING_POINT_PRECISION)
.arg(outboundSentPPS, 5, 'f', FLOATING_POINT_PRECISION);
emit outboundEditPacketsChanged(m_outboundEditPackets);
// Entity Edits update time
auto averageEditDelta = entitiesTree->getAverageEditDeltas();
auto maxEditDelta = entitiesTree->getMaxEditDelta();
m_entityUpdateTime = QString("Average: %1 (usecs) / Max: %2 (usecs)")
.arg(averageEditDelta)
.arg(maxEditDelta);
emit entityUpdateTimeChanged(m_entityUpdateTime);
// Entity Edits
auto bytesPerEdit = entitiesTree->getAverageEditBytes();
auto updatesPerSecond = _averageUpdatesPerSecond.getAverage();
if (updatesPerSecond < 1) {
updatesPerSecond = 0; // we don't really care about small updates per second so suppress those
}
m_entityUpdates = QString("%1 updates per second / %2 total updates / Average Size: %3 bytes")
.arg(updatesPerSecond, 5, 'f', FLOATING_POINT_PRECISION)
.arg(totalTrackedEdits)
.arg(bytesPerEdit);
emit entityUpdatesChanged(m_entityUpdates);
updateOctreeServers();
}
void OctreeStatsProvider::updateOctreeServers() {
int serverCount = 0;
showOctreeServersOfType(serverCount, NodeType::EntityServer, "Entity",
qApp->getEntityServerJurisdictions());
if (m_serversNum != serverCount) {
m_serversNum = serverCount;
emit serversNumChanged(m_serversNum);
}
}
void OctreeStatsProvider::showOctreeServersOfType(int& serverCount, NodeType_t serverType, const char* serverTypeName,
NodeToJurisdictionMap& serverJurisdictions) {
m_servers.clear();
auto nodeList = DependencyManager::get<NodeList>();
nodeList->eachNode([&](const SharedNodePointer& node) {
// only send to the NodeTypes that are NodeType_t_VOXEL_SERVER
if (node->getType() == serverType) {
serverCount++;
QString lesserDetails;
QString moreDetails;
QString mostDetails;
if (node->getActiveSocket()) {
lesserDetails += "active ";
} else {
lesserDetails += "inactive ";
}
QUuid nodeUUID = node->getUUID();
// lookup our nodeUUID in the jurisdiction map, if it's missing then we're
// missing at least one jurisdiction
serverJurisdictions.withReadLock([&] {
if (serverJurisdictions.find(nodeUUID) == serverJurisdictions.end()) {
lesserDetails += " unknown jurisdiction ";
return;
}
const JurisdictionMap& map = serverJurisdictions[nodeUUID];
auto rootCode = map.getRootOctalCode();
if (rootCode) {
QString rootCodeHex = octalCodeToHexString(rootCode.get());
VoxelPositionSize rootDetails;
voxelDetailsForCode(rootCode.get(), rootDetails);
AACube serverBounds(glm::vec3(rootDetails.x, rootDetails.y, rootDetails.z), rootDetails.s);
lesserDetails += QString(" jurisdiction: %1 [%2, %3, %4: %5]")
.arg(rootCodeHex)
.arg(rootDetails.x)
.arg(rootDetails.y)
.arg(rootDetails.z)
.arg(rootDetails.s);
} else {
lesserDetails += " jurisdiction has no rootCode";
} // root code
});
// now lookup stats details for this server...
NodeToOctreeSceneStats* sceneStats = qApp->getOcteeSceneStats();
sceneStats->withReadLock([&] {
if (sceneStats->find(nodeUUID) != sceneStats->end()) {
OctreeSceneStats& stats = sceneStats->at(nodeUUID);
float lastFullEncode = stats.getLastFullTotalEncodeTime() / USECS_PER_MSEC;
float lastFullSend = stats.getLastFullElapsedTime() / USECS_PER_MSEC;
float lastFullSendInSeconds = stats.getLastFullElapsedTime() / USECS_PER_SECOND;
float lastFullPackets = stats.getLastFullTotalPackets();
float lastFullPPS = lastFullPackets;
if (lastFullSendInSeconds > 0) {
lastFullPPS = lastFullPackets / lastFullSendInSeconds;
}
mostDetails += QString("<br/><br/>Last Full Scene... Encode: %1 ms Send: %2 ms Packets: %3 Bytes: %4 Rate: %5 PPS")
.arg(lastFullEncode)
.arg(lastFullSend)
.arg(lastFullPackets)
.arg(stats.getLastFullTotalBytes())
.arg(lastFullPPS);
for (int i = 0; i < OctreeSceneStats::ITEM_COUNT; i++) {
OctreeSceneStats::Item item = (OctreeSceneStats::Item)(i);
OctreeSceneStats::ItemInfo& itemInfo = stats.getItemInfo(item);
mostDetails += QString("<br/> %1 %2")
.arg(itemInfo.caption).arg(stats.getItemValue(item));
}
moreDetails += "<br/>Node UUID: " +nodeUUID.toString() + " ";
moreDetails += QString("<br/>Elements: %1 total %2 internal %3 leaves ")
.arg(stats.getTotalElements())
.arg(stats.getTotalInternal())
.arg(stats.getTotalLeaves());
const SequenceNumberStats& seqStats = stats.getIncomingOctreeSequenceNumberStats();
qint64 clockSkewInUsecs = node->getClockSkewUsec();
qint64 clockSkewInMS = clockSkewInUsecs / (qint64)USECS_PER_MSEC;
moreDetails += QString("<br/>Incoming Packets: %1/ Lost: %2/ Recovered: %3")
.arg(stats.getIncomingPackets())
.arg(seqStats.getLost())
.arg(seqStats.getRecovered());
moreDetails += QString("<br/> Out of Order: %1/ Early: %2/ Late: %3/ Unreasonable: %4")
.arg(seqStats.getOutOfOrder())
.arg(seqStats.getEarly())
.arg(seqStats.getLate())
.arg(seqStats.getUnreasonable());
moreDetails += QString("<br/> Average Flight Time: %1 msecs")
.arg(stats.getIncomingFlightTimeAverage());
moreDetails += QString("<br/> Average Ping Time: %1 msecs")
.arg(node->getPingMs());
moreDetails += QString("<br/> Average Clock Skew: %1 msecs [%2]")
.arg(clockSkewInMS)
.arg(formatUsecTime(clockSkewInUsecs));
moreDetails += QString("<br/>Incoming Bytes: %1 Wasted Bytes: %2")
.arg(stats.getIncomingBytes())
.arg(stats.getIncomingWastedBytes());
}
});
m_servers.append(lesserDetails);
m_servers.append(moreDetails);
m_servers.append(mostDetails);
} // is VOXEL_SERVER
});
emit serversChanged(m_servers);
}

View file

@ -0,0 +1,154 @@
//
// OctreeStatsProvider.h
// interface/src/ui
//
// Created by Vlad Stelmahovsky on 3/12/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
//
#ifndef hifi_OctreeStatsProvider_h
#define hifi_OctreeStatsProvider_h
#include <OctreeSceneStats.h>
#include <QTimer>
#include <QColor>
#include "DependencyManager.h"
#define MAX_STATS 100
#define MAX_VOXEL_SERVERS 50
#define DEFAULT_COLOR 0
class OctreeStatsProvider : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
Q_PROPERTY(int serversNum READ serversNum NOTIFY serversNumChanged)
Q_PROPERTY(QString serverElements READ serverElements NOTIFY serverElementsChanged)
Q_PROPERTY(QString localElements READ localElements NOTIFY localElementsChanged)
Q_PROPERTY(QString localElementsMemory READ localElementsMemory NOTIFY localElementsMemoryChanged)
Q_PROPERTY(QString sendingMode READ sendingMode NOTIFY sendingModeChanged)
Q_PROPERTY(QString processedPackets READ processedPackets NOTIFY processedPacketsChanged)
Q_PROPERTY(QString processedPacketsElements READ processedPacketsElements NOTIFY processedPacketsElementsChanged)
Q_PROPERTY(QString processedPacketsEntities READ processedPacketsEntities NOTIFY processedPacketsEntitiesChanged)
Q_PROPERTY(QString processedPacketsTiming READ processedPacketsTiming NOTIFY processedPacketsTimingChanged)
Q_PROPERTY(QString outboundEditPackets READ outboundEditPackets NOTIFY outboundEditPacketsChanged)
Q_PROPERTY(QString entityUpdateTime READ entityUpdateTime NOTIFY entityUpdateTimeChanged)
Q_PROPERTY(QString entityUpdates READ entityUpdates NOTIFY entityUpdatesChanged)
Q_PROPERTY(QStringList servers READ servers NOTIFY serversChanged)
public:
OctreeStatsProvider(QObject* parent, NodeToOctreeSceneStats* model);
~OctreeStatsProvider();
int serversNum() const;
QString serverElements() const {
return m_serverElements;
}
QString localElements() const {
return m_localElements;
}
QString localElementsMemory() const {
return m_localElementsMemory;
}
QString sendingMode() const {
return m_sendingMode;
}
QString processedPackets() const {
return m_processedPackets;
}
QString processedPacketsElements() const {
return m_processedPacketsElements;
}
QString processedPacketsEntities() const {
return m_processedPacketsEntities;
}
QString processedPacketsTiming() const {
return m_processedPacketsTiming;
}
QString outboundEditPackets() const {
return m_outboundEditPackets;
}
QString entityUpdateTime() const {
return m_entityUpdateTime;
}
QString entityUpdates() const {
return m_entityUpdates;
}
QStringList servers() const {
return m_servers;
}
signals:
void serversNumChanged(int serversNum);
void serverElementsChanged(const QString &serverElements);
void localElementsChanged(const QString &localElements);
void sendingModeChanged(const QString &sendingMode);
void processedPacketsChanged(const QString &processedPackets);
void localElementsMemoryChanged(const QString &localElementsMemory);
void processedPacketsElementsChanged(const QString &processedPacketsElements);
void processedPacketsEntitiesChanged(const QString &processedPacketsEntities);
void processedPacketsTimingChanged(const QString &processedPacketsTiming);
void outboundEditPacketsChanged(const QString &outboundEditPackets);
void entityUpdateTimeChanged(const QString &entityUpdateTime);
void entityUpdatesChanged(const QString &entityUpdates);
void serversChanged(const QStringList &servers);
public slots:
void startUpdates();
void stopUpdates();
QColor getColor() const;
private slots:
void updateOctreeStatsData();
protected:
void updateOctreeServers();
void showOctreeServersOfType(int& serverNumber, NodeType_t serverType,
const char* serverTypeName, NodeToJurisdictionMap& serverJurisdictions);
private:
NodeToOctreeSceneStats* _model;
int _statCount;
const int SAMPLES_PER_SECOND = 10;
SimpleMovingAverage _averageUpdatesPerSecond;
quint64 _lastWindowAt = usecTimestampNow();
quint64 _lastKnownTrackedEdits = 0;
quint64 _lastRefresh = 0;
QTimer _updateTimer;
int m_serversNum {0};
QString m_serverElements;
QString m_localElements;
QString m_localElementsMemory;
QString m_sendingMode;
QString m_processedPackets;
QString m_processedPacketsElements;
QString m_processedPacketsEntities;
QString m_processedPacketsTiming;
QString m_outboundEditPackets;
QString m_entityUpdateTime;
QString m_entityUpdates;
QStringList m_servers;
};
#endif // hifi_OctreeStatsProvider_h

View file

@ -198,15 +198,16 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(avatarMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(avatarMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AvatarMixer)));
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
} else {
STAT_UPDATE(avatarMixerInKbps, -1);
STAT_UPDATE(avatarMixerInPps, -1);
STAT_UPDATE(avatarMixerOutKbps, -1);
STAT_UPDATE(avatarMixerOutPps, -1);
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
}
STAT_UPDATE(myAvatarSendRate, avatarManager->getMyAvatarSendRate());
SharedNodePointer audioMixerNode = nodeList->soloNodeOfType(NodeType::AudioMixer);
auto audioClient = DependencyManager::get<AudioClient>();
if (audioMixerNode || force) {
STAT_UPDATE(audioMixerKbps, roundf(
bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer) +
@ -214,10 +215,30 @@ void Stats::updateStats(bool force) {
STAT_UPDATE(audioMixerPps, roundf(
bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer) +
bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerInKbps, roundf(bandwidthRecorder->getAverageInputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerInPps, roundf(bandwidthRecorder->getAverageInputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutKbps, roundf(bandwidthRecorder->getAverageOutputKilobitsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMixerOutPps, roundf(bandwidthRecorder->getAverageOutputPacketsPerSecond(NodeType::AudioMixer)));
STAT_UPDATE(audioMicOutboundPPS, audioClient->getMicAudioOutboundPPS());
STAT_UPDATE(audioSilentOutboundPPS, audioClient->getSilentOutboundPPS());
STAT_UPDATE(audioAudioInboundPPS, audioClient->getAudioInboundPPS());
STAT_UPDATE(audioSilentInboundPPS, audioClient->getSilentInboundPPS());
} else {
STAT_UPDATE(audioMixerKbps, -1);
STAT_UPDATE(audioMixerPps, -1);
STAT_UPDATE(audioMixerInKbps, -1);
STAT_UPDATE(audioMixerInPps, -1);
STAT_UPDATE(audioMixerOutKbps, -1);
STAT_UPDATE(audioMixerOutPps, -1);
STAT_UPDATE(audioMicOutboundPPS, -1);
STAT_UPDATE(audioSilentOutboundPPS, -1);
STAT_UPDATE(audioAudioInboundPPS, -1);
STAT_UPDATE(audioSilentInboundPPS, -1);
}
STAT_UPDATE(audioCodec, audioClient->getSelectedAudioFormat());
STAT_UPDATE(audioNoiseGate, audioClient->getNoiseGateOpen() ? "Open" : "Closed");
auto loadingRequests = ResourceCache::getLoadingRequests();
STAT_UPDATE(downloads, loadingRequests.size());

View file

@ -70,8 +70,20 @@ class Stats : public QQuickItem {
STATS_PROPERTY(int, avatarMixerOutKbps, 0)
STATS_PROPERTY(int, avatarMixerOutPps, 0)
STATS_PROPERTY(float, myAvatarSendRate, 0)
STATS_PROPERTY(int, audioMixerInKbps, 0)
STATS_PROPERTY(int, audioMixerInPps, 0)
STATS_PROPERTY(int, audioMixerOutKbps, 0)
STATS_PROPERTY(int, audioMixerOutPps, 0)
STATS_PROPERTY(int, audioMixerKbps, 0)
STATS_PROPERTY(int, audioMixerPps, 0)
STATS_PROPERTY(int, audioMicOutboundPPS, 0)
STATS_PROPERTY(int, audioSilentOutboundPPS, 0)
STATS_PROPERTY(int, audioAudioInboundPPS, 0)
STATS_PROPERTY(int, audioSilentInboundPPS, 0)
STATS_PROPERTY(QString, audioCodec, QString())
STATS_PROPERTY(QString, audioNoiseGate, QString())
STATS_PROPERTY(int, downloads, 0)
STATS_PROPERTY(int, downloadLimit, 0)
STATS_PROPERTY(int, downloadsPending, 0)
@ -180,8 +192,19 @@ signals:
void avatarMixerOutKbpsChanged();
void avatarMixerOutPpsChanged();
void myAvatarSendRateChanged();
void audioMixerInKbpsChanged();
void audioMixerInPpsChanged();
void audioMixerOutKbpsChanged();
void audioMixerOutPpsChanged();
void audioMixerKbpsChanged();
void audioMixerPpsChanged();
void audioMicOutboundPPSChanged();
void audioSilentOutboundPPSChanged();
void audioAudioInboundPPSChanged();
void audioSilentInboundPPSChanged();
void audioCodecChanged();
void audioNoiseGateChanged();
void downloadsChanged();
void downloadLimitChanged();
void downloadsPendingChanged();

View file

@ -768,6 +768,26 @@ bool Overlays::mousePressEvent(QMouseEvent* event) {
return false;
}
bool Overlays::mouseDoublePressEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseDoublePressEvent");
PickRay ray = qApp->computePickRay(event->x(), event->y());
RayToOverlayIntersectionResult rayPickResult = findRayIntersectionForMouseEvent(ray);
if (rayPickResult.intersects) {
_currentClickingOnOverlayID = rayPickResult.overlayID;
// Only Web overlays can have focus.
auto thisOverlay = std::dynamic_pointer_cast<Web3DOverlay>(getOverlay(_currentClickingOnOverlayID));
if (thisOverlay) {
auto pointerEvent = calculatePointerEvent(thisOverlay, ray, rayPickResult, event, PointerEvent::Press);
emit mouseDoublePressOnOverlay(_currentClickingOnOverlayID, pointerEvent);
return true;
}
}
emit mouseDoublePressOffOverlay();
return false;
}
bool Overlays::mouseReleaseEvent(QMouseEvent* event) {
PerformanceTimer perfTimer("Overlays::mouseReleaseEvent");

View file

@ -101,6 +101,7 @@ public:
OverlayID addOverlay(Overlay::Pointer overlay);
bool mousePressEvent(QMouseEvent* event);
bool mouseDoublePressEvent(QMouseEvent* event);
bool mouseReleaseEvent(QMouseEvent* event);
bool mouseMoveEvent(QMouseEvent* event);
@ -300,9 +301,11 @@ signals:
void panelDeleted(OverlayID id);
void mousePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
void mouseDoublePressOnOverlay(OverlayID overlayID, const PointerEvent& event);
void mouseReleaseOnOverlay(OverlayID overlayID, const PointerEvent& event);
void mouseMoveOnOverlay(OverlayID overlayID, const PointerEvent& event);
void mousePressOffOverlay();
void mouseDoublePressOffOverlay();
void hoverEnterOverlay(OverlayID overlayID, const PointerEvent& event);
void hoverOverOverlay(OverlayID overlayID, const PointerEvent& event);

View file

@ -44,6 +44,7 @@
#include "avatar/AvatarManager.h"
#include "AudioClient.h"
#include "LODManager.h"
#include "ui/OctreeStatsProvider.h"
static const float DPI = 30.47f;
static const float INCHES_TO_METERS = 1.0f / 39.3701f;
@ -185,6 +186,7 @@ void Web3DOverlay::loadSourceURL() {
_webSurface->getRootContext()->setContextProperty("Tablet", DependencyManager::get<TabletScriptingInterface>().data());
_webSurface->getRootContext()->setContextProperty("Assets", DependencyManager::get<AssetMappingsScriptingInterface>().data());
_webSurface->getRootContext()->setContextProperty("LODManager", DependencyManager::get<LODManager>().data());
_webSurface->getRootContext()->setContextProperty("OctreeStats", DependencyManager::get<OctreeStatsProvider>().data());
_webSurface->getRootContext()->setContextProperty("pathToFonts", "../../");
tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface->getRootItem(), _webSurface.data());

View file

@ -608,6 +608,13 @@ void AudioClient::handleAudioEnvironmentDataPacket(QSharedPointer<ReceivedMessag
}
void AudioClient::handleAudioDataPacket(QSharedPointer<ReceivedMessage> message) {
if (message->getType() == PacketType::SilentAudioFrame) {
_silentInbound.increment();
} else {
_audioInbound.increment();
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
@ -1021,9 +1028,10 @@ void AudioClient::handleAudioInput() {
// if we performed the noise gate we can get values from it instead of enumerating the samples again
_lastInputLoudness = _inputGate.getLastLoudness();
if (_inputGate.clippedInLastFrame()) {
if (_inputGate.clippedInLastBlock()) {
_timeSinceLastClip = 0.0f;
}
} else {
float loudness = 0.0f;
@ -1041,6 +1049,12 @@ void AudioClient::handleAudioInput() {
emit inputReceived({ reinterpret_cast<char*>(networkAudioSamples), numNetworkBytes });
if (_inputGate.openedInLastBlock()) {
emit noiseGateOpened();
} else if (_inputGate.closedInLastBlock()) {
emit noiseGateClosed();
}
} else {
// our input loudness is 0, since we're muted
_lastInputLoudness = 0;
@ -1057,9 +1071,13 @@ void AudioClient::handleAudioInput() {
// the output from the input gate (eventually, this could be crossfaded)
// and allow the codec to properly encode down to silent/zero. If we still
// have _lastInputLoudness of 0 in our NEXT frame, we will send a silent packet
if (_lastInputLoudness == 0 && !_inputGate.closedInLastFrame()) {
if (_lastInputLoudness == 0 && !_inputGate.closedInLastBlock()) {
packetType = PacketType::SilentAudioFrame;
_silentOutbound.increment();
} else {
_micAudioOutbound.increment();
}
Transform audioTransform;
audioTransform.setTranslation(_positionGetter());
audioTransform.setRotation(_orientationGetter());
@ -1084,6 +1102,7 @@ void AudioClient::handleAudioInput() {
}
}
// FIXME - should this go through the noise gate and honor mute and echo?
void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
Transform audioTransform;
audioTransform.setTranslation(_positionGetter());
@ -1096,6 +1115,8 @@ void AudioClient::handleRecordedAudioInput(const QByteArray& audio) {
encodedBuffer = audio;
}
_micAudioOutbound.increment();
// FIXME check a flag to see if we should echo audio?
emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber,
audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale,

View file

@ -46,6 +46,8 @@
#include <AudioLimiter.h>
#include <AudioConstants.h>
#include <shared/RateCounter.h>
#include <plugins/CodecPlugin.h>
#include "AudioIOStats.h"
@ -121,6 +123,13 @@ public:
void negotiateAudioFormat();
void selectAudioFormat(const QString& selectedCodecName);
Q_INVOKABLE QString getSelectedAudioFormat() const { return _selectedCodecName; }
Q_INVOKABLE bool getNoiseGateOpen() const { return _inputGate.isOpen(); }
Q_INVOKABLE float getSilentOutboundPPS() const { return _silentOutbound.rate(); }
Q_INVOKABLE float getMicAudioOutboundPPS() const { return _micAudioOutbound.rate(); }
Q_INVOKABLE float getSilentInboundPPS() const { return _silentInbound.rate(); }
Q_INVOKABLE float getAudioInboundPPS() const { return _audioInbound.rate(); }
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
MixedProcessedAudioStream& getReceivedAudioStream() { return _receivedAudioStream; }
@ -218,6 +227,8 @@ signals:
void inputReceived(const QByteArray& inputSamples);
void outputBytesToNetwork(int numBytes);
void inputBytesFromNetwork(int numBytes);
void noiseGateOpened();
void noiseGateClosed();
void changeDevice(const QAudioDeviceInfo& outputDeviceInfo);
void deviceChanged();
@ -382,6 +393,11 @@ private:
Encoder* _encoder { nullptr }; // for outbound mic stream
QThread* _checkDevicesThread { nullptr };
RateCounter<> _silentOutbound;
RateCounter<> _micAudioOutbound;
RateCounter<> _silentInbound;
RateCounter<> _audioInbound;
};

View file

@ -19,16 +19,16 @@
const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f;
AudioNoiseGate::AudioNoiseGate() :
_inputFrameCounter(0),
_inputBlockCounter(0),
_lastLoudness(0.0f),
_quietestFrame(std::numeric_limits<float>::max()),
_loudestFrame(0.0f),
_didClipInLastFrame(false),
_quietestBlock(std::numeric_limits<float>::max()),
_loudestBlock(0.0f),
_didClipInLastBlock(false),
_dcOffset(0.0f),
_measuredFloor(0.0f),
_sampleCounter(0),
_isOpen(false),
_framesToClose(0)
_blocksToClose(0)
{
}
@ -37,7 +37,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
//
// DC Offset correction
//
// Measure the DC offset over a trailing number of frames, and remove it from the input signal.
// Measure the DC offset over a trailing number of blocks, and remove it from the input signal.
// This causes the noise background measurements and server muting to be more accurate. Many off-board
// ADC's have a noticeable DC offset.
//
@ -51,7 +51,7 @@ void AudioNoiseGate::removeDCOffset(int16_t* samples, int numSamples) {
// Update measured DC offset
measuredDcOffset /= numSamples;
if (_dcOffset == 0.0f) {
// On first frame, copy over measured offset
// On first block, copy over measured offset
_dcOffset = measuredDcOffset;
} else {
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
@ -69,13 +69,13 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
//
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
// Make this value lower for more sensitivity and less rejection of noise.
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
// NOISE_GATE_WIDTH: The number of samples in an audio block for which the height must be exceeded
// to open the gate.
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
// NOISE_GATE_CLOSE_BLOCK_DELAY: Once the noise is below the gate height for the block, how many blocks
// will we wait before closing the gate.
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
// NOISE_GATE_BLOCKS_TO_AVERAGE: How many audio blocks should we average together to compute noise floor.
// More means better rejection but also can reject continuous things like singing.
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
// NUMBER_OF_NOISE_SAMPLE_BLOCKS: How often should we re-evaluate the noise floor?
float loudness = 0;
int thisSample = 0;
@ -83,16 +83,16 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
const float NOISE_GATE_HEIGHT = 7.0f;
const int NOISE_GATE_WIDTH = 5;
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
const int NOISE_GATE_CLOSE_BLOCK_DELAY = 5;
const int NOISE_GATE_BLOCKS_TO_AVERAGE = 5;
// Check clipping, and check if should open noise gate
_didClipInLastFrame = false;
_didClipInLastBlock = false;
for (int i = 0; i < numSamples; i++) {
thisSample = std::abs(samples[i]);
if (thisSample >= ((float) AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
_didClipInLastFrame = true;
_didClipInLastBlock = true;
}
loudness += thisSample;
@ -104,54 +104,81 @@ void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
_lastLoudness = fabs(loudness / numSamples);
if (_quietestFrame > _lastLoudness) {
_quietestFrame = _lastLoudness;
if (_quietestBlock > _lastLoudness) {
_quietestBlock = _lastLoudness;
}
if (_loudestFrame < _lastLoudness) {
_loudestFrame = _lastLoudness;
if (_loudestBlock < _lastLoudness) {
_loudestBlock = _lastLoudness;
}
const int FRAMES_FOR_NOISE_DETECTION = 400;
if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) {
_quietestFrame = std::numeric_limits<float>::max();
_loudestFrame = 0.0f;
_inputFrameCounter = 0;
if (_inputBlockCounter++ > FRAMES_FOR_NOISE_DETECTION) {
_quietestBlock = std::numeric_limits<float>::max();
_loudestBlock = 0.0f;
_inputBlockCounter = 0;
}
// If Noise Gate is enabled, check and turn the gate on and off
float averageOfAllSampleFrames = 0.0f;
_sampleFrames[_sampleCounter++] = _lastLoudness;
if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
float averageOfAllSampleBlocks = 0.0f;
_sampleBlocks[_sampleCounter++] = _lastLoudness;
if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_BLOCKS) {
float smallestSample = std::numeric_limits<float>::max();
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i += NOISE_GATE_FRAMES_TO_AVERAGE) {
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_BLOCKS - NOISE_GATE_BLOCKS_TO_AVERAGE; i += NOISE_GATE_BLOCKS_TO_AVERAGE) {
float thisAverage = 0.0f;
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
thisAverage += _sampleFrames[j];
averageOfAllSampleFrames += _sampleFrames[j];
for (int j = i; j < i + NOISE_GATE_BLOCKS_TO_AVERAGE; j++) {
thisAverage += _sampleBlocks[j];
averageOfAllSampleBlocks += _sampleBlocks[j];
}
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
thisAverage /= NOISE_GATE_BLOCKS_TO_AVERAGE;
if (thisAverage < smallestSample) {
smallestSample = thisAverage;
}
}
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
averageOfAllSampleBlocks /= NUMBER_OF_NOISE_SAMPLE_BLOCKS;
_measuredFloor = smallestSample;
_sampleCounter = 0;
}
_closedInLastBlock = false;
_openedInLastBlock = false;
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
_openedInLastBlock = !_isOpen;
_isOpen = true;
_framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY;
_blocksToClose = NOISE_GATE_CLOSE_BLOCK_DELAY;
} else {
if (--_framesToClose == 0) {
_closedInLastFrame = !_isOpen;
if (--_blocksToClose == 0) {
_closedInLastBlock = _isOpen;
_isOpen = false;
}
}
if (!_isOpen) {
memset(samples, 0, numSamples * sizeof(int16_t));
// First block after being closed gets faded to silence, we fade across
// the entire block on fading out. All subsequent blocks are muted by being slammed
// to zeros
if (_closedInLastBlock) {
float fadeSlope = (1.0f / numSamples);
for (int i = 0; i < numSamples; i++) {
float fadedSample = (1.0f - ((float)i * fadeSlope)) * (float)samples[i];
samples[i] = (int16_t)fadedSample;
}
} else {
memset(samples, 0, numSamples * sizeof(int16_t));
}
_lastLoudness = 0;
}
if (_openedInLastBlock) {
// would be nice to do a little crossfade from silence, but we only want to fade
// across the first 1/10th of the block, because we don't want to miss early
// transients.
int fadeSamples = numSamples / 10; // fade over 1/10th of the samples
float fadeSlope = (1.0f / fadeSamples);
for (int i = 0; i < fadeSamples; i++) {
float fadedSample = (float)i * fadeSlope * (float)samples[i];
samples[i] = (int16_t)fadedSample;
}
}
}

View file

@ -14,7 +14,7 @@
#include <stdint.h>
const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
const int NUMBER_OF_NOISE_SAMPLE_BLOCKS = 300;
class AudioNoiseGate {
public:
@ -23,26 +23,29 @@ public:
void gateSamples(int16_t* samples, int numSamples);
void removeDCOffset(int16_t* samples, int numSamples);
bool clippedInLastFrame() const { return _didClipInLastFrame; }
bool closedInLastFrame() const { return _closedInLastFrame; }
bool clippedInLastBlock() const { return _didClipInLastBlock; }
bool closedInLastBlock() const { return _closedInLastBlock; }
bool openedInLastBlock() const { return _openedInLastBlock; }
bool isOpen() const { return _isOpen; }
float getMeasuredFloor() const { return _measuredFloor; }
float getLastLoudness() const { return _lastLoudness; }
static const float CLIPPING_THRESHOLD;
private:
int _inputFrameCounter;
int _inputBlockCounter;
float _lastLoudness;
float _quietestFrame;
float _loudestFrame;
bool _didClipInLastFrame;
float _quietestBlock;
float _loudestBlock;
bool _didClipInLastBlock;
float _dcOffset;
float _measuredFloor;
float _sampleFrames[NUMBER_OF_NOISE_SAMPLE_FRAMES];
float _sampleBlocks[NUMBER_OF_NOISE_SAMPLE_BLOCKS];
int _sampleCounter;
bool _isOpen;
bool _closedInLastFrame { false };
int _framesToClose;
bool _closedInLastBlock { false };
bool _openedInLastBlock { false };
int _blocksToClose;
};
#endif // hifi_AudioNoiseGate_h

View file

@ -736,6 +736,52 @@ void EntityTreeRenderer::mousePressEvent(QMouseEvent* event) {
}
}
void EntityTreeRenderer::mouseDoublePressEvent(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;
}
PerformanceTimer perfTimer("EntityTreeRenderer::mouseDoublePressEvent");
PickRay ray = _viewState->computePickRay(event->x(), event->y());
bool precisionPicking = !_dontDoPrecisionPicking;
RayToEntityIntersectionResult rayPickResult = findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
if (rayPickResult.intersects) {
//qCDebug(entitiesrenderer) << "mouseDoublePressEvent over entity:" << rayPickResult.entityID;
QString urlString = rayPickResult.properties.getHref();
QUrl url = QUrl(urlString, QUrl::StrictMode);
if (url.isValid() && !url.isEmpty()){
DependencyManager::get<AddressManager>()->handleLookupString(urlString);
}
glm::vec2 pos2D = projectOntoEntityXYPlane(rayPickResult.entity, ray, rayPickResult);
PointerEvent pointerEvent(PointerEvent::Press, MOUSE_POINTER_ID,
pos2D, rayPickResult.intersection,
rayPickResult.surfaceNormal, ray.direction,
toPointerButton(*event), toPointerButtons(*event), Qt::NoModifier);
emit mouseDoublePressOnEntity(rayPickResult.entityID, pointerEvent);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(rayPickResult.entityID, "mouseDoublePressOnEntity", pointerEvent);
}
_currentClickingOnEntityID = rayPickResult.entityID;
emit clickDownOnEntity(_currentClickingOnEntityID, pointerEvent);
if (_entitiesScriptEngine) {
_entitiesScriptEngine->callEntityScriptMethod(_currentClickingOnEntityID, "doubleclickOnEntity", pointerEvent);
}
_lastPointerEvent = pointerEvent;
_lastPointerEventValid = true;
} else {
emit mouseDoublePressOffEntity();
}
}
void EntityTreeRenderer::mouseReleaseEvent(QMouseEvent* event) {
// If we don't have a tree, or we're in the process of shutting down, then don't
// process these events.

View file

@ -90,6 +90,7 @@ public:
// event handles which may generate entity related events
void mouseReleaseEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseDoublePressEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
/// connect our signals to anEntityScriptingInterface for firing of events related clicking,
@ -103,9 +104,11 @@ public:
signals:
void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void mouseDoublePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void mousePressOffEntity();
void mouseDoublePressOffEntity();
void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);
void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event);

View file

@ -143,12 +143,35 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) {
}
void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) {
// first bump and prune contacts for all objects in the list
// bump and prune contacts for all objects in the list
for (auto object : objects) {
bumpAndPruneContacts(object);
}
// then remove them
if (_activeStaticBodies.size() > 0) {
// _activeStaticBodies was not cleared last frame.
// The only way to get here is if a static object were moved but we did not actually step the simulation last
// frame (because the framerate is faster than our physics simulation rate). When this happens we must scan
// _activeStaticBodies for objects that were recently deleted so we don't try to access a dangling pointer.
for (auto object : objects) {
btRigidBody* body = object->getRigidBody();
std::vector<btRigidBody*>::reverse_iterator itr = _activeStaticBodies.rbegin();
while (itr != _activeStaticBodies.rend()) {
if (body == *itr) {
if (*itr != *(_activeStaticBodies.rbegin())) {
// swap with rbegin
*itr = *(_activeStaticBodies.rbegin());
}
_activeStaticBodies.pop_back();
break;
}
++itr;
}
}
}
// remove bodies
for (auto object : objects) {
btRigidBody* body = object->getRigidBody();
if (body) {

View file

@ -32,10 +32,13 @@ protected:
Q_INVOKABLE void setStereoInput(bool stereo);
signals:
void mutedByMixer();
void environmentMuted();
void receivedFirstPacket();
void disconnected();
void mutedByMixer(); /// the client has been muted by the mixer
void environmentMuted(); /// the entire environment has been muted by the mixer
void receivedFirstPacket(); /// the client has received its first packet from the audio mixer
void disconnected(); /// the client has been disconnected from the audio mixer
void noiseGateOpened(); /// the noise gate has opened
void noiseGateClosed(); /// the noise gate has closed
void inputReceived(const QByteArray& inputSamples); /// a frame of mic input audio has been received and processed
private:
AudioScriptingInterface();

View file

@ -142,7 +142,7 @@ QString encodeEntityIdIntoEntityUrl(const QString& url, const QString& entityID)
QString ScriptEngine::logException(const QScriptValue& exception) {
auto message = formatException(exception);
scriptErrorMessage(qPrintable(message));
scriptErrorMessage(message);
return message;
}
@ -453,7 +453,7 @@ void ScriptEngine::loadURL(const QUrl& scriptURL, bool reload) {
}
void ScriptEngine::scriptErrorMessage(const QString& message) {
qCCritical(scriptengine) << message;
qCCritical(scriptengine) << qPrintable(message);
emit errorMessage(message);
}

View file

@ -48,6 +48,9 @@ QScriptValue PointerEvent::toScriptValue(QScriptEngine* engine, const PointerEve
case Press:
obj.setProperty("type", "Press");
break;
case DoublePress:
obj.setProperty("type", "DoublePress");
break;
case Release:
obj.setProperty("type", "Release");
break;
@ -131,6 +134,8 @@ void PointerEvent::fromScriptValue(const QScriptValue& object, PointerEvent& eve
QString typeStr = type.isString() ? type.toString() : "Move";
if (typeStr == "Press") {
event._type = Press;
} else if (typeStr == "DoublePress") {
event._type = DoublePress;
} else if (typeStr == "Release") {
event._type = Release;
} else {

View file

@ -28,9 +28,10 @@ public:
};
enum EventType {
Press, // A button has just been pressed
Release, // A button has just been released
Move // The pointer has just moved
Press, // A button has just been pressed
DoublePress, // A button has just been double pressed
Release, // A button has just been released
Move // The pointer has just moved
};
PointerEvent();

View file

@ -24,29 +24,34 @@ public:
RateCounter() { _rate = 0; } // avoid use of std::atomic copy ctor
void increment(size_t count = 1) {
auto now = usecTimestampNow();
float currentIntervalMs = (now - _start) / (float) USECS_PER_MSEC;
if (currentIntervalMs > (float) INTERVAL) {
float currentCount = _count;
float intervalSeconds = currentIntervalMs / (float) MSECS_PER_SECOND;
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
_start = now;
_count = 0;
};
checkRate();
_count += count;
}
float rate() const { return _rate; }
float rate() const { checkRate(); return _rate; }
uint8_t precision() const { return PRECISION; }
uint32_t interval() const { return INTERVAL; }
private:
uint64_t _start { usecTimestampNow() };
size_t _count { 0 };
mutable uint64_t _start { usecTimestampNow() };
mutable size_t _count { 0 };
const float _scale { powf(10, PRECISION) };
std::atomic<float> _rate;
mutable std::atomic<float> _rate;
void checkRate() const {
auto now = usecTimestampNow();
float currentIntervalMs = (now - _start) / (float)USECS_PER_MSEC;
if (currentIntervalMs > (float)INTERVAL) {
float currentCount = _count;
float intervalSeconds = currentIntervalMs / (float)MSECS_PER_SECOND;
_rate = roundf(currentCount / intervalSeconds * _scale) / _scale;
_start = now;
_count = 0;
};
}
};
#endif

View file

@ -0,0 +1,19 @@
(function() {
var _this;
function DoubleClickExample() {
_this = this;
return;
}
DoubleClickExample.prototype = {
clickDownOnEntity: function() {
print("clickDownOnEntity");
},
doubleclickOnEntity: function() {
print("doubleclickOnEntity");
}
};
return new DoubleClickExample();
});

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var DEFAULT_SCRIPTS = [
var DEFAULT_SCRIPTS_COMBINED = [
"system/progress.js",
"system/away.js",
"system/audio.js",
@ -27,17 +27,13 @@ var DEFAULT_SCRIPTS = [
"system/tablet-users.js",
"system/selectAudioDevice.js",
"system/notifications.js",
"system/controllers/squeezeHands.js",
"system/controllers/controllerDisplayManager.js",
"system/controllers/handControllerGrab.js",
"system/controllers/handControllerPointer.js",
"system/controllers/grab.js",
"system/controllers/teleport.js",
"system/controllers/toggleAdvancedMovementForHandControllers.js",
"system/dialTone.js",
"system/firstPersonHMD.js",
"system/tablet-ui/tabletUI.js"
];
var DEFAULT_SCRIPTS_SEPARATE = [
"system/controllers/controllerScripts.js"
];
// add a menu item for debugging
var MENU_CATEGORY = "Developer";
@ -64,16 +60,24 @@ if (Menu.menuExists(MENU_CATEGORY) && !Menu.menuItemExists(MENU_CATEGORY, MENU_I
});
}
function runDefaultsTogether() {
for (var j in DEFAULT_SCRIPTS) {
Script.include(DEFAULT_SCRIPTS[j]);
function loadSeparateDefaults() {
for (var i in DEFAULT_SCRIPTS_SEPARATE) {
Script.load(DEFAULT_SCRIPTS_SEPARATE[i]);
}
}
function runDefaultsSeparately() {
for (var i in DEFAULT_SCRIPTS) {
Script.load(DEFAULT_SCRIPTS[i]);
function runDefaultsTogether() {
for (var i in DEFAULT_SCRIPTS_COMBINED) {
Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
}
loadSeparateDefaults();
}
function runDefaultsSeparately() {
for (var i in DEFAULT_SCRIPTS_COMBINED) {
Script.load(DEFAULT_SCRIPTS_COMBINED[i]);
}
loadSeparateDefaults();
}
// start all scripts

View file

@ -0,0 +1,95 @@
"use strict";
//
// audioScope.js
// scripts/system/
//
// Created by Brad Hefta-Gaub on 3/10/2016
// Copyright 2016 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
//
/* global Script, Tablet, AudioScope, Audio */
(function () { // BEGIN LOCAL_SCOPE
var scopeVisibile = AudioScope.getVisible();
var scopePaused = AudioScope.getPause();
var autoPause = false;
var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
var showScopeButton = tablet.addButton({
icon: "icons/tablet-icons/scope.svg",
text: "Audio Scope",
isActive: scopeVisibile
});
var scopePauseImage = "icons/tablet-icons/scope-pause.svg";
var scopePlayImage = "icons/tablet-icons/scope-play.svg";
var pauseScopeButton = tablet.addButton({
icon: scopePaused ? scopePlayImage : scopePauseImage,
text: scopePaused ? "Unpause" : "Pause",
isActive: scopePaused
});
var autoPauseScopeButton = tablet.addButton({
icon: "icons/tablet-icons/scope-auto.svg",
text: "Auto Pause",
isActive: autoPause
});
function setScopePause(paused) {
scopePaused = paused;
pauseScopeButton.editProperties({
isActive: scopePaused,
icon: scopePaused ? scopePlayImage : scopePauseImage,
text: scopePaused ? "Unpause" : "Pause"
});
AudioScope.setPause(scopePaused);
}
showScopeButton.clicked.connect(function () {
// toggle button active state
scopeVisibile = !scopeVisibile;
showScopeButton.editProperties({
isActive: scopeVisibile
});
AudioScope.setVisible(scopeVisibile);
});
pauseScopeButton.clicked.connect(function () {
// toggle button active state
setScopePause(!scopePaused);
});
autoPauseScopeButton.clicked.connect(function () {
// toggle button active state
autoPause = !autoPause;
autoPauseScopeButton.editProperties({
isActive: autoPause,
text: autoPause ? "Auto Pause" : "Manual"
});
});
Script.scriptEnding.connect(function () {
tablet.removeButton(showScopeButton);
tablet.removeButton(pauseScopeButton);
tablet.removeButton(autoPauseScopeButton);
});
Audio.noiseGateOpened.connect(function(){
if (autoPause) {
setScopePause(false);
}
});
Audio.noiseGateClosed.connect(function(){
// noise gate closed
if (autoPause) {
setScopePause(true);
}
});
}()); // END LOCAL_SCOPE

View file

@ -0,0 +1,41 @@
"use strict";
// controllerScripts.js
//
// Created by David Rowe on 15 Mar 2017.
// Copyright 2017 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var CONTOLLER_SCRIPTS = [
"squeezeHands.js",
"controllerDisplayManager.js",
"handControllerGrab.js",
"handControllerPointer.js",
"grab.js",
"teleport.js",
"toggleAdvancedMovementForHandControllers.js",
];
var DEBUG_MENU_ITEM = "Debug defaultScripts.js";
function runDefaultsTogether() {
for (var j in CONTOLLER_SCRIPTS) {
Script.include(CONTOLLER_SCRIPTS[j]);
}
}
function runDefaultsSeparately() {
for (var i in CONTOLLER_SCRIPTS) {
Script.load(CONTOLLER_SCRIPTS[i]);
}
}
if (Menu.isOptionChecked(DEBUG_MENU_ITEM)) {
runDefaultsSeparately();
} else {
runDefaultsTogether();
}