First pass

This commit is contained in:
Zach Fox 2017-01-10 16:40:48 -08:00
parent f9a36d68ad
commit 8bb200902f
13 changed files with 105 additions and 8 deletions

View file

@ -68,7 +68,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
packetReceiver.registerListener(PacketType::NodeMuteRequest, this, "handleNodeMuteRequestPacket");
packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket");
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket");
packetReceiver.registerListener(PacketType::PerAvatarGainSet, this, "handlePerAvatarGainSetDataPacket");
connect(nodeList.data(), &NodeList::nodeKilled, this, &AudioMixer::handleNodeKilled);
}
@ -186,7 +187,9 @@ void AudioMixer::handleNodeKilled(SharedNodePointer killedNode) {
nodeList->eachNode([&killedNode](const SharedNodePointer& node) {
auto clientData = dynamic_cast<AudioMixerClientData*>(node->getLinkedData());
if (clientData) {
clientData->removeHRTFsForNode(killedNode->getUUID());
QUuid killedUUID = killedNode->getUUID();
clientData->removePerAvatarGain(killedUUID);
clientData->removeHRTFsForNode(killedUUID);
}
});
}
@ -240,6 +243,17 @@ void AudioMixer::handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> p
sendingNode->parseIgnoreRequestMessage(packet);
}
void AudioMixer::handlePerAvatarGainSetDataPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto clientData = dynamic_cast<AudioMixerClientData*>(sendingNode->getLinkedData());
if (clientData) {
// parse the UUID from the packet
QUuid ignoredUUID = QUuid::fromRfc4122(packet->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
float gain;
packet->readPrimitive(&gain);
clientData->setPerAvatarGain(ignoredUUID, gain);
}
}
void AudioMixer::handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
sendingNode->parseIgnoreRadiusRequestMessage(packet);
}

View file

@ -66,6 +66,7 @@ private slots:
void handleRadiusIgnoreRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void handlePerAvatarGainSetDataPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
void start();
void removeHRTFsForFinishedInjector(const QUuid& streamID);

View file

@ -95,6 +95,10 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
float getPerAvatarGain(const QUuid& avatarID) { return (_perAvatarGain.count(avatarID) ? _perAvatarGain.at(avatarID) : 1.0f); }
void setPerAvatarGain(const QUuid& avatarID, float gain) { _perAvatarGain[avatarID] = gain; }
void removePerAvatarGain(const QUuid& avatarID) { _perAvatarGain.erase(avatarID); }
signals:
void injectorStreamFinished(const QUuid& streamIdentifier);
@ -125,6 +129,8 @@ private:
bool _shouldMuteClient { false };
bool _requestsDomainListData { false };
std::unordered_map<QUuid, float> _perAvatarGain;
};
#endif // hifi_AudioMixerClientData_h

View file

@ -252,12 +252,13 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) {
// Enumerate the audio streams attached to the otherNode
auto streamsCopy = otherData->getAudioStreams();
float thisAvatarGain = nodeData->getPerAvatarGain(otherNode->getUUID());
for (auto& streamPair : streamsCopy) {
auto otherNodeStream = streamPair.second;
bool isSelfWithEcho = ((*otherNode == *node) && (otherNodeStream->shouldLoopbackForNode()));
// Add all audio streams that should be added to the mix
if (isSelfWithEcho || (!isSelfWithEcho && !insideIgnoreRadius)) {
addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream);
addStreamToMix(*nodeData, otherNode->getUUID(), *nodeAudioStream, *otherNodeStream, thisAvatarGain);
}
}
}
@ -278,7 +279,7 @@ bool AudioMixerSlave::prepareMix(const SharedNodePointer& node) {
}
void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, const QUuid& sourceNodeID,
const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd) {
const AvatarAudioStream& listeningNodeStream, const PositionalAudioStream& streamToAdd, float perAvatarGain) {
// to reduce artifacts we calculate the gain and azimuth for every source for this listener
// even if we are not going to end up mixing in this source
@ -295,7 +296,7 @@ void AudioMixerSlave::addStreamToMix(AudioMixerClientData& listenerNodeData, con
float distance = glm::max(glm::length(relativePosition), EPSILON);
// figure out the gain for this source at the listener
float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho);
float gain = gainForSource(listeningNodeStream, streamToAdd, relativePosition, isEcho) + (perAvatarGain - 1.0f);
// figure out the azimuth to this source at the listener
float azimuth = isEcho ? 0.0f : azimuthForSource(listeningNodeStream, listeningNodeStream, relativePosition);

View file

@ -43,7 +43,7 @@ private:
bool prepareMix(const SharedNodePointer& node);
// add a stream to the mix
void addStreamToMix(AudioMixerClientData& listenerData, const QUuid& streamerID,
const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer);
const AvatarAudioStream& listenerStream, const PositionalAudioStream& streamer, float perAvatarGain);
float gainForSource(const AvatarAudioStream& listener, const PositionalAudioStream& streamer,
const glm::vec3& relativePosition, bool isEcho);

View file

