mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
Merge pull request #4439: FEAT(client, mainwindow): Show volume adjustments
This feature optionally allows users to display the set volume adjustments next to the user's/listener's name. It will only be shown if it is not equal to 0 dB (aka: it is actually having an effect). By default this option is enabled since it is only visible if volume adjustments are set up anyways.
This commit is contained in:
commit
ade9e9e46b
@ -518,7 +518,7 @@ bool AudioOutput::mix(void *outbuff, unsigned int frameCount) {
|
||||
AudioOutputSpeech *speech = qobject_cast<AudioOutputSpeech *>(aop);
|
||||
if (speech) {
|
||||
const ClientUser *user = speech->p;
|
||||
volumeAdjustment *= user->fLocalVolume;
|
||||
volumeAdjustment *= user->getLocalVolumeAdjustments();
|
||||
|
||||
if (user->cChannel && ChannelListener::isListening(g.uiSession, user->cChannel->iId) && (speech->ucFlags & SpeechFlags::Listen)) {
|
||||
// We are receiving this audio packet only because we are listening to the channel
|
||||
|
||||
@ -24,11 +24,14 @@ ClientUser::ClientUser(QObject *p) : QObject(p),
|
||||
fPowerMin(0.0f),
|
||||
fPowerMax(0.0f),
|
||||
fAverageAvailable(0.0f),
|
||||
fLocalVolume(1.0f),
|
||||
iFrames(0),
|
||||
iSequence(0) {
|
||||
}
|
||||
|
||||
float ClientUser::getLocalVolumeAdjustments() const {
|
||||
return m_localVolume;
|
||||
}
|
||||
|
||||
ClientUser *ClientUser::get(unsigned int uiSession) {
|
||||
QReadLocker lock(&c_qrwlUsers);
|
||||
ClientUser *p = c_qmUsers.value(uiSession);
|
||||
@ -239,6 +242,13 @@ void ClientUser::setRecording(bool recording) {
|
||||
emit recordingStateChanged();
|
||||
}
|
||||
|
||||
void ClientUser::setLocalVolumeAdjustment(float adjustment) {
|
||||
float oldAdjustment = m_localVolume;
|
||||
m_localVolume = adjustment;
|
||||
|
||||
emit localVolumeAdjustmentsChanged(m_localVolume, oldAdjustment);
|
||||
}
|
||||
|
||||
bool ClientUser::lessThanOverlay(const ClientUser *first, const ClientUser *second) {
|
||||
if (g.s.os.osSort == OverlaySettings::LastStateChange) {
|
||||
// Talkers above non-talkers
|
||||
|
||||
@ -17,6 +17,10 @@ class ClientUser : public QObject, public User {
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(ClientUser)
|
||||
|
||||
protected:
|
||||
float m_localVolume = 1.0f;
|
||||
|
||||
public:
|
||||
Settings::TalkState tsState;
|
||||
Timer tLastTalkStateChange;
|
||||
@ -26,7 +30,6 @@ class ClientUser : public QObject, public User {
|
||||
|
||||
float fPowerMin, fPowerMax;
|
||||
float fAverageAvailable;
|
||||
float fLocalVolume;
|
||||
|
||||
int iFrames;
|
||||
int iSequence;
|
||||
@ -37,6 +40,8 @@ class ClientUser : public QObject, public User {
|
||||
QString getFlagsString() const;
|
||||
ClientUser(QObject *p = nullptr);
|
||||
|
||||
float getLocalVolumeAdjustments() const;
|
||||
|
||||
/**
|
||||
* Determines whether a user is active or not
|
||||
* A user is active when it is currently speaking or when the user has
|
||||
@ -74,11 +79,13 @@ class ClientUser : public QObject, public User {
|
||||
void setSelfDeaf(bool deaf);
|
||||
void setPrioritySpeaker(bool priority);
|
||||
void setRecording(bool recording);
|
||||
void setLocalVolumeAdjustment(float adjustment);
|
||||
signals:
|
||||
void talkingStateChanged();
|
||||
void muteDeafStateChanged();
|
||||
void prioritySpeakerStateChanged();
|
||||
void recordingStateChanged();
|
||||
void localVolumeAdjustmentsChanged(float newAdjustment, float oldAdjustment);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -189,6 +189,7 @@ void LookConfig::load(const Settings &r) {
|
||||
loadCheckBox(qcbHideTray, r.bHideInTray);
|
||||
loadCheckBox(qcbStateInTray, r.bStateInTray);
|
||||
loadCheckBox(qcbShowUserCount, r.bShowUserCount);
|
||||
loadCheckBox(qcbShowVolumeAdjustments, r.bShowVolumeAdjustments);
|
||||
loadCheckBox(qcbShowContextMenuInMenuBar, r.bShowContextMenuInMenuBar);
|
||||
loadCheckBox(qcbShowTransmitModeComboBox, r.bShowTransmitModeComboBox);
|
||||
loadCheckBox(qcbHighContrast, r.bHighContrast);
|
||||
@ -249,6 +250,7 @@ void LookConfig::save() const {
|
||||
s.bHideInTray = qcbHideTray->isChecked();
|
||||
s.bStateInTray = qcbStateInTray->isChecked();
|
||||
s.bShowUserCount = qcbShowUserCount->isChecked();
|
||||
s.bShowVolumeAdjustments = qcbShowVolumeAdjustments->isChecked();
|
||||
s.bShowContextMenuInMenuBar = qcbShowContextMenuInMenuBar->isChecked();
|
||||
s.bShowTransmitModeComboBox = qcbShowTransmitModeComboBox->isChecked();
|
||||
s.bHighContrast = qcbHighContrast->isChecked();
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>728</width>
|
||||
<height>1120</height>
|
||||
<height>1148</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -671,6 +671,33 @@
|
||||
<string>Channel Tree</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0">
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="qcbFilterHidesEmptyChannels">
|
||||
<property name="text">
|
||||
<string>Filter automatically hides empty channels</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="MUComboBox" name="qcbExpand">
|
||||
<property name="toolTip">
|
||||
<string>When to automatically expand channels</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This sets which channels to automatically expand. <i>None</i> and <i>All</i> will expand no or all channels, while <i>Only with users</i> will expand and collapse channels as users join and leave them.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="MUComboBox" name="qcbUserDrag">
|
||||
<property name="toolTip">
|
||||
<string>This changes the behavior when moving users.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This sets the behavior of user drags; it can be used to prevent accidental dragging. <i>Move</i> moves the user without prompting. <i>Do Nothing</i> does nothing and prints an error message. <i>Ask</i> uses a message box to confirm if you really wanted to move the user.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="qliUserDrag">
|
||||
<property name="text">
|
||||
@ -678,13 +705,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="qliExpand">
|
||||
<property name="text">
|
||||
<string>Expand</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="qliChannelDrag">
|
||||
<property name="text">
|
||||
@ -692,6 +712,20 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="qcbChatBarUseSelection">
|
||||
<property name="text">
|
||||
<string>Use selected item as the chat bar target</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="qliExpand">
|
||||
<property name="text">
|
||||
<string>Expand</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="qcbShowUserCount">
|
||||
<property name="toolTip">
|
||||
@ -726,36 +760,12 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="qcbChatBarUseSelection">
|
||||
<property name="text">
|
||||
<string>Use selected item as the chat bar target</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="MUComboBox" name="qcbExpand">
|
||||
<widget class="QCheckBox" name="qcbShowVolumeAdjustments">
|
||||
<property name="toolTip">
|
||||
<string>When to automatically expand channels</string>
|
||||
<string>Show the local volume adjustment for each user (if any).</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This sets which channels to automatically expand. <i>None</i> and <i>All</i> will expand no or all channels, while <i>Only with users</i> will expand and collapse channels as users join and leave them.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="qcbFilterHidesEmptyChannels">
|
||||
<property name="text">
|
||||
<string>Filter automatically hides empty channels</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="MUComboBox" name="qcbUserDrag">
|
||||
<property name="toolTip">
|
||||
<string>This changes the behavior when moving users.</string>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>This sets the behavior of user drags; it can be used to prevent accidental dragging. <i>Move</i> moves the user without prompting. <i>Do Nothing</i> does nothing and prints an error message. <i>Ask</i> uses a message box to confirm if you really wanted to move the user.</string>
|
||||
<string>Show volume adjustments</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
||||
@ -522,7 +522,7 @@ void MainWindow::msgUserState(const MumbleProto::UserState &msg) {
|
||||
pDst->setLocalIgnore(true);
|
||||
if (g.db->isLocalIgnoredTTS(pDst->qsHash))
|
||||
pDst->setLocalIgnoreTTS(true);
|
||||
pDst->fLocalVolume = g.db->getUserLocalVolume(pDst->qsHash);
|
||||
pDst->setLocalVolumeAdjustment(g.db->getUserLocalVolume(pDst->qsHash));
|
||||
}
|
||||
|
||||
if (msg.has_self_deaf() || msg.has_self_mute()) {
|
||||
|
||||
@ -362,6 +362,7 @@ Settings::Settings() {
|
||||
bStateInTray = true;
|
||||
bUsage = true;
|
||||
bShowUserCount = false;
|
||||
bShowVolumeAdjustments = true;
|
||||
bChatBarUseSelection = false;
|
||||
bFilterHidesEmptyChannels = true;
|
||||
bFilterActive = false;
|
||||
@ -1184,6 +1185,8 @@ void Settings::save() {
|
||||
SAVELOAD(bStateInTray, "ui/stateintray");
|
||||
SAVELOAD(bUsage, "ui/usage");
|
||||
SAVELOAD(bShowUserCount, "ui/showusercount");
|
||||
SAVELOAD(bShowVolumeAdjustments, "ui/showVolumeAdjustments");
|
||||
SAVELOAD(bShowVolumeAdjustments, "ui/showVolumeAdjustments");
|
||||
SAVELOAD(bChatBarUseSelection, "ui/chatbaruseselection");
|
||||
SAVELOAD(bFilterHidesEmptyChannels, "ui/filterhidesemptychannels");
|
||||
SAVELOAD(bFilterActive, "ui/filteractive");
|
||||
|
||||
@ -324,6 +324,7 @@ struct Settings {
|
||||
bool bStateInTray;
|
||||
bool bUsage;
|
||||
bool bShowUserCount;
|
||||
bool bShowVolumeAdjustments;
|
||||
bool bChatBarUseSelection;
|
||||
bool bFilterHidesEmptyChannels;
|
||||
bool bFilterActive;
|
||||
|
||||
@ -306,10 +306,7 @@ void TalkingUI::addUser(const ClientUser *user) {
|
||||
// We also verify whether the name for that user matches up (if it is contained in m_entries) in case
|
||||
// a user didn't get removed from the map but its ID got reused by a new client.
|
||||
|
||||
// Strip HTML from the displayed name
|
||||
const QString name = QTextDocumentFragment::fromHtml(m_entries[user->uiSession].name->text()).toPlainText();
|
||||
|
||||
nameMatches = name == user->qsName;
|
||||
nameMatches = m_entries[user->uiSession].qsName == user->qsName;
|
||||
|
||||
if (!nameMatches) {
|
||||
// Hide the stale user
|
||||
@ -342,11 +339,12 @@ void TalkingUI::addUser(const ClientUser *user) {
|
||||
background->setProperty("userName", user->qsName);
|
||||
|
||||
QLabel *name = new QLabel(background);
|
||||
const QString displayString = UserModel::createDisplayString(*user, false, nullptr);
|
||||
if (isSelf) {
|
||||
// Indicate self by bold name
|
||||
name->setText(QString::fromLatin1("<b>%1</b>").arg(user->qsName));
|
||||
name->setText(QString::fromLatin1("<b>%1</b>").arg(displayString));
|
||||
} else {
|
||||
name->setText(user->qsName);
|
||||
name->setText(displayString);
|
||||
}
|
||||
|
||||
QLabel *icon = new QLabel(background);
|
||||
@ -354,7 +352,7 @@ void TalkingUI::addUser(const ClientUser *user) {
|
||||
icon->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
||||
|
||||
// As a default use the passive state
|
||||
Entry entry = {icon, name, background, nullptr, user->uiSession, Settings::Passive};
|
||||
Entry entry = {icon, name, background, nullptr, user->uiSession, Settings::Passive, user->qsName};
|
||||
setTalkingIcon(entry);
|
||||
m_entries.insert(user->uiSession, entry);
|
||||
|
||||
@ -374,6 +372,8 @@ void TalkingUI::addUser(const ClientUser *user) {
|
||||
|
||||
m_timers.insert(user->uiSession, timer);
|
||||
|
||||
QObject::connect(user, &ClientUser::localVolumeAdjustmentsChanged, this, &TalkingUI::on_userLocalVolumeAdjustmentsChanged);
|
||||
|
||||
// If this user is currently selected, mark him/her as such
|
||||
if (g.mw && g.mw->pmModel && g.mw->pmModel->getSelectedUser() == user) {
|
||||
if (m_entries.contains(user->uiSession)) {
|
||||
@ -862,3 +862,12 @@ void TalkingUI::on_muteDeafStateChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void TalkingUI::on_userLocalVolumeAdjustmentsChanged(float, float) {
|
||||
ClientUser *user = qobject_cast<ClientUser *>(sender());
|
||||
|
||||
if (user && m_entries.contains(user->uiSession)) {
|
||||
Entry entry = m_entries[user->uiSession];
|
||||
|
||||
entry.name->setText(UserModel::createDisplayString(*user, false, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ struct Entry {
|
||||
QLabel *statusIcons;
|
||||
unsigned int userSession;
|
||||
Settings::TalkState talkingState;
|
||||
QString qsName;
|
||||
};
|
||||
|
||||
/// The talking UI is a widget that will display the users you are currently
|
||||
@ -142,6 +143,7 @@ class TalkingUI : public QWidget {
|
||||
void on_settingsChanged();
|
||||
void on_clientDisconnected(unsigned int userSession);
|
||||
void on_muteDeafStateChanged();
|
||||
void on_userLocalVolumeAdjustmentsChanged(float newAdjustment, float oldAdjustment);
|
||||
};
|
||||
|
||||
#endif // MUMBLE_MUMBLE_TALKINGUI_H_
|
||||
|
||||
@ -60,7 +60,7 @@ UserLocalVolumeDialog::UserLocalVolumeDialog(unsigned int sessionId,
|
||||
if (user) {
|
||||
QString title = tr("Adjusting local volume for %1").arg(user->qsName);
|
||||
setWindowTitle(title);
|
||||
qsUserLocalVolume->setValue(qRound(log2(user->fLocalVolume) * 6.0));
|
||||
qsUserLocalVolume->setValue(qRound(log2(user->getLocalVolumeAdjustments()) * 6.0));
|
||||
m_originalVolumeAdjustmentDecibel = qsUserLocalVolume->value();
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ void UserLocalVolumeDialog::on_qsUserLocalVolume_valueChanged(int value) {
|
||||
ClientUser *user = ClientUser::get(m_clientSession);
|
||||
if (user) {
|
||||
// Decibel formula: +6db = *2
|
||||
user->fLocalVolume = static_cast<float>(pow(2.0, qsUserLocalVolume->value() / 6.0));
|
||||
user->setLocalVolumeAdjustment(static_cast<float>(pow(2.0, qsUserLocalVolume->value() / 6.0)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ void UserLocalVolumeDialog::on_qbbUserLocalVolume_clicked(QAbstractButton *butto
|
||||
ClientUser *user = ClientUser::get(m_clientSession);
|
||||
if (user) {
|
||||
if (!user->qsHash.isEmpty()) {
|
||||
g.db->setUserLocalVolume(user->qsHash, user->fLocalVolume);
|
||||
g.db->setUserLocalVolume(user->qsHash, user->getLocalVolumeAdjustments());
|
||||
} else {
|
||||
g.mw->logChangeNotPermanent(QObject::tr("Local Volume Adjustment..."), user);
|
||||
}
|
||||
|
||||
@ -454,11 +454,12 @@ QVariant UserModel::data(const QModelIndex &idx, int role) const {
|
||||
break;
|
||||
case Qt::DisplayRole:
|
||||
if (idx.column() == 0) {
|
||||
if (! p->qsFriendName.isEmpty() && (p->qsFriendName.toLower() != p->qsName.toLower()))
|
||||
return QString::fromLatin1("%1 (%2)").arg(p->qsName).arg(p->qsFriendName);
|
||||
else
|
||||
return p->qsName;
|
||||
// Get the channel the user/listener is in
|
||||
const Channel *parentChannel = item->parent ? item->parent->cChan : nullptr;
|
||||
|
||||
return createDisplayString(*p, item->isListener, parentChannel);
|
||||
}
|
||||
|
||||
// Most of the following icons are for non-listeners (as listeners are merely proxies) only
|
||||
// but in order to not change the order of the icons, the condition is added to each case
|
||||
// individually instead of checking it up front.
|
||||
@ -1847,3 +1848,40 @@ void UserModel::updateOverlay() const {
|
||||
#endif
|
||||
g.lcd->updateUserView();
|
||||
}
|
||||
|
||||
|
||||
QString UserModel::createDisplayString(const ClientUser &user, bool isChannelListener, const Channel *parentChannel) {
|
||||
// Get the configured volume adjustment. Depending on whether
|
||||
// this display string is for a ChannelListener or a regular user, we have to fetch
|
||||
// the volume adjustment differently.
|
||||
float volumeAdjustment = isChannelListener && parentChannel
|
||||
? ChannelListener::getListenerLocalVolumeAdjustment(parentChannel->iId) : user.getLocalVolumeAdjustments();
|
||||
|
||||
// Transform the adjustment into dB
|
||||
// *2 == 6 dB
|
||||
int localVolumeDecibel = std::round(log2f(volumeAdjustment) * 6);
|
||||
|
||||
// Create a friend-tag
|
||||
QString friendTag;
|
||||
if (!user.qsFriendName.isEmpty() && user.qsName.compare(user.qsFriendName, Qt::CaseInsensitive) != 0) {
|
||||
friendTag = QString::fromLatin1("(%2)").arg(user.qsFriendName);
|
||||
}
|
||||
|
||||
// Create a tag that indicates the volume adjustments
|
||||
QString volumeTag;
|
||||
if (std::abs(localVolumeDecibel) > 0 && g.s.bShowVolumeAdjustments) {
|
||||
volumeTag = QString::asprintf("|%+d|", localVolumeDecibel);
|
||||
}
|
||||
|
||||
QString displayString = user.qsName;
|
||||
|
||||
if (!friendTag.isEmpty()) {
|
||||
displayString += " " + friendTag;
|
||||
}
|
||||
|
||||
if (!volumeTag.isEmpty()) {
|
||||
displayString += " " + volumeTag;
|
||||
}
|
||||
|
||||
return displayString;
|
||||
}
|
||||
|
||||
@ -186,6 +186,9 @@ class UserModel : public QAbstractItemModel {
|
||||
|
||||
unsigned int uiSessionComment;
|
||||
int iChannelDescription;
|
||||
|
||||
|
||||
static QString createDisplayString(const ClientUser &user, bool isChannelListener, const Channel *parentChannel);
|
||||
public slots:
|
||||
/// Invalidates the model data of the ClientUser triggering this slot.
|
||||
void userStateChanged();
|
||||
|
||||
@ -4267,6 +4267,14 @@ The setting only applies for new messages, the already shown ones will retain th
|
||||
<source>Silent user lifetime</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show the local volume adjustment for each user (if any).</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Show volume adjustments</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user