Skip to content

Fix knob linking / refactor linking #7883

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from

Conversation

szeli1
Copy link
Contributor

@szeli1 szeli1 commented May 9, 2025

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:

  1. AutomatableModels store a list of AutomatableModels that they are linked to.
  2. setValue() and other functions make recursive calls to themself resulting in the Linked controls can cause crashes #7869 crash
  3. Each AutomatableModel can unlink form other AutomatableModels, unlinking is the inverse operation to linking.

After this PR:

  1. AutomatableModels store a pointer to an other AutomatableModel making a linked list. The end is connected to the first element resulting in a "ring"
  2. setValue() and others are now recursion free, the code runs for every linked model, more efficiently than before.
  3. 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.

Issues:

  • Const AutomatableModel::value() uses a hack non const setValue() to update linked widgets if a controller is connected.
  • Linked widgets do not clamp each other unlike before this PR.

How to test:

  1. Press ctrl and drag a Knob onto an other Knob. They should now be linked.
  2. Change the first knob's value, the second knob should change.
  3. Click on "remove all linked controls" for the first knob, if done, the knobs should be unlinked.
  4. Repeat these steps and link together combo boxes, mute buttons, "lcd" displays, and anything that can be automated.
  5. Make sure to link different kinds of widgets together.
  6. See if Linked controls can cause crashes #7869 is fixed.

@szeli1 szeli1 changed the title Fix knob linking Fix knob linking / refactor linking May 9, 2025
@szeli1 szeli1 marked this pull request as draft May 9, 2025 18:55
@szeli1 szeli1 marked this pull request as ready for review May 9, 2025 21:28
@firiox
Copy link

firiox commented May 9, 2025

Hey Bro! Nice job I was just testint It and I have a problem I think. When you link mixer channels to the volume fader It changes them so fast that It was jumping for o to 4 really fast thought I think a better way to do this would be to divide the volume range by the number of mixers channels so It is more responsive I guess I don't know if thats challenging but I guess so anyway I got ninja and could test it that for each volume step mixer channels got update right so this is very subjective right but I think It'll feel alot better.
Another thing following up the testing I added the mute toggle to the volume and mixer channel link and It got gimmick like the mute toggle gets toggled before the mixer gets it's first value It is kinda strange like it's not the same method for changing 1 value for the mute toggl and the mixer channel kinda feels off Do you understand me? anyway It's a step forward and this is just final tunning I guess. So to be more clear you can do a half step and the mute toggle gets triggered but the mixer channel is still in 0 and then another half step and the mixer channels finally reaches his next value. Weird right? (It is kinda a ninja movement thought but puts me off)

@szeli1
Copy link
Contributor Author

szeli1 commented May 10, 2025

Another thing following up the testing I added the mute toggle to the volume and mixer channel link and It got gimmick like the mute toggle gets toggled before the mixer gets it's first value It is kinda strange like it's not the same method for changing 1 value for the mute toggl and the mixer channel kinda feels off Do you understand me?

This is because internally everything is stored as a float even the mute buttons, but these internal values do not range from 0 to 1, but from a custom min value to a custom max value. Each widget receives the same value and interprets it with their own min-max. For example the mixer channel lcd widget gets "2" as input and sends it over to the mute button, the mute button ranges from 0 to 1 so it clamps the "2" to "1" and turns on. If you connect a mixer channel lcd widget to a volume knob, the volume knob ranges from 0 to 200 but the lcd widget ranges from 0 to the number of mixer channels, for example 4. This leads to the behavior you are experiencing.

Its feels badly designed, I will see what I can do.

@regulus79
Copy link
Contributor

I tested this, and it definitely seems to fix the bug.

Also it seems like you also addressed #7621, somehow.

