/****************************************************************************** Copyright (C) 2023 by Lain Bailey Zachary Lund Philippe Groarke This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ******************************************************************************/ #include "OBSBasic.hpp" #include using namespace std; void OBSBasic::UpdateVolumeControlsDecayRate() { double meterDecayRate = config_get_double(activeConfiguration, "Audio", "MeterDecayRate"); for (size_t i = 0; i < volumes.size(); i++) { volumes[i]->SetMeterDecayRate(meterDecayRate); } } void OBSBasic::UpdateVolumeControlsPeakMeterType() { uint32_t peakMeterTypeIdx = config_get_uint(activeConfiguration, "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; } for (size_t i = 0; i < volumes.size(); i++) { volumes[i]->setPeakMeterType(peakMeterType); } } void OBSBasic::ClearVolumeControls() { for (VolControl *vol : volumes) delete vol; volumes.clear(); } void OBSBasic::RefreshVolumeColors() { for (VolControl *vol : volumes) { vol->refreshColors(); } } void OBSBasic::HideAudioControl() { QAction *action = reinterpret_cast(sender()); VolControl *vol = action->property("volControl").value(); obs_source_t *source = vol->GetSource(); if (!SourceMixerHidden(source)) { SetSourceMixerHidden(source, true); DeactivateAudioSource(source); } } void OBSBasic::UnhideAllAudioControls() { auto UnhideAudioMixer = [this](obs_source_t *source) /* -- */ { if (!obs_source_active(source)) return true; if (!SourceMixerHidden(source)) return true; SetSourceMixerHidden(source, false); ActivateAudioSource(source); return true; }; using UnhideAudioMixer_t = decltype(UnhideAudioMixer); auto PreEnum = [](void *data, obs_source_t *source) -> bool /* -- */ { return (*static_cast(data))(source); }; obs_enum_sources(PreEnum, &UnhideAudioMixer); } void OBSBasic::ToggleHideMixer() { OBSSceneItem item = GetCurrentSceneItem(); OBSSource source = obs_sceneitem_get_source(item); if (!SourceMixerHidden(source)) { SetSourceMixerHidden(source, true); DeactivateAudioSource(source); } else { SetSourceMixerHidden(source, false); ActivateAudioSource(source); } } void OBSBasic::LockVolumeControl(bool lock) { QAction *action = reinterpret_cast(sender()); VolControl *vol = action->property("volControl").value(); obs_source_t *source = vol->GetSource(); OBSDataAutoRelease priv_settings = obs_source_get_private_settings(source); obs_data_set_bool(priv_settings, "volume_locked", lock); vol->EnableSlider(!lock); } void OBSBasic::VolControlContextMenu() { VolControl *vol = reinterpret_cast(sender()); /* ------------------- */ QAction lockAction(QTStr("LockVolume"), this); lockAction.setCheckable(true); lockAction.setChecked(SourceVolumeLocked(vol->GetSource())); QAction hideAction(QTStr("Hide"), this); QAction unhideAllAction(QTStr("UnhideAll"), this); QAction mixerRenameAction(QTStr("Rename"), this); QAction copyFiltersAction(QTStr("Copy.Filters"), this); QAction pasteFiltersAction(QTStr("Paste.Filters"), this); QAction filtersAction(QTStr("Filters"), this); QAction propertiesAction(QTStr("Properties"), this); QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this); QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked( config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); /* ------------------- */ connect(&hideAction, &QAction::triggered, this, &OBSBasic::HideAudioControl, Qt::DirectConnection); connect(&unhideAllAction, &QAction::triggered, this, &OBSBasic::UnhideAllAudioControls, Qt::DirectConnection); connect(&lockAction, &QAction::toggled, this, &OBSBasic::LockVolumeControl, Qt::DirectConnection); connect(&mixerRenameAction, &QAction::triggered, this, &OBSBasic::MixerRenameSource, Qt::DirectConnection); connect(©FiltersAction, &QAction::triggered, this, &OBSBasic::AudioMixerCopyFilters, Qt::DirectConnection); connect(&pasteFiltersAction, &QAction::triggered, this, &OBSBasic::AudioMixerPasteFilters, Qt::DirectConnection); connect(&filtersAction, &QAction::triggered, this, &OBSBasic::GetAudioSourceFilters, Qt::DirectConnection); connect(&propertiesAction, &QAction::triggered, this, &OBSBasic::GetAudioSourceProperties, Qt::DirectConnection); connect(&advPropAction, &QAction::triggered, this, &OBSBasic::on_actionAdvAudioProperties_triggered, Qt::DirectConnection); /* ------------------- */ connect(&toggleControlLayoutAction, &QAction::changed, this, &OBSBasic::ToggleVolControlLayout, Qt::DirectConnection); /* ------------------- */ hideAction.setProperty("volControl", QVariant::fromValue(vol)); lockAction.setProperty("volControl", QVariant::fromValue(vol)); mixerRenameAction.setProperty("volControl", QVariant::fromValue(vol)); copyFiltersAction.setProperty("volControl", QVariant::fromValue(vol)); pasteFiltersAction.setProperty("volControl", QVariant::fromValue(vol)); filtersAction.setProperty("volControl", QVariant::fromValue(vol)); propertiesAction.setProperty("volControl", QVariant::fromValue(vol)); /* ------------------- */ copyFiltersAction.setEnabled(obs_source_filter_count(vol->GetSource()) > 0); pasteFiltersAction.setEnabled(!obs_weak_source_expired(copyFiltersSource)); QMenu popup; vol->SetContextMenu(&popup); popup.addAction(&lockAction); popup.addSeparator(); popup.addAction(&unhideAllAction); popup.addAction(&hideAction); popup.addAction(&mixerRenameAction); popup.addSeparator(); popup.addAction(©FiltersAction); popup.addAction(&pasteFiltersAction); popup.addSeparator(); popup.addAction(&toggleControlLayoutAction); popup.addSeparator(); popup.addAction(&filtersAction); popup.addAction(&propertiesAction); popup.addAction(&advPropAction); // toggleControlLayoutAction deletes and re-creates the volume controls // meaning that "vol" would be pointing to freed memory. if (popup.exec(QCursor::pos()) != &toggleControlLayoutAction) vol->SetContextMenu(nullptr); } void OBSBasic::on_hMixerScrollArea_customContextMenuRequested() { StackedMixerAreaContextMenuRequested(); } void OBSBasic::on_vMixerScrollArea_customContextMenuRequested() { StackedMixerAreaContextMenuRequested(); } void OBSBasic::StackedMixerAreaContextMenuRequested() { QAction unhideAllAction(QTStr("UnhideAll"), this); QAction advPropAction(QTStr("Basic.MainMenu.Edit.AdvAudio"), this); QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked( config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); /* ------------------- */ connect(&unhideAllAction, &QAction::triggered, this, &OBSBasic::UnhideAllAudioControls, Qt::DirectConnection); connect(&advPropAction, &QAction::triggered, this, &OBSBasic::on_actionAdvAudioProperties_triggered, Qt::DirectConnection); /* ------------------- */ connect(&toggleControlLayoutAction, &QAction::changed, this, &OBSBasic::ToggleVolControlLayout, Qt::DirectConnection); /* ------------------- */ QMenu popup; popup.addAction(&unhideAllAction); popup.addSeparator(); popup.addAction(&toggleControlLayoutAction); popup.addSeparator(); popup.addAction(&advPropAction); popup.exec(QCursor::pos()); } void OBSBasic::ToggleMixerLayout(bool vertical) { if (vertical) { ui->stackedMixerArea->setMinimumSize(180, 220); ui->stackedMixerArea->setCurrentIndex(1); } else { ui->stackedMixerArea->setMinimumSize(220, 0); ui->stackedMixerArea->setCurrentIndex(0); } } void OBSBasic::ToggleVolControlLayout() { bool vertical = !config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl"); config_set_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl", vertical); ToggleMixerLayout(vertical); // We need to store it so we can delete current and then add // at the right order vector sources; for (size_t i = 0; i != volumes.size(); i++) sources.emplace_back(volumes[i]->GetSource()); ClearVolumeControls(); for (const auto &source : sources) ActivateAudioSource(source); } void OBSBasic::on_actionMixerToolbarAdvAudio_triggered() { on_actionAdvAudioProperties_triggered(); } void OBSBasic::on_actionMixerToolbarMenu_triggered() { QAction unhideAllAction(QTStr("UnhideAll"), this); connect(&unhideAllAction, &QAction::triggered, this, &OBSBasic::UnhideAllAudioControls, Qt::DirectConnection); QAction toggleControlLayoutAction(QTStr("VerticalLayout"), this); toggleControlLayoutAction.setCheckable(true); toggleControlLayoutAction.setChecked( config_get_bool(App()->GetUserConfig(), "BasicWindow", "VerticalVolControl")); connect(&toggleControlLayoutAction, &QAction::changed, this, &OBSBasic::ToggleVolControlLayout, Qt::DirectConnection); QMenu popup; popup.addAction(&unhideAllAction); popup.addSeparator(); popup.addAction(&toggleControlLayoutAction); popup.exec(QCursor::pos()); }