Compare commits

..

1 Commits

Author SHA1 Message Date
WarmUpTill
28d7bfc32e Add audio condition check if source is enabled on a specific track 2026-08-29 23:10:52 +02:00
6 changed files with 129 additions and 73 deletions

View File

@@ -342,7 +342,9 @@ AdvSceneSwitcher.condition.audio.type.volume="Configured volume level"
AdvSceneSwitcher.condition.audio.type.syncOffset="Sync offset"
AdvSceneSwitcher.condition.audio.type.monitor="Audio monitoring"
AdvSceneSwitcher.condition.audio.type.balance="Audio balance"
AdvSceneSwitcher.condition.audio.type.track="Audio track"
AdvSceneSwitcher.condition.audio.entry="{{checkType}}of{{audioSources}}is{{condition}}{{volume}}{{volumeDB}}{{percentDBToggle}}{{syncOffset}}{{monitorTypes}}"
AdvSceneSwitcher.condition.audio.layout.track="{{audioSources}}is enabled on{{checkType}}{{track}}"
AdvSceneSwitcher.condition.cursor="Cursor"
AdvSceneSwitcher.condition.cursor.type.region="is in region"
AdvSceneSwitcher.condition.cursor.type.moving="is moving"
@@ -2364,6 +2366,7 @@ AdvSceneSwitcher.tempVar.audio.muted="Source muted"
AdvSceneSwitcher.tempVar.audio.sync_offset="Source audio sync offset"
AdvSceneSwitcher.tempVar.audio.monitor="Source audio monitor type"
AdvSceneSwitcher.tempVar.audio.balance="Source audio balance"
AdvSceneSwitcher.tempVar.audio.trackEnabled="Audio track enabled"
AdvSceneSwitcher.tempVar.clipboard.mimeType.primary="Primary clipboard item MIME type"
AdvSceneSwitcher.tempVar.clipboard.mimeType.primary.description="Highest priority MIME type of the current item stored in clipboard, if available."
@@ -3107,4 +3110,3 @@ AdvSceneSwitcher.sceneGroupTab.defaultname="Scene Group %1"
AdvSceneSwitcher.sceneGroupTab.exists="Scene Group or Scene name exists already"
AdvSceneSwitcher.sceneGroupTab.help="Scene Groups can be selected as a target just like a regular scene.\n\nAs the name suggests a scene group is a collection of multiple scenes.\nThe scene group will advance through the list of its assigned scenes depending on the configured settings, which can be found on the right side.\n\nYou can configure the scene group to advance to the next scene in the list:\nAfter a number of times the scene group is selected as a target.\nAfter a certain amount of time has passed.\nOr randomly.\n\nFor example, a scene group containing the scenes ...\nScene 1\nScene 2\nScene 3 \n... will activate \"Scene 1\" the first time it is selected as a target.\nThe second time it will activate \"Scene 2\".\nThe remaining times \"Scene 3\" will be activated.\n\nClick the highlighted plus symbol below to add a new scene group."
AdvSceneSwitcher.sceneGroupTab.scenes.help="Select the scene group you want to modify on the left.\n\nSelect a scene to add to this scene group by selecting the scene above and clicking the plus symbol below.\n\nA scene can be added multiple times to the same scene group."
AdvSceneSwitcher.kwin.flatpakPermissionWarning="Window detection via KWin is unavailable.\nThe OBS Studio Flatpak requires additional permissions to enable this feature.\nSee the Advanced Scene Switcher wiki for details."

View File

@@ -1,9 +1,5 @@
#include "platform-funcs.hpp"
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "plugin-state-helpers.hpp"
#include "ui-helpers.hpp"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
@@ -632,39 +628,27 @@ int ignoreXerror(Display *d, XErrorEvent *e)
return 0;
}
static void handleKWinInitFailure(bool kwinAvailable)
{
blog(LOG_INFO, "not using KWin compat");
const bool onKDE = qEnvironmentVariable("XDG_CURRENT_DESKTOP")
.contains("KDE", Qt::CaseInsensitive);
const bool inFlatpak = qEnvironmentVariableIsSet("FLATPAK_ID");
if (!kwinAvailable && onKDE && inFlatpak)
AddFinishedLoadingStep([]() {
DisplayTrayMessage(
obs_module_text("AdvSceneSwitcher.pluginName"),
obs_module_text("AdvSceneSwitcher.kwin.flatpakPermissionWarning"));
});
}
void PlatformInit()
{
const bool kwinAvailable = isKWinAvailable();
KWin = kwinAvailable && registerKWinDBusListener(&notifier) &&
startKWinScript(KWinScriptObjectPath);
if (KWin)
blog(LOG_INFO, "using KWin compat");
else
handleKWinInitFailure(kwinAvailable);
initProcps();
initProc2();
auto display = disp();
if (!display) {
return;
}
KWin = isKWinAvailable();
if (!(KWin && startKWinScript(KWinScriptObjectPath) &&
registerKWinDBusListener(&notifier))) {
// something bad happened while trying to initialize
// the KWin script/dbus so disable it
KWin = false;
blog(LOG_INFO, "not using KWin compat");
} else {
blog(LOG_INFO, "using KWin compat");
}
initXss();
initProcps();
initProc2();
XSetErrorHandler(ignoreXerror);
}

View File

@@ -5,7 +5,6 @@
#include <QDir>
#include <QFile>
#include <QFileDevice>
#include <QStandardPaths>
#include <QTextStream>
#include <QtDBus/QDBusConnectionInterface>
#include <QtDBus/QDBusInterface>
@@ -98,13 +97,11 @@ bool isKWinAvailable()
bool startKWinScript(QString &scriptObjectPath)
{
const QString scriptPath =
QStandardPaths::writableLocation(
QStandardPaths::RuntimeLocation) +
"/AdvancedSceneSwitcher/KWinFocusNotifier.js";
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
const QString script =
R"(var adss = "com.obsproject.Studio.Plugin.SceneSwitcher";
var adssPath = "/com/obsproject/Studio/Plugin/SceneSwitcher";
R"(var adss = "com.github.AdvancedSceneSwitcher";
var adssPath = "/com/github/AdvancedSceneSwitcher";
function trackWindow(window) {
var id = window.internalId.toString();
@@ -132,8 +129,7 @@ workspace.windowActivated.connect(function(client) {
}))";
if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) {
blog(LOG_ERROR,
"error creating KWinFocusNotifier script directory");
blog(LOG_ERROR, "error creating /tmp/AdvancedSceneSwitcher");
return false;
}
@@ -202,8 +198,8 @@ bool stopKWinScript(const QString &scriptObjectPath)
bool registerKWinDBusListener(FocusNotifier *notifier)
{
static const QString serviceName = "com.obsproject.Studio.Plugin.SceneSwitcher";
static const QString objectPath = "/com/obsproject/Studio/Plugin/SceneSwitcher";
static const QString serviceName = "com.github.AdvancedSceneSwitcher";
static const QString objectPath = "/com/github/AdvancedSceneSwitcher";
auto bus = QDBusConnection::sessionBus();
if (bus.objectRegisteredAt(objectPath)) {

View File

@@ -12,7 +12,7 @@ namespace advss {
class FocusNotifier final : public QObject {
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "com.obsproject.Studio.Plugin.SceneSwitcher")
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
static std::mutex _mutex;
static int activePID;

View File

@@ -25,6 +25,8 @@ const static std::map<MacroConditionAudio::Type, std::string> checkTypes = {
"AdvSceneSwitcher.condition.audio.type.monitor"},
{MacroConditionAudio::Type::BALANCE,
"AdvSceneSwitcher.condition.audio.type.balance"},
{MacroConditionAudio::Type::TRACK,
"AdvSceneSwitcher.condition.audio.type.track"},
};
const static std::map<MacroConditionAudio::OutputCondition, std::string>
@@ -245,6 +247,20 @@ bool MacroConditionAudio::CheckBalance()
return ret && source;
}
bool MacroConditionAudio::CheckTrack()
{
if (!_audioSource.GetSource()) {
return false;
}
OBSSourceAutoRelease source =
obs_weak_source_get_source(_audioSource.GetSource());
uint32_t mixers = obs_source_get_audio_mixers(source);
bool enabled = (mixers >> (uint32_t)(_track - 1)) & 1u;
SetTempVarValue("track_enabled", enabled ? "true" : "false");
return enabled && source;
}
bool MacroConditionAudio::CheckCondition()
{
bool ret = false;
@@ -264,6 +280,9 @@ bool MacroConditionAudio::CheckCondition()
case Type::BALANCE:
ret = CheckBalance();
break;
case Type::TRACK:
ret = CheckTrack();
break;
}
if (GetVariableValue().empty()) {
@@ -281,6 +300,7 @@ bool MacroConditionAudio::Save(obs_data_t *obj) const
_volumePercent.Save(obj, "volume");
_syncOffset.Save(obj, "syncOffset");
_balance.Save(obj, "balance");
_track.Save(obj, "track");
obs_data_set_int(obj, "checkType", static_cast<int>(_checkType));
obs_data_set_int(obj, "outputCondition",
static_cast<int>(_outputCondition));
@@ -346,6 +366,7 @@ bool MacroConditionAudio::Load(obs_data_t *obj)
_useDb = obs_data_get_bool(obj, "useDb");
_volumeDB.Load(obj, "volumeDB");
}
_track.Load(obj, "track");
return true;
}
@@ -421,6 +442,12 @@ void MacroConditionAudio::SetupTempVars()
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.balance"));
break;
case Type::TRACK:
AddTempvar(
"track_enabled",
obs_module_text(
"AdvSceneSwitcher.tempVar.audio.trackEnabled"));
break;
default:
break;
}
@@ -468,6 +495,7 @@ static QStringList getAudioSourcesList()
MacroConditionAudioEdit::MacroConditionAudioEdit(
QWidget *parent, std::shared_ptr<MacroConditionAudio> entryData)
: QWidget(parent),
_layout(new QHBoxLayout),
_checkTypes(new QComboBox()),
_sources(new SourceSelectionWidget(this, getAudioSourcesList, true)),
_condition(new QComboBox()),
@@ -476,7 +504,8 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
_percentDBToggle(new QPushButton),
_syncOffset(new VariableSpinBox()),
_monitorTypes(new QComboBox),
_balance(new SliderSpinBox(0., 1., ""))
_balance(new SliderSpinBox(0., 1., "")),
_track(new VariableSpinBox())
{
_volumePercent->setSuffix("%");
_volumePercent->setMaximum(100);
@@ -491,6 +520,9 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
_syncOffset->setMaximum(20000);
_syncOffset->setSuffix("ms");
_track->setMinimum(1);
_track->setMaximum(32);
QWidget::connect(_checkTypes, SIGNAL(currentIndexChanged(int)), this,
SLOT(CheckTypeChanged(int)));
QWidget::connect(
@@ -519,27 +551,16 @@ MacroConditionAudioEdit::MacroConditionAudioEdit(
this, SLOT(VolumeDBChanged(const NumberVariable<double> &)));
QWidget::connect(_percentDBToggle, SIGNAL(clicked()), this,
SLOT(PercentDBClicked()));
QWidget::connect(
_track,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(TrackChanged(const NumberVariable<int> &)));
populateCheckTypes(_checkTypes);
PopulateMonitorTypeSelection(_monitorTypes);
QHBoxLayout *switchLayout = new QHBoxLayout;
std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
{"{{checkType}}", _checkTypes},
{"{{audioSources}}", _sources},
{"{{volume}}", _volumePercent},
{"{{syncOffset}}", _syncOffset},
{"{{monitorTypes}}", _monitorTypes},
{"{{balance}}", _balance},
{"{{condition}}", _condition},
{"{{volumeDB}}", _volumeDB},
{"{{percentDBToggle}}", _percentDBToggle},
};
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.audio.entry"),
switchLayout, widgetPlaceholders);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addLayout(switchLayout);
mainLayout->addLayout(_layout);
mainLayout->addWidget(_balance);
setLayout(mainLayout);
@@ -608,6 +629,12 @@ void MacroConditionAudioEdit::BalanceChanged(const NumberVariable<double> &value
_entryData->_balance = value;
}
void MacroConditionAudioEdit::TrackChanged(const NumberVariable<int> &value)
{
GUARD_LOADING_AND_LOCK();
_entryData->_track = value;
}
void MacroConditionAudioEdit::VolumeDBChanged(
const NumberVariable<double> &value)
{
@@ -692,6 +719,8 @@ void MacroConditionAudioEdit::CheckTypeChanged(int idx)
} else if (_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME) {
populateVolumeConditionSelection(_condition);
} else if (_entryData->GetType() == MacroConditionAudio::Type::TRACK) {
_condition->clear();
}
SetWidgetVisibility();
}
@@ -708,6 +737,7 @@ void MacroConditionAudioEdit::UpdateEntryData()
_syncOffset->SetValue(_entryData->_syncOffset);
_monitorTypes->setCurrentIndex(_entryData->_monitorType);
_balance->SetDoubleValue(_entryData->_balance);
_track->SetValue(_entryData->_track);
_checkTypes->setCurrentIndex(
_checkTypes->findData(static_cast<int>(_entryData->GetType())));
@@ -748,26 +778,63 @@ void MacroConditionAudioEdit::SetWidgetVisibility()
return;
}
_layout->removeWidget(_checkTypes);
_layout->removeWidget(_sources);
_layout->removeWidget(_condition);
_layout->removeWidget(_volumePercent);
_layout->removeWidget(_volumeDB);
_layout->removeWidget(_percentDBToggle);
_layout->removeWidget(_syncOffset);
_layout->removeWidget(_monitorTypes);
_layout->removeWidget(_track);
ClearLayout(_layout);
const std::unordered_map<std::string, QWidget *> placeholders = {
{"{{checkType}}", _checkTypes},
{"{{audioSources}}", _sources},
{"{{condition}}", _condition},
{"{{volume}}", _volumePercent},
{"{{volumeDB}}", _volumeDB},
{"{{percentDBToggle}}", _percentDBToggle},
{"{{syncOffset}}", _syncOffset},
{"{{monitorTypes}}", _monitorTypes},
{"{{track}}", _track},
};
const bool isTrack = _entryData->GetType() ==
MacroConditionAudio::Type::TRACK;
PlaceWidgets(
obs_module_text(
isTrack ? "AdvSceneSwitcher.condition.audio.layout.track"
: "AdvSceneSwitcher.condition.audio.entry"),
_layout, placeholders);
_condition->setVisible(
_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME ||
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET);
_syncOffset->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET);
_monitorTypes->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::MONITOR);
!isTrack &&
(_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME ||
_entryData->GetType() ==
MacroConditionAudio::Type::CONFIGURED_VOLUME ||
_entryData->GetType() == MacroConditionAudio::Type::BALANCE ||
_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET));
_syncOffset->setVisible(!isTrack &&
_entryData->GetType() ==
MacroConditionAudio::Type::SYNC_OFFSET);
_monitorTypes->setVisible(!isTrack &&
_entryData->GetType() ==
MacroConditionAudio::Type::MONITOR);
_track->setVisible(isTrack);
_volumePercent->setVisible(!isTrack && HasVolumeControl() &&
!_entryData->_useDb);
_volumeDB->setVisible(!isTrack && HasVolumeControl() &&
_entryData->_useDb);
_percentDBToggle->setText(_entryData->_useDb ? "dB" : "%");
_percentDBToggle->setVisible(!isTrack && HasVolumeControl());
_balance->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::BALANCE);
_volMeter->setVisible(_entryData->GetType() ==
MacroConditionAudio::Type::OUTPUT_VOLUME);
_volumePercent->setVisible(HasVolumeControl() && !_entryData->_useDb);
_volumeDB->setVisible(HasVolumeControl() && _entryData->_useDb);
_percentDBToggle->setText(_entryData->_useDb ? "dB" : "%");
_percentDBToggle->setVisible(HasVolumeControl());
adjustSize();
}

View File

@@ -5,6 +5,7 @@
#include "source-selection.hpp"
#include <limits>
#include <QHBoxLayout>
#include <QWidget>
#include <QComboBox>
#include <chrono>
@@ -36,6 +37,7 @@ public:
SYNC_OFFSET,
MONITOR,
BALANCE,
TRACK,
};
void SetType(const Type &);
Type GetType() const { return _checkType; }
@@ -60,6 +62,7 @@ public:
IntVariable _syncOffset = 0;
obs_monitoring_type _monitorType = OBS_MONITORING_TYPE_NONE;
DoubleVariable _balance = 0.5;
IntVariable _track = 1;
OutputCondition _outputCondition = OutputCondition::ABOVE;
VolumeCondition _volumeCondition = VolumeCondition::ABOVE;
obs_volmeter_t *_volmeter = nullptr;
@@ -70,6 +73,7 @@ private:
bool CheckSyncOffset();
bool CheckMonitor();
bool CheckBalance();
bool CheckTrack();
void SetupTempVars();
float GetVolumePeak();
@@ -111,6 +115,7 @@ private slots:
void VolumeDBChanged(const NumberVariable<double> &value);
void PercentDBClicked();
void SyncSliderAndValueSelection(bool sliderMoved);
void TrackChanged(const NumberVariable<int> &value);
signals:
void HeaderInfoChanged(const QString &);
@@ -119,6 +124,7 @@ private:
void SetWidgetVisibility();
bool HasVolumeControl() const;
QHBoxLayout *_layout;
QComboBox *_checkTypes;
SourceSelectionWidget *_sources;
QComboBox *_condition;
@@ -128,6 +134,7 @@ private:
VariableSpinBox *_syncOffset;
QComboBox *_monitorTypes;
SliderSpinBox *_balance;
VariableSpinBox *_track;
VolControl *_volMeter = nullptr;
std::shared_ptr<MacroConditionAudio> _entryData;