From 117e22047c5752352342d64e3fb7ce00a4eb8113 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:45:04 -0500 Subject: Second Life viewer sources 1.18.1.2 --- linden/indra/newview/llfloateractivespeakers.cpp | 831 +++++++++++++++++++++++ 1 file changed, 831 insertions(+) create mode 100644 linden/indra/newview/llfloateractivespeakers.cpp (limited to 'linden/indra/newview/llfloateractivespeakers.cpp') diff --git a/linden/indra/newview/llfloateractivespeakers.cpp b/linden/indra/newview/llfloateractivespeakers.cpp new file mode 100644 index 0000000..1c3990e --- /dev/null +++ b/linden/indra/newview/llfloateractivespeakers.cpp @@ -0,0 +1,831 @@ +/** + * @file llfloateractivespeakers.cpp + * @brief Management interface for muting and controlling volume of residents currently speaking + * + * Copyright (c) 2005-2007, Linden Research, Inc. + * + * Second Life Viewer Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in doc/GPL-license.txt in this distribution, or + * online at http://secondlife.com/developers/opensource/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file doc/FLOSS-exception.txt in this software distribution, or + * online at http://secondlife.com/developers/opensource/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llfloateractivespeakers.h" + +#include "llagent.h" +#include "llvoavatar.h" +#include "llfloateravatarinfo.h" +#include "llvieweruictrlfactory.h" +#include "llviewercontrol.h" +#include "llscrolllistctrl.h" +#include "llbutton.h" +#include "lltextbox.h" +#include "llmutelist.h" +#include "llviewerobjectlist.h" +#include "llimpanel.h" // LLVoiceChannel +#include "llsdutil.h" + +const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers +const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f); +const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f); +const F32 TYPING_ANIMATION_FPS = 2.5f; + +LLLocalSpeakerMgr* gLocalSpeakerMgr = NULL; +LLActiveSpeakerMgr* gActiveChannelSpeakerMgr = NULL; + +LLSpeaker::speaker_map_t LLSpeaker::sSpeakers; + +LLSpeaker::LLSpeaker(const LLUUID& id, const LLString& name, const ESpeakerType type) : + mStatus(LLSpeaker::STATUS_TEXT_ONLY), + mLastSpokeTime(0.f), + mSpeechVolume(0.f), + mHasSpoken(FALSE), + mDotColor(LLColor4::white), + mID(id), + mTyping(FALSE), + mSortIndex(0), + mType(type) +{ + mHandle.init(); + sSpeakers.insert(std::make_pair(mHandle, this)); + if (name.empty() && type == SPEAKER_AGENT) + { + lookupName(); + } + else + { + mDisplayName = name; + } + mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); +} + +LLSpeaker::~LLSpeaker() +{ + sSpeakers.erase(mHandle); +} + +void LLSpeaker::lookupName() +{ + gCacheName->getName(mID, onAvatarNameLookup, new LLViewHandle(mHandle)); +} + +//static +void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data) +{ + LLViewHandle speaker_handle = *(LLViewHandle*)user_data; + delete (LLViewHandle*)user_data; + + speaker_map_t::iterator found_it = sSpeakers.find(speaker_handle); + if (found_it != sSpeakers.end()) + { + LLSpeaker* speakerp = found_it->second; + if (speakerp) + { + speakerp->mDisplayName = llformat("%s %s", first, last); + } + } +} + + +// helper sort class +struct LLSortRecentSpeakers +{ + bool operator()(const LLPointer lhs, const LLPointer rhs) const; +}; + +bool LLSortRecentSpeakers::operator()(const LLPointer lhs, const LLPointer rhs) const +{ + // Sort first on status + if (lhs->mStatus != rhs->mStatus) + { + return (lhs->mStatus < rhs->mStatus); + } + + // and then on last speaking time + if(lhs->mLastSpokeTime != rhs->mLastSpokeTime) + { + return (lhs->mLastSpokeTime > rhs->mLastSpokeTime); + } + + // and finally (only if those are both equal), on name. + return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 ); +} + +LLFloaterActiveSpeakers::LLFloaterActiveSpeakers(const LLSD& seed) : mPanel(NULL) +{ + mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL); + // do not automatically open singleton floaters (as result of getInstance()) + BOOL no_open = FALSE; + gUICtrlFactory->buildFloater(this, "floater_active_speakers.xml", &getFactoryMap(), no_open); + //RN: for now, we poll voice client every frame to get voice amplitude feedback + //gVoiceClient->addObserver(this); + mPanel->refreshSpeakers(); +} + +LLFloaterActiveSpeakers::~LLFloaterActiveSpeakers() +{ +} + +void LLFloaterActiveSpeakers::onClose(bool app_quitting) +{ + setVisible(FALSE); +} + +void LLFloaterActiveSpeakers::draw() +{ + // update state every frame to get live amplitude feedback + mPanel->refreshSpeakers(); + LLFloater::draw(); +} + +BOOL LLFloaterActiveSpeakers::postBuild() +{ + mPanel = (LLPanelActiveSpeakers*)LLUICtrlFactory::getPanelByName(this, "active_speakers_panel"); + return TRUE; +} + +void LLFloaterActiveSpeakers::onChange() +{ + //refresh(); +} + +//static +void* LLFloaterActiveSpeakers::createSpeakersPanel(void* data) +{ + // don't show text only speakers + return new LLPanelActiveSpeakers(gActiveChannelSpeakerMgr, FALSE); +} + + +// +// LLPanelActiveSpeakers +// +LLPanelActiveSpeakers::LLPanelActiveSpeakers(LLSpeakerMgr* data_source, BOOL show_text_chatters) : + mSpeakerList(NULL), + mMuteVoiceCtrl(NULL), + mMuteTextCtrl(NULL), + mNameText(NULL), + mProfileBtn(NULL), + mShowTextChatters(show_text_chatters), + mSpeakerMgr(data_source) +{ + setMouseOpaque(FALSE); +} + +LLPanelActiveSpeakers::~LLPanelActiveSpeakers() +{ + +} + +BOOL LLPanelActiveSpeakers::postBuild() +{ + mSpeakerList = LLUICtrlFactory::getScrollListByName(this, "speakers_list"); + + mMuteTextCtrl = (LLUICtrl*)getCtrlByNameAndType("mute_text_btn", WIDGET_TYPE_DONTCARE); + childSetCommitCallback("mute_text_btn", onClickMuteTextCommit, this); + + mMuteVoiceCtrl = (LLUICtrl*)getCtrlByNameAndType("mute_btn", WIDGET_TYPE_DONTCARE); + childSetCommitCallback("mute_btn", onClickMuteVoiceCommit, this); + childSetAction("mute_btn", onClickMuteVoice, this); + + childSetCommitCallback("speaker_volume", onVolumeChange, this); + + mNameText = LLUICtrlFactory::getTextBoxByName(this, "resident_name"); + + mProfileBtn = LLUICtrlFactory::getButtonByName(this, "profile_btn"); + childSetAction("profile_btn", onClickProfile, this); + return TRUE; +} + +void LLPanelActiveSpeakers::refreshSpeakers() +{ + // store off current selection and scroll state to preserve across list rebuilds + LLUUID selected_id = mSpeakerList->getSimpleSelectedValue().asUUID(); + S32 scroll_pos = mSpeakerList->getScrollInterface()->getScrollPos(); + + BOOL sort_ascending = mSpeakerList->getSortAscending(); + LLString sort_column = mSpeakerList->getSortColumnName(); + // TODO: put this in xml + // enforces default sort column of speaker status + if (sort_column.empty()) + { + sort_column = "speaking_status"; + } + + mSpeakerMgr->update(); + + // clear scrolling list widget of names + mSpeakerList->clearRows(); + + LLSpeakerMgr::speaker_list_t speaker_list; + mSpeakerMgr->getSpeakerList(&speaker_list, mShowTextChatters); + for (LLSpeakerMgr::speaker_list_t::const_iterator speaker_it = speaker_list.begin(); speaker_it != speaker_list.end(); ++speaker_it) + { + LLUUID speaker_id = (*speaker_it)->mID; + LLPointer speakerp = (*speaker_it); + + // since we are forced to sort by text, encode sort order as string + LLString speaking_order_sort_string = llformat("%010d", speakerp->mSortIndex); + + LLSD row; + row["id"] = speaker_id; + + row["columns"][0]["column"] = "icon_speaking_status"; + row["columns"][0]["type"] = "icon"; + row["columns"][0]["color"] = speakerp->mDotColor.getValue(); + LLString icon_image_id; + + S32 icon_image_idx = llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f)); + switch(icon_image_idx) + { + case 0: + icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl0.tga"); + break; + case 1: + icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl1.tga"); + break; + case 2: + icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl2.tga"); + break; + } + //if (speakerp->mTyping) + //{ + // S32 typing_anim_idx = llround(mIconAnimationTimer.getElapsedTimeF32() * TYPING_ANIMATION_FPS) % 3; + // switch(typing_anim_idx) + // { + // case 0: + // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing1.tga")); + // break; + // case 1: + // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing2.tga")); + // break; + // case 2: + // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing3.tga")); + // break; + // default: + // break; + // } + //} + + row["columns"][0]["value"] = speakerp->mStatus == LLSpeaker::STATUS_MUTED ? + gViewerArt.getString("mute_icon.tga") : icon_image_id; + if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE) // if voice is disabled for this speaker + { + // non voice speakers have hidden icons, render as transparent + row["columns"][0]["color"] = LLColor4(0.f, 0.f, 0.f, 0.f).getValue(); + } + row["columns"][1]["column"] = "speaker_name"; + row["columns"][1]["type"] = "text"; + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + // draw inactive speakers in gray + row["columns"][1]["color"] = LLColor4::grey4.getValue(); + } + + if (speakerp->mDisplayName.empty()) + { + row["columns"][1]["value"] = LLCacheName::getDefaultName(); + } + else + { + row["columns"][1]["value"] = speakerp->mDisplayName; + } + + row["columns"][2]["column"] = "speaking_status"; + row["columns"][2]["type"] = "text"; + + // print speaking ordinal in a text-sorting friendly manner + row["columns"][2]["value"] = speaking_order_sort_string; + + mSpeakerList->addElement(row); + } + + //restore sort order, selection, etc + mSpeakerList->sortByColumn(sort_column, sort_ascending); + // make sure something is selected + if (selected_id.isNull()) + { + mSpeakerList->selectFirstItem(); + } + else + { + mSpeakerList->selectByValue(selected_id); + } + + LLPointer speakerp = mSpeakerMgr->findSpeaker(selected_id); + + if (gMuteListp) + { + // update UI for selected participant + if (mMuteVoiceCtrl) + { + mMuteVoiceCtrl->setValue(gMuteListp->isMuted(selected_id, LLMute::flagVoiceChat)); + mMuteVoiceCtrl->setEnabled(selected_id.notNull() + && selected_id != gAgent.getID() + && mSpeakerMgr->isVoiceActive() + && (speakerp.notNull() && speakerp->mType == LLSpeaker::SPEAKER_AGENT)); + } + if (mMuteTextCtrl) + { + mMuteTextCtrl->setValue(gMuteListp->isMuted(selected_id, LLMute::flagTextChat)); + mMuteTextCtrl->setEnabled(selected_id.notNull() && selected_id != gAgent.getID() && speakerp.notNull() && !gMuteListp->isLinden(speakerp->mDisplayName)); + } + childSetValue("speaker_volume", gVoiceClient->getUserVolume(selected_id)); + childSetEnabled("speaker_volume", selected_id.notNull() + && selected_id != gAgent.getID() + && mSpeakerMgr->isVoiceActive() + && (speakerp.notNull() && speakerp->mType == LLSpeaker::SPEAKER_AGENT)); + if (mProfileBtn) + { + mProfileBtn->setEnabled(selected_id.notNull()); + } + } + + // show selected user name in large font + if (mNameText) + { + if (speakerp) + { + mNameText->setValue(speakerp->mDisplayName); + } + else + { + mNameText->setValue(""); + } + } + + // keep scroll value stable + mSpeakerList->getScrollInterface()->setScrollPos(scroll_pos); +} + +void LLPanelActiveSpeakers::setSpeaker(const LLUUID& id, const LLString& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +{ + mSpeakerMgr->setSpeaker(id, name, status, type); +} + + +//static +void LLPanelActiveSpeakers::onClickMuteTextCommit(LLUICtrl* ctrl, void* user_data) +{ + LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; + LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); + BOOL is_muted = gMuteListp->isMuted(speaker_id, LLMute::flagTextChat); + std::string name; + + //fill in name using voice client's copy of name cache + LLPointer speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id); + if (speakerp.isNull()) + { + return; + } + + name = speakerp->mDisplayName; + + LLMute mute(speaker_id, name, speakerp->mType == LLSpeaker::SPEAKER_AGENT ? LLMute::AGENT : LLMute::OBJECT); + + if (!is_muted) + { + gMuteListp->add(mute, LLMute::flagTextChat); + } + else + { + gMuteListp->remove(mute, LLMute::flagTextChat); + } +} + +//static +void LLPanelActiveSpeakers::onClickMuteVoice(void* user_data) +{ + onClickMuteVoiceCommit(NULL, user_data); +} + +//static +void LLPanelActiveSpeakers::onClickMuteVoiceCommit(LLUICtrl* ctrl, void* user_data) +{ + LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; + LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); + BOOL is_muted = gMuteListp->isMuted(speaker_id, LLMute::flagVoiceChat); + std::string name; + + LLPointer speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id); + if (speakerp.isNull()) + { + return; + } + + name = speakerp->mDisplayName; + + // muting voice means we're dealing with an agent + LLMute mute(speaker_id, name, LLMute::AGENT); + + if (!is_muted) + { + gMuteListp->add(mute, LLMute::flagVoiceChat); + } + else + { + gMuteListp->remove(mute, LLMute::flagVoiceChat); + } +} + + +//static +void LLPanelActiveSpeakers::onVolumeChange(LLUICtrl* source, void* user_data) +{ + LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; + LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); + + gVoiceClient->setUserVolume(speaker_id, (F32)panelp->childGetValue("speaker_volume").asReal()); +} + +//static +void LLPanelActiveSpeakers::onClickProfile(void* user_data) +{ + LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data; + LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID(); + + LLFloaterAvatarInfo::showFromDirectory(speaker_id); +} + +// +// LLSpeakerMgr +// + +LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) : + mVoiceChannel(channelp) +{ +} + +LLSpeakerMgr::~LLSpeakerMgr() +{ +} + +LLPointer LLSpeakerMgr::setSpeaker(const LLUUID& id, const LLString& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type) +{ + if (id.isNull()) return NULL; + + LLPointer speakerp; + if (mSpeakers.find(id) == mSpeakers.end()) + { + speakerp = new LLSpeaker(id, name, type); + speakerp->mStatus = status; + mSpeakers.insert(std::make_pair(speakerp->mID, speakerp)); + mSpeakersSorted.push_back(speakerp); + } + else + { + speakerp = findSpeaker(id); + if (speakerp.notNull()) + { + // keep highest priority status (lowest value) instead of overriding current value + speakerp->mStatus = llmin(speakerp->mStatus, status); + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id + // we need to override speakers that we think are objects when we find out they are really + // residents + if (type == LLSpeaker::SPEAKER_AGENT) + { + speakerp->mType = LLSpeaker::SPEAKER_AGENT; + speakerp->lookupName(); + } + } + } + + return speakerp; +} + +void LLSpeakerMgr::update() +{ + if (!gVoiceClient) + { + return; + } + + LLColor4 speaking_color = gSavedSettings.getColor4("SpeakingColor"); + LLColor4 overdriven_color = gSavedSettings.getColor4("OverdrivenColor"); + + updateSpeakerList(); + + // update status of all current speakers + BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + + speaker_map_t::iterator cur_speaker_it = speaker_it++; + + if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id)) + { + speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id); + + if (gVoiceClient->getOnMuteList(speaker_id)) + { + speakerp->mStatus = LLSpeaker::STATUS_MUTED; + speakerp->mDotColor = LLColor4::white; + } + else if (gVoiceClient->getIsSpeaking(speaker_id)) + { + // reset inactivity expiration + if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } + speakerp->mStatus = LLSpeaker::STATUS_SPEAKING; + // interpolate between active color and full speaking color based on power of speech output + speakerp->mDotColor = speaking_color; + if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL) + { + speakerp->mDotColor = overdriven_color; + } + } + else + { + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + + if (speakerp->mHasSpoken) + { + // have spoken once, not currently speaking + speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN; + } + else + { + // default state for being in voice channel + speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE; + } + } + } + // speaker no longer registered in voice channel, demote to text only + else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL) + { + speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY; + speakerp->mSpeechVolume = 0.f; + speakerp->mDotColor = ACTIVE_COLOR; + } + } + + // sort by status then time last spoken + std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers()); + + // for recent speakers who are not currently speaking, show "recent" color dot for most recent + // fading to "active" color + + S32 recent_speaker_count = 0; + S32 sort_index = 0; + speaker_list_t::iterator sorted_speaker_it; + for(sorted_speaker_it = mSpeakersSorted.begin(); + sorted_speaker_it != mSpeakersSorted.end(); ) + { + LLPointer speakerp = *sorted_speaker_it; + + // color code recent speakers who are not currently speaking + if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN) + { + speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f)); + recent_speaker_count++; + } + + // stuff sort ordinal into speaker so the ui can sort by this value + speakerp->mSortIndex = sort_index++; + + // remove speakers that have been gone too long + if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired()) + { + mSpeakers.erase(speakerp->mID); + sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it); + } + else + { + ++sorted_speaker_it; + } + } +} + +void LLSpeakerMgr::updateSpeakerList() +{ + // are we bound to the currently active voice channel? + if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive())) + { + LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList(); + LLVoiceClient::participantMap::iterator participant_it; + + // add new participants to our list of known speakers + for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it) + { + LLVoiceClient::participantState* participantp = participant_it->second; + setSpeaker(participantp->mAvatarID, "", LLSpeaker::STATUS_VOICE_ACTIVE); + } + } +} + +const LLPointer LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id) +{ + speaker_map_t::iterator found_it = mSpeakers.find(speaker_id); + if (found_it == mSpeakers.end()) + { + return NULL; + } + return found_it->second; +} + +void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text) +{ + speaker_list->clear(); + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLPointer speakerp = speaker_it->second; + // what about text only muted or inactive? + if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY) + { + speaker_list->push_back(speakerp); + } + } +} + +void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mTyping = typing; + } +} + +// speaker has chatted via either text or voice +void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id) +{ + LLPointer speakerp = findSpeaker(speaker_id); + if (speakerp.notNull()) + { + speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32(); + speakerp->mHasSpoken = TRUE; + } +} + +BOOL LLSpeakerMgr::isVoiceActive() +{ + // mVoiceChannel = NULL means current voice channel, whatever it is + return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive(); +} + + +// +// LLIMSpeakerMgr +// +LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel) +{ +} + +void LLIMSpeakerMgr::updateSpeakerList() +{ + // don't do normal updates which are pulled from voice channel + // rely on user list reported by sim + return; +} + +void LLIMSpeakerMgr::processSpeakerList(LLSD list) +{ + for(LLSD::array_iterator list_it = list.beginArray(); + list_it != list.endArray(); + ++list_it) + { + LLUUID agent_id(list_it->asUUID()); + + setSpeaker(agent_id, "", LLSpeaker::STATUS_TEXT_ONLY); + } +} + +void LLIMSpeakerMgr::processSpeakerMap(LLSD map) +{ + for(LLSD::map_iterator map_it = map.beginMap(); + map_it != map.endMap(); + ++map_it) + { + // add as new speaker + setSpeaker(LLUUID(map_it->first)); + } +} + + + +void LLIMSpeakerMgr::processSpeakerListUpdate(LLSD update) +{ + for(LLSD::map_iterator update_it = update.beginMap(); + update_it != update.endMap(); + ++update_it) + { + LLUUID agent_id(update_it->first); + + if (update_it->second.asString() == "LEAVE") + { + LLPointer speakerp = findSpeaker(agent_id); + if (speakerp) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + } + else if (update_it->second.asString() == "ENTER") + { + // add or update speaker + setSpeaker(agent_id); + } + else + { + llwarns << "LLIMSpeakerMgr::processSpeakerListUpdate() : bad membership list update " << ll_print_sd(update_it->second) << llendl; + } + } +} + + +// +// LLActiveSpeakerMgr +// + +LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL) +{ +} + +void LLActiveSpeakerMgr::updateSpeakerList() +{ + // point to whatever the current voice channel is + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + + // always populate from active voice channel + if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel) + { + mSpeakers.clear(); + mSpeakersSorted.clear(); + mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel(); + } + LLSpeakerMgr::updateSpeakerList(); +} + + + +// +// LLLocalSpeakerMgr +// + +LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance()) +{ +} + +LLLocalSpeakerMgr::~LLLocalSpeakerMgr () +{ +} + +void LLLocalSpeakerMgr::updateSpeakerList() +{ + // pull speakers from voice channel + LLSpeakerMgr::updateSpeakerList(); + + // add non-voice speakers in chat range + std::vector< LLCharacter* >::iterator avatar_it; + for(avatar_it = LLCharacter::sInstances.begin(); avatar_it != LLCharacter::sInstances.end(); ++avatar_it) + { + LLVOAvatar* avatarp = (LLVOAvatar*)*avatar_it; + if (dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) <= CHAT_NORMAL_RADIUS) + { + setSpeaker(avatarp->getID()); + } + } + + // check if text only speakers have moved out of chat range + for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it) + { + LLUUID speaker_id = speaker_it->first; + LLSpeaker* speakerp = speaker_it->second; + if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY) + { + LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id); + if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS) + { + speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL; + speakerp->mDotColor = INACTIVE_COLOR; + speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT); + } + } + } +} -- cgit v1.1