diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 4632fd7d..d5d20f1e 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -175,7 +175,9 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="Wähle eine Datei aus von der Szene AdvSceneSwitcher.sceneSequenceTab.loadFail="Laden der Einstellungen ist fehlgeschlagen!" AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Laden der Einstellungen war erfolgreich!" AdvSceneSwitcher.sceneSequenceTab.fileType="Text Dateien (*.txt)" -AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}}" +AdvSceneSwitcher.sceneSequenceTab.interruptible="kann unterbrochen werden" +AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Andere Szenenwechselmethoden können diese Sequenz unterbrechen" +AdvSceneSwitcher.sceneSequenceTab.entry="Wenn {{startScenes}} aktiv ist wechsle zu {{scenes}} nach {{delay}} {{delayUnits}} mit {{transitions}} {{interruptible}}" ; Audio Tab AdvSceneSwitcher.audioTab.title="Audio" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 8938fb8f..237a5713 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -175,7 +175,9 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="Select a file to read Scene Sequenc AdvSceneSwitcher.sceneSequenceTab.loadFail="Advanced Scene Switcher failed to import settings!" AdvSceneSwitcher.sceneSequenceTab.loadSuccess="Advanced Scene Switcher settings imported successfully!" AdvSceneSwitcher.sceneSequenceTab.fileType="Text files (*.txt)" -AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}}" +AdvSceneSwitcher.sceneSequenceTab.interruptible="interruptible" +AdvSceneSwitcher.sceneSequenceTab.interruptibleHint="Other switching methods are allowed to interrupt this scene sequence" +AdvSceneSwitcher.sceneSequenceTab.entry="When {{startScenes}} is active switch to {{scenes}} after {{delay}} {{delayUnits}} using {{transitions}} {{interruptible}}" ; Audio Tab AdvSceneSwitcher.audioTab.title="Audio" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 73f05d52..230dfc62 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -171,7 +171,7 @@ AdvSceneSwitcher.sceneSequenceTab.loadTitle="从文件加载序列 ..." AdvSceneSwitcher.sceneSequenceTab.loadFail="导入失败" AdvSceneSwitcher.sceneSequenceTab.loadSuccess="导入失败成功" AdvSceneSwitcher.sceneSequenceTab.fileType="文本文件 (*.txt)" -AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} {{delayUnits}}后使用转场特效 {{transitions}} 切换到场景 {{scenes}}" +AdvSceneSwitcher.sceneSequenceTab.entry="当场景 {{startScenes}} 被激活 {{delay}} {{delayUnits}}后使用转场特效 {{transitions}} 切换到场景 {{scenes}} {{interruptible}}" ; Audio Tab AdvSceneSwitcher.audioTab.title="音频" diff --git a/src/headers/switch-sequence.hpp b/src/headers/switch-sequence.hpp index d8db9ef1..fc55f5cb 100644 --- a/src/headers/switch-sequence.hpp +++ b/src/headers/switch-sequence.hpp @@ -15,6 +15,8 @@ struct SceneSequenceSwitch : SceneSwitcherEntry { OBSWeakSource startScene = nullptr; double delay = 0; int delayMultiplier = 1; + bool interruptible = false; + unsigned int matchCount = 0; const char *getType() { return "sequence"; } bool initialized(); @@ -25,11 +27,13 @@ struct SceneSequenceSwitch : SceneSwitcherEntry { inline SceneSequenceSwitch(OBSWeakSource startScene_, OBSWeakSource scene_, OBSWeakSource transition_, double delay_, - int delayMultiplier_, bool usePreviousScene_) + int delayMultiplier_, bool interruptible_, + bool usePreviousScene_) : SceneSwitcherEntry(scene_, transition_, usePreviousScene_), startScene(startScene_), delay(delay_), - delayMultiplier(delayMultiplier_) + delayMultiplier(delayMultiplier_), + interruptible(interruptible_) { } }; @@ -50,11 +54,13 @@ private slots: void DelayChanged(double delay); void DelayUnitsChanged(int idx); void StartSceneChanged(const QString &text); + void InterruptibleChanged(int state); private: QDoubleSpinBox *delay; QComboBox *delayUnits; QComboBox *startScenes; + QCheckBox *interruptible; SceneSequenceSwitch *switchData; }; diff --git a/src/switch-sequence.cpp b/src/switch-sequence.cpp index 080e433e..daa67a2e 100644 --- a/src/switch-sequence.cpp +++ b/src/switch-sequence.cpp @@ -153,6 +153,59 @@ void AdvSceneSwitcher::on_sceneSequenceLoad_clicked() close(); } +void matchInterruptible(SwitcherData *switcher, SceneSequenceSwitch &s, + bool &match, OBSWeakSource &scene, + OBSWeakSource &transition) +{ + bool durationReached = s.matchCount * (switcher->interval / 1000.0) >= + s.delay; + + s.matchCount++; + + if (durationReached) { + match = true; + scene = (s.usePreviousScene) ? switcher->previousScene + : s.scene; + transition = s.transition; + s.matchCount = 0; + if (switcher->verbose) + s.logMatch(); + } +} + +void matchUninterruptible(SwitcherData *switcher, SceneSequenceSwitch &s, + obs_source_t *currentSource, + std::unique_lock &lock, bool &match, + OBSWeakSource &scene, OBSWeakSource &transition) +{ + // scene was already active for the previous cycle so remove this time + int dur = s.delay * 1000 - switcher->interval; + if (dur > 0) { + switcher->waitScene = currentSource; + + if (switcher->verbose) + s.logSleep(dur); + + switcher->cv.wait_for(lock, std::chrono::milliseconds(dur)); + switcher->waitScene = nullptr; + } + obs_source_t *currentSource2 = obs_frontend_get_current_scene(); + + // only switch if user hasn't changed scene manually + if (currentSource == currentSource2) { + match = true; + scene = (s.usePreviousScene) ? switcher->previousScene + : s.scene; + transition = s.transition; + if (switcher->verbose) + s.logMatch(); + } else if (switcher->verbose) { + blog(LOG_INFO, "sequence canceled"); + } + + obs_source_release(currentSource2); +} + void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene, OBSWeakSource &transition, std::unique_lock &lock) @@ -168,34 +221,20 @@ void SwitcherData::checkSceneSequence(bool &match, OBSWeakSource &scene, continue; if (s.startScene == ws) { - int dur = s.delay * 1000 - interval; - if (dur > 0) { - waitScene = currentSource; - if (verbose) - s.logSleep(dur); - - cv.wait_for(lock, - std::chrono::milliseconds(dur)); - waitScene = nullptr; - } - obs_source_t *currentSource2 = - obs_frontend_get_current_scene(); - - // only switch if user hasn't changed scene manually - if (currentSource == currentSource2) { - match = true; - scene = (s.usePreviousScene) ? previousScene - : s.scene; - transition = s.transition; - if (verbose) - s.logMatch(); - } else if (verbose) { - blog(LOG_INFO, "sequence canceled"); + if (s.interruptible) { + matchInterruptible(switcher, s, match, scene, + transition); + } else { + matchUninterruptible(switcher, s, currentSource, + lock, match, scene, + transition); } - obs_source_release(currentSource2); - break; + if (match) + break; + } else { + s.matchCount = 0; } } obs_source_release(currentSource); @@ -229,6 +268,8 @@ void SwitcherData::saveSceneSequenceSwitches(obs_data_t *obj) obs_data_set_double(array_obj, "delay", s.delay); obs_data_set_int(array_obj, "delayMultiplier", s.delayMultiplier); + obs_data_set_bool(array_obj, "interruptible", + s.interruptible); obs_data_array_push_back(sceneSequenceArray, array_obj); } @@ -265,12 +306,14 @@ void SwitcherData::loadSceneSequenceSwitches(obs_data_t *obj) if (delayMultiplier == 0 || (delayMultiplier != 1 && delayMultiplier % 60 != 0)) delayMultiplier = 1; + bool interruptible = + obs_data_get_bool(array_obj, "interruptible"); switcher->sceneSequenceSwitches.emplace_back( GetWeakSourceByName(scene1), GetWeakSourceByName(scene2), GetWeakTransitionByName(transition), delay, - delayMultiplier, + delayMultiplier, interruptible, (strcmp(scene2, previous_scene_name) == 0)); obs_data_release(array_obj); @@ -321,6 +364,8 @@ SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s) delay = new QDoubleSpinBox(); delayUnits = new QComboBox(); startScenes = new QComboBox(); + interruptible = new QCheckBox(obs_module_text( + "AdvSceneSwitcher.sceneSequenceTab.interruptible")); QWidget::connect(delay, SIGNAL(valueChanged(double)), this, SLOT(DelayChanged(double))); @@ -329,10 +374,14 @@ SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s) QWidget::connect(startScenes, SIGNAL(currentTextChanged(const QString &)), this, SLOT(StartSceneChanged(const QString &))); + QWidget::connect(interruptible, SIGNAL(stateChanged(int)), this, + SLOT(InterruptibleChanged(int))); delay->setMaximum(99999.000000); AdvSceneSwitcher::populateSceneSelection(startScenes, false); populateDelayUnits(delayUnits); + interruptible->setToolTip(obs_module_text( + "AdvSceneSwitcher.sceneSequenceTab.interruptibleHint")); if (s) { switch (s->delayMultiplier) { @@ -350,6 +399,7 @@ SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s) } startScenes->setCurrentText( GetWeakSourceName(s->startScene).c_str()); + interruptible->setChecked(s->interruptible); } QHBoxLayout *mainLayout = new QHBoxLayout; @@ -358,7 +408,8 @@ SequenceWidget::SequenceWidget(SceneSequenceSwitch *s) : SwitchWidget(s) {"{{scenes}}", scenes}, {"{{delay}}", delay}, {"{{delayUnits}}", delayUnits}, - {"{{transitions}}", transitions}}; + {"{{transitions}}", transitions}, + {"{{interruptible}}", interruptible}}; placeWidgets(obs_module_text("AdvSceneSwitcher.sceneSequenceTab.entry"), mainLayout, widgetPlaceholders); setLayout(mainLayout); @@ -434,3 +485,11 @@ void SequenceWidget::StartSceneChanged(const QString &text) std::lock_guard lock(switcher->m); switchData->startScene = GetWeakSourceByQString(text); } + +void SequenceWidget::InterruptibleChanged(int state) +{ + if (loading || !switchData) + return; + std::lock_guard lock(switcher->m); + switchData->interruptible = state; +}