re-merge e7cef87fae39 - EXT-6031 refactoring of voice volume representation

master
Roxie Linden 2010-05-04 14:03:30 -07:00
parent 658ccc3e85
commit 2848d35e2c
4 changed files with 165 additions and 89 deletions

View File

@ -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;

View 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;
};

View File

@ -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;
}
}
}

View File

@ -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;