You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A tuplet isn't a typical spanner in the sense that it modifies the duration of the notes it contains. It would be interesting to consider making Tuplets easy to author by giving them the single start/stop treatment as we contemplate with spanners.
However with tuplets there are some differences. A tuplet stops when it stops, by definition it lasts for a certain amount of musical time.
One thing that will come up with nested tuplets as well: truly nested tuplets don't seem like such a big problem, but in musicxml I believe you could have a "nested" tuplet where the "first" tuplet ends before the "nested" tuplet ends. I imagine someone like Ferneyhough has done this even though I've never seen it.
Anyway the idea is simple on its own, a tuplet object that either contains its notes or where the start conveys all the information such that the user doesn't have to modify each tuplet's note and enter the stop sentinel.
What makes tuplets hard to author is not the start/stop sentinels — it is that MusicXML spreads one musical fact across every member note. The author means "3 in the time of 2 eighths, starting here"; the encoding wants a cumulative <time-modification> on each note (nested tuplets multiply: 3:2 inside 3:2 is 9:4), <normal-type>/<normal-dot> only where the tuplet's unit differs from a note's own <type>, and paired <tuplet> notations with hand-allocated numbers. #428 settled that the writer transcribes these fields rather than inferring them, so the fussy knowledge needs an opt-in home: a helper that writes ordinary fields the author could have set by hand. The writer stays honest, fidelity is untouched, and the automatic experience the old inference gestured at gets delivered whole instead of half.
On shape: containment fights the model twice. Notes are already contained — voice vectors ordered by tick — so a tuplet object owning notes would be a second owner competing with the voice. And the Ferneyhough case rules it out: overlapping tuplets are not a tree, while <tuplet> number-pairing is flat and permits them. The start-conveys-all form survives both. The observation that a tuplet lasts a definite musical time is the key: TupletStart already carries actual/normal number, duration name, and dots, so the extent is derivable — normalNumber × the normal unit of musical time from the start. Both the stop sentinel and every member's ratio are computable from the start alone.
Two altitudes, not mutually exclusive:
A non-breaking helper now: takes the voice's notes, the member range (or just the start index, since the extent is derivable), and the ratio; multiplies it into each member's existing time-modification so nesting works by construction; sets normal-type only where the unit differs from the note's own type; allocates the lowest numberLevel unused across the overlap; appends the TupletStart/TupletStop. Ticks stay the author's positional truth (principle 1); the helper adds the descriptive layer and can verify the range's tick span matches the ratio. Misuse stays boring: an empty or misaligned range applies nothing.
The Support spanners specified as a single object instead of start and stop pairs. #393 treatment later: tuplets as single objects in the model — start position plus ratio — with the reader deriving them and the writer expanding them. That is a breaking redesign and belongs with the spanners-branch design; the helper's computation becomes its writer half, so (1) is a stepping stone, not a detour.
// Sketch only. Multiplies the ratio into each member note's time-modification,// sets timeModificationNormalType only where the unit differs from the note's// own durationName, allocates a free numberLevel, appends TupletStart/TupletStop.boolapplyTuplet(std::vector<NoteData>& voiceNotes,
size_t firstNoteIndex, size_t noteCount,
int actualNumber, int normalNumber,
DurationName unit, int unitDots);
Highly related to both #428 and #393.
A tuplet isn't a typical spanner in the sense that it modifies the duration of the notes it contains. It would be interesting to consider making Tuplets easy to author by giving them the single start/stop treatment as we contemplate with spanners.
However with tuplets there are some differences. A tuplet stops when it stops, by definition it lasts for a certain amount of musical time.
One thing that will come up with nested tuplets as well: truly nested tuplets don't seem like such a big problem, but in musicxml I believe you could have a "nested" tuplet where the "first" tuplet ends before the "nested" tuplet ends. I imagine someone like Ferneyhough has done this even though I've never seen it.
Anyway the idea is simple on its own, a tuplet object that either contains its notes or where the start conveys all the information such that the user doesn't have to modify each tuplet's note and enter the stop sentinel.
AI Notes
What makes tuplets hard to author is not the start/stop sentinels — it is that MusicXML spreads one musical fact across every member note. The author means "3 in the time of 2 eighths, starting here"; the encoding wants a cumulative
<time-modification>on each note (nested tuplets multiply: 3:2 inside 3:2 is 9:4),<normal-type>/<normal-dot>only where the tuplet's unit differs from a note's own<type>, and paired<tuplet>notations with hand-allocated numbers. #428 settled that the writer transcribes these fields rather than inferring them, so the fussy knowledge needs an opt-in home: a helper that writes ordinary fields the author could have set by hand. The writer stays honest, fidelity is untouched, and the automatic experience the old inference gestured at gets delivered whole instead of half.On shape: containment fights the model twice. Notes are already contained — voice vectors ordered by tick — so a tuplet object owning notes would be a second owner competing with the voice. And the Ferneyhough case rules it out: overlapping tuplets are not a tree, while
<tuplet>number-pairing is flat and permits them. The start-conveys-all form survives both. The observation that a tuplet lasts a definite musical time is the key:TupletStartalready carries actual/normal number, duration name, and dots, so the extent is derivable — normalNumber × the normal unit of musical time from the start. Both the stop sentinel and every member's ratio are computable from the start alone.Two altitudes, not mutually exclusive:
A non-breaking helper now: takes the voice's notes, the member range (or just the start index, since the extent is derivable), and the ratio; multiplies it into each member's existing time-modification so nesting works by construction; sets normal-type only where the unit differs from the note's own type; allocates the lowest numberLevel unused across the overlap; appends the TupletStart/TupletStop. Ticks stay the author's positional truth (principle 1); the helper adds the descriptive layer and can verify the range's tick span matches the ratio. Misuse stays boring: an empty or misaligned range applies nothing.
The Support spanners specified as a single object instead of start and stop pairs. #393 treatment later: tuplets as single objects in the model — start position plus ratio — with the reader deriving them and the writer expanding them. That is a breaking redesign and belongs with the spanners-branch design; the helper's computation becomes its writer half, so (1) is a stepping stone, not a detour.