From 2e897e0cc9fc0dcd1788c0303d1b688688af6e74 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 22 Dec 2016 14:01:03 -0800 Subject: [PATCH 1/3] Working first pass Seems roughly same as mic meter. Works for other avatars, though using agent avatars (crowd-agent.js + summon.js), seems not to work. I'll investigate that... --- interface/resources/qml/hifi/NameCard.qml | 4 +- interface/resources/qml/hifi/Pal.qml | 16 +++++++- scripts/system/pal.js | 46 ++++++++++++++++++++++- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 0bc6afd241..9b90ae6c3b 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -33,6 +33,7 @@ Row { property string userName: "" property int displayTextHeight: 18 property int usernameTextHeight: 12 + property real audioLevel: 0.0 Column { id: avatarImage @@ -91,7 +92,6 @@ Row { // VU Meter Rectangle { // CHANGEME to the appropriate type! id: nameCardVUMeter - objectName: "AvatarInputs" // Size width: parent.width height: 8 @@ -110,7 +110,7 @@ Row { Rectangle { id: vuMeterLevel // Size - width: (nameCardVUMeter.audioLevel) * parent.width + width: (thisNameCard.audioLevel) * parent.width // Style color: "#dbdbdb" // Very appropriate hex value here radius: parent.radius diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index af99d9a2e1..aac3cc2e2c 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -58,6 +58,7 @@ Item { // Properties displayName: myData.displayName userName: myData.userName + audioLevel: myData.audioLevel // Size width: nameCardWidth height: parent.height @@ -190,6 +191,7 @@ Item { // Properties displayName: styleData.value userName: model.userName + audioLevel: model.audioLevel visible: !isCheckBox // Size width: nameCardWidth @@ -292,7 +294,7 @@ Item { } property var userData: [] - property var myData: ({displayName: "", userName: ""}) // valid dummy until set + property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set property bool iAmAdmin: false function findSessionIndex(sessionId, optionalData) { // no findIndex in .qml var i, data = optionalData || userData, length = data.length; @@ -343,6 +345,18 @@ Item { userData[userIndex].userName = userName; // Defensive programming } break; + case 'updateAudioLevel': + var userId = message.params[0]; + var audioLevel = message.params[1]; + if (!userId) { + myData.audioLevel = audioLevel; + myCard.audioLevel = audioLevel; + } else { + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).audioLevel = audioLevel; + userData[userIndex].audioLevel = audioLevel; + } + break; default: console.log('Unrecognized message:', JSON.stringify(message)); } diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 9d419e5a0f..14b3f483da 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -119,7 +119,8 @@ function populateUserList() { var avatarPalDatum = { displayName: avatar.sessionDisplayName, userName: '', - sessionId: id || '' + sessionId: id || '', + audioLevel: getAudioLevel(id) }; // If the current user is an admin OR // they're requesting their own username ("id" is blank)... @@ -262,6 +263,49 @@ function onClicked() { pal.setVisible(!pal.visible); } +var AVERAGING_RATIO = 0.05 +var LOUDNESS_FLOOR = 11.0; +var LOUDNESS_SCALE = 2.8 / 5.0; +var LOG2 = Math.log(2.0); +var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) +var accumulatedLevels={}; + +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; + + // we will do exponential moving average by taking some the last loudness and averaging + accumulatedLevels[id] = AVERAGING_RATIO*(accumulatedLevels[id] || 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). + var logLevel = Math.log(accumulatedLevels[id]+1) / LOG2; + + if (logLevel <= LOUDNESS_FLOOR) { + audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE; + } else { + audioLevel = (logLevel - (LOUDNESS_FLOOR - 1.0)) * LOUDNESS_SCALE; + } + if (audioLevel > 1.0) { + audioLevel = 1; + } + return audioLevel; +} + + +// we will update the audioLevels periodically +// TODO: tune for efficiency - expecially with large numbers of avatars +Script.setInterval(function () { + if (pal.visible) { + AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { + var level = getAudioLevel(id); + pal.sendToQml({method: 'updateAudioLevel', params: [id, level]}); + }); + } +}, AUDIO_LEVEL_UPDATE_INTERVAL_MS); // // Button state. // From 9eed430f49340c7aab2fad59d3a81a9334c5574e Mon Sep 17 00:00:00 2001 From: David Kelly Date: Thu, 22 Dec 2016 18:09:19 -0800 Subject: [PATCH 2/3] CR feedback --- interface/resources/qml/hifi/Pal.qml | 21 ++++++++++++--------- scripts/system/pal.js | 8 ++++++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index aac3cc2e2c..a03556a56e 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -346,15 +346,18 @@ Item { } break; case 'updateAudioLevel': - var userId = message.params[0]; - var audioLevel = message.params[1]; - if (!userId) { - myData.audioLevel = audioLevel; - myCard.audioLevel = audioLevel; - } else { - var userIndex = findSessionIndex(userId); - userModel.get(userIndex).audioLevel = audioLevel; - userData[userIndex].audioLevel = audioLevel; + for (var userId in message.params) { + var audioLevel = message.params[userId]; + // If the userId is 0, we're updating "myData". + if (userId == 0) { + myData.audioLevel = audioLevel; + myCard.audioLevel = audioLevel; // Defensive programming + } else { + console.log("userid:" + userId); + var userIndex = findSessionIndex(userId); + userModel.get(userIndex).audioLevel = audioLevel; + userData[userIndex].audioLevel = audioLevel; // Defensive programming + } } break; default: diff --git a/scripts/system/pal.js b/scripts/system/pal.js index 14b3f483da..a19c6a8c29 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -120,7 +120,7 @@ function populateUserList() { displayName: avatar.sessionDisplayName, userName: '', sessionId: id || '', - audioLevel: getAudioLevel(id) + audioLevel: 0.0 }; // If the current user is an admin OR // they're requesting their own username ("id" is blank)... @@ -300,10 +300,14 @@ function getAudioLevel(id) { // TODO: tune for efficiency - expecially with large numbers of avatars Script.setInterval(function () { if (pal.visible) { + var param = {}; AvatarList.getAvatarIdentifiers().sort().forEach(function (id) { var level = getAudioLevel(id); - pal.sendToQml({method: 'updateAudioLevel', params: [id, level]}); + // qml didn't like an object with null/empty string for a key, so... + var userId = id || 0; + param[userId]= level; }); + pal.sendToQml({method: 'updateAudioLevel', params: param}); } }, AUDIO_LEVEL_UPDATE_INTERVAL_MS); // From 5065f027e79787b6da58385756ac36cfda93e5a1 Mon Sep 17 00:00:00 2001 From: David Kelly Date: Fri, 23 Dec 2016 13:11:42 -0800 Subject: [PATCH 3/3] whitespace --- scripts/system/pal.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/system/pal.js b/scripts/system/pal.js index a19c6a8c29..2b24a76cae 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -268,7 +268,7 @@ var LOUDNESS_FLOOR = 11.0; var LOUDNESS_SCALE = 2.8 / 5.0; var LOG2 = Math.log(2.0); var AUDIO_LEVEL_UPDATE_INTERVAL_MS = 100; // 10hz for now (change this and change the AVERAGING_RATIO too) -var accumulatedLevels={}; +var accumulatedLevels = {}; function getAudioLevel(id) { // the VU meter should work similarly to the one in AvatarInputs: log scale, exponentially averaged @@ -278,11 +278,11 @@ function getAudioLevel(id) { var audioLevel = 0.0; // we will do exponential moving average by taking some the last loudness and averaging - accumulatedLevels[id] = AVERAGING_RATIO*(accumulatedLevels[id] || 0 ) + (1-AVERAGING_RATIO)*(avatar.audioLoudness); + accumulatedLevels[id] = AVERAGING_RATIO * (accumulatedLevels[id] || 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). - var logLevel = Math.log(accumulatedLevels[id]+1) / LOG2; + var logLevel = Math.log(accumulatedLevels[id] + 1) / LOG2; if (logLevel <= LOUDNESS_FLOOR) { audioLevel = logLevel / LOUDNESS_FLOOR * LOUDNESS_SCALE;