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

Fix #34451: Add rest position in musicxml export #9955

Conversation

julienripoche
Copy link

@julienripoche julienripoche commented Dec 2, 2021

Resolves: https://musescore.org/en/node/34451

Export rest position attributes (default-x, default-y) in the same way it has already been done for notes.

  • I signed CLA
  • I made sure the code in the PR follows the coding rules
  • I made sure the code compiles on my machine
  • I made sure there are no unnecessary changes in the code
  • I made sure the title of the PR reflects the core meaning of the issue you are solving
  • I made sure the commit message(s) contain a description and answer the question "Why do those changes fix that particular issue?" or "Why are those changes really necessary as improvements?"
  • I made sure the commit message title starts with "fix #424242:" if there is a related issue
  • I created the test (mtest, vtest, script test) to verify the changes I made

@julienripoche
Copy link
Author

Hi ! I added some code to export rest positional attributes in the same way as for the notes.
I originally tested it on the v3.6.2 branch and it worked as expected.
But I couldn't manage to install the latest version of Qt in order to build the master branch on my machine.

@Jojo-Schmitz
Copy link
Contributor

@lvinken Mind to double-check this?

@@ -3757,6 +3781,7 @@ void ExportMusicXml::rest(Rest* rest, int staff)

QString noteTag = QString("note");
noteTag += color2xml(rest);
noteTag += restPosition(this, rest);
Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz Dec 2, 2021

Choose a reason for hiding this comment

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

I wonder if we could simply use noteTag += notePosition(this, dynamic_cast<Note*>(rest)); here, i.e. fully reuse the existing code (which except for that type is 100% identical) rather than duplicating it? Makes the entire fix a one-liner.
Or is this to 'hacky'?

Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz Dec 2, 2021

Choose a reason for hiding this comment

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

Or, probably much cleaner, change notePosition to this:

//---------------------------------------------------------
//   noteRestPosition
//---------------------------------------------------------

QString ExportMusicXml::noteRestPosition(const ExportMusicXml* const expMxml, const ChordRest* const cr)
{
    QString res;

    IF_ASSERT_FAILED(!cr->isNote() && !cr->isRest()) {
        return res;
    }

    if (configuration()->musicxmlExportLayout()) {
        const double pageHeight  = expMxml->getTenthsFromInches(expMxml->score()->styleD(Sid::pageHeight));

        double measureX = expMxml->getTenthsFromDots(cr->measure()->pagePos().x());
        double measureY = pageHeight - expMxml->getTenthsFromDots(cr->measure()->pagePos().y());
        double noteX = expMxml->getTenthsFromDots(cr->pagePos().x());
        double noteY = pageHeight - expMxml->getTenthsFromDots(cr->pagePos().y());

        res += QString(" default-x=\"%1\"").arg(QString::number(noteX - measureX, 'f', 2));
        res += QString(" default-y=\"%1\"").arg(QString::number(noteY - measureY, 'f', 2));
    }

    return res;
}

(not 100% sure whether I got the assertion right)

Copy link
Contributor

@Jojo-Schmitz Jojo-Schmitz Dec 2, 2021

Choose a reason for hiding this comment

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

Maybe even call it chordRestPosition() and drop the IF_ASSERT_FAILED part entirely, as it should be legal for chords too.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually just forget about it, it just doesn't work. And the code isn't 100% identical either, notePosition() picks on the chord a note belongs to, restPosition()(of course) does not.
So back to your initial approach, 2 methods for 2 types, which similar but not identical code.
Sorry for the mess...

@julienripoche
Copy link
Author

julienripoche commented Dec 2, 2021

I actually thought it was possible but I'm not very familliar with c++ and thus did not know the correct syntax.
I tried what you proposed.

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Dec 2, 2021

Well, yeah, I'm not sure about that casting trick either, it certainly does look quite hacky, but maybe my alternative approach is better, no casting needed and inside making sure the single method for both is reall only used on notes and rests, not also on chords.

@@ -3757,6 +3757,7 @@ void ExportMusicXml::rest(Rest* rest, int staff)

QString noteTag = QString("note");
noteTag += color2xml(rest);
noteTag += notePosition(this, dynamic_cast<Note*>(rest)),
Copy link
Contributor

Choose a reason for hiding this comment

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

semicolon, not colon ;-)

@julienripoche
Copy link
Author

Oups sorry...

@julienripoche
Copy link
Author

Should I try with your cleaner approach ?

@Jojo-Schmitz
Copy link
Contributor

I'd like input from others on that

@julienripoche
Copy link
Author

Oh yes I forgot to mention this slight difference between the notePosition and restPosition methods.
So I rolled back to the 2 methods version.

@Jojo-Schmitz
Copy link
Contributor

Actually the issue this fixes is https://musescore.org/en/node/34451, https://musescore.org/en/node/34346 is just a forum thread that triggered the creation of that issue.
You may want to start the PR and/or commit title with "Fix #34451: "

@julienripoche julienripoche changed the title Add rest position in musicxml export Fix #34451: Add rest position in musicxml export Dec 2, 2021
@julienripoche
Copy link
Author

Done for PR and commit titles

Jojo-Schmitz added a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 3, 2021
@lvinken
Copy link
Contributor

lvinken commented Dec 4, 2021

Checked, thanks for looking into this. In its current state (commit 30dd4e2) the code does exactly what it is supposed to do.

