//// Created by HifiExperiments on 12/30/2023// 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// #include "SoundEntityItem.h" #include <QtCore/QDebug> #include "EntitiesLogging.h"#include "EntityItemProperties.h"#include "EntityTree.h"#include "EntityTreeElement.h" EntityItemPointer SoundEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { std::shared_ptr<SoundEntityItem> entity(new SoundEntityItem(entityID), [](SoundEntityItem* ptr) { ptr->deleteLater(); }); entity->setProperties(properties); return entity;} // our non-pure virtual subclass for now...SoundEntityItem::SoundEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) { _type = EntityTypes::Sound;} SoundEntityItem::~SoundEntityItem() { auto manager = DependencyManager::get<AudioInjectorManager>(); if (manager && _injector) { manager->stop(_injector); }} EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class @Sound_ENTITY_COPY_TO@ return properties;} bool SoundEntityItem::setSubClassProperties(const EntityItemProperties& properties) { bool somethingChanged = false; @Sound_ENTITY_SET_FROM@ return somethingChanged;} EntityPropertyFlags SoundEntityItem::getEntityProperties(EncodeBitstreamParams& params) const { EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params); @Sound_REQUESTED_PROPS@ return requestedProperties;} void SoundEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, EntityPropertyFlags& requestedProperties, EntityPropertyFlags& propertyFlags, EntityPropertyFlags& propertiesDidntFit, int& propertyCount, OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; @Sound_ENTITY_APPEND@ } int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, ReadBitstreamToTreeParams& args, EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) { int bytesRead = 0; const unsigned char* dataAt = data; @Sound_ENTITY_READ@ return bytesRead;} void SoundEntityItem::debugDump() const { qCDebug(entities) << "SoundEntityItem id:" << getEntityItemID() << "---------------------------------------------"; qCDebug(entities) << " name:" << _name; qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow()); qCDebug(entities) << " pointer:" << this; @Sound_ENTITY_DEBUG@ } bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const { bool clientShouldMakeSound = _localOnly || isMyAvatarEntity() || tree->isServerlessMode(); bool serverShouldMakeSound = !_localOnly; return (clientShouldMakeSound && tree->getIsClient()) || (serverShouldMakeSound && tree->isEntityServer());} void SoundEntityItem::update(const quint64& now) { const auto tree = getTree(); if (tree) { std::lock_guard<std::recursive_mutex> lock(_soundLock); _updateNeeded = false; withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get<SoundCache>()->getSound(_soundURL); } }); if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } }} void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) { EntityItem::locationChanged(tellPhysics, tellChildren); updateSound();} void SoundEntityItem::dimensionsChanged() { EntityItem::dimensionsChanged(); updateSound();} void SoundEntityItem::setSoundURL(const QString& value) { bool changed = false; withWriteLock([&] { if (value != _soundURL) { _soundURL = value; changed = true; } }); if (changed) { const auto tree = getTree(); if (!tree) { _updateNeeded = true; return; } std::lock_guard<std::recursive_mutex> lock(_soundLock); withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get<SoundCache>()->getSound(_soundURL); } }); if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } }} QString SoundEntityItem::getSoundURL() const { return resultWithReadLock<QString>([&] { return _soundURL; });} void SoundEntityItem::setVolume(float value) { bool changed = false; withWriteLock([&] { if (value != _volume) { _volume = value; changed = true; } }); if (changed) { updateSound(); }} float SoundEntityItem::getVolume() const { return resultWithReadLock<float>([&] { return _volume; });} void SoundEntityItem::setTimeOffset(float value) { bool changed = false; withWriteLock([&] { if (value != _timeOffset) { _timeOffset = value; changed = true; } }); if (changed) { updateSound(true); }} float SoundEntityItem::getTimeOffset() const { return resultWithReadLock<float>([&] { return _timeOffset; });} void SoundEntityItem::setPitch(float value) { bool changed = false; withWriteLock([&] { if (value != _pitch) { _pitch = value; changed = true; } }); if (changed) { updateSound(true); }} float SoundEntityItem::getPitch() const { return resultWithReadLock<float>([&] { return _pitch; });} void SoundEntityItem::setPlaying(bool value) { bool changed = false; withWriteLock([&] { if (value != _playing) { _playing = value; changed = true; } }); if (changed) { updateSound(); }} bool SoundEntityItem::getPlaying() const { return resultWithReadLock<float>([&] { return _playing; });} void SoundEntityItem::setLoop(bool value) { bool changed = false; withWriteLock([&] { if (value != _loop) { _loop = value; changed = true; } }); if (changed) { updateSound(true); }} bool SoundEntityItem::getLoop() const { return resultWithReadLock<float>([&] { return _loop; });} void SoundEntityItem::setPositional(bool value) { bool changed = false; withWriteLock([&] { if (value != _positional) { _positional = value; changed = true; } }); if (changed) { updateSound(); }} bool SoundEntityItem::getPositional() const { return resultWithReadLock<float>([&] { return _positional; });} void SoundEntityItem::setLocalOnly(bool value) { bool changed = false; withWriteLock([&] { if (value != _localOnly) { _localOnly = value; changed = true; } }); if (changed) { const auto tree = getTree(); if (!tree) { _updateNeeded = true; return; } std::lock_guard<std::recursive_mutex> lock(_soundLock); bool createdSound = false; withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get<SoundCache>()->getSound(_soundURL); createdSound = true; } }); if (!createdSound) { _sound = nullptr; if (_injector) { DependencyManager::get<AudioInjectorManager>()->stop(_injector); _injector = nullptr; } } if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } }} bool SoundEntityItem::getLocalOnly() const { return resultWithReadLock<float>([&] { return _localOnly; });} bool SoundEntityItem::restartSound(bool lock) { if (lock) { _soundLock.lock(); } if (!_sound) { if (lock) { _soundLock.unlock(); } return false; } AudioInjectorOptions options; withReadLock([&] { const glm::quat orientation = getWorldOrientation(); options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint())); options.positionSet = _positional; options.volume = _volume; options.loop = _loop; options.orientation = orientation; options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic options.secondOffset = _timeOffset; options.pitch = _pitch; }); // stereo option isn't set from script, this comes from sound metadata or filename options.stereo = _sound->isStereo(); options.ambisonic = _sound->isAmbisonic(); if (_injector) { DependencyManager::get<AudioInjectorManager>()->setOptions(_injector, options); } else { _injector = DependencyManager::get<AudioInjectorManager>()->playSound(_sound, options); } if (lock) { _soundLock.unlock(); } return true;} void SoundEntityItem::updateSound(bool restart) { std::lock_guard<std::recursive_mutex> lock(_soundLock); if (!_sound) { return; } if (restart) { if (_injector) { DependencyManager::get<AudioInjectorManager>()->stop(_injector); _injector = nullptr; } } if (_playing) { restartSound(); } else { if (_injector) { DependencyManager::get<AudioInjectorManager>()->stop(_injector); _injector = nullptr; } }}
//
// Created by HifiExperiments on 12/30/2023
// 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
//
#include "SoundEntityItem.h"
#include <QtCore/QDebug>
#include "EntitiesLogging.h"
#include "EntityItemProperties.h"
#include "EntityTree.h"
#include "EntityTreeElement.h"
EntityItemPointer SoundEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
std::shared_ptr<SoundEntityItem> entity(new SoundEntityItem(entityID), [](SoundEntityItem* ptr) { ptr->deleteLater(); });
entity->setProperties(properties);
return entity;
}
// our non-pure virtual subclass for now...
SoundEntityItem::SoundEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
_type = EntityTypes::Sound;
}
SoundEntityItem::~SoundEntityItem() {
auto manager = DependencyManager::get<AudioInjectorManager>();
if (manager && _injector) {
manager->stop(_injector);
}
}
EntityItemProperties SoundEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
@Sound_ENTITY_COPY_TO@
return properties;
}
bool SoundEntityItem::setSubClassProperties(const EntityItemProperties& properties) {
bool somethingChanged = false;
@Sound_ENTITY_SET_FROM@
return somethingChanged;
}
EntityPropertyFlags SoundEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
@Sound_REQUESTED_PROPS@
return requestedProperties;
}
void SoundEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
@Sound_ENTITY_APPEND@
}
int SoundEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
bool& somethingChanged) {
int bytesRead = 0;
const unsigned char* dataAt = data;
@Sound_ENTITY_READ@
return bytesRead;
}
void SoundEntityItem::debugDump() const {
qCDebug(entities) << "SoundEntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " name:" << _name;
qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition());
qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions());
qCDebug(entities) << " editedAgo:" << debugTime(getLastEdited(), usecTimestampNow());
qCDebug(entities) << " pointer:" << this;
@Sound_ENTITY_DEBUG@
}
bool SoundEntityItem::shouldCreateSound(const EntityTreePointer& tree) const {
bool clientShouldMakeSound = _localOnly || isMyAvatarEntity() || tree->isServerlessMode();
bool serverShouldMakeSound = !_localOnly;
return (clientShouldMakeSound && tree->getIsClient()) || (serverShouldMakeSound && tree->isEntityServer());
}
void SoundEntityItem::update(const quint64& now) {
const auto tree = getTree();
if (tree) {
std::lock_guard<std::recursive_mutex> lock(_soundLock);
_updateNeeded = false;
withReadLock([&] {
if (shouldCreateSound(tree)) {
_sound = DependencyManager::get<SoundCache>()->getSound(_soundURL);
}
});
if (_sound) {
if (_sound->isLoaded()) {
updateSound(true);
} else {
connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); });
}
}
}
}
void SoundEntityItem::locationChanged(bool tellPhysics, bool tellChildren) {
EntityItem::locationChanged(tellPhysics, tellChildren);
updateSound();
}
void SoundEntityItem::dimensionsChanged() {
EntityItem::dimensionsChanged();
updateSound();
}
void SoundEntityItem::setSoundURL(const QString& value) {
bool changed = false;
withWriteLock([&] {
if (value != _soundURL) {
_soundURL = value;
changed = true;
}
});
if (changed) {
const auto tree = getTree();
if (!tree) {
_updateNeeded = true;
return;
}
std::lock_guard<std::recursive_mutex> lock(_soundLock);
withReadLock([&] {
if (shouldCreateSound(tree)) {
_sound = DependencyManager::get<SoundCache>()->getSound(_soundURL);
}
});
if (_sound) {
if (_sound->isLoaded()) {
updateSound(true);
} else {
connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); });
}
}
}
}
QString SoundEntityItem::getSoundURL() const {
return resultWithReadLock<QString>([&] {
return _soundURL;
});
}
void SoundEntityItem::setVolume(float value) {
bool changed = false;
withWriteLock([&] {
if (value != _volume) {
_volume = value;
changed = true;
}
});
if (changed) {
updateSound();
}
}
float SoundEntityItem::getVolume() const {
return resultWithReadLock<float>([&] {
return _volume;
});
}
void SoundEntityItem::setTimeOffset(float value) {
bool changed = false;
withWriteLock([&] {
if (value != _timeOffset) {
_timeOffset = value;
changed = true;
}
});
if (changed) {
updateSound(true);
}
}
float SoundEntityItem::getTimeOffset() const {
return resultWithReadLock<float>([&] {
return _timeOffset;
});
}
void SoundEntityItem::setPitch(float value) {
bool changed = false;
withWriteLock([&] {
if (value != _pitch) {
_pitch = value;
changed = true;
}
});
if (changed) {
updateSound(true);
}
}
float SoundEntityItem::getPitch() const {
return resultWithReadLock<float>([&] {
return _pitch;
});
}
void SoundEntityItem::setPlaying(bool value) {
bool changed = false;
withWriteLock([&] {
if (value != _playing) {
_playing = value;
changed = true;
}
});
if (changed) {
updateSound();
}
}
bool SoundEntityItem::getPlaying() const {
return resultWithReadLock<float>([&] {
return _playing;
});
}
void SoundEntityItem::setLoop(bool value) {
bool changed = false;
withWriteLock([&] {
if (value != _loop) {
_loop = value;
changed = true;
}
});
if (changed) {
updateSound(true);
}
}
bool SoundEntityItem::getLoop() const {
return resultWithReadLock<float>([&] {
return _loop;
});
}
void SoundEntityItem::setPositional(bool value) {
bool changed = false;
withWriteLock([&] {
if (value != _positional) {
_positional = value;
changed = true;
}
});
if (changed) {
updateSound();
}
}
bool SoundEntityItem::getPositional() const {
return resultWithReadLock<float>([&] {
return _positional;
});
}
void SoundEntityItem::setLocalOnly(bool value) {
bool changed = false;
withWriteLock([&] {
if (value != _localOnly) {
_localOnly = value;
changed = true;
}
});
if (changed) {
const auto tree = getTree();
if (!tree) {
_updateNeeded = true;
return;
}
std::lock_guard<std::recursive_mutex> lock(_soundLock);
bool createdSound = false;
withReadLock([&] {
if (shouldCreateSound(tree)) {
_sound = DependencyManager::get<SoundCache>()->getSound(_soundURL);
createdSound = true;
}
});
if (!createdSound) {
_sound = nullptr;
if (_injector) {
DependencyManager::get<AudioInjectorManager>()->stop(_injector);
_injector = nullptr;
}
}
if (_sound) {
if (_sound->isLoaded()) {
updateSound(true);
} else {
connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); });
}
}
}
}
bool SoundEntityItem::getLocalOnly() const {
return resultWithReadLock<float>([&] {
return _localOnly;
});
}
bool SoundEntityItem::restartSound(bool lock) {
if (lock) {
_soundLock.lock();
}
if (!_sound) {
if (lock) {
_soundLock.unlock();
}
return false;
}
AudioInjectorOptions options;
withReadLock([&] {
const glm::quat orientation = getWorldOrientation();
options.position = getWorldPosition() + orientation * (getScaledDimensions() * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));
options.positionSet = _positional;
options.volume = _volume;
options.loop = _loop;
options.orientation = orientation;
options.localOnly = _localOnly || _sound->isAmbisonic(); // force localOnly when ambisonic
options.secondOffset = _timeOffset;
options.pitch = _pitch;
});
// stereo option isn't set from script, this comes from sound metadata or filename
options.stereo = _sound->isStereo();
options.ambisonic = _sound->isAmbisonic();
if (_injector) {
DependencyManager::get<AudioInjectorManager>()->setOptions(_injector, options);
} else {
_injector = DependencyManager::get<AudioInjectorManager>()->playSound(_sound, options);
}
if (lock) {
_soundLock.unlock();
}
return true;
}
void SoundEntityItem::updateSound(bool restart) {
std::lock_guard<std::recursive_mutex> lock(_soundLock);
if (!_sound) {
return;
}
if (restart) {
if (_injector) {
DependencyManager::get<AudioInjectorManager>()->stop(_injector);
_injector = nullptr;
}
}
if (_playing) {
restartSound();
} else {
if (_injector) {
DependencyManager::get<AudioInjectorManager>()->stop(_injector);
_injector = nullptr;
}
}
}