@ -10,6 +10,7 @@
//
import QtQuick 2.5
import QtQuick.Controls 1.4
import QtGraphicalEffects 1.0
import "../styles-uit"
@ -27,12 +28,14 @@ Row {
}
// Properties
property int contentHeight: 50
property int contentHeight: isMyCard ? 50 : 70
property string uuid: ""
property string displayName: ""
property string userName: ""
property int displayTextHeight: 18
property int usernameTextHeight: 12
property real audioLevel: 0.0
property bool isMyCard: false
/* User image commented out for now - will probably be re-introduced later.
Column {
@ -138,5 +141,33 @@ Row {
}
}
}
// Per-Avatar Gain Slider Spacer
Item {
width: parent.width
height: 4
visible: !isMyCard
}
// Per-Avatar Gain Slider
Slider {
id: gainSlider
visible: !isMyCard
width: parent.width
height: 16
value: 1.0
minimumValue: 0.0
maximumValue: 1.5
stepSize: 0.1
updateValueWhileDragging: false
onValueChanged: updateGainFromQML(uuid, value)
}
}
function updateGainFromQML(avatarUuid, gainValue) {
var data = {
sessionId: avatarUuid,
gain: (Math.exp(gainValue) - 1) / (Math.E - 1)
};
pal.sendToScript({method: 'updateGain', params: data});
}
}

View file

@ -65,6 +65,7 @@ Rectangle {
displayName: myData.displayName
userName: myData.userName
audioLevel: myData.audioLevel
isMyCard: true
// Size
width: nameCardWidth
height: parent.height
@ -206,6 +207,7 @@ Rectangle {
userName: model && model.userName
audioLevel: model && model.audioLevel
visible: !isCheckBox && !isButton
uuid: model && model.sessionId
// Size
width: nameCardWidth
height: parent.height

View file

@ -951,6 +951,29 @@ void NodeList::maybeSendIgnoreSetToNode(SharedNodePointer newNode) {
}
}
void NodeList::setAvatarGain(const QUuid& nodeID, float gain) {
// cannot set gain of yourself or nobody
if (!nodeID.isNull() && _sessionUUID != nodeID) {
auto audioMixer = soloNodeOfType(NodeType::AudioMixer);
if (audioMixer) {
// setup the packet
auto setAvatarGainPacket = NLPacket::create(PacketType::PerAvatarGainSet, NUM_BYTES_RFC4122_UUID + sizeof(float), true);
// write the node ID to the packet
setAvatarGainPacket->write(nodeID.toRfc4122());
setAvatarGainPacket->writePrimitive((gain < 5.0f ? gain : 5.0f));
qCDebug(networking) << "Sending Set Avatar Gain packet UUID: " << uuidStringWithoutCurlyBraces(nodeID) << "Gain:" << gain;
sendPacket(std::move(setAvatarGainPacket), *audioMixer);
} else {
qWarning() << "Couldn't find audio mixer to send set gain request";
}
} else {
qWarning() << "NodeList::setAvatarGain called with an invalid ID or an ID which matches the current session ID:" << nodeID;
}
}
void NodeList::kickNodeBySessionID(const QUuid& nodeID) {
// send a request to domain-server to kick the node with the given session ID
// the domain-server will handle the persistence of the kick (via username or IP)

View file

@ -82,6 +82,7 @@ public:
bool isIgnoringNode(const QUuid& nodeID) const;
void personalMuteNodeBySessionID(const QUuid& nodeID, bool muteEnabled);
bool isPersonalMutingNode(const QUuid& nodeID) const;
void setAvatarGain(const QUuid& nodeID, float gain);
void kickNodeBySessionID(const QUuid& nodeID);
void muteNodeBySessionID(const QUuid& nodeID);

View file

@ -106,7 +106,8 @@ public:
ViewFrustum,
RequestsDomainListData,
ExitingSpaceBubble,
LAST_PACKET_TYPE = ExitingSpaceBubble
PerAvatarGainSet,
LAST_PACKET_TYPE = ExitingSpaceBubble // FIXME!!!
};
};

View file

@ -42,6 +42,11 @@ bool UsersScriptingInterface::getPersonalMuteStatus(const QUuid& nodeID) {
return DependencyManager::get<NodeList>()->isPersonalMutingNode(nodeID);
}
void UsersScriptingInterface::setAvatarGain(const QUuid& nodeID, float gain) {
// ask the NodeList to set the gain of the specified avatar
DependencyManager::get<NodeList>()->setAvatarGain(nodeID, gain);
}
void UsersScriptingInterface::kick(const QUuid& nodeID) {
// ask the NodeList to kick the user with the given session ID
DependencyManager::get<NodeList>()->kickNodeBySessionID(nodeID);

View file

@ -61,6 +61,14 @@ public slots:
*/
bool getPersonalMuteStatus(const QUuid& nodeID);
/**jsdoc
* Sets an avatar's gain for you and you only.
* @function Users.setAvatarGain
* @param {nodeID} nodeID The node or session ID of the user whose gain you want to modify.
* @param {float} gain The gain of the avatar you'd like to set.
*/
void setAvatarGain(const QUuid& nodeID, float gain);
/**jsdoc
* Kick another user.
* @function Users.kick

View file

@ -233,6 +233,10 @@ pal.fromQml.connect(function (message) { // messages are {method, params}, like
removeOverlays();
populateUserList();
break;
case 'updateGain':
data = message.params;
Users.setAvatarGain(data['sessionId'], data['gain']);
break;
default:
print('Unrecognized message from Pal.qml:', JSON.stringify(message));
}