Correct automatic beam subdivision, even under tuplets
As my Google Summer of Code project of summer 2023, these patches try to
fix defects of the current automatic beam subdivision algorithm in many
corner cases and complex beaming patterns. Some of these corner cases
include unusual baseMoments
and beatStructures
, but most notably
under tuplets and layers of nested tuplets. The current algorithm relies
on the value of baseMoment
and beatStructure
to tune parameters.
However, these values are intended for Auto_beam_engraver
to decide
whether to start or end beams, not necessarily for subdivision. As a
result, parameter tuning of automatic subdivision is heavily limited
with the current algorithm. See
https://gitlab.com/lilypond/lilypond/uploads/9f968b929162a0d70d212ac2e99ec841/beam-subdivisions--notes-for-reimplementation--urs-liska.pdf
Algorithm details
With these patches, the automatic subdivision algorithm no longer relies
on those values and may be tuned using two new Moment
properties:
minimumBeamSubdivisionInterval
and maximumBeamSubdivisionInterval
;
and boolean respectIncompleteBeams
. minimumBeamSubdivisionInterval
has default value of 0, maximumBeamSubdivisionInterval
has default
value of infinity. If we want to subdivide a beam of 8 consecutive 32nd
notes, they would be subdivided by 8th and 16th note levels, under the
default values, meaning that the beamlets between the 1st and 2nd note,
and 6th and 7th note are chipped by 1, and the beamlets between 4th and
5th note are chipped by 2. If maximumBeamSubdivisionInterval
is set to
1/16, the 8th note level would not be subdivided (beamlets between 4th
and 5th note are chipped by just 1). If minimumBeamSubdivisionInterval
is set to 1/8, the only beamlet chipping occurs between 4th and 5th note
by 2. All examples in the PDF linked above, which show the flaws of the
current beam subdivision algorithm versus a correct rendering, can be
correctly reproduced with these patches. Another flaw of the current
beam subdivision algorithm limits the amount of beams removed from a
subdividision when the remaining beam length cannot complete the metric
value of the subdivision. These patches remove this special rule, unless
property respectIncompleteBeams
, whose purpose is to preserve
compatibility with this special rule, is set.
As for the C++ source files edited, beaming-pattern.cc
has been
reworked. One notable change is that the start moments of stems provided
to Beaming_pattern
are absolute, rather than relative to the start of
the current measure. This change is needed to allow the possibility of
the first stem being part of a tuplet span that started in an earlier
measure.
Engravers Beam_engraver
, and Auto_beam_engraver
now inherit from a
new class Template_engraver_for_beams
that I made to reduce code
redundancy between the two. Tuplet_engraver
has been heavily modified
as these patches transform Tuplet_description
into a smob with which
the aforementioned engravers can interact (through internal context
property currentTupletDescription
).
Stem_engraver
no longer sets property tuplet-start
of Stem grobs as
my patches no longer depend on it.
Changes other than code
This patch introudces a couple of regression tests to test beam
subdivision and its quirks. 2 regression tests--beam-subdivision
and
beam-subdivide-quarter-notes
--, without any modifications, fail with
my patches due to their reliance of baseMoment
as a limit on beam
subdivision. This patch modifies those 2 regression tests by replacing
baseMoment
with minimumBeamSubdivideInterval
(I went ahead and
expanded beam-subdivision
to additionally test my 2 new properties.
One convert-ly rule has been added to warn people using
subdividingBeams
; Writing a convert-ly rule that automatically sets
minimumBeamSubdivisionInterval
to the value of baseMoment
seems
unfeasible, if not impossible.
The Rhythm section 1.2.4 of the Notation documentation has been edited.