I do agree with Jojo that the duplication of code is not desired and also think the mechanism to generate default-x and default-y should be generalised (and extended to other elements).

Wrt to this change specifically, as the code (both for Note and Rest) depends the element's postion and its containing measure's position, in master both Note and Rest inherit from EngravingItem and EngravingItem defines the measure() function, the solution for now could be to create a single function supporting both Note and Rest with an EngravingItem as parameter.

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Dec 4, 2021

So something like this?

//---------------------------------------------------------
//   elementPosition
//---------------------------------------------------------

QString ExportMusicXml::elementPosition(const ExportMusicXml* const expMxml, const EngravingItem* const elm)
{
    QString res;

    if (configuration()->musicxmlExportLayout()) {
        const double pageHeight  = expMxml->getTenthsFromInches(expMxml->score()->styleD(Sid::pageHeight));

        const auto meas = elm->findMeasure();
        IF_ASSERT_FAILED(meas) {
            return res;
        }

        double measureX = expMxml->getTenthsFromDots(meas->pagePos().x());
        double measureY = pageHeight - expMxml->getTenthsFromDots(meas->pagePos().y());
        double noteX = expMxml->getTenthsFromDots(elm->pagePos().x());
        double noteY = pageHeight - expMxml->getTenthsFromDots(elm->pagePos().y());

        res += QString(" default-x=\"%1\"").arg(QString::number(noteX - measureX, 'f', 2));
        res += QString(" default-y=\"%1\"").arg(QString::number(noteY - measureY, 'f', 2));
    }

    return res;
}

(Now tested: It does build!)

@lvinken : it apparently is findMeasure(), not measure(), just like in 3.x too

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Dec 4, 2021

For 3.x this here does seem work:

//---------------------------------------------------------
//   elementPosition
//---------------------------------------------------------

static QString elementPosition(const ExportMusicXml* const expMxml, const Element* const elm)
      {
      QString res;

      if (preferences.getBool(PREF_EXPORT_MUSICXML_EXPORTLAYOUT)) {
            const double pageHeight  = expMxml->getTenthsFromInches(expMxml->score()->styleD(Sid::pageHeight));

            const auto meas = elm->findMeasure();
            Q_ASSERT(!meas);
            if (!meas)
                  return res;

            double measureX = expMxml->getTenthsFromDots(meas->pagePos().x());
            double measureY = pageHeight - expMxml->getTenthsFromDots(meas->pagePos().y());
            double noteX = expMxml->getTenthsFromDots(elm->pagePos().x());
            double noteY = pageHeight - expMxml->getTenthsFromDots(elm->pagePos().y());

            res += QString(" default-x=\"%1\"").arg(QString::number(noteX - measureX,'f',2));
            res += QString(" default-y=\"%1\"").arg(QString::number(noteY - measureY,'f',2));
            }

      return res;
      }

@lvinken
Copy link
Contributor

lvinken commented Dec 4, 2021

Both Jojo's 3.x and master solution look OK to me.

Jojo-Schmitz added a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 4, 2021
Jojo-Schmitz added a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 7, 2021
Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 8, 2021
Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 13, 2021
Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Dec 13, 2021
@julienripoche
Copy link
Author

Hi @Jojo-Schmitz and happy new year !
Should I commit your elementPosition method for use in the master branch ?
I understand you take care of the 3.x branch ?

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Jan 10, 2022

Not sure, either way is apparently OK. Mine seems a bit more generic though, and @lvinken seems to agree on making it more generic.
And yes, that's what 've done to the 3.x branch (rather to #9000)

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Feb 4, 2022

Note that the Q_ASSERT(!meas) im my version for 3.x (#9955 (comment)) is just wrong and bad.
But then the IF_ASSERT_FAILED(meas) for master (#9955 (comment)) is probably equally bad and might better just be if (!meas).

Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Feb 4, 2022
@lvinken
Copy link
Contributor

lvinken commented Feb 4, 2022

I think an assertion is kind of useful here: if the code is generalised it should be robust against being called for unexpected element types (such as elements that are not attached to measures). In that case for a production build simply returning an empty string should be OK. For a development build either a message or a failed assertion would be OK.

This could be achieved by
Q_ASSERT(meas); if (!meas) return res;

Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Feb 5, 2022
@Jojo-Schmitz
Copy link
Contributor

OK, so for master is should be IF_ASSERT_FAILED(!meas)

@lvinken
Copy link
Contributor

lvinken commented Feb 5, 2022 via email

@Jojo-Schmitz
Copy link
Contributor

Jojo-Schmitz commented Feb 5, 2022

No, it is like a normal if, but logs/abends when the condition is true:

#define IF_ASSERT_FAILED_X(cond, msg) if (!(cond)) { \
        LOGE() << "\"ASSERT FAILED!\":" << msg << __FILE__ << __LINE__; \
        Q_ASSERT(cond); \
} \
    if (!(cond)) \

#define IF_ASSERT_FAILED(cond) IF_ASSERT_FAILED_X(cond, #cond)

bit of a screwed up logic that threw me off several time already (and here too)

@julienripoche are you going to implement it that way, or should I?

I just did it, see PR #10456. It ran all the tests and passed them too.

Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request Feb 7, 2022
Jojo-Schmitz pushed a commit to Jojo-Schmitz/MuseScore that referenced this pull request May 12, 2022
@cbjeukendrup
Copy link
Contributor

Closing, since this has been replaced by #10456.

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.

4 participants