first cut, pretty ugly

This commit is contained in:
David Kelly 2017-02-17 10:24:01 -07:00
parent 5bef6ea029
commit 51246a2e45
3 changed files with 73 additions and 40 deletions

View file

@ -31,6 +31,7 @@ Item {
property real displayNameTextPixelSize: 18
property int usernameTextHeight: 12
property real audioLevel: 0.0
property real avgAudioLevel: 0.0
property bool isMyCard: false
property bool selected: false
property bool isAdmin: false
@ -335,7 +336,7 @@ Item {
}
}
// Per-Avatar Gain Slider
// Per-Avatar Gain Slider
Slider {
id: gainSlider
// Size
@ -369,7 +370,7 @@ Item {
mouse.accepted = false
}
onReleased: {
// the above mouse.accepted seems to make this
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false
}

View file

@ -2,7 +2,7 @@
// Pal.qml
// qml/hifi
//
// People Action List
// People Action List
//
// Created by Howard Stearns on 12/12/2016
// Copyright 2016 High Fidelity, Inc.
@ -29,8 +29,8 @@ Rectangle {
property int myCardHeight: 90
property int rowHeight: 70
property int actionButtonWidth: 55
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 5 : 3) - 4 - hifi.dimensions.scrollbarBackgroundWidth
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, avgAudioLevel: 0.0, admin: true}) // valid dummy until set
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
property bool iAmAdmin: false
@ -86,6 +86,7 @@ Rectangle {
displayName: myData.displayName
userName: myData.userName
audioLevel: myData.audioLevel
avgAudioLevel: myData.avgAudioLevel
isMyCard: true
// Size
width: nameCardWidth
@ -184,6 +185,13 @@ Rectangle {
movable: false
resizable: false
}
TableViewColumn {
role: "avgAudioLevel"
title: "VOL"
width: actionButtonWidth
movable: false
resizable: false
}
TableViewColumn {
visible: iAmAdmin
role: "mute"
@ -218,6 +226,7 @@ Rectangle {
id: itemCell
property bool isCheckBox: styleData.role === "personalMute" || styleData.role === "ignore"
property bool isButton: styleData.role === "mute" || styleData.role === "kick"
property bool isText: styleData.role == "avgAudioLevel"
// This NameCard refers to the cell that contains an avatar's
// DisplayName and UserName
NameCard {
@ -226,7 +235,8 @@ Rectangle {
displayName: styleData.value
userName: model ? model.userName : ""
audioLevel: model ? model.audioLevel : 0.0
visible: !isCheckBox && !isButton
avgAudioLevel: model ? model.avgAudioLevel : 0.0
visible: !isCheckBox && !isButton && !isText
uuid: model ? model.sessionId : ""
selected: styleData.selected
isAdmin: model && model.admin
@ -236,7 +246,16 @@ Rectangle {
// Anchors
anchors.left: parent.left
}
Text {
id: avgAudioVolume
text: model ? model.avgAudioLevel : 0.0
visible: isText
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
width: parent.width
height: parent.height
}
// This CheckBox belongs in the columns that contain the stateful action buttons ("Mute" & "Ignore" for now)
// KNOWN BUG with the Checkboxes: When clicking in the center of the sorting header, the checkbox
// will appear in the "hovered" state. Hovering over the checkbox will fix it.
@ -272,7 +291,7 @@ Rectangle {
checked = Qt.binding(function() { return (model[styleData.role])})
}
}
// This Button belongs in the columns that contain the stateless action buttons ("Silence" & "Ban" for now)
HifiControls.Button {
id: actionButton
@ -542,23 +561,28 @@ Rectangle {
}
}
break;
case 'updateAudioLevel':
case 'updateAudioLevel':
for (var userId in message.params) {
var audioLevel = message.params[userId];
var audioLevel = message.params[userId][0];
var avgAudioLevel = message.params[userId][1];
// If the userId is 0, we're updating "myData".
if (userId == 0) {
myData.audioLevel = audioLevel;
myCard.audioLevel = audioLevel; // Defensive programming
myData.avgAudioLevel = avgAudioLevel;
myCard.avgAudioLevel = avgAudioLevel;
} else {
var userIndex = findSessionIndex(userId);
if (userIndex != -1) {
userModel.setProperty(userIndex, "audioLevel", audioLevel);
userModelData[userIndex].audioLevel = audioLevel; // Defensive programming
userModel.setProperty(userIndex, "avgAudioLevel", avgAudioLevel);
userModelData[userIndex].avgAudioLevel = avgAudioLevel;
}
}
}
break;
case 'clearLocalQMLData':
case 'clearLocalQMLData':
ignored = {};
gainSliderValueDB = {};
break;

View file

@ -13,7 +13,7 @@
(function() { // BEGIN LOCAL_SCOPE
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
// hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed
// something, will revisit as this is sorta horrible.
const UNSELECTED_TEXTURES = {"idle-D": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png"),
"idle-E": Script.resolvePath("./assets/models/Avatar-Overlay-v1.fbx/Avatar-Overlay-v1.fbm/avatar-overlay-idle.png")
@ -87,7 +87,7 @@ ExtendedOverlay.prototype.hover = function (hovering) {
} else {
lastHoveringId = 0;
}
}
}
this.editOverlay({color: color(this.selected, hovering, this.audioLevel)});
if (this.model) {
this.model.editOverlay({textures: textures(this.selected, hovering)});
@ -104,7 +104,7 @@ ExtendedOverlay.prototype.select = function (selected) {
if (this.selected === selected) {
return;
}
UserActivityLogger.palAction(selected ? "avatar_selected" : "avatar_deselected", this.key);
this.editOverlay({color: color(selected, this.hovering, this.audioLevel)});
@ -273,7 +273,7 @@ function sendToQml(message) {
//
function addAvatarNode(id) {
var selected = ExtendedOverlay.isSelected(id);
return new ExtendedOverlay(id, "sphere", {
return new ExtendedOverlay(id, "sphere", {
drawInFront: true,
solid: true,
alpha: 0.8,
@ -290,6 +290,7 @@ function populateUserList(selectData) {
userName: '',
sessionId: id || '',
audioLevel: 0.0,
avgAudioLevel: 0.0,
admin: false,
personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null
ignore: !!id && Users.getIgnoreStatus(id) // ditto
@ -341,7 +342,7 @@ function updateOverlays() {
var target = avatar.position;
var distance = Vec3.distance(target, eye);
var offset = 0.2;
// base offset on 1/2 distance from hips to head if we can
var headIndex = avatar.getJointIndex("Head");
if (headIndex > 0) {
@ -350,7 +351,7 @@ function updateOverlays() {
// get diff between target and eye (a vector pointing to the eye from avatar position)
var diff = Vec3.subtract(target, eye);
// move a bit in front, towards the camera
target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset));
@ -361,12 +362,12 @@ function updateOverlays() {
overlay.editOverlay({
color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel),
position: target,
dimensions: 0.032 * distance
dimensions: 0.032 * distance
});
if (overlay.model) {
overlay.model.ping = pingPong;
overlay.model.editOverlay({
position: target,
position: target,
scale: 0.2 * distance, // constant apparent size
rotation: Camera.orientation
});
@ -433,7 +434,7 @@ function handleMouseMoveEvent(event) { // find out which overlay (if any) is ove
handleMouseMove(pickRay);
}
function handleTriggerPressed(hand, value) {
// The idea is if you press one trigger, it is the one
// The idea is if you press one trigger, it is the one
// we will consider the mouse. Even if the other is pressed,
// we ignore it until this one is no longer pressed.
isPressed = value > TRIGGER_PRESS_THRESHOLD;
@ -441,10 +442,10 @@ function handleTriggerPressed(hand, value) {
currentHandPressed = isPressed ? hand : 0;
return;
}
if (currentHandPressed == hand) {
if (currentHandPressed == hand) {
currentHandPressed = isPressed ? hand : 0;
return;
}
}
// otherwise, the other hand is still triggered
// so do nothing.
}
@ -574,41 +575,48 @@ function receiveMessage(channel, messageString, senderID) {
Messages.subscribe(CHANNEL);
Messages.messageReceived.connect(receiveMessage);
var AVERAGING_RATIO = 0.05;
var LONG_AVERAGING_RATIO = 0.75;
var LOUDNESS_FLOOR = 11.0;
var LOUDNESS_SCALE = 2.8 / 5.0;
var LOG2 = Math.log(2.0);
var myData = {}; // we're not includied in ExtendedOverlay.get.
function scaleAudio(val) {
var audioLevel = 0.0;
if (val <= LOUDNESS_FLOOR) {
audioLevel = val / LOUDNESS_FLOOR * LOUDNESS_SCALE;
} else {
audioLevel = (val -(LOUDNESS_FLOOR -1 )) * LOUDNESS_SCALE;
}
if (audioLevel > 1.0) {
audioLevel = 1;
}
return audioLevel;
}
function getAudioLevel(id) {
// the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged
// But of course it gets the data at a different rate, so we tweak the averaging ratio and frequency
// of updating (the latter for efficiency too).
var avatar = AvatarList.getAvatar(id);
var audioLevel = 0.0;
var avgAudioLevel = 0.0;
var data = id ? ExtendedOverlay.get(id) : myData;
if (!data) {
return audioLevel;
}
if (data) {
// we will do exponential moving average by taking some the last loudness and averaging
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness);
// we will do exponential moving average by taking some the last loudness and averaging
data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatar.audioLoudness);
data.longAccumulatedLevel = LONG_AVERAGING_RATIO * (data.longAccumulatedLevel || 0) + (1 - LONG_AVERAGING_RATIO) * (avatar.audioLoudness);
// add 1 to insure we don't go log() and hit -infinity. Math.log is
// natural log, so to get log base 2, just divide by ln(2).
var logLevel = Math.log(data.accumulatedLevel + 1) / LOG2;
// add 1 to insure we don't go log() and hit -infinity. Math.log is
// natural log, so to get log base 2, just divide by ln(2).
audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2);
avgAudioLevel = scaleAudio(Math.log(data.longAccumulatedLevel + 1) / LOG2);
if (logLevel <= LOUDNESS_FLOOR) {
audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE;
} else {
audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE;
data.audioLevel = audioLevel;
data.averageAudioLevel = avgAudioLevel;
}
if (audioLevel > 1.0) {
audioLevel = 1;
}
data.audioLevel = audioLevel;
return audioLevel;
return [audioLevel, avgAudioLevel];
}
function createAudioInterval(interval) {