// // 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 #include "EntitiesLogging.h" #include "EntityItemProperties.h" #include "EntityTree.h" #include "EntityTreeElement.h" EntityItemPointer SoundEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { std::shared_ptr 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(); 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 lock(_soundLock); _updateNeeded = false; withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->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 lock(_soundLock); withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_soundURL); } }); if (_sound) { if (_sound->isLoaded()) { updateSound(true); } else { connect(_sound.data(), &Resource::finished, this, [&] { updateSound(true); }); } } } } QString SoundEntityItem::getSoundURL() const { return resultWithReadLock([&] { 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([&] { 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([&] { 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([&] { 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([&] { 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([&] { 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([&] { 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 lock(_soundLock); bool createdSound = false; withReadLock([&] { if (shouldCreateSound(tree)) { _sound = DependencyManager::get()->getSound(_soundURL); createdSound = true; } }); if (!createdSound) { _sound = nullptr; if (_injector) { DependencyManager::get()->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([&] { 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()->setOptions(_injector, options); } else { _injector = DependencyManager::get()->playSound(_sound, options); } if (lock) { _soundLock.unlock(); } return true; } void SoundEntityItem::updateSound(bool restart) { std::lock_guard lock(_soundLock); if (!_sound) { return; } if (restart) { if (_injector) { DependencyManager::get()->stop(_injector); _injector = nullptr; } } if (_playing) { restartSound(); } else { if (_injector) { DependencyManager::get()->stop(_injector); _injector = nullptr; } } }