mirror of
https://github.com/mumble-voip/mumble.git
synced 2025-10-26 11:19:16 +00:00
FEAT(client): Indicate talking users when locally muted
Before this commit it is impossible to tell whether a client is talking or not once that client is locally muted. This commit changes this by introducing a new talking state "MutedTalking" for these special cases. In order for this talking state to come in existence, the audio code was altered so that audio packets are no longer dropped if the associated user is locally muted. Instead the audio frame will pass the normal processing chain until it comes to actually decoding it. For Opus packets only the sample count is read out without actually doing the work of decoding. For all other codecs the packet is decoded as usual. In a final step the sample buffer is filled/overwritten with zeros in order to mute the stream. With this commit in place, the UI will indicate a user that is talking but is muted with a special icon. Note that this is only the case in the modern themes (Light and Dark) but not for the Classic theme as I wasn't able to alter the talking icon accordingly. There is an altered version of the icon added with this commit but that one doesn't render properly within Qt (it does render everywhere else though). Making this feature work for the classic theme is a simple matter of getting the icon to work and then editing ClassicTheme.qrc which currently points to the same icon as for passive users for the MutedTalking state.
This commit is contained in:
parent
4afab7c67d
commit
706e2edd04
@ -359,7 +359,11 @@ bool AudioOutputSpeech::prepareSampleBuffer(unsigned int frameCount) {
|
||||
} else if (umtType == MessageHandler::UDPVoiceOpus) {
|
||||
#ifdef USE_OPUS
|
||||
if (oCodec) {
|
||||
decodedSamples = oCodec->opus_decode_float(opusState,
|
||||
if (qba.isEmpty() || !(p && p->bLocalMute)) {
|
||||
// If qba is empty, we have to let Opus know about the packet loss
|
||||
// Otherwise if the associated user is not locally muted, we want to decode the audio packet
|
||||
// normally in order to be able to play it.
|
||||
decodedSamples = oCodec->opus_decode_float(opusState,
|
||||
qba.isEmpty() ?
|
||||
nullptr :
|
||||
reinterpret_cast<const unsigned char *>(qba.constData()),
|
||||
@ -367,6 +371,17 @@ bool AudioOutputSpeech::prepareSampleBuffer(unsigned int frameCount) {
|
||||
pOut,
|
||||
iAudioBufferSize,
|
||||
0);
|
||||
} else {
|
||||
// If the packet is non-empty, but the associated user is locally muted,
|
||||
// we don't have to decode the packet. Instead it is enough to know how many
|
||||
// samples it contained so that we can then mute the appropriate output length
|
||||
decodedSamples = oCodec->opus_packet_get_samples_per_frame(
|
||||
reinterpret_cast<const unsigned char *>(qba.constData()),
|
||||
SAMPLE_RATE);
|
||||
}
|
||||
|
||||
// The returned sample count we get from the Opus functions refer to samples per channel.
|
||||
// Thus in order to get the total amount, we have to multiply by the channel count.
|
||||
decodedSamples *= channels;
|
||||
}
|
||||
|
||||
@ -460,6 +475,14 @@ bool AudioOutputSpeech::prepareSampleBuffer(unsigned int frameCount) {
|
||||
}
|
||||
}
|
||||
nextframe:
|
||||
if (p && p->bLocalMute) {
|
||||
// Overwrite the output with zeros as this user is muted
|
||||
// NOTE: If Opus is used, then in this case no samples have actually been decoded and thus
|
||||
// we don't discard previously done work (in form of decoding the audio stream) by overwriting
|
||||
// it with zeros.
|
||||
memset(pOut, 0, decodedSamples * sizeof(float));
|
||||
}
|
||||
|
||||
spx_uint32_t inlen = decodedSamples / channels; // per channel
|
||||
spx_uint32_t outlen = static_cast<unsigned int>(ceilf(static_cast<float>(decodedSamples / channels * iMixerFreq) / static_cast<float>(iSampleRate)));
|
||||
if (srs && bLastAlive) {
|
||||
@ -492,6 +515,11 @@ nextframe:
|
||||
ts = Settings::Whispering;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ts != Settings::Passive && p->bLocalMute) {
|
||||
ts = Settings::MutedTalking;
|
||||
}
|
||||
|
||||
p->setTalking(ts);
|
||||
}
|
||||
|
||||
|
||||
@ -613,6 +613,7 @@ void MainWindow::updateTrayIcon() {
|
||||
} else if (p && g.s.bStateInTray) {
|
||||
switch (p->tsState) {
|
||||
case Settings::Talking:
|
||||
case Settings::MutedTalking:
|
||||
qstiIcon->setIcon(qiTalkingOn);
|
||||
break;
|
||||
case Settings::Whispering:
|
||||
@ -2431,6 +2432,7 @@ void MainWindow::userStateChanged() {
|
||||
|
||||
break;
|
||||
case Settings::Passive:
|
||||
case Settings::MutedTalking:
|
||||
default:
|
||||
g.bAttenuateOthers = false;
|
||||
g.prioritySpeakerActiveOverride = false;
|
||||
|
||||
@ -77,6 +77,8 @@ OpusCodec::OpusCodec() {
|
||||
RESOLVE(opus_decoder_destroy);
|
||||
|
||||
RESOLVE(opus_decoder_get_nb_samples);
|
||||
|
||||
RESOLVE(opus_packet_get_samples_per_frame);
|
||||
}
|
||||
|
||||
OpusCodec::~OpusCodec() {
|
||||
|
||||
@ -41,6 +41,8 @@ class OpusCodec {
|
||||
int (__cdecl *opus_decode_float)(OpusDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec);
|
||||
|
||||
int (__cdecl *opus_decoder_get_nb_samples)(OpusDecoder *st, const unsigned char packet[], opus_int32 len);
|
||||
|
||||
int (__cdecl *opus_packet_get_samples_per_frame)(const unsigned char *data, opus_int32 Fs);
|
||||
};
|
||||
|
||||
#endif // OPUSCODEC_H_
|
||||
|
||||
@ -106,6 +106,9 @@ void OverlayEditorScene::updateUserName() {
|
||||
case Settings::Talking:
|
||||
qsName = Overlay::tr("Talking");
|
||||
break;
|
||||
case Settings::MutedTalking:
|
||||
qsName = QObject::tr("Talking (muted)");
|
||||
break;
|
||||
case Settings::Whispering:
|
||||
qsName = Overlay::tr("Whisper");
|
||||
break;
|
||||
|
||||
@ -134,6 +134,9 @@ void OverlayUser::updateLayout() {
|
||||
case Settings::Talking:
|
||||
qsName = Overlay::tr("Talking");
|
||||
break;
|
||||
case Settings::MutedTalking:
|
||||
qsName = QObject::tr("Talking (muted)");
|
||||
break;
|
||||
case Settings::Whispering:
|
||||
qsName = Overlay::tr("Whisper");
|
||||
break;
|
||||
|
||||
@ -264,6 +264,7 @@ void OverlayUserGroup::updateUsers() {
|
||||
if (qlExampleUsers.isEmpty()) {
|
||||
qlExampleUsers << new OverlayUser(Settings::Passive, uiHeight, os);
|
||||
qlExampleUsers << new OverlayUser(Settings::Talking, uiHeight, os);
|
||||
qlExampleUsers << new OverlayUser(Settings::MutedTalking, uiHeight, os);
|
||||
qlExampleUsers << new OverlayUser(Settings::Whispering, uiHeight, os);
|
||||
qlExampleUsers << new OverlayUser(Settings::Shouting, uiHeight, os);
|
||||
}
|
||||
|
||||
@ -241,7 +241,7 @@ void ServerHandler::handleVoicePacket(unsigned int msgFlags, PacketDataStream &p
|
||||
pds >> uiSession;
|
||||
ClientUser *p = ClientUser::get(uiSession);
|
||||
AudioOutputPtr ao = g.ao;
|
||||
if (ao && p && ! p->bLocalMute && !(((msgFlags & 0x1f) == 2) && g.s.bWhisperFriends && p->qsFriendName.isEmpty())) {
|
||||
if (ao && p && !(((msgFlags & 0x1f) == 2) && g.s.bWhisperFriends && p->qsFriendName.isEmpty())) {
|
||||
unsigned int iSeq;
|
||||
pds >> iSeq;
|
||||
QByteArray qba;
|
||||
|
||||
@ -168,6 +168,7 @@ OverlaySettings::OverlaySettings() {
|
||||
osSort = Alphabetical;
|
||||
|
||||
qcUserName[Settings::Passive] = QColor(170, 170, 170);
|
||||
qcUserName[Settings::MutedTalking] = QColor(170, 170, 170);
|
||||
qcUserName[Settings::Talking] = QColor(255, 255, 255);
|
||||
qcUserName[Settings::Whispering] = QColor(128, 255, 128);
|
||||
qcUserName[Settings::Shouting] = QColor(255, 128, 255);
|
||||
@ -206,6 +207,7 @@ void OverlaySettings::setPreset(const OverlayPresets preset) {
|
||||
qfChannel = qfUserName;
|
||||
|
||||
fUser[Settings::Passive] = 0.5f;
|
||||
fUser[Settings::MutedTalking] = 0.5f;
|
||||
fUser[Settings::Talking] = (7.0f / 8.0f);
|
||||
fUser[Settings::Whispering] = (7.0f / 8.0f);
|
||||
fUser[Settings::Shouting] = (7.0f / 8.0f);
|
||||
@ -245,6 +247,7 @@ void OverlaySettings::setPreset(const OverlayPresets preset) {
|
||||
qfChannel = qfUserName;
|
||||
|
||||
fUser[Settings::Passive] = 0.5f;
|
||||
fUser[Settings::MutedTalking] = 0.5f;
|
||||
fUser[Settings::Talking] = (7.0f / 8.0f);
|
||||
fUser[Settings::Whispering] = (7.0f / 8.0f);
|
||||
fUser[Settings::Shouting] = (7.0f / 8.0f);
|
||||
|
||||
@ -81,7 +81,7 @@ struct OverlaySettings {
|
||||
qreal fZoom;
|
||||
unsigned int uiColumns;
|
||||
|
||||
QColor qcUserName[4];
|
||||
QColor qcUserName[5];
|
||||
QFont qfUserName;
|
||||
|
||||
QColor qcChannel;
|
||||
@ -107,7 +107,7 @@ struct OverlaySettings {
|
||||
qreal fChannel;
|
||||
qreal fMutedDeafened;
|
||||
qreal fAvatar;
|
||||
qreal fUser[4];
|
||||
qreal fUser[5];
|
||||
qreal fFps;
|
||||
|
||||
QRectF qrfUserName;
|
||||
@ -148,7 +148,7 @@ struct Settings {
|
||||
enum ChannelExpand { NoChannels, ChannelsWithUsers, AllChannels };
|
||||
enum ChannelDrag { Ask, DoNothing, Move };
|
||||
enum ServerShow { ShowPopulated, ShowReachable, ShowAll };
|
||||
enum TalkState { Passive, Talking, Whispering, Shouting };
|
||||
enum TalkState { Passive, Talking, Whispering, Shouting, MutedTalking };
|
||||
enum IdleAction { Nothing, Deafen, Mute };
|
||||
typedef QPair<QList<QSslCertificate>, QSslKey> KeyPair;
|
||||
|
||||
|
||||
@ -36,6 +36,7 @@ TalkingUI::TalkingUI(QWidget *parent)
|
||||
m_timers(),
|
||||
m_currentSelection(nullptr),
|
||||
m_talkingIcon(QIcon(QLatin1String("skin:talking_on.svg"))),
|
||||
m_mutedTalkingIcon(QIcon(QLatin1String("skin:talking_muted.svg"))),
|
||||
m_passiveIcon(QIcon(QLatin1String("skin:talking_off.svg"))),
|
||||
m_shoutingIcon(QIcon(QLatin1String("skin:talking_alt.svg"))),
|
||||
m_whisperingIcon(QIcon(QLatin1String("skin:talking_whisper.svg"))) {
|
||||
@ -90,6 +91,9 @@ void TalkingUI::setFontSize(QWidget *widget) {
|
||||
void TalkingUI::setIcon(Entry &entry) const {
|
||||
const QIcon *icon = nullptr;
|
||||
switch (entry.talkingState) {
|
||||
case Settings::MutedTalking:
|
||||
icon = &m_mutedTalkingIcon;
|
||||
break;
|
||||
case Settings::Talking:
|
||||
icon = &m_talkingIcon;
|
||||
break;
|
||||
|
||||
@ -50,6 +50,8 @@ class TalkingUI : public QWidget {
|
||||
|
||||
/// The icon for a talking user
|
||||
QIcon m_talkingIcon;
|
||||
/// The icon for a talking user that is currently (locally) muted
|
||||
QIcon m_mutedTalkingIcon;
|
||||
/// The icon for a silent user
|
||||
QIcon m_passiveIcon;
|
||||
/// The icon for a shouting user
|
||||
|
||||
@ -237,6 +237,7 @@ QString ModelItem::hash() const {
|
||||
UserModel::UserModel(QObject *p) : QAbstractItemModel(p) {
|
||||
qiTalkingOff=QIcon(QLatin1String("skin:talking_off.svg"));
|
||||
qiTalkingOn=QIcon(QLatin1String("skin:talking_on.svg"));
|
||||
qiTalkingMuted=QIcon(QLatin1String("skin:talking_muted.svg"));
|
||||
qiTalkingShout=QIcon(QLatin1String("skin:talking_alt.svg"));
|
||||
qiTalkingWhisper=QIcon(QLatin1String("skin:talking_whisper.svg"));
|
||||
qiPrioritySpeaker=QIcon(QLatin1String("skin:priority_speaker.svg"));
|
||||
@ -425,6 +426,8 @@ QVariant UserModel::data(const QModelIndex &idx, int role) const {
|
||||
switch (p->tsState) {
|
||||
case Settings::Talking:
|
||||
return qiTalkingOn;
|
||||
case Settings::MutedTalking:
|
||||
return qiTalkingMuted;
|
||||
case Settings::Whispering:
|
||||
return qiTalkingWhisper;
|
||||
case Settings::Shouting:
|
||||
|
||||
@ -67,7 +67,7 @@ class UserModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(UserModel)
|
||||
protected:
|
||||
QIcon qiTalkingOn, qiTalkingWhisper, qiTalkingShout, qiTalkingOff;
|
||||
QIcon qiTalkingOn, qiTalkingMuted, qiTalkingWhisper, qiTalkingShout, qiTalkingOff;
|
||||
QIcon qiMutedPushToMute, qiMutedSelf, qiMutedServer, qiMutedLocal, qiIgnoredLocal, qiMutedSuppressed;
|
||||
QIcon qiPrioritySpeaker;
|
||||
QIcon qiRecording;
|
||||
|
||||
105
themes/Classic/talking_muted.svg
Normal file
105
themes/Classic/talking_muted.svg
Normal file
@ -0,0 +1,105 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.0" height="300" width="300">
|
||||
<defs>
|
||||
<filter x="0" y="0" width="1" height="1" id="e" color-interpolation-filters="sRGB">
|
||||
<feGaussianBlur result="result1" in="SourceAlpha" stdDeviation="5"/>
|
||||
<feComposite in2="result1" result="result3" k4="-2" k1="-1" k2="3.2" operator="arithmetic"/>
|
||||
<feColorMatrix result="result2" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 10 0"/>
|
||||
<feComposite in2="result2" operator="out" in="SourceGraphic" result="fbSourceGraphic"/>
|
||||
<feGaussianBlur result="result4" stdDeviation=".01" in="fbSourceGraphic"/>
|
||||
<feComposite result="result5" in2="result4" in="fbSourceGraphic" operator="out"/>
|
||||
<feBlend result="result6" in2="result5" in="result1" mode="multiply"/>
|
||||
<feBlend result="fbSourceGraphic" in2="result6" in="result4" mode="screen"/>
|
||||
<feColorMatrix values="0 0 0 -1 0 0 0 0 -1 0 0 0 0 -1 0 0 0 0 1 0" in="fbSourceGraphic" result="fbSourceGraphicAlpha"/>
|
||||
<feColorMatrix in="fbSourceGraphic" result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="f" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="g" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="h" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="i" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="j" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="k" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="l" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="n" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="o" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="p" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="r" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="t" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="v" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="x" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="z" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="B" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<filter id="D" x="0" y="0" width="1" height="1" color-interpolation-filters="sRGB">
|
||||
<feColorMatrix result="colormatrix1" values="0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0.299 0.587 0.114 0 0 0 0 0 1 0"/>
|
||||
</filter>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .26721 0 176.356)" r="44.733" fy="253.965" fx="77.72" cy="253.965" cx="77.72" id="C" xlink:href="#b"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .26721 -.377 175.793)" r="44.733" fy="268.725" fx="79.442" cy="268.725" cx="79.442" id="w" xlink:href="#b"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .25472 0 179.363)" r="43.983" fy="240.664" fx="77.178" cy="240.664" cx="77.178" id="y" xlink:href="#c"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .26721 0 176.356)" r="44.733" fy="240.664" fx="77.178" cy="240.664" cx="77.178" id="A" xlink:href="#c"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .26721 0 176.356)" r="44.733" fy="240.664" fx="77.178" cy="240.664" cx="77.178" id="u" xlink:href="#c"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(1 0 0 .26721 0 176.356)" r="44.733" fy="240.664" fx="77.178" cy="240.664" cx="77.178" id="s" xlink:href="#c"/>
|
||||
<radialGradient gradientUnits="userSpaceOnUse" gradientTransform="matrix(.8489 0 0 .26721 10.645 184.451)" r="44.733" fy="206.04" fx="77.915" cy="206.04" cx="77.915" id="m" xlink:href="#d"/>
|
||||
<linearGradient id="d">
|
||||
<stop offset="0"/>
|
||||
<stop offset="1" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="a">
|
||||
<stop offset="0" stop-color="#be0000"/>
|
||||
<stop offset="1" stop-color="#ff0f0f" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="b">
|
||||
<stop offset="0" stop-color="#501616"/>
|
||||
<stop offset="1" stop-color="#501616" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="c">
|
||||
<stop offset="0" stop-color="#df9393"/>
|
||||
<stop offset="1" stop-color="#e29898" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
<linearGradient gradientTransform="translate(-22.223 53.74)" gradientUnits="userSpaceOnUse" y2="86.509" x2="31.19" y1="93.563" x1="311.182" id="q" xlink:href="#a"/>
|
||||
</defs>
|
||||
<g transform="translate(-22.223 53.74)">
|
||||
<path d="M240.664 129.876a103.942 44.813 0 01-103.942 44.813A103.942 44.813 0 0132.78 129.876a103.942 44.813 0 01103.942-44.814 103.942 44.813 0 01103.942 44.814z" transform="matrix(1.28772 0 0 1.33616 -1.335 -85.475)" fill="#1a1a1a" fill-rule="evenodd" filter="url(#e)"/>
|
||||
<path d="M123.237 107.365a55.81 9.025 0 01-55.81 9.025 55.81 9.025 0 01-55.809-9.025 55.81 9.025 0 0155.81-9.025 55.81 9.025 0 0155.809 9.025z" transform="matrix(1.84628 0 0 3.24528 46.812 -286.96)" fill="#fff" fill-rule="evenodd" filter="url(#f)"/>
|
||||
<rect transform="matrix(.9996 -.02815 .02172 .99976 0 0)" ry="4.515" y="55.128" x="168.934" height="40.286" width="39.043" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.402" filter="url(#g)"/>
|
||||
<rect transform="matrix(-.9996 -.02815 -.02172 .99976 0 0)" ry="4.515" y="46.002" x="-173.608" height="40.286" width="39.043" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.402" filter="url(#h)"/>
|
||||
<rect transform="matrix(.99556 -.09412 .06318 .998 0 0)" ry="4.473" y="67.94" x="203.64" height="39.915" width="31.143" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.246" filter="url(#i)"/>
|
||||
<rect transform="matrix(.96591 -.25886 .15165 .98843 0 0)" ry="4.45" y="102.48" x="227.082" height="39.71" width="27.979" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.178" filter="url(#j)"/>
|
||||
<rect transform="matrix(-.99556 -.09412 -.06318 .998 0 0)" ry="4.473" y="34.902" x="-138.396" height="39.915" width="31.143" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.246" filter="url(#k)"/>
|
||||
<rect transform="matrix(-.96591 -.25886 -.15165 .98843 0 0)" ry="4.45" y="13.94" x="-113.004" height="39.71" width="27.979" opacity=".6" fill="#ffe6d5" fill-rule="evenodd" stroke="#916f6f" stroke-width="1.178" filter="url(#l)"/>
|
||||
<path d="M121.162 240.664a43.983 11.203 0 01-43.984 11.203 43.983 11.203 0 01-43.983-11.203 43.983 11.203 0 0143.983-11.203 43.983 11.203 0 0143.984 11.203z" transform="matrix(2.65343 0 0 1.07637 -29.66 -201.164)" opacity=".76" fill="url(#m)" fill-rule="evenodd" filter="url(#n)"/>
|
||||
<path d="M72.328 26.623c-28.332 15.884-42.23 46.881-42.23 60.23 0 24.809 20.688 46.749 45.446 71.546 17.728 17.755 30.813 23.287 59.452 27.766 14.695 2.299 52.035 1.913 62.7.94 39.64-3.581 51.274-8.702 69.197-24.773 25.868-23.197 48.346-49.985 48.346-75.48.444-4.776-10.545-43.139-43.146-59.067C247.686 15.861 213.86.277 196.552 2.671c-20.71 2.865-28.507 2.606-48.985-.234C127.366.309 90.606 16.044 72.328 26.623zM192.141 58.54c7.21-1.088 16.275-3.706 30.448-2.745 1.059.072 26.823 3.835 36.465 7.094 33.602 11.356 44.575 19.482 44.17 21.439 0 10.443-10.152 14.195-32.24 25.611-8.899 4.6-25.752 14.169-44.244 13.898-20.938-.306-41.19 2.959-39.796 3.744-14.53 2.795-13.154 3.194-25.408.379-5.35-1.997-18.921-5.294-45.873-3.689-10.761.64-22.42-4.344-40.477-16.32C52.43 92.86 45.456 96.83 45.456 86.667c0-5.468 12.34-15.425 37.743-23.012 6.451-1.927 33.629-7.44 33.869-7.077 14.97-1.803 27.65 1.132 35.166 1.91 15.42 2.359 24.957 2.146 39.907.053z" fill="#ff3737" fill-rule="evenodd" filter="url(#o)"/>
|
||||
<path d="M129.308 82.987l13.394-6.982" filter="url(#p)"/>
|
||||
</g>
|
||||
<path d="M49.86 80.56C21.529 96.443 7.63 127.44 7.63 140.79c0 24.809 20.689 46.748 45.447 71.545 17.728 17.756 30.812 23.288 59.451 27.767 14.695 2.299 52.036 1.913 62.7.94 39.641-3.581 51.274-8.702 69.197-24.774 25.869-23.196 48.346-49.985 48.346-75.478.444-4.777-10.545-43.14-43.146-59.068-24.406-11.924-58.231-27.508-75.54-25.114-20.711 2.865-28.507 2.606-48.985-.234-20.202-2.128-56.962 13.606-75.24 24.185zm119.814 31.917c7.209-1.088 16.274-3.706 30.448-2.744 1.058.071 26.822 3.835 36.465 7.094 33.601 11.356 44.575 19.482 44.17 21.438 0 10.444-10.152 14.196-32.24 25.612-8.9 4.6-25.752 14.169-44.245 13.898-20.938-.306-41.19 2.958-39.796 3.744-14.53 2.794-13.153 3.194-25.407.378-5.35-1.996-18.922-5.293-45.874-3.688-10.76.64-22.42-4.344-40.476-16.32-22.758-15.093-29.731-11.122-29.731-21.285 0-5.468 12.341-15.426 37.744-23.013 6.45-1.926 33.629-7.439 33.869-7.076 14.969-1.803 27.65 1.132 35.166 1.91 15.42 2.358 24.957 2.146 39.907.052z" opacity=".85" fill="url(#q)" fill-rule="evenodd" filter="url(#r)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
@ -44,6 +44,7 @@
|
||||
<file alias="talking_alt.svg">Classic/talking_alt.svg</file>
|
||||
<file alias="talking_off.svg">Classic/talking_off.svg</file>
|
||||
<file alias="talking_on.svg">Classic/talking_on.svg</file>
|
||||
<file alias="talking_muted.svg">Classic/talking_off.svg</file>
|
||||
<file alias="talking_whisper.svg">Classic/talking_whisper.svg</file>
|
||||
<file alias="theme.ini">Classic/theme.ini</file>
|
||||
<file alias="actions/audio-input-microphone-muted.svg">Classic/tango/actions/audio-input-microphone-muted.svg</file>
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit 9467fe87e7d56a5e1506b8285b6a630e6b46298b
|
||||
Subproject commit 0e9dd7fc95d28ca1b0c1d01c7bf13b02806afb53
|
||||
@ -50,6 +50,7 @@
|
||||
<file alias="talking_alt.svg">Mumble/talking_alt.svg</file>
|
||||
<file alias="talking_off.svg">Mumble/talking_off.svg</file>
|
||||
<file alias="talking_on.svg">Mumble/talking_on.svg</file>
|
||||
<file alias="talking_muted.svg">Mumble/talking_muted.svg</file>
|
||||
<file alias="talking_whisper.svg">Mumble/talking_whisper.svg</file>
|
||||
<file alias="theme.ini">Mumble/theme.ini</file>
|
||||
<file alias="actions/audio-input-microphone-muted.svg">Mumble/actions/audio-input-microphone-muted.svg</file>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user