mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-04-06 00:13:44 +02:00
move audio zones to zone entity properties
This commit is contained in:
parent
d928cb4ccd
commit
b8d2d71e07
18 changed files with 610 additions and 91 deletions
|
@ -31,6 +31,7 @@
|
|||
#include <StDev.h>
|
||||
#include <UUID.h>
|
||||
#include <CPUDetect.h>
|
||||
#include <ZoneEntityItem.h>
|
||||
|
||||
#include "AudioLogging.h"
|
||||
#include "AudioHelpers.h"
|
||||
|
@ -39,6 +40,7 @@
|
|||
#include "AvatarAudioStream.h"
|
||||
#include "InjectedAudioStream.h"
|
||||
#include "crash-handler/CrashHandler.h"
|
||||
#include "../entities/AssignmentParentFinder.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -56,9 +58,7 @@ float AudioMixer::_noiseMutingThreshold{ DEFAULT_NOISE_MUTING_THRESHOLD };
|
|||
float AudioMixer::_attenuationPerDoublingInDistance{ DEFAULT_ATTENUATION_PER_DOUBLING_IN_DISTANCE };
|
||||
map<QString, shared_ptr<CodecPlugin>> AudioMixer::_availableCodecs{ };
|
||||
QStringList AudioMixer::_codecPreferenceOrder{};
|
||||
vector<AudioMixer::ZoneDescription> AudioMixer::_audioZones;
|
||||
vector<AudioMixer::ZoneSettings> AudioMixer::_zoneSettings;
|
||||
vector<AudioMixer::ReverbSettings> AudioMixer::_zoneReverbSettings;
|
||||
unordered_map<QString, AudioMixer::ZoneSettings> AudioMixer::_audioZones;
|
||||
|
||||
AudioMixer::AudioMixer(ReceivedMessage& message) :
|
||||
ThreadedAssignment(message)
|
||||
|
@ -112,6 +112,8 @@ AudioMixer::AudioMixer(ReceivedMessage& message) :
|
|||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleNodeMuteRequestPacket));
|
||||
packetReceiver.registerListener(PacketType::KillAvatar,
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleKillAvatarPacket));
|
||||
packetReceiver.registerListenerForTypes({ PacketType::OctreeStats, PacketType::EntityData, PacketType::EntityErase },
|
||||
PacketReceiver::makeSourcedListenerReference<AudioMixer>(this, &AudioMixer::handleOctreePacket));
|
||||
|
||||
packetReceiver.registerListenerForTypes({
|
||||
PacketType::ReplicatedMicrophoneAudioNoEcho,
|
||||
|
@ -405,7 +407,7 @@ void AudioMixer::start() {
|
|||
|
||||
// prepare the NodeList
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({
|
||||
NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::Agent, NodeType::EntityScriptServer, NodeType::EntityServer,
|
||||
NodeType::UpstreamAudioMixer, NodeType::DownstreamAudioMixer
|
||||
});
|
||||
nodeList->linkedDataCreateCallback = [&](Node* node) { getOrCreateClientData(node); };
|
||||
|
@ -417,12 +419,19 @@ void AudioMixer::start() {
|
|||
parseSettingsObject(settingsObject);
|
||||
}
|
||||
|
||||
setupEntityQuery();
|
||||
|
||||
// mix state
|
||||
unsigned int frame = 1;
|
||||
|
||||
while (!_isFinished) {
|
||||
auto ticTimer = _ticTiming.timer();
|
||||
|
||||
// Set our query each frame
|
||||
{
|
||||
_entityViewer.queryOctree();
|
||||
}
|
||||
|
||||
if (_startFrameTimestamp.time_since_epoch().count() == 0) {
|
||||
_startFrameTimestamp = _idealFrameTimestamp = p_high_resolution_clock::now();
|
||||
} else {
|
||||
|
@ -555,8 +564,6 @@ void AudioMixer::clearDomainSettings() {
|
|||
_noiseMutingThreshold = DEFAULT_NOISE_MUTING_THRESHOLD;
|
||||
_codecPreferenceOrder.clear();
|
||||
_audioZones.clear();
|
||||
_zoneSettings.clear();
|
||||
_zoneReverbSettings.clear();
|
||||
}
|
||||
|
||||
void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
||||
|
@ -727,8 +734,13 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
if (allOk) {
|
||||
glm::vec3 corner(xMin, yMin, zMin);
|
||||
glm::vec3 dimensions(xMax - xMin, yMax - yMin, zMax - zMin);
|
||||
AABox zoneAABox(corner, dimensions);
|
||||
_audioZones.push_back({ zoneName, zoneAABox });
|
||||
|
||||
Transform t;
|
||||
t.setTranslation(corner + 0.5f * dimensions);
|
||||
t.setScale(dimensions);
|
||||
_audioZones[zoneName].inverseTransform = t.getInverseMatrix();
|
||||
_audioZones[zoneName].volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
|
||||
qCDebug(audio) << "Added zone:" << zoneName << "(corner:" << corner << ", dimensions:" << dimensions << ")";
|
||||
}
|
||||
}
|
||||
|
@ -749,28 +761,17 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
coefficientObject.contains(LISTENER) &&
|
||||
coefficientObject.contains(COEFFICIENT)) {
|
||||
|
||||
auto itSource = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) {
|
||||
return description.name == coefficientObject.value(SOURCE).toString();
|
||||
});
|
||||
auto itListener = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) {
|
||||
return description.name == coefficientObject.value(LISTENER).toString();
|
||||
});
|
||||
auto itSource = _audioZones.find(coefficientObject.value(SOURCE).toString());
|
||||
|
||||
bool ok;
|
||||
float coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok);
|
||||
|
||||
if (ok && coefficient <= 1.0f && itSource != _audioZones.end()) {
|
||||
auto listener = coefficientObject.value(LISTENER).toString();
|
||||
itSource->second.listeners.emplace_back(listener);
|
||||
itSource->second.coefficients.emplace_back(coefficient);
|
||||
|
||||
if (ok && coefficient <= 1.0f &&
|
||||
itSource != end(_audioZones) &&
|
||||
itListener != end(_audioZones)) {
|
||||
|
||||
ZoneSettings settings;
|
||||
settings.source = itSource - begin(_audioZones);
|
||||
settings.listener = itListener - begin(_audioZones);
|
||||
settings.coefficient = coefficient;
|
||||
|
||||
_zoneSettings.push_back(settings);
|
||||
qCDebug(audio) << "Added Coefficient:" << itSource->name << itListener->name << settings.coefficient;
|
||||
qCDebug(audio) << "Added Coefficient:" << itSource->first << listener << coefficient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -791,21 +792,16 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
reverbObject.contains(WET_LEVEL)) {
|
||||
|
||||
bool okReverbTime, okWetLevel;
|
||||
auto itZone = find_if(begin(_audioZones), end(_audioZones), [&](const ZoneDescription& description) {
|
||||
return description.name == reverbObject.value(ZONE).toString();
|
||||
});
|
||||
auto itZone = _audioZones.find(reverbObject.value(ZONE).toString());
|
||||
float reverbTime = reverbObject.value(REVERB_TIME).toString().toFloat(&okReverbTime);
|
||||
float wetLevel = reverbObject.value(WET_LEVEL).toString().toFloat(&okWetLevel);
|
||||
|
||||
if (okReverbTime && okWetLevel && itZone != end(_audioZones)) {
|
||||
ReverbSettings settings;
|
||||
settings.zone = itZone - begin(_audioZones);
|
||||
settings.reverbTime = reverbTime;
|
||||
settings.wetLevel = wetLevel;
|
||||
if (okReverbTime && okWetLevel && itZone != _audioZones.end()) {
|
||||
itZone->second.reverbEnabled = true;
|
||||
itZone->second.reverbTime = reverbTime;
|
||||
itZone->second.wetLevel = wetLevel;
|
||||
|
||||
_zoneReverbSettings.push_back(settings);
|
||||
|
||||
qCDebug(audio) << "Added Reverb:" << itZone->name << reverbTime << wetLevel;
|
||||
qCDebug(audio) << "Added Reverb:" << itZone->first << reverbTime << wetLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -813,6 +809,107 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioMixer::setupEntityQuery() {
|
||||
_entityViewer.init();
|
||||
EntityTreePointer entityTree = _entityViewer.getTree();
|
||||
DependencyManager::registerInheritance<SpatialParentFinder, AssignmentParentFinder>();
|
||||
DependencyManager::set<AssignmentParentFinder>(entityTree);
|
||||
|
||||
connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AudioMixer::entityAdded);
|
||||
connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AudioMixer::entityRemoved);
|
||||
|
||||
// ES query: {"type": "Zone"}
|
||||
QJsonObject zoneQuery;
|
||||
zoneQuery["type"] = "Zone";
|
||||
|
||||
QJsonObject queryFlags;
|
||||
queryFlags["includeAncestors"] = true;
|
||||
queryFlags["includeDescendants"] = true;
|
||||
zoneQuery["flags"] = queryFlags;
|
||||
zoneQuery["name"] = true; // Handy for debugging.
|
||||
|
||||
_entityViewer.getOctreeQuery().setJSONParameters(zoneQuery);
|
||||
}
|
||||
|
||||
void AudioMixer::handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||
PacketType packetType = message->getType();
|
||||
|
||||
switch (packetType) {
|
||||
case PacketType::OctreeStats:
|
||||
{ // Ignore stats, but may have a different Entity packet appended.
|
||||
OctreeHeadlessViewer::parseOctreeStats(message, senderNode);
|
||||
const auto piggyBackedSizeWithHeader = message->getBytesLeftToRead();
|
||||
if (piggyBackedSizeWithHeader > 0) {
|
||||
// pull out the piggybacked packet and create a new QSharedPointer<NLPacket> for it
|
||||
auto buffer = std::unique_ptr<char[]>(new char[piggyBackedSizeWithHeader]);
|
||||
memcpy(buffer.get(), message->getRawMessage() + message->getPosition(), piggyBackedSizeWithHeader);
|
||||
|
||||
auto newPacket = NLPacket::fromReceivedPacket(std::move(buffer), piggyBackedSizeWithHeader, message->getSenderSockAddr());
|
||||
auto newMessage = QSharedPointer<ReceivedMessage>::create(*newPacket);
|
||||
handleOctreePacket(newMessage, senderNode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PacketType::EntityData:
|
||||
_entityViewer.processDatagram(*message, senderNode);
|
||||
break;
|
||||
|
||||
case PacketType::EntityErase:
|
||||
_entityViewer.processEraseMessage(*message, senderNode);
|
||||
break;
|
||||
|
||||
default:
|
||||
qCDebug(audio) << "Unexpected packet type:" << packetType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void updateAudioZone(EntityItem* entity, std::unordered_map<QString, AudioMixer::ZoneSettings>& audioZones) {
|
||||
auto zoneEntity = (ZoneEntityItem*)entity;
|
||||
auto& audioZone = audioZones[entity->getID().toString()];
|
||||
auto& audioSettings = zoneEntity->getAudioProperties();
|
||||
|
||||
vec3 dimensions = entity->getScaledDimensions();
|
||||
Transform t;
|
||||
t.setTranslation(entity->getWorldPosition());
|
||||
t.setScale(dimensions);
|
||||
t.setRotation(entity->getWorldOrientation());
|
||||
audioZone.inverseTransform = t.getInverseMatrix();
|
||||
audioZone.volume = dimensions.x * dimensions.y * dimensions.z;
|
||||
|
||||
audioZone.reverbEnabled = audioSettings.getReverbEnabled();
|
||||
audioZone.reverbTime = audioSettings.getReverbTime();
|
||||
audioZone.wetLevel = audioSettings.getReverbWetLevel();
|
||||
|
||||
audioZone.listeners.clear();
|
||||
auto listenerZones = audioSettings.getListenerZones();
|
||||
audioZone.listeners.reserve(listenerZones.length());
|
||||
for (auto& listener : listenerZones) {
|
||||
audioZone.listeners.push_back(listener.toString());
|
||||
}
|
||||
|
||||
audioZone.coefficients = audioSettings.getListenerAttenuationCoefficients().toStdVector();
|
||||
|
||||
/*qCDebug(audio) << "Updated audio zone:" << entity->getID().toString() << "(position:" << t.getTranslation()
|
||||
<< ", dimensions:" << t.getScale() << ")";*/
|
||||
}
|
||||
|
||||
void AudioMixer::entityAdded(EntityItem* entity) {
|
||||
if (entity->getType() == EntityTypes::Zone) {
|
||||
updateAudioZone(entity, _audioZones);
|
||||
entity->registerChangeHandler([entity](const EntityItemID& entityItemID) {
|
||||
updateAudioZone(entity, _audioZones);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::entityRemoved(EntityItem* entity) {
|
||||
if (entity->getType() == EntityTypes::Zone) {
|
||||
_audioZones.erase(entity->getID().toString());
|
||||
}
|
||||
}
|
||||
|
||||
AudioMixer::Timer::Timing::Timing(uint64_t& sum) : _sum(sum) {
|
||||
_timing = p_high_resolution_clock::now();
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
#include <QtCore/QSharedPointer>
|
||||
|
||||
#include <AABox.h>
|
||||
#include <Transform.h>
|
||||
#include <AudioHRTF.h>
|
||||
#include <AudioRingBuffer.h>
|
||||
#include <ThreadedAssignment.h>
|
||||
|
@ -25,6 +25,8 @@
|
|||
#include "AudioMixerStats.h"
|
||||
#include "AudioMixerSlavePool.h"
|
||||
|
||||
#include "../entities/EntityTreeHeadlessViewer.h"
|
||||
|
||||
class PositionalAudioStream;
|
||||
class AvatarAudioStream;
|
||||
class AudioHRTF;
|
||||
|
@ -36,28 +38,22 @@ class AudioMixer : public ThreadedAssignment {
|
|||
public:
|
||||
AudioMixer(ReceivedMessage& message);
|
||||
|
||||
|
||||
struct ZoneDescription {
|
||||
QString name;
|
||||
AABox area;
|
||||
};
|
||||
struct ZoneSettings {
|
||||
int source;
|
||||
int listener;
|
||||
float coefficient;
|
||||
};
|
||||
struct ReverbSettings {
|
||||
int zone;
|
||||
float reverbTime;
|
||||
float wetLevel;
|
||||
glm::mat4 inverseTransform;
|
||||
float volume { FLT_MAX };
|
||||
|
||||
bool reverbEnabled { false };
|
||||
float reverbTime { 0.0f };
|
||||
float wetLevel { 0.0f };
|
||||
|
||||
std::vector<QString> listeners;
|
||||
std::vector<float> coefficients;
|
||||
};
|
||||
|
||||
static int getStaticJitterFrames() { return _numStaticJitterFrames; }
|
||||
static bool shouldMute(float quietestFrame) { return quietestFrame > _noiseMutingThreshold; }
|
||||
static float getAttenuationPerDoublingInDistance() { return _attenuationPerDoublingInDistance; }
|
||||
static const std::vector<ZoneDescription>& getAudioZones() { return _audioZones; }
|
||||
static const std::vector<ZoneSettings>& getZoneSettings() { return _zoneSettings; }
|
||||
static const std::vector<ReverbSettings>& getReverbSettings() { return _zoneReverbSettings; }
|
||||
static const std::unordered_map<QString, ZoneSettings>& getAudioZones() { return _audioZones; }
|
||||
static const std::pair<QString, CodecPluginPointer> negotiateCodec(std::vector<QString> codecs);
|
||||
|
||||
static bool shouldReplicateTo(const Node& from, const Node& to) {
|
||||
|
@ -72,12 +68,17 @@ public slots:
|
|||
void run() override;
|
||||
void sendStatsPacket() override;
|
||||
|
||||
// Audio zone possibly changed
|
||||
void entityAdded(EntityItem* entity);
|
||||
void entityRemoved(EntityItem* entity);
|
||||
|
||||
private slots:
|
||||
// packet handlers
|
||||
void handleMuteEnvironmentPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleNodeMuteRequestPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleNodeKilled(SharedNodePointer killedNode);
|
||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void handleOctreePacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||
|
||||
void queueAudioPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||
void queueReplicatedAudioPacket(QSharedPointer<ReceivedMessage> packet);
|
||||
|
@ -146,14 +147,17 @@ private:
|
|||
static std::map<QString, CodecPluginPointer> _availableCodecs;
|
||||
static QStringList _codecPreferenceOrder;
|
||||
|
||||
static std::vector<ZoneDescription> _audioZones;
|
||||
static std::vector<ZoneSettings> _zoneSettings;
|
||||
static std::vector<ReverbSettings> _zoneReverbSettings;
|
||||
static std::unordered_map<QString, ZoneSettings> _audioZones;
|
||||
|
||||
float _throttleStartTarget = 0.9f;
|
||||
float _throttleBackoffTarget = 0.44f;
|
||||
|
||||
AudioMixerSlave::SharedData _workerSharedData;
|
||||
|
||||
void setupEntityQuery();
|
||||
|
||||
// Attach to entity tree for audio zone info.
|
||||
EntityTreeHeadlessViewer _entityViewer;
|
||||
};
|
||||
|
||||
#endif // hifi_AudioMixer_h
|
||||
|
|
|
@ -646,27 +646,36 @@ void sendMutePacket(const SharedNodePointer& node, AudioMixerClientData& data) {
|
|||
data.setShouldMuteClient(false);
|
||||
}
|
||||
|
||||
const AABox UNIT_BOX(vec3(-0.5f), vec3(1.0f));
|
||||
void sendEnvironmentPacket(const SharedNodePointer& node, AudioMixerClientData& data) {
|
||||
bool hasReverb = false;
|
||||
float reverbTime, wetLevel;
|
||||
|
||||
auto& reverbSettings = AudioMixer::getReverbSettings();
|
||||
auto& audioZones = AudioMixer::getAudioZones();
|
||||
|
||||
AvatarAudioStream* stream = data.getAvatarAudioStream();
|
||||
glm::vec3 streamPosition = stream->getPosition();
|
||||
vec4 streamPosition = vec4(stream->getPosition(), 1.0f);
|
||||
|
||||
// find reverb properties
|
||||
for (const auto& settings : reverbSettings) {
|
||||
AABox box = audioZones[settings.zone].area;
|
||||
if (box.contains(streamPosition)) {
|
||||
hasReverb = true;
|
||||
reverbTime = settings.reverbTime;
|
||||
wetLevel = settings.wetLevel;
|
||||
break;
|
||||
QString bestZone;
|
||||
float bestZoneVolume = FLT_MAX;
|
||||
for (const auto& zone : audioZones) {
|
||||
if (zone.second.reverbEnabled) {
|
||||
vec4 localPosition = zone.second.inverseTransform * streamPosition;
|
||||
if (UNIT_BOX.contains(localPosition) && zone.second.volume < bestZoneVolume) {
|
||||
bestZone = zone.first;
|
||||
bestZoneVolume = zone.second.volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestZoneVolume < FLT_MAX) {
|
||||
const auto& zone = audioZones.at(bestZone);
|
||||
hasReverb = zone.reverbEnabled;
|
||||
reverbTime = zone.reverbTime;
|
||||
wetLevel = zone.wetLevel;
|
||||
}
|
||||
|
||||
// check if data changed
|
||||
bool dataChanged = (stream->hasReverb() != hasReverb) ||
|
||||
(stream->hasReverb() && (stream->getRevebTime() != reverbTime || stream->getWetLevel() != wetLevel));
|
||||
|
@ -759,18 +768,40 @@ float computeGain(float masterAvatarGain,
|
|||
}
|
||||
|
||||
auto& audioZones = AudioMixer::getAudioZones();
|
||||
auto& zoneSettings = AudioMixer::getZoneSettings();
|
||||
|
||||
// find distance attenuation coefficient
|
||||
float attenuationPerDoublingInDistance = AudioMixer::getAttenuationPerDoublingInDistance();
|
||||
for (const auto& settings : zoneSettings) {
|
||||
if (audioZones[settings.source].area.contains(streamToAdd.getPosition()) &&
|
||||
audioZones[settings.listener].area.contains(listeningNodeStream.getPosition())) {
|
||||
attenuationPerDoublingInDistance = settings.coefficient;
|
||||
break;
|
||||
|
||||
float bestZonesVolume = FLT_MAX;
|
||||
float bestZonesCoefficient;
|
||||
for (const auto& sourceZone : audioZones) {
|
||||
if (sourceZone.second.listeners.size() > 0 && sourceZone.second.listeners.size() == sourceZone.second.coefficients.size()) {
|
||||
vec4 localSourcePosition = sourceZone.second.inverseTransform * vec4(streamToAdd.getPosition(), 1.0f);
|
||||
if (UNIT_BOX.contains(localSourcePosition)) {
|
||||
size_t listenerIndex = 0;
|
||||
for (const auto& listener : sourceZone.second.listeners) {
|
||||
const auto& listenerZone = audioZones.find(listener);
|
||||
if (listenerZone != audioZones.end()) {
|
||||
vec4 localListenerPosition = listenerZone->second.inverseTransform * vec4(listeningNodeStream.getPosition(), 1.0f);
|
||||
if (UNIT_BOX.contains(localListenerPosition)) {
|
||||
// This isn't an exact solution, but we target the smallest sum of volumes of the source and listener zones
|
||||
const float zonesVolume = sourceZone.second.volume + listenerZone->second.volume;
|
||||
if (zonesVolume < bestZonesVolume) {
|
||||
bestZonesVolume = zonesVolume;
|
||||
bestZonesCoefficient = sourceZone.second.coefficients[listenerIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
listenerIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestZonesVolume < FLT_MAX) {
|
||||
attenuationPerDoublingInDistance = bestZonesCoefficient;
|
||||
}
|
||||
|
||||
if (attenuationPerDoublingInDistance < 0.0f) {
|
||||
// translate a negative zone setting to distance limit
|
||||
const float MIN_DISTANCE_LIMIT = ATTN_DISTANCE_REF + 1.0f; // silent after 1m
|
||||
|
|
|
@ -1085,7 +1085,7 @@ void AvatarMixer::setupEntityQuery() {
|
|||
DependencyManager::set<AssignmentParentFinder>(entityTree);
|
||||
|
||||
connect(entityTree.get(), &EntityTree::addingEntityPointer, this, &AvatarMixer::entityAdded);
|
||||
connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityChange);
|
||||
connect(entityTree.get(), &EntityTree::deletingEntityPointer, this, &AvatarMixer::entityRemoved);
|
||||
|
||||
// ES query: {"avatarPriority": true, "type": "Zone"}
|
||||
QJsonObject priorityZoneQuery;
|
||||
|
@ -1140,7 +1140,7 @@ void AvatarMixer::entityAdded(EntityItem* entity) {
|
|||
if (entity->getType() == EntityTypes::Zone) {
|
||||
_dirtyHeroStatus = true;
|
||||
entity->registerChangeHandler([this](const EntityItemID& entityItemID) {
|
||||
entityChange();
|
||||
_dirtyHeroStatus = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1151,10 +1151,6 @@ void AvatarMixer::entityRemoved(EntityItem * entity) {
|
|||
}
|
||||
}
|
||||
|
||||
void AvatarMixer::entityChange() {
|
||||
_dirtyHeroStatus = true;
|
||||
}
|
||||
|
||||
void AvatarMixer::aboutToFinish() {
|
||||
DependencyManager::destroy<ResourceManager>();
|
||||
DependencyManager::destroy<ResourceCacheSharedItems>();
|
||||
|
|
|
@ -52,7 +52,6 @@ public slots:
|
|||
// Avatar zone possibly changed
|
||||
void entityAdded(EntityItem* entity);
|
||||
void entityRemoved(EntityItem* entity);
|
||||
void entityChange();
|
||||
|
||||
private slots:
|
||||
void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||
|
|
|
@ -1239,7 +1239,7 @@ void OctreeServer::beginRunning() {
|
|||
|
||||
// we need to ask the DS about agents so we can ping/reply with them
|
||||
nodeList->addSetOfNodeTypesToNodeInterestSet({ NodeType::Agent, NodeType::EntityScriptServer,
|
||||
NodeType::AvatarMixer });
|
||||
NodeType::AvatarMixer, NodeType::AudioMixer });
|
||||
|
||||
beforeRun(); // after payload has been processed
|
||||
|
||||
|
|
|
@ -1235,7 +1235,7 @@
|
|||
{
|
||||
"name": "zones",
|
||||
"type": "table",
|
||||
"label": "Zones",
|
||||
"label": "Zones (deprecated, use Zone Entities)",
|
||||
"help": "In this table you can define a set of zones in which you can specify various audio properties.",
|
||||
"numbered": false,
|
||||
"content_setting": true,
|
||||
|
@ -1287,7 +1287,7 @@
|
|||
{
|
||||
"name": "attenuation_coefficients",
|
||||
"type": "table",
|
||||
"label": "Attenuation Coefficients",
|
||||
"label": "Attenuation Coefficients (deprecated, use Zone Entities)",
|
||||
"help": "In this table you can set custom attenuation coefficients between audio zones",
|
||||
"content_setting": true,
|
||||
"numbered": true,
|
||||
|
@ -1317,7 +1317,7 @@
|
|||
{
|
||||
"name": "reverb",
|
||||
"type": "table",
|
||||
"label": "Reverb Settings",
|
||||
"label": "Reverb Settings (deprecated, use Zone Entities)",
|
||||
"help": "In this table you can set reverb levels for audio zones. For a medium-sized (e.g., 100 square meter) meeting room, try a decay time of around 1.5 seconds and a wet/dry mix of 25%. For an airplane hangar or cathedral, try a decay time of 4 seconds and a wet/dry mix of 50%.",
|
||||
"numbered": true,
|
||||
"content_setting": true,
|
||||
|
|
|
@ -44,6 +44,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation;
|
|||
SkyboxPropertyGroup EntityItemProperties::_staticSkybox;
|
||||
HazePropertyGroup EntityItemProperties::_staticHaze;
|
||||
BloomPropertyGroup EntityItemProperties::_staticBloom;
|
||||
ZoneAudioPropertyGroup EntityItemProperties::_staticAudio;
|
||||
KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
|
||||
AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight;
|
||||
GrabPropertyGroup EntityItemProperties::_staticGrab;
|
||||
|
@ -87,6 +88,7 @@ void EntityItemProperties::debugDump() const {
|
|||
getKeyLight().debugDump();
|
||||
getAmbientLight().debugDump();
|
||||
getBloom().debugDump();
|
||||
getAudio().debugDump();
|
||||
getGrab().debugDump();
|
||||
|
||||
qCDebug(entities) << " changed properties...";
|
||||
|
@ -613,6 +615,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
changedProperties += _skybox.getChangedProperties();
|
||||
changedProperties += _haze.getChangedProperties();
|
||||
changedProperties += _bloom.getChangedProperties();
|
||||
changedProperties += _audio.getChangedProperties();
|
||||
CHECK_PROPERTY_CHANGE(PROP_FLYING_ALLOWED, flyingAllowed);
|
||||
CHECK_PROPERTY_CHANGE(PROP_GHOSTING_ALLOWED, ghostingAllowed);
|
||||
CHECK_PROPERTY_CHANGE(PROP_FILTER_URL, filterURL);
|
||||
|
@ -853,7 +856,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* avatar entities, <code>false</code> if they won't be.
|
||||
* @property {Uuid} cloneOriginID - The ID of the entity that this entity was cloned from.
|
||||
*
|
||||
* @property {Uuid[]} renderWithZones=[]] - A list of entity IDs representing with which zones this entity should render.
|
||||
* @property {Uuid[]} renderWithZones=[] - A list of entity IDs representing with which zones this entity should render.
|
||||
* If it is empty, this entity will render normally. Otherwise, this entity will only render if your avatar is within
|
||||
* one of the zones in this list.
|
||||
* @property {BillboardMode} billboardMode="none" - Whether the entity is billboarded to face the camera. Use the rotation
|
||||
|
@ -1481,6 +1484,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.ComponentMode} bloomMode="inherit" - Configures the bloom in the zone.
|
||||
* @property {Entities.Bloom} bloom - The bloom properties of the zone.
|
||||
*
|
||||
* @property {Entities.ZoneAudio} audio - The audio properties of the zone.
|
||||
*
|
||||
* @property {boolean} flyingAllowed=true - <code>true</code> if visitors can fly in the zone; <code>false</code> if they
|
||||
* cannot. Only works for domain entities.
|
||||
* @property {boolean} ghostingAllowed=true - <code>true</code> if visitors with avatar collisions turned off will not
|
||||
|
@ -1834,6 +1839,7 @@ ScriptValue EntityItemProperties::copyToScriptValue(ScriptEngine* engine, bool s
|
|||
_skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
_audio.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
}
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed);
|
||||
|
@ -2211,6 +2217,7 @@ void EntityItemProperties::copyFromScriptValue(const ScriptValue& object, bool h
|
|||
_skybox.copyFromScriptValue(object, namesSet, _defaultSettings);
|
||||
_haze.copyFromScriptValue(object, namesSet, _defaultSettings);
|
||||
_bloom.copyFromScriptValue(object, namesSet, _defaultSettings);
|
||||
_audio.copyFromScriptValue(object, namesSet, _defaultSettings);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(flyingAllowed, bool, setFlyingAllowed);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(ghostingAllowed, bool, setGhostingAllowed);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(filterURL, QString, setFilterURL);
|
||||
|
@ -2496,6 +2503,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
_skybox.merge(other._skybox);
|
||||
_haze.merge(other._haze);
|
||||
_bloom.merge(other._bloom);
|
||||
_audio.merge(other._audio);
|
||||
COPY_PROPERTY_IF_CHANGED(flyingAllowed);
|
||||
COPY_PROPERTY_IF_CHANGED(ghostingAllowed);
|
||||
COPY_PROPERTY_IF_CHANGED(filterURL);
|
||||
|
@ -2892,6 +2900,13 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize);
|
||||
}
|
||||
{ // Audio
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients);
|
||||
}
|
||||
ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString);
|
||||
|
@ -3307,6 +3322,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
_staticBloom.setProperties(properties);
|
||||
_staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
_staticAudio.setProperties(properties);
|
||||
_staticAudio.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, properties.getFlyingAllowed());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, properties.getGhostingAllowed());
|
||||
APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, properties.getFilterURL());
|
||||
|
@ -3775,6 +3793,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
properties.getAudio().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
|
||||
|
@ -4147,6 +4166,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_skybox.markAllChanged();
|
||||
_haze.markAllChanged();
|
||||
_bloom.markAllChanged();
|
||||
_audio.markAllChanged();
|
||||
_flyingAllowedChanged = true;
|
||||
_ghostingAllowedChanged = true;
|
||||
_filterURLChanged = true;
|
||||
|
@ -4738,6 +4758,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
getSkybox().listChangedProperties(out);
|
||||
getHaze().listChangedProperties(out);
|
||||
getBloom().listChangedProperties(out);
|
||||
getAudio().listChangedProperties(out);
|
||||
if (flyingAllowedChanged()) {
|
||||
out += "flyingAllowed";
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include "BloomPropertyGroup.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
#include "RingGizmoPropertyGroup.h"
|
||||
#include "ZoneAudioPropertyGroup.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
|
@ -331,6 +332,7 @@ public:
|
|||
DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup);
|
||||
DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup);
|
||||
DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup);
|
||||
DEFINE_PROPERTY_GROUP(Audio, audio, ZoneAudioPropertyGroup);
|
||||
DEFINE_PROPERTY(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool, ZoneEntityItem::DEFAULT_FLYING_ALLOWED);
|
||||
DEFINE_PROPERTY(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool, ZoneEntityItem::DEFAULT_GHOSTING_ALLOWED);
|
||||
DEFINE_PROPERTY(PROP_FILTER_URL, FilterURL, filterURL, QString, ZoneEntityItem::DEFAULT_FILTER_URL);
|
||||
|
|
|
@ -167,6 +167,11 @@ enum EntityPropertyList {
|
|||
PROP_DERIVED_32,
|
||||
PROP_DERIVED_33,
|
||||
PROP_DERIVED_34,
|
||||
PROP_DERIVED_35,
|
||||
PROP_DERIVED_36,
|
||||
PROP_DERIVED_37,
|
||||
PROP_DERIVED_38,
|
||||
PROP_DERIVED_39,
|
||||
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
||||
|
@ -301,6 +306,12 @@ enum EntityPropertyList {
|
|||
PROP_AVATAR_PRIORITY = PROP_DERIVED_33,
|
||||
// Screen-sharing
|
||||
PROP_SCREENSHARE = PROP_DERIVED_34,
|
||||
// Audio
|
||||
PROP_REVERB_ENABLED = PROP_DERIVED_35,
|
||||
PROP_REVERB_TIME = PROP_DERIVED_36,
|
||||
PROP_REVERB_WET_LEVEL = PROP_DERIVED_37,
|
||||
PROP_LISTENER_ZONES = PROP_DERIVED_38,
|
||||
PROP_LISTENER_ATTENUATION_COEFFICIENTS = PROP_DERIVED_39,
|
||||
|
||||
// Polyvox
|
||||
PROP_VOXEL_VOLUME_SIZE = PROP_DERIVED_0,
|
||||
|
|
194
libraries/entities/src/ZoneAudioPropertyGroup.cpp
Normal file
194
libraries/entities/src/ZoneAudioPropertyGroup.cpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
//
|
||||
// ZoneAudioPropertyGroup.cpp
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by HifiExperiments on 11/28/23
|
||||
// Copyright 2023 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "ZoneAudioPropertyGroup.h"
|
||||
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
void ZoneAudioPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties, ScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_ENABLED, Audio, audio, ReverbEnabled, reverbEnabled);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_TIME, Audio, audio, ReverbTime, reverbTime);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_REVERB_WET_LEVEL, Audio, audio, ReverbWetLevel, reverbWetLevel);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ZONES, Audio, audio, ListenerZones, listenerZones);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, Audio, audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients);
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::copyFromScriptValue(const ScriptValue& object, const QSet<QString> &namesSet, bool& _defaultSettings) {
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbEnabled, bool, setReverbEnabled);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbTime, float, setReverbTime);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, reverbWetLevel, float, setReverbWetLevel);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerZones, qVectorQUuid, setListenerZones);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(audio, listenerAttenuationCoefficients, qVectorFloat, setListenerAttenuationCoefficients);
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::merge(const ZoneAudioPropertyGroup& other) {
|
||||
COPY_PROPERTY_IF_CHANGED(reverbEnabled);
|
||||
COPY_PROPERTY_IF_CHANGED(reverbTime);
|
||||
COPY_PROPERTY_IF_CHANGED(reverbWetLevel);
|
||||
COPY_PROPERTY_IF_CHANGED(listenerZones);
|
||||
COPY_PROPERTY_IF_CHANGED(listenerAttenuationCoefficients);
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::debugDump() const {
|
||||
qCDebug(entities) << " ZoneAudioPropertyGroup: ---------------------------------------------";
|
||||
qCDebug(entities) << " _reverbEnabled:" << _reverbEnabled;
|
||||
qCDebug(entities) << " _reverbTime:" << _reverbTime;
|
||||
qCDebug(entities) << " _reverbWetLevel:" << _reverbWetLevel;
|
||||
qCDebug(entities) << " _listenerZones:" << _listenerZones;
|
||||
qCDebug(entities) << " _listenerAttenuationCoefficients:" << _listenerAttenuationCoefficients;
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::listChangedProperties(QList<QString>& out) {
|
||||
if (reverbEnabledChanged()) {
|
||||
out << "reverbEnabled";
|
||||
}
|
||||
if (reverbTimeChanged()) {
|
||||
out << "reverbTime";
|
||||
}
|
||||
if (reverbWetLevelChanged()) {
|
||||
out << "reverbWetLevel";
|
||||
}
|
||||
if (listenerZonesChanged()) {
|
||||
out << "listenerZones";
|
||||
}
|
||||
if (listenerAttenuationCoefficientsChanged()) {
|
||||
out << "listenerAttenuationCoefficients";
|
||||
}
|
||||
}
|
||||
|
||||
bool ZoneAudioPropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ZoneAudioPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) {
|
||||
int bytesRead = 0;
|
||||
bool overwriteLocalData = true;
|
||||
bool somethingChanged = false;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled);
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime);
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel);
|
||||
READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector<QUuid>, setListenerZones);
|
||||
READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector<float>, setListenerAttenuationCoefficients);
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_ENABLED, ReverbEnabled);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_TIME, ReverbTime);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_REVERB_WET_LEVEL, ReverbWetLevel);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ZONES, ListenerZones);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
Q_UNUSED(somethingChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::markAllChanged() {
|
||||
_reverbEnabledChanged = true;
|
||||
_reverbTimeChanged = true;
|
||||
_reverbWetLevelChanged = true;
|
||||
_listenerZonesChanged = true;
|
||||
_listenerAttenuationCoefficientsChanged = true;
|
||||
}
|
||||
|
||||
EntityPropertyFlags ZoneAudioPropertyGroup::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_REVERB_ENABLED, reverbEnabled);
|
||||
CHECK_PROPERTY_CHANGE(PROP_REVERB_TIME, reverbTime);
|
||||
CHECK_PROPERTY_CHANGE(PROP_REVERB_WET_LEVEL, reverbWetLevel);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LISTENER_ZONES, listenerZones);
|
||||
CHECK_PROPERTY_CHANGE(PROP_LISTENER_ATTENUATION_COEFFICIENTS, listenerAttenuationCoefficients);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::getProperties(EntityItemProperties& properties) const {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbEnabled, getReverbEnabled);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbTime, getReverbTime);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ReverbWetLevel, getReverbWetLevel);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerZones, getListenerZones);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Audio, ListenerAttenuationCoefficients, getListenerAttenuationCoefficients);
|
||||
}
|
||||
|
||||
bool ZoneAudioPropertyGroup::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbEnabled, reverbEnabled, setReverbEnabled);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbTime, reverbTime, setReverbTime);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ReverbWetLevel, reverbWetLevel, setReverbWetLevel);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerZones, listenerZones, setListenerZones);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Audio, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, setListenerAttenuationCoefficients);
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
EntityPropertyFlags ZoneAudioPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_REVERB_ENABLED;
|
||||
requestedProperties += PROP_REVERB_TIME;
|
||||
requestedProperties += PROP_REVERB_WET_LEVEL;
|
||||
requestedProperties += PROP_LISTENER_ZONES;
|
||||
requestedProperties += PROP_LISTENER_ATTENUATION_COEFFICIENTS;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void ZoneAudioPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_ENABLED, getReverbEnabled());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_TIME, getReverbTime());
|
||||
APPEND_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, getReverbWetLevel());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LISTENER_ZONES, getListenerZones());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, getListenerAttenuationCoefficients());
|
||||
}
|
||||
|
||||
int ZoneAudioPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) {
|
||||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_ENABLED, bool, setReverbEnabled);
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_TIME, float, setReverbTime);
|
||||
READ_ENTITY_PROPERTY(PROP_REVERB_WET_LEVEL, float, setReverbWetLevel);
|
||||
READ_ENTITY_PROPERTY(PROP_LISTENER_ZONES, QVector<QUuid>, setListenerZones);
|
||||
READ_ENTITY_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, QVector<float>, setListenerAttenuationCoefficients);
|
||||
|
||||
return bytesRead;
|
||||
}
|
97
libraries/entities/src/ZoneAudioPropertyGroup.h
Normal file
97
libraries/entities/src/ZoneAudioPropertyGroup.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// ZoneAudioPropertyGroup.h
|
||||
// libraries/entities/src
|
||||
//
|
||||
// Created by HifiExperiments on 11/28/23
|
||||
// Copyright 2023 Overte e.V.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#ifndef hifi_ZoneAudioPropertyGroup_h
|
||||
#define hifi_ZoneAudioPropertyGroup_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "PropertyGroup.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
class EntityItemProperties;
|
||||
class EncodeBitstreamParams;
|
||||
class OctreePacketData;
|
||||
class EntityTreeElementExtraEncodeData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
class ScriptEngine;
|
||||
class ScriptValue;
|
||||
|
||||
/*@jsdoc
|
||||
* Zone audio is defined by the following properties:
|
||||
* @typedef {object} Entities.ZoneAudio
|
||||
* @property {boolean} reverbEnabled=false - If reverb should be enabled for listeners in this zone.
|
||||
* @property {number} reverbTime=1.0 - The time (seconds) for the reverb tail to decay by 60dB, also known as RT60.
|
||||
* @property {number} reverbWetLevel=50 - Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%).
|
||||
* @property {Uuid[]} listenerZones=[] - A list of entity IDs representing listener zones with this zone as a source.
|
||||
* Sounds from this zone being heard by a listener in a listener zone will be attenuated by the corresponding
|
||||
* <code>listenerAttenuationCoefficient</code>.
|
||||
* @property {number[]} listenerAttenuationCoefficients=[] - A list of attenuation coefficients. Each coefficient will be
|
||||
* applied to sounds coming from this zone and being heard by a listener in the corresponding <code>listenerZone</code>.
|
||||
*/
|
||||
class ZoneAudioPropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
// EntityItemProperty related helpers
|
||||
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, ScriptValue& properties,
|
||||
ScriptEngine* engine, bool skipDefaults,
|
||||
EntityItemProperties& defaultEntityProperties) const override;
|
||||
virtual void copyFromScriptValue(const ScriptValue& object, const QSet<QString> &namesSet, bool& _defaultSettings) override;
|
||||
|
||||
void merge(const ZoneAudioPropertyGroup& other);
|
||||
|
||||
virtual void debugDump() const override;
|
||||
virtual void listChangedProperties(QList<QString>& out) override;
|
||||
|
||||
virtual bool appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
|
||||
const unsigned char*& dataAt, int& processedBytes) override;
|
||||
virtual void markAllChanged() override;
|
||||
virtual EntityPropertyFlags getChangedProperties() const override;
|
||||
|
||||
// EntityItem related helpers
|
||||
// methods for getting/setting all properties of an entity
|
||||
virtual void getProperties(EntityItemProperties& propertiesOut) const override;
|
||||
|
||||
/// returns true if something changed
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
DEFINE_PROPERTY(PROP_REVERB_ENABLED, ReverbEnabled, reverbEnabled, bool, false);
|
||||
DEFINE_PROPERTY(PROP_REVERB_TIME, ReverbTime, reverbTime, float, 1.0f);
|
||||
DEFINE_PROPERTY(PROP_REVERB_WET_LEVEL, ReverbWetLevel, reverbWetLevel, float, 50.0f);
|
||||
DEFINE_PROPERTY(PROP_LISTENER_ZONES, ListenerZones, listenerZones, QVector<QUuid>, QVector<QUuid>());
|
||||
DEFINE_PROPERTY(PROP_LISTENER_ATTENUATION_COEFFICIENTS, ListenerAttenuationCoefficients, listenerAttenuationCoefficients, QVector<float>, QVector<float>());
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_ZoneAudioPropertyGroup_h
|
|
@ -60,6 +60,7 @@ EntityItemProperties ZoneEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
});
|
||||
_hazeProperties.getProperties(properties);
|
||||
_bloomProperties.getProperties(properties);
|
||||
_audioProperties.getProperties(properties);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed);
|
||||
|
@ -90,6 +91,7 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
|||
});
|
||||
_hazePropertiesChanged |= _hazeProperties.setProperties(properties);
|
||||
_bloomPropertiesChanged |= _bloomProperties.setProperties(properties);
|
||||
bool audioPropertiesChanged = _audioProperties.setProperties(properties);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed);
|
||||
|
@ -103,8 +105,8 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(avatarPriority, setAvatarPriority);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(screenshare, setScreenshare);
|
||||
|
||||
somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged ||
|
||||
_skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged;
|
||||
somethingChanged |= _keyLightPropertiesChanged || _ambientLightPropertiesChanged || _skyboxPropertiesChanged ||
|
||||
_hazePropertiesChanged || _bloomPropertiesChanged || audioPropertiesChanged;
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
@ -168,6 +170,13 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
dataAt += bytesFromBloom;
|
||||
}
|
||||
|
||||
{
|
||||
int bytesFromAudio = _audioProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData, somethingChanged);
|
||||
bytesRead += bytesFromAudio;
|
||||
dataAt += bytesFromAudio;
|
||||
}
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed);
|
||||
READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed);
|
||||
READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL);
|
||||
|
@ -194,6 +203,7 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += _skyboxProperties.getEntityProperties(params);
|
||||
requestedProperties += _hazeProperties.getEntityProperties(params);
|
||||
requestedProperties += _bloomProperties.getEntityProperties(params);
|
||||
requestedProperties += _audioProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_FLYING_ALLOWED;
|
||||
requestedProperties += PROP_GHOSTING_ALLOWED;
|
||||
|
@ -235,6 +245,8 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
_bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
_audioProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed());
|
||||
APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed());
|
||||
|
@ -267,6 +279,7 @@ void ZoneEntityItem::debugDump() const {
|
|||
_skyboxProperties.debugDump();
|
||||
_hazeProperties.debugDump();
|
||||
_bloomProperties.debugDump();
|
||||
_audioProperties.debugDump();
|
||||
}
|
||||
|
||||
void ZoneEntityItem::setShapeType(ShapeType type) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "ZoneAudioPropertyGroup.h"
|
||||
|
||||
class ZoneEntityItem : public EntityItem {
|
||||
public:
|
||||
|
@ -46,10 +47,9 @@ public:
|
|||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
|
||||
static bool getZonesArePickable() { return _zonesArePickable; }
|
||||
|
@ -90,6 +90,7 @@ public:
|
|||
|
||||
const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; }
|
||||
const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; }
|
||||
const ZoneAudioPropertyGroup& getAudioProperties() const { return _audioProperties; }
|
||||
|
||||
bool getFlyingAllowed() const { return _flyingAllowed; }
|
||||
void setFlyingAllowed(bool value) { _flyingAllowed = value; }
|
||||
|
@ -152,6 +153,7 @@ protected:
|
|||
SkyboxPropertyGroup _skyboxProperties;
|
||||
HazePropertyGroup _hazeProperties;
|
||||
BloomPropertyGroup _bloomProperties;
|
||||
ZoneAudioPropertyGroup _audioProperties;
|
||||
|
||||
bool _flyingAllowed { DEFAULT_FLYING_ALLOWED };
|
||||
bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED };
|
||||
|
@ -167,7 +169,7 @@ protected:
|
|||
bool _keyLightPropertiesChanged { false };
|
||||
bool _ambientLightPropertiesChanged { false };
|
||||
bool _skyboxPropertiesChanged { false };
|
||||
bool _hazePropertiesChanged{ false };
|
||||
bool _hazePropertiesChanged { false };
|
||||
bool _bloomPropertiesChanged { false };
|
||||
|
||||
static bool _drawZoneBoundaries;
|
||||
|
|
|
@ -293,6 +293,7 @@ enum class EntityVersion : PacketVersion {
|
|||
Mirror,
|
||||
EntityTags,
|
||||
WantsKeyboardFocus,
|
||||
AudioZones,
|
||||
|
||||
// Add new versions above here
|
||||
NUM_PACKET_TYPE,
|
||||
|
|
|
@ -158,6 +158,21 @@
|
|||
"bloom.bloomSize": {
|
||||
"tooltip": "The radius of bloom. The higher the value, the larger the bloom."
|
||||
},
|
||||
"audio.reverbEnabled": {
|
||||
"tooltip": "If reverb should be enabled for listeners in this zone."
|
||||
},
|
||||
"audio.reverbTime": {
|
||||
"tooltip": "The time (seconds) for the reverb tail to decay by 60dB."
|
||||
},
|
||||
"audio.reverbWetLevel": {
|
||||
"tooltip": "Adjusts the wet/dry percentage, from completely dry (0%) to completely wet (100%)."
|
||||
},
|
||||
"audio.listenerZones": {
|
||||
"tooltip": "A list of entity IDs representing listener zones with this zone as a source."
|
||||
},
|
||||
"audio.listenerAttenuationCoefficients": {
|
||||
"tooltip": "A list of attenuation coefficients."
|
||||
},
|
||||
"avatarPriority": {
|
||||
"tooltip": "Alter Avatars' update priorities."
|
||||
},
|
||||
|
|
|
@ -620,6 +620,42 @@ const GROUPS = [
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "zone_audio",
|
||||
label: "ZONE AUDIO",
|
||||
properties: [
|
||||
{
|
||||
label: "Enable Reverb",
|
||||
type: "bool",
|
||||
propertyID: "audio.enableReverb"
|
||||
},
|
||||
{
|
||||
label: "Reverb Time",
|
||||
type: "number-draggable",
|
||||
min: 0,
|
||||
max: 10,
|
||||
step: 0.1,
|
||||
decimals: 2,
|
||||
propertyID: "audio.reverbTime",
|
||||
showPropertyRule: { "audio.enableReverb": "true" },
|
||||
},
|
||||
{
|
||||
label: "Reverb Wet Level",
|
||||
type: "number-draggable",
|
||||
min: 0,
|
||||
max: 100,
|
||||
step: 1,
|
||||
decimals: 1,
|
||||
propertyID: "audio.reverbWetLevel",
|
||||
showPropertyRule: { "audio.enableReverb": "true" },
|
||||
},
|
||||
{
|
||||
label: "Listener Zones",
|
||||
type: "multipleZonesSelection",
|
||||
propertyID: "audio.listenerZones",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "model",
|
||||
label: "MODEL",
|
||||
|
@ -1764,7 +1800,7 @@ const GROUPS_PER_TYPE = {
|
|||
Shape: [ 'base', 'shape', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
|
||||
Text: [ 'base', 'text', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
|
||||
Zone: [ 'base', 'zone', 'zone_key_light', 'zone_skybox', 'zone_ambient_light', 'zone_haze',
|
||||
'zone_bloom', 'zone_avatar_priority', 'spatial', 'behavior', 'scripts', 'physics' ],
|
||||
'zone_bloom', 'zone_avatar_priority', 'zone_audio', 'spatial', 'behavior', 'scripts', 'physics' ],
|
||||
Model: [ 'base', 'model', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
|
||||
Image: [ 'base', 'image', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
|
||||
Web: [ 'base', 'web', 'spatial', 'behavior', 'scripts', 'collision', 'physics' ],
|
||||
|
|
BIN
scripts/system/create/entityProperties/html/tabs/zone_audio.png
Normal file
BIN
scripts/system/create/entityProperties/html/tabs/zone_audio.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 598 B |
Loading…
Reference in a new issue