Skip to content
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

Add laissez vibrer ties #25435

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

Add laissez vibrer ties #25435

wants to merge 6 commits into from

Conversation

miiizen
Copy link
Contributor

@miiizen miiizen commented Nov 5, 2024

Resolves: #16726
This PR adds laissez vibrer ties. They are added via a toggle in the toolbar.

Screen.Recording.2024-11-05.at.15.03.42.mov

Laissez vib ties are drawn as normal ties, with all the same style options plus a minimum length available in the properties panel.

Screen.Recording.2024-11-05.at.15.04.51.mov

When multiple LV ties are added to a chord, the 'minimum length' for whichever tie starts furthest to the right is honoured, and then the right endpoints of all the other ties are extended to match.

Screen.Recording.2024-11-05.at.15.07.21.mov

There is also the option to use SMuFL symbols instead of MuseScore's own ties. When added to a chord, these will all end at the rightmost point any tie reaches and will start at the same point as their length is not variable.
image

Properties:
image

Style settings:
image

This element is implemented as a child of the Tie class which makes it a Spanner without an end element. The relevant methods have been overriden to enforce this. The spanner will also only ever have a single segment.

In old scores, laissez vibrer articulations will be converted to the new ties.

Copy link
Contributor

@rettinghaus rettinghaus left a comment

Choose a reason for hiding this comment

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

Looks pretty nice. Thank you so much. One comment on my side for the MusicXML export.

NB: the export to MEI also has to be adjusted. I can do that, if you like.

const Tie* tieFor = note->tieFor();
if (tieFor && ExportMusicXml::canWrite(tieFor)) {
if (tieFor && !laissezVib && ExportMusicXml::canWrite(tieFor)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This way all styling of lv ties gets lost. Better to adjust the previous clause to just exchange type="start" with type="let-ring".

@rettinghaus
Copy link
Contributor

Another thing that I noticed during testing: lv styles are not being saved, so direction, color and line styles are lost.

@XiaoMigros
Copy link
Contributor

Should l.v. ties get a tie placement style option too?

@oktophonie
Copy link
Contributor

@XiaoMigros They follow that of normal ties, which can also be overridden in Properties.

@miiizen
Copy link
Contributor Author

miiizen commented Nov 8, 2024

@rettinghaus I'd be really grateful if you could take a look at the MEI side of things when this is merged!

@bkunda
Copy link

bkunda commented Nov 11, 2024

Really loving this PR @miiizen!

Just one nit-picky UI point:

We should use the label "Laissez vibrer" in these places, instead of "Laissez vib"

Screenshot 2024-11-11 at 1 26 20 pm Screenshot 2024-11-11 at 1 26 31 pm

Add laisser vib class

Add styles

Add lv layout methods

Create smufl symbol layout

Refactor laisser vib to be a child of Tie

Add MuseScore tie layout

Create properties panel for lv ties

Add style settings

Add icons and input toolbar button

Fix layout & file reading/writing
Copy link
Contributor

@mike-spa mike-spa left a comment

Choose a reason for hiding this comment

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

Outstanding work!

initElementStyle(&laissezVibStyle);
}

mu::engraving::PropertyValue mu::engraving::LaissezVib::getProperty(Pid propertyId) const
Copy link
Contributor

Choose a reason for hiding this comment

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

mu::engraving shouldn't be necessary here, as you have the using namespace statement above. Actually, I think all of this should be done inside the engraving namespace . Meaning that instead of having the using namespace you'd just open namespace mu::engraving { ... } and put all your code inside it (that's how we do it for all the other items if I'm not mistaken).

return up() ? SymId::articLaissezVibrerAbove : SymId::articLaissezVibrerBelow;
}

static int compareNotesPos(const Note* n1, const Note* n2)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a function that I can see being useful in many other contexts. In fact, I suspect that there may be something similar to this already, I can definitely remember having to use similar logic before, but I may be wrong. In any case, a better place for this could be in the utils file.

}
}

void LaissezVib::calculateDirection()
Copy link
Contributor

Choose a reason for hiding this comment

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

Couple of thoughts (referred to all three of these functions, namely calculateDirection, calculateIsInside, isOuterTieOfChord).

  1. I wonder if it would be worth trying to factor out some of the reused code here. I can see that the logic is quite similar to normal ties, with the obvious difference that we aren't looking at the end chord. The answer may well be no: sometimes avoiding repetitions at all costs can result in messier code. But do give it a thought, if you can see any easy way of doing it. A pattern that I've found useful sometimes when dealing with overrides like this is to separate the part that's common from the part that's overridden (see for instance Spanner::computeEndElement and Spanner::doComputeEndElement to see what I mean).
  2. Independently from the previous point: perhaps calculateDirection, which is the more strictly layout-dependend one, is worth moving out of the DOM and into the layout module?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This and the point above became immediately obvious when beginning partial ties! I'll tidy these up and just have single methods in Tie which handle both start & end or only one or the other.

@@ -220,31 +220,31 @@ class Spanner : public EngravingItem
void setNoteSpan(Note* startNote, Note* endNote);

EngravingItem* startElement() const { return m_startElement; }
EngravingItem* endElement() const { return m_endElement; }
virtual EngravingItem* endElement() const { return m_endElement; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it necessary to override all of these methods? Does it become too annoying if you just ensure that m_endElement is always null for LV ties and let the other methods (which work with m_endElement) deal with it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought this was pretty ugly, glad to get rid of it. It does work when only overriding setEndNote and setEndElement to which I've added a stronger assertion.

@@ -1350,48 +1351,6 @@ void SlurTieLayout::avoidPreBendsOnTab(const Chord* sc, const Chord* ec, SlurTie
}
}

TieSegment* SlurTieLayout::layoutTieWithNoEndNote(Tie* item)
Copy link
Contributor

Choose a reason for hiding this comment

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

THANK YOU, I couldn't wait to get rid of this horror

@@ -19,12 +19,13 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_ENGRAVING_SLURTIELAYOUT_DEV_H
#define MU_ENGRAVING_SLURTIELAYOUT_DEV_H
#pragma once
Copy link
Contributor

Choose a reason for hiding this comment

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

Nothing bad about this, but since we use the #ifndef / #endif for every other header file in our repo, I'd rather keep it consistent.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is changing; we're now gradually switching to #pragma once everywhere. New files should use #pragma once, and existing files could/should be updated when they are touched anyway.

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.

Functional corrections to l.v. tie symbol (multiple notes per chord & default placement)
7 participants