Skip to content

Conversation

@jacobtylerwalls
Copy link
Member

@jacobtylerwalls jacobtylerwalls commented Jan 26, 2026

Closes #1780

Before
consolidateCompletedTuplets() was violating the Hippocractic Oath of tuplet doctors: first, break no well-formed tuplets.

In more detail:

While scanning this stream...

s = converter.parse('tinyNotation: 2/4 trip{c8 d8 e8} trip{e8 e8 r8}')

the region spanning 1.0 QL from {e8 - e8 - e8} was replaced with a single e4, even though it broke the tuplets it spanned.

The reason was that the algorithm scanned the first e8, determined that it didn't match the pitch of the immediately prior d8, and reset all of the search state.

After
Now, search state is maintained regardless of whether a note is reexpressible or whether it matches the prior note. This way, search state is always cleared when a tuplet is completed, ensuring no candidates for consolidation are identified inside other well-formed tuplets.

@jacobtylerwalls jacobtylerwalls changed the title Jtw/tuplet follow ups Prevent consolidateCompletedTuplets() from breaking tuplets Jan 26, 2026
@jacobtylerwalls jacobtylerwalls changed the title Prevent consolidateCompletedTuplets() from breaking tuplets Prevent consolidateCompletedTuplets() from breaking well-formed tuplets Jan 26, 2026
@coveralls
Copy link

coveralls commented Jan 26, 2026

Coverage Status

coverage: 93.059%. remained the same
when pulling 30d9355 on jacobtylerwalls:jtw/tuplet-follow-ups
into b8306fc on cuthbertLab:master.

@jacobtylerwalls jacobtylerwalls changed the title Prevent consolidateCompletedTuplets() from breaking well-formed tuplets Prevent consolidateCompletedTuplets() from breaking complete tuplets Jan 26, 2026
# else: pass


class TupletSearchState: # pylint: disable=W0201
Copy link
Member Author

Choose a reason for hiding this comment

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

Happy to move this to another module if this is too exposed (or too confusing with TupletFixer).

The location in the duration module explains why you see stringified annotations or stringified gEBC() calls, because this module can't import note. So that would be ripe for a polish if there's a better location.

@jacobtylerwalls jacobtylerwalls marked this pull request as ready for review January 26, 2026 00:48
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.

consolidateCompletedTuplets combines rests across different triplets that causes malformed MusicXML output

2 participants