Skip to content

Conversation

@gregchapman-dev
Copy link
Contributor

Two fixes in parseFlatElements: (1) prefer a gap to a complex duration hidden rest, not just for an inexpressible duration hidden rest. (2) jump to end of measure needs to pass the distance to end of measure, not the offset of end of measure.

…n hidden rest, not just for an inexpressible duration hidden rest. (2) jump to end of measure needs to pass the distance to end of measure, not the offset of end of measure.
@gregchapman-dev
Copy link
Contributor Author

This is the first split-out PR from PR #1768.

@coveralls
Copy link

coveralls commented Jul 26, 2025

Coverage Status

coverage: 93.009% (+0.01%) from 92.998%
when pulling 2fcf874 on gregchapman-dev:gregc/musicXmlWriterFixes
into eb5a88f on cuthbertLab:master.

@mscuthbert
Copy link
Member

Why do we prefer a gap to a complex rest? Isn't multiple rests even better?

@gregchapman-dev
Copy link
Contributor Author

I was going for the "least amount of change", and I knew a gap would work since we were already doing it for inexpressible durations.

@mscuthbert
Copy link
Member

Ah. It sounds more like a bug to fix that we do it for inexpressible. Let me look more closely there.

@mscuthbert
Copy link
Member

Are there tests that go with this? Something that would fail before and not fail now----I still don't have a clue what the problem is that this PR is solving! I must be very dense!

@mscuthbert
Copy link
Member

When you say gap are you saying forward tag?

Currently if I put a rest of 5.0 followed by a qtr note what happens in the xml and after this change what happens is I guess what I'm not clear on.

Forward tags are only appropriate in multi voice staves really.

@gregchapman-dev
Copy link
Contributor Author

Yes, the code (prior to this PR) was turning inexpressible hidden rests into forward tags, and I added code (in this PR) to also turn complex hidden rests into forward tags. I had some scores that had complex hidden rests in them, and they would crash during export to MusicXML without this fix. I think that if you want it, both inexpressible and complex hidden rests could be turned into a sequence of non-complex hidden rests (not sure about inexpressible, but it seems like I've read something somewhere about how to do it).

I can certainly add a test that tries to write a MusicXML file from a code-constructed score that has a complex hidden rest in it.

@gregchapman-dev
Copy link
Contributor Author

Oh, and answering your further comment: your example of hidden rest of 5.0 followed by a qtr note would crash before this change ('complex durations not writable to MusicXML', or something like that), and after the change would write a forward tag of duration 5.0 followed by a quarter note. (Note that this only happens for hidden rests.)

@mscuthbert
Copy link
Member

Great -- can you add that as a test and show. If you look at the Test class in test_m21ToXml it has a helper method self.getXml(m21Object) which will let you make simple assertions about the structure of XML

(Please don't add a assertEqual(self.getXml(...)), "long-quoted-string-with-lots-of-unrelated-xml") since this will require the test case to be rewritten if something unrelated changes`). Thanks

@gregchapman-dev
Copy link
Contributor Author

I used self.getET(obj) instead. Without my change this test raises MusicXMLException; with my change, it survives, and I check that the appropriate number of notes (1), forwards (1), and rests (0) are found in the ElementTree.

'''
complexRest = note.Rest()
complexRest.quarterLength = 5.0
complexRest.style.hideObjectOnPrint = True
Copy link
Member

Choose a reason for hiding this comment

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

ah -- these are hidden rests already -- okay -- that was an important part.

# if necessary, jump to end of the measure.
if self.offsetInMeasure < firstPassEndOffsetInMeasure:
self.moveForward(firstPassEndOffsetInMeasure)
self.moveForward(opFrac(firstPassEndOffsetInMeasure - self.offsetInMeasure))
Copy link
Member

Choose a reason for hiding this comment

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

Is this part of the same bug fix or a separate bug fix -- it doesn't seem to have anything to do with complex or not complex types? Thus is it actually being tested by the new tests below? I don't think so, so please test 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.

Unrelated. I'll try to write a test for 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.

For the record: this is a bug I introduced back when I was implementing SpannerAnchors in the first place. I assumed the wrong thing about what offset should be passed to moveForward. The end result was that MusicXML write from SpannerAnchor-y scores was pretty busted. And I only noticed now because we're making SpannerAnchors now during MusicXML read, so the bug is triggered.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Still working on this test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just pushed this new test.

Comment on lines 715 to 717
self.assertEqual(len(tree.findall('.//forward')), 1)
self.assertEqual(len(tree.findall('.//note')), 1)
self.assertEqual(len(tree.findall('.//rest')), 0)
Copy link
Member

Choose a reason for hiding this comment

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

Do we test that the forward tag moves forward 5 beats? It's little things like this that help make sure bugs don't creep in in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good idea. I'm on 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.

Just pushed this test improvement.

p.append(m)
s = stream.Score()
s.append(p)
tree = self.getET(s, makeNotation=False)
Copy link
Member

Choose a reason for hiding this comment

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

So all of this only applies if makeNotation is False?????? I'm flabbergasted -- tempted to remove it as an option. There is a reason why we make notation before showing -- to prevent crashes like this. Im really not interested in debugging anything else that comes forward only in makeNotation=False mode.

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'll try to avoid fixing things specific to makeNotation=False in the future. Sorry about that!

@gregchapman-dev
Copy link
Contributor Author

OK, I think that's all the tests you asked for.

@gregchapman-dev
Copy link
Contributor Author

This PR is ready for (re-)review.

if n.isRest and n.style.hideObjectOnPrint and n.duration.type == 'inexpressible':
if (n.isRest
and n.style.hideObjectOnPrint
and n.duration.type in ('inexpressible', 'complex')):
Copy link
Member

Choose a reason for hiding this comment

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

Greg -- 'complex' durations don't exist in m21ToXml unless makeNotation=False. I don't want fixes in here that cause different code paths for the two -- a complex duration in a stream at this point is an error.

@mscuthbert
Copy link
Member

There are still things specific to makeNotation=False here. Please remove.

@gregchapman-dev gregchapman-dev changed the title Two fixes in m21ToXML.py:parseFlatElements() One fix in m21ToXML.py:parseFlatElements() Aug 11, 2025
@gregchapman-dev
Copy link
Contributor Author

Ok, it's just the math fix now.

@mscuthbert
Copy link
Member

Thanks!

@mscuthbert mscuthbert merged commit f0d6d78 into cuthbertLab:master Aug 11, 2025
8 checks passed
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.

3 participants