re-merge e7cef87fae39 - EXT-6031 refactoring of voice volume representation
parent
658ccc3e85
commit
2848d35e2c
|
|
@ -43,6 +43,10 @@
|
|||
|
||||
const F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
|
||||
|
||||
const F32 LLVoiceClient::VOLUME_MIN = 0.f;
|
||||
const F32 LLVoiceClient::VOLUME_DEFAULT = 0.5f;
|
||||
const F32 LLVoiceClient::VOLUME_MAX = 1.0f;
|
||||
|
||||
std::string LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
|
||||
{
|
||||
std::string result = "UNKNOWN";
|
||||
|
|
@ -795,23 +799,85 @@ LLSpeakerVolumeStorage::~LLSpeakerVolumeStorage()
|
|||
|
||||
void LLSpeakerVolumeStorage::storeSpeakerVolume(const LLUUID& speaker_id, F32 volume)
|
||||
{
|
||||
mSpeakersData[speaker_id] = volume;
|
||||
if ((volume >= LLVoiceClient::VOLUME_MIN) && (volume <= LLVoiceClient::VOLUME_MAX))
|
||||
{
|
||||
mSpeakersData[speaker_id] = volume;
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "Stored volume = " << volume << " for " << id << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS("Voice") << "Attempted to store out of range volume " << volume << " for " << speaker_id << LL_ENDL;
|
||||
llassert(0);
|
||||
}
|
||||
}
|
||||
|
||||
S32 LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id)
|
||||
bool LLSpeakerVolumeStorage::getSpeakerVolume(const LLUUID& speaker_id, F32& volume)
|
||||
{
|
||||
// Return value of -1 indicates no level is stored for this speaker
|
||||
S32 ret_val = -1;
|
||||
speaker_data_map_t::const_iterator it = mSpeakersData.find(speaker_id);
|
||||
|
||||
if (it != mSpeakersData.end())
|
||||
{
|
||||
F32 f_val = it->second;
|
||||
// volume can amplify by as much as 4x!
|
||||
S32 ivol = (S32)(400.f * f_val * f_val);
|
||||
ret_val = llclamp(ivol, 0, 400);
|
||||
volume = it->second;
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "Retrieved stored volume = " << volume << " for " << id << LL_ENDL;
|
||||
|
||||
return true;
|
||||
}
|
||||
return ret_val;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void LLSpeakerVolumeStorage::removeSpeakerVolume(const LLUUID& speaker_id)
|
||||
{
|
||||
mSpeakersData.erase(speaker_id);
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "Removing stored volume for " << id << LL_ENDL;
|
||||
}
|
||||
|
||||
/* static */ F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in)
|
||||
{
|
||||
// Convert to linear-logarithmic [0.0..1.0] with 0.5 = 0dB
|
||||
// from legacy characteristic composed of two square-curves
|
||||
// that intersect at volume_in = 0.5, volume_out = 0.56
|
||||
|
||||
F32 volume_out = 0.f;
|
||||
volume_in = llclamp(volume_in, 0.f, 1.0f);
|
||||
|
||||
if (volume_in <= 0.5f)
|
||||
{
|
||||
volume_out = volume_in * volume_in * 4.f * 0.56f;
|
||||
}
|
||||
else
|
||||
{
|
||||
volume_out = (1.f - 0.56f) * (4.f * volume_in * volume_in - 1.f) / 3.f + 0.56f;
|
||||
}
|
||||
|
||||
return volume_out;
|
||||
}
|
||||
|
||||
/* static */ F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in)
|
||||
{
|
||||
// Convert from linear-logarithmic [0.0..1.0] with 0.5 = 0dB
|
||||
// to legacy characteristic composed of two square-curves
|
||||
// that intersect at volume_in = 0.56, volume_out = 0.5
|
||||
|
||||
F32 volume_out = 0.f;
|
||||
volume_in = llclamp(volume_in, 0.f, 1.0f);
|
||||
|
||||
if (volume_in <= 0.56f)
|
||||
{
|
||||
volume_out = sqrt(volume_in / (4.f * 0.56f));
|
||||
}
|
||||
else
|
||||
{
|
||||
volume_out = sqrt((3.f * (volume_in - 0.56f) / (1.f - 0.56f) + 1.f) / 4.f);
|
||||
}
|
||||
|
||||
return volume_out;
|
||||
}
|
||||
|
||||
void LLSpeakerVolumeStorage::load()
|
||||
|
|
@ -819,6 +885,8 @@ void LLSpeakerVolumeStorage::load()
|
|||
// load per-resident voice volume information
|
||||
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
|
||||
|
||||
LL_INFOS("Voice") << "Loading stored speaker volumes from: " << filename << LL_ENDL;
|
||||
|
||||
LLSD settings_llsd;
|
||||
llifstream file;
|
||||
file.open(filename);
|
||||
|
|
@ -830,7 +898,10 @@ void LLSpeakerVolumeStorage::load()
|
|||
for (LLSD::map_const_iterator iter = settings_llsd.beginMap();
|
||||
iter != settings_llsd.endMap(); ++iter)
|
||||
{
|
||||
mSpeakersData.insert(std::make_pair(LLUUID(iter->first), (F32)iter->second.asReal()));
|
||||
// Maintain compatibility with 1.23 non-linear saved volume levels
|
||||
F32 volume = transformFromLegacyVolume((F32)iter->second.asReal());
|
||||
|
||||
storeSpeakerVolume(LLUUID(iter->first), volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -845,9 +916,14 @@ void LLSpeakerVolumeStorage::save()
|
|||
std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, SETTINGS_FILE_NAME);
|
||||
LLSD settings_llsd;
|
||||
|
||||
LL_INFOS("Voice") << "Saving stored speaker volumes to: " << filename << LL_ENDL;
|
||||
|
||||
for(speaker_data_map_t::const_iterator iter = mSpeakersData.begin(); iter != mSpeakersData.end(); ++iter)
|
||||
{
|
||||
settings_llsd[iter->first.asString()] = iter->second;
|
||||
// Maintain compatibility with 1.23 non-linear saved volume levels
|
||||
F32 volume = transformToLegacyVolume(iter->second);
|
||||
|
||||
settings_llsd[iter->first.asString()] = volume;
|
||||
}
|
||||
|
||||
llofstream file;
|
||||
|
|
|
|||
|
|
@ -272,7 +272,11 @@ public:
|
|||
|
||||
const LLVoiceVersionInfo getVersion();
|
||||
|
||||
static const F32 OVERDRIVEN_POWER_LEVEL;
|
||||
static const F32 OVERDRIVEN_POWER_LEVEL;
|
||||
|
||||
static const F32 VOLUME_MIN;
|
||||
static const F32 VOLUME_DEFAULT;
|
||||
static const F32 VOLUME_MAX;
|
||||
|
||||
void updateSettings(); // call after loading settings and whenever they change
|
||||
|
||||
|
|
@ -406,31 +410,34 @@ protected:
|
|||
/**
|
||||
* Speaker volume storage helper class
|
||||
**/
|
||||
|
||||
class LLSpeakerVolumeStorage : public LLSingleton<LLSpeakerVolumeStorage>
|
||||
{
|
||||
LOG_CLASS(LLSpeakerVolumeStorage);
|
||||
public:
|
||||
|
||||
/**
|
||||
* Sets internal voluem level for specified user.
|
||||
* Stores volume level for specified user.
|
||||
*
|
||||
* @param[in] speaker_id - LLUUID of user to store volume level for
|
||||
* @param[in] volume - external volume level to be stored for user.
|
||||
* @param[in] speaker_id - LLUUID of user to store volume level for.
|
||||
* @param[in] volume - volume level to be stored for user.
|
||||
*/
|
||||
void storeSpeakerVolume(const LLUUID& speaker_id, F32 volume);
|
||||
|
||||
/**
|
||||
* Gets stored external volume level for specified speaker.
|
||||
* Gets stored volume level for specified speaker
|
||||
*
|
||||
* If specified user is not found default level will be returned. It is equivalent of
|
||||
* external level 0.5 from the 0.0..1.0 range.
|
||||
* Default external level is calculated as: internal = 400 * external^2
|
||||
* Maps 0.0 to 1.0 to internal values 0-400 with default 0.5 == 100
|
||||
*
|
||||
* @param[in] speaker_id - LLUUID of user to get his volume level
|
||||
* @param[in] speaker_id - LLUUID of user to retrieve volume level for.
|
||||
* @param[out] volume - set to stored volume if found, otherwise unmodified.
|
||||
* @return - true if a stored volume is found.
|
||||
*/
|
||||
S32 getSpeakerVolume(const LLUUID& speaker_id);
|
||||
bool getSpeakerVolume(const LLUUID& speaker_id, F32& volume);
|
||||
|
||||
/**
|
||||
* Removes stored volume level for specified user.
|
||||
*
|
||||
* @param[in] speaker_id - LLUUID of user to remove.
|
||||
*/
|
||||
void removeSpeakerVolume(const LLUUID& speaker_id);
|
||||
|
||||
private:
|
||||
friend class LLSingleton<LLSpeakerVolumeStorage>;
|
||||
|
|
@ -442,6 +449,9 @@ private:
|
|||
void load();
|
||||
void save();
|
||||
|
||||
static F32 LLSpeakerVolumeStorage::transformFromLegacyVolume(F32 volume_in);
|
||||
static F32 LLSpeakerVolumeStorage::transformToLegacyVolume(F32 volume_in);
|
||||
|
||||
typedef std::map<LLUUID, F32> speaker_data_map_t;
|
||||
speaker_data_map_t mSpeakersData;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -78,6 +78,8 @@
|
|||
|
||||
#define USE_SESSION_GROUPS 0
|
||||
|
||||
const F32 VOLUME_SCALE_VIVOX = 0.01f;
|
||||
|
||||
const F32 SPEAKING_TIMEOUT = 1.f;
|
||||
|
||||
static const std::string VOICE_SERVER_TYPE = "Vivox";
|
||||
|
|
@ -1476,9 +1478,10 @@ void LLVivoxVoiceClient::stateMachine()
|
|||
enforceTether();
|
||||
}
|
||||
|
||||
// Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
|
||||
// or every 10hz, whichever is sooner.
|
||||
if((mAudioSession && mAudioSession->mVolumeDirty) || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
|
||||
// Send an update only if the ptt or mute state has changed (which shouldn't be able to happen that often
|
||||
// -- the user can only click so fast) or every 10hz, whichever is sooner.
|
||||
// Sending for every volume update causes an excessive flood of messages whenever a volume slider is dragged.
|
||||
if((mAudioSession && mAudioSession->mMuteDirty) || mPTTDirty || mUpdateTimer.hasExpired())
|
||||
{
|
||||
mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
|
||||
sendPositionalUpdate();
|
||||
|
|
@ -2475,11 +2478,12 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
|
|||
stream << "</Request>\n\n\n";
|
||||
}
|
||||
|
||||
if(mAudioSession && mAudioSession->mVolumeDirty)
|
||||
if(mAudioSession && (mAudioSession->mVolumeDirty || mAudioSession->mMuteDirty))
|
||||
{
|
||||
participantMap::iterator iter = mAudioSession->mParticipantsByURI.begin();
|
||||
|
||||
mAudioSession->mVolumeDirty = false;
|
||||
mAudioSession->mMuteDirty = false;
|
||||
|
||||
for(; iter != mAudioSession->mParticipantsByURI.end(); iter++)
|
||||
{
|
||||
|
|
@ -2490,7 +2494,8 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
|
|||
// Can't set volume/mute for yourself
|
||||
if(!p->mIsSelf)
|
||||
{
|
||||
int volume = 56; // nominal default value
|
||||
// scale from the range 0.0-1.0 to vivox volume in the range 0-100
|
||||
S32 volume = llround(p->mVolume / VOLUME_SCALE_VIVOX);
|
||||
bool mute = p->mOnMuteList;
|
||||
|
||||
if(p->mUserVolume != -1)
|
||||
|
|
@ -2514,10 +2519,15 @@ void LLVivoxVoiceClient::sendPositionalUpdate(void)
|
|||
// If we want the user to be muted, set their volume to 0 as well.
|
||||
// This isn't perfect, but it will at least reduce their volume to a minimum.
|
||||
volume = 0;
|
||||
// Mark the current volume level as set to prevent incoming events
|
||||
// changing it to 0, so that we can return to it when unmuting.
|
||||
p->mVolumeSet = true;
|
||||
}
|
||||
|
||||
if(volume == 0)
|
||||
{
|
||||
mute = true;
|
||||
}
|
||||
|
||||
LL_DEBUGS("Voice") << "Setting volume/mute for avatar " << p->mAvatarID << " to " << volume << (mute?"/true":"/false") << LL_ENDL;
|
||||
|
||||
|
|
@ -3671,15 +3681,12 @@ void LLVivoxVoiceClient::participantUpdatedEvent(
|
|||
participant->mPower = 0.0f;
|
||||
}
|
||||
|
||||
// *HACK: Minimal hack to fix EXT-6508, ignore the incoming volume if it is zero.
|
||||
// This happens because we send volume zero to Vivox when someone is muted,
|
||||
// Vivox then send it back to us, overwriting the previous volume.
|
||||
// Remove this hack once volume refactoring from EXT-6031 is applied.
|
||||
if (volume != 0)
|
||||
{
|
||||
participant->mVolume = volume;
|
||||
}
|
||||
|
||||
// Ignore incoming volume level if it has been explicitly set, or there
|
||||
// is a volume or mute change pending.
|
||||
if ( !participant->mVolumeSet && !participant->mVolumeDirty)
|
||||
{
|
||||
participant->mVolume = (F32)volume * VOLUME_SCALE_VIVOX;
|
||||
}
|
||||
|
||||
// *HACK: mantipov: added while working on EXT-3544
|
||||
/*
|
||||
|
|
@ -4089,7 +4096,7 @@ LLVivoxVoiceClient::participantState::participantState(const std::string &uri) :
|
|||
mIsModeratorMuted(false),
|
||||
mLastSpokeTimestamp(0.f),
|
||||
mPower(0.f),
|
||||
mVolume(-1),
|
||||
mVolume(LLVoiceClient::VOLUME_DEFAULT),
|
||||
mOnMuteList(false),
|
||||
mUserVolume(-1),
|
||||
mVolumeDirty(false),
|
||||
|
|
@ -4141,7 +4148,7 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti
|
|||
result->mAvatarID = id;
|
||||
|
||||
if(result->updateMuteState())
|
||||
mVolumeDirty = true;
|
||||
mMuteDirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -4153,7 +4160,11 @@ LLVivoxVoiceClient::participantState *LLVivoxVoiceClient::sessionState::addParti
|
|||
|
||||
mParticipantsByUUID.insert(participantUUIDMap::value_type(result->mAvatarID, result));
|
||||
|
||||
result->mUserVolume = LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID);
|
||||
if (LLSpeakerVolumeStorage::getInstance()->getSpeakerVolume(result->mAvatarID, result->mVolume))
|
||||
{
|
||||
result->mVolumeDirty = true;
|
||||
mVolumeDirty = true;
|
||||
}
|
||||
|
||||
LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
|
||||
}
|
||||
|
|
@ -5351,51 +5362,21 @@ BOOL LLVivoxVoiceClient::getOnMuteList(const LLUUID& id)
|
|||
return result;
|
||||
}
|
||||
|
||||
// External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
|
||||
// internal = 400 * external^2
|
||||
// External accessors.
|
||||
F32 LLVivoxVoiceClient::getUserVolume(const LLUUID& id)
|
||||
{
|
||||
F32 result = 0.0f;
|
||||
// Minimum volume will be returned for users with voice disabled
|
||||
F32 result = LLVoiceClient::VOLUME_MIN;
|
||||
|
||||
participantState *participant = findParticipantByID(id);
|
||||
if(participant)
|
||||
participantState *participant = findParticipantByID(id);
|
||||
if(participant)
|
||||
{
|
||||
S32 ires = 100; // nominal default volume
|
||||
|
||||
if(participant->mIsSelf)
|
||||
{
|
||||
// Always make it look like the user's own volume is set at the default.
|
||||
}
|
||||
else if(participant->mUserVolume != -1)
|
||||
{
|
||||
// Use the internal volume
|
||||
ires = participant->mUserVolume;
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "mapping from mUserVolume " << ires << LL_ENDL;
|
||||
}
|
||||
else if(participant->mVolume != -1)
|
||||
{
|
||||
// Map backwards from vivox volume
|
||||
result = participant->mVolume;
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "mapping from mVolume " << participant->mVolume << LL_ENDL;
|
||||
|
||||
if(participant->mVolume < 56)
|
||||
{
|
||||
ires = (participant->mVolume * 100) / 56;
|
||||
}
|
||||
else
|
||||
{
|
||||
ires = (((participant->mVolume - 56) * 300) / (100 - 56)) + 100;
|
||||
}
|
||||
}
|
||||
result = sqrtf(((F32)ires) / 400.f);
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "mVolume = " << result << " for " << id << LL_ENDL;
|
||||
}
|
||||
|
||||
// Enable this when debugging voice slider issues. It's way to spammy even for debug-level logging.
|
||||
// LL_DEBUGS("Voice") << "returning " << result << LL_ENDL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -5404,16 +5385,23 @@ void LLVivoxVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
|
|||
if(mAudioSession)
|
||||
{
|
||||
participantState *participant = findParticipantByID(id);
|
||||
if (participant)
|
||||
if (participant && !participant->mIsSelf)
|
||||
{
|
||||
// store this volume setting for future sessions
|
||||
LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume);
|
||||
// volume can amplify by as much as 4x!
|
||||
S32 ivol = (S32)(400.f * volume * volume);
|
||||
participant->mUserVolume = llclamp(ivol, 0, 400);
|
||||
participant->mVolumeDirty = TRUE;
|
||||
mAudioSession->mVolumeDirty = TRUE;
|
||||
if (!is_approx_equal(volume, LLVoiceClient::VOLUME_DEFAULT))
|
||||
{
|
||||
// Store this volume setting for future sessions if it has been
|
||||
// changed from the default
|
||||
LLSpeakerVolumeStorage::getInstance()->storeSpeakerVolume(id, volume);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove stored volume setting if it is returned to the default
|
||||
LLSpeakerVolumeStorage::getInstance()->removeSpeakerVolume(id);
|
||||
}
|
||||
|
||||
participant->mVolume = llclamp(volume, LLVoiceClient::VOLUME_MIN, LLVoiceClient::VOLUME_MAX);
|
||||
participant->mVolumeDirty = true;
|
||||
mAudioSession->mVolumeDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ protected:
|
|||
public:
|
||||
participantState(const std::string &uri);
|
||||
|
||||
bool updateMuteState();
|
||||
bool updateMuteState(); // true if mute state has changed
|
||||
bool isAvatar();
|
||||
|
||||
std::string mURI;
|
||||
|
|
@ -270,13 +270,14 @@ protected:
|
|||
LLFrameTimer mSpeakingTimeout;
|
||||
F32 mLastSpokeTimestamp;
|
||||
F32 mPower;
|
||||
int mVolume;
|
||||
F32 mVolume;
|
||||
std::string mGroupID;
|
||||
int mUserVolume;
|
||||
bool mPTT;
|
||||
bool mIsSpeaking;
|
||||
bool mIsModeratorMuted;
|
||||
bool mOnMuteList; // true if this avatar is on the user's mute list (and should be muted)
|
||||
bool mVolumeSet; // true if incoming volume messages should not change the volume
|
||||
bool mVolumeDirty; // true if this participant needs a volume command sent (either mOnMuteList or mUserVolume has changed)
|
||||
bool mAvatarIDValid;
|
||||
bool mIsSelf;
|
||||
|
|
@ -333,6 +334,7 @@ protected:
|
|||
// Set to true when the mute state of someone in the participant list changes.
|
||||
// The code will have to walk the list to find the changed participant(s).
|
||||
bool mVolumeDirty;
|
||||
bool mMuteDirty;
|
||||
|
||||
bool mParticipantsChanged;
|
||||
participantMap mParticipantsByURI;
|
||||
|
|
|
|||
Loading…
Reference in New Issue