| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972 |
- #include "VolumeControl.hpp"
- #include <components/MuteCheckBox.hpp>
- #include <components/VolumeMeter.hpp>
- #include <components/VolumeName.hpp>
- #include <components/VolumeSlider.hpp>
- #include <dialogs/NameDialog.hpp>
- #include <widgets/OBSBasic.hpp>
- #include <QMessageBox>
- #include <QObjectCleanupHandler>
- #include "moc_VolumeControl.cpp"
- namespace {
- bool isSourceUnassigned(obs_source_t *source)
- {
- uint32_t mixes = (obs_source_get_audio_mixers(source) & ((1 << MAX_AUDIO_MIXES) - 1));
- obs_monitoring_type mt = obs_source_get_monitoring_type(source);
- return mixes == 0 && mt != OBS_MONITORING_TYPE_MONITOR_ONLY;
- }
- void showUnassignedWarning(const char *name)
- {
- auto msgBox = [=]() {
- QMessageBox msgbox(App()->GetMainWindow());
- msgbox.setWindowTitle(QTStr("VolControl.UnassignedWarning.Title"));
- msgbox.setText(QTStr("VolControl.UnassignedWarning.Text").arg(name));
- msgbox.setIcon(QMessageBox::Icon::Information);
- msgbox.addButton(QMessageBox::Ok);
- QCheckBox *cb = new QCheckBox(QTStr("DoNotShowAgain"));
- msgbox.setCheckBox(cb);
- msgbox.exec();
- if (cb->isChecked()) {
- config_set_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources", true);
- config_save_safe(App()->GetUserConfig(), "tmp", nullptr);
- }
- };
- QMetaObject::invokeMethod(App(), "Exec", Qt::QueuedConnection, Q_ARG(VoidFunc, msgBox));
- }
- } // namespace
- VolumeControl::VolumeControl(obs_source_t *source, QWidget *parent, bool vertical)
- : weakSource_(OBSGetWeakRef(source)),
- obs_fader(obs_fader_create(OBS_FADER_LOG)),
- vertical(vertical),
- contextMenu(nullptr),
- QFrame(parent)
- {
- setAttribute(Qt::WA_OpaquePaintEvent, true);
- utils = std::make_unique<idian::Utils>(this);
- uuid = obs_source_get_uuid(source);
- mainLayout = new QBoxLayout(QBoxLayout::LeftToRight, this);
- mainLayout->setContentsMargins(0, 0, 0, 0);
- mainLayout->setSpacing(0);
- setLayout(mainLayout);
- categoryLabel = new QLabel("Active");
- categoryLabel->setAlignment(Qt::AlignCenter);
- utils->addClass(categoryLabel, "mixer-category");
- utils->addClass(categoryLabel, "text-tiny");
- nameButton = new VolumeName(source, this);
- nameButton->setMaximumWidth(140);
- utils->addClass(nameButton, "text-small");
- utils->addClass(nameButton, "mixer-name");
- muteButton = new QPushButton(this);
- muteButton->setCheckable(true);
- utils->addClass(muteButton, "btn-mute");
- monitorButton = new QPushButton(this);
- monitorButton->setCheckable(true);
- utils->addClass(monitorButton, "btn-monitor");
- volumeLabel = new QLabel(this);
- volumeLabel->setObjectName("volLabel");
- slider = new VolumeSlider(obs_fader, Qt::Horizontal, this);
- slider->setMinimum(0);
- slider->setMaximum(int(FADER_PRECISION));
- sourceName = obs_source_get_name(source);
- setObjectName(sourceName);
- utils->applyStateStylingEventFilter(muteButton);
- utils->applyStateStylingEventFilter(monitorButton);
- volumeMeter = new VolumeMeter(this, source);
- bool muted = obs_source_muted(source);
- bool unassigned = isSourceUnassigned(source);
- volumeMeter->setMuted(muted || unassigned);
- setLayoutVertical(vertical);
- setName(sourceName);
- obs_fader_add_callback(obs_fader, obsVolumeChanged, this);
- obsSignals.reserve(9);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "mute", obsVolumeMuted, this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_mixers", obsMixersOrMonitoringChanged,
- this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_monitoring", obsMixersOrMonitoringChanged,
- this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "activate", VolumeControl::obsSourceActivated,
- this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "deactivate",
- VolumeControl::obsSourceDeactivated, this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_activate",
- VolumeControl::obsSourceActivated, this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "audio_deactivate",
- VolumeControl::obsSourceDeactivated, this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "remove", VolumeControl::obsSourceDestroy, this);
- obsSignals.emplace_back(obs_source_get_signal_handler(source), "destroy", VolumeControl::obsSourceDestroy,
- this);
- setContextMenuPolicy(Qt::CustomContextMenu);
- connect(this, &QWidget::customContextMenuRequested, this, &VolumeControl::showVolumeControlMenu);
- connect(nameButton, &VolumeName::renamed, this, &VolumeControl::setName);
- connect(nameButton, &VolumeName::clicked, this, [&]() { showVolumeControlMenu(); });
- connect(slider, &VolumeSlider::valueChanged, this, &VolumeControl::sliderChanged);
- connect(muteButton, &QPushButton::clicked, this, &VolumeControl::handleMuteButton);
- connect(monitorButton, &QPushButton::clicked, this, &VolumeControl::handleMonitorButton);
- OBSBasic *main = OBSBasic::Get();
- if (main) {
- connect(main, &OBSBasic::profileSettingChanged, this,
- [this](const std::string &category, const std::string &name) {
- if (category == "Audio" && name == "MeterDecayRate") {
- updateDecayRate();
- } else if (category == "Audio" && name == "PeakMeterType") {
- updatePeakMeterType();
- }
- });
- }
- obs_fader_attach_source(obs_fader, source);
- // Call volume changed once to init the slider position and label
- changeVolume();
- updateMixerState();
- }
- VolumeControl::~VolumeControl()
- {
- obs_fader_remove_callback(obs_fader, obsVolumeChanged, this);
- obsSignals.clear();
- if (contextMenu) {
- contextMenu->close();
- }
- }
- void VolumeControl::obsVolumeChanged(void *data, float)
- {
- VolumeControl *volControl = static_cast<VolumeControl *>(data);
- QMetaObject::invokeMethod(volControl, "changeVolume", Qt::QueuedConnection);
- }
- void VolumeControl::obsVolumeMuted(void *data, calldata_t *)
- {
- VolumeControl *volControl = static_cast<VolumeControl *>(data);
- QMetaObject::invokeMethod(volControl, "updateMixerState", Qt::QueuedConnection);
- }
- void VolumeControl::obsMixersOrMonitoringChanged(void *data, calldata_t *)
- {
- VolumeControl *volControl = static_cast<VolumeControl *>(data);
- QMetaObject::invokeMethod(volControl, "updateMixerState", Qt::QueuedConnection);
- }
- void VolumeControl::obsSourceActivated(void *data, calldata_t *)
- {
- QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "sourceActiveChanged", Qt::QueuedConnection,
- Q_ARG(bool, true));
- }
- void VolumeControl::obsSourceDeactivated(void *data, calldata_t *)
- {
- QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "sourceActiveChanged", Qt::QueuedConnection,
- Q_ARG(bool, false));
- }
- void VolumeControl::obsSourceDestroy(void *data, calldata_t *)
- {
- QMetaObject::invokeMethod(static_cast<VolumeControl *>(data), "handleSourceDestroyed", Qt::QueuedConnection);
- }
- void VolumeControl::setLayoutVertical(bool vertical)
- {
- QBoxLayout *newLayout = new QBoxLayout(vertical ? QBoxLayout::TopToBottom : QBoxLayout::LeftToRight);
- newLayout->setContentsMargins(0, 0, 0, 0);
- newLayout->setSpacing(0);
- if (vertical) {
- setMaximumWidth(110);
- setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
- QHBoxLayout *categoryLayout = new QHBoxLayout;
- QHBoxLayout *nameLayout = new QHBoxLayout;
- QHBoxLayout *controlLayout = new QHBoxLayout;
- QHBoxLayout *volLayout = new QHBoxLayout;
- QFrame *meterFrame = new QFrame;
- QHBoxLayout *meterLayout = new QHBoxLayout;
- volumeMeter->setVertical(true);
- volumeMeter->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
- slider->setOrientation(Qt::Vertical);
- slider->setLayoutDirection(Qt::LeftToRight);
- slider->setDisplayTicks(true);
- nameButton->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
- categoryLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
- volumeLabel->setAlignment(Qt::AlignLeft);
- categoryLayout->setAlignment(Qt::AlignCenter);
- nameLayout->setAlignment(Qt::AlignCenter);
- meterLayout->setAlignment(Qt::AlignCenter);
- controlLayout->setAlignment(Qt::AlignCenter);
- volLayout->setAlignment(Qt::AlignCenter);
- meterFrame->setObjectName("volMeterFrame");
- categoryLayout->setContentsMargins(0, 0, 0, 0);
- categoryLayout->setSpacing(0);
- categoryLayout->addWidget(categoryLabel);
- nameLayout->setContentsMargins(0, 0, 0, 0);
- nameLayout->setSpacing(0);
- nameLayout->addWidget(nameButton);
- controlLayout->setContentsMargins(0, 0, 0, 0);
- controlLayout->setSpacing(0);
- // Add Headphone (audio monitoring) widget here
- controlLayout->addWidget(muteButton);
- controlLayout->addWidget(monitorButton);
- meterLayout->setContentsMargins(0, 0, 0, 0);
- meterLayout->setSpacing(0);
- meterLayout->addWidget(slider);
- meterLayout->addWidget(volumeMeter);
- meterFrame->setLayout(meterLayout);
- volLayout->setContentsMargins(0, 0, 0, 0);
- volLayout->setSpacing(0);
- volLayout->addWidget(volumeLabel);
- volLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::Maximum));
- newLayout->addItem(categoryLayout);
- newLayout->addItem(nameLayout);
- newLayout->addItem(volLayout);
- newLayout->addWidget(meterFrame);
- newLayout->addItem(controlLayout);
- newLayout->setStretch(0, 0);
- newLayout->setStretch(1, 0);
- newLayout->setStretch(2, 0);
- newLayout->setStretch(3, 1);
- newLayout->setStretch(4, 0);
- volumeMeter->setFocusProxy(slider);
- } else {
- setMaximumWidth(QWIDGETSIZE_MAX);
- setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);
- QVBoxLayout *textLayout = new QVBoxLayout;
- QHBoxLayout *controlLayout = new QHBoxLayout;
- QFrame *meterFrame = new QFrame;
- QVBoxLayout *meterLayout = new QVBoxLayout;
- QVBoxLayout *buttonLayout = new QVBoxLayout;
- volumeMeter->setVertical(false);
- volumeMeter->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
- slider->setOrientation(Qt::Horizontal);
- slider->setLayoutDirection(Qt::LeftToRight);
- slider->setDisplayTicks(true);
- nameButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
- categoryLabel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
- volumeLabel->setAlignment(Qt::AlignRight);
- QHBoxLayout *textTopLayout = new QHBoxLayout;
- textTopLayout->setContentsMargins(0, 0, 0, 0);
- textTopLayout->addWidget(categoryLabel);
- textTopLayout->addWidget(volumeLabel);
- textLayout->setContentsMargins(0, 0, 0, 0);
- textLayout->addItem(textTopLayout);
- textLayout->addWidget(nameButton);
- meterFrame->setObjectName("volMeterFrame");
- meterFrame->setLayout(meterLayout);
- meterLayout->setContentsMargins(0, 0, 0, 0);
- meterLayout->setSpacing(0);
- meterLayout->addWidget(slider);
- meterLayout->addWidget(volumeMeter);
- meterLayout->setStretch(0, 2);
- meterLayout->setStretch(1, 2);
- buttonLayout->setContentsMargins(0, 0, 0, 0);
- buttonLayout->setSpacing(0);
- buttonLayout->addWidget(muteButton);
- buttonLayout->addWidget(monitorButton);
- controlLayout->addItem(buttonLayout);
- controlLayout->addWidget(meterFrame);
- newLayout->addItem(textLayout);
- newLayout->addItem(controlLayout);
- newLayout->setStretch(0, 3);
- newLayout->setStretch(1, 6);
- volumeMeter->setFocusProxy(slider);
- }
- QWidget().setLayout(mainLayout);
- setLayout(newLayout);
- mainLayout = newLayout;
- updateTabOrder();
- adjustSize();
- }
- void VolumeControl::showVolumeControlMenu(QPoint pos)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- QMenu *popup = new QMenu(this);
- // Create menu QActions
- QAction *lockAction = new QAction(QTStr("LockVolume"), popup);
- lockAction->setCheckable(true);
- lockAction->setChecked(mixerStatus().has(VolumeControl::MixerStatus::Locked));
- bool isGlobal = mixerStatus().has(VolumeControl::MixerStatus::Global);
- QAction *pinAction = new QAction(QTStr("Basic.AudioMixer.Pin"), popup);
- bool isPinned = mixerStatus().has(VolumeControl::MixerStatus::Pinned);
- if (isPinned) {
- pinAction->setText(QTStr("Basic.AudioMixer.Unpin"));
- }
- QAction *hideAction = new QAction(QTStr("Basic.AudioMixer.Hide"), popup);
- bool isHidden = mixerStatus().has(VolumeControl::MixerStatus::Hidden);
- if (isHidden && !isGlobal) {
- hideAction->setText(QTStr("Basic.AudioMixer.Unhide"));
- }
- QAction *unhideAllAction = new QAction(QTStr("UnhideAll"), popup);
- QAction *mixerRenameAction = new QAction(QTStr("Rename"), popup);
- QAction *copyFiltersAction = new QAction(QTStr("Copy.Filters"), popup);
- QAction *pasteFiltersAction = new QAction(QTStr("Paste.Filters"), popup);
- QAction *filtersAction = new QAction(QTStr("Filters"), popup);
- QAction *propertiesAction = new QAction(QTStr("Properties"), popup);
- // Set properties on actions that require source reference
- hideAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- pinAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- mixerRenameAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- copyFiltersAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- pasteFiltersAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- filtersAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- propertiesAction->setProperty("source", QVariant::fromValue<OBSSource>(source));
- // Connect actions to signals
- OBSBasic *main = OBSBasic::Get();
- connect(unhideAllAction, &QAction::triggered, this, [this]() { emit unhideAll(); });
- connect(hideAction, &QAction::triggered, this, [this, isHidden]() { setHiddenInMixer(!isHidden); });
- connect(
- pinAction, &QAction::triggered, this, [this, isPinned]() { setPinnedInMixer(!isPinned); },
- Qt::DirectConnection);
- connect(lockAction, &QAction::toggled, this, &VolumeControl::setLocked);
- connect(copyFiltersAction, &QAction::triggered, main, &OBSBasic::actionCopyFilters);
- connect(pasteFiltersAction, &QAction::triggered, main, &OBSBasic::actionPasteFilters);
- connect(mixerRenameAction, &QAction::triggered, this, &VolumeControl::renameSource);
- connect(filtersAction, &QAction::triggered, main, &OBSBasic::actionOpenSourceFilters);
- connect(propertiesAction, &QAction::triggered, main, &OBSBasic::actionOpenSourceProperties);
- // Enable/disable actions
- copyFiltersAction->setEnabled(obs_source_filter_count(source) > 0);
- pasteFiltersAction->setEnabled(!obs_weak_source_expired(main->copyFiltersSource()));
- if (isGlobal) {
- pinAction->setDisabled(true);
- hideAction->setDisabled(true);
- }
- if (isPinned) {
- hideAction->setDisabled(true);
- }
- // Build menu
- popup->addAction(unhideAllAction);
- popup->addSeparator();
- popup->addAction(pinAction);
- popup->addAction(hideAction);
- popup->addAction(lockAction);
- popup->addSeparator();
- popup->addAction(copyFiltersAction);
- popup->addAction(pasteFiltersAction);
- popup->addSeparator();
- popup->addAction(mixerRenameAction);
- popup->addSeparator();
- popup->addAction(filtersAction);
- popup->addAction(propertiesAction);
- // Calculate menu position
- QPoint popupPos = mapToGlobal(pos);
- if (pos.isNull()) {
- QPoint menuPos = nameButton->mapToGlobal(nameButton->rect().bottomLeft());
- QSize menuSize = popup->sizeHint();
- QRect available = QGuiApplication::screenAt(menuPos)->availableGeometry();
- int spaceBelow = available.bottom() - menuPos.y();
- int spaceAbove = menuPos.y() - available.top();
- if (menuSize.height() > spaceBelow && spaceAbove > spaceBelow) {
- menuPos = nameButton->mapToGlobal(nameButton->rect().topLeft());
- menuPos.ry() -= menuSize.height();
- }
- if (menuPos.x() + menuSize.width() > available.right()) {
- menuPos.rx() = available.right() - menuSize.width();
- }
- popupPos = menuPos;
- }
- popup->popup(popupPos);
- connect(popup, &QMenu::aboutToHide, popup, &QMenu::deleteLater);
- }
- void VolumeControl::renameSource()
- {
- QAction *action = reinterpret_cast<QAction *>(sender());
- obs_source_t *source = action->property("source").value<OBSSource>();
- const char *prevName = obs_source_get_name(source);
- for (;;) {
- std::string name;
- bool accepted = NameDialog::AskForName(this, QTStr("Basic.Main.MixerRename.Title"),
- QTStr("Basic.Main.MixerRename.Text"), name, QT_UTF8(prevName));
- if (!accepted) {
- return;
- }
- if (name.empty()) {
- OBSMessageBox::warning(this, QTStr("NoNameEntered.Title"), QTStr("NoNameEntered.Text"));
- continue;
- }
- OBSSourceAutoRelease sourceTest = obs_get_source_by_name(name.c_str());
- if (sourceTest) {
- OBSMessageBox::warning(this, QTStr("NameExists.Title"), QTStr("NameExists.Text"));
- continue;
- }
- std::string prevName(obs_source_get_name(source));
- auto undo = [prevName](const std::string &data) {
- OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
- obs_source_set_name(source, prevName.c_str());
- };
- std::string editedName = name;
- auto redo = [editedName](const std::string &data) {
- OBSSourceAutoRelease source = obs_get_source_by_uuid(data.c_str());
- obs_source_set_name(source, editedName.c_str());
- };
- OBSBasic *main = OBSBasic::Get();
- const char *uuid = obs_source_get_uuid(source);
- main->undo_s.add_action(QTStr("Undo.Rename").arg(name.c_str()), undo, redo, uuid, uuid);
- obs_source_set_name(source, name.c_str());
- break;
- }
- }
- void VolumeControl::changeVolume()
- {
- QSignalBlocker blocker(slider);
- slider->setValue((int)(obs_fader_get_deflection(obs_fader) * FADER_PRECISION));
- updateText();
- }
- void VolumeControl::setLocked(bool locked)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- OBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);
- obs_data_set_bool(priv_settings, "volume_locked", locked);
- enableSlider(!locked);
- mixerStatus().set(VolumeControl::MixerStatus::Locked, locked);
- OBSBasic *main = OBSBasic::Get();
- emit main->mixerStatusChanged(uuid);
- }
- void VolumeControl::updateCategoryLabel()
- {
- QString labelText = QTStr("Basic.AudioMixer.Category.Active");
- if (mixerStatus().has(VolumeControl::MixerStatus::Unassigned)) {
- labelText = QTStr("Basic.AudioMixer.Category.Unassigned");
- } else if (mixerStatus().has(VolumeControl::MixerStatus::Global)) {
- labelText = QTStr("Basic.AudioMixer.Category.Global");
- } else if (mixerStatus().has(VolumeControl::MixerStatus::Pinned)) {
- labelText = QTStr("Basic.AudioMixer.Category.Pinned");
- } else if (mixerStatus().has(VolumeControl::MixerStatus::Hidden)) {
- labelText = QTStr("Basic.AudioMixer.Category.Hidden");
- } else if (!mixerStatus().has(VolumeControl::MixerStatus::Active)) {
- labelText = QTStr("Basic.AudioMixer.Category.Inactive");
- if (mixerStatus().has(VolumeControl::MixerStatus::Preview)) {
- labelText = QTStr("Basic.AudioMixer.Category.Preview");
- }
- }
- bool stylePinned = mixerStatus().has(VolumeControl::MixerStatus::Global) ||
- mixerStatus().has(VolumeControl::MixerStatus::Pinned);
- bool styleInactive = mixerStatus().has(VolumeControl::MixerStatus::Active) != true;
- bool styleHidden = mixerStatus().has(VolumeControl::MixerStatus::Hidden);
- bool styleUnassigned = mixerStatus().has(VolumeControl::MixerStatus::Unassigned);
- bool stylePreviewed = mixerStatus().has(VolumeControl::MixerStatus::Preview);
- utils->toggleClass("volume-pinned", stylePinned);
- utils->toggleClass("volume-inactive", styleInactive);
- utils->toggleClass("volume-preview", styleInactive && stylePreviewed);
- utils->toggleClass("volume-hidden", styleHidden && !stylePinned);
- utils->toggleClass("volume-unassigned", styleUnassigned);
- categoryLabel->setText(labelText);
- utils->polishChildren();
- volumeMeter->updateBackgroundCache();
- }
- void VolumeControl::updateDecayRate()
- {
- OBSBasic *main = OBSBasic::Get();
- double meterDecayRate = config_get_double(main->Config(), "Audio", "MeterDecayRate");
- setMeterDecayRate(meterDecayRate);
- }
- void VolumeControl::updatePeakMeterType()
- {
- OBSBasic *main = OBSBasic::Get();
- uint32_t peakMeterTypeIdx = config_get_uint(main->Config(), "Audio", "PeakMeterType");
- enum obs_peak_meter_type peakMeterType;
- switch (peakMeterTypeIdx) {
- case 0:
- peakMeterType = SAMPLE_PEAK_METER;
- break;
- case 1:
- peakMeterType = TRUE_PEAK_METER;
- break;
- default:
- peakMeterType = SAMPLE_PEAK_METER;
- break;
- }
- setPeakMeterType(peakMeterType);
- }
- void VolumeControl::setMuted(bool mute)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- bool prev = obs_source_muted(source);
- bool unassigned = isSourceUnassigned(source);
- obs_source_set_muted(source, mute);
- if (!mute && unassigned) {
- // Show notice about the source no being assigned to any tracks
- bool has_shown_warning =
- config_get_bool(App()->GetUserConfig(), "General", "WarnedAboutUnassignedSources");
- if (!has_shown_warning) {
- showUnassignedWarning(obs_source_get_name(source));
- }
- }
- auto undo_redo = [](const std::string &uuid, bool val) {
- OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
- obs_source_set_muted(source, val);
- };
- QString text = QTStr(mute ? "Undo.Volume.Mute" : "Undo.Volume.Unmute");
- const char *name = obs_source_get_name(source);
- OBSBasic::Get()->undo_s.add_action(text.arg(name), std::bind(undo_redo, std::placeholders::_1, prev),
- std::bind(undo_redo, std::placeholders::_1, mute), uuid, uuid);
- }
- void VolumeControl::setMonitoring(obs_monitoring_type type)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- obs_monitoring_type prevMonitoringType = obs_source_get_monitoring_type(source);
- obs_source_set_monitoring_type(source, type);
- auto undo_redo = [](const std::string &uuid, obs_monitoring_type val) {
- OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
- obs_source_set_monitoring_type(source, val);
- };
- QString text = QTStr("Undo.MonitoringType.Change");
- const char *name = obs_source_get_name(source);
- OBSBasic::Get()->undo_s.add_action(text.arg(name),
- std::bind(undo_redo, std::placeholders::_1, prevMonitoringType),
- std::bind(undo_redo, std::placeholders::_1, type), uuid, uuid);
- }
- void VolumeControl::sourceActiveChanged(bool active)
- {
- setUseDisabledColors(!active);
- mixerStatus().set(VolumeControl::MixerStatus::Active, active);
- OBSBasic *main = OBSBasic::Get();
- emit main->mixerStatusChanged(uuid);
- }
- void VolumeControl::updateMixerState()
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- deleteLater();
- return;
- }
- bool muted = obs_source_muted(source);
- bool unassigned = isSourceUnassigned(source);
- obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);
- bool isActive = obs_source_active(source) && obs_source_audio_active(source);
- mixerStatus().set(VolumeControl::MixerStatus::Active, isActive);
- setUseDisabledColors(!isActive);
- mixerStatus().set(VolumeControl::MixerStatus::Unassigned, unassigned);
- QSignalBlocker blockMute(muteButton);
- QSignalBlocker blockMonitor(monitorButton);
- bool showAsMuted = muted || monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY;
- bool showAsMonitored = monitoringType != OBS_MONITORING_TYPE_NONE;
- bool showAsUnassigned = !muted && unassigned;
- volumeMeter->setMuted((showAsMuted || showAsUnassigned) && !showAsMonitored);
- // Qt doesn't support overriding the QPushButton icon using pseudo state selectors like :checked
- // in QSS so we set a checked class selector on the button to be used instead.
- utils->toggleClass(muteButton, "checked", showAsMuted);
- utils->toggleClass(monitorButton, "checked", showAsMonitored);
- utils->toggleClass(muteButton, "mute-unassigned", showAsUnassigned);
- muteButton->setChecked(showAsMuted);
- monitorButton->setChecked(showAsMonitored);
- if (showAsUnassigned) {
- QIcon unassignedIcon;
- unassignedIcon.addFile(QString::fromUtf8(":/res/images/unassigned.svg"), QSize(16, 16),
- QIcon::Mode::Normal, QIcon::State::Off);
- muteButton->setIcon(unassignedIcon);
- } else if (showAsMuted) {
- QIcon mutedIcon;
- mutedIcon.addFile(QString::fromUtf8(":/res/images/mute.svg"), QSize(16, 16), QIcon::Mode::Normal,
- QIcon::State::Off);
- muteButton->setIcon(mutedIcon);
- } else {
- QIcon unmutedIcon;
- unmutedIcon.addFile(QString::fromUtf8(":/settings/images/settings/audio.svg"), QSize(16, 16),
- QIcon::Mode::Normal, QIcon::State::Off);
- muteButton->setIcon(unmutedIcon);
- }
- if (showAsMonitored) {
- QIcon monitorOnIcon;
- monitorOnIcon.addFile(QString::fromUtf8(":/res/images/headphones.svg"), QSize(16, 16),
- QIcon::Mode::Normal, QIcon::State::Off);
- monitorButton->setIcon(monitorOnIcon);
- } else {
- QIcon monitorOffIcon;
- monitorOffIcon.addFile(QString::fromUtf8(":/res/images/headphones-off.svg"), QSize(16, 16),
- QIcon::Mode::Normal, QIcon::State::Off);
- monitorButton->setIcon(monitorOffIcon);
- }
- utils->repolish(muteButton);
- utils->repolish(monitorButton);
- updateCategoryLabel();
- }
- void VolumeControl::handleMuteButton(bool mute)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- // The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.
- // Both buttons must be in their "off" state in order to actually process it as a mute.
- // Otherwise, clicking "Mute" with monitoring enabled will toggle the monitoring type.
- obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);
- if (mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT) {
- setMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);
- } else if (!mute && monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {
- setMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
- } else {
- setMuted(mute);
- }
- }
- void VolumeControl::handleMonitorButton(bool enableMonitoring)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- // The Mute and Monitor buttons in the volume mixer work as a pseudo quad-state toggle.
- // The source is only ever actually "Muted" if Monitoring is set to None.
- obs_monitoring_type monitoringType = obs_source_get_monitoring_type(source);
- bool muted = obs_source_muted(source);
- if (!enableMonitoring) {
- setMonitoring(OBS_MONITORING_TYPE_NONE);
- if (monitoringType == OBS_MONITORING_TYPE_MONITOR_ONLY) {
- setMuted(true);
- }
- } else if (enableMonitoring && muted) {
- setMonitoring(OBS_MONITORING_TYPE_MONITOR_ONLY);
- setMuted(false);
- } else if (enableMonitoring && !muted) {
- setMonitoring(OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT);
- }
- }
- void VolumeControl::sliderChanged(int vol)
- {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- float prev = obs_source_get_volume(source);
- obs_fader_set_deflection(obs_fader, float(vol) / FADER_PRECISION);
- updateText();
- auto undo_redo = [](const std::string &uuid, float val) {
- OBSSourceAutoRelease source = obs_get_source_by_uuid(uuid.c_str());
- obs_source_set_volume(source, val);
- };
- float val = obs_source_get_volume(source);
- const char *name = obs_source_get_name(source);
- OBSBasic::Get()->undo_s.add_action(QTStr("Undo.Volume.Change").arg(name),
- std::bind(undo_redo, std::placeholders::_1, prev),
- std::bind(undo_redo, std::placeholders::_1, val), uuid, uuid, true);
- }
- void VolumeControl::updateText()
- {
- QString text;
- float db = obs_fader_get_db(obs_fader);
- if (db < -96.0f) {
- text = "-inf dB";
- } else {
- text = QString::number(db, 'f', 1).append(" dB");
- }
- volumeLabel->setText(text);
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- bool muted = obs_source_muted(source);
- const char *accTextLookup = muted ? "VolControl.SliderMuted" : "VolControl.SliderUnmuted";
- QString sourceName = obs_source_get_name(source);
- QString accText = QTStr(accTextLookup).arg(sourceName);
- slider->setAccessibleName(accText);
- }
- void VolumeControl::setVertical(bool vertical_)
- {
- if (vertical == vertical_) {
- return;
- }
- vertical = vertical_;
- setLayoutVertical(vertical);
- }
- void VolumeControl::updateTabOrder()
- {
- QWidget *prevFocus = firstWidget()->previousInFocusChain();
- QWidget *lastFocus = lastWidget()->nextInFocusChain();
- if (vertical) {
- setTabOrder(prevFocus, nameButton);
- setTabOrder(nameButton, slider);
- setTabOrder(slider, muteButton);
- setTabOrder(muteButton, monitorButton);
- setTabOrder(monitorButton, lastFocus);
- } else {
- setTabOrder(prevFocus, nameButton);
- setTabOrder(nameButton, muteButton);
- setTabOrder(muteButton, monitorButton);
- setTabOrder(monitorButton, slider);
- setTabOrder(slider, lastFocus);
- }
- }
- void VolumeControl::updateName()
- {
- setName(sourceName);
- }
- void VolumeControl::setName(QString name)
- {
- sourceName = name;
- muteButton->setAccessibleName(QTStr("VolControl.Mute").arg(name));
- }
- void VolumeControl::setMeterDecayRate(qreal q)
- {
- volumeMeter->setPeakDecayRate(q);
- }
- void VolumeControl::setPeakMeterType(enum obs_peak_meter_type peakMeterType)
- {
- volumeMeter->setPeakMeterType(peakMeterType);
- }
- void VolumeControl::enableSlider(bool enable)
- {
- slider->setEnabled(enable);
- }
- void VolumeControl::setUseDisabledColors(bool greyscale)
- {
- volumeMeter->setUseDisabledColors(greyscale);
- }
- void VolumeControl::setGlobalInMixer(bool global)
- {
- if (mixerStatus().has(VolumeControl::MixerStatus::Global) != global) {
- mixerStatus().set(VolumeControl::MixerStatus::Global, global);
- OBSBasic *main = OBSBasic::Get();
- emit main->mixerStatusChanged(uuid);
- }
- }
- void VolumeControl::setPinnedInMixer(bool pinned)
- {
- if (mixerStatus().has(VolumeControl::MixerStatus::Pinned) != pinned) {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- OBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);
- obs_data_set_bool(priv_settings, "mixer_pinned", pinned);
- mixerStatus().set(VolumeControl::MixerStatus::Pinned, pinned);
- if (pinned) {
- // Unset hidden state when pinning controls
- setHiddenInMixer(false);
- }
- OBSBasic *main = OBSBasic::Get();
- emit main->mixerStatusChanged(uuid);
- }
- }
- void VolumeControl::setHiddenInMixer(bool hidden)
- {
- if (mixerStatus().has(VolumeControl::MixerStatus::Hidden) != hidden) {
- OBSSource source = OBSGetStrongRef(weakSource());
- if (!source) {
- return;
- }
- OBSDataAutoRelease priv_settings = obs_source_get_private_settings(source);
- obs_data_set_bool(priv_settings, "mixer_hidden", hidden);
- mixerStatus().set(VolumeControl::MixerStatus::Hidden, hidden);
- OBSBasic *main = OBSBasic::Get();
- emit main->mixerStatusChanged(uuid);
- }
- }
- void VolumeControl::refreshColors()
- {
- volumeMeter->refreshColors();
- }
- void VolumeControl::setLevels(const float magnitude[MAX_AUDIO_CHANNELS], const float peak[MAX_AUDIO_CHANNELS],
- const float inputPeak[MAX_AUDIO_CHANNELS])
- {
- if (volumeMeter) {
- volumeMeter->setLevels(magnitude, peak, inputPeak);
- }
- }
|