aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/newview/llfloateractivespeakers.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/newview/llfloateractivespeakers.cpp831
1 files changed, 831 insertions, 0 deletions
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 @@
1/**
2 * @file llfloateractivespeakers.cpp
3 * @brief Management interface for muting and controlling volume of residents currently speaking
4 *
5 * Copyright (c) 2005-2007, Linden Research, Inc.
6 *
7 * Second Life Viewer Source Code
8 * The source code in this file ("Source Code") is provided by Linden Lab
9 * to you under the terms of the GNU General Public License, version 2.0
10 * ("GPL"), unless you have obtained a separate licensing agreement
11 * ("Other License"), formally executed by you and Linden Lab. Terms of
12 * the GPL can be found in doc/GPL-license.txt in this distribution, or
13 * online at http://secondlife.com/developers/opensource/gplv2
14 *
15 * There are special exceptions to the terms and conditions of the GPL as
16 * it is applied to this Source Code. View the full text of the exception
17 * in the file doc/FLOSS-exception.txt in this software distribution, or
18 * online at http://secondlife.com/developers/opensource/flossexception
19 *
20 * By copying, modifying or distributing this software, you acknowledge
21 * that you have read and understood your obligations described above,
22 * and agree to abide by those obligations.
23 *
24 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
25 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
26 * COMPLETENESS OR PERFORMANCE.
27 */
28
29#include "llviewerprecompiledheaders.h"
30
31#include "llfloateractivespeakers.h"
32
33#include "llagent.h"
34#include "llvoavatar.h"
35#include "llfloateravatarinfo.h"
36#include "llvieweruictrlfactory.h"
37#include "llviewercontrol.h"
38#include "llscrolllistctrl.h"
39#include "llbutton.h"
40#include "lltextbox.h"
41#include "llmutelist.h"
42#include "llviewerobjectlist.h"
43#include "llimpanel.h" // LLVoiceChannel
44#include "llsdutil.h"
45
46const F32 SPEAKER_TIMEOUT = 10.f; // seconds of not being on voice channel before removed from list of active speakers
47const LLColor4 INACTIVE_COLOR(0.3f, 0.3f, 0.3f, 0.5f);
48const LLColor4 ACTIVE_COLOR(0.5f, 0.5f, 0.5f, 1.f);
49const F32 TYPING_ANIMATION_FPS = 2.5f;
50
51LLLocalSpeakerMgr* gLocalSpeakerMgr = NULL;
52LLActiveSpeakerMgr* gActiveChannelSpeakerMgr = NULL;
53
54LLSpeaker::speaker_map_t LLSpeaker::sSpeakers;
55
56LLSpeaker::LLSpeaker(const LLUUID& id, const LLString& name, const ESpeakerType type) :
57 mStatus(LLSpeaker::STATUS_TEXT_ONLY),
58 mLastSpokeTime(0.f),
59 mSpeechVolume(0.f),
60 mHasSpoken(FALSE),
61 mDotColor(LLColor4::white),
62 mID(id),
63 mTyping(FALSE),
64 mSortIndex(0),
65 mType(type)
66{
67 mHandle.init();
68 sSpeakers.insert(std::make_pair(mHandle, this));
69 if (name.empty() && type == SPEAKER_AGENT)
70 {
71 lookupName();
72 }
73 else
74 {
75 mDisplayName = name;
76 }
77 mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
78}
79
80LLSpeaker::~LLSpeaker()
81{
82 sSpeakers.erase(mHandle);
83}
84
85void LLSpeaker::lookupName()
86{
87 gCacheName->getName(mID, onAvatarNameLookup, new LLViewHandle(mHandle));
88}
89
90//static
91void LLSpeaker::onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data)
92{
93 LLViewHandle speaker_handle = *(LLViewHandle*)user_data;
94 delete (LLViewHandle*)user_data;
95
96 speaker_map_t::iterator found_it = sSpeakers.find(speaker_handle);
97 if (found_it != sSpeakers.end())
98 {
99 LLSpeaker* speakerp = found_it->second;
100 if (speakerp)
101 {
102 speakerp->mDisplayName = llformat("%s %s", first, last);
103 }
104 }
105}
106
107
108// helper sort class
109struct LLSortRecentSpeakers
110{
111 bool operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const;
112};
113
114bool LLSortRecentSpeakers::operator()(const LLPointer<LLSpeaker> lhs, const LLPointer<LLSpeaker> rhs) const
115{
116 // Sort first on status
117 if (lhs->mStatus != rhs->mStatus)
118 {
119 return (lhs->mStatus < rhs->mStatus);
120 }
121
122 // and then on last speaking time
123 if(lhs->mLastSpokeTime != rhs->mLastSpokeTime)
124 {
125 return (lhs->mLastSpokeTime > rhs->mLastSpokeTime);
126 }
127
128 // and finally (only if those are both equal), on name.
129 return( lhs->mDisplayName.compare(rhs->mDisplayName) < 0 );
130}
131
132LLFloaterActiveSpeakers::LLFloaterActiveSpeakers(const LLSD& seed) : mPanel(NULL)
133{
134 mFactoryMap["active_speakers_panel"] = LLCallbackMap(createSpeakersPanel, NULL);
135 // do not automatically open singleton floaters (as result of getInstance())
136 BOOL no_open = FALSE;
137 gUICtrlFactory->buildFloater(this, "floater_active_speakers.xml", &getFactoryMap(), no_open);
138 //RN: for now, we poll voice client every frame to get voice amplitude feedback
139 //gVoiceClient->addObserver(this);
140 mPanel->refreshSpeakers();
141}
142
143LLFloaterActiveSpeakers::~LLFloaterActiveSpeakers()
144{
145}
146
147void LLFloaterActiveSpeakers::onClose(bool app_quitting)
148{
149 setVisible(FALSE);
150}
151
152void LLFloaterActiveSpeakers::draw()
153{
154 // update state every frame to get live amplitude feedback
155 mPanel->refreshSpeakers();
156 LLFloater::draw();
157}
158
159BOOL LLFloaterActiveSpeakers::postBuild()
160{
161 mPanel = (LLPanelActiveSpeakers*)LLUICtrlFactory::getPanelByName(this, "active_speakers_panel");
162 return TRUE;
163}
164
165void LLFloaterActiveSpeakers::onChange()
166{
167 //refresh();
168}
169
170//static
171void* LLFloaterActiveSpeakers::createSpeakersPanel(void* data)
172{
173 // don't show text only speakers
174 return new LLPanelActiveSpeakers(gActiveChannelSpeakerMgr, FALSE);
175}
176
177
178//
179// LLPanelActiveSpeakers
180//
181LLPanelActiveSpeakers::LLPanelActiveSpeakers(LLSpeakerMgr* data_source, BOOL show_text_chatters) :
182 mSpeakerList(NULL),
183 mMuteVoiceCtrl(NULL),
184 mMuteTextCtrl(NULL),
185 mNameText(NULL),
186 mProfileBtn(NULL),
187 mShowTextChatters(show_text_chatters),
188 mSpeakerMgr(data_source)
189{
190 setMouseOpaque(FALSE);
191}
192
193LLPanelActiveSpeakers::~LLPanelActiveSpeakers()
194{
195
196}
197
198BOOL LLPanelActiveSpeakers::postBuild()
199{
200 mSpeakerList = LLUICtrlFactory::getScrollListByName(this, "speakers_list");
201
202 mMuteTextCtrl = (LLUICtrl*)getCtrlByNameAndType("mute_text_btn", WIDGET_TYPE_DONTCARE);
203 childSetCommitCallback("mute_text_btn", onClickMuteTextCommit, this);
204
205 mMuteVoiceCtrl = (LLUICtrl*)getCtrlByNameAndType("mute_btn", WIDGET_TYPE_DONTCARE);
206 childSetCommitCallback("mute_btn", onClickMuteVoiceCommit, this);
207 childSetAction("mute_btn", onClickMuteVoice, this);
208
209 childSetCommitCallback("speaker_volume", onVolumeChange, this);
210
211 mNameText = LLUICtrlFactory::getTextBoxByName(this, "resident_name");
212
213 mProfileBtn = LLUICtrlFactory::getButtonByName(this, "profile_btn");
214 childSetAction("profile_btn", onClickProfile, this);
215 return TRUE;
216}
217
218void LLPanelActiveSpeakers::refreshSpeakers()
219{
220 // store off current selection and scroll state to preserve across list rebuilds
221 LLUUID selected_id = mSpeakerList->getSimpleSelectedValue().asUUID();
222 S32 scroll_pos = mSpeakerList->getScrollInterface()->getScrollPos();
223
224 BOOL sort_ascending = mSpeakerList->getSortAscending();
225 LLString sort_column = mSpeakerList->getSortColumnName();
226 // TODO: put this in xml
227 // enforces default sort column of speaker status
228 if (sort_column.empty())
229 {
230 sort_column = "speaking_status";
231 }
232
233 mSpeakerMgr->update();
234
235 // clear scrolling list widget of names
236 mSpeakerList->clearRows();
237
238 LLSpeakerMgr::speaker_list_t speaker_list;
239 mSpeakerMgr->getSpeakerList(&speaker_list, mShowTextChatters);
240 for (LLSpeakerMgr::speaker_list_t::const_iterator speaker_it = speaker_list.begin(); speaker_it != speaker_list.end(); ++speaker_it)
241 {
242 LLUUID speaker_id = (*speaker_it)->mID;
243 LLPointer<LLSpeaker> speakerp = (*speaker_it);
244
245 // since we are forced to sort by text, encode sort order as string
246 LLString speaking_order_sort_string = llformat("%010d", speakerp->mSortIndex);
247
248 LLSD row;
249 row["id"] = speaker_id;
250
251 row["columns"][0]["column"] = "icon_speaking_status";
252 row["columns"][0]["type"] = "icon";
253 row["columns"][0]["color"] = speakerp->mDotColor.getValue();
254 LLString icon_image_id;
255
256 S32 icon_image_idx = llmin(2, llfloor((speakerp->mSpeechVolume / LLVoiceClient::OVERDRIVEN_POWER_LEVEL) * 3.f));
257 switch(icon_image_idx)
258 {
259 case 0:
260 icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl0.tga");
261 break;
262 case 1:
263 icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl1.tga");
264 break;
265 case 2:
266 icon_image_id = gViewerArt.getString("icn_active-speakers-dot-lvl2.tga");
267 break;
268 }
269 //if (speakerp->mTyping)
270 //{
271 // S32 typing_anim_idx = llround(mIconAnimationTimer.getElapsedTimeF32() * TYPING_ANIMATION_FPS) % 3;
272 // switch(typing_anim_idx)
273 // {
274 // case 0:
275 // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing1.tga"));
276 // break;
277 // case 1:
278 // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing2.tga"));
279 // break;
280 // case 2:
281 // row["columns"][0]["overlay"] = LLUUID(gViewerArt.getString("icn_active-speakers-typing3.tga"));
282 // break;
283 // default:
284 // break;
285 // }
286 //}
287
288 row["columns"][0]["value"] = speakerp->mStatus == LLSpeaker::STATUS_MUTED ?
289 gViewerArt.getString("mute_icon.tga") : icon_image_id;
290 if (speakerp->mStatus > LLSpeaker::STATUS_VOICE_ACTIVE) // if voice is disabled for this speaker
291 {
292 // non voice speakers have hidden icons, render as transparent
293 row["columns"][0]["color"] = LLColor4(0.f, 0.f, 0.f, 0.f).getValue();
294 }
295 row["columns"][1]["column"] = "speaker_name";
296 row["columns"][1]["type"] = "text";
297 if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL)
298 {
299 // draw inactive speakers in gray
300 row["columns"][1]["color"] = LLColor4::grey4.getValue();
301 }
302
303 if (speakerp->mDisplayName.empty())
304 {
305 row["columns"][1]["value"] = LLCacheName::getDefaultName();
306 }
307 else
308 {
309 row["columns"][1]["value"] = speakerp->mDisplayName;
310 }
311
312 row["columns"][2]["column"] = "speaking_status";
313 row["columns"][2]["type"] = "text";
314
315 // print speaking ordinal in a text-sorting friendly manner
316 row["columns"][2]["value"] = speaking_order_sort_string;
317
318 mSpeakerList->addElement(row);
319 }
320
321 //restore sort order, selection, etc
322 mSpeakerList->sortByColumn(sort_column, sort_ascending);
323 // make sure something is selected
324 if (selected_id.isNull())
325 {
326 mSpeakerList->selectFirstItem();
327 }
328 else
329 {
330 mSpeakerList->selectByValue(selected_id);
331 }
332
333 LLPointer<LLSpeaker> speakerp = mSpeakerMgr->findSpeaker(selected_id);
334
335 if (gMuteListp)
336 {
337 // update UI for selected participant
338 if (mMuteVoiceCtrl)
339 {
340 mMuteVoiceCtrl->setValue(gMuteListp->isMuted(selected_id, LLMute::flagVoiceChat));
341 mMuteVoiceCtrl->setEnabled(selected_id.notNull()
342 && selected_id != gAgent.getID()
343 && mSpeakerMgr->isVoiceActive()
344 && (speakerp.notNull() && speakerp->mType == LLSpeaker::SPEAKER_AGENT));
345 }
346 if (mMuteTextCtrl)
347 {
348 mMuteTextCtrl->setValue(gMuteListp->isMuted(selected_id, LLMute::flagTextChat));
349 mMuteTextCtrl->setEnabled(selected_id.notNull() && selected_id != gAgent.getID() && speakerp.notNull() && !gMuteListp->isLinden(speakerp->mDisplayName));
350 }
351 childSetValue("speaker_volume", gVoiceClient->getUserVolume(selected_id));
352 childSetEnabled("speaker_volume", selected_id.notNull()
353 && selected_id != gAgent.getID()
354 && mSpeakerMgr->isVoiceActive()
355 && (speakerp.notNull() && speakerp->mType == LLSpeaker::SPEAKER_AGENT));
356 if (mProfileBtn)
357 {
358 mProfileBtn->setEnabled(selected_id.notNull());
359 }
360 }
361
362 // show selected user name in large font
363 if (mNameText)
364 {
365 if (speakerp)
366 {
367 mNameText->setValue(speakerp->mDisplayName);
368 }
369 else
370 {
371 mNameText->setValue("");
372 }
373 }
374
375 // keep scroll value stable
376 mSpeakerList->getScrollInterface()->setScrollPos(scroll_pos);
377}
378
379void LLPanelActiveSpeakers::setSpeaker(const LLUUID& id, const LLString& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
380{
381 mSpeakerMgr->setSpeaker(id, name, status, type);
382}
383
384
385//static
386void LLPanelActiveSpeakers::onClickMuteTextCommit(LLUICtrl* ctrl, void* user_data)
387{
388 LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
389 LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
390 BOOL is_muted = gMuteListp->isMuted(speaker_id, LLMute::flagTextChat);
391 std::string name;
392
393 //fill in name using voice client's copy of name cache
394 LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
395 if (speakerp.isNull())
396 {
397 return;
398 }
399
400 name = speakerp->mDisplayName;
401
402 LLMute mute(speaker_id, name, speakerp->mType == LLSpeaker::SPEAKER_AGENT ? LLMute::AGENT : LLMute::OBJECT);
403
404 if (!is_muted)
405 {
406 gMuteListp->add(mute, LLMute::flagTextChat);
407 }
408 else
409 {
410 gMuteListp->remove(mute, LLMute::flagTextChat);
411 }
412}
413
414//static
415void LLPanelActiveSpeakers::onClickMuteVoice(void* user_data)
416{
417 onClickMuteVoiceCommit(NULL, user_data);
418}
419
420//static
421void LLPanelActiveSpeakers::onClickMuteVoiceCommit(LLUICtrl* ctrl, void* user_data)
422{
423 LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
424 LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
425 BOOL is_muted = gMuteListp->isMuted(speaker_id, LLMute::flagVoiceChat);
426 std::string name;
427
428 LLPointer<LLSpeaker> speakerp = panelp->mSpeakerMgr->findSpeaker(speaker_id);
429 if (speakerp.isNull())
430 {
431 return;
432 }
433
434 name = speakerp->mDisplayName;
435
436 // muting voice means we're dealing with an agent
437 LLMute mute(speaker_id, name, LLMute::AGENT);
438
439 if (!is_muted)
440 {
441 gMuteListp->add(mute, LLMute::flagVoiceChat);
442 }
443 else
444 {
445 gMuteListp->remove(mute, LLMute::flagVoiceChat);
446 }
447}
448
449
450//static
451void LLPanelActiveSpeakers::onVolumeChange(LLUICtrl* source, void* user_data)
452{
453 LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
454 LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
455
456 gVoiceClient->setUserVolume(speaker_id, (F32)panelp->childGetValue("speaker_volume").asReal());
457}
458
459//static
460void LLPanelActiveSpeakers::onClickProfile(void* user_data)
461{
462 LLPanelActiveSpeakers* panelp = (LLPanelActiveSpeakers*)user_data;
463 LLUUID speaker_id = panelp->mSpeakerList->getValue().asUUID();
464
465 LLFloaterAvatarInfo::showFromDirectory(speaker_id);
466}
467
468//
469// LLSpeakerMgr
470//
471
472LLSpeakerMgr::LLSpeakerMgr(LLVoiceChannel* channelp) :
473 mVoiceChannel(channelp)
474{
475}
476
477LLSpeakerMgr::~LLSpeakerMgr()
478{
479}
480
481LLPointer<LLSpeaker> LLSpeakerMgr::setSpeaker(const LLUUID& id, const LLString& name, LLSpeaker::ESpeakerStatus status, LLSpeaker::ESpeakerType type)
482{
483 if (id.isNull()) return NULL;
484
485 LLPointer<LLSpeaker> speakerp;
486 if (mSpeakers.find(id) == mSpeakers.end())
487 {
488 speakerp = new LLSpeaker(id, name, type);
489 speakerp->mStatus = status;
490 mSpeakers.insert(std::make_pair(speakerp->mID, speakerp));
491 mSpeakersSorted.push_back(speakerp);
492 }
493 else
494 {
495 speakerp = findSpeaker(id);
496 if (speakerp.notNull())
497 {
498 // keep highest priority status (lowest value) instead of overriding current value
499 speakerp->mStatus = llmin(speakerp->mStatus, status);
500 speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
501 // RN: due to a weird behavior where IMs from attached objects come from the wearer's agent_id
502 // we need to override speakers that we think are objects when we find out they are really
503 // residents
504 if (type == LLSpeaker::SPEAKER_AGENT)
505 {
506 speakerp->mType = LLSpeaker::SPEAKER_AGENT;
507 speakerp->lookupName();
508 }
509 }
510 }
511
512 return speakerp;
513}
514
515void LLSpeakerMgr::update()
516{
517 if (!gVoiceClient)
518 {
519 return;
520 }
521
522 LLColor4 speaking_color = gSavedSettings.getColor4("SpeakingColor");
523 LLColor4 overdriven_color = gSavedSettings.getColor4("OverdrivenColor");
524
525 updateSpeakerList();
526
527 // update status of all current speakers
528 BOOL voice_channel_active = (!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive());
529 for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end();)
530 {
531 LLUUID speaker_id = speaker_it->first;
532 LLSpeaker* speakerp = speaker_it->second;
533
534 speaker_map_t::iterator cur_speaker_it = speaker_it++;
535
536 if (voice_channel_active && gVoiceClient->getVoiceEnabled(speaker_id))
537 {
538 speakerp->mSpeechVolume = gVoiceClient->getCurrentPower(speaker_id);
539
540 if (gVoiceClient->getOnMuteList(speaker_id))
541 {
542 speakerp->mStatus = LLSpeaker::STATUS_MUTED;
543 speakerp->mDotColor = LLColor4::white;
544 }
545 else if (gVoiceClient->getIsSpeaking(speaker_id))
546 {
547 // reset inactivity expiration
548 if (speakerp->mStatus != LLSpeaker::STATUS_SPEAKING)
549 {
550 speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
551 speakerp->mHasSpoken = TRUE;
552 }
553 speakerp->mStatus = LLSpeaker::STATUS_SPEAKING;
554 // interpolate between active color and full speaking color based on power of speech output
555 speakerp->mDotColor = speaking_color;
556 if (speakerp->mSpeechVolume > LLVoiceClient::OVERDRIVEN_POWER_LEVEL)
557 {
558 speakerp->mDotColor = overdriven_color;
559 }
560 }
561 else
562 {
563 speakerp->mSpeechVolume = 0.f;
564 speakerp->mDotColor = ACTIVE_COLOR;
565
566 if (speakerp->mHasSpoken)
567 {
568 // have spoken once, not currently speaking
569 speakerp->mStatus = LLSpeaker::STATUS_HAS_SPOKEN;
570 }
571 else
572 {
573 // default state for being in voice channel
574 speakerp->mStatus = LLSpeaker::STATUS_VOICE_ACTIVE;
575 }
576 }
577 }
578 // speaker no longer registered in voice channel, demote to text only
579 else if (speakerp->mStatus != LLSpeaker::STATUS_NOT_IN_CHANNEL)
580 {
581 speakerp->mStatus = LLSpeaker::STATUS_TEXT_ONLY;
582 speakerp->mSpeechVolume = 0.f;
583 speakerp->mDotColor = ACTIVE_COLOR;
584 }
585 }
586
587 // sort by status then time last spoken
588 std::sort(mSpeakersSorted.begin(), mSpeakersSorted.end(), LLSortRecentSpeakers());
589
590 // for recent speakers who are not currently speaking, show "recent" color dot for most recent
591 // fading to "active" color
592
593 S32 recent_speaker_count = 0;
594 S32 sort_index = 0;
595 speaker_list_t::iterator sorted_speaker_it;
596 for(sorted_speaker_it = mSpeakersSorted.begin();
597 sorted_speaker_it != mSpeakersSorted.end(); )
598 {
599 LLPointer<LLSpeaker> speakerp = *sorted_speaker_it;
600
601 // color code recent speakers who are not currently speaking
602 if (speakerp->mStatus == LLSpeaker::STATUS_HAS_SPOKEN)
603 {
604 speakerp->mDotColor = lerp(speaking_color, ACTIVE_COLOR, clamp_rescale((F32)recent_speaker_count, -2.f, 3.f, 0.f, 1.f));
605 recent_speaker_count++;
606 }
607
608 // stuff sort ordinal into speaker so the ui can sort by this value
609 speakerp->mSortIndex = sort_index++;
610
611 // remove speakers that have been gone too long
612 if (speakerp->mStatus == LLSpeaker::STATUS_NOT_IN_CHANNEL && speakerp->mActivityTimer.hasExpired())
613 {
614 mSpeakers.erase(speakerp->mID);
615 sorted_speaker_it = mSpeakersSorted.erase(sorted_speaker_it);
616 }
617 else
618 {
619 ++sorted_speaker_it;
620 }
621 }
622}
623
624void LLSpeakerMgr::updateSpeakerList()
625{
626 // are we bound to the currently active voice channel?
627 if ((!mVoiceChannel && gVoiceClient->inProximalChannel()) || (mVoiceChannel && mVoiceChannel->isActive()))
628 {
629 LLVoiceClient::participantMap* participants = gVoiceClient->getParticipantList();
630 LLVoiceClient::participantMap::iterator participant_it;
631
632 // add new participants to our list of known speakers
633 for (participant_it = participants->begin(); participant_it != participants->end(); ++participant_it)
634 {
635 LLVoiceClient::participantState* participantp = participant_it->second;
636 setSpeaker(participantp->mAvatarID, "", LLSpeaker::STATUS_VOICE_ACTIVE);
637 }
638 }
639}
640
641const LLPointer<LLSpeaker> LLSpeakerMgr::findSpeaker(const LLUUID& speaker_id)
642{
643 speaker_map_t::iterator found_it = mSpeakers.find(speaker_id);
644 if (found_it == mSpeakers.end())
645 {
646 return NULL;
647 }
648 return found_it->second;
649}
650
651void LLSpeakerMgr::getSpeakerList(speaker_list_t* speaker_list, BOOL include_text)
652{
653 speaker_list->clear();
654 for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
655 {
656 LLPointer<LLSpeaker> speakerp = speaker_it->second;
657 // what about text only muted or inactive?
658 if (include_text || speakerp->mStatus != LLSpeaker::STATUS_TEXT_ONLY)
659 {
660 speaker_list->push_back(speakerp);
661 }
662 }
663}
664
665void LLSpeakerMgr::setSpeakerTyping(const LLUUID& speaker_id, BOOL typing)
666{
667 LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
668 if (speakerp.notNull())
669 {
670 speakerp->mTyping = typing;
671 }
672}
673
674// speaker has chatted via either text or voice
675void LLSpeakerMgr::speakerChatted(const LLUUID& speaker_id)
676{
677 LLPointer<LLSpeaker> speakerp = findSpeaker(speaker_id);
678 if (speakerp.notNull())
679 {
680 speakerp->mLastSpokeTime = mSpeechTimer.getElapsedTimeF32();
681 speakerp->mHasSpoken = TRUE;
682 }
683}
684
685BOOL LLSpeakerMgr::isVoiceActive()
686{
687 // mVoiceChannel = NULL means current voice channel, whatever it is
688 return LLVoiceClient::voiceEnabled() && mVoiceChannel && mVoiceChannel->isActive();
689}
690
691
692//
693// LLIMSpeakerMgr
694//
695LLIMSpeakerMgr::LLIMSpeakerMgr(LLVoiceChannel* channel) : LLSpeakerMgr(channel)
696{
697}
698
699void LLIMSpeakerMgr::updateSpeakerList()
700{
701 // don't do normal updates which are pulled from voice channel
702 // rely on user list reported by sim
703 return;
704}
705
706void LLIMSpeakerMgr::processSpeakerList(LLSD list)
707{
708 for(LLSD::array_iterator list_it = list.beginArray();
709 list_it != list.endArray();
710 ++list_it)
711 {
712 LLUUID agent_id(list_it->asUUID());
713
714 setSpeaker(agent_id, "", LLSpeaker::STATUS_TEXT_ONLY);
715 }
716}
717
718void LLIMSpeakerMgr::processSpeakerMap(LLSD map)
719{
720 for(LLSD::map_iterator map_it = map.beginMap();
721 map_it != map.endMap();
722 ++map_it)
723 {
724 // add as new speaker
725 setSpeaker(LLUUID(map_it->first));
726 }
727}
728
729
730
731void LLIMSpeakerMgr::processSpeakerListUpdate(LLSD update)
732{
733 for(LLSD::map_iterator update_it = update.beginMap();
734 update_it != update.endMap();
735 ++update_it)
736 {
737 LLUUID agent_id(update_it->first);
738
739 if (update_it->second.asString() == "LEAVE")
740 {
741 LLPointer<LLSpeaker> speakerp = findSpeaker(agent_id);
742 if (speakerp)
743 {
744 speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
745 speakerp->mDotColor = INACTIVE_COLOR;
746 speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
747 }
748 }
749 else if (update_it->second.asString() == "ENTER")
750 {
751 // add or update speaker
752 setSpeaker(agent_id);
753 }
754 else
755 {
756 llwarns << "LLIMSpeakerMgr::processSpeakerListUpdate() : bad membership list update " << ll_print_sd(update_it->second) << llendl;
757 }
758 }
759}
760
761
762//
763// LLActiveSpeakerMgr
764//
765
766LLActiveSpeakerMgr::LLActiveSpeakerMgr() : LLSpeakerMgr(NULL)
767{
768}
769
770void LLActiveSpeakerMgr::updateSpeakerList()
771{
772 // point to whatever the current voice channel is
773 mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
774
775 // always populate from active voice channel
776 if (LLVoiceChannel::getCurrentVoiceChannel() != mVoiceChannel)
777 {
778 mSpeakers.clear();
779 mSpeakersSorted.clear();
780 mVoiceChannel = LLVoiceChannel::getCurrentVoiceChannel();
781 }
782 LLSpeakerMgr::updateSpeakerList();
783}
784
785
786
787//
788// LLLocalSpeakerMgr
789//
790
791LLLocalSpeakerMgr::LLLocalSpeakerMgr() : LLSpeakerMgr(LLVoiceChannelProximal::getInstance())
792{
793}
794
795LLLocalSpeakerMgr::~LLLocalSpeakerMgr ()
796{
797}
798
799void LLLocalSpeakerMgr::updateSpeakerList()
800{
801 // pull speakers from voice channel
802 LLSpeakerMgr::updateSpeakerList();
803
804 // add non-voice speakers in chat range
805 std::vector< LLCharacter* >::iterator avatar_it;
806 for(avatar_it = LLCharacter::sInstances.begin(); avatar_it != LLCharacter::sInstances.end(); ++avatar_it)
807 {
808 LLVOAvatar* avatarp = (LLVOAvatar*)*avatar_it;
809 if (dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) <= CHAT_NORMAL_RADIUS)
810 {
811 setSpeaker(avatarp->getID());
812 }
813 }
814
815 // check if text only speakers have moved out of chat range
816 for (speaker_map_t::iterator speaker_it = mSpeakers.begin(); speaker_it != mSpeakers.end(); ++speaker_it)
817 {
818 LLUUID speaker_id = speaker_it->first;
819 LLSpeaker* speakerp = speaker_it->second;
820 if (speakerp->mStatus == LLSpeaker::STATUS_TEXT_ONLY)
821 {
822 LLVOAvatar* avatarp = (LLVOAvatar*)gObjectList.findObject(speaker_id);
823 if (!avatarp || dist_vec(avatarp->getPositionAgent(), gAgent.getPositionAgent()) > CHAT_NORMAL_RADIUS)
824 {
825 speakerp->mStatus = LLSpeaker::STATUS_NOT_IN_CHANNEL;
826 speakerp->mDotColor = INACTIVE_COLOR;
827 speakerp->mActivityTimer.resetWithExpiry(SPEAKER_TIMEOUT);
828 }
829 }
830 }
831}