Skip to content

Conversation

@gregchapman-dev
Copy link
Contributor

Note that I had to check md['software'] as it was being constructed, instead of afterward, because Metadata() actually adds a software item for the music21 version that read the file, and I don't want to be confused by that.

…d['software'] as it was being constructed, instead of afterward, because Metadata() actually adds a software item for the music21 version that read the file, and I don't want to be confused by that.
@gregchapman-dev
Copy link
Contributor Author

@jacobtylerwalls What do you think?

@TimFelixBeyer
Copy link
Contributor

FWIW I don't think this is enough to fully fix the issue. A lot of additional changes in my PR are centered around avoiding unnecessary calls to makeRests. With this version we still add rests that don't exist.

@gregchapman-dev
Copy link
Contributor Author

Yeah, I just realized that. Trying again.

@gregchapman-dev
Copy link
Contributor Author

(and sorry about jumping all over this, but I just remembered that my 2 nested PRs depend on this, and I need to get it done ASAP; remind me NEVER to have PRs that depend on each other again, this sucks a lot.)

@TimFelixBeyer
Copy link
Contributor

TimFelixBeyer commented May 15, 2025

While I personally would love this change, when I proposed this version in my PR (#1636 (comment)) it wasn't accepted (explanation here: #1636 (comment))

I know it's a lot, but I highly encourage you to read through the full thread in #1636 to understand the maintainers' perspective.
Again I (and also some other users in #1636) agree that m21 should just implement the spec without trying to fix idiosyncratic problems with some notation software, but its quite a nuanced issue I suppose.

@gregchapman-dev
Copy link
Contributor Author

Yeah, this is my attempt to get what you and I both need, and also make Jacob happy. (Hi @jacobtylerwalls!)

@coveralls
Copy link

coveralls commented May 15, 2025

Coverage Status

coverage: 93.005% (-0.006%) from 93.011%
when pulling c6b7607 on gregchapman-dev:gregc/forwardIsHiddenRestOnlyForFinale
into 52fc784 on cuthbertLab:master.

@gregchapman-dev
Copy link
Contributor Author

Hoping we can ignore the slight decrease in coverage. I promise my next two PRs will increase coverage in MusicXML read/write a lot.

@gregchapman-dev
Copy link
Contributor Author

gregchapman-dev commented May 15, 2025

OK, @jacobtylerwalls, I think this meets everyone's needs. This is the first of three PRs of mine that need to go in sequentially, the next one is PR #1768 (fixes many Spanner and SpannerAnchor-related bugs) and the last one is PR #1766, a new (better) implementation of PedalMarks that replaces the one that was merged onto master recently. My goal is to get that merged onto master before @mscuthbert makes the next music21 release (otherwise I think we might have to remove the PedalMark support that's on master). Can we expedite getting this one in, since it is the first of the blockers for that PedalMark work?

@mscuthbert
Copy link
Member

mscuthbert commented May 16, 2025

(and sorry about jumping all over this, but I just remembered that my 2 nested PRs depend on this, and I need to get it done ASAP; remind me NEVER to have PRs that depend on each other again, this sucks a lot.)

Hi -- this is some very complex and subtle code changes whose discussions etc. are now spread over 4 PRs, and it's getting more than a bit frustrating to me. I'm not going to review any more code until I have a clear prose explanation for how the proposed solution to the problem given here differs from and is superior to the previous proposed PRs/solutions and, importantly, why it is significantly superior to the status quo.

A superior solution, btw., does not add epicycles within epicycles of added complexity for all parts of the system to improve one thing. I haven't pushed enough against the complexity in terms of future dev's time and energy of some proposed solutions. Thanks!

@gregchapman-dev
Copy link
Contributor Author

@jacobtylerwalls I think you should speak up here. I think this is what you were asking for, but perhaps I have missed something.

@gregchapman-dev
Copy link
Contributor Author

And for the record, Myke, what's happening is that as I work on pedal marks, I am finding bugs in other parts of the system that block me (granted, some of them are my bugs, but I'm not adding layers of complexity, I'm fixing long-standing bugs).

@gregchapman-dev
Copy link
Contributor Author

I will work on some prose about these three related PRs, though. You make a good point that I haven't described this for you very well at all.

@gregchapman-dev
Copy link
Contributor Author

A description of the three related PRs (this is one of them) has been added to PR #1766.

@jacobtylerwalls
Copy link
Member

Taking a look

Copy link
Member

@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

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

Thanks, as agreed in #1636 this is an improvement in correctness (limit workaround to Finale only) and net simplicity (given fewer makeRests() calls mixed in with parsing).

self.parts = []

self.musicXmlVersion = defaults.musicxmlVersion
self.wasWrittenByFinale = False
Copy link
Member

@jacobtylerwalls jacobtylerwalls May 17, 2025

Choose a reason for hiding this comment

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

I think it would be better to localize this in xmlForward() along the lines I suggested here, checking metadataObj.software. A helper function could be extracted to do this checking.

I see from your commit message you tried this and found that setEncoding() is lossy -- we lose information about whether music21 was already in the software tag on the document -- but I think that shouldn't stop us from doing the right thing; we should just have at most one parser-level variable about that rather than one parser-level variable (and lower-order variables) about Finale-ness.

Checking in xmlForward should be fast, since we can depend on the metadata object always existing at the the top of the stream (no long O(n) searches finding no metadatas anywhere):

md = self.xmlMetadata(mxScore)
s.coreInsert(0, md)

You'd have to change that coreInsert (as modeled in that diff I linked).

@mscuthbert do you agree that would achieve better "locality of behavior" than adding more parser attributes for edge cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Assuming I'm understanding this correctly, I'm not sure much simplicity is gained by replacing self.wasWrittenByFinale with self.music21WasActuallyInTheDocsSoftwareMetadata (or some other better name). Maybe a rename with the existing semantics? self.applyFinaleWorkarounds, or something like that, that feels more like it is a legit parser-level variable.

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'm also going to argue that performing the "loop over the software metadata" for every <forward> element is going to be significantly more expensive than doing it once per document.

Copy link
Member

Choose a reason for hiding this comment

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

Yep, do it once. I believe that the order of software tags written out by music21 is the order we found was most common at the time (17 years ago) for how to layer them. So we really only need to look if the top layer is Finale.

Copy link
Member

Choose a reason for hiding this comment

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

self.applyFinaleWorkarounds sounds very good. Or if we want to make it very generic self.softwareWorkarounds: set[str] = {'finale'}

@jacobtylerwalls
Copy link
Member

Also, please update the PR title to something that will be more helpful years from now.

@gregchapman-dev gregchapman-dev changed the title Proposed solution for PR #1636 (which I cannot edit) Interpret empty <forward> tags as rests on Finale documents only (derived from PR #1636, thanks @TimFelixBeyer!) May 17, 2025
@gregchapman-dev
Copy link
Contributor Author

gregchapman-dev commented May 17, 2025

All done except for the continuing discussion of "locality of behavior" regarding self.wasWrittenByFinale.

Copy link
Member

@jacobtylerwalls jacobtylerwalls left a comment

Choose a reason for hiding this comment

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

Thanks! I'll wait a few days to see if @mscuthbert wants to weigh in.

@mscuthbert
Copy link
Member

Huge thanks to @gregchapman-dev and to @jacobtylerwalls and to @TimFelixBeyer -- I finally get what through all the weeds and difficulties what we're essentially all in agreement we're trying to do. And based on finally understanding everything, my normal worry-wart objection "what if the next version of Finale fixes this?" is now moot by experience. Let me do a last readthrough and see what to do.

r.style.hideObjectOnPrint = True
self.addToStaffReference(mxObj, r)
self.insertInMeasureOrVoice(mxObj, r)
if self.parent.parent.wasWrittenByFinale:
Copy link
Member

Choose a reason for hiding this comment

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

One of the few times I wish Python were Javascript -- need to check for grandparent existence...

if (self.parent
    and self.parent.parent
    and self.parent.parent.wasWrittenbyFinale
):

Vote YES on PEP 505

Add safety for MeasureParsers created w/o Part/ScoreParsers (testing, extensions, etc.)

Add TESTS to make sure that Finale workarounds are only done if the score was last exported by Finale.  Tests are mostly on processing Encoding, which suddenly matters a lot!  :-)
@mscuthbert
Copy link
Member

Thanks everyone -- I realize now that the details prevented me from seeing the big picture.

In my last push I renamed some things to help me understand what things (like storing the final rest made from <forward>) existed just to get through Finale workarounds.

I added a test of the encoding parser (long overdue before this even) and now I think I fully understand how this PR improves the system without making it too much more complex.

Approved and merging.

Please take a minute after this to close issues/PRs that are now fixed -- or to reformulate them (renaming helps a LOT!) so that I can understand their relevance, and let's get some more Piano marks etc. in.

Greg -- seeing on the list that m21 isn't currently working on Python 3.13 for Windows users, I might make a quick 9.7 release ASAP to help them even if PianoMarks aren't in fast. But I'll do 9.9 quickly afterwards to get that in, promise.

(In case eyebrows go up -- since m21 9 worked with the latest Python w/ numpy 1.26 and now less than a year later no longer works with the latest Python w/ recent numpy, I'm considering it more of a bug-fix release than a non-backwards-compatible release, since our dependencies didn't think about 18-24 month support on their end).

@mscuthbert mscuthbert merged commit 7ec852f into cuthbertLab:master May 22, 2025
8 checks passed
@mscuthbert
Copy link
Member

Closing #1636 (Harvard PR)

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.

5 participants