diff --git a/src/mumble/AudioConfigDialog.cpp b/src/mumble/AudioConfigDialog.cpp index 06169a33a..e934bead1 100644 --- a/src/mumble/AudioConfigDialog.cpp +++ b/src/mumble/AudioConfigDialog.cpp @@ -454,6 +454,13 @@ void AudioOutputDialog::load(const Settings &r) { loadCheckBox(qcbAttenuateOthersOnTalk, r.bAttenuateOthersOnTalk); loadCheckBox(qcbAttenuateOthers, r.bAttenuateOthers); loadCheckBox(qcbAttenuateUsersOnPrioritySpeak, r.bAttenuateUsersOnPrioritySpeak); + loadCheckBox(qcbOnlyAttenuateSameOutput, r.bOnlyAttenuateSameOutput); + loadCheckBox(qcbAttenuateLoopbacks, r.bAttenuateLoopbacks); + if (AudioOutputRegistrar::current == QLatin1String("PulseAudio")) { + qgbAdvancedAttenuation->setVisible(true); + } else { + qgbAdvancedAttenuation->setVisible(false); + } loadSlider(qsJitter, r.iJitterBufferSize); loadComboBox(qcbLoopback, r.lmLoopMode); loadSlider(qsPacketDelay, static_cast(r.dMaxPacketDelay)); @@ -467,6 +474,7 @@ void AudioOutputDialog::load(const Settings &r) { qsOtherVolume->setEnabled(r.bAttenuateOthersOnTalk || r.bAttenuateOthers); qlOtherVolume->setEnabled(r.bAttenuateOthersOnTalk || r.bAttenuateOthers); + qcbAttenuateLoopbacks->setEnabled(r.bOnlyAttenuateSameOutput); } void AudioOutputDialog::save() const { @@ -475,6 +483,8 @@ void AudioOutputDialog::save() const { s.fOtherVolume = 1.0f - (static_cast(qsOtherVolume->value()) / 100.0f); s.bAttenuateOthersOnTalk = qcbAttenuateOthersOnTalk->isChecked(); s.bAttenuateOthers = qcbAttenuateOthers->isChecked(); + s.bOnlyAttenuateSameOutput = qcbOnlyAttenuateSameOutput->isChecked(); + s.bAttenuateLoopbacks = qcbAttenuateLoopbacks->isChecked(); s.bAttenuateUsersOnPrioritySpeak = qcbAttenuateUsersOnPrioritySpeak->isChecked(); s.iJitterBufferSize = qsJitter->value(); s.qsAudioOutput = qcbSystem->currentText(); @@ -522,6 +532,11 @@ void AudioOutputDialog::on_qcbSystem_currentIndexChanged(int) { bool canmute = aor->canMuteOthers(); qsOtherVolume->setEnabled(canmute); qcbAttenuateOthersOnTalk->setEnabled(canmute); + if (aor->name == QLatin1String("PulseAudio")) { + qgbAdvancedAttenuation->setVisible(true); + } else { + qgbAdvancedAttenuation->setVisible(false); + } qcbAttenuateOthers->setEnabled(canmute); qlOtherVolume->setEnabled(canmute); @@ -605,10 +620,16 @@ void AudioOutputDialog::on_qcbAttenuateOthersOnTalk_clicked(bool checked) { bool b = qcbAttenuateOthers->isChecked() || checked; qsOtherVolume->setEnabled(b); qlOtherVolume->setEnabled(b); + qgbAdvancedAttenuation->setEnabled(b); } void AudioOutputDialog::on_qcbAttenuateOthers_clicked(bool checked) { bool b = qcbAttenuateOthersOnTalk->isChecked() || checked; qsOtherVolume->setEnabled(b); qlOtherVolume->setEnabled(b); + qgbAdvancedAttenuation->setEnabled(b); +} + +void AudioOutputDialog::on_qcbOnlyAttenuateSameOutput_clicked(bool checked) { + qcbAttenuateLoopbacks->setEnabled(checked); } diff --git a/src/mumble/AudioConfigDialog.h b/src/mumble/AudioConfigDialog.h index a01fde395..a41615cd8 100644 --- a/src/mumble/AudioConfigDialog.h +++ b/src/mumble/AudioConfigDialog.h @@ -103,6 +103,7 @@ class AudioOutputDialog : public ConfigWidget, public Ui::AudioOutput { void on_qcbPositional_stateChanged(int); void on_qcbAttenuateOthersOnTalk_clicked(bool checked); void on_qcbAttenuateOthers_clicked(bool checked); + void on_qcbOnlyAttenuateSameOutput_clicked(bool checked); }; #endif diff --git a/src/mumble/AudioOutput.ui b/src/mumble/AudioOutput.ui index 6cb71f9f1..4491c5023 100644 --- a/src/mumble/AudioOutput.ui +++ b/src/mumble/AudioOutput.ui @@ -333,6 +333,44 @@ + + + + + + + Advanced Attenuation Options + + + + + + If checked, Mumble will only attenuate applications that are using the same output source as Mumble + + + <b>Attenuate only applications using the same output as Mumble</b><br />If checked, applications that use a different output than Mumble will not be attenuated. + + + Only attenuate applications using the same output device + + + + + + + If checked, PulseAudio loopback modules will be attenuated + + + <b>Attenuate PulseAudio loopback modules</b><br />If loopback modules are linked to Mumble's output device/sink, they will also be attenuated. + + + Attenuate PulseAudio loopback modules + + + + + + diff --git a/src/mumble/PulseAudio.cpp b/src/mumble/PulseAudio.cpp index 7eea13483..bb0c8c888 100644 --- a/src/mumble/PulseAudio.cpp +++ b/src/mumble/PulseAudio.cpp @@ -106,6 +106,7 @@ PulseAudioSystem::PulseAudioSystem() { bAttenuating = false; iRemainingOperations = 0; bPulseIsGood = false; + iSinkId = -1; pam = pa_threaded_mainloop_new(); pa_mainloop_api *api = pa_threaded_mainloop_get_api(pam); @@ -229,6 +230,7 @@ void PulseAudioSystem::eventCallback(pa_mainloop_api *api, pa_defer_event *) { if (do_stop) { qWarning("PulseAudio: Stopping output"); pa_stream_disconnect(pasOutput); + iSinkId = -1; } else if (do_start) { qWarning("PulseAudio: Starting output: %s", qPrintable(odev)); pa_buffer_attr buff; @@ -245,6 +247,7 @@ void PulseAudioSystem::eventCallback(pa_mainloop_api *api, pa_defer_event *) { qsOutputCache = odev; pa_stream_connect_playback(pasOutput, qPrintable(odev), &buff, PA_STREAM_ADJUST_LATENCY, NULL, NULL); + pa_context_get_sink_info_by_name(pacContext, qPrintable(odev), sink_info_callback, this); } } @@ -438,6 +441,16 @@ void PulseAudioSystem::server_callback(pa_context *, const pa_server_info *i, vo pas->wakeup(); } +void PulseAudioSystem::sink_info_callback(pa_context *, const pa_sink_info *i, int eol, void *userdata) { + PulseAudioSystem *pas = reinterpret_cast(userdata); + if (!i || eol) { + return; + } + + pas->iSinkId = i->index; +} + + void PulseAudioSystem::stream_callback(pa_stream *s, void *userdata) { PulseAudioSystem *pas = reinterpret_cast(userdata); switch (pa_stream_get_state(s)) { @@ -614,6 +627,19 @@ void PulseAudioSystem::volume_sink_input_list_callback(pa_context *c, const pa_s PulseAudioSystem *pas = reinterpret_cast(userdata); if (eol == 0) { + // If we're using the default of "enable attenuation on all ouputs" and output from an application is loopbacked, + // both the loopback and the application will be attenuated leading to double attenuation. + if (!g.s.bOnlyAttenuateSameOutput && pas->iSinkId > -1 && !strcmp(i->driver, "module-loopback.c")) { + return; + } + // If we're not attenuating different sinks and the input is not on this sink, don't attenuate. Or, + // if the input is a loopback module and connected to Mumble's sink, also ignore it (loopbacks are used to connect + // sinks). An attenuated loopback means an indirect application attenuation. + if (g.s.bOnlyAttenuateSameOutput && pas->iSinkId > -1) { + if (int(i->sink) != pas->iSinkId || (int(i->sink) == pas->iSinkId && !strcmp(i->driver, "module-loopback.c") && !g.s.bAttenuateLoopbacks)) { + return; + } + } // ensure we're not attenuating ourselves! if (strcmp(i->name, mumble_sink_input) != 0) { // create a new entry diff --git a/src/mumble/PulseAudio.h b/src/mumble/PulseAudio.h index 65823bf18..1652ad500 100644 --- a/src/mumble/PulseAudio.h +++ b/src/mumble/PulseAudio.h @@ -74,6 +74,7 @@ class PulseAudioSystem : public QObject { bool bAttenuating; int iRemainingOperations; + int iSinkId; QHash qhVolumes; QList qlMatchedSinks; QHash qhUnmatchedSinks; @@ -85,6 +86,7 @@ class PulseAudioSystem : public QObject { static void sink_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata); static void source_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata); static void server_callback(pa_context *c, const pa_server_info *i, void *userdata); + static void sink_info_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata); static void stream_callback(pa_stream *s, void *userdata); static void read_callback(pa_stream *s, size_t bytes, void *userdata); static void write_callback(pa_stream *s, size_t bytes, void *userdata); diff --git a/src/mumble/Settings.cpp b/src/mumble/Settings.cpp index 0c3a6e7cc..b0a17870d 100644 --- a/src/mumble/Settings.cpp +++ b/src/mumble/Settings.cpp @@ -256,6 +256,8 @@ Settings::Settings() { bAttenuateOthersOnTalk = false; bAttenuateOthers = true; bAttenuateUsersOnPrioritySpeak = false; + bOnlyAttenuateSameOutput = false; + bAttenuateLoopbacks = false; iMinLoudness = 1000; iVoiceHold = 50; iJitterBufferSize = 1; @@ -570,6 +572,8 @@ void Settings::load(QSettings* settings_ptr) { SAVELOAD(bAttenuateOthers, "audio/attenuateothers"); SAVELOAD(bAttenuateOthersOnTalk, "audio/attenuateothersontalk"); SAVELOAD(bAttenuateUsersOnPrioritySpeak, "audio/attenuateusersonpriorityspeak"); + SAVELOAD(bOnlyAttenuateSameOutput, "audio/onlyattenuatesameoutput"); + SAVELOAD(bAttenuateLoopbacks, "audio/attenuateloopbacks"); LOADENUM(vsVAD, "audio/vadsource"); SAVELOAD(fVADmin, "audio/vadmin"); SAVELOAD(fVADmax, "audio/vadmax"); @@ -870,6 +874,8 @@ void Settings::save() { SAVELOAD(bAttenuateOthers, "audio/attenuateothers"); SAVELOAD(bAttenuateOthersOnTalk, "audio/attenuateothersontalk"); SAVELOAD(bAttenuateUsersOnPrioritySpeak, "audio/attenuateusersonpriorityspeak"); + SAVELOAD(bOnlyAttenuateSameOutput, "audio/onlyattenuatesameoutput"); + SAVELOAD(bAttenuateLoopbacks, "audio/attenuateloopbacks"); SAVELOAD(vsVAD, "audio/vadsource"); SAVELOAD(fVADmin, "audio/vadmin"); SAVELOAD(fVADmax, "audio/vadmax"); diff --git a/src/mumble/Settings.h b/src/mumble/Settings.h index 2a7ab5c92..ad9246114 100644 --- a/src/mumble/Settings.h +++ b/src/mumble/Settings.h @@ -204,6 +204,8 @@ struct Settings { bool bAttenuateOthersOnTalk; bool bAttenuateOthers; bool bAttenuateUsersOnPrioritySpeak; + bool bOnlyAttenuateSameOutput; + bool bAttenuateLoopbacks; int iOutputDelay; QString qsALSAInput, qsALSAOutput;