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...
This commit is contained in:
David Kelly 2016-12-22 14:01:03 -08:00
parent e67d6e347d
commit 2e897e0cc9
3 changed files with 62 additions and 4 deletions

View file

@ -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

View file

@ -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));
}

View file

@ -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.
//