From b8632bb00a249f98baccb6c6bc695747b81aef21 Mon Sep 17 00:00:00 2001 From: Rye Mutt <rye@alchemyviewer.org> Date: Wed, 10 Nov 2021 23:17:07 -0500 Subject: [PATCH] Add sound explorer --- indra/llaudio/llaudioengine.cpp | 95 +++- indra/llaudio/llaudioengine.h | 55 +- indra/newview/CMakeLists.txt | 2 + indra/newview/alfloaterexploresounds.cpp | 530 ++++++++++++++++++ indra/newview/alfloaterexploresounds.h | 53 ++ indra/newview/llviewerfloaterreg.cpp | 2 + indra/newview/llviewermessage.cpp | 2 +- .../default/xui/en/floater_explore_sounds.xml | 55 ++ .../skins/default/xui/en/menu_viewer.xml | 11 + 9 files changed, 792 insertions(+), 13 deletions(-) create mode 100644 indra/newview/alfloaterexploresounds.cpp create mode 100644 indra/newview/alfloaterexploresounds.h create mode 100644 indra/newview/skins/default/xui/en/floater_explore_sounds.xml diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp index f3c49422371..238fbed1d96 100644 --- a/indra/llaudio/llaudioengine.cpp +++ b/indra/llaudio/llaudioengine.cpp @@ -46,7 +46,6 @@ extern void request_sound(const LLUUID &sound_guid); LLAudioEngine* gAudiop = NULL; - // // LLAudioEngine implementation // @@ -805,7 +804,7 @@ F64 LLAudioEngine::mapWindVecToPan(const LLVector3& wind_vec) void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, - const S32 type, const LLVector3d &pos_global) + const S32 type, const LLVector3d &pos_global, const LLUUID& source_object, const LLUUID& audio_source_id) { // Create a new source (since this can't be associated with an existing source. //LL_INFOS() << "Localized: " << audio_uuid << LL_ENDL; @@ -816,10 +815,13 @@ void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_i return; } - LLUUID source_id; - source_id.generate(); + LLUUID source_id = audio_source_id; + if (source_id.isNull()) + { + source_id.generate(); + } - LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain, type); + LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain, type, source_object, true); addAudioSource(asp); if (pos_global.isExactlyZero()) { @@ -1269,13 +1271,73 @@ void LLAudioEngine::assetCallback(const LLUUID &uuid, LLAssetType::EType type, v gAudiop->startNextTransfer(); } +void LLAudioEngine::logSoundPlay(const LLUUID& id, LLVector3d position, S32 type, const LLUUID& assetid, const LLUUID& ownerid, const LLUUID& sourceid, bool is_trigger, bool is_looped) +{ + pruneSoundLog(); + if (mSoundHistory.size() > 2048) + return; // Might clear out oldest entries before giving up? + + LLSoundHistoryItem item; + item.mID = id; + item.mPosition = position; + item.mType = type; + item.mAssetID = assetid; + item.mOwnerID = ownerid; + item.mSourceID = sourceid; + item.mPlaying = true; + item.mTimeStarted = LLTimer::getElapsedSeconds(); + item.mTimeStopped = F64_MAX; + item.mIsTrigger = is_trigger; + item.mIsLooped = is_looped; + + item.mReviewed = false; + item.mReviewedCollision = false; + + mSoundHistory.insert_or_assign(id, item); +} + +void LLAudioEngine::logSoundStop(const LLUUID& id) +{ + auto iter = mSoundHistory.find(id); + if (iter != mSoundHistory.end()) + { + LLSoundHistoryItem& hist_item = iter->second; + hist_item.mPlaying = false; + hist_item.mTimeStopped = LLTimer::getElapsedSeconds(); + pruneSoundLog(); + } +} + +void LLAudioEngine::pruneSoundLog() +{ + if (++mSoundHistoryPruneCounter >= 64) + { + mSoundHistoryPruneCounter = 0; + while (mSoundHistory.size() > 256) + { + auto iter = mSoundHistory.begin(); + auto end = mSoundHistory.end(); + U64 lowest_time = (*iter).second.mTimeStopped; + LLUUID lowest_id = (*iter).first; + for (; iter != end; ++iter) + { + if ((*iter).second.mTimeStopped < lowest_time) + { + lowest_time = (*iter).second.mTimeStopped; + lowest_id = (*iter).first; + } + } + mSoundHistory.erase(lowest_id); + } + } +} // // LLAudioSource implementation // -LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain, const S32 type) +LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain, const S32 type, const LLUUID& source_id, const bool isTrigger) : mID(id), mOwnerID(owner_id), mPriority(0.f), @@ -1291,11 +1353,13 @@ LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 mType(type), mChannelp(NULL), mCurrentDatap(NULL), - mQueuedDatap(NULL) + mQueuedDatap(NULL), + mSourceID(source_id), + mIsTrigger(isTrigger) { + mLogID.generate(); } - LLAudioSource::~LLAudioSource() { if (mChannelp) @@ -1304,6 +1368,11 @@ LLAudioSource::~LLAudioSource() mChannelp->setSource(NULL); mChannelp = NULL; } + + if (mType != LLAudioEngine::AUDIO_TYPE_UI) // && mSourceID.notNull()) + { + gAudiop->logSoundStop(mLogID); + } } @@ -1413,12 +1482,18 @@ bool LLAudioSource::setupChannel() bool LLAudioSource::play(const LLUUID &audio_uuid) { + if(mType != LLAudioEngine::AUDIO_TYPE_UI) //&& mSourceID.notNull()) + { + gAudiop->logSoundPlay(mLogID, mPositionGlobal, mType, audio_uuid, mOwnerID, mSourceID, mIsTrigger, mLoop); + } + // Special abuse of play(); don't play a sound, but kill it. if (audio_uuid.isNull()) { if (getChannel()) { - getChannel()->setSource(NULL); + if(getChannel()->getSource()) + getChannel()->setSource(NULL); setChannel(NULL); if (!isMuted()) { @@ -1438,6 +1513,8 @@ bool LLAudioSource::play(const LLUUID &audio_uuid) } LLAudioData *adp = gAudiop->getAudioData(audio_uuid); + if( !adp ) + return false; addAudioData(adp); if (isMuted()) diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h index f1be608baab..2cc4e3026e2 100644 --- a/indra/llaudio/llaudioengine.h +++ b/indra/llaudio/llaudioengine.h @@ -40,6 +40,7 @@ #include "llextendedstatus.h" #include "lllistener.h" +#include "absl/container/node_hash_map.h" #include "absl/container/flat_hash_map.h" const F32 LL_WIND_UPDATE_INTERVAL = 0.1f; @@ -49,7 +50,7 @@ const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; const F32 DEFAULT_MIN_DISTANCE = 2.0f; #define MAX_CHANNELS 30 -#define MAX_BUFFERS 40 // Some extra for preloading, maybe? +#define MAX_BUFFERS 60 // Some extra for preloading, maybe? class LLAudioSource; class LLAudioData; @@ -58,6 +59,7 @@ class LLAudioChannelOpenAL; class LLAudioBuffer; class LLStreamingAudioInterface; struct SoundData; +struct LLSoundHistoryItem; // // LLAudioEngine definition @@ -133,7 +135,9 @@ class LLAudioEngine // Owner ID is the owner of the object making the request void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, - const LLVector3d &pos_global = LLVector3d::zero); + const LLVector3d &pos_global = LLVector3d::zero, + const LLUUID& source_object = LLUUID::null, + const LLUUID& audio_source_id = LLUUID::null); void triggerSound(const SoundData& soundData); bool preloadSound(const LLUUID &id); @@ -239,6 +243,17 @@ class LLAudioEngine LLFrameTimer mWindUpdateTimer; +public: + void logSoundPlay(const LLUUID& id, LLVector3d position, S32 type, const LLUUID& assetid, const LLUUID& ownerid, const LLUUID& sourceid, bool is_trigger, bool is_looped); + void logSoundStop(const LLUUID& id); + void pruneSoundLog(); + + auto& getSoundLog() { return mSoundHistory; } +private: + S32 mSoundHistoryPruneCounter = 0; + + using sound_history_map = absl::node_hash_map<LLUUID, LLSoundHistoryItem>; + sound_history_map mSoundHistory; private: void setDefaults(); LLStreamingAudioInterface *mStreamingAudioImpl; @@ -257,7 +272,7 @@ class LLAudioSource public: // owner_id is the id of the agent responsible for making this sound // play, for example, the owner of the object currently playing it - LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE); + LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, const LLUUID& source_id = LLUUID::null, const bool isTrigger = true); virtual ~LLAudioSource(); virtual void update(); // Update this audio source @@ -297,6 +312,8 @@ class LLAudioSource virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); } const LLUUID &getID() const { return mID; } + const LLUUID &getLogID() const { return mLogID; } + bool isDone() const; bool isMuted() const { return mSourceMuted; } @@ -331,6 +348,9 @@ class LLAudioSource S32 mType; LLVector3d mPositionGlobal; LLVector3 mVelocity; + LLUUID mLogID; + LLUUID mSourceID; + bool mIsTrigger; //LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to. LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to. @@ -473,4 +493,33 @@ struct SoundData extern LLAudioEngine* gAudiop; +struct LLSoundHistoryItem +{ + LLUUID mID; + LLVector3d mPosition; + S32 mType; + bool mPlaying; + LLUUID mAssetID; + LLUUID mOwnerID; + LLUUID mSourceID; + bool mIsTrigger; + bool mIsLooped; + F64 mTimeStarted; + F64 mTimeStopped; + bool mReviewed; + bool mReviewedCollision; + + LLSoundHistoryItem() + : mType(0) + , mPlaying(0) + , mIsTrigger(0) + , mIsLooped(0) + , mTimeStarted(0.f) + , mTimeStopped(0.f) + , mReviewed(false) + , mReviewedCollision(false) + { + } +}; + #endif diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index aa162b19d47..6541c606637 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -123,6 +123,7 @@ set(viewer_SOURCE_FILES alchatcommand.cpp alcontrolcache.cpp alfloaterao.cpp + alfloaterexploresounds.cpp alfloaterparticleeditor.cpp alfloaterregiontracker.cpp alpanelaomini.cpp @@ -794,6 +795,7 @@ set(viewer_HEADER_FILES alchatcommand.h alcontrolcache.h alfloaterao.h + alfloaterexploresounds.h alfloaterparticleeditor.h alfloaterregiontracker.h alpanelaomini.h diff --git a/indra/newview/alfloaterexploresounds.cpp b/indra/newview/alfloaterexploresounds.cpp new file mode 100644 index 00000000000..cde36f30637 --- /dev/null +++ b/indra/newview/alfloaterexploresounds.cpp @@ -0,0 +1,530 @@ +/** + * @file ALFloaterExploreSounds.cpp + */ + +#include "llviewerprecompiledheaders.h" + +#include "alfloaterexploresounds.h" +#include "llcheckboxctrl.h" +#include "llscrolllistctrl.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llavatarnamecache.h" +#include "llviewerobjectlist.h" +#include "llviewerregion.h" +#include "llstring.h" +#include "lltrans.h" +#include "rlvhandler.h" + +static const size_t num_collision_sounds = 28; +const LLUUID collision_sounds[num_collision_sounds] = +{ + LLUUID("dce5fdd4-afe4-4ea1-822f-dd52cac46b08"), + LLUUID("51011582-fbca-4580-ae9e-1a5593f094ec"), + LLUUID("68d62208-e257-4d0c-bbe2-20c9ea9760bb"), + LLUUID("75872e8c-bc39-451b-9b0b-042d7ba36cba"), + LLUUID("6a45ba0b-5775-4ea8-8513-26008a17f873"), + LLUUID("992a6d1b-8c77-40e0-9495-4098ce539694"), + LLUUID("2de4da5a-faf8-46be-bac6-c4d74f1e5767"), + LLUUID("6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d"), + LLUUID("14209133-4961-4acc-9649-53fc38ee1667"), + LLUUID("bc4a4348-cfcc-4e5e-908e-8a52a8915fe6"), + LLUUID("9e5c1297-6eed-40c0-825a-d9bcd86e3193"), + LLUUID("e534761c-1894-4b61-b20c-658a6fb68157"), + LLUUID("8761f73f-6cf9-4186-8aaa-0948ed002db1"), + LLUUID("874a26fd-142f-4173-8c5b-890cd846c74d"), + LLUUID("0e24a717-b97e-4b77-9c94-b59a5a88b2da"), + LLUUID("75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2"), + LLUUID("153c8bf7-fb89-4d89-b263-47e58b1b4774"), + LLUUID("55c3e0ce-275a-46fa-82ff-e0465f5e8703"), + LLUUID("24babf58-7156-4841-9a3f-761bdbb8e237"), + LLUUID("aca261d8-e145-4610-9e20-9eff990f2c12"), + LLUUID("0642fba6-5dcf-4d62-8e7b-94dbb529d117"), + LLUUID("25a863e8-dc42-4e8a-a357-e76422ace9b5"), + LLUUID("9538f37c-456e-4047-81be-6435045608d4"), + LLUUID("8c0f84c3-9afd-4396-b5f5-9bca2c911c20"), + LLUUID("be582e5d-b123-41a2-a150-454c39e961c8"), + LLUUID("c70141d4-ba06-41ea-bcbc-35ea81cb8335"), + LLUUID("7d1826f4-24c4-4aac-8c2e-eff45df37783"), + LLUUID("063c97d3-033a-4e9b-98d8-05c8074922cb") +}; + +ALFloaterExploreSounds::ALFloaterExploreSounds(const LLSD& key) +: LLFloater(key), LLEventTimer(0.25f) +{ +} + +ALFloaterExploreSounds::~ALFloaterExploreSounds() +{ + for (blacklist_avatar_name_cache_connection_map_t::iterator it = mBlacklistAvatarNameCacheConnections.begin(); it != mBlacklistAvatarNameCacheConnections.end(); ++it) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + } + mBlacklistAvatarNameCacheConnections.clear(); + + mLocalPlayingAudioSourceIDs.clear(); +} + +BOOL ALFloaterExploreSounds::postBuild() +{ + getChild<LLButton>("play_locally_btn")->setClickedCallback(boost::bind(&ALFloaterExploreSounds::handlePlayLocally, this)); + getChild<LLButton>("look_at_btn")->setClickedCallback(boost::bind(&ALFloaterExploreSounds::handleLookAt, this)); + getChild<LLButton>("stop_btn")->setClickedCallback(boost::bind(&ALFloaterExploreSounds::handleStop, this)); + getChild<LLButton>("bl_btn")->setClickedCallback(boost::bind(&ALFloaterExploreSounds::blacklistSound, this)); + getChild<LLButton>("stop_locally_btn")->setClickedCallback(boost::bind(&ALFloaterExploreSounds::handleStopLocally, this)); + + mHistoryScroller = getChild<LLScrollListCtrl>("sound_list"); + mHistoryScroller->setCommitCallback(boost::bind(&ALFloaterExploreSounds::handleSelection, this)); + mHistoryScroller->setDoubleClickCallback(boost::bind(&ALFloaterExploreSounds::handlePlayLocally, this)); + mHistoryScroller->sortByColumn("playing", TRUE); + + mCollisionSounds = getChild<LLCheckBoxCtrl>("collision_chk"); + mRepeatedAssets = getChild<LLCheckBoxCtrl>("repeated_asset_chk"); + mAvatarSounds = getChild<LLCheckBoxCtrl>("avatars_chk"); + mObjectSounds = getChild<LLCheckBoxCtrl>("objects_chk"); + mPaused = getChild<LLCheckBoxCtrl>("pause_chk"); + + return TRUE; +} + +void ALFloaterExploreSounds::handleSelection() +{ + size_t num_selected = mHistoryScroller->getAllSelected().size(); + bool multiple = (num_selected > 1); + childSetEnabled("look_at_btn", (num_selected && !multiple)); + childSetEnabled("play_locally_btn", num_selected); + childSetEnabled("stop_btn", num_selected); + childSetEnabled("bl_btn", num_selected); +} + +LLSoundHistoryItem ALFloaterExploreSounds::getItem(const LLUUID& itemID) +{ + if (!gAudiop) + { + LLSoundHistoryItem item; + item.mID = LLUUID::null; + return item; + } + + const auto& sound_log = gAudiop->getSoundLog(); + auto found = sound_log.find(itemID); + if (found != sound_log.end()) + { + return found->second; + } + else + { + // If log is paused, hopefully we can find it in mLastHistory + std::list<LLSoundHistoryItem>::iterator iter = mLastHistory.begin(); + std::list<LLSoundHistoryItem>::iterator end = mLastHistory.end(); + for ( ; iter != end; ++iter) + { + if ((*iter).mID == itemID) + { + return (*iter); + } + } + } + LLSoundHistoryItem item; + item.mID = LLUUID::null; + return item; +} + +class LLSoundHistoryItemCompare +{ +public: + bool operator() (LLSoundHistoryItem first, LLSoundHistoryItem second) + { + if (first.mPlaying) + { + if (second.mPlaying) + { + return (first.mTimeStarted > second.mTimeStarted); + } + else + { + return true; + } + } + else if (second.mPlaying) + { + return false; + } + else + { + return (first.mTimeStopped > second.mTimeStopped); + } + } +}; + +BOOL ALFloaterExploreSounds::tick() +{ + static const std::string str_playing = getString("Playing"); + static LLUIString str_not_playing = getString("NotPlaying"); + static const std::string str_type_ui = getString("Type_UI"); + static const std::string str_type_avatar = getString("Type_Avatar"); + static const std::string str_type_trigger_sound = getString("Type_llTriggerSound"); + static const std::string str_type_loop_sound = getString("Type_llLoopSound"); + static const std::string str_type_play_sound = getString("Type_llPlaySound"); + static const std::string str_unknown_name = LLTrans::getString("AvatarNameWaiting"); + + bool show_collision_sounds = mCollisionSounds->get(); + bool show_repeated_assets = mRepeatedAssets->get(); + bool show_avatars = mAvatarSounds->get(); + bool show_objects = mObjectSounds->get(); + + std::list<LLSoundHistoryItem> history; + if (mPaused->get()) + { + history = mLastHistory; + } + else + { + if (gAudiop) + { + for (const auto& sound_pair : gAudiop->getSoundLog()) + { + history.push_back(sound_pair.second); + } + LLSoundHistoryItemCompare c; + history.sort(c); + } + mLastHistory = history; + } + + // Save scroll pos and selection so they can be restored + S32 scroll_pos = mHistoryScroller->getScrollPos(); + uuid_vec_t selected_ids; + std::vector<LLScrollListItem*> selected_items = mHistoryScroller->getAllSelected(); + std::vector<LLScrollListItem*>::iterator selection_iter = selected_items.begin(); + std::vector<LLScrollListItem*>::iterator selection_end = selected_items.end(); + for (; selection_iter != selection_end; ++selection_iter) + { + selected_ids.push_back((*selection_iter)->getUUID()); + } + + mHistoryScroller->clearRows(); + + std::list<LLUUID> unique_asset_list; + + std::list<LLSoundHistoryItem>::iterator iter = history.begin(); + std::list<LLSoundHistoryItem>::iterator end = history.end(); + for ( ; iter != end; ++iter) + { + LLSoundHistoryItem item = (*iter); + + bool is_avatar = item.mOwnerID == item.mSourceID; + if (is_avatar && !show_avatars) + { + continue; + } + + bool is_object = !is_avatar; + if (is_object && !show_objects) + { + continue; + } + + bool is_repeated_asset = std::find(unique_asset_list.begin(), unique_asset_list.end(), item.mAssetID) != unique_asset_list.end(); + if (is_repeated_asset && !show_repeated_assets) + { + continue; + } + + if (!item.mReviewed) + { + item.mReviewedCollision = std::find(&collision_sounds[0], &collision_sounds[num_collision_sounds], item.mAssetID) != &collision_sounds[num_collision_sounds]; + item.mReviewed = true; + } + + bool is_collision_sound = item.mReviewedCollision; + if (is_collision_sound && !show_collision_sounds) + { + continue; + } + + unique_asset_list.push_back(item.mAssetID); + + LLSD element; + element["id"] = item.mID; + + LLSD& playing_column = element["columns"][0]; + playing_column["column"] = "playing"; + if (item.mPlaying) + { + playing_column["value"] = " " + str_playing; + } + else + { + LLStringUtil::format_map_t format_args; + format_args["TIME"] = llformat("%.1f", static_cast<F32>((LLTimer::getElapsedSeconds() - item.mTimeStopped) / 60.0)); + str_not_playing.setArgs(format_args); + playing_column["value"] = str_not_playing.getString(); + } + + LLSD& type_column = element["columns"][1]; + type_column["column"] = "type"; + if (item.mType == LLAudioEngine::AUDIO_TYPE_UI) + { + // this shouldn't happen for now, as UI is forbidden in the log + type_column["value"] = str_type_ui; + } + else + { + std::string type; + + if (is_avatar) + { + type = str_type_avatar; + } + else + { + if (item.mIsTrigger) + { + type = str_type_trigger_sound; + } + else + { + if (item.mIsLooped) + { + type = str_type_loop_sound; + } + else + { + type = str_type_play_sound; + } + } + } + + type_column["value"] = type; + } + + LLSD& owner_column = element["columns"][2]; + owner_column["column"] = "owner"; + LLAvatarName av_name; + if (LLAvatarNameCache::get(item.mOwnerID, &av_name)) + { + owner_column["value"] = !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES) ? av_name.getCompleteName() : RlvStrings::getAnonym(av_name); + } + else + { + owner_column["value"] = str_unknown_name; + } + + LLSD& sound_column = element["columns"][3]; + sound_column["column"] = "sound"; + sound_column["value"] = item.mAssetID.asString().substr(0,16); + + mHistoryScroller->addElement(element, ADD_BOTTOM); + } + + mHistoryScroller->selectMultiple(selected_ids); + mHistoryScroller->setScrollPos(scroll_pos); + + // Clean up stopped local audio source IDs + uuid_vec_t stopped_audio_src_ids; + uuid_vec_t::iterator audio_src_id_iter = mLocalPlayingAudioSourceIDs.begin(); + uuid_vec_t::iterator audio_src_id_end = mLocalPlayingAudioSourceIDs.end(); + for (; audio_src_id_iter != audio_src_id_end; ++audio_src_id_iter) + { + LLUUID audio_src_id = *audio_src_id_iter; + LLAudioSource* audio_source = gAudiop->findAudioSource(audio_src_id); + if (!audio_source || audio_source->isDone()) + { + stopped_audio_src_ids.push_back(audio_src_id); + } + } + + for (uuid_vec_t::iterator stopped_audio_src_ids_iter = stopped_audio_src_ids.begin(); + stopped_audio_src_ids_iter != stopped_audio_src_ids.end(); ++stopped_audio_src_ids_iter) + { + uuid_vec_t::iterator find_iter = std::find(mLocalPlayingAudioSourceIDs.begin(), mLocalPlayingAudioSourceIDs.end(), *stopped_audio_src_ids_iter); + if (find_iter != mLocalPlayingAudioSourceIDs.end()) + { + mLocalPlayingAudioSourceIDs.erase(find_iter); + } + } + + childSetEnabled("stop_locally_btn", mLocalPlayingAudioSourceIDs.size() > 0); + + return FALSE; +} + +void ALFloaterExploreSounds::handlePlayLocally() +{ + std::vector<LLScrollListItem*> selection = mHistoryScroller->getAllSelected(); + std::vector<LLScrollListItem*>::iterator selection_iter = selection.begin(); + std::vector<LLScrollListItem*>::iterator selection_end = selection.end(); + uuid_vec_t asset_list; + for ( ; selection_iter != selection_end; ++selection_iter) + { + LLSoundHistoryItem item = getItem((*selection_iter)->getValue()); + if (item.mID.isNull()) + { + continue; + } + + // Unique assets only + if (std::find(asset_list.begin(), asset_list.end(), item.mAssetID) == asset_list.end()) + { + asset_list.push_back(item.mAssetID); + LLUUID audio_source_id = LLUUID::generateNewID(); + gAudiop->triggerSound(item.mAssetID, gAgent.getID(), 1.0f, LLAudioEngine::AUDIO_TYPE_UI, LLVector3d::zero, LLUUID::null, audio_source_id); + mLocalPlayingAudioSourceIDs.push_back(audio_source_id); + } + } + + childSetEnabled("stop_locally_btn", mLocalPlayingAudioSourceIDs.size() > 0); +} + +void ALFloaterExploreSounds::handleLookAt() +{ + LLUUID selection = mHistoryScroller->getSelectedValue().asUUID(); + LLSoundHistoryItem item = getItem(selection); // Single item only + if (item.mID.isNull()) + { + return; + } + + LLVector3d pos_global = item.mPosition; + + // Try to find object position + if (item.mSourceID.notNull()) + { + LLViewerObject* object = gObjectList.findObject(item.mSourceID); + if (object) + { + pos_global = object->getPositionGlobal(); + } + } + + // Move the camera + // Find direction to self (reverse) + LLVector3d cam = gAgent.getPositionGlobal() - pos_global; + cam.normalize(); + // Go 4 meters back and 3 meters up + cam *= 4.0; + cam += pos_global; + cam += LLVector3d(0.0, 0.0, 3.0); + + gAgentCamera.setFocusOnAvatar(FALSE, FALSE); + gAgentCamera.setCameraPosAndFocusGlobal(cam, pos_global, item.mSourceID); + gAgentCamera.setCameraAnimating(FALSE); +} + +void ALFloaterExploreSounds::handleStop() +{ + if (!gAudiop) + return; + + auto& sound_log = gAudiop->getSoundLog(); + + std::vector<LLScrollListItem*> selection = mHistoryScroller->getAllSelected(); + for (const auto& selection_item : selection) + { + LLSoundHistoryItem item = getItem(selection_item->getValue()); + if (item.mID.notNull() && item.mPlaying) + { + LLAudioSource* audio_source = gAudiop->findAudioSource(item.mSourceID); + if (audio_source) + { + S32 type = item.mType; + audio_source->setType(LLAudioEngine::AUDIO_TYPE_UI); + audio_source->play(LLUUID::null); + audio_source->setType(type); + } + else + { + LL_WARNS("SoundExplorer") << "audio source for source ID " << item.mSourceID << " already gone but still marked as playing. Fixing ..." << LL_ENDL; + auto iter = sound_log.find(item.mID); + if (iter != sound_log.end()) + { + iter->second.mPlaying = false; + iter->second.mTimeStopped = LLTimer::getElapsedSeconds(); + } + else + { + for (auto& histItem : mLastHistory) + { + if (histItem.mID == item.mID) + { + histItem.mPlaying = false; + histItem.mTimeStopped = LLTimer::getElapsedSeconds(); + break; + } + } + } + continue; + } + } + } +} + +void ALFloaterExploreSounds::handleStopLocally() +{ + uuid_vec_t::iterator audio_source_id_iter = mLocalPlayingAudioSourceIDs.begin(); + uuid_vec_t::iterator audio_source_id_end = mLocalPlayingAudioSourceIDs.end(); + for (; audio_source_id_iter != audio_source_id_end; ++audio_source_id_iter) + { + LLUUID audio_source_id = *audio_source_id_iter; + LLAudioSource* audio_source = gAudiop->findAudioSource(audio_source_id); + if (audio_source && !audio_source->isDone()) + { + audio_source->play(LLUUID::null); + } + } + + mLocalPlayingAudioSourceIDs.clear(); +} + +//add sound to blacklist +void ALFloaterExploreSounds::blacklistSound() +{ + std::vector<LLScrollListItem*> selection = mHistoryScroller->getAllSelected(); + std::vector<LLScrollListItem*>::iterator selection_iter = selection.begin(); + std::vector<LLScrollListItem*>::iterator selection_end = selection.end(); + + for ( ; selection_iter != selection_end; ++selection_iter) + { + LLSoundHistoryItem item = getItem((*selection_iter)->getValue()); + if (item.mID.isNull()) + { + continue; + } + + std::string region_name; + LLViewerRegion* cur_region = gAgent.getRegion(); + if (cur_region) + { + region_name = cur_region->getName(); + } + + blacklist_avatar_name_cache_connection_map_t::iterator it = mBlacklistAvatarNameCacheConnections.find(item.mOwnerID); + if (it != mBlacklistAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mBlacklistAvatarNameCacheConnections.erase(it); + } + LLAvatarNameCache::callback_connection_t cb = LLAvatarNameCache::get(item.mOwnerID, boost::bind(&ALFloaterExploreSounds::onBlacklistAvatarNameCacheCallback, this, _1, _2, item.mAssetID, region_name)); + mBlacklistAvatarNameCacheConnections.insert(std::make_pair(item.mOwnerID, cb)); + } +} + +void ALFloaterExploreSounds::onBlacklistAvatarNameCacheCallback(const LLUUID& av_id, const LLAvatarName& av_name, const LLUUID& asset_id, const std::string& region_name) +{ + blacklist_avatar_name_cache_connection_map_t::iterator it = mBlacklistAvatarNameCacheConnections.find(av_id); + if (it != mBlacklistAvatarNameCacheConnections.end()) + { + if (it->second.connected()) + { + it->second.disconnect(); + } + mBlacklistAvatarNameCacheConnections.erase(it); + } +} diff --git a/indra/newview/alfloaterexploresounds.h b/indra/newview/alfloaterexploresounds.h new file mode 100644 index 00000000000..657e1547bc6 --- /dev/null +++ b/indra/newview/alfloaterexploresounds.h @@ -0,0 +1,53 @@ +/** + * @file alfloaterexploresounds.h + */ + +#ifndef AL_ALFLOATEREXPLORESOUNDS_H +#define AL_ALFLOATEREXPLORESOUNDS_H + +#include "llfloater.h" +#include "lleventtimer.h" +#include "llaudioengine.h" +#include "llavatarnamecache.h" + +class LLCheckBoxCtrl; +class LLScrollListCtrl; + +class ALFloaterExploreSounds final +: public LLFloater, public LLEventTimer +{ +public: + ALFloaterExploreSounds(const LLSD& key); + BOOL postBuild(); + + BOOL tick(); + + LLSoundHistoryItem getItem(const LLUUID& itemID); + +private: + virtual ~ALFloaterExploreSounds(); + void handlePlayLocally(); + void handleLookAt(); + void handleStop(); + void handleStopLocally(); + void handleSelection(); + void blacklistSound(); + + LLScrollListCtrl* mHistoryScroller; + LLCheckBoxCtrl* mCollisionSounds; + LLCheckBoxCtrl* mRepeatedAssets; + LLCheckBoxCtrl* mAvatarSounds; + LLCheckBoxCtrl* mObjectSounds; + LLCheckBoxCtrl* mPaused; + + std::list<LLSoundHistoryItem> mLastHistory; + + uuid_vec_t mLocalPlayingAudioSourceIDs; + + typedef std::map<LLUUID, boost::signals2::connection> blacklist_avatar_name_cache_connection_map_t; + blacklist_avatar_name_cache_connection_map_t mBlacklistAvatarNameCacheConnections; + + void onBlacklistAvatarNameCacheCallback(const LLUUID& av_id, const LLAvatarName& av_name, const LLUUID& asset_id, const std::string& region_name); +}; + +#endif diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index e85a3b8a9bc..ccd1df31ca4 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -32,6 +32,7 @@ #include "llviewerfloaterreg.h" #include "alfloaterao.h" +#include "alfloaterexploresounds.h" #include "alfloaterparticleeditor.h" #include "alfloaterregiontracker.h" #include "llcommandhandler.h" @@ -418,6 +419,7 @@ void LLViewerFloaterReg::registerFloaters() LLFloaterReg::add("particle_editor", "floater_particle_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterParticleEditor>); LLFloaterReg::add("quick_settings", "floater_quick_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>); LLFloaterReg::add("region_tracker", "floater_region_tracker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterRegionTracker>); + LLFloaterReg::add("sound_explorer", "floater_explore_sounds.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterExploreSounds>); LLFloaterReg::registerControlVariables(); // Make sure visibility and rect controls get preserved when saving } diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 35996663f5f..d7554022d68 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -4146,7 +4146,7 @@ void process_sound_trigger(LLMessageSystem *msg, void **) return; } - gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global); + gAudiop->triggerSound(sound_id, owner_id, gain, LLAudioEngine::AUDIO_TYPE_SFX, pos_global, object_id); } void process_preload_sound(LLMessageSystem *msg, void **user_data) diff --git a/indra/newview/skins/default/xui/en/floater_explore_sounds.xml b/indra/newview/skins/default/xui/en/floater_explore_sounds.xml new file mode 100644 index 00000000000..26e0e6adac7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_explore_sounds.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + can_close="true" + can_minimize="true" + positioning="centered" + can_resize="true" + height="300" + width="550" + min_width="550" + min_height="170" + name="sound_explorer" + title="Sounds" + save_rect="true" + save_visibility="true" + single_instance="true" + help_topic="fs_sound_explorer"> + <floater.string name="Playing"> + Playing + </floater.string> + <floater.string name="NotPlaying"> + [TIME] min ago + </floater.string> + <floater.string name="Type_UI"> + UI + </floater.string> + <floater.string name="Type_Avatar"> + Avatar + </floater.string> + <floater.string name="Type_llTriggerSound"> + llTriggerSound + </floater.string> + <floater.string name="Type_llLoopSound"> + llLoopSound + </floater.string> + <floater.string name="Type_llPlaySound"> + llPlaySound + </floater.string> + <check_box follows="top|left" bottom_delta="20" left="5" width="64" name="avatars_chk" label="Avatars" /> + <check_box follows="top|left" bottom_delta="0" left_delta="64" width="64" name="objects_chk" label="Objects" initial_value="true" /> + <check_box follows="top|left" bottom_delta="0" left_delta="64" width="160" name="collision_chk" label="Default Collision Sounds" /> + <check_box follows="top|left" bottom_delta="0" left_delta="160" width="110" name="repeated_asset_chk" label="Repeated Asset" /> + <check_box follows="top|left" bottom_delta="0" left_delta="110" width="80" name="pause_chk" label="Pause Log" initial_value="false"/> + <scroll_list column_padding="0" draw_heading="true" follows="all" left="10" multi_select="true" name="sound_list" search_column="0" top="40" right="-10" bottom="-60"> + <columns dynamicwidth="false" width="80" label="Playing" name="playing" /> + <columns dynamicwidth="false" width="100" label="Type" name="type" /> + <columns dynamicwidth="true" label="Owner" name="owner" /> + <columns dynamicwidth="false" width="0" label="Sound" name="sound" /> + </scroll_list> + <button bottom_delta="25" follows="left|bottom" height="20" label="Play Locally" name="play_locally_btn" left="10" width="95" enabled="false"/> + <button bottom_delta="0" follows="left|bottom" height="20" label="Stop Locally" name="stop_locally_btn" left_delta="100" width="95" enabled="false"/> + <button bottom_delta="0" follows="left|bottom" height="20" label="Look At" name="look_at_btn" left_delta="100" width="95" enabled="false"/> + + <button bottom_delta="0" follows="left|bottom" height="20" label="Stop" name="stop_btn" right="-10" width="95" enabled="false"/> + <button bottom_delta="0" follows="left|bottom" height="20" label="Blacklist" name="bl_btn" right="-110" width="95" enabled="false" visible="false"/> +</floater> diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 598fef6d46f..e34d6f5624f 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -687,6 +687,17 @@ <menu_item_call.on_click function="World.SyncAnimations" /> </menu_item_call> + <menu_item_check + label="Sound Explorer" + name="Sound Explorer" + use_mac_ctrl="true"> + <menu_item_check.on_check + function="Floater.Visible" + parameter="sound_explorer" /> + <menu_item_check.on_click + function="Floater.Toggle" + parameter="sound_explorer" /> + </menu_item_check> <menu_item_separator/> <menu_item_call label="Landmark This Place" -- GitLab