-
Notifications
You must be signed in to change notification settings - Fork 406
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
WIP: Apply tuplet to multiple components to express 5/6 or 7/3 QL #763
Conversation
Ah! All this comes as a necessary consequence of a refactor around music21 v.2 where components do not have their own tuplets. In the past it would have been possible to express this:
as:
But the (huge speed increase and huge simplification) change to DurationTuples (from DurationComponent objects) made this impossible. What would be very helpful to put in this PR is a new attribute on the Duration object (and its slots)
this has an analogy to Pitch's Then
and be able to reconnect the first three 16ths under the tuplet into one eighth note. But with
From the strict=False example, later, we would be able to do fancy things like reconnect the final two 16th note triplets together if there is a 16th note triplet following it in the stream, etc. in the makeNotation or something like that. It would move closer to a very-long-term-goal of getting this Stream...
...to express itself properly as containing a triplet of eighth, dotted eighth, sixteenth, rather than a triplet eighth - break tuplet - normal eighth - start tuplet again - triplet 16th. None of that needs to go in this PR, but if |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
853e3ba
to
67b1d01
Compare
Well... closer.... Update: this "extra" bracket is a consequence of the mixture of tuplet types: the sixteenths are 3/2/16th, and the eighth and quarters are 3/2/quarter. So I experimented with letting the bracket extend over the mixed tuplets, which I can get music21 to represent, but musicxml readers will vary. Here is the diff: index c8d4309f1..eeafff764 100644
--- a/music21/stream/makeNotation.py
+++ b/music21/stream/makeNotation.py
@@ -1285,7 +1285,9 @@ def makeTupletBrackets(s, *, inPlace=False):
# this, below, is optional:
# if next normal type is not the same as this one, also stop
- elif tupletNext is None or completionCount >= completionTarget:
+ elif (tupletNext is None
+ or completionCount == completionTarget
+ or tupletPrevious.tupletMultiplier() != tupletObj.tupletMultiplier()):
tupletObj.type = 'stop' # should be impossible once frozen...
completionTarget = None # reset
completionCount = 0 # reset
@@ -1480,4 +1482,6 @@ class Test(unittest.TestCase):
if __name__ == '__main__':
import music21
- music21.mainTest(Test)
+ sch = music21.corpus.parse('schoenberg/opus19', 6)
+ sch.stripTies().show()
+ #music21.mainTest(Test) Finale closing the bracket early even though the "stop" is not set until the last quarter note: MuseScore with the less presumptive rendering: |
Btw -- I am still thinking about this -- sorry it's been so slow to get on it. It's a big thought about whether something that was wrong but "looked good" is preferable to something that's right but looks really poor on output. |
Sounds good, no rush. Just tested a file against it and there's a bug -- corrupt XML output, so I can set this back to draft for now. There have a been couple instances where I've thought .show() needs a makeNotation=False argument so that you can freeze what you have (for instance, if you've run makeNotation with splitAtDurations=False). That could be a way to accommodate both wrong-looking-good and right-looking-ugly scenarios. |
show definitely needs a makeNotation=False. But before we do that, I'm going to create a new issue to help get keywords from getting out of hand. |
… into components
Here's what would suffice for a "don't make notation" argument (which doubles as "don't make a deepcopy") index ecc445d38..9c6925e31 100644
--- a/music21/converter/subConverters.py
+++ b/music21/converter/subConverters.py
@@ -974,8 +974,15 @@ class ConverterMusicXML(SubConverter):
defaults.title = ''
defaults.author = ''
+ dataBytes: bytes = b''
generalExporter = m21ToXml.GeneralObjectExporter(obj)
- dataBytes: bytes = generalExporter.parse()
+ if ('Score' in obj.classes
+ and 'makeNotation' in keywords
+ and keywords['makeNotation'] is False):
+ # this keyword could have been wellFormed=True instead of makeNotation=False
+ dataBytes = generalExporter.parseWellFormedObject()
+ else:
+ dataBytes = generalExporter.parse()
writeDataStreamFp = fp
if fp is not None and subformats is not None: Given that, do you want to wait until a larger keywords refactor is tackled before dropping this in? |
jotting a note for future self: above patch isn't enough, because |
Hey @napulen happy to hear of your interest here. This ended up touching a lot of parts of the system, so here's my recap of where I think things stand right now:
So what's left is the magic ✨ For a little proof of concept for 1/3 QL - 1/3 QL - 1 QL (rest) - 1/3 QL I would think it would go like:
I don't want to take all the fun, so if you want to pair or split some of this up, send me a note. |
Hey @jacobtylerwalls, thanks for the great summary. Do you have a timeline for this? I want to go for the Humdrum voice issue first. Just to know when are you expecting to finish this PR. |
I would say May is the most likely point for me to dig in to the hard tasks here. I could probably get the expressionIsInferred bits and the don't makeNotation bits merged in smaller PRs first. So yeah, plenty of time to focus on Humdrum! |
Pieces of this were merged in other PRs, but the main idea -- applying a tuplet to multiple components -- can be more easily handled in a new PR once #904 is addressed; this one no longer applies cleanly. |
Fixed #572
Before: Durations such as 5/6 or 7/3 QL were expressed as non-power of 2 tuplets, which caused display problems.
Now: Upon extracting the largest type (such as 0.5 QL from 5/6 QL or 2.0 QL from 7/3 QL), if the remainder can be expressed as a single tuplet, express the largest type and the remainder in smaller components over which a tuplet can be applied.
Added support to the musicxml exporter for creating the necessary visualUpdate: added a call to remake tuplet brackets inside<tuplet>
elements from each split component.splitAtDurations()
Update: added the keywordUpdate: calls newsplitAtDurations
tomakeNotation()
and moved the logic in the xml exporter.splitAtDurations
method on Stream.Update: adds attribute/slot
expressionIsInferred
Update: extends tuplet brackets over tuplets that may not share the same type (3/2/eighth and 3/2/16th) but at least share the same ratio. This is to avoid unnecessarily proliferating start/stop brackets. Respected in MuseScore at least.