From 30d5ffa32fc17bcbd7d06894bad88fdbaebbc946 Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Wed, 24 Jul 2019 21:20:18 +0200 Subject: [PATCH 1/2] engine: attempt to do Scene crossfade without using blend modes --- engine/src/chaser.cpp | 33 +++--- engine/src/chaser.h | 7 +- engine/src/chaseraction.h | 3 +- engine/src/chaserrunner.cpp | 105 ++++++++++++------ engine/src/chaserrunner.h | 9 +- engine/src/fadechannel.h | 3 +- engine/src/genericfader.cpp | 52 ++++++--- engine/src/genericfader.h | 7 ++ engine/src/scene.cpp | 58 +++++++++- engine/src/scene.h | 20 +++- engine/test/chaser/chaser_test.cpp | 8 +- .../test/chaserrunner/chaserrunner_test.cpp | 14 +-- qmlui/virtualconsole/vccuelist.cpp | 19 ++-- ui/src/chasereditor.cpp | 9 +- ui/src/virtualconsole/vccuelist.cpp | 25 +++-- ui/src/virtualconsole/vcframe.cpp | 2 + 16 files changed, 261 insertions(+), 113 deletions(-) diff --git a/engine/src/chaser.cpp b/engine/src/chaser.cpp index 6bdd60ddd5..36ca482d31 100644 --- a/engine/src/chaser.cpp +++ b/engine/src/chaser.cpp @@ -62,7 +62,8 @@ Chaser::Chaser(Doc *doc) this, SLOT(slotFunctionRemoved(quint32))); m_startupAction.m_action = ChaserNoAction; - m_startupAction.m_intensity = 1.0; + m_startupAction.m_masterIntensity = 1.0; + m_startupAction.m_stepIntensity = 1.0; m_startupAction.m_fadeMode = FromFunction; m_startupAction.m_stepIndex = -1; } @@ -498,7 +499,8 @@ void Chaser::setAction(ChaserAction &action) { m_startupAction.m_action = action.m_action; m_startupAction.m_stepIndex = action.m_stepIndex; - m_startupAction.m_intensity = action.m_intensity; + m_startupAction.m_masterIntensity = action.m_masterIntensity; + m_startupAction.m_stepIntensity = action.m_stepIntensity; m_startupAction.m_fadeMode = action.m_fadeMode; } } @@ -553,19 +555,6 @@ ChaserRunnerStep Chaser::currentRunningStep() const return ret; } -void Chaser::adjustStepIntensity(qreal fraction, int stepIndex, FadeControlMode fadeControl) -{ - QMutexLocker runnerLocker(&m_runnerMutex); - if (m_runner != NULL) - { - m_runner->adjustStepIntensity(fraction * getAttributeValue(Intensity), stepIndex, fadeControl); - } - else - { - m_startupAction.m_intensity = fraction * getAttributeValue(Intensity); - } -} - bool Chaser::contains(quint32 functionId) { Doc *doc = this->doc(); @@ -683,3 +672,17 @@ int Chaser::adjustAttribute(qreal fraction, int attributeId) return attrIndex; } + +void Chaser::adjustStepIntensity(qreal fraction, int stepIndex, FadeControlMode fadeControl) +{ + QMutexLocker runnerLocker(&m_runnerMutex); + if (m_runner != NULL) + { + m_runner->adjustStepIntensity(fraction, stepIndex, fadeControl); + } + else + { + m_startupAction.m_masterIntensity = getAttributeValue(Function::Intensity); + m_startupAction.m_stepIntensity = fraction; + } +} diff --git a/engine/src/chaser.h b/engine/src/chaser.h index 090a116fe5..f50b192e13 100644 --- a/engine/src/chaser.h +++ b/engine/src/chaser.h @@ -238,9 +238,6 @@ public slots: /** Get the first step of the running list. If none is running this returns NULL */ ChaserRunnerStep currentRunningStep() const; - /** Adjust the intensities of chaser steps. */ - void adjustStepIntensity(qreal fraction, int stepIndex = -1, FadeControlMode fadeControl = FromFunction); - private: ChaserAction m_startupAction; @@ -292,6 +289,10 @@ public slots: public: /** @reimp */ int adjustAttribute(qreal fraction, int attributeId); + + /** Adjust the intensities of chaser steps. */ + void adjustStepIntensity(qreal fraction, int stepIndex = -1, + FadeControlMode fadeControl = FromFunction); }; /** @} */ diff --git a/engine/src/chaseraction.h b/engine/src/chaseraction.h index 75dfeb293d..b46e0fb602 100644 --- a/engine/src/chaseraction.h +++ b/engine/src/chaseraction.h @@ -34,7 +34,8 @@ enum ChaserActionType typedef struct { ChaserActionType m_action; - qreal m_intensity; + qreal m_masterIntensity; + qreal m_stepIntensity; int m_fadeMode; int m_stepIndex; } ChaserAction; diff --git a/engine/src/chaserrunner.cpp b/engine/src/chaserrunner.cpp index b48af3deab..c932a0ea73 100644 --- a/engine/src/chaserrunner.cpp +++ b/engine/src/chaserrunner.cpp @@ -40,13 +40,15 @@ ChaserRunner::ChaserRunner(const Doc *doc, const Chaser *chaser, quint32 startTi , m_updateOverrideSpeeds(false) , m_startOffset(0) , m_lastRunStepIdx(-1) + , m_lastFunctionID(Function::invalidId()) , m_roundTime(new QElapsedTimer()) , m_order() { Q_ASSERT(chaser != NULL); m_pendingAction.m_action = ChaserNoAction; - m_pendingAction.m_intensity = 1.0; + m_pendingAction.m_masterIntensity = 1.0; + m_pendingAction.m_stepIntensity = 1.0; m_pendingAction.m_fadeMode = Chaser::FromFunction; m_pendingAction.m_stepIndex = -1; @@ -232,7 +234,8 @@ void ChaserRunner::setAction(ChaserAction &action) switch (action.m_action) { case ChaserNoAction: - m_pendingAction.m_intensity = action.m_intensity; + m_pendingAction.m_masterIntensity = action.m_masterIntensity; + m_pendingAction.m_stepIntensity = action.m_stepIntensity; break; case ChaserStopStep: @@ -244,9 +247,8 @@ void ChaserRunner::setAction(ChaserAction &action) if (action.m_stepIndex == step->m_index) { qDebug() << "Stopping step idx:" << action.m_stepIndex << "(running:" << m_runnerSteps.count() << ")"; + m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId(); step->m_function->stop(functionParent()); - // restore the original Function blend mode - step->m_function->setBlendMode(step->m_blendMode); m_runnerSteps.removeOne(step); delete step; stopped = true; @@ -257,10 +259,6 @@ void ChaserRunner::setAction(ChaserAction &action) { ChaserRunnerStep *lastStep = m_runnerSteps.at(0); m_lastRunStepIdx = lastStep->m_index; - // when only one step remains in the running list, - // it has to run with its original blend mode - if (lastStep->m_function) - lastStep->m_function->setBlendMode(lastStep->m_blendMode); emit currentStepChanged(m_lastRunStepIdx); } } @@ -269,7 +267,8 @@ void ChaserRunner::setAction(ChaserAction &action) // copy to pending action. Will be processed at the next write call default: m_pendingAction.m_stepIndex = action.m_stepIndex; - m_pendingAction.m_intensity = action.m_intensity; + m_pendingAction.m_masterIntensity = action.m_masterIntensity; + m_pendingAction.m_stepIntensity = action.m_stepIntensity; m_pendingAction.m_fadeMode = action.m_fadeMode; m_pendingAction.m_action = action.m_action; break; @@ -415,26 +414,29 @@ void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, i { fraction = CLAMP(fraction, qreal(0.0), qreal(1.0)); - //qDebug() << "Adjust intensity" << fraction << "step:" << requestedStepIndex << "fade:" << fadeControl; + qDebug() << "Adjust intensity" << fraction << "step:" << requestedStepIndex << "fade:" << fadeControl; int stepIndex = requestedStepIndex; if (stepIndex == -1) { stepIndex = m_lastRunStepIdx; - // store the intensity to be applied - // at the next step startup - m_pendingAction.m_intensity = fraction; + // store the intensity to be applied at the next step startup + m_pendingAction.m_masterIntensity = fraction; } foreach(ChaserRunnerStep *step, m_runnerSteps) { if (stepIndex == step->m_index && step->m_function != NULL) { - if (fadeControl == Chaser::BlendedCrossfade && fraction != 1.0) - step->m_function->setBlendMode(Universe::AdditiveBlend); + if (requestedStepIndex == -1 && step->m_function->type() == Function::SceneType) + { + Scene *scene = qobject_cast(step->m_function); + scene->adjustAttribute(fraction, step->m_pIntensityOverrideId); + } else - step->m_function->setBlendMode(step->m_blendMode); - step->m_function->adjustAttribute(fraction, step->m_intensityOverrideId); + { + step->m_function->adjustAttribute(fraction, step->m_intensityOverrideId); + } return; } } @@ -448,9 +450,13 @@ void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, i return; // not found ? It means we need to start a new step and crossfade kicks in ! - startNewStep(stepIndex, m_doc->masterTimer(), fraction, fadeControl); + startNewStep(stepIndex, m_doc->masterTimer(), m_pendingAction.m_masterIntensity, fraction, fadeControl); } +/**************************************************************************** + * Running + ****************************************************************************/ + void ChaserRunner::clearRunningList() { // empty the running queue @@ -458,21 +464,17 @@ void ChaserRunner::clearRunningList() { if (step->m_function) { - // restore the original Function blend mode and fade out time - step->m_function->setBlendMode(step->m_blendMode); + // restore the original Function fade out time step->m_function->setOverrideFadeOutSpeed(stepFadeOut(step->m_index)); step->m_function->stop(functionParent()); + m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId(); } delete step; } m_runnerSteps.clear(); } -/**************************************************************************** - * Running - ****************************************************************************/ - -void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal intensity, +void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal mIntensity, qreal sIntensity, int fadeControl, quint32 elapsed) { if (m_chaser == NULL || m_chaser->stepsCount() == 0) @@ -488,7 +490,29 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal intensity, ChaserRunnerStep *newStep = new ChaserRunnerStep(); newStep->m_index = index; - newStep->m_blendMode = func->blendMode(); + + // check if blending between Scenes is needed + if (m_lastFunctionID != Function::invalidId() && + func->type() == Function::SceneType) + { + Scene *scene = qobject_cast(func); + scene->setBlendFunctionID(m_lastFunctionID); + } + + // this happens only during crossfades + if (m_runnerSteps.count()) + { + ChaserRunnerStep *lastStep = m_runnerSteps.last(); + if (lastStep->m_function && + lastStep->m_function->type() == Function::SceneType && + func->type() == Function::SceneType) + { + Scene *lastScene = qobject_cast(lastStep->m_function); + lastScene->setBlendFunctionID(Function::invalidId()); + Scene *scene = qobject_cast(func); + scene->setBlendFunctionID(lastStep->m_function->id()); + } + } switch (fadeControl) { @@ -499,8 +523,6 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal intensity, case Chaser::Blended: newStep->m_fadeIn = stepFadeIn(index); newStep->m_fadeOut = stepFadeOut(index); - if (newStep->m_fadeIn) - func->setBlendMode(Universe::AdditiveBlend); break; case Chaser::Crossfade: newStep->m_fadeIn = 0; @@ -509,7 +531,6 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal intensity, case Chaser::BlendedCrossfade: newStep->m_fadeIn = 0; newStep->m_fadeOut = 0; - func->setBlendMode(Universe::AdditiveBlend); break; } @@ -535,15 +556,26 @@ void ChaserRunner::startNewStep(int index, MasterTimer *timer, qreal intensity, } qDebug() << "Starting step" << index << "fade in" << newStep->m_fadeIn - << "fade out" << newStep->m_fadeOut << "intensity" << intensity + << "fade out" << newStep->m_fadeOut << "intensity" << mIntensity << "fadeMode" << fadeControl; // Set intensity before starting the function. Otherwise the intensity // might momentarily jump too high. - newStep->m_intensityOverrideId = newStep->m_function->requestAttributeOverride(Function::Intensity, intensity); + if (func->type() == Function::SceneType) + { + Scene *scene = qobject_cast(func); + newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, sIntensity); + newStep->m_pIntensityOverrideId = scene->requestAttributeOverride(Scene::ParentIntensity, mIntensity); + qDebug() << "Set step intensity:" << sIntensity << ", master:" << mIntensity; + } + else + { + newStep->m_intensityOverrideId = func->requestAttributeOverride(Function::Intensity, mIntensity * sIntensity); + } + // Start the fire up ! - newStep->m_function->start(timer, functionParent(), 0, newStep->m_fadeIn, newStep->m_fadeOut, - newStep->m_function->defaultSpeed(), m_chaser->tempoType()); + func->start(timer, functionParent(), 0, newStep->m_fadeIn, newStep->m_fadeOut, + func->defaultSpeed(), m_chaser->tempoType()); m_runnerSteps.append(newStep); m_roundTime->restart(); } @@ -694,7 +726,8 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) clearRunningList(); m_lastRunStepIdx = m_pendingAction.m_stepIndex; qDebug() << "Starting from step" << m_lastRunStepIdx << "@ offset" << m_startOffset; - startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_intensity, m_pendingAction.m_fadeMode); + startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_masterIntensity, + m_pendingAction.m_stepIntensity, m_pendingAction.m_fadeMode); emit currentStepChanged(m_lastRunStepIdx); } break; @@ -719,6 +752,7 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) if (step->m_duration != 0) prevStepRoundElapsed = step->m_elapsed % step->m_duration; + m_lastFunctionID = step->m_function->type() == Function::SceneType ? step->m_function->id() : Function::invalidId(); step->m_function->stop(functionParent(), m_chaser->type() == Function::SequenceType); delete step; m_runnerSteps.removeOne(step); @@ -750,7 +784,8 @@ bool ChaserRunner::write(MasterTimer *timer, QList universes) { int blend = m_pendingAction.m_action == ChaserNoAction ? Chaser::FromFunction : m_pendingAction.m_fadeMode; - startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_intensity, blend, prevStepRoundElapsed); + startNewStep(m_lastRunStepIdx, timer, m_pendingAction.m_masterIntensity, + m_pendingAction.m_stepIntensity, blend, prevStepRoundElapsed); emit currentStepChanged(m_lastRunStepIdx); } else diff --git a/engine/src/chaserrunner.h b/engine/src/chaserrunner.h index 0e31a481de..d9e7903776 100644 --- a/engine/src/chaserrunner.h +++ b/engine/src/chaserrunner.h @@ -51,6 +51,7 @@ typedef struct uint m_duration; //! Step hold in ms Universe::BlendMode m_blendMode; //! The original Function blend mode int m_intensityOverrideId; //! An ID to control the step intensity + int m_pIntensityOverrideId; //! An ID to control the step parent intensity } ChaserRunnerStep; class ChaserRunner : public QObject @@ -154,6 +155,7 @@ private slots: quint32 m_startOffset; //! Start step offset time in milliseconds ChaserAction m_pendingAction; //! Action to be performed on steps at the next write call int m_lastRunStepIdx; //! Index of the last step ran + quint32 m_lastFunctionID; //! ID of the last Function ran (Scene only) QElapsedTimer *m_roundTime; //! Counts the time between steps QVector m_order; //! Array of step indices in a randomized order @@ -174,8 +176,8 @@ private slots: void clearRunningList(); /** - * Start a Chaser step Function with the given $index, at the given $intensity - * and from the given $elapsed time. + * Start a Chaser step Function with the given $index, at the given + * master and step intensity and from the given $elapsed time. * $fadeControl specifies how the step Function should fade, according to * the Chaser::FadeControlMode enumeration: * - Chaser::FromFunction will use the original Function fadeIn time @@ -184,7 +186,8 @@ private slots: * - Chaser::LinkedCrossfade is like Crossfade, and the Function will also be requested * to use the Universe::AdditiveBlend mode */ - void startNewStep(int index, MasterTimer *timer, qreal intensity, int fadeControl, quint32 elapsed = 0); + void startNewStep(int index, MasterTimer *timer, qreal mIntensity, qreal sIntensity, + int fadeControl, quint32 elapsed = 0); /** * Get the index of the next step that should be started, diff --git a/engine/src/fadechannel.h b/engine/src/fadechannel.h index 194c93a638..c72bb8e418 100644 --- a/engine/src/fadechannel.h +++ b/engine/src/fadechannel.h @@ -51,7 +51,8 @@ class FadeChannel Flashing = (1 << 4), /** Is flashing */ Relative = (1 << 5), /** Relative position */ Override = (1 << 6), /** Override the current universe value */ - Autoremove = (1 << 7) /** Automatically remove the channel once value is written */ + Autoremove = (1 << 7), /** Automatically remove the channel once value is written */ + CrossFade = (1 << 8) /** Channel subject to crossfade */ }; /** Create a new FadeChannel with empty/invalid values */ diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index c052f0c456..2d6f459da6 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -29,6 +29,7 @@ GenericFader::GenericFader(QObject *parent) , m_fid(Function::invalidId()) , m_priority(Universe::Auto) , m_intensity(1.0) + , m_parentIntensity(1.0) , m_paused(false) , m_enabled(true) , m_fadeOut(false) @@ -156,6 +157,8 @@ void GenericFader::write(Universe *universe) if (m_monitoring) emit preWriteData(universe->id(), universe->preGMValues()); + qreal compIntensity = intensity() * parentIntensity(); + QMutableHashIterator it(m_channels); while (it.hasNext() == true) { @@ -163,7 +166,6 @@ void GenericFader::write(Universe *universe) int flags = fc.flags(); int address = int(fc.addressInUniverse()); uchar value; - Universe::BlendMode blendMode = m_blendMode; // Calculate the next step if (m_paused) @@ -174,25 +176,20 @@ void GenericFader::write(Universe *universe) // Apply intensity to channels that can fade if (fc.canFade()) { - if (flags & FadeChannel::Intensity) + if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) { - value = fc.current(intensity()); + if (flags & FadeChannel::LTP) + value = uchar(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity()); + else + value = qreal(fc.target()) * parentIntensity(); } - else if (blendMode != Universe::NormalBlend && - fc.fadeTime() == 0 && (flags & FadeChannel::LTP)) + else if (flags & FadeChannel::Intensity) { - // this translates into: LTP + crossfade. - // Value is proportional between start and target, depending on intensity - value = uchar((qreal(fc.target() - fc.start()) * intensity()) + fc.start()); + value = fc.current(compIntensity); } } - // LTP non intensity channels must use normal blending, otherwise they - // will be added up in case of additive blending - if ((flags & FadeChannel::LTP) && (flags & FadeChannel::Intensity) == 0) - blendMode = Universe::NormalBlend; - - //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value; + //qDebug() << "[GenericFader] >>> uni:" << universe->id() << ", address:" << address << ", value:" << value << "int:" << compIntensity; if (flags & FadeChannel::Override) { universe->write(address, value, true); @@ -204,12 +201,12 @@ void GenericFader::write(Universe *universe) } else { - universe->writeBlended(address, value, blendMode); + universe->writeBlended(address, value, m_blendMode); } if (((flags & FadeChannel::Intensity) && (flags & FadeChannel::HTP) && - blendMode == Universe::NormalBlend) || m_fadeOut) + m_blendMode == Universe::NormalBlend) || m_fadeOut) { // Remove all channels that reach their target _zero_ value. // They have no effect either way so removing them saves a bit of CPU. @@ -232,9 +229,21 @@ qreal GenericFader::intensity() const void GenericFader::adjustIntensity(qreal fraction) { + qDebug() << name() << "I FADER intensity" << fraction << ", PARENT:" << m_parentIntensity; m_intensity = fraction; } +qreal GenericFader::parentIntensity() const +{ + return m_parentIntensity; +} + +void GenericFader::setParentIntensity(qreal fraction) +{ + qDebug() << name() << "P FADER intensity" << m_intensity << ", PARENT:" << fraction; + m_parentIntensity = fraction; +} + bool GenericFader::isPaused() const { return m_paused; @@ -292,3 +301,14 @@ void GenericFader::setMonitoring(bool enable) { m_monitoring = enable; } + +void GenericFader::resetCrossfade() +{ + qDebug() << name() << "resetting crossfade channels"; + QMutableHashIterator it(m_channels); + while (it.hasNext() == true) + { + FadeChannel& fc(it.next().value()); + fc.removeFlag(FadeChannel::CrossFade); + } +} diff --git a/engine/src/genericfader.h b/engine/src/genericfader.h index 95182b8027..7eba714329 100644 --- a/engine/src/genericfader.h +++ b/engine/src/genericfader.h @@ -108,6 +108,10 @@ class GenericFader : public QObject qreal intensity() const; void adjustIntensity(qreal fraction); + /** Get/Set an optional intensity for the fader parent Function */ + qreal parentIntensity() const; + void setParentIntensity(qreal fraction); + /** Get/Set the pause state of this fader */ bool isPaused() const; void setPaused(bool paused); @@ -133,6 +137,8 @@ class GenericFader : public QObject /** Enable/disable universe monitoring before writing new data */ void setMonitoring(bool enable); + void resetCrossfade(); + signals: /** Signal emitted when monitoring is enabled. * Data is preGM and includes the whole universe */ @@ -144,6 +150,7 @@ class GenericFader : public QObject int m_priority; QHash m_channels; qreal m_intensity; + qreal m_parentIntensity; bool m_paused; bool m_enabled; bool m_fadeOut; diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index dc78362772..6ce92e2b25 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -43,8 +43,10 @@ Scene::Scene(Doc* doc) : Function(doc, Function::SceneType) , m_legacyFadeBus(Bus::invalid()) + , m_blendFunctionID(Function::invalidId()) { setName(tr("New Scene")); + registerAttribute(tr("ParentIntensity"), Multiply | Single); } Scene::~Scene() @@ -600,6 +602,8 @@ void Scene::writeDMX(MasterTimer *timer, QList ua) fader = ua[universe]->requestFader(); fader->adjustIntensity(getAttributeValue(Intensity)); fader->setBlendMode(blendMode()); + fader->setName(name()); + fader->setParentFunctionID(id()); m_fadersMap[universe] = fader; } @@ -657,6 +661,9 @@ void Scene::write(MasterTimer *timer, QList ua) fader->setName(name()); fader->setParentFunctionID(id()); m_fadersMap[universe] = fader; + + //if (blendFunctionID() != Function::invalidId()) + fader->setParentIntensity(getAttributeValue(ParentIntensity)); } FadeChannel *fc = fader->getChannelFader(doc(), ua[universe], scv.fxi, scv.channel); @@ -664,8 +671,26 @@ void Scene::write(MasterTimer *timer, QList ua) // when blend mode is not normal (e.g. additive) perform a full // from-0 fade only on intensity channels and let LTP channels // fade from the current universe value to their target - if (blendMode() != Universe::NormalBlend && (fc->flags() & FadeChannel::Intensity)) - fc->setCurrent(0); + //if (blendMode() != Universe::NormalBlend && (fc->flags() & FadeChannel::Intensity)) + // fc->setCurrent(0); + + /** If a blend Function has been set, check if this channel needs to + * be blended from a previous value. If so, mark it for crossfade + * and apply all the specific rules */ + if (blendFunctionID() != Function::invalidId() && (fc->flags() & FadeChannel::Intensity)) + { + qDebug() << "----- BLEND FROM ID" << blendFunctionID(); + Scene *blendScene = qobject_cast(doc()->function(blendFunctionID())); + if (blendScene != NULL) + { + if (blendScene->checkValue(scv)) + { + fc->addFlag(FadeChannel::CrossFade); + fc->setCurrent(blendScene->value(scv.fxi, scv.channel)); + qDebug() << "----- BLEND VALUE" << fc->current(); + } + } + } qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current() << "to" << scv.value; @@ -727,6 +752,9 @@ void Scene::postRun(MasterTimer* timer, QList ua) m_fadersMap.clear(); + // autonomously reset a blend function if set + setBlendFunctionID(Function::invalidId()); + Function::postRun(timer, ua); } @@ -746,6 +774,14 @@ int Scene::adjustAttribute(qreal fraction, int attributeId) fader->adjustIntensity(getAttributeValue(Function::Intensity)); } } + else if (attrIndex == ParentIntensity) + { + foreach (QSharedPointer fader, m_fadersMap.values()) + { + if (!fader.isNull()) + fader->setParentIntensity(getAttributeValue(ParentIntensity)); + } + } return attrIndex; } @@ -769,3 +805,21 @@ void Scene::setBlendMode(Universe::BlendMode mode) Function::setBlendMode(mode); } + +quint32 Scene::blendFunctionID() const +{ + return m_blendFunctionID; +} + +void Scene::setBlendFunctionID(quint32 fid) +{ + m_blendFunctionID = fid; + if (isRunning() && fid == Function::invalidId()) + { + foreach (QSharedPointer fader, m_fadersMap.values()) + { + if (!fader.isNull()) + fader->resetCrossfade(); + } + } +} diff --git a/engine/src/scene.h b/engine/src/scene.h index 8c7aa6fe4b..db508be9fe 100644 --- a/engine/src/scene.h +++ b/engine/src/scene.h @@ -61,6 +61,12 @@ class Scene : public Function, public DMXSource * Initialization *********************************************************************/ public: + enum SceneAttr + { + Intensity = Function::Intensity, + ParentIntensity + }; + /** * Construct a new scene function, with given parent object. If the * parent is not a Doc* object, the debug build asserts. @@ -77,8 +83,6 @@ class Scene : public Function, public DMXSource /** @reimp */ QIcon getIcon() const; - void setChildrenFlag(bool flag); - /** @reimp */ quint32 totalDuration(); @@ -245,11 +249,21 @@ public slots: int adjustAttribute(qreal fraction, int attributeId); /************************************************************************* - * Blend mode + * Blending *************************************************************************/ public: /** @reimp */ void setBlendMode(Universe::BlendMode mode); + + /** Get/Set the ID of a Function to blend from. + * When preparing the faders of this Scene, + * blend function ID will be taken into account + * to blend channels from a value to another */ + quint32 blendFunctionID() const; + void setBlendFunctionID(quint32 fid); + +protected: + quint32 m_blendFunctionID; }; /** @} */ diff --git a/engine/test/chaser/chaser_test.cpp b/engine/test/chaser/chaser_test.cpp index 19cfbfa3ac..b8d7a57cc0 100644 --- a/engine/test/chaser/chaser_test.cpp +++ b/engine/test/chaser/chaser_test.cpp @@ -954,13 +954,13 @@ void Chaser_Test::adjustIntensity() c->preRun(&timer); c->adjustAttribute(0.5, Function::Intensity); - QCOMPARE(c->m_runner->m_pendingAction.m_intensity, qreal(0.5)); + QCOMPARE(c->m_runner->m_pendingAction.m_masterIntensity, qreal(0.5)); c->adjustAttribute(0.8, Function::Intensity); - QCOMPARE(c->m_runner->m_pendingAction.m_intensity, qreal(0.8)); + QCOMPARE(c->m_runner->m_pendingAction.m_masterIntensity, qreal(0.8)); c->adjustAttribute(1.5, Function::Intensity); - QCOMPARE(c->m_runner->m_pendingAction.m_intensity, qreal(1.0)); + QCOMPARE(c->m_runner->m_pendingAction.m_masterIntensity, qreal(1.0)); c->adjustAttribute(-0.1, Function::Intensity); - QCOMPARE(c->m_runner->m_pendingAction.m_intensity, qreal(0.0)); + QCOMPARE(c->m_runner->m_pendingAction.m_masterIntensity, qreal(0.0)); c->postRun(&timer, ua); // Mustn't crash after postRun diff --git a/engine/test/chaserrunner/chaserrunner_test.cpp b/engine/test/chaserrunner/chaserrunner_test.cpp index 5a363d4365..0104c3f501 100644 --- a/engine/test/chaserrunner/chaserrunner_test.cpp +++ b/engine/test/chaserrunner/chaserrunner_test.cpp @@ -113,7 +113,7 @@ void ChaserRunner_Test::initial() QCOMPARE(cr.m_direction, Function::Forward); QCOMPARE(cr.m_startOffset, quint32(0)); QCOMPARE(cr.m_pendingAction.m_action, ChaserNoAction); - QCOMPARE(cr.m_pendingAction.m_intensity, 1.0); + QCOMPARE(cr.m_pendingAction.m_masterIntensity, 1.0); QCOMPARE(cr.m_pendingAction.m_stepIndex, -1); QCOMPARE(cr.m_pendingAction.m_fadeMode, (int)Chaser::FromFunction); QCOMPARE(cr.m_lastRunStepIdx, -1); @@ -1322,7 +1322,7 @@ void ChaserRunner_Test::adjustIntensity() timer.timerTick(); QCOMPARE(timer.m_functionList.size(), 1); QCOMPARE(timer.m_functionList[0], m_scene1); - QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(0.5)); + QCOMPARE(m_scene1->getAttributeValue(Scene::ParentIntensity), qreal(0.5)); QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(1.0)); QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(1.0)); @@ -1331,7 +1331,7 @@ void ChaserRunner_Test::adjustIntensity() QCOMPARE(timer.m_functionList.size(), 1); QCOMPARE(timer.m_functionList[0], m_scene2); QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(1.0)); - QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(0.5)); + QCOMPARE(m_scene2->getAttributeValue(Scene::ParentIntensity), qreal(0.5)); QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(1.0)); QVERIFY(cr.write(&timer, QList()) == true); @@ -1340,18 +1340,18 @@ void ChaserRunner_Test::adjustIntensity() QCOMPARE(timer.m_functionList[0], m_scene3); QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(1.0)); QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(1.0)); - QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(0.5)); + QCOMPARE(m_scene3->getAttributeValue(Scene::ParentIntensity), qreal(0.5)); cr.adjustStepIntensity(0.7); QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(1.0)); QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(1.0)); - QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(0.7)); + QCOMPARE(m_scene3->getAttributeValue(Scene::ParentIntensity), qreal(0.7)); QVERIFY(cr.write(&timer, QList()) == true); timer.timerTick(); QCOMPARE(timer.m_functionList.size(), 1); QCOMPARE(timer.m_functionList[0], m_scene1); - QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(0.7)); + QCOMPARE(m_scene1->getAttributeValue(Scene::ParentIntensity), qreal(0.7)); QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(1.0)); QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(1.0)); @@ -1360,7 +1360,7 @@ void ChaserRunner_Test::adjustIntensity() QCOMPARE(timer.m_functionList.size(), 1); QCOMPARE(timer.m_functionList[0], m_scene2); QCOMPARE(m_scene1->getAttributeValue(Function::Intensity), qreal(1.0)); - QCOMPARE(m_scene2->getAttributeValue(Function::Intensity), qreal(0.7)); + QCOMPARE(m_scene2->getAttributeValue(Scene::ParentIntensity), qreal(0.7)); QCOMPARE(m_scene3->getAttributeValue(Function::Intensity), qreal(1.0)); cr.adjustStepIntensity(1.5); diff --git a/qmlui/virtualconsole/vccuelist.cpp b/qmlui/virtualconsole/vccuelist.cpp index f3232dd3b1..047bf51eb8 100644 --- a/qmlui/virtualconsole/vccuelist.cpp +++ b/qmlui/virtualconsole/vccuelist.cpp @@ -295,13 +295,10 @@ int VCCueList::nextStepIndex() const qreal VCCueList::getPrimaryIntensity() const { - qreal value; if (sideFaderMode() == Steps) - value = 1.0; - else - value = m_primaryTop ? qreal(m_sideFaderLevel / 100.0) : qreal((100 - m_sideFaderLevel) / 100.0); + return 1.0; - return value * intensity(); + return m_primaryTop ? qreal(m_sideFaderLevel / 100.0) : qreal((100 - m_sideFaderLevel) / 100.0); } int VCCueList::getFadeMode() const @@ -573,7 +570,8 @@ void VCCueList::startChaser(int startIndex) ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = startIndex; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); @@ -645,7 +643,8 @@ void VCCueList::playClicked() ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = m_playbackIndex; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); @@ -702,7 +701,8 @@ void VCCueList::previousClicked() { ChaserAction action; action.m_action = ChaserPreviousStep; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } @@ -742,7 +742,8 @@ void VCCueList::nextClicked() { ChaserAction action; action.m_action = ChaserNextStep; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } diff --git a/ui/src/chasereditor.cpp b/ui/src/chasereditor.cpp index ee50af73a9..240ada293e 100644 --- a/ui/src/chasereditor.cpp +++ b/ui/src/chasereditor.cpp @@ -1126,7 +1126,8 @@ void ChaserEditor::slotTestPlay() ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = idx; - action.m_intensity = 1.0; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; m_chaser->setAction(action); } m_chaser->start(m_doc->masterTimer(), functionParent()); @@ -1149,7 +1150,8 @@ void ChaserEditor::slotTestPreviousClicked() { ChaserAction action; action.m_action = ChaserPreviousStep; - action.m_intensity = 1.0; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; m_chaser->setAction(action); } @@ -1157,7 +1159,8 @@ void ChaserEditor::slotTestNextClicked() { ChaserAction action; action.m_action = ChaserNextStep; - action.m_intensity = 1.0; + action.m_masterIntensity = 1.0; + action.m_stepIntensity = 1.0; m_chaser->setAction(action); } diff --git a/ui/src/virtualconsole/vccuelist.cpp b/ui/src/virtualconsole/vccuelist.cpp index 844a4a5cf4..2dd9637ce4 100644 --- a/ui/src/virtualconsole/vccuelist.cpp +++ b/ui/src/virtualconsole/vccuelist.cpp @@ -559,13 +559,10 @@ int VCCueList::getLastTreeIndex() qreal VCCueList::getPrimaryIntensity() const { - qreal value; if (sideFaderMode() == Steps) - value = 1.0; - else - value = m_primaryTop ? qreal(m_sideFader->value() / 100.0) : qreal((100 - m_sideFader->value()) / 100.0); + return 1.0; - return value * intensity(); + return m_primaryTop ? qreal(m_sideFader->value() / 100.0) : qreal((100 - m_sideFader->value()) / 100.0); } void VCCueList::notifyFunctionStarting(quint32 fid, qreal intensity) @@ -730,7 +727,8 @@ void VCCueList::slotNextCue() { ChaserAction action; action.m_action = ChaserNextStep; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } @@ -775,7 +773,8 @@ void VCCueList::slotPreviousCue() { ChaserAction action; action.m_action = ChaserPreviousStep; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } @@ -976,7 +975,8 @@ void VCCueList::startChaser(int startIndex) ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = startIndex; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); @@ -1155,7 +1155,8 @@ void VCCueList::slotSideFaderValueChanged(int value) ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = newStep; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } @@ -1380,13 +1381,14 @@ void VCCueList::adjustIntensity(qreal val) if (ch != NULL) { adjustFunctionIntensity(ch, val); - +/* // Refresh intensity of current steps if (!ch->stopped() && sideFaderMode() == Crossfade && m_sideFader->value() != 100) { ch->adjustStepIntensity((qreal)m_sideFader->value() / 100, m_primaryTop ? m_primaryIndex : m_secondaryIndex); ch->adjustStepIntensity((qreal)(100 - m_sideFader->value()) / 100, m_primaryTop ? m_secondaryIndex : m_primaryIndex); } +*/ } VCWidget::adjustIntensity(val); @@ -1470,7 +1472,8 @@ void VCCueList::playCueAtIndex(int idx) ChaserAction action; action.m_action = ChaserSetStepIndex; action.m_stepIndex = m_primaryIndex; - action.m_intensity = getPrimaryIntensity(); + action.m_masterIntensity = intensity(); + action.m_stepIntensity = getPrimaryIntensity(); action.m_fadeMode = getFadeMode(); ch->setAction(action); } diff --git a/ui/src/virtualconsole/vcframe.cpp b/ui/src/virtualconsole/vcframe.cpp index 21e2b832a0..e804844f90 100644 --- a/ui/src/virtualconsole/vcframe.cpp +++ b/ui/src/virtualconsole/vcframe.cpp @@ -1170,7 +1170,9 @@ bool VCFrame::loadXML(QXmlStreamReader &root) /* Create a new slider into its parent */ VCSlider* slider = new VCSlider(this, m_doc); if (slider->loadXML(root) == false) + { delete slider; + } else { addWidgetToPageMap(slider); From b9c9918afcfe4bc6744cb68f35f98122bbc9553e Mon Sep 17 00:00:00 2001 From: Massimo Callegari Date: Mon, 29 Jul 2019 20:07:55 +0200 Subject: [PATCH 2/2] engine: more crossfade cases covered --- engine/src/chaserrunner.cpp | 2 +- engine/src/genericfader.cpp | 10 ++++------ engine/src/scene.cpp | 30 +++++++++++------------------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/engine/src/chaserrunner.cpp b/engine/src/chaserrunner.cpp index c932a0ea73..58aa6cc26e 100644 --- a/engine/src/chaserrunner.cpp +++ b/engine/src/chaserrunner.cpp @@ -414,7 +414,7 @@ void ChaserRunner::adjustStepIntensity(qreal fraction, int requestedStepIndex, i { fraction = CLAMP(fraction, qreal(0.0), qreal(1.0)); - qDebug() << "Adjust intensity" << fraction << "step:" << requestedStepIndex << "fade:" << fadeControl; + //qDebug() << "Adjust intensity" << fraction << "step:" << requestedStepIndex << "fade:" << fadeControl; int stepIndex = requestedStepIndex; if (stepIndex == -1) diff --git a/engine/src/genericfader.cpp b/engine/src/genericfader.cpp index 2d6f459da6..1a3539ac66 100644 --- a/engine/src/genericfader.cpp +++ b/engine/src/genericfader.cpp @@ -178,10 +178,8 @@ void GenericFader::write(Universe *universe) { if ((flags & FadeChannel::CrossFade) && fc.fadeTime() == 0) { - if (flags & FadeChannel::LTP) - value = uchar(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity()); - else - value = qreal(fc.target()) * parentIntensity(); + // morph start <-> target depending on intensities + value = uchar(((qreal(fc.target() - fc.start()) * intensity()) + fc.start()) * parentIntensity()); } else if (flags & FadeChannel::Intensity) { @@ -229,7 +227,7 @@ qreal GenericFader::intensity() const void GenericFader::adjustIntensity(qreal fraction) { - qDebug() << name() << "I FADER intensity" << fraction << ", PARENT:" << m_parentIntensity; + //qDebug() << name() << "I FADER intensity" << fraction << ", PARENT:" << m_parentIntensity; m_intensity = fraction; } @@ -240,7 +238,7 @@ qreal GenericFader::parentIntensity() const void GenericFader::setParentIntensity(qreal fraction) { - qDebug() << name() << "P FADER intensity" << m_intensity << ", PARENT:" << fraction; + //qDebug() << name() << "P FADER intensity" << m_intensity << ", PARENT:" << fraction; m_parentIntensity = fraction; } diff --git a/engine/src/scene.cpp b/engine/src/scene.cpp index 6ce92e2b25..21813a0622 100644 --- a/engine/src/scene.cpp +++ b/engine/src/scene.cpp @@ -662,37 +662,29 @@ void Scene::write(MasterTimer *timer, QList ua) fader->setParentFunctionID(id()); m_fadersMap[universe] = fader; - //if (blendFunctionID() != Function::invalidId()) fader->setParentIntensity(getAttributeValue(ParentIntensity)); } FadeChannel *fc = fader->getChannelFader(doc(), ua[universe], scv.fxi, scv.channel); - // when blend mode is not normal (e.g. additive) perform a full - // from-0 fade only on intensity channels and let LTP channels - // fade from the current universe value to their target - //if (blendMode() != Universe::NormalBlend && (fc->flags() & FadeChannel::Intensity)) - // fc->setCurrent(0); - /** If a blend Function has been set, check if this channel needs to * be blended from a previous value. If so, mark it for crossfade - * and apply all the specific rules */ - if (blendFunctionID() != Function::invalidId() && (fc->flags() & FadeChannel::Intensity)) + * and set its current value */ + if (blendFunctionID() != Function::invalidId()) { - qDebug() << "----- BLEND FROM ID" << blendFunctionID(); Scene *blendScene = qobject_cast(doc()->function(blendFunctionID())); - if (blendScene != NULL) + if (blendScene != NULL && blendScene->checkValue(scv)) { - if (blendScene->checkValue(scv)) - { - fc->addFlag(FadeChannel::CrossFade); - fc->setCurrent(blendScene->value(scv.fxi, scv.channel)); - qDebug() << "----- BLEND VALUE" << fc->current(); - } + fc->addFlag(FadeChannel::CrossFade); + fc->setCurrent(blendScene->value(scv.fxi, scv.channel)); + qDebug() << "----- BLEND from Scene" << blendScene->name() + << ", fixture:" << scv.fxi << ", channel:" << scv.channel << ", value:" << fc->current(); } } - - qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current() << "to" << scv.value; + else + { + qDebug() << "Scene" << name() << "add channel" << scv.channel << "from" << fc->current() << "to" << scv.value; + } fc->setStart(fc->current()); fc->setTarget(scv.value);