import abjad
[docs]def merge_partial_tuplets(selection: abjad.Selection,
*,
merge_across_barlines: bool = False,
) -> None:
r"""Mutates an input |abjad.Selection| in place and has no return value;
this function merges all consecutive partial tuplets with the same ratio
and which sum up to an assignable duration. Partial tuplets can result from
algorithmic manipulations such as phasing or looping, which can slice
through a tuplet.
Basic usage:
Usage is simple:
>>> staff = abjad.Staff(r"\times 2/3 {c'1} \times 2/3 {d'2}")
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
c'1
}
\times 2/3
{
d'2
}
}
.. figure:: ../_images/merge_partial_tuplets-ilr68s15kqb.png
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
c'1
d'2
}
}
.. figure:: ../_images/merge_partial_tuplets-qe29etsedx.png
.. note::
Auxjad automatically adds this function as an extension function to
|abjad.mutate|. It can thus be used from either |auxjad.mutate|_ or
|abjad.mutate| namespaces. Therefore, the two lines below are
equivalent:
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.mutate.merge_partial_tuplets(staff[:])
Multiple consecutive partial tuplets:
This function can also handle several consecutive partial tuplets:
>>> staff = abjad.Staff(
... r"\times 2/3 {c'2} \times 2/3 {d'2} \times 2/3 {e'2}"
... )
>>> abjad.show(staff)
.. docs::
{
\times 2/3
{
c'2
}
\times 2/3
{
d'2
}
\times 2/3
{
e'2
}
}
.. figure:: ../_images/merge_partial_tuplets-9rh7vpu208j.png
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
c'2
d'2
e'2
}
}
.. figure:: ../_images/merge_partial_tuplets-oy1imqisx2.png
``merge_across_barlines``:
By default, partial tuplets are not merged across barlines.
>>> staff = abjad.Staff(
... r"\time 3/4 c'2. "
... r"\times 2/3 {d'4} r4 \times 2/3 {e'2} "
... r"\times 2/3 {f'4} r4 \times 2/3 {g'2}"
... )
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.show(staff)
.. docs::
\new Staff
{
\time 3/4
c'2.
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
d'4
}
r4
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
e'2
}
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
f'4
}
r4
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
g'2
}
}
.. figure:: ../_images/merge_partial_tuplets-3rjib7pctml.png
To change this behaviour, set ``merge_across_barlines`` to ``True``.
>>> staff = abjad.Staff(
... r"\time 3/4 c'2. "
... r"\times 2/3 {d'4} r4 \times 2/3 {e'2} "
... r"\times 2/3 {f'4} r4 \times 2/3 {g'2}"
... )
>>> auxjad.mutate.merge_partial_tuplets(
... staff[:],
... merge_across_barlines=True,
... )
>>> abjad.show(staff)
.. docs::
\new Staff
{
\time 3/4
c'2.
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
d'4
}
r4
\times 2/3
{
e'2
f'4
}
r4
\tweak edge-height #'(0.7 . 0)
\times 2/3
{
g'2
}
}
.. figure:: ../_images/merge_partial_tuplets-icud1ejcmzc.png
Tied partial tuplets:
Tied partial tuplets are also handled by this function.
>>> staff = abjad.Staff(
... r"\times 2/3 {r4} \times 2/3 {c'2} "
... r"\times 4/5 {d'2~} \times 4/5{d'8}"
... )
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
r4
}
\times 2/3
{
c'2
}
\times 4/5
{
d'2
~
}
\times 4/5
{
d'8
}
}
.. figure:: ../_images/merge_partial_tuplets-st4zw38qfce.png
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
r4
c'2
}
\times 4/5
{
d'2
~
d'8
}
}
.. figure:: ../_images/merge_partial_tuplets-1pky5fsh2nl.png
Indicators:
Indicators stay the same in the merged tuplet.
>>> staff = abjad.Staff(
... r"\times 2/3 {c'2\p\< d'2} \times 2/3 {e'2\ff}"
... )
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
c'2
\p
\<
d'2
}
\times 2/3
{
e'2
\ff
}
}
.. figure:: ../_images/merge_partial_tuplets-7cdtafl348h.png
>>> auxjad.mutate.merge_partial_tuplets(staff[:])
>>> abjad.show(staff)
.. docs::
\new Staff
{
\times 2/3
{
c'2
\p
\<
d'2
e'2
\ff
}
}
.. figure:: ../_images/merge_partial_tuplets-j9rmdfbawce.png
.. tip::
The method |auxjad.mutate.extract_trivial_tuplets()| can be used
after merging partial tuplets to further clean the output. The method
|auxjad.mutate.auto_rewrite_meter()| can also be used for this
purpose, as it will not only rewrite the metric notation of a staff but
also apply both |auxjad.mutate.merge_partial_tuplets()| and
|auxjad.mutate.extract_trivial_tuplets()| to the output.
.. note::
When using |abjad.Container|'s, all time signatures in the output will
be commented out with ``%%%.`` This is because Abjad only applies time
signatures to containers that belong to a |abjad.Staff|. The present
function works with either |abjad.Container| and |abjad.Staff|.
>>> container = abjad.Container(r"\time 3/4 c'4 d'4 e'4")
>>> abjad.show(container)
.. docs::
{
%%% \time 3/4 %%%
c'4
d'4
e'4
}
.. figure:: ../_images/merge_partial_tuplets-945s36mfdn.png
>>> staff = abjad.Staff([container])
>>> abjad.show(container)
.. docs::
{
\time 3/4
c'4
d'4
e'4
}
.. figure:: ../_images/merge_partial_tuplets-3b4tyqrnttw.png
.. warning::
The input selection must be a contiguous logical voice. When dealing
with a container with multiple subcontainers (e.g. a score containing
multiple staves), the best approach is to cycle through these
subcontainers, applying this function to them individually.
"""
if not isinstance(selection, abjad.Selection):
raise TypeError("argument must be 'abjad.Selection'")
if not isinstance(merge_across_barlines, bool):
raise TypeError("'merge_across_barlines' must be 'bool'")
tuplets = selection.tuplets()
if len(tuplets) <= 1:
return
def _process_tuplets(tuplets):
for index in range(len(tuplets[:-1])):
for upper_index in range(index, len(tuplets)):
if (tuplets[index].multiplier
== tuplets[upper_index].multiplier):
tuplet_group = tuplets[index:upper_index + 1]
else:
break
if tuplet_group.are_contiguous_logical_voice():
durations = [abjad.get.duration(tuplet)
for tuplet in tuplet_group]
sum_durations = sum(durations)
if (all(duration.implied_prolation != 1
for duration in durations)
and sum_durations.implied_prolation == 1):
for tuplet in tuplet_group[1:]:
tuplet_group[0].extend(tuplet)
abjad.mutate.extract(tuplet)
if not merge_across_barlines:
measures = selection.group_by_measure()
for measure in measures:
tuplets = abjad.select(measure).tuplets()
if len(tuplets) <= 1:
continue
_process_tuplets(tuplets)
else:
_process_tuplets(tuplets)