One thing I noticed is that now if two knobs with different ranges are linked (like volume and panning), if you move one knob out of the range of the other knob (like moving panning to negative), it lets you do that and just sets the value of the other knob to within its own bounds (volume doesn't go below 0). However, on master, if you try to turn one knob past the end of a linked knob, it won't let you, it will stop you from turning it. If you try to turn panning below 0, it won't let you. Well, sometimes. Sometimes it would still let you do it if you did some complicated linking between multiple knobs.

Maybe that's a good thing in this PR? It feels like it probably gives users more freedom. Though of course it means if you move the other knob, the first knob will snap back, which might not be desirable idk. But if it's also in master sometimes... idk, this PR feels like a much cleaner system.

@bratpeki bratpeki self-assigned this May 22, 2025
@bratpeki
Copy link
Member

Tried to cause the crash described in the corresponding issue, and couldn't! 🎉

It seems the image for the "Remove all linked controls" is missing and I get this issue:

Error loading icon pixmap "edit-delete": "File not found"

I also observe this on master.

@bratpeki
Copy link
Member

bratpeki commented May 22, 2025

LCD linking to other LCD items isn't possible. Interestingly, you can link LCDs to knobs.

@szeli1
Copy link
Contributor Author

szeli1 commented May 23, 2025

Thanks for testing @regulus79 and @bratpeki

@szeli1
Copy link
Contributor Author

szeli1 commented May 23, 2025

LCD linking to other LCD items isn't possible. Interestingly, you can link LCDs to knobs.

Yes sounds like LCD widgets do not accept the link for some reason.

@szeli1
Copy link
Contributor Author

szeli1 commented May 23, 2025

Maybe that's a good thing in this PR? It feels like it probably gives users more freedom

I can make it work like in old lmms, but currently linking isn't saved so this new behavior won't change anyone's project and It will add an additional loop for setValue which would make setValue more computationally expensive. This isn't ideal for the next step: removing controller connection and replacing it with linking.

@bratpeki
Copy link
Member

Error loading icon pixmap "edit-delete": "File not found"

I also observe this on master.

I find this very strange, TBH. Can someome check they're also getting this in the terminal when removing linking in the context menu?

@szeli1
Copy link
Contributor Author

szeli1 commented May 23, 2025

I find this very strange, TBH. Can someome check they're also getting this in the terminal when removing linking in the context menu?

I got this message also, there is no "edit-delete" file in the theme. I could fix it if you want it, but I'm unsure what kind of picture to use / make.

@regulus79
Copy link
Contributor

I also get that message. edit-delete.png doesn't appear to exist in the default or classic theme, and doing git log -- data/themes/default/edit-delete.png gives nothing, so I'm not sure it existed in the past either.

Also, 1.2.2 doesn't appear to have the icon either, although I don't get any warning in the terminal.

image

@szeli1
Copy link
Contributor Author

szeli1 commented May 24, 2025

I added a new svg for unlinking @bratpeki

@bratpeki
Copy link
Member

Cool! Any idea why the old one was missing? Or maybe it was never there?

@bratpeki
Copy link
Member

I also get that message. edit-delete.png doesn't appear to exist in the default or classic theme, and doing git log -- data/themes/default/edit-delete.png gives nothing, so I'm not sure it existed in the past either.

Also, 1.2.2 doesn't appear to have the icon either, although I don't get any warning in the terminal.

image

I see!

Copy link
Member

@AW1534 AW1534 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested this quite rigorously, didn't find any issues. Just have a few style nitpicks.

Comment on lines +1073 to 1074
m_vi->knobFModel[ i ]->setValue(f_value, true);
m_vi->knobFModel[ i ]->setInitValue( f_value );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
m_vi->knobFModel[ i ]->setValue(f_value, true);
m_vi->knobFModel[ i ]->setInitValue( f_value );
m_vi->knobFModel[i]->setValue(f_value, true);
m_vi->knobFModel[i]->setInitValue(f_value);

Comment on lines +466 to 467
m_vi2->knobFModel[ i ]->setValue(f_value, true);
m_vi2->knobFModel[ i ]->setInitValue( f_value );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
m_vi2->knobFModel[ i ]->setValue(f_value, true);
m_vi2->knobFModel[ i ]->setInitValue( f_value );
m_vi2->knobFModel[i]->setValue(f_value, true);
m_vi2->knobFModel[i]->setInitValue(f_value);

Comment on lines 51 to 53
m_range( max - min ),
m_centerValue( m_minValue ),
m_valueChanged( false ),

This comment was marked as off-topic.

Comment on lines 55 to 59
m_hasStrictStepSize( false ),
m_nextLink(nullptr),
m_controllerConnection( nullptr ),
m_valueBuffer( static_cast<int>( Engine::audioEngine()->framesPerPeriod() ) ),
m_lastUpdatedPeriod( -1 ),

This comment was marked as off-topic.

m_linkedModels.erase( m_linkedModels.end() - 1 );
}
// unlink this from anything else
unlinkModel();

if( m_controllerConnection )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if( m_controllerConnection )
if(m_controllerConnection)

Comment on lines 328 to 330
template<class T> T AutomatableModel::logToLinearScale( T value ) const
{
return castValue<T>( lmms::logToLinearScale( minValue<float>(), maxValue<float>(), static_cast<float>( value ) ) );

This comment was marked as off-topic.

Comment on lines +101 to 102
menu->addAction( embed::getIconPixmap( "edit_unlink" ),
AutomatableModel::tr( "Remove all linked controls" ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
menu->addAction( embed::getIconPixmap( "edit_unlink" ),
AutomatableModel::tr( "Remove all linked controls" ),
menu->addAction(embed::getIconPixmap("edit_unlink"),
AutomatableModel::tr("Remove all linked controls"),

}
}

if( m_oldValue != val )
if (m_oldValue != val)
{
m_valueBuffer.interpolate( m_oldValue, val );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
m_valueBuffer.interpolate( m_oldValue, val );
m_valueBuffer.interpolate(m_oldValue, val);

@@ -342,16 +342,18 @@ void LadspaControl::unlinkControls( LadspaControl * _control )
switch( m_port->data_type )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
switch( m_port->data_type )
switch(m_port->data_type)

void setAutomatedValue( const float value );
void setValue( const float value );
void setAutomatedValue(float value);
void setValue(float value, bool isAutomated = false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between setting the automated value via setAutomatedValue() and setValue(..., true)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Linked controls can cause crashes
5 participants