Fix knob linking / refactor linking (#7883)
Closes #7869 This PR aims to fix linking bugs and it aims to make linking code faster. In the future I would like to replace controller and automation code with linked models, so it is essential for this feature to work as efficiently as possible. Before this PR: - AutomatableModels store a list of AutomatableModels that they are linked to. - setValue() and other functions make recursive calls to themself resulting in the #7869 crash. - Each AutomatableModel can unlink from other AutomatableModels, unlinking is the inverse operation to linking. After this PR: - AutomatableModels store a pointer to an other AutomatableModel making a linked list. The end is connected to the first element resulting in a "ring". - setValue() and others are now recursion free, the code runs for every linked model, more efficiently than before. - Each AutomatableModel can NOT unlink form other AutomatableModels, unlinking is NOT the inverse operation to linking. AutomatableModels can unlink themself from the linked list, they can not unlink themself from single models. --------- Co-authored-by: allejok96 <allejok96@gmail.com>
This commit is contained in:
@@ -77,8 +77,6 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
using AutoModelVector = std::vector<AutomatableModel*>;
|
||||
|
||||
enum class ScaleType
|
||||
{
|
||||
Linear,
|
||||
@@ -150,22 +148,26 @@ public:
|
||||
template<class T>
|
||||
inline T value( int frameOffset = 0 ) const
|
||||
{
|
||||
if (m_controllerConnection)
|
||||
// TODO
|
||||
// The `m_value` should only be updated whenever the Controller value changes,
|
||||
// instead of the Model calling `controller->currentValue()` every time.
|
||||
// This becomes even worse in the case of linked Models, where it has to
|
||||
// loop through the list of all links.
|
||||
|
||||
if (m_useControllerValue)
|
||||
{
|
||||
if (!m_useControllerValue)
|
||||
{
|
||||
return castValue<T>(m_value);
|
||||
}
|
||||
else
|
||||
if (m_controllerConnection)
|
||||
{
|
||||
return castValue<T>(controllerValue(frameOffset));
|
||||
}
|
||||
for (auto next = m_nextLink; next != this; next = next->m_nextLink)
|
||||
{
|
||||
if (next->controllerConnection() && next->useControllerValue())
|
||||
{
|
||||
return castValue<T>(fittedValue(next->controllerValue(frameOffset)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hasLinkedModels())
|
||||
{
|
||||
return castValue<T>( controllerValue( frameOffset ) );
|
||||
}
|
||||
|
||||
return castValue<T>( m_value );
|
||||
}
|
||||
|
||||
@@ -211,8 +213,7 @@ public:
|
||||
|
||||
void setInitValue( const float value );
|
||||
|
||||
void setAutomatedValue( const float value );
|
||||
void setValue( const float value );
|
||||
void setValue(const float value, const bool isAutomated = false);
|
||||
|
||||
void incValue( int steps )
|
||||
{
|
||||
@@ -249,11 +250,10 @@ public:
|
||||
m_centerValue = centerVal;
|
||||
}
|
||||
|
||||
//! link @p m1 and @p m2, let @p m1 take the values of @p m2
|
||||
static void linkModels( AutomatableModel* m1, AutomatableModel* m2 );
|
||||
static void unlinkModels( AutomatableModel* m1, AutomatableModel* m2 );
|
||||
|
||||
void unlinkAllModels();
|
||||
//! link this to @p model, copying the value from @p model
|
||||
void linkToModel(AutomatableModel* model);
|
||||
//! @return number of other models linked to this
|
||||
size_t countLinks() const;
|
||||
|
||||
/**
|
||||
* @brief Saves settings (value, automation links and controller connections) of AutomatableModel into
|
||||
@@ -276,9 +276,9 @@ public:
|
||||
|
||||
virtual QString displayValue( const float val ) const = 0;
|
||||
|
||||
bool hasLinkedModels() const
|
||||
bool isLinked() const
|
||||
{
|
||||
return !m_linkedModels.empty();
|
||||
return m_nextLink != this;
|
||||
}
|
||||
|
||||
// a way to track changed values in the model and avoid using signals/slots - useful for speed-critical code.
|
||||
@@ -311,13 +311,14 @@ public:
|
||||
s_periodCounter = 0;
|
||||
}
|
||||
|
||||
bool useControllerValue()
|
||||
bool useControllerValue() const
|
||||
{
|
||||
return m_useControllerValue;
|
||||
}
|
||||
|
||||
public slots:
|
||||
virtual void reset();
|
||||
void unlink();
|
||||
void unlinkControllerConnection();
|
||||
void setUseControllerValue(bool b = true);
|
||||
|
||||
@@ -367,9 +368,15 @@ private:
|
||||
loadSettings( element, "value" );
|
||||
}
|
||||
|
||||
void linkModel( AutomatableModel* model );
|
||||
void unlinkModel( AutomatableModel* model );
|
||||
void setValueInternal(const float value);
|
||||
|
||||
//! linking is stored in a linked list ring
|
||||
//! @return the model whose `m_nextLink` is `this`,
|
||||
//! or `this` if there are no linked models
|
||||
AutomatableModel* getLastLinkedModel() const;
|
||||
//! @return true if the `model` is in the linked list
|
||||
bool isLinkedToModel(AutomatableModel* model) const;
|
||||
|
||||
//! @brief Scales @value from linear to logarithmic.
|
||||
//! Value should be within [0,1]
|
||||
template<class T> T logToLinearScale( T value ) const;
|
||||
@@ -389,16 +396,15 @@ private:
|
||||
float m_centerValue;
|
||||
|
||||
bool m_valueChanged;
|
||||
|
||||
// currently unused?
|
||||
float m_oldValue;
|
||||
int m_setValueDepth;
|
||||
float m_oldValue; //!< used by valueBuffer for interpolation
|
||||
|
||||
// used to determine if step size should be applied strictly (ie. always)
|
||||
// or only when value set from gui (default)
|
||||
bool m_hasStrictStepSize;
|
||||
|
||||
AutoModelVector m_linkedModels;
|
||||
//! an `AutomatableModel` can be linked together with others in a linked list
|
||||
//! the list has no end, the last model is connected to the first forming a ring
|
||||
AutomatableModel* m_nextLink;
|
||||
|
||||
|
||||
//! NULL if not appended to controller, otherwise connection info
|
||||
|
||||
@@ -98,7 +98,6 @@ public slots:
|
||||
void execConnectionDialog();
|
||||
void removeConnection();
|
||||
void editSongGlobalAutomation();
|
||||
void unlinkAllModels();
|
||||
void removeSongGlobalAutomation();
|
||||
|
||||
private slots:
|
||||
|
||||
Reference in New Issue
Block a user