From 92756d81ad8296b1c6caa3b4bcdaa16080e53877 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jul 2018 15:03:01 -0700 Subject: [PATCH 1/5] Implement MS16562: Massive PAL speedup! --- interface/resources/qml/hifi/Pal.qml | 4 +- interface/src/avatar/AvatarManager.cpp | 39 +++++++ interface/src/avatar/AvatarManager.h | 2 + scripts/system/pal.js | 148 ++++++++++++------------- 4 files changed, 112 insertions(+), 81 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 3059707313..06ad57139d 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1108,7 +1108,7 @@ Rectangle { function findNearbySessionIndex(sessionId, optionalData) { // no findIndex in .qml var data = optionalData || nearbyUserModelData, length = data.length; for (var i = 0; i < length; i++) { - if (data[i].sessionId === sessionId) { + if (data[i].sessionId === sessionId.toString()) { return i; } } @@ -1120,7 +1120,7 @@ Rectangle { var data = message.params; var index = -1; iAmAdmin = Users.canKick; - index = findNearbySessionIndex('', data); + index = findNearbySessionIndex(MyAvatar.sessionUUID, data); if (index !== -1) { myData = data[index]; data.splice(index, 1); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 29ad71ead6..a7ee5f4869 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "AvatarLogging.h" @@ -668,3 +669,41 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV DependencyManager::get()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); } } + +QString AvatarManager::getPalData(const QList specificAvatarIdentifiers) { + QJsonArray palData; + + auto avatarMap = getHashCopy(); + AvatarHash::iterator itr = avatarMap.begin(); + while (itr != avatarMap.end()) { + std::shared_ptr avatar = std::static_pointer_cast(*itr); + QString currentSessionUUID = avatar->getSessionUUID().toString(); + if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) { + QJsonObject thisAvatarPalData; + thisAvatarPalData.insert("sessionUUID", currentSessionUUID); + thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName()); + thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness()); + thisAvatarPalData.insert("isReplicated", avatar->getIsReplicated()); + + glm::vec3 position = avatar->getWorldPosition(); + QJsonObject jsonPosition; + jsonPosition.insert("x", position.x); + jsonPosition.insert("y", position.y); + jsonPosition.insert("z", position.z); + thisAvatarPalData.insert("position", jsonPosition); + + float palOrbOffset = 0.2f; + int headIndex = avatar->getJointIndex("Head"); + if (headIndex > 0) { + glm::vec3 jointTranslation = avatar->getAbsoluteJointTranslationInObjectFrame(headIndex); + palOrbOffset = jointTranslation.y / 2; + } + thisAvatarPalData.insert("palOrbOffset", palOrbOffset); + + palData.append(thisAvatarPalData); + } + ++itr; + } + QJsonDocument doc(palData); + return doc.toJson(QJsonDocument::Compact); +} diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ff29c1b381..507767866b 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -157,6 +157,8 @@ public: */ Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value); + Q_INVOKABLE QString getPalData(const QList specificAvatarIdentifiers = QList()); + float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index e967ee6469..4a0d866d66 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -19,7 +19,7 @@ var populateNearbyUserList, color, textures, removeOverlays, controllerComputePickRay, onTabletButtonClicked, onTabletScreenChanged, receiveMessage, avatarDisconnected, clearLocalQMLDataAndClosePAL, - createAudioInterval, tablet, CHANNEL, getConnectionData, findableByChanged, + tablet, CHANNEL, getConnectionData, findableByChanged, avatarAdded, avatarRemoved, avatarSessionChanged; // forward references; // hardcoding these as it appears we cannot traverse the originalTextures in overlays??? Maybe I've missed @@ -447,21 +447,24 @@ function populateNearbyUserList(selectData, oldAudioData) { verticalAngleNormal = filter && Quat.getRight(orientation), horizontalAngleNormal = filter && Quat.getUp(orientation); avatarsOfInterest = {}; - avatars.forEach(function (id) { - var avatar = AvatarList.getAvatar(id); - var name = avatar.sessionDisplayName; + + var avatarData = JSON.parse(AvatarList.getPalData()); + + avatarData.forEach(function (currentAvatarData) { + var id = currentAvatarData.sessionUUID; + var name = currentAvatarData.sessionDisplayName; if (!name) { // Either we got a data packet but no identity yet, or something is really messed up. In any case, // we won't be able to do anything with this user, so don't include them. // In normal circumstances, a refresh will bring in the new user, but if we're very heavily loaded, // we could be losing and gaining people randomly. - print('No avatar identity data for', id); + print('No avatar identity data for', currentAvatarData.sessionUUID); return; } - if (id && myPosition && (Vec3.distance(avatar.position, myPosition) > filter.distance)) { + if (id && myPosition && (Vec3.distance(currentAvatarData.position, myPosition) > filter.distance)) { return; } - var normal = id && filter && Vec3.normalize(Vec3.subtract(avatar.position, myPosition)); + var normal = id && filter && Vec3.normalize(Vec3.subtract(currentAvatarData.position, myPosition)); var horizontal = normal && angleBetweenVectorsInPlane(normal, forward, horizontalAngleNormal); var vertical = normal && angleBetweenVectorsInPlane(normal, forward, verticalAngleNormal); if (id && filter && ((Math.abs(horizontal) > horizontalHalfAngle) || (Math.abs(vertical) > verticalHalfAngle))) { @@ -480,11 +483,11 @@ function populateNearbyUserList(selectData, oldAudioData) { personalMute: !!id && Users.getPersonalMuteStatus(id), // expects proper boolean, not null ignore: !!id && Users.getIgnoreStatus(id), // ditto isPresent: true, - isReplicated: avatar.isReplicated + isReplicated: currentAvatarData.isReplicated }; // Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin. Users.requestUsernameFromID(id); - if (id) { + if (id !== MyAvatar.sessionUUID) { addAvatarNode(id); // No overlay for ourselves avatarsOfInterest[id] = true; } else { @@ -515,30 +518,60 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { updateUser(data); } +function updateAudioLevel(overlay, avatarData) { + // 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 audioLevel = 0.0; + var avgAudioLevel = 0.0; + + // we will do exponential moving average by taking some the last loudness and averaging + overlay.accumulatedLevel = AVERAGING_RATIO * (overlay.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.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). + audioLevel = scaleAudio(Math.log(overlay.accumulatedLevel + 1) / LOG2); + + // decay avgAudioLevel + avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (overlay.avgAudioLevel || 0), audioLevel); + + overlay.avgAudioLevel = avgAudioLevel; + overlay.audioLevel = audioLevel; + + // now scale for the gain. Also, asked to boost the low end, so one simple way is + // to take sqrt of the value. Lets try that, see how it feels. + avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75))); + + + var param = {}; + var level = [audioLevel, avgAudioLevel]; + var userId = avatarData.sessionUUID || 0; + param[userId] = level; + sendToQml({ method: 'updateAudioLevel', params: param }); +} + var pingPong = true; function updateOverlays() { var eye = Camera.position; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - if (!id || !avatarsOfInterest[id]) { + + var avatarData = JSON.parse(AvatarList.getPalData()); + + avatarData.forEach(function (currentAvatarData) { + if (currentAvatarData.sessionUUID === MyAvatar.sessionUUID || !avatarsOfInterest[currentAvatarData.sessionUUID]) { return; // don't update ourself, or avatars we're not interested in } - var avatar = AvatarList.getAvatar(id); - if (!avatar) { - return; // will be deleted below if there had been an overlay. - } - var overlay = ExtendedOverlay.get(id); + var overlay = ExtendedOverlay.get(currentAvatarData.sessionUUID); if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. - print('Adding non-PAL avatar node', id); - overlay = addAvatarNode(id); + print('Adding non-PAL avatar node', currentAvatarData.sessionUUID); + overlay = addAvatarNode(currentAvatarData.sessionUUID); } - var target = avatar.position; + + updateAudioLevel(overlay, currentAvatarData); + + var target = currentAvatarData.position; var distance = Vec3.distance(target, eye); - var offset = 0.2; + var offset = currentAvatarData.palOrbOffset; var diff = Vec3.subtract(target, eye); // get diff between target and eye (a vector pointing to the eye from avatar position) - var headIndex = avatar.getJointIndex("Head"); // base offset on 1/2 distance from hips to head if we can - if (headIndex > 0) { - offset = avatar.getAbsoluteJointTranslationInObjectFrame(headIndex).y / 2; - } // move a bit in front, towards the camera target = Vec3.subtract(target, Vec3.multiply(Vec3.normalize(diff), offset)); @@ -548,7 +581,7 @@ function updateOverlays() { overlay.ping = pingPong; overlay.editOverlay({ - color: color(ExtendedOverlay.isSelected(id), overlay.hovering, overlay.audioLevel), + color: color(ExtendedOverlay.isSelected(currentAvatarData.sessionUUID), overlay.hovering, overlay.audioLevel), position: target, dimensions: 0.032 * distance }); @@ -704,6 +737,13 @@ function wireEventBridge(on) { hasEventBridge = false; } } + } + +var UPDATE_INTERVAL_MS = 100; +function createUpdateInterval() { + return Script.setInterval(function () { + updateOverlays(); + }, UPDATE_INTERVAL_MS); } function onTabletScreenChanged(type, url) { @@ -719,10 +759,8 @@ function onTabletScreenChanged(type, url) { ContextOverlay.enabled = false; Users.requestsDomainListData = true; - audioTimer = createAudioInterval(AUDIO_LEVEL_UPDATE_INTERVAL_MS); - tablet.tabletShownChanged.connect(tabletVisibilityChanged); - Script.update.connect(updateOverlays); + updateInterval = createUpdateInterval(); Controller.mousePressEvent.connect(handleMouseEvent); Controller.mouseMoveEvent.connect(handleMouseMoveEvent); Users.usernameFromIDReply.connect(usernameFromIDReply); @@ -778,50 +816,6 @@ function scaleAudio(val) { 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) { - - // 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); - - // 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); - - // decay avgAudioLevel - avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel); - - data.avgAudioLevel = avgAudioLevel; - data.audioLevel = audioLevel; - - // now scale for the gain. Also, asked to boost the low end, so one simple way is - // to take sqrt of the value. Lets try that, see how it feels. - avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[id] || 0.75))); - } - return [audioLevel, avgAudioLevel]; -} - -function createAudioInterval(interval) { - // we will update the audioLevels periodically - // TODO: tune for efficiency - expecially with large numbers of avatars - return Script.setInterval(function () { - var param = {}; - AvatarList.getAvatarIdentifiers().forEach(function (id) { - var level = getAudioLevel(id), - userId = id || 0; // qml didn't like an object with null/empty string for a key, so... - param[userId] = level; - }); - sendToQml({method: 'updateAudioLevel', params: param}); - }, interval); -} - function avatarDisconnected(nodeID) { // remove from the pal list sendToQml({method: 'avatarDisconnected', params: [nodeID]}); @@ -874,11 +868,11 @@ startup(); var isWired = false; -var audioTimer; -var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) function off() { if (isWired) { - Script.update.disconnect(updateOverlays); + if (updateInterval) { + Script.clearInterval(updateInterval); + } Controller.mousePressEvent.disconnect(handleMouseEvent); Controller.mouseMoveEvent.disconnect(handleMouseMoveEvent); tablet.tabletShownChanged.disconnect(tabletVisibilityChanged); @@ -889,10 +883,6 @@ function off() { Users.requestsDomainListData = false; isWired = false; - - if (audioTimer) { - Script.clearInterval(audioTimer); - } } removeOverlays(); From fa518b995aad602c4c483f6470a5880d46b4f151 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 12 Jul 2018 15:16:54 -0700 Subject: [PATCH 2/5] Fix my audio level --- scripts/system/pal.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 4a0d866d66..b811f5e95a 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -525,27 +525,30 @@ function updateAudioLevel(overlay, avatarData) { var audioLevel = 0.0; var avgAudioLevel = 0.0; - // we will do exponential moving average by taking some the last loudness and averaging - overlay.accumulatedLevel = AVERAGING_RATIO * (overlay.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.audioLoudness); + if (overlay) { + // we will do exponential moving average by taking some the last loudness and averaging + overlay.accumulatedLevel = AVERAGING_RATIO * (overlay.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.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). - audioLevel = scaleAudio(Math.log(overlay.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(overlay.accumulatedLevel + 1) / LOG2); - // decay avgAudioLevel - avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (overlay.avgAudioLevel || 0), audioLevel); + // decay avgAudioLevel + avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (overlay.avgAudioLevel || 0), audioLevel); - overlay.avgAudioLevel = avgAudioLevel; - overlay.audioLevel = audioLevel; - - // now scale for the gain. Also, asked to boost the low end, so one simple way is - // to take sqrt of the value. Lets try that, see how it feels. - avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75))); + overlay.avgAudioLevel = avgAudioLevel; + overlay.audioLevel = audioLevel; + // now scale for the gain. Also, asked to boost the low end, so one simple way is + // to take sqrt of the value. Lets try that, see how it feels. + avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75))); + } else { + audioLevel = scaleAudio(Math.log(((1 - AVERAGING_RATIO) * (avatarData.audioLoudness)) + 1) / LOG2); + } var param = {}; var level = [audioLevel, avgAudioLevel]; - var userId = avatarData.sessionUUID || 0; + var userId = avatarData.sessionUUID === MyAvatar.sessionUUID ? 0 : avatarData.sessionUUID; param[userId] = level; sendToQml({ method: 'updateAudioLevel', params: param }); } @@ -557,6 +560,8 @@ function updateOverlays() { var avatarData = JSON.parse(AvatarList.getPalData()); avatarData.forEach(function (currentAvatarData) { + updateAudioLevel(overlay, currentAvatarData); + if (currentAvatarData.sessionUUID === MyAvatar.sessionUUID || !avatarsOfInterest[currentAvatarData.sessionUUID]) { return; // don't update ourself, or avatars we're not interested in } @@ -566,8 +571,6 @@ function updateOverlays() { overlay = addAvatarNode(currentAvatarData.sessionUUID); } - updateAudioLevel(overlay, currentAvatarData); - var target = currentAvatarData.position; var distance = Vec3.distance(target, eye); var offset = currentAvatarData.palOrbOffset; From 4daa0c65378c25ff8bb3bfbcd55cb5fdae8e4126 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jul 2018 12:47:45 -0700 Subject: [PATCH 3/5] CR feedback --- interface/resources/qml/hifi/Pal.qml | 6 +++--- interface/src/avatar/AvatarManager.cpp | 14 +++++++++++--- interface/src/avatar/AvatarManager.h | 11 ++++++++++- scripts/system/pal.js | 10 +++++----- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 06ad57139d..c66ed1fe18 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -1120,7 +1120,7 @@ Rectangle { var data = message.params; var index = -1; iAmAdmin = Users.canKick; - index = findNearbySessionIndex(MyAvatar.sessionUUID, data); + index = findNearbySessionIndex("", data); if (index !== -1) { myData = data[index]; data.splice(index, 1); @@ -1197,8 +1197,8 @@ Rectangle { for (var userId in message.params) { var audioLevel = message.params[userId][0]; var avgAudioLevel = message.params[userId][1]; - // If the userId is 0, we're updating "myData". - if (userId == 0) { + // If the userId is "", we're updating "myData". + if (userId === "") { myData.audioLevel = audioLevel; myCard.audioLevel = audioLevel; // Defensive programming myData.avgAudioLevel = avgAudioLevel; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index a7ee5f4869..bb9a78d546 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -670,7 +670,7 @@ void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptV } } -QString AvatarManager::getPalData(const QList specificAvatarIdentifiers) { + QVariantMap AvatarManager::getPalData(const QList specificAvatarIdentifiers) { QJsonArray palData; auto avatarMap = getHashCopy(); @@ -680,6 +680,13 @@ QString AvatarManager::getPalData(const QList specificAvatarIdentifiers QString currentSessionUUID = avatar->getSessionUUID().toString(); if (specificAvatarIdentifiers.isEmpty() || specificAvatarIdentifiers.contains(currentSessionUUID)) { QJsonObject thisAvatarPalData; + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + + if (currentSessionUUID == myAvatar->getSessionUUID().toString()) { + currentSessionUUID = ""; + } + thisAvatarPalData.insert("sessionUUID", currentSessionUUID); thisAvatarPalData.insert("sessionDisplayName", avatar->getSessionDisplayName()); thisAvatarPalData.insert("audioLoudness", avatar->getAudioLoudness()); @@ -704,6 +711,7 @@ QString AvatarManager::getPalData(const QList specificAvatarIdentifiers } ++itr; } - QJsonDocument doc(palData); - return doc.toJson(QJsonDocument::Compact); + QJsonObject doc; + doc.insert("data", palData); + return doc.toVariantMap(); } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 507767866b..c6f71b498d 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -157,7 +157,16 @@ public: */ Q_INVOKABLE void setAvatarSortCoefficient(const QString& name, const QScriptValue& value); - Q_INVOKABLE QString getPalData(const QList specificAvatarIdentifiers = QList()); + /**jsdoc + * Used in the PAL for getting PAL-related data about avatars nearby. Using this method is faster + * than iterating over each avatar and obtaining data about them in JavaScript, as that method + * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. + * @function AvatarManager.getPalData + * @param {string list} specificAvatarIdentifiers - A list of specific Avatar Identifiers about which + * you want to get PAL data + * @returns {string} + */ + Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList()); float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index b811f5e95a..d8dae80b85 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -448,7 +448,7 @@ function populateNearbyUserList(selectData, oldAudioData) { horizontalAngleNormal = filter && Quat.getUp(orientation); avatarsOfInterest = {}; - var avatarData = JSON.parse(AvatarList.getPalData()); + var avatarData = AvatarList.getPalData().data; avatarData.forEach(function (currentAvatarData) { var id = currentAvatarData.sessionUUID; @@ -487,7 +487,7 @@ function populateNearbyUserList(selectData, oldAudioData) { }; // Everyone needs to see admin status. Username and fingerprint returns default constructor output if the requesting user isn't an admin. Users.requestUsernameFromID(id); - if (id !== MyAvatar.sessionUUID) { + if (id !== "") { addAvatarNode(id); // No overlay for ourselves avatarsOfInterest[id] = true; } else { @@ -548,7 +548,7 @@ function updateAudioLevel(overlay, avatarData) { var param = {}; var level = [audioLevel, avgAudioLevel]; - var userId = avatarData.sessionUUID === MyAvatar.sessionUUID ? 0 : avatarData.sessionUUID; + var userId = avatarData.sessionUUID; param[userId] = level; sendToQml({ method: 'updateAudioLevel', params: param }); } @@ -557,12 +557,12 @@ var pingPong = true; function updateOverlays() { var eye = Camera.position; - var avatarData = JSON.parse(AvatarList.getPalData()); + var avatarData = AvatarList.getPalData().data; avatarData.forEach(function (currentAvatarData) { updateAudioLevel(overlay, currentAvatarData); - if (currentAvatarData.sessionUUID === MyAvatar.sessionUUID || !avatarsOfInterest[currentAvatarData.sessionUUID]) { + if (currentAvatarData.sessionUUID === "" || !avatarsOfInterest[currentAvatarData.sessionUUID]) { return; // don't update ourself, or avatars we're not interested in } var overlay = ExtendedOverlay.get(currentAvatarData.sessionUUID); From c3ffe7eb97f26f2c4073932cd83faa645d0b81dc Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 13 Jul 2018 13:55:32 -0700 Subject: [PATCH 4/5] Fix bubble shading; fix audio loudness calculation --- scripts/system/pal.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d8dae80b85..fcff83a3d3 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -518,32 +518,32 @@ function usernameFromIDReply(id, username, machineFingerprint, isAdmin) { updateUser(data); } -function updateAudioLevel(overlay, avatarData) { +function updateAudioLevel(avatarData) { // 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 audioLevel = 0.0; var avgAudioLevel = 0.0; - if (overlay) { + var data = avatarData.sessionUUID === "" ? myData : ExtendedOverlay.get(avatarData.sessionUUID); + + if (data) { // we will do exponential moving average by taking some the last loudness and averaging - overlay.accumulatedLevel = AVERAGING_RATIO * (overlay.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.audioLoudness); + data.accumulatedLevel = AVERAGING_RATIO * (data.accumulatedLevel || 0) + (1 - AVERAGING_RATIO) * (avatarData.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). - audioLevel = scaleAudio(Math.log(overlay.accumulatedLevel + 1) / LOG2); + audioLevel = scaleAudio(Math.log(data.accumulatedLevel + 1) / LOG2); // decay avgAudioLevel - avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (overlay.avgAudioLevel || 0), audioLevel); + avgAudioLevel = Math.max((1 - AUDIO_PEAK_DECAY) * (data.avgAudioLevel || 0), audioLevel); - overlay.avgAudioLevel = avgAudioLevel; - overlay.audioLevel = audioLevel; + data.avgAudioLevel = avgAudioLevel; + data.audioLevel = audioLevel; // now scale for the gain. Also, asked to boost the low end, so one simple way is // to take sqrt of the value. Lets try that, see how it feels. avgAudioLevel = Math.min(1.0, Math.sqrt(avgAudioLevel * (sessionGains[avatarData.sessionUUID] || 0.75))); - } else { - audioLevel = scaleAudio(Math.log(((1 - AVERAGING_RATIO) * (avatarData.audioLoudness)) + 1) / LOG2); } var param = {}; @@ -560,11 +560,11 @@ function updateOverlays() { var avatarData = AvatarList.getPalData().data; avatarData.forEach(function (currentAvatarData) { - updateAudioLevel(overlay, currentAvatarData); if (currentAvatarData.sessionUUID === "" || !avatarsOfInterest[currentAvatarData.sessionUUID]) { return; // don't update ourself, or avatars we're not interested in } + updateAudioLevel(currentAvatarData); var overlay = ExtendedOverlay.get(currentAvatarData.sessionUUID); if (!overlay) { // For now, we're treating this as a temporary loss, as from the personal space bubble. Add it back. print('Adding non-PAL avatar node', currentAvatarData.sessionUUID); From ac2351e49f9466a7cc2760233ec8db7ce0f95f73 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 16 Jul 2018 09:33:36 -0700 Subject: [PATCH 5/5] Make JSDoc happy --- interface/src/avatar/AvatarManager.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index c6f71b498d..53461146e5 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -162,9 +162,9 @@ public: * than iterating over each avatar and obtaining data about them in JavaScript, as that method * locks and unlocks each avatar's data structure potentially hundreds of times per update tick. * @function AvatarManager.getPalData - * @param {string list} specificAvatarIdentifiers - A list of specific Avatar Identifiers about which - * you want to get PAL data - * @returns {string} + * @param {string[]} specificAvatarIdentifiers - A list of specific Avatar Identifiers about + * which you want to get PAL data + * @returns {object} */ Q_INVOKABLE QVariantMap getPalData(const QList specificAvatarIdentifiers = QList());