From 35151ea1341d81651baa2bff4490d5c58660a412 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 3 Feb 2021 19:50:37 +0100 Subject: [PATCH] Add scene trigger tab (#110) --- CMakeLists.txt | 2 + data/locale/de-DE.ini | 20 + data/locale/en-US.ini | 19 + forms/advanced-scene-switcher.ui | 364 ++++++++------- src/advanced-scene-switcher.cpp | 51 +-- src/general.cpp | 311 +++---------- src/headers/advanced-scene-switcher.hpp | 16 +- src/headers/scene-trigger.hpp | 68 +++ src/headers/switcher-data-structs.hpp | 24 +- src/scene-trigger.cpp | 571 ++++++++++++++++++++++++ src/switch-time.cpp | 2 +- src/switcher-data-structs.cpp | 5 - 12 files changed, 971 insertions(+), 482 deletions(-) create mode 100644 src/headers/scene-trigger.hpp create mode 100644 src/scene-trigger.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 26da8964..ca06efea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,7 @@ set(advanced-scene-switcher_HEADERS src/headers/curl-helper.hpp src/headers/volume-control.hpp src/headers/scene-group.hpp + src/headers/scene-trigger.hpp src/headers/switch-audio.hpp src/headers/switch-executable.hpp src/headers/switch-file.hpp @@ -75,6 +76,7 @@ set(advanced-scene-switcher_SOURCES src/advanced-scene-switcher-module.c src/switcher-data-structs.cpp src/scene-group.cpp + src/scene-trigger.cpp src/switch-transitions.cpp src/switch-screen-region.cpp src/switch-priority.cpp diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 92004e7c..cb36b762 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -219,6 +219,26 @@ AdvSceneSwitcher.sceneGroupTab.exists="Szenengruppen- oder Szenenname existiert AdvSceneSwitcher.sceneGroupTab.help="Szenengruppen können, genau wie reguläre Szenen, als Ziel eines Szenenwechsler Eintrags ausgewählt werden.\n\nSzenengruppen bestehen aus einer Liste von Szenen.\nDie aktive Szene der Szenengruppe schreitet abhängig von den konfigurierten Einstellungen durch die Liste der zugewiesenen Szenen fort.\n\nMögliche Einstellungen zum Fortschreiten der aktiven Szene sind:\nAnzahl der Szenenwechsel.\nZeit seit dem letzten Szenenwechsel in der Szenengruppe.\nOder zufällig.\n\nSo kann zum Beispiel eine Szenengruppe welche die folgenden Szenen enthält ...\nSzene 1\nSzene 2\nSzene 3 \n... beim erstem Mal zu \"Szene 1\" wechseln.\nBeim zweiten Mal zu \"Szene 2\".\nDie restlichem Male wechselt die Szenengruppe zu \"Szene 3\".\n\nKlicke auf das markierte Plus Symbol, um eine neuen Szenengruppe hinzuzufügen." AdvSceneSwitcher.sceneGroupTab.scenes.help="Wähle die Szenengruppe, die du bearbeiten möchtest, auf der linken Seite aus.\n\nWähle oben eine Szene aus, die du zur Szenengruppe hinzufügen möchtest, und klicke das Plus Symbol.\n\nDie gleiche Szene kann mehrfach in derselben Szenengruppe auftauchen." +; Scene Trigger Tab +AdvSceneSwitcher.sceneTriggerTab.title="Szenen Trigger" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--Trigger auswählen--" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="aktiv ist" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="nicht aktiv ist" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="verlassen wird" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--Aktion auswählen--" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="Aufnahme starten" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="Aufnahme pausieren" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="Aufnahme fortsetzen" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="Aufnahme stoppen" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="Streamen stoppen" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="Streamen starten" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="Replay Buffer starten" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="Replay Buffer stoppen" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="Quelle stumm schalten" +AdvSceneSwitcher.sceneTriggerTab.entry="Wenn {{scenes}} {{triggers}} {{actions}} {{audioSources}} nach {{duration}} " +AdvSceneSwitcher.sceneTriggerTab.help="Dieser Tab ermöglicht es automatisiert Handlungen, wie etwa das Stoppen von einer Aufnahme oder des Streamens, bei einem Szenenwechsel auszuführen." + + ; Hotkey AdvSceneSwitcher.hotkey.startSwitcherHotkey="Starte den Erweiteren Szenenwechsler" AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stoppe den Erweiteren Szenenwechsler" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index ec22f881..77644b5f 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -219,6 +219,25 @@ 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." +; Scene Trigger Tab +AdvSceneSwitcher.sceneTriggerTab.title="Scene Triggers" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none="--select trigger--" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive="is active" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive="is not active" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave="switched away from" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none="--select action--" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording="start recording" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording="pause recording" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording="unpause recording" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording="stop recording" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming="stop streaming" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming="start streaming" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer="start replay buffer" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer="stop replay buffer" +AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource="mute source" +AdvSceneSwitcher.sceneTriggerTab.entry="When {{scenes}} {{triggers}} {{actions}} {{audioSources}} after {{duration}}" +AdvSceneSwitcher.sceneTriggerTab.help="This tab allows you to trigger actions on scene changes, like stopping recording or streaming." + ; Hotkey AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher" AdvSceneSwitcher.hotkey.stopSwitcherHotkey="Stop the Advanced Scene Switcher" diff --git a/forms/advanced-scene-switcher.ui b/forms/advanced-scene-switcher.ui index 45bf5881..f5452759 100644 --- a/forms/advanced-scene-switcher.ui +++ b/forms/advanced-scene-switcher.ui @@ -152,6 +152,44 @@ AdvSceneSwitcher.generalTab.generalBehavior + + + + + + AdvSceneSwitcher.generalTab.generalBehavior.cooldown + + + + + + + s + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + @@ -247,7 +285,7 @@ - + @@ -271,14 +309,7 @@ - - - - Qt::Horizontal - - - - + @@ -302,150 +333,6 @@ - - - - - - AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart1 - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - - AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart2 - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStop1 - - - - - - - - - - AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStop2 - - - - - - - - 0 - 0 - - - - - 100 - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - AdvSceneSwitcher.generalTab.generalBehavior.cooldown - - - - - - - s - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - @@ -3563,6 +3450,177 @@ + + + AdvSceneSwitcher.sceneTriggerTab.title + + + + + + + + + 0 + 0 + + + + false + + + + + + + AdvSceneSwitcher.sceneTriggerTab.help + + + Qt::AlignCenter + + + true + + + + + + + + + + + + 22 + 22 + + + + true + + + addIconSmall + + + + + + + + 22 + 22 + + + + true + + + removeIconSmall + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + Qt::Vertical + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + 22 + 22 + + + + + + + + ../../../forms/images/up.svg../../../forms/images/up.svg + + + true + + + upArrowIconSmall + + + + + + + + 22 + 22 + + + + + + + + ../../../forms/images/down.svg../../../forms/images/down.svg + + + true + + + downArrowIconSmall + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + diff --git a/src/advanced-scene-switcher.cpp b/src/advanced-scene-switcher.cpp index e51b31a2..d0115100 100644 --- a/src/advanced-scene-switcher.cpp +++ b/src/advanced-scene-switcher.cpp @@ -60,6 +60,7 @@ void AdvSceneSwitcher::loadUI() setupTimeTab(); setupAudioTab(); setupSceneGroupTab(); + setupTriggerTab(); setTabOrder(); @@ -364,22 +365,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) obs_data_t *obj = obs_data_create(); - switcher->saveSceneGroups(obj); - switcher->saveWindowTitleSwitches(obj); - switcher->saveScreenRegionSwitches(obj); - switcher->savePauseSwitches(obj); - switcher->saveSceneSequenceSwitches(obj); - switcher->saveSceneTransitions(obj); - switcher->saveIdleSwitches(obj); - switcher->saveExecutableSwitches(obj); - switcher->saveRandomSwitches(obj); - switcher->saveFileSwitches(obj); - switcher->saveMediaSwitches(obj); - switcher->saveTimeSwitches(obj); - switcher->saveAudioSwitches(obj); - switcher->saveGeneralSettings(obj); - switcher->saveHotkeys(obj); - switcher->saveVersion(obj, g_GIT_SHA1); + switcher->saveSettings(obj); obs_data_set_obj(save_data, "advanced-scene-switcher", obj); @@ -395,21 +381,7 @@ static void SaveSceneSwitcher(obs_data_t *save_data, bool saving, void *) if (switcher->versionChanged(obj, g_GIT_SHA1)) AdvSceneSwitcher::AskBackup(obj); - switcher->loadSceneGroups(obj); - switcher->loadWindowTitleSwitches(obj); - switcher->loadScreenRegionSwitches(obj); - switcher->loadPauseSwitches(obj); - switcher->loadSceneSequenceSwitches(obj); - switcher->loadSceneTransitions(obj); - switcher->loadIdleSwitches(obj); - switcher->loadExecutableSwitches(obj); - switcher->loadRandomSwitches(obj); - switcher->loadFileSwitches(obj); - switcher->loadMediaSwitches(obj); - switcher->loadTimeSwitches(obj); - switcher->loadAudioSwitches(obj); - switcher->loadGeneralSettings(obj); - switcher->loadHotkeys(obj); + switcher->loadSettings(obj); obs_data_release(obj); @@ -479,14 +451,6 @@ void SwitcherData::Thread() break; } - if (autoStopEnable) { - autoStopStreamAndRecording(); - } - - if (autoStartEnable) { - autoStartStreamRecording(); - } - if (checkPause()) { continue; } @@ -675,13 +639,12 @@ void handleSceneChange(SwitcherData *s) obs_weak_source_t *ws = obs_source_get_weak_source(source); obs_source_release(source); obs_weak_source_release(ws); - if (source && s->previousScene2 != ws) { - s->previousScene = s->previousScene2; - s->previousScene2 = ws; + if (source && s->previousSceneHelper != ws) { + s->previousScene = s->previousSceneHelper; + s->previousSceneHelper = ws; } - //reset events only hanled on scene change - s->autoStartedRecently = false; + switcher->checkTriggers(); } void handleTransitionStop(SwitcherData *s) diff --git a/src/general.cpp b/src/general.cpp index 27e779c7..0d3ffd97 100644 --- a/src/general.cpp +++ b/src/general.cpp @@ -2,8 +2,9 @@ #include "headers/advanced-scene-switcher.hpp" #include "headers/utility.hpp" +#include "headers/version.h" -constexpr auto tab_count = 14; +constexpr auto tab_count = 15; QMetaObject::Connection inactivePluse; @@ -136,160 +137,6 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *) obs_frontend_save(); } -void AdvSceneSwitcher::on_autoStopScenes_currentTextChanged(const QString &text) -{ - if (loading) - return; - - std::lock_guard lock(switcher->m); - UpdateAutoStopScene(text); -} - -void AdvSceneSwitcher::on_autoStopSceneCheckBox_stateChanged(int state) -{ - if (loading) - return; - - std::lock_guard lock(switcher->m); - if (!state) { - ui->autoStopScenes->setDisabled(true); - ui->autoStopType->setDisabled(true); - switcher->autoStopEnable = false; - } else { - ui->autoStopScenes->setDisabled(false); - ui->autoStopType->setDisabled(false); - switcher->autoStopEnable = true; - if (!switcher->autoStopScene) - UpdateAutoStopScene(ui->autoStopScenes->currentText()); - } -} - -void AdvSceneSwitcher::on_autoStopType_currentIndexChanged(int index) -{ - if (loading) - return; - std::lock_guard lock(switcher->m); - switcher->autoStopType = (AutoStartStopType)index; -} - -void AdvSceneSwitcher::UpdateAutoStopScene(const QString &name) -{ - obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); - obs_weak_source_t *ws = obs_source_get_weak_source(scene); - - switcher->autoStopScene = ws; - - obs_weak_source_release(ws); - obs_source_release(scene); -} - -void SwitcherData::autoStopStreamAndRecording() -{ - obs_source_t *currentSource = obs_frontend_get_current_scene(); - obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); - - if (ws && autoStopScene == ws) { - if ((switcher->autoStopType == STREAMING || - switcher->autoStopType == RECORINDGSTREAMING) && - obs_frontend_streaming_active()) { - blog(LOG_INFO, - "Stopping stream because scene '%s' is active", - obs_source_get_name(currentSource)); - obs_frontend_streaming_stop(); - } - if ((switcher->autoStopType == RECORDING || - switcher->autoStopType == RECORINDGSTREAMING) && - obs_frontend_recording_active()) { - blog(LOG_INFO, - "Stopping record because scene '%s' is active", - obs_source_get_name(currentSource)); - obs_frontend_recording_stop(); - } - } - obs_source_release(currentSource); - obs_weak_source_release(ws); -} - -void AdvSceneSwitcher::on_autoStartType_currentIndexChanged(int index) -{ - if (loading) - return; - std::lock_guard lock(switcher->m); - switcher->autoStartType = (AutoStartStopType)index; -} - -void AdvSceneSwitcher::on_autoStartScenes_currentTextChanged(const QString &text) -{ - if (loading) - return; - - std::lock_guard lock(switcher->m); - UpdateAutoStartScene(text); -} - -void AdvSceneSwitcher::on_autoStartSceneCheckBox_stateChanged(int state) -{ - if (loading) - return; - - std::lock_guard lock(switcher->m); - if (!state) { - ui->autoStartScenes->setDisabled(true); - ui->autoStartType->setDisabled(true); - switcher->autoStartEnable = false; - } else { - ui->autoStartScenes->setDisabled(false); - ui->autoStartType->setDisabled(false); - switcher->autoStartEnable = true; - if (!switcher->autoStartScene) - UpdateAutoStartScene( - ui->autoStartScenes->currentText()); - } -} - -void AdvSceneSwitcher::UpdateAutoStartScene(const QString &name) -{ - obs_source_t *scene = obs_get_source_by_name(name.toUtf8().constData()); - obs_weak_source_t *ws = obs_source_get_weak_source(scene); - - switcher->autoStartScene = ws; - - obs_weak_source_release(ws); - obs_source_release(scene); -} - -void SwitcherData::autoStartStreamRecording() -{ - if (autoStartedRecently) - return; - - obs_source_t *currentSource = obs_frontend_get_current_scene(); - obs_weak_source_t *ws = obs_source_get_weak_source(currentSource); - - if (ws && autoStartScene == ws) { - if ((switcher->autoStartType == STREAMING || - switcher->autoStartType == RECORINDGSTREAMING) && - !obs_frontend_streaming_active()) { - blog(LOG_INFO, - "Starting stream because scene '%s' is active", - obs_source_get_name(currentSource)); - obs_frontend_streaming_start(); - } - if ((switcher->autoStartType == RECORDING || - switcher->autoStartType == RECORINDGSTREAMING) && - !obs_frontend_recording_active()) { - blog(LOG_INFO, - "Starting record because scene '%s' is active", - obs_source_get_name(currentSource)); - obs_frontend_recording_start(); - } - } - obs_source_release(currentSource); - obs_weak_source_release(ws); - - autoStartedRecently = true; -} - void AdvSceneSwitcher::on_verboseLogging_stateChanged(int state) { if (loading) @@ -347,20 +194,7 @@ void AdvSceneSwitcher::on_exportSettings_clicked() obs_data_t *obj = obs_data_create(); - switcher->saveWindowTitleSwitches(obj); - switcher->saveScreenRegionSwitches(obj); - switcher->savePauseSwitches(obj); - switcher->saveSceneSequenceSwitches(obj); - switcher->saveSceneTransitions(obj); - switcher->saveIdleSwitches(obj); - switcher->saveExecutableSwitches(obj); - switcher->saveRandomSwitches(obj); - switcher->saveFileSwitches(obj); - switcher->saveMediaSwitches(obj); - switcher->saveTimeSwitches(obj); - switcher->saveAudioSwitches(obj); - switcher->saveGeneralSettings(obj); - switcher->saveHotkeys(obj); + switcher->saveSettings(obj); obs_data_save_json(obj, file.fileName().toUtf8().constData()); @@ -399,20 +233,7 @@ void AdvSceneSwitcher::on_importSettings_clicked() return; } - switcher->loadWindowTitleSwitches(obj); - switcher->loadScreenRegionSwitches(obj); - switcher->loadPauseSwitches(obj); - switcher->loadSceneSequenceSwitches(obj); - switcher->loadSceneTransitions(obj); - switcher->loadIdleSwitches(obj); - switcher->loadExecutableSwitches(obj); - switcher->loadRandomSwitches(obj); - switcher->loadFileSwitches(obj); - switcher->loadMediaSwitches(obj); - switcher->loadTimeSwitches(obj); - switcher->loadAudioSwitches(obj); - switcher->loadGeneralSettings(obj); - switcher->loadHotkeys(obj); + switcher->loadSettings(obj); obs_data_release(obj); @@ -473,6 +294,9 @@ int findTabIndex(QTabWidget *tabWidget, int pos) case 13: tabName = "sceneGroupTab"; break; + case 14: + tabName = "sceneTriggerTab"; + break; } QWidget *page = tabWidget->findChild(tabName); @@ -515,6 +339,53 @@ void AdvSceneSwitcher::on_tabWidget_currentChanged(int index) SetShowFrames(); } +void SwitcherData::loadSettings(obs_data_t *obj) +{ + if (!obj) + return; + + switcher->loadSceneGroups(obj); + switcher->loadWindowTitleSwitches(obj); + switcher->loadScreenRegionSwitches(obj); + switcher->loadPauseSwitches(obj); + switcher->loadSceneSequenceSwitches(obj); + switcher->loadSceneTransitions(obj); + switcher->loadIdleSwitches(obj); + switcher->loadExecutableSwitches(obj); + switcher->loadRandomSwitches(obj); + switcher->loadFileSwitches(obj); + switcher->loadMediaSwitches(obj); + switcher->loadTimeSwitches(obj); + switcher->loadAudioSwitches(obj); + switcher->loadSceneTriggers(obj); + switcher->loadGeneralSettings(obj); + switcher->loadHotkeys(obj); +} + +void SwitcherData::saveSettings(obs_data_t *obj) +{ + if (!obj) + return; + + saveSceneGroups(obj); + saveWindowTitleSwitches(obj); + saveScreenRegionSwitches(obj); + savePauseSwitches(obj); + saveSceneSequenceSwitches(obj); + saveSceneTransitions(obj); + saveIdleSwitches(obj); + saveExecutableSwitches(obj); + saveRandomSwitches(obj); + saveFileSwitches(obj); + saveMediaSwitches(obj); + saveTimeSwitches(obj); + saveAudioSwitches(obj); + saveSceneTriggers(obj); + saveGeneralSettings(obj); + saveHotkeys(obj); + saveVersion(obj, g_GIT_SHA1); +} + void SwitcherData::saveGeneralSettings(obs_data_t *obj) { obs_data_set_int(obj, "interval", switcher->interval); @@ -532,20 +403,6 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj) obs_data_set_bool(obj, "active", !switcher->stop); obs_data_set_int(obj, "startup_behavior", switcher->startupBehavior); - std::string autoStopSceneName = - GetWeakSourceName(switcher->autoStopScene); - obs_data_set_bool(obj, "autoStopEnable", switcher->autoStopEnable); - obs_data_set_int(obj, "autoStopType", switcher->autoStopType); - obs_data_set_string(obj, "autoStopSceneName", - autoStopSceneName.c_str()); - - std::string autoStartSceneName = - GetWeakSourceName(switcher->autoStartScene); - obs_data_set_bool(obj, "autoStartEnable", switcher->autoStartEnable); - obs_data_set_int(obj, "autoStartType", switcher->autoStartType); - obs_data_set_string(obj, "autoStartSceneName", - autoStartSceneName.c_str()); - obs_data_set_bool(obj, "verbose", switcher->verbose); obs_data_set_bool(obj, "disableHints", switcher->disableHints); @@ -592,6 +449,7 @@ void SwitcherData::saveGeneralSettings(obs_data_t *obj) obs_data_set_int(obj, "sequenceTabPos", switcher->tabOrder[11]); obs_data_set_int(obj, "audioTabPos", switcher->tabOrder[12]); obs_data_set_int(obj, "sceneGroupTabPos", switcher->tabOrder[13]); + obs_data_set_int(obj, "triggerTabPos", switcher->tabOrder[14]); } void SwitcherData::loadGeneralSettings(obs_data_t *obj) @@ -618,20 +476,6 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj) if (switcher->startupBehavior == STOP) switcher->stop = true; - std::string autoStopScene = - obs_data_get_string(obj, "autoStopSceneName"); - switcher->autoStopEnable = obs_data_get_bool(obj, "autoStopEnable"); - switcher->autoStopType = - (AutoStartStopType)obs_data_get_int(obj, "autoStopType"); - switcher->autoStopScene = GetWeakSourceByName(autoStopScene.c_str()); - - std::string autoStartScene = - obs_data_get_string(obj, "autoStartSceneName"); - switcher->autoStartEnable = obs_data_get_bool(obj, "autoStartEnable"); - switcher->autoStartType = - (AutoStartStopType)obs_data_get_int(obj, "autoStartType"); - switcher->autoStartScene = GetWeakSourceByName(autoStartScene.c_str()); - switcher->verbose = obs_data_get_bool(obj, "verbose"); switcher->disableHints = obs_data_get_bool(obj, "disableHints"); @@ -693,6 +537,7 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj) obs_data_set_default_int(obj, "sequenceTabPos", 11); obs_data_set_default_int(obj, "audioTabPos", 12); obs_data_set_default_int(obj, "sceneGroupTabPos", 13); + obs_data_set_default_int(obj, "triggerTabPos", 14); switcher->tabOrder.emplace_back( (int)(obs_data_get_int(obj, "generalTabPos"))); @@ -722,6 +567,8 @@ void SwitcherData::loadGeneralSettings(obs_data_t *obj) (int)(obs_data_get_int(obj, "audioTabPos"))); switcher->tabOrder.emplace_back( (int)(obs_data_get_int(obj, "sceneGroupTabPos"))); + switcher->tabOrder.emplace_back( + (int)(obs_data_get_int(obj, "triggerTabPos"))); } void SwitcherData::checkNoMatchSwitch(bool &match, OBSWeakSource &scene, @@ -767,21 +614,9 @@ void SwitcherData::checkSwitchCooldown(bool &match) blog(LOG_INFO, "cooldown active - ignoring match"); } -void populateAutoStartStopTypeSelection(QComboBox *cb) -{ - cb->addItem(obs_module_text( - "AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recording")); - cb->addItem(obs_module_text( - "AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.streaming")); - cb->addItem(obs_module_text( - "AdvSceneSwitcher.generalTab.generalBehavior.automaticallyStart.recordingAndStreaming")); -} - void AdvSceneSwitcher::setupGeneralTab() { populateSceneSelection(ui->noMatchSwitchScene, false); - populateSceneSelection(ui->autoStopScenes, false); - populateSceneSelection(ui->autoStartScenes, false); if (switcher->switchIfNotMatching == SWITCH) { ui->noMatchSwitch->setChecked(true); @@ -804,36 +639,6 @@ void AdvSceneSwitcher::setupGeneralTab() ui->cooldownTime->setToolTip(obs_module_text( "AdvSceneSwitcher.generalTab.generalBehavior.cooldownHint")); - populateAutoStartStopTypeSelection(ui->autoStopType); - - ui->autoStopSceneCheckBox->setChecked(switcher->autoStopEnable); - ui->autoStopScenes->setCurrentText( - GetWeakSourceName(switcher->autoStopScene).c_str()); - ui->autoStopType->setCurrentIndex(switcher->autoStopType); - - if (ui->autoStopSceneCheckBox->checkState()) { - ui->autoStopScenes->setDisabled(false); - ui->autoStopType->setDisabled(false); - } else { - ui->autoStopScenes->setDisabled(true); - ui->autoStopType->setDisabled(true); - } - - populateAutoStartStopTypeSelection(ui->autoStartType); - - ui->autoStartSceneCheckBox->setChecked(switcher->autoStartEnable); - ui->autoStartScenes->setCurrentText( - GetWeakSourceName(switcher->autoStartScene).c_str()); - ui->autoStartType->setCurrentIndex(switcher->autoStartType); - - if (ui->autoStartSceneCheckBox->checkState()) { - ui->autoStartScenes->setDisabled(false); - ui->autoStartType->setDisabled(false); - } else { - ui->autoStartScenes->setDisabled(true); - ui->autoStartType->setDisabled(true); - } - ui->verboseLogging->setChecked(switcher->verbose); ui->uiHintsDisable->setChecked(switcher->disableHints); diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 838c7cec..a3176be3 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -35,8 +35,6 @@ public: int IgnoreIdleWindowsFindByData(const QString &window); void UpdateNonMatchingScene(const QString &name); - void UpdateAutoStopScene(const QString &name); - void UpdateAutoStartScene(const QString &name); void SetEditSceneGroup(SceneGroup &sg); void loadUI(); @@ -54,6 +52,7 @@ public: void setupTimeTab(); void setupAudioTab(); void setupSceneGroupTab(); + void setupTriggerTab(); void setTabOrder(); static bool DisplayMessage(QString msg, bool question = false); @@ -130,14 +129,6 @@ public slots: void on_sceneSequenceUp_clicked(); void on_sceneSequenceDown_clicked(); - void on_autoStopSceneCheckBox_stateChanged(int state); - void on_autoStopType_currentIndexChanged(int index); - void on_autoStopScenes_currentTextChanged(const QString &text); - - void on_autoStartSceneCheckBox_stateChanged(int state); - void on_autoStartType_currentIndexChanged(int index); - void on_autoStartScenes_currentTextChanged(const QString &text); - void on_verboseLogging_stateChanged(int state); void on_uiHintsDisable_stateChanged(int state); @@ -207,6 +198,11 @@ public slots: void on_sceneGroupSceneUp_clicked(); void on_sceneGroupSceneDown_clicked(); + void on_triggerAdd_clicked(); + void on_triggerRemove_clicked(); + void on_triggerUp_clicked(); + void on_triggerDown_clicked(); + void on_priorityUp_clicked(); void on_priorityDown_clicked(); void on_threadPriority_currentTextChanged(const QString &text); diff --git a/src/headers/scene-trigger.hpp b/src/headers/scene-trigger.hpp new file mode 100644 index 00000000..bc68f20a --- /dev/null +++ b/src/headers/scene-trigger.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "switch-generic.hpp" + +enum class sceneTriggerType { + NONE = 0, + SCENE_ACTIVE = 1, + SCENE_INACTIVE = 2, + SCENE_LEAVE = 3, +}; + +enum class sceneTriggerAction { + NONE = 0, + START_RECORDING, + PAUSE_RECORDING, + UNPAUSE_RECORDING, + STOP_RECORDING, + + START_STREAMING, + STOP_STREAMING, + + START_REPLAY_BUFFER, + STOP_REPLAY_BUFFER, + + MUTE_SOURCE, +}; + +struct SceneTrigger : SceneSwitcherEntry { + static bool pause; + sceneTriggerType triggerType = sceneTriggerType::NONE; + sceneTriggerAction triggerAction = sceneTriggerAction::NONE; + double duration = 0; + OBSWeakSource audioSource = nullptr; + + const char *getType() { return "trigger"; } + void save(obs_data_t *obj); + void load(obs_data_t *obj); + + bool checkMatch(OBSWeakSource previousScene); + void performAction(); + void logMatch(); +}; + +class SceneTriggerWidget : public SwitchWidget { + Q_OBJECT + +public: + SceneTriggerWidget(QWidget *parent, SceneTrigger *s); + SceneTrigger *getSwitchData(); + void setSwitchData(SceneTrigger *s); + + static void swapSwitchData(SceneTriggerWidget *s1, + SceneTriggerWidget *s2); + +private slots: + void TriggerTypeChanged(int index); + void TriggerActionChanged(int index); + void DurationChanged(double dur); + void AudioSourceChanged(const QString &text); + +private: + QComboBox *triggers; + QComboBox *actions; + QDoubleSpinBox *duration; + QComboBox *audioSources; + + SceneTrigger *switchData; +}; diff --git a/src/headers/switcher-data-structs.hpp b/src/headers/switcher-data-structs.hpp index aafa6426..7ff9cfc3 100644 --- a/src/headers/switcher-data-structs.hpp +++ b/src/headers/switcher-data-structs.hpp @@ -8,6 +8,7 @@ #include #include "scene-group.hpp" +#include "scene-trigger.hpp" #include "switch-audio.hpp" #include "switch-executable.hpp" #include "switch-file.hpp" @@ -26,11 +27,6 @@ constexpr auto previous_scene_name = "Previous Scene"; typedef enum { NO_SWITCH = 0, SWITCH = 1, RANDOM_SWITCH = 2 } NoMatch; typedef enum { PERSIST = 0, START = 1, STOP = 2 } StartupBehavior; -typedef enum { - RECORDING = 0, - STREAMING = 1, - RECORINDGSTREAMING = 2 -} AutoStartStopType; typedef struct transitionData { std::string name = ""; @@ -61,7 +57,7 @@ struct SwitcherData { obs_source_t *waitScene = nullptr; OBSWeakSource previousScene = nullptr; - OBSWeakSource previousScene2 = nullptr; + OBSWeakSource previousSceneHelper = nullptr; OBSWeakSource lastRandomScene; OBSWeakSource nonMatchingScene; NoMatch switchIfNotMatching = NO_SWITCH; @@ -92,14 +88,7 @@ struct SwitcherData { std::deque executableSwitches; - bool autoStopEnable = false; - AutoStartStopType autoStopType = RECORINDGSTREAMING; - OBSWeakSource autoStopScene; - - bool autoStartEnable = false; - AutoStartStopType autoStartType = RECORDING; - OBSWeakSource autoStartScene; - bool autoStartedRecently = false; + std::deque sceneTriggers; std::deque sceneTransitions; std::deque defaultSceneTransitions; @@ -166,8 +155,6 @@ struct SwitcherData { bool prioFuncsValid(); void writeSceneInfoToFile(); void writeToStatusFile(QString status); - void autoStopStreamAndRecording(); - void autoStartStreamRecording(); bool checkPause(); void checkDefaultSceneTransitions(bool &match, OBSWeakSource &transition); @@ -199,7 +186,9 @@ struct SwitcherData { void checkNoMatchSwitch(bool &match, OBSWeakSource &scene, OBSWeakSource &transition, int &sleep); void checkSwitchCooldown(bool &match); + void checkTriggers(); + void saveSettings(obs_data_t *obj); void saveWindowTitleSwitches(obs_data_t *obj); void saveScreenRegionSwitches(obs_data_t *obj); void savePauseSwitches(obs_data_t *obj); @@ -213,10 +202,12 @@ struct SwitcherData { void saveTimeSwitches(obs_data_t *obj); void saveAudioSwitches(obs_data_t *obj); void saveSceneGroups(obs_data_t *obj); + void saveSceneTriggers(obs_data_t *obj); void saveGeneralSettings(obs_data_t *obj); void saveHotkeys(obs_data_t *obj); void saveVersion(obs_data_t *obj, std::string currentVersion); + void loadSettings(obs_data_t *obj); void loadWindowTitleSwitches(obs_data_t *obj); void loadScreenRegionSwitches(obs_data_t *obj); void loadPauseSwitches(obs_data_t *obj); @@ -231,6 +222,7 @@ struct SwitcherData { void loadTimeSwitches(obs_data_t *obj); void loadAudioSwitches(obs_data_t *obj); void loadSceneGroups(obs_data_t *obj); + void loadSceneTriggers(obs_data_t *obj); void loadGeneralSettings(obs_data_t *obj); void loadHotkeys(obs_data_t *obj); diff --git a/src/scene-trigger.cpp b/src/scene-trigger.cpp new file mode 100644 index 00000000..1d3840fc --- /dev/null +++ b/src/scene-trigger.cpp @@ -0,0 +1,571 @@ +#include + +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +bool SceneTrigger::pause = false; +static QMetaObject::Connection addPulse; + +void AdvSceneSwitcher::on_triggerAdd_clicked() +{ + std::lock_guard lock(switcher->m); + switcher->sceneTriggers.emplace_back(); + + listAddClicked(ui->sceneTriggers, + new SceneTriggerWidget(this, + &switcher->sceneTriggers.back()), + ui->triggerAdd, &addPulse); + + ui->triggerHelp->setVisible(false); +} + +void AdvSceneSwitcher::on_triggerRemove_clicked() +{ + QListWidgetItem *item = ui->sceneTriggers->currentItem(); + if (!item) + return; + + { + std::lock_guard lock(switcher->m); + int idx = ui->sceneTriggers->currentRow(); + auto &switches = switcher->sceneTriggers; + switches.erase(switches.begin() + idx); + } + + delete item; +} + +void AdvSceneSwitcher::on_triggerUp_clicked() +{ + int index = ui->sceneTriggers->currentRow(); + if (!listMoveUp(ui->sceneTriggers)) + return; + + SceneTriggerWidget *s1 = + (SceneTriggerWidget *)ui->sceneTriggers->itemWidget( + ui->sceneTriggers->item(index)); + SceneTriggerWidget *s2 = + (SceneTriggerWidget *)ui->sceneTriggers->itemWidget( + ui->sceneTriggers->item(index - 1)); + SceneTriggerWidget::swapSwitchData(s1, s2); + + std::lock_guard lock(switcher->m); + + std::swap(switcher->sceneTriggers[index], + switcher->sceneTriggers[index - 1]); +} + +void AdvSceneSwitcher::on_triggerDown_clicked() +{ + int index = ui->sceneTriggers->currentRow(); + + if (!listMoveDown(ui->sceneTriggers)) + return; + + SceneTriggerWidget *s1 = + (SceneTriggerWidget *)ui->sceneTriggers->itemWidget( + ui->sceneTriggers->item(index)); + SceneTriggerWidget *s2 = + (SceneTriggerWidget *)ui->sceneTriggers->itemWidget( + ui->sceneTriggers->item(index + 1)); + SceneTriggerWidget::swapSwitchData(s1, s2); + + std::lock_guard lock(switcher->m); + + std::swap(switcher->sceneTriggers[index], + switcher->sceneTriggers[index + 1]); +} + +void SceneTrigger::logMatch() +{ + std::string sceneName = ""; + std::string statusName = ""; + std::string actionName = ""; + + switch (triggerType) { + case sceneTriggerType::NONE: + statusName = "NONE"; + break; + case sceneTriggerType::SCENE_ACTIVE: + statusName = "SCENE ACTIVE"; + break; + case sceneTriggerType::SCENE_INACTIVE: + statusName = "SCENE INACTIVE"; + break; + case sceneTriggerType::SCENE_LEAVE: + statusName = "SCENE LEAVE"; + break; + default: + break; + } + + switch (triggerAction) { + case sceneTriggerAction::NONE: + actionName = "NONE"; + break; + case sceneTriggerAction::START_RECORDING: + actionName = "START RECORDING"; + break; + case sceneTriggerAction::PAUSE_RECORDING: + actionName = "PAUSE RECORDING"; + break; + case sceneTriggerAction::UNPAUSE_RECORDING: + actionName = "UNPAUSE RECORDING"; + break; + case sceneTriggerAction::STOP_RECORDING: + actionName = "STOP RECORDING"; + break; + case sceneTriggerAction::START_STREAMING: + actionName = "START STREAMING"; + break; + case sceneTriggerAction::STOP_STREAMING: + actionName = "STOP STREAMING"; + break; + case sceneTriggerAction::START_REPLAY_BUFFER: + actionName = "START REPLAY BUFFER"; + break; + case sceneTriggerAction::STOP_REPLAY_BUFFER: + actionName = "STOP REPLAY BUFFER"; + break; + case sceneTriggerAction::MUTE_SOURCE: + actionName = "MUTE (" + GetWeakSourceName(audioSource) + ")"; + break; + default: + actionName = "UNKOWN"; + break; + } + + blog(LOG_INFO, + "scene '%s' in status '%s' triggering action '%s' after %f seconds", + GetWeakSourceName(scene).c_str(), statusName.c_str(), + actionName.c_str(), duration); +} + +void frontEndActionThread(sceneTriggerAction action, double delay) +{ + long long mil = delay * 1000; + std::this_thread::sleep_for(std::chrono::milliseconds(mil)); + + switch (action) { + case sceneTriggerAction::NONE: + break; + case sceneTriggerAction::START_RECORDING: + obs_frontend_recording_start(); + break; + case sceneTriggerAction::PAUSE_RECORDING: + obs_frontend_recording_pause(true); + break; + case sceneTriggerAction::UNPAUSE_RECORDING: + obs_frontend_recording_pause(false); + break; + case sceneTriggerAction::STOP_RECORDING: + obs_frontend_recording_stop(); + break; + case sceneTriggerAction::START_STREAMING: + obs_frontend_streaming_start(); + break; + case sceneTriggerAction::STOP_STREAMING: + obs_frontend_streaming_stop(); + break; + case sceneTriggerAction::START_REPLAY_BUFFER: + obs_frontend_replay_buffer_start(); + break; + case sceneTriggerAction::STOP_REPLAY_BUFFER: + obs_frontend_replay_buffer_stop(); + break; + case sceneTriggerAction::MUTE_SOURCE: + obs_frontend_replay_buffer_stop(); + break; + default: + break; + } +} + +void muteThread(OBSWeakSource source, double delay) +{ + long long mil = delay * 1000; + std::this_thread::sleep_for(std::chrono::milliseconds(mil)); + + auto s = obs_weak_source_get_source(source); + obs_source_set_muted(s, true); + obs_source_release(s); +} + +bool isFrontendAction(sceneTriggerAction triggerAction) +{ + return triggerAction == sceneTriggerAction::START_RECORDING || + triggerAction == sceneTriggerAction::PAUSE_RECORDING || + triggerAction == sceneTriggerAction::UNPAUSE_RECORDING || + triggerAction == sceneTriggerAction::STOP_RECORDING || + triggerAction == sceneTriggerAction::START_STREAMING || + triggerAction == sceneTriggerAction::STOP_STREAMING || + triggerAction == sceneTriggerAction::START_REPLAY_BUFFER || + triggerAction == sceneTriggerAction::STOP_REPLAY_BUFFER; +} + +void SceneTrigger::performAction() +{ + if (triggerAction == sceneTriggerAction::NONE) + return; + + std::thread t; + + if (isFrontendAction(triggerAction)) + t = std::thread(frontEndActionThread, triggerAction, duration); + else + t = std::thread(muteThread, audioSource, duration); + t.detach(); +} + +bool SceneTrigger::checkMatch(OBSWeakSource previousScene) +{ + OBSSource source = obs_frontend_get_current_scene(); + OBSWeakSource currentScene = obs_source_get_weak_source(source); + obs_source_release(source); + obs_weak_source_release(currentScene); + + switch (triggerType) { + case sceneTriggerType::NONE: + return false; + case sceneTriggerType::SCENE_ACTIVE: + return currentScene == scene; + case sceneTriggerType::SCENE_INACTIVE: + return currentScene != scene; + case sceneTriggerType::SCENE_LEAVE: + return previousScene == scene; + } + return false; +} + +void SwitcherData::checkTriggers() +{ + if (SceneTrigger::pause || stop) { + return; + } + + for (auto &t : sceneTriggers) { + if (t.checkMatch(previousScene)) { + t.logMatch(); + t.performAction(); + } + } +} + +void SwitcherData::saveSceneTriggers(obs_data_t *obj) +{ + obs_data_array_t *triggerArray = obs_data_array_create(); + for (auto &s : switcher->sceneTriggers) { + obs_data_t *array_obj = obs_data_create(); + + s.save(array_obj); + obs_data_array_push_back(triggerArray, array_obj); + + obs_data_release(array_obj); + } + obs_data_set_array(obj, "triggers", triggerArray); + obs_data_array_release(triggerArray); +} + +// To be removed in future version +void loadOldAutoStopStart(obs_data_t *obj) +{ + typedef enum { + RECORDING = 0, + STREAMING = 1, + RECORINDGSTREAMING = 2 + } AutoStartStopType; + + if (obs_data_get_bool(obj, "autoStopEnable")) { + std::string autoStopScene = + obs_data_get_string(obj, "autoStopSceneName"); + int action = obs_data_get_int(obj, "autoStopType"); + + if (action == RECORDING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStopScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::STOP_RECORDING; + } + + if (action == STREAMING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStopScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::STOP_STREAMING; + } + + if (action == RECORINDGSTREAMING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStopScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::STOP_RECORDING; + + switcher->sceneTriggers.emplace_back(); + auto &s2 = switcher->sceneTriggers.back(); + s2.scene = GetWeakSourceByName(autoStopScene.c_str()); + s2.triggerType = sceneTriggerType::SCENE_ACTIVE; + s2.triggerAction = sceneTriggerAction::STOP_STREAMING; + } + } + + if (obs_data_get_bool(obj, "autoStartEnable")) { + std::string autoStartScene = + obs_data_get_string(obj, "autoStartSceneName"); + int action = obs_data_get_int(obj, "autoStartType"); + + if (action == RECORDING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStartScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::START_RECORDING; + } + + if (action == STREAMING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStartScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::START_STREAMING; + } + + if (action == RECORINDGSTREAMING) { + switcher->sceneTriggers.emplace_back(); + auto &s = switcher->sceneTriggers.back(); + s.scene = GetWeakSourceByName(autoStartScene.c_str()); + s.triggerType = sceneTriggerType::SCENE_ACTIVE; + s.triggerAction = sceneTriggerAction::START_RECORDING; + + switcher->sceneTriggers.emplace_back(); + auto &s2 = switcher->sceneTriggers.back(); + s2.scene = GetWeakSourceByName(autoStartScene.c_str()); + s2.triggerType = sceneTriggerType::SCENE_ACTIVE; + s2.triggerAction = sceneTriggerAction::START_STREAMING; + } + } +} + +void SwitcherData::loadSceneTriggers(obs_data_t *obj) +{ + switcher->sceneTriggers.clear(); + + loadOldAutoStopStart(obj); + + obs_data_array_t *triggerArray = obs_data_get_array(obj, "triggers"); + size_t count = obs_data_array_count(triggerArray); + + for (size_t i = 0; i < count; i++) { + obs_data_t *array_obj = obs_data_array_item(triggerArray, i); + + switcher->sceneTriggers.emplace_back(); + sceneTriggers.back().load(array_obj); + + obs_data_release(array_obj); + } + obs_data_array_release(triggerArray); +} + +void AdvSceneSwitcher::setupTriggerTab() +{ + for (auto &s : switcher->sceneTriggers) { + QListWidgetItem *item; + item = new QListWidgetItem(ui->sceneTriggers); + ui->sceneTriggers->addItem(item); + SceneTriggerWidget *sw = new SceneTriggerWidget(this, &s); + item->setSizeHint(sw->minimumSizeHint()); + ui->sceneTriggers->setItemWidget(item, sw); + } + + if (switcher->sceneTriggers.size() == 0) { + addPulse = PulseWidget(ui->triggerAdd, QColor(Qt::green)); + ui->triggerHelp->setVisible(true); + } else { + ui->triggerHelp->setVisible(false); + } +} + +void SceneTrigger::save(obs_data_t *obj) +{ + obs_source_t *sceneSource = obs_weak_source_get_source(scene); + const char *sceneName = obs_source_get_name(sceneSource); + obs_data_set_string(obj, "scene", sceneName); + obs_source_release(sceneSource); + + obs_data_set_int(obj, "triggerType", static_cast(triggerType)); + obs_data_set_int(obj, "triggerAction", static_cast(triggerAction)); + obs_data_set_double(obj, "duration", duration); + + obs_source_t *source = obs_weak_source_get_source(audioSource); + const char *audioSourceName = obs_source_get_name(source); + obs_data_set_string(obj, "audioSource", audioSourceName); + obs_source_release(source); +} + +void SceneTrigger::load(obs_data_t *obj) +{ + const char *sceneName = obs_data_get_string(obj, "scene"); + scene = GetWeakSourceByName(sceneName); + + triggerType = static_cast( + obs_data_get_int(obj, "triggerType")); + triggerAction = static_cast( + obs_data_get_int(obj, "triggerAction")); + duration = obs_data_get_double(obj, "duration"); + + const char *audioSourceName = obs_data_get_string(obj, "audioSource"); + audioSource = GetWeakSourceByName(audioSourceName); +} + +inline void populateTriggers(QComboBox *list) +{ + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.none")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneActive")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneInactive")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerType.sceneLeave")); +} + +inline void populateActions(QComboBox *list) +{ + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.none")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startRecording")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.pauseRecording")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.unpauseRecording")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopRecording")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startStreaming")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopStreaming")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.startReplayBuffer")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.stopReplayBuffer")); + list->addItem(obs_module_text( + "AdvSceneSwitcher.sceneTriggerTab.sceneTriggerAction.muteSource")); +} + +SceneTriggerWidget::SceneTriggerWidget(QWidget *parent, SceneTrigger *s) + : SwitchWidget(parent, s, false, false) +{ + triggers = new QComboBox(); + actions = new QComboBox(); + duration = new QDoubleSpinBox(); + audioSources = new QComboBox(); + + duration->setMinimum(0.0); + duration->setMaximum(99.000000); + duration->setSuffix("s"); + + QWidget::connect(triggers, SIGNAL(currentIndexChanged(int)), this, + SLOT(TriggerTypeChanged(int))); + QWidget::connect(actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(TriggerActionChanged(int))); + QWidget::connect(duration, SIGNAL(valueChanged(double)), this, + SLOT(DurationChanged(double))); + QWidget::connect(audioSources, + SIGNAL(currentTextChanged(const QString &)), this, + SLOT(AudioSourceChanged(const QString &))); + + populateTriggers(triggers); + populateActions(actions); + AdvSceneSwitcher::populateAudioSelection(audioSources); + + if (s) { + triggers->setCurrentIndex(static_cast(s->triggerType)); + actions->setCurrentIndex(static_cast(s->triggerAction)); + duration->setValue(s->duration); + + audioSources->setCurrentText( + GetWeakSourceName(s->audioSource).c_str()); + if (s->triggerAction == sceneTriggerAction::MUTE_SOURCE) + audioSources->show(); + else + audioSources->hide(); + } + + QHBoxLayout *mainLayout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{triggers}}", triggers}, + {"{{actions}}", actions}, + {"{{audioSources}}", audioSources}, + {"{{duration}}", duration}, + {"{{scenes}}", scenes}}; + placeWidgets(obs_module_text("AdvSceneSwitcher.sceneTriggerTab.entry"), + mainLayout, widgetPlaceholders); + setLayout(mainLayout); + + switchData = s; + + loading = false; +} + +SceneTrigger *SceneTriggerWidget::getSwitchData() +{ + return switchData; +} + +void SceneTriggerWidget::setSwitchData(SceneTrigger *s) +{ + switchData = s; +} + +void SceneTriggerWidget::swapSwitchData(SceneTriggerWidget *s1, + SceneTriggerWidget *s2) +{ + SwitchWidget::swapSwitchData(s1, s2); + + SceneTrigger *t = s1->getSwitchData(); + s1->setSwitchData(s2->getSwitchData()); + s2->setSwitchData(t); +} + +void SceneTriggerWidget::TriggerTypeChanged(int index) +{ + if (loading || !switchData) + return; + std::lock_guard lock(switcher->m); + switchData->triggerType = static_cast(index); +} + +void SceneTriggerWidget::TriggerActionChanged(int index) +{ + if (loading || !switchData) + return; + { + std::lock_guard lock(switcher->m); + switchData->triggerAction = + static_cast(index); + } + + if (switchData->triggerAction == sceneTriggerAction::MUTE_SOURCE) + audioSources->show(); + else + audioSources->hide(); +} + +void SceneTriggerWidget::DurationChanged(double dur) +{ + if (loading || !switchData) + return; + std::lock_guard lock(switcher->m); + switchData->duration = dur; +} + +void SceneTriggerWidget::AudioSourceChanged(const QString &text) +{ + if (loading || !switchData) + return; + std::lock_guard lock(switcher->m); + switchData->audioSource = GetWeakSourceByQString(text); +} diff --git a/src/switch-time.cpp b/src/switch-time.cpp index 411f16f8..46d20708 100644 --- a/src/switch-time.cpp +++ b/src/switch-time.cpp @@ -229,7 +229,7 @@ void TimeSwitch::load(obs_data_t *obj) time = QTime::fromString(obs_data_get_string(obj, "time")); } -void populateTriggers(QComboBox *list) +inline void populateTriggers(QComboBox *list) { list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.anyDay")); list->addItem(obs_module_text("AdvSceneSwitcher.timeTab.mondays")); diff --git a/src/switcher-data-structs.cpp b/src/switcher-data-structs.cpp index d41b841e..282fdc0e 100644 --- a/src/switcher-data-structs.cpp +++ b/src/switcher-data-structs.cpp @@ -40,11 +40,6 @@ void SwitcherData::Prune() sceneSequenceSwitches.begin() + i--); } - if (!WeakSourceValid(autoStopScene)) { - autoStopScene = nullptr; - autoStopEnable = false; - } - for (size_t i = 0; i < sceneTransitions.size(); i++) { SceneTransition &s = sceneTransitions[i]; if (!s.valid())