Example #1
0
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)
def rests_to_multimeasure_rest(selection: abjad.Selection,
                               *,
                               ignore_clefs: bool = False,
                               ) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function looks for measures filled with regular rests and converts
    them into an |abjad.MultimeasureRest|.

    Basic usage:
        Converts any measure filled with regular rests into a measure with a
        single multi-measure rest.

        >>> container = abjad.Container(r"r1")
        >>> auxjad.mutate(container[:]).rests_to_multimeasure_rest()
        >>> abjad.f(container)
        {
            R1
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-uvebc7ft1zo.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).rests_to_multimeasure_rest()
        >>> abjad.mutate(staff[:]).rests_to_multimeasure_rest()

    Multiple rests:
        Works with measures with multiple regular rests.

        >>> container = abjad.Container(r"r2 r8.. r32 r16 r8 r16")
        >>> abjad.f(container)
        {
            r2
            r8..
            r32
            r16
            r8
            r16
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-jk4m1wzsyfa.png

        >>> auxjad.mutate(container[:]).rests_to_multimeasure_rest()
        >>> abjad.f(container)
        {
            R1
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-z8u0cs3fzdi.png

    .. 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.f(container)
        {
            %%% \time 3/4 %%%
            c'4
            d'4
            e'4
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-qtq55xbkkts.png

        >>> staff = abjad.Staff([container])
        >>> abjad.f(container)
        {
            \time 3/4
            c'4
            d'4
            e'4
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-9hceg93vrmv.png

    Time signature changes:
        Works with selections from containers with multiple time signatures as
        well as notes.

        >>> container = abjad.Staff(r"\time 3/4 r2. | "
        ...                         "\time 6/8 r2. | "
        ...                         "\time 5/4 c'1 ~ c'4 | r1 r4"
        ...                         )
        >>> auxjad.mutate(container[:]).rests_to_multimeasure_rest()
        >>> abjad.f(container)
        \new Staff
        {
            \time 3/4
            R1 * 3/4
            \time 6/8
            R1 * 3/4
            \time 5/4
            c'1
            ~
            c'4
            R1 * 5/4
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-a9sqdcznoq.png

    Tuplets:
        Works with containers with tuplets.

        >>> container = abjad.Container(r"\times 2/3 {r2 r2 r2}")
        >>> abjad.f(container)
        {
            \times 2/3 {
                r2
                r2
                r2
            }
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-480a9zqvk2a.png

        >>> auxjad.mutate(container[:]).rests_to_multimeasure_rest()
        >>> abjad.f(container)
        {
            R1
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-r5yg3a3f97q.png

        It also works with containers with tuplets within tuplets.

        >>> container = abjad.Container(
        ...     r"r2 \times 2/3 {r2 r4} \times 4/5 {r2. \times 2/3 {r2 r4}}"
        ... )
        >>> abjad.f(container)
        {
            r2
            \times 2/3 {
                r2
                r4
            }
            \times 4/5 {
                r2.
                \times 2/3 {
                    r2
                    r4
                }
            }
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-codydc205jw.png

        >>> auxjad.mutate(container[:]).rests_to_multimeasure_rest()
        >>> abjad.f(container)
        {
            R1
            R1
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-f647t5j3jgw.png

    ``ignore_clefs``
        By default, the last clef of an empty measure is preserved when
        replacing it with a multi-measure rest:

        >>> staff = abjad.Staff(r"\clef bass r4 r4 \times 2/3 {r4 r8} r4 "
        ...                     r"\time 3/4 \clef treble r2. "
        ...                     r"\time 5/4 r2 \clef bass r2."
        ...                     )
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "bass"
            r4
            r4
            \times 2/3 {
                r4
                r8
            }
            r4
            \time 3/4
            \clef "treble"
            r2.
            \time 5/4
            r2
            \clef "bass"
            r2.
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-6GMRGmYkEQ.png

        >>> abjad.mutate(staff[:]).rests_to_multimeasure_rest()
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "bass"
            R1
            \time 3/4
            \clef "treble"
            R1 * 3/4
            \time 5/4
            \clef "bass"
            R1 * 5/4
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-UnL6ZoFoDC.png

        Invoke the mutation with ``ignore_clefs`` set to ``True`` to disable
        this behaviour and ignore all clefs:

        >>> staff = abjad.Staff(r"\clef bass r4 r4 \times 2/3 {r4 r8} r4 "
        ...                     r"\time 3/4 \clef treble r2. "
        ...                     r"\time 5/4 r2 \clef bass r2."
        ...                     )
        >>> abjad.mutate(staff[:]).rests_to_multimeasure_rest(
        ...     ignore_clefs=True,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            R1
            \time 3/4
            R1 * 3/4
            \time 5/4
            R1 * 5/4
        }

        .. figure:: ../_images/rests_to_multimeasure_rest-KGRZJ8fvQF.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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(ignore_clefs, bool):
        raise TypeError("'ignore_clefs' must be 'bool'")

    measures = selection.group_by_measure()
    effective_time_signature = abjad.TimeSignature((4, 4))
    for measure in measures:
        head = abjad.select(measure).leaf(0)
        time_signature = abjad.inspect(head).indicator(abjad.TimeSignature)
        if time_signature is not None:
            effective_time_signature = time_signature
        if all([isinstance(leaf, abjad.Rest) for leaf in measure.leaves()]):
            if not ignore_clefs:
                for leaf in measure.leaves()[::-1]:
                    clef = abjad.inspect(leaf).indicator(abjad.Clef)
                    if clef is not None:
                        break
            duration = abjad.inspect(measure).duration()
            if duration == effective_time_signature.duration:
                if duration == 1:
                    multiplier = None
                else:
                    multiplier = abjad.Multiplier(duration)
                multimeasure_rest = abjad.MultimeasureRest(
                    (4, 4),
                    multiplier=multiplier,
                )
                if time_signature is not None:
                    abjad.attach(time_signature, multimeasure_rest)
                if not ignore_clefs and clef is not None:
                    abjad.attach(clef, multimeasure_rest)
                for indicator in abjad.inspect(head).indicators():
                    if isinstance(indicator, (abjad.BarLine,
                                              abjad.Fermata,
                                              abjad.KeySignature,
                                              abjad.LilyPondLiteral,
                                              abjad.MetronomeMark,
                                              abjad.RehearsalMark,
                                              abjad.Repeat,
                                              abjad.StaffChange,
                                              abjad.StartMarkup,
                                              abjad.StartTextSpan,
                                              abjad.StopTextSpan,
                                              )):
                        abjad.attach(indicator, multimeasure_rest)
                abjad.mutate(measure).replace(multimeasure_rest)
Example #3
0
def merge_hairpins(selection: abjad.Selection) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function merges all consecutive hairpins that share a same trend.

    Basic usage:
        Merging crescendo hairpins:

        >>> staff = abjad.Staff(r"c'4\pp\< d'4 e'4\p\< f'4 g'1\mp")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                \p
                \<
                f'4
                g'1
                \mp
            }

        ..  figure:: ../_images/merge_hairpins-b4it6YReZ9.png

        >>> abjad.mutate.merge_hairpins(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                f'4
                g'1
                \mp
            }

        ..  figure:: ../_images/merge_hairpins-s5cU8PINVO.png

        Merging diminuendo hairpins:

        >>> staff = abjad.Staff(r"c'4\ff\> d'4 e'4\mf\> f'4 g'1\pp")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \ff
                \>
                d'4
                e'4
                \mf
                \>
                f'4
                g'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-GUIqXKDXaQ.png

        >>> abjad.mutate.merge_hairpins(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \ff
                \>
                d'4
                e'4
                f'4
                g'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-adbT777qMg.png

        The merge only occurs when hairpins share a trend:

        >>> staff = abjad.Staff(r"c'4\pp\< d'4 e'4\p\> f'4 g'1\pp")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                \p
                \>
                f'4
                g'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-Lvzn2f6PU6.png

        >>> abjad.mutate.merge_hairpins(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                \p
                \>
                f'4
                g'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-EJdWJwkUJE.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(staff[:]).merge_hairpins()
        >>> abjad.mutate(staff[:]).merge_hairpins()

    Gaps:
        If there are any gaps between hairpins (i.e. they finish on an earlier
        leaf than the start of the new hairpin), they will not be merged even
        if they share a trend.

        >>> staff = abjad.Staff(
        ...     r"c'1\pp\< d'2\f d'2\< e'1\ff f'1\f\> "
        ...     r"g'2\mp g'2\> a'1\! b'1\ppp"
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \pp
                \<
                d'2
                \f
                d'2
                \<
                e'1
                \ff
                f'1
                \f
                \>
                g'2
                \mp
                g'2
                \>
                a'1
                \!
                b'1
                \ppp
            }

        ..  figure:: ../_images/merge_hairpins-3Bo4GAfHaX.png

        >>> auxjad.mutate.merge_hairpins(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \pp
                \<
                d'2
                \f
                d'2
                \<
                e'1
                \ff
                f'1
                \f
                \>
                g'2
                \mp
                g'2
                \>
                a'1
                \!
                b'1
                \ppp
            }

        ..  figure:: ../_images/merge_hairpins-HaF1ZUdqtx.png

    Multiple merges:
        This function can merge an indefinite number of hairpins:

        >>> staff = abjad.Staff(
        ...     r"c'4\pp\< d'4 e'4\p\< f'4 g'4\mp\< a'4 b'4\mf\< c''4"
        ...     r"d''4\f\> c''4 b'4\mf\> a'4 g'4\mp\> f'4 e'4\p\> d'4"
        ...     r"c'1\pp"
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                \p
                \<
                f'4
                g'4
                \mp
                \<
                a'4
                b'4
                \mf
                \<
                c''4
                d''4
                \f
                \>
                c''4
                b'4
                \mf
                \>
                a'4
                g'4
                \mp
                \>
                f'4
                e'4
                \p
                \>
                d'4
                c'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-DrT5XyaGWQ.png

        >>> auxjad.mutate.merge_hairpins(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                \pp
                \<
                d'4
                e'4
                f'4
                g'4
                a'4
                b'4
                c''4
                d''4
                \f
                \>
                c''4
                b'4
                a'4
                g'4
                f'4
                e'4
                d'4
                c'1
                \pp
            }

        ..  figure:: ../_images/merge_hairpins-Z2yDxl8dlI.png
    """
    if not isinstance(selection, abjad.Selection):
        raise TypeError("argument must be 'abjad.Selection'")
    active_hairpin = None
    for leaf in selection.leaves():
        leaf_hairpin = abjad.get.indicator(leaf, abjad.StartHairpin)
        if active_hairpin is not None and leaf_hairpin is not None:
            if active_hairpin.shape == leaf_hairpin.shape:
                abjad.detach(abjad.StartHairpin, leaf)
                if abjad.get.indicator(leaf, abjad.Dynamic) is not None:
                    abjad.detach(abjad.Dynamic, leaf)
        if (abjad.get.indicator(leaf, abjad.Dynamic) is not None
                or abjad.get.indicator(leaf, abjad.StopHairpin) is not None):
            if leaf_hairpin is None:
                active_hairpin = None
        if leaf_hairpin is not None:
            active_hairpin = leaf_hairpin
Example #4
0
def rests_to_multimeasure_rest(
    selection: abjad.Selection,
    *,
    ignore_clefs: bool = False,
    ignore_dynamics: bool = False,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function looks for measures filled with regular rests and converts
    them into an |abjad.MultimeasureRest|.

    Basic usage:
        Converts any measure filled with regular rests into a measure with a
        single multi-measure rest.

        >>> container = abjad.Container(r"\time 3/4 r2.")
        >>> abjad.show(container)

        ..  docs::

            {
                \time 3/4
                r2.
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-VIj5iWyLCG.png

        >>> auxjad.mutate.rests_to_multimeasure_rest(container[:])
        >>> abjad.show(container)

        ..  docs::

            {
                \time 3/4
                R1 * 3/4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-uvebc7ft1zo.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.rests_to_multimeasure_rest(staff[:])
        >>> abjad.mutate.rests_to_multimeasure_rest(staff[:])

    Multiple rests:
        Works with measures with multiple regular rests.

        >>> container = abjad.Container(r"r2 r8.. r32 r16 r8 r16")
        >>> abjad.show(container)

        ..  docs::

            {
                r2
                r8..
                r32
                r16
                r8
                r16
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-jk4m1wzsyfa.png

        >>> auxjad.mutate.rests_to_multimeasure_rest(container[:])
        >>> abjad.show(container)

        ..  docs::

            {
                R1
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-z8u0cs3fzdi.png

    ..  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/rests_to_multimeasure_rest-qtq55xbkkts.png

        >>> staff = abjad.Staff([container])
        >>> abjad.show(container)

        ..  docs::

            {
                \time 3/4
                c'4
                d'4
                e'4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-9hceg93vrmv.png

    Time signature changes:
        Works with selections from containers with multiple time signatures as
        well as notes.

        >>> container = abjad.Staff(
        ...     r"\time 3/4 r2. | "
        ...     "\time 6/8 r2. | "
        ...     "\time 5/4 c'1 ~ c'4 | r1 r4"
        ... )
        >>> abjad.show(container)

        ..  docs::

            \new Staff
            {
                \time 3/4
                r2.
                \time 6/8
                r2.
                \time 5/4
                c'1
                ~
                c'4
                r1
                r4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-oQfFRihzEY.png

        >>> auxjad.mutate.rests_to_multimeasure_rest(container[:])
        >>> abjad.show(container)

        ..  docs::

            \new Staff
            {
                \time 3/4
                R1 * 3/4
                \time 6/8
                R1 * 3/4
                \time 5/4
                c'1
                ~
                c'4
                R1 * 5/4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-a9sqdcznoq.png

    Tuplets:
        Works with containers with tuplets.

        >>> container = abjad.Container(r"\times 2/3 {r2 r2 r2}")
        >>> abjad.show(container)

        ..  docs::

            {
                \times 2/3
                {
                    r2
                    r2
                    r2
                }
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-480a9zqvk2a.png

        >>> auxjad.mutate.rests_to_multimeasure_rest(container[:])
        >>> abjad.show(container)

        ..  docs::

            {
                R1
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-r5yg3a3f97q.png

        It also works with containers with tuplets within tuplets.

        >>> container = abjad.Container(
        ...     r"r2 \times 2/3 {r2 r4} \times 4/5 {r2. \times 2/3 {r2 r4}}"
        ... )
        >>> abjad.show(container)

        ..  docs::

            {
                r2
                \times 2/3
                {
                    r2
                    r4
                }
                \times 4/5
                {
                    r2.
                    \times 2/3
                    {
                        r2
                        r4
                    }
                }
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-codydc205jw.png

        >>> auxjad.mutate.rests_to_multimeasure_rest(container[:])
        >>> abjad.show(container)

        ..  docs::

            {
                R1
                R1
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-f647t5j3jgw.png

    ``ignore_clefs``
        By default, the last clef of an empty measure is preserved when
        replacing it with a multi-measure rest:

        >>> staff = abjad.Staff(
        ...     r"\clef bass r4 r4 \times 2/3 {r4 r8} r4 "
        ...     r"\time 3/4 \clef treble r2. "
        ...     r"\time 5/4 r2 \clef bass r2."
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \clef "bass"
                r4
                r4
                \times 2/3
                {
                    r4
                    r8
                }
                r4
                \time 3/4
                \clef "treble"
                r2.
                \time 5/4
                r2
                \clef "bass"
                r2.
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-6GMRGmYkEQ.png

        >>> abjad.mutate.rests_to_multimeasure_rest(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \clef "bass"
                R1
                \time 3/4
                \clef "treble"
                R1 * 3/4
                \time 5/4
                \clef "bass"
                R1 * 5/4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-UnL6ZoFoDC.png

        Invoke the mutation with ``ignore_clefs`` set to ``True`` to disable
        this behaviour and ignore all clefs:

        >>> staff = abjad.Staff(
        ...     r"\clef bass r4 r4 \times 2/3 {r4 r8} r4 "
        ...     r"\time 3/4 \clef treble r2. "
        ...     r"\time 5/4 r2 \clef bass r2."
        ... )
        >>> abjad.mutate.rests_to_multimeasure_rest(
        ...     staff[:],
        ...     ignore_clefs=True,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                R1
                \time 3/4
                R1 * 3/4
                \time 5/4
                R1 * 5/4
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-KGRZJ8fvQF.png

    ``ignore_dynamics``
        By default, the last dynamic or hairpin of an empty measure is
        preserved when replacing it with a multi-measure rest:

        >>> staff = abjad.Staff(r"c'1\p\< r2\! r2 d'1\f\> r2 r2\ppp")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                r2
                \!
                r2
                d'1
                \f
                \>
                r2
                r2
                \ppp
            }


        ..  figure:: ../_images/rests_to_multimeasure_rest-J9T5UY8r9w.png

        >>> abjad.mutate.rests_to_multimeasure_rest(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                R1
                \!
                d'1
                \f
                \>
                R1
                \ppp
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-77r9QeaZBA.png

        Invoke the mutation with ``ignore_dynamics`` set to ``True`` to disable
        this behaviour and ignore all dynamics and hairpins:

        >>> staff = abjad.Staff(r"c'1\p\< r2\! r2 d'1\f\> r2 r2\ppp")
        >>> abjad.mutate.rests_to_multimeasure_rest(
        ...     staff[:],
        ...     ignore_dynamics=True,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                R1
                d'1
                \f
                \>
                R1
            }

        ..  figure:: ../_images/rests_to_multimeasure_rest-3UhZukx9Pw.png

        ..  warning::

            Note that dynamics are only ignored when converting rests to
            multi-measure rests. All other dynamics are preserved in the score.
            This can result in problems displaying dynamics when one or more
            unterminated hairpins is present. In the example above, the last
            note's hairpin is unterminated and, because of that, LilyPond
            ignores all dynamics in that staff:

            >>> staff = abjad.Staff(r"c'1\p\< r2\! r2 d'1\f\> r2 r2\ppp")
            >>> abjad.mutate.rests_to_multimeasure_rest(
            ...     staff[:],
            ...     ignore_dynamics=True,
            ... )
            >>> string = abjad.lilypond(staff)
            >>> print(string)
            \new Staff
            {
                c'1
                \p
                \<
                R1
                d'1
                \f
                \>
                R1
            }

    ..  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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(ignore_clefs, bool):
        raise TypeError("'ignore_clefs' must be 'bool'")
    if not isinstance(ignore_dynamics, bool):
        raise TypeError("'ignore_dynamics' must be 'bool'")

    measures = selection.group_by_measure()
    effective_time_signature = abjad.TimeSignature((4, 4))
    indicators_tuple = (
        abjad.BarLine,
        abjad.Fermata,
        abjad.KeySignature,
        abjad.LilyPondLiteral,
        abjad.MetronomeMark,
        abjad.RehearsalMark,
        abjad.Repeat,
        abjad.StaffChange,
        abjad.StartMarkup,
        abjad.StartTextSpan,
        abjad.StopTextSpan,
    )

    for measure in measures:
        head = abjad.select(measure).leaf(0)
        time_signature = abjad.get.indicator(head, abjad.TimeSignature)
        if time_signature is not None:
            effective_time_signature = time_signature
        if all([isinstance(leaf, abjad.Rest) for leaf in measure.leaves()]):
            if not ignore_clefs:
                for leaf in measure.leaves()[::-1]:
                    clef = abjad.get.indicator(leaf, abjad.Clef)
                    if clef is not None:
                        break
            if not ignore_dynamics:
                for leaf in measure.leaves()[::-1]:
                    dynamics = abjad.get.indicator(leaf, (
                        abjad.Dynamic,
                        abjad.StartHairpin,
                        abjad.StopHairpin,
                    ))
                    if dynamics is not None:
                        break
            duration = abjad.get.duration(measure)
            if duration == effective_time_signature.duration:
                if duration == 1:
                    multiplier = None
                else:
                    multiplier = abjad.Multiplier(duration)
                multimeasure_rest = abjad.MultimeasureRest(
                    (4, 4),
                    multiplier=multiplier,
                )
                if time_signature is not None:
                    abjad.attach(time_signature, multimeasure_rest)
                if not ignore_clefs and clef is not None:
                    abjad.attach(clef, multimeasure_rest)
                if not ignore_dynamics and dynamics is not None:
                    abjad.attach(dynamics, multimeasure_rest)
                for indicator in abjad.get.indicators(head):
                    if isinstance(indicator, indicators_tuple):
                        abjad.attach(indicator, multimeasure_rest)
                abjad.mutate.replace(measure, multimeasure_rest)
Example #5
0
def prettify_rewrite_meter(
    selection: abjad.Selection,
    meter: Union[abjad.Meter, abjad.TimeSignature],
    *,
    fuse_across_groups_of_beats: bool = True,
    fuse_quadruple_meter: bool = True,
    fuse_triple_meter: bool = True,
    extract_trivial_tuplets: bool = True,
    split_quadruple_meter: bool = True,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function fuses pitched leaves according to the rules shown below,
    improving the default output of |abjad.mutate().rewrite_meter()|.

    Basic usage:
        Meters whose denominators are a crotchet or longer get tied notes
        within a beat after |abjad.mutate().rewrite_meter()| when they are at
        an offset ``denominator / 4``, so a rhythm such as  ``denominator / 4``
        ``denominator / 2`` ``denominator / 4`` becomes ``denominator / 4``
        ``denominator / 4`` ``~`` ``denominator / 4`` ``denominator / 4``. This
        function looks for those specific cases and fuses them, generating an
        output which is often more readable.

        >>> staff = abjad.Staff(
        ...     r"\time 3/4 c'16 d'8 e'16 f'16 g'16 a'8 b'8 c''16 d''16"
        ... )
        >>> meter = abjad.Meter((3, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'16
            d'16
            ~
            d'16
            e'16
            f'16
            g'16
            a'8
            b'8
            c''16
            d''16
        }

        .. figure:: ../_images/prettify_rewrite_meter-vlnd7l5fb7s.png

        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'16
            d'8
            e'16
            f'16
            g'16
            a'8
            b'8
            c''16
            d''16
        }

        .. figure:: ../_images/prettify_rewrite_meter-e7vfnese0ut.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.mutate(staff[:]).prettify_rewrite_meter(meter)

    Other examples:
        The rhythm of the leaves just before and after the two leaves to be
        fused can be different than ``denominator / 4``, as the function
        searches for logical ties of specific length and offset, and its
        surroundings do not matter.

        >>> staff = abjad.Staff(r"\time 3/4 c'32 d'32 e'8 f'16 "
        ...                     r"\times 2/3 {g'32 a'32 b'32} c''8 "
        ...                     r"r16 r32. d''64 e''8 f''32 g''32"
        ...                     )
        >>> meter = abjad.Meter((3, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'32
            d'32
            e'16
            ~
            e'16
            f'16
            \times 2/3 {
                g'32
                a'32
                b'32
            }
            c''16
            ~
            c''16
            r16
            r32.
            d''64
            e''16
            ~
            e''16
            f''32
            g''32
        }

        .. figure:: ../_images/prettify_rewrite_meter-kw09gse2zxj.png

        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'32
            d'32
            e'8
            f'16
            \times 2/3 {
                g'32
                a'32
                b'32
            }
            c''8
            r16
            r32.
            d''64
            e''8
            f''32
            g''32
        }

        .. figure:: ../_images/prettify_rewrite_meter-u5gmtdippsa.png

    ``fuse_across_groups_of_beats``:
        By default, this function also fuses rhythms of type
        ``denominator / 2`` ``denominator / 2`` ``~`` ``denominator / 2``
        ``denominator / 2``, becoming ``denominator / 2`` ``denominator``
        ``denominator / 2``. This is only applied when the meter's structure
        has a depth of 2, which is the case for meters with numerators equal to
        or larger than ``5``.

        >>> staff = abjad.Staff(r"\time 6/4 c'8 d'4 e'4 f'4 g'4 a'4 b'8")
        >>> meter = abjad.Meter((6, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 6/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'8
            ~
            f'8
            g'8
            ~
            g'8
            a'8
            ~
            a'8
            b'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-tqi4p0u8qog.png

        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 6/4
            c'8
            d'4
            e'4
            f'8
            ~
            f'8
            g'4
            a'4
            b'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-riif1glyqpo.png

        to disable this behaviour, set the optional keyword argument
        ``fuse_across_groups_of_beats`` to ``False``.

        >>> staff = abjad.Staff(r"\time 6/4 c'8 d'4 e'4 f'4 g'4 a'4 b'8")
        >>> meter = abjad.Meter((6, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(
        ...     meter,
        ...     fuse_across_groups_of_beats=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            \time 6/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'8
            ~
            f'8
            g'8
            ~
            g'8
            a'8
            ~
            a'8
            b'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-ki5xbiteij.png

    |abjad.Meter| with ``increase_monotonic=True``:
        The fused notes will respect the beat structures of such meters, even
        when ``increase_monotonic`` is set to the non-default value ``True``.
        Compare the outputs below.

        >>> staff = abjad.Staff(r"\time 7/4 c'8 d'4 e'4 f'4 g'4 a'4 b'4 c''8")
        >>> meter = abjad.Meter((7, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 7/4
            c'8
            d'4
            e'4
            f'8
            ~
            f'8
            g'4
            a'8
            ~
            a'8
            b'4
            c''8
        }

        .. figure:: ../_images/prettify_rewrite_meter-bud0jhkvvl.png

        >>> staff = abjad.Staff(r"\time 7/4 c'8 d'4 e'4 f'4 g'4 a'4 b'4 c''8")
        >>> meter = abjad.Meter((7, 4), increase_monotonic=True)
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 7/4
            c'8
            d'4
            e'8
            ~
            e'8
            f'4
            g'8
            ~
            g'8
            a'4
            b'4
            c''8
        }

        .. figure:: ../_images/prettify_rewrite_meter-47y86pbwwv5.png

    Multiple measures at once:
        This function can take handle multiple measures at once, as long as
        they share the same meter.

        >>> staff = abjad.Staff(r"\time 5/8 c'16 d'8 e'8 f'8 g'8 a'16 ~ "
        ...                     r"a'16 b'8 c''8 d''8 e''8 f''16"
        ...                     )
        >>> meter = abjad.Meter((5, 8))
        >>> for measure in abjad.select(staff[:]).group_by_measure():
        ...     abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 5/8
            c'16
            d'16
            ~
            d'16
            e'16
            ~
            e'16
            f'16
            ~
            f'16
            g'16
            ~
            g'16
            a'16
            ~
            a'16
            b'16
            ~
            b'16
            c''16
            ~
            c''16
            d''16
            ~
            d''16
            e''16
            ~
            e''16
            f''16
        }

        .. figure:: ../_images/prettify_rewrite_meter-8jdzmvf9yl.png

        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 5/8
            c'16
            d'8
            e'8
            f'16
            ~
            f'16
            g'8
            a'16
            ~
            a'16
            b'8
            c''8
            d''16
            ~
            d''16
            e''8
            f''16
        }

        .. figure:: ../_images/prettify_rewrite_meter-pcn8x9hr6bb.png

    Multiple measures:
        Similarly to |abjad.mutate().rewrite_meter()|, this function accepts
        selections of multiple measures:

        >>> staff = abjad.Staff(r"\time 4/4 c'8 d'4 e'4 f'4 g'8 | "
        ...                     r"a'8 b'4 c''8 d''16 e''4 f''8.")
        >>> meter = abjad.Meter((4, 4))
        >>> for measure in abjad.select(staff[:]).group_by_measure():
        ...     abjad.mutate(measure).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 4/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'8
            ~
            f'8
            g'8
            a'8
            b'8
            ~
            b'8
            c''8
            d''16
            e''8.
            ~
            e''16
            f''8.
        }

        .. figure:: ../_images/prettify_rewrite_meter-s8fg7a2k0tr.png

        >>> for measure in abjad.select(staff[:]).group_by_measure():
        ...     auxjad.mutate(measure).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 4/4
            c'8
            d'4
            e'8
            ~
            e'8
            f'4
            g'8
            a'8
            b'4
            c''8
            d''16
            e''8.
            ~
            e''16
            f''8.
        }

        .. figure:: ../_images/prettify_rewrite_meter-rgd7ok7fkq.png

    Multiple measures with different meters:
        If the measures have different meters, they can be passed on
        individually using :func:`zip()` as shown below.

        >>> staff = abjad.Staff(r"\time 3/4 c'8 d'4 e'4 f'16 g'16 | "
        ...                     r"\time 4/4 a'8 b'4 c''8 d''16 e''4 f''8.")
        >>> meters = [abjad.Meter((3, 4)), abjad.Meter((4, 4))]
        >>> for meter, measure in zip(
        ...     meters,
        ...     abjad.select(staff[:]).group_by_measure(),
        ... ):
        ...     abjad.mutate(measure).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'16
            g'16
            \time 4/4
            a'8
            b'8
            ~
            b'8
            c''8
            d''16
            e''8.
            ~
            e''16
            f''8.
        }

        .. figure:: ../_images/prettify_rewrite_meter-o2izz0m7s9k.png

        >>> for meter, measure in zip(
        ...     meters,
        ...     abjad.select(staff[:]).group_by_measure(),
        ... ):
        ...     auxjad.mutate(measure).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'8
            d'4
            e'4
            f'16
            g'16
            \time 4/4
            a'8
            b'4
            c''8
            d''16
            e''8.
            ~
            e''16
            f''8.
        }

        .. figure:: ../_images/prettify_rewrite_meter-zh89kk66zon.png

    ``fuse_quadruple_meter``:
        This function also takes care of two special cases, namely quadruple
        and triple meters. By default, it will fuse leaves in quadruple meters
        across beats 1 and 2, and across beats 3 and 4 (as long as they fulfil
        the other requirements of duration and offset).

        >>> staff = abjad.Staff(r"\time 4/4 c'8 d'4 e'4 f'4 g'8")
        >>> meter = abjad.Meter((4, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 4/4
            c'8
            d'4
            e'8
            ~
            e'8
            f'4
            g'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-nap4bbf7mxe.png

        Set ``fuse_quadruple_meter`` to ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(r"\time 4/4 c'8 d'4 e'4 f'4 g'8")
        >>> meter = abjad.Meter((4, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(
        ...     meter,
        ...     fuse_quadruple_meter=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            \time 4/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'8
            ~
            f'8
            g'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-juipg9nzna.png

    ``fuse_triple_meter``:
        In the case of triple meters, it will fuse leaves across any beat as
        long as the previously mentioned conditions of offset and duration are
        met.

        >>> staff = abjad.Staff(r"\time 3/4 c'8 d'4 e'4 f'8")
        >>> meter = abjad.Meter((3, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'8
            d'4
            e'4
            f'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-4wg3grpb94p.png

        Similarly to the example before, set ``fuse_triple_meter`` to ``False``
        to disable this behaviour.

        >>> staff = abjad.Staff(r"\time 3/4 c'8 d'4 e'4 f'8")
        >>> meter = abjad.Meter((3, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> auxjad.mutate(staff[:]).prettify_rewrite_meter(
        ...     meter,
        ...     fuse_triple_meter=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'8
            d'8
            ~
            d'8
            e'8
            ~
            e'8
            f'8
        }

        .. figure:: ../_images/prettify_rewrite_meter-l16ostzscta.png

    ``extract_trivial_tuplets``:
        By default, this function extracts the contents of tuples that consist
        solely of rests, or solely of tied notes and chords.

        >>> staff = abjad.Staff(
        ...     r"\times 2/3 {c'4 ~ c'8} \times 2/3 {d'8 r4} "
        ...     r"\times 2/3 {r8 r8 r8} \times 2/3 {<e' g'>8 ~ <e' g'>4}"
        ... )
        >>> meter = abjad.Meter((4, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \times 2/3 {
                d'8
                r4
            }
            r4
            <e' g'>4
        }

        .. figure:: ../_images/prettify_rewrite_meter-a72jx4fc1xd.png

        Set ``extract_trivial_tuplets`` to ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(
        ...     r"\times 2/3 {c'4 ~ c'8} \times 2/3 {d'8 r4} "
        ...     r"\times 2/3 {r8 r8 r8} \times 2/3 {<e' g'>8 ~ <e' g'>4}"
        ... )
        >>> meter = abjad.Meter((4, 4))
        >>> abjad.mutate(staff[:]).rewrite_meter(meter)
        >>> abjad.mutate(staff[:]).prettify_rewrite_meter(
        ...     meter,
        ...     extract_trivial_tuplets=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            \times 2/3 {
                c'4.
            }
            \times 2/3 {
                d'8
                r4
            }
            \times 2/3 {
                r4.
            }
            \times 2/3 {
                <e' g'>4.
            }
        }

        .. figure:: ../_images/prettify_rewrite_meter-v9q0ka94qcd.png

    ``split_quadruple_meter``
        When applying |abjad.mutate().rewrite_meter()| to a selection with
        quadruple meter and without using a deeper ``boundary_depth`` than the
        default, the resulting notation will often have leaves crossing the
        third beat of the measure, as shown below.

        >>> staff = abjad.Staff(
        ...     r"c'4 d'2 r4"
        ...     r"e'4. f'2 g'8"
        ...     r"a'4. b'4. c''4"
        ...     r"d''16 e''8. f''4. g''4 a''8"
        ... )
        >>> meter = abjad.Meter((4, 4))
        >>> for measure in abjad.select(staff[:]).group_by_measure():
        ...     abjad.mutate(measure).rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            d'2
            r4
            e'4.
            f'8
            ~
            f'4.
            g'8
            a'4.
            b'4.
            c''4
            d''16
            e''8.
            f''4.
            g''8
            ~
            g''8
            a''8
        }

        .. figure:: ../_images/prettify_rewrite_meter-1wvhrjife1i.png

        This function tests those leaves against a series of rules, splitting
        them when the tests fails. In the case shown above, the first two bars
        are very easy to read rhythmically, but the third and fourth are less
        so. This is due to the dotted crotchet, which starts off a beat,
        crossing the third beat of the measure. This function will split these
        sort of leaves as shown below.

        >>> abjad.mutate(staff[:]).prettify_rewrite_meter(meter)
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            d'2
            r4
            e'4.
            f'8
            ~
            f'4.
            g'8
            a'4.
            b'8
            ~
            b'4
            c''4
            d''16
            e''8.
            f''4
            ~
            f''8
            g''4
            a''8
        }
        .. figure:: ../_images/prettify_rewrite_meter-56dy04wjzg.png

        Set ``split_quadruple_meter`` to ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(
        ...     r"c'4 d'2 r4"
        ...     r"e'4. f'2 g'8"
        ...     r"a'4. b'4. c''4"
        ...     r"d''16 e''8. f''4. g''4 a''8"
        ... )
        >>> meter = abjad.Meter((4, 4))
        >>> for measure in abjad.select(staff[:]).group_by_measure():
        ...     abjad.mutate(measure).rewrite_meter(meter)
        >>> abjad.mutate(staff[:]).prettify_rewrite_meter(
        ...     meter,
        ...     split_quadruple_meter=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            d'2
            r4
            e'4.
            f'8
            ~
            f'4.
            g'8
            a'4.
            b'4.
            c''4
            d''16
            e''8.
            f''4.
            g''4
            a''8
        }

        .. figure:: ../_images/prettify_rewrite_meter-ww1x0zsxlnd.png

    .. tip::

        Use :func:`auxjad.auto_rewrite_meter()` to automatically apply
        |abjad.mutate().rewrite_meter()| and
        |auxjad.mutate().prettify_rewrite_meter()| to a container with multiple
        time signatures.

    .. 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("first argument must be 'abjad.Selection'")
    if not selection.leaves().are_contiguous_logical_voice():
        raise ValueError("first argument must be contiguous logical voice")
    if not isinstance(meter, (abjad.Meter, abjad.TimeSignature)):
        raise TypeError("argument must be 'abjad.Meter' or "
                        "'abjad.TimeSignature'")
    if not isinstance(fuse_across_groups_of_beats, bool):
        raise TypeError("'fuse_across_groups_of_beats' must be 'bool'")
    if not isinstance(fuse_quadruple_meter, bool):
        raise TypeError("'fuse_quadruple_meter' must be 'bool'")
    if not isinstance(fuse_triple_meter, bool):
        raise TypeError("'fuse_triple_meter' must be 'bool'")
    if not isinstance(extract_trivial_tuplets, bool):
        raise TypeError("'extract_trivial_tuplets' must be 'bool'")
    if not isinstance(split_quadruple_meter, bool):
        raise TypeError("'split_quadruple_meter' must be 'bool'")

    if isinstance(meter, abjad.TimeSignature):
        meter = abjad.Meter(meter.pair)

    logical_ties = selection.logical_ties(pitched=True)
    if len(logical_ties) == 0:
        return

    first_leaf = selection.leaf(0)
    initial_offset = abjad.inspect(first_leaf).timespan().start_offset
    base = 1 / meter.denominator

    def _merge_indicators_then_fuse(logical_tie):
        last_indicators = abjad.inspect(logical_tie[-1]).indicators()
        initial_indicators = abjad.inspect(logical_tie[0]).indicators()
        initial_indicators_types = tuple(
            type(indicator) for indicator in initial_indicators)
        abjad.mutate(logical_tie).fuse()
        for indicator in last_indicators:
            if isinstance(indicator, (
                    abjad.Dynamic,
                    abjad.StopSlur,
                    abjad.StopGroup,
                    abjad.StopHairpin,
                    abjad.StopTextSpan,
                    abjad.StopTrillSpan,
                    abjad.StopPianoPedal,
                    abjad.StopPhrasingSlur,
            )):
                if not isinstance(indicator, initial_indicators_types):
                    abjad.attach(indicator, logical_tie[0])

    for logical_tie in logical_ties.filter_duration("==", base / 2):
        offset = abjad.inspect(logical_tie).timespan().start_offset
        offset -= initial_offset
        offset %= base
        if offset == base / 4:
            _merge_indicators_then_fuse(logical_tie)

    if fuse_across_groups_of_beats:
        for logical_tie in logical_ties.filter_duration("==", base):
            offset = abjad.inspect(logical_tie).timespan().start_offset
            offset -= initial_offset
            offset %= meter.duration
            offset_mod = offset % base
            if offset_mod == base / 2:
                if (not offset + base / 2
                        in meter.depthwise_offset_inventory[1]):
                    _merge_indicators_then_fuse(logical_tie)

    if fuse_quadruple_meter and meter.numerator == 4:
        for logical_tie in logical_ties.filter_duration("==", base):
            offset = abjad.inspect(logical_tie).timespan().start_offset
            offset -= initial_offset
            offset %= meter.duration
            offset_mod = offset % base
            if offset_mod == base / 2:
                if not offset + base / 2 in (
                        abjad.Offset(0, 1),
                        abjad.Offset(2 * base),
                        abjad.Offset(4 * base),
                ):
                    _merge_indicators_then_fuse(logical_tie)

    if fuse_triple_meter and meter.numerator == 3:
        for logical_tie in logical_ties.filter_duration("==", base):
            offset = abjad.inspect(logical_tie).timespan().start_offset
            offset -= initial_offset
            offset %= meter.duration
            offset_mod = offset % base
            if offset_mod == base / 2:
                if not offset + base / 2 in (
                        abjad.Offset(0, 1),
                        abjad.Offset(3 * base),
                ):
                    _merge_indicators_then_fuse(logical_tie)

    logical_ties = selection.logical_ties()  # splitting not only pitched
    if split_quadruple_meter and meter.numerator == 4:
        half_point_offset = abjad.Offset(2 * base)
        for logical_tie in logical_ties:
            offset0 = abjad.inspect(logical_tie).timespan().start_offset
            offset0 -= initial_offset
            offset0 %= meter.duration
            if offset0 == abjad.Offset(0, 1):
                # do not split things like c'1, c'2.. r8, c'2. r4, etc.
                continue
            offset1 = abjad.inspect(logical_tie).timespan().stop_offset
            offset1 -= initial_offset
            offset1 %= meter.duration
            if offset1 == abjad.Offset(0, 1):
                offset1 = abjad.Offset(meter.duration)
            if (offset0 % abjad.Offset(base) == abjad.Offset(0, 1)
                    and offset1 % abjad.Offset(base) == abjad.Offset(0, 1)):
                # do not split things like r4 c'2.
                continue
            if offset0 < half_point_offset < offset1:
                if (offset1 == abjad.Offset(meter.duration)
                        and len(logical_tie) == 1):
                    # do not split r8 c'2.., r16 c'2..., etc.
                    break
                if any(
                        abjad.inspect(leaf).duration() == abjad.Duration(2 *
                                                                         base)
                        for leaf in logical_tie):
                    # do not split r8 c'8~c'2 r4, r16 c'8.~c'2~c'8 r8, etc.
                    break
                # do not split tuplets
                getter = abjad.select().logical_ties()
                result = selection.tuplets().map(getter).flatten()
                if logical_tie in result:
                    break
                duration = abjad.Duration(half_point_offset - offset0)
                abjad.mutate(logical_tie).split([duration])

    if extract_trivial_tuplets:
        extract_trivial_tuplets_function(selection)
Example #6
0
def remove_repeated_dynamics(
    selection: abjad.Selection,
    *,
    ignore_hairpins: bool = False,
    reset_after_rests: bool = False,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function removes all consecutive repeated dynamic markings.

    Basic usage:
        When two consecutive leaves have identical dynamics, the second
        one is removed:

        >>> staff = abjad.Staff(r"\time 3/8 c'4\pp d'8\pp | c'4\f d'8\f")
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/8
            c'4
            \pp
            d'8
            \pp
            c'4
            \f
            d'8
            \f
        }

        .. figure:: ../_images/remove_repeated_dynamics-anw32e9i0f.png

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/8
            c'4
            \pp
            d'8
            c'4
            \f
            d'8
        }

        .. figure:: ../_images/remove_repeated_dynamics-i5ylxkzv7md.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.mutate(staff[:]).remove_repeated_dynamics()

    Dynamic structure:
        The function also removes dynamics that are separated by an arbitrary
        number of leaves without dynamics:

        >>> staff = abjad.Staff(r"\time 3/8 c'4\p d'8 | e'4.\p | c'4\p d'8\f")
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/8
            c'4
            \p
            d'8
            e'4.
            \p
            c'4
            \p
            d'8
            \f
        }

        .. figure:: ../_images/remove_repeated_dynamics-ha1x7s8d2fb.png

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/8
            c'4
            \p
            d'8
            e'4.
            c'4
            d'8
            \f
        }

        .. figure:: ../_images/remove_repeated_dynamics-g157jbbojhv.png

    Subcontainers:
        The container from which the selection is made can also have
        subcontainers:

        >>> staff = abjad.Staff([abjad.Note("c'2"),
        ...                      abjad.Chord("<d' f'>2"),
        ...                      abjad.Tuplet((2, 3), "g2 a2 b2"),
        ...                      ])
        >>> abjad.attach(abjad.Dynamic('ppp'), staff[0])
        >>> abjad.attach(abjad.Dynamic('ppp'), staff[1])
        >>> abjad.attach(abjad.Dynamic('ppp'), staff[2][0])
        >>> abjad.f(staff)
        \new Staff
        {
            c'2
            \ppp
            <d' f'>2
            \ppp
            \times 2/3 {
                g2
                \ppp
                a2
                b2
            }
        }

        .. figure:: ../_images/remove_repeated_dynamics-4h9xze4780d.png

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.f(staff)
        \new Staff
        {
            c'2
            \ppp
            <d' f'>2
            \times 2/3 {
                g2
                a2
                b2
            }
        }

        .. figure:: ../_images/remove_repeated_dynamics-7n9aaveoslu.png

    ``ignore_hairpins``:
        By default, repeated dynamics with hairpins in between are not removed,
        but consecutive ones will.

        >>> staff = abjad.Staff(r"c'2\p\< d'2\f\> | c'2\f d'2\f | e'1\p")
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \p
            \<
            d'1
            \f
            \>
            c'1
            \f
            d'1
            \f
            e'1
            \p
        }

        .. figure:: ../_images/remove_repeated_dynamics-frmlobo3gis.png

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \p
            \<
            d'1
            \f
            \>
            c'1
            \f
            d'1
            e'1
            \p
        }

        .. figure:: ../_images/remove_repeated_dynamics-ov05k1imubj.png

        To override the previous behaviour, set ``ignore_hairpins=True`` and
        hairpins will be ignored.

        >>> staff = abjad.Staff(r"c'2\p\< d'2\f\> | c'2\f d'2\f | e'1\p")
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \p
            \<
            d'1
            \f
            \>
            c'1
            \f
            d'1
            \f
            e'1
            \p
        }

        .. figure:: ../_images/remove_repeated_dynamics-2hdkt6cyca1.png

        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics(
        ...     ignore_hairpins=True,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \p
            \<
            d'1
            \f
            \>
            c'1
            d'1
            e'1
            \p
        }

        .. figure:: ../_images/remove_repeated_dynamics-xkaipizr2jr.png

    ``reset_after_rests``:
        By default, rests are treated just like any other leaf and thus notes
        with an identical dynamic separated by an arbitrary number of rests
        will be considered as repeated and the second dynamic will be removed.

        >>> staff = abjad.Staff(r"c'4\pp r2. | c'1\pp")
        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics()
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \pp
            r2.
            c'1
        }

        .. figure:: ../_images/remove_repeated_dynamics-wtno2t8qroh.png

        To override the previous behaviour, set ``reset_after_rests=True`` and
        dynamics will always be restated after a rest.

        >>> staff = abjad.Staff(r"c'4\pp r2. | c'1\pp")
        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics(
        ...     reset_after_rests=True,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \pp
            r2.
            c'1
            \pp
        }

        .. figure:: ../_images/remove_repeated_dynamics-3e6g7u0q1i1.png

    ``reset_after_rests`` and non-:obj:`bool` values:
        The argument ``reset_after_rests`` takes not only :obj:`bool` values
        but also durations (|abjad.Duration|, :obj:`tuple`, :obj:`float`,
        etc.). This sets the maximum length of rests before which identical
        dynamics are restated. If the total length of rests falls below that
        value, then repeated dynamics are removed.

        In the case below, a rest of ``r2``. is shorter than a duration of
        ``(4, 4)``, so the repeated dynamic is removed.

        >>> staff = abjad.Staff(r"c'4\pp r2. | c'1\pp")
        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics(
        ...     reset_after_rests=(4, 4)
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \pp
            r2.
            c'1
        }

        .. figure:: ../_images/remove_repeated_dynamics-b323xuesujc.png

        But setting the duration to ``2/4`` forces the dynamic to be restated.

        >>> staff = abjad.Staff(r"c'4\pp r2. | c'1\pp")
        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics(
        ...     reset_after_rests=2 / 4
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \pp
            r2.
            c'1
            \pp
        }

        .. figure:: ../_images/remove_repeated_dynamics-64dppx3cp99.png

    ``reset_after_rests``:
        The function also handles measure rests with ``reset_after_rests``.

        >>> staff = abjad.Staff(r"c'4\pp r2. | c'4\pp r2. | R1 | c'1\pp")
        >>> auxjad.mutate(staff[:]).remove_repeated_dynamics(
        ...     reset_after_rests=abjad.Duration(4, 4)
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'4
            \pp
            r2.
            c'4
            r2.
            R1
            c'1
            \pp
        }

        .. figure:: ../_images/remove_repeated_dynamics-jt7akhtbsge.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 abjad.select(selection).leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(ignore_hairpins, bool):
        raise TypeError("'ignore_hairpins' must be 'bool'")
    if not isinstance(
            reset_after_rests,
        (bool, int, float, tuple, str, abjad.Duration),
    ):
        raise TypeError("'reset_after_rests' must be a number, 'bool' or "
                        "'abjad.Duration'")

    previous_dynamic = None
    current_dynamic = None
    duration_since_last_note = abjad.Duration(0)
    for leaf in selection.leaves():
        if type(leaf) in (abjad.Rest, abjad.MultimeasureRest):
            if isinstance(reset_after_rests, bool) and reset_after_rests:
                previous_dynamic = None
            elif reset_after_rests:
                duration_since_last_note += leaf.written_duration
                if (duration_since_last_note >=
                        abjad.Duration(reset_after_rests)):
                    previous_dynamic = None
        else:
            duration_since_last_note = abjad.Duration(0)
            indicators = abjad.inspect(leaf).indicators()
            hairpin_present = any([
                isinstance(indicator, abjad.StartHairpin)
                for indicator in indicators
            ])
            if hairpin_present and not ignore_hairpins:
                for indicator in indicators:
                    if isinstance(indicator, abjad.Dynamic):
                        if (indicator is not None
                                and indicator == previous_dynamic):
                            abjad.detach(abjad.Dynamic, leaf)
                current_dynamic = None
            else:
                for indicator in indicators:
                    if isinstance(indicator, abjad.Dynamic):
                        current_dynamic = indicator
            if current_dynamic != previous_dynamic:
                previous_dynamic = current_dynamic
            elif current_dynamic is not None:
                abjad.detach(abjad.Dynamic, leaf)
Example #7
0
def selection_is_full(selection: abjad.Selection) -> bool:
    r"""Returns a :obj:`bool` representing whether the last measure of an input
    |abjad.Selection| is fully filled in or not.

    Basic usage:
        Returns ``True`` if the last measure of a selection is full, otherwise
        returns ``False``. If no time signature is encountered at the
        beginning, it uses LilyPond's convention and considers the container
        as in 4/4.

        >>> container1 = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> container2 = abjad.Container(r"c'4 d'4 e'4")
        >>> container3 = abjad.Container(r"c'4 d'4 e'4 f'4 | c'4")
        >>> container4 = abjad.Container(r"c'4 d'4 e'4 f'4 | c'4 d'4 e'4 f'4")
        >>> auxjad.inspect(container1[:]).selection_is_full()
        True
        >>> auxjad.inspect(container2[:]).selection_is_full()
        False
        >>> auxjad.inspect(container3[:]).selection_is_full()
        False
        >>> auxjad.inspect(container4[:]).selection_is_full()
        True

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.inspect()|. Therefore it can be used from either
        :func:`auxjad.inspect()` or |abjad.inspect()|, as shown below:

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> auxjad.inspect(container[:]).selection_is_full()
        True
        >>> abjad.inspect(container[:]).selection_is_full()
        True

    Time signature changes:
        Handles any time signatures as well as changes of time signature.

        >>> container1 = abjad.Container(r"\time 4/4 c'4 d'4 e'4 f'4")
        >>> container2 = abjad.Container(r"\time 3/4 a2. \time 2/4 r2")
        >>> container3 = abjad.Container(r"\time 5/4 g1 ~ g4 \time 4/4 af'2")
        >>> container4 = abjad.Container(r"\time 6/8 c'2 ~ c'8")
        >>> auxjad.inspect(container1[:]).selection_is_full()
        True
        >>> auxjad.inspect(container2[:]).selection_is_full()
        True
        >>> auxjad.inspect(container3[:]).selection_is_full()
        False
        >>> auxjad.inspect(container4[:]).selection_is_full()
        False

    Partial time signatures:
        Correctly handles partial time signatures.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> time_signature = abjad.TimeSignature((3, 4), partial=(1, 4))
        >>> abjad.attach(time_signature, container[0])
        >>> auxjad.inspect(container[:]).selection_is_full()
        True

    Multi-measure rests:
        It also handles multi-measure rests.

        >>> container1 = abjad.Container(r"R1")
        >>> container2 = abjad.Container(r"\time 3/4 R1*3/4 \time 2/4 r2")
        >>> container3 = abjad.Container(r"\time 5/4 R1*5/4 \time 4/4 g''4")
        >>> container4 = abjad.Container(r"\time 6/8 R1*1/2")
        >>> auxjad.inspect(container1[:]).selection_is_full()
        True
        >>> auxjad.inspect(container2[:]).selection_is_full()
        True
        >>> auxjad.inspect(container3[:]).selection_is_full()
        False
        >>> auxjad.inspect(container4[:]).selection_is_full()
        False

    .. error::

        If a selection is malformed, i.e. it has an underfilled measure before
        a time signature change, the function raises a :exc:`ValueError`
        exception. This is also the case when a selection starts in the middle
        of a measure.

        >>> container = abjad.Container(r"\time 5/4 g''1 \time 4/4 f'1")
        >>> auxjad.inspect(container[:]).selection_is_full()
        ValueError: 'selection' is malformed, with an underfull measure
        preceding a time signature change

    .. 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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    return underfull_duration(selection) == abjad.Duration(0)
def respell_augmented_unisons(
    selection: abjad.Selection,
    *,
    include_multiples: bool = False,
    respell_by_pitch_class: bool = False,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function changes the accidentals of individual pitches of all chords
    in a container in order to avoid augmented unisons.

    Basic usage:
        To use this function, apply it to a selection that contains chords that
        have augmented unisons.

        >>> container = abjad.Container(r"c'4 r4 <ef' e'>4 g'4 <c' cs'>4 r2.")
        >>> abjad.show(container)

        ..  docs::

            {
                c'4
                r4
                <ef' e'>4
                g'4
                <c' cs'>4
                r2.
            }

        ..  figure:: ../_images/respell_augmented_unisons-OXnGvQzGT2.png

        >>> auxjad.mutate.respell_augmented_unisons(container[:])
        >>> abjad.show(container)

        ..  docs::

            {
                c'4
                r4
                <ds' e'>4
                g'4
                <c' df'>4
                r2.
            }

        ..  figure:: ../_images/respell_augmented_unisons-x33afbbamt.png

        This can be useful when using tuples of integers to create chords that
        contain minor seconds:

        >>> pitches = [(0, 1), (8, 9, 12), (0, 4, 5, 6), (-1, 5, 6)]
        >>> durations = [(1, 8), (3, 8), (7, 16), (1, 16)]
        >>> maker = abjad.LeafMaker()
        >>> chords = maker(pitches, durations)
        >>> staff = abjad.Staff(chords)
        >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic')
        >>> abjad.attach(literal, staff)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' cs'>8
                <af' a' c''>4.
                <c' e' f' fs'>4..
                <b f' fs'>16
            }

        ..  figure:: ../_images/respell_augmented_unisons-mWVWwV9uBx.png

        >>> auxjad.mutate.respell_augmented_unisons(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' df'>8
                <gs' a' c''>4.
                <c' e' f' gf'>4..
                <b f' gf'>16
            }

        ..  figure:: ../_images/respell_augmented_unisons-IfzaseW4oS.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.respell_augmented_unisons(staff[:])
        >>> abjad.mutate.respell_augmented_unisons(staff[:])

    2-note chords:
        The example below shows first the default spelling of 2-note chords in
        Abjad followed by the respelt 2-note chords.

        >>> staff = abjad.Staff()
        >>> for pitch in range(12):
        ...     staff.append(abjad.Chord([pitch, pitch + 1], (1, 16)))
        >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic')
        >>> abjad.attach(literal, staff)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' cs'>16
                <cs' d'>16
                <d' ef'>16
                <ef' e'>16
                <e' f'>16
                <f' fs'>16
                <fs' g'>16
                <g' af'>16
                <af' a'>16
                <a' bf'>16
                <bf' b'>16
                <b' c''>16
            }

        ..  figure:: ../_images/respell_augmented_unisons-qQduIqCRpz.png

        >>> auxjad.mutate.respell_augmented_unisons(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' df'>16
                <cs' d'>16
                <d' ef'>16
                <ds' e'>16
                <e' f'>16
                <f' gf'>16
                <fs' g'>16
                <g' af'>16
                <gs' a'>16
                <a' bf'>16
                <as' b'>16
                <b' c''>16
            }

        ..  figure:: ../_images/respell_augmented_unisons-jvg032q24il.png

    augmented unissons in chords with 3 or more pitches:
        The function looks for all augmented unissons in chords of 3 or more
        pitches:

        >>> staff = abjad.Staff(r"<a c' cs' f'>1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <a c' cs' f'>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-IklJO81q1E.png

        >>> auxjad.mutate.respell_augmented_unisons(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <a c' df' f'>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-gyficck05p.png

    ``include_multiples``:
        By default, this function only changes spelling for pitches that are
        1 semitone apart.

        >>> staff = abjad.Staff(r"<c' cs''>1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <c' cs''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-HPqFrADjeh.png

        >>> auxjad.mutate.respell_augmented_unisons(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <c' cs''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-uszf11qb72d.png

        To consider pitches in different octaves (thus including augmented
        unisons, augmented octaves, augmented fifteenths, etc.), call this
        function with the keyword argument ``include_multiples`` set to
        ``True``.

        >>> staff = abjad.Staff(r"<c' cs''>1")
        >>> auxjad.mutate.respell_augmented_unisons(
        ...     staff[:],
        ...     include_multiples=True,
        ... )

        ..  docs::

            \new Staff
            {
                <c' df''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-8am8cu2rmgi.png

    ``respell_by_pitch_class``:
        By default, when this function changes the spelling of a pitch, it does
        not change the spelling of all other pitches with the same pitch-class.

        >>> staff = abjad.Staff(r"<c' cs' cs''>1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                    <c' cs' cs''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-eWixL7iCEq.png

        >>> auxjad.mutate.respell_augmented_unisons(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <c' df' cs''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-47d16xk6gvs.png

        To alter all pitch-classes, call this function with the keyword
        argument ``respell_by_pitch_class`` set to ``True``.

        >>> staff = abjad.Staff(r"<c' cs' cs''>1")
        >>> auxjad.mutate.respell_augmented_unisons(
        ...     staff[:],
        ...     respell_by_pitch_class=True,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                <c' df' df''>1
            }

        ..  figure:: ../_images/respell_augmented_unisons-kobft0oq9sl.png
    """
    if not isinstance(selection, abjad.Selection):
        raise TypeError("argument must be 'abjad.Selection'")
    if not isinstance(respell_by_pitch_class, bool):
        raise TypeError("'respell_by_pitch_class' must be 'bool'")
    if not isinstance(include_multiples, bool):
        raise TypeError("'include_multiples' must be 'bool'")

    for leaf in selection.leaves():
        if isinstance(leaf, abjad.Chord):
            accidentals = [pitch.accidental for pitch in leaf.written_pitches]
            original_accidentals = accidentals[:]
            if not include_multiples:
                # dealing only with intervals of size equal to 1 semitone
                for i in range(len(leaf.written_pitches) - 1):
                    p1 = leaf.written_pitches[i]
                    p2 = leaf.written_pitches[i + 1]
                    interval12 = p1 - p2
                    try:
                        p3 = leaf.written_pitches[i + 2]
                        interval23 = p2 - p3
                    except IndexError:
                        p3 = None
                        interval23 = None
                    if (interval12 == abjad.NamedInterval('+A1')
                            and interval23 != abjad.NamedInterval('+A1')
                            and interval23 != abjad.NamedInterval('+m2')):
                        if not respell_by_pitch_class:
                            # respelling only one single note
                            if p1.accidental == abjad.Accidental('f'):
                                accidentals[i] = abjad.Accidental('s')
                            elif p1.accidental == abjad.Accidental(''):
                                accidentals[i + 1] = abjad.Accidental('f')
                        else:
                            # respelling all matching pitch-classes
                            if p1.accidental == abjad.Accidental('f'):
                                for j, p in enumerate(leaf.written_pitches):
                                    if p.pitch_class == p1.pitch_class:
                                        accidentals[j] = abjad.Accidental('s')
                            elif p1.accidental == abjad.Accidental(''):
                                for j, p in enumerate(leaf.written_pitches):
                                    if p.pitch_class == p2.pitch_class:
                                        accidentals[j] = abjad.Accidental('f')
            else:
                # dealing with augmented unisons as well as augmented 8as,
                # 15ths, etc.
                for i in range(len(leaf.written_pitches) - 1):
                    for j in range(i + 1, len(leaf.written_pitches)):
                        # no p3 this time since p1 and p2 are not necessary
                        # consecutive pitches in the chord
                        p1 = leaf.written_pitches[i]
                        p2 = leaf.written_pitches[j]
                        interval = abjad.NamedIntervalClass(p1 - p2)
                        if (interval in (
                                abjad.NamedIntervalClass('+A1'),
                                abjad.NamedIntervalClass('-d1'),
                        )):
                            # no need for respell_by_pitch_class since this
                            # will go through all notes in the chord anyway
                            if p1.accidental == abjad.Accidental('f'):
                                accidentals[i] = abjad.Accidental('s')
                            if p1.accidental == abjad.Accidental('s'):
                                accidentals[i] = abjad.Accidental('f')
                            elif p1.accidental == abjad.Accidental(''):
                                if p2.accidental == abjad.Accidental('s'):
                                    accidentals[j] = abjad.Accidental('f')
                                elif p2.accidental == abjad.Accidental('f'):
                                    accidentals[j] = abjad.Accidental('s')
            # rewritting chord with new spelling
            respelt_pitches = []
            for pitch, accidental, original_accidental in zip(
                    leaf.written_pitches,
                    accidentals,
                    original_accidentals,
            ):
                if (accidental != original_accidental
                        and accidental == abjad.Accidental('f')):
                    respelt_pitches.append(pitch._respell(accidental='flats'))
                elif (accidental != original_accidental
                      and accidental == abjad.Accidental('s')):
                    respelt_pitches.append(pitch._respell(accidental='sharps'))
                else:
                    respelt_pitches.append(pitch)
            leaf.written_pitches = respelt_pitches
Example #9
0
def reposition_dynamics(selection: abjad.Selection,
                        *,
                        allow_hairpins_under_rests: bool = False,
                        check_hairpin_trends: bool = True,
                        remove_repeated_dynamics: bool = True,
                        allow_hairpin_to_rest_with_dynamic: bool = True,
                        ) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function shifts all dynamics from rests to the next pitched leaves. It
    will also adjust hairpins if necessary.

    Basic usage:
        This function will shift dynamics under rests to the next pitched leaf.

        >>> staff = abjad.Staff(r"c'1\p d'2 r2\f r1 e'1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'2
                r2
                \f
                r1
                e'1
            }

        ..  figure:: ../_images/reposition_dynamics-sqwvevg7o9.png

        >>> staff = abjad.Staff(r"c'1\p d'2 r2\f r1 e'1")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'2
                r2
                r1
                e'1
                \f
            }

        ..  figure:: ../_images/reposition_dynamics-v18uzh1zjs.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.reposition_dynamics(staff[:])
        >>> abjad.mutate.reposition_dynamics(staff[:])

    Removing dynamics:
        If the next pitched leaf already contain a dynamic, this function will
        simply remove the dynamic under the rest.

        >>> staff = abjad.Staff(r"c'1\p d'2 r2\f r1\mf e'1\pp")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'2
                r2
                \f
                r1
                \mf
                e'1
                \pp
            }

        ..  figure:: ../_images/reposition_dynamics-aom2qywcn9m.png

        >>> staff = abjad.Staff(r"c'1\p d'2 r2\f r1\mf e'1\pp")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'2
                r2
                r1
                e'1
                \pp
            }

        ..  figure:: ../_images/reposition_dynamics-2ua73x102fp.png

    ``remove_repeated_dynamics``:
        By default indentical repeated dynamics are omitted.

        >>> staff = abjad.Staff(r"c'1\p d'1 r1\f e'1\p")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'1
                r1
                e'1
            }

        ..  figure:: ../_images/reposition_dynamics-i4x8b1z1ak.png

        Set the optional keyword argument ``remove_repeated_dynamics`` to
        ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(r"c'1\p d'1 r1\f e'1\p")
        >>> auxjad.mutate.reposition_dynamics(
        ...     staff[:],
        ...     remove_repeated_dynamics=False,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'1
                r1
                e'1
                \p
            }

        ..  figure:: ../_images/reposition_dynamics-se8a5aeqer.png

    ``allow_hairpins_under_rests``:
        This function will shorten hairpins until rests by default.

        >>> staff = abjad.Staff(r"c'1\p\< d'2 r2 r1\f e'1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                d'2
                r2
                r1
                \f
                e'1
            }

        ..  figure:: ../_images/reposition_dynamics-r28po3j3hd.png

        >>> staff = abjad.Staff(r"c'1\p\< d'2 r2 r1\f e'1")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                d'2
                r2
                \!
                r1
                e'1
                \f
            }

        ..  figure:: ../_images/reposition_dynamics-n60vvnqrcnr.png

        Set the optional keyword argument ``allow_hairpins_under_rests`` to
        ``True`` to allow hairpins to extend cross rests.

        >>> staff = abjad.Staff(r"c'1\p\< d'2 r2 r1\f e'1")
        >>> auxjad.mutate.reposition_dynamics(
        ...     staff[:],
        ...     allow_hairpins_under_rests=True,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                d'2
                r2
                r1
                e'1
                \f
            }

        ..  figure:: ../_images/reposition_dynamics-ugit7ijz89h.png

    ``allow_hairpin_to_rest_with_dynamic``:
        Notice that if a hairpin leads to a rest with dynamic, that one is not
        removed.

        >>> staff = abjad.Staff(r"c'1\p\< d'2 r2\f r1 e'1")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                d'2
                r2
                \f
                r1
                e'1
            }

        ..  figure:: ../_images/reposition_dynamics-t9z4y5zzj6.png

        Set the argument ``allow_hairpin_to_rest_with_dynamic`` to ``False`` to
        disable this behaviour.

        >>> staff = abjad.Staff(r"c'1\p\< d'2 r2\f r1 e'1")
        >>> auxjad.mutate.reposition_dynamics(
        ...     staff[:],
        ...     allow_hairpin_to_rest_with_dynamic=False,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \<
                d'2
                r2
                \!
                r1
                e'1
                \f
            }

        ..  figure:: ../_images/reposition_dynamics-wpwweov55qf.png

    ``check_hairpin_trends``:
        This function will remove any hairpins connecting dynamics that grow in
        the opposite direction to the hairpin's trend, such as a diminuendo
        hairpin from piano to forte.

        >>> staff = abjad.Staff(r"c'1\p\> d'1\f\> e'1\p")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                d'1
                \f
                \>
                e'1
                \p
            }

        ..  figure:: ../_images/reposition_dynamics-f0b4ppb71ii.png

        This behaviour can be disabled by setting the argument
        ``check_hairpin_trends`` to ``False``.

        >>> staff = abjad.Staff(r"c'1\p\> d'1\f\> e'1\p")
        >>> auxjad.mutate.reposition_dynamics(
        ...     staff[:],
        ...     check_hairpin_trends=False,
        ... )
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \>
                d'1
                \f
                \>
                e'1
                \p
            }

        ..  figure:: ../_images/reposition_dynamics-1e2ugszm95fi.png

    ..  note::

        The behaviour described above is only applicable when a hairpin ends
        on a dynamic. Using the hairpin terminator ``\!`` before a dynamic
        change will not cause a hairpin to be removed as it is not considered
        to be connecting dynamics of the opposite trend.

        >>> staff = abjad.Staff(r"c'1\p\> d'1\! e'1\f\> f'1\p")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                \>
                d'1
                \!
                e'1
                \f
                \>
                f'1
                \p
            }

        ..  figure:: ../_images/reposition_dynamics-77g0uwthbgd.png

    Types of hairpins:
        This function can handle multiple types of hairpins as well as niente
        dynamics.

        >>> staff = abjad.Staff(r"c'1 d'1 e'1 r1\mf r1\ff f'1 r1 g'1")
        >>> abjad.attach(abjad.Dynamic('niente', hide=True), staff[0])
        >>> abjad.attach(abjad.Dynamic('niente', hide=True), staff[7])
        >>> abjad.attach(abjad.StartHairpin('o<'), staff[0])
        >>> abjad.attach(abjad.StartHairpin('>o'), staff[4])
        >>> abjad.attach(abjad.StopHairpin(), staff[7])
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                - \tweak circled-tip ##t
                \<
                d'1
                e'1
                r1
                \mf
                r1
                f'1
                \ff
                - \tweak circled-tip ##t
                \>
                r1
                \!
                g'1
            }

        ..  figure:: ../_images/reposition_dynamics-m8it9awv1ce.png

        >>> staff = abjad.Staff(
        ...     r"c'1\p d'1\f\> e'1\ff\< r1\fff f'1\p\> g'1\ppp"
        ... )
        >>> abjad.attach(abjad.StartHairpin('--'), staff[0])
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                - \tweak stencil #constante-hairpin
                \<
                d'1
                \f
                e'1
                \ff
                \<
                r1
                \fff
                f'1
                \p
                \>
                g'1
                \ppp
            }

        ..  figure:: ../_images/reposition_dynamics-fosad5ltzj.png

    Multi-measure rests:
        Multi-measure rests are also supported.

        >>> staff = abjad.Staff(r"c'1\p R1\f d'1")
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                R1
                \f
                d'1
            }

        ..  figure:: ../_images/reposition_dynamics-uj6jasfs2uh.png

        >>> staff = abjad.Staff(r"c'1\p R1\f d'1")
        >>> auxjad.mutate.reposition_dynamics(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'1
                \p
                R1
                d'1
                \f
            }

        ..  figure:: ../_images/reposition_dynamics-axpcbm9hocd.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 abjad.select(selection).leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(allow_hairpins_under_rests, bool):
        raise TypeError("'allow_hairpins_under_rests' must be 'bool'")
    if not isinstance(check_hairpin_trends, bool):
        raise TypeError("'check_hairpin_trends' must be 'bool'")
    if not isinstance(remove_repeated_dynamics, bool):
        raise TypeError("'remove_repeated_dynamics' must be 'bool'")
    if not isinstance(allow_hairpin_to_rest_with_dynamic, bool):
        raise TypeError("'allow_hairpin_to_rest_with_dynamic' must be 'bool'")

    leaves = selection.leaves()

    # shifting dynamics and hairpins from rests to notes
    shifted_dynamic = None
    shifted_hairpin = None
    active_hairpin = None
    for leaf in leaves:
        if isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest)):
            if abjad.get.indicator(leaf, abjad.Dynamic) is not None:
                previous_leaf = abjad.select(leaf).with_previous_leaf()[0]
                if (allow_hairpin_to_rest_with_dynamic
                        and active_hairpin is not None
                        and not isinstance(
                            previous_leaf,
                            (abjad.Rest, abjad.MultimeasureRest),
                        )):
                    active_hairpin = None
                else:
                    shifted_dynamic = abjad.get.indicator(leaf, abjad.Dynamic)
                    abjad.detach(abjad.Dynamic, leaf)
            if abjad.get.indicator(leaf, abjad.StartHairpin) is not None:
                shifted_hairpin = abjad.get.indicator(leaf, abjad.StartHairpin)
                abjad.detach(abjad.StartHairpin, leaf)
        else:
            if abjad.get.indicator(leaf, abjad.Dynamic) is None:
                if shifted_dynamic is not None:
                    abjad.attach(shifted_dynamic, leaf)
                    if (abjad.get.indicator(leaf, abjad.StopHairpin)
                            is not None):
                        abjad.detach(abjad.StopHairpin, leaf)
                if shifted_hairpin is not None:
                    abjad.attach(shifted_hairpin, leaf)
            else:
                active_hairpin = None
            if abjad.get.indicator(leaf, abjad.StopHairpin) is not None:
                active_hairpin = None
            shifted_dynamic = None
            shifted_hairpin = None
            if active_hairpin is None:
                active_hairpin = abjad.get.indicator(leaf, abjad.StartHairpin)

    # stopping hairpins under rests if not allowed
    if not allow_hairpins_under_rests:
        effective_hairpin = None
        for leaf in leaves:
            start_hairpin = abjad.get.indicator(leaf, abjad.StartHairpin)
            if start_hairpin is not None:
                effective_hairpin = start_hairpin
                continue
            if isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest)):
                if effective_hairpin is not None:
                    if abjad.get.indicator(leaf, abjad.StopHairpin) is None:
                        abjad.attach(abjad.StopHairpin(), leaf)
                    effective_hairpin = None
            else:
                dynamic = abjad.get.indicator(leaf, abjad.Dynamic)
                stop_hairpin = abjad.get.indicator(leaf, abjad.StopHairpin)
                if dynamic is not None or stop_hairpin is not None:
                    effective_hairpin = None

    # cleaning up hairpins
    effective_dynamic = None
    for index, leaf in enumerate(leaves[:-1]):
        if abjad.get.indicator(leaf, abjad.Dynamic) is not None:
            effective_dynamic = abjad.get.indicator(leaf, abjad.Dynamic)
        start_hairpin = abjad.get.indicator(leaf, abjad.StartHairpin)
        if start_hairpin is not None and check_hairpin_trends:
            for next_leaf in leaves[index + 1:]:
                next_dynamic = abjad.get.indicator(next_leaf, abjad.Dynamic)
                if next_dynamic is not None and effective_dynamic is not None:
                    if '<' in start_hairpin.shape:
                        if next_dynamic.ordinal <= effective_dynamic.ordinal:
                            abjad.detach(abjad.StartHairpin, leaf)
                    elif '>' in start_hairpin.shape:
                        if next_dynamic.ordinal >= effective_dynamic.ordinal:
                            abjad.detach(abjad.StartHairpin, leaf)
                    break
                elif (abjad.get.indicator(next_leaf, abjad.StopHairpin)
                        is not None):
                    break
    if abjad.get.indicator(leaves[-1], abjad.StartHairpin) is not None:
        abjad.detach(abjad.StartHairpin, leaves[-1])

    # removing unecessary StopHairpin's
    for leaf in leaves:
        if (abjad.get.indicator(leaf, abjad.StopHairpin) is not None
                and abjad.get.indicator(leaf, abjad.Dynamic) is not None):
            abjad.detach(abjad.StopHairpin, leaf)
    target_leaf = None
    for leaf in leaves[::-1]:
        if abjad.get.indicator(leaf, abjad.StopHairpin) is not None:
            if target_leaf is not None:
                abjad.detach(abjad.StopHairpin, target_leaf)
            target_leaf = leaf
        elif (abjad.get.indicator(leaf, abjad.StartHairpin) is not None
                or abjad.get.indicator(leaf, abjad.Dynamic)) is not None:
            target_leaf = None

    # removing repeated dynamics if required
    if remove_repeated_dynamics:
        remove_repeated_dynamics_(selection)
Example #10
0
def respell_accidentals(
    selection: abjad.Selection,
    *,
    include_multiples: bool = False,
    respell_by_pitch_class: bool = False,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function changes the accidentals of individual pitches of all chords
    in a container in order to avoid augmented unisons.

    Basic usage:
        To use this function, apply it to a selection that contains chords that
        have augmented unisons.

        >>> container = abjad.Container(r"c'4 r4 <ef' e'>4 g'4 <c' cs'>4 r2.")
        >>> auxjad.mutate(container[:]).respell_accidentals()
        >>> abjad.f(container)
        {
            c'4
            r4
            <ds' e'>4
            g'4
            <c' df'>4
            r2.
        }

        .. figure:: ../_images/respell_accidentals-x33afbbamt.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).respell_accidentals()
        >>> abjad.mutate(staff[:]).respell_accidentals()

    2-note chords:
        The example below shows the default spelling of 2-note chords in
        Abjad in the upper staff, and the respelt 2-note chords in the bottom
        staff.

        >>> staff1 = abjad.Staff()
        >>> staff2 = abjad.Staff()
        >>> for pitch in range(12):
        ...     staff1.append(abjad.Chord([pitch, pitch + 1], (1, 16)))
        ...     staff2.append(abjad.Chord([pitch, pitch + 1], (1, 16)))
        >>> auxjad.mutate(staff2[:]).respell_accidentals()
        >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic')
        >>> abjad.attach(literal, staff1)
        >>> abjad.attach(literal, staff2)
        >>> score = abjad.Score([staff1, staff2])
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' cs'>16
                <cs' d'>16
                <d' ef'>16
                <ef' e'>16
                <e' f'>16
                <f' fs'>16
                <fs' g'>16
                <g' af'>16
                <af' a'>16
                <a' bf'>16
                <bf' b'>16
                <b' c''>16
            }
            \new Staff
            {
                \accidentalStyle dodecaphonic
                <c' df'>16
                <cs' d'>16
                <d' ef'>16
                <ds' e'>16
                <e' f'>16
                <f' gf'>16
                <fs' g'>16
                <g' af'>16
                <gs' a'>16
                <a' bf'>16
                <as' b'>16
                <b' c''>16
            }
        >>

        .. figure:: ../_images/respell_accidentals-jvg032q24il.png

    augmented unissons in larger chords:
        The function looks for all augmented unissons in chords of 3 or more
        pitches:

        >>> container1 = abjad.Container(r"<a c' cs' f'>1")
        >>> container2 = abjad.Container(r"<a c' cs' f'>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals()
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <a c' cs' f'>1
            }
            {
                <a c' df' f'>1
            }
        }

        .. figure:: ../_images/respell_accidentals-gyficck05p.png

        It is not a problem if the pitches are input out of order.

        >>> container1 = abjad.Container(r"<e' cs' g' ef'>1")
        >>> container2 = abjad.Container(r"<e' cs' g' ef'>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals()
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <cs' ef' e' g'>1
            }
            {
                <cs' ds' e' g'>1
            }
        }

        .. figure:: ../_images/respell_accidentals-xbu6u6mu6qo.png

    ``include_multiples``:
        By default, this function only changes spelling for pitches that are
        1 semitone apart.

        >>> container1 = abjad.Container(r"<c' cs''>1")
        >>> container2 = abjad.Container(r"<c' cs''>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals()
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <c' cs''>1
            }
            {
                <c' cs''>1
            }
        }

        .. figure:: ../_images/respell_accidentals-uszf11qb72d.png

        To consider pitches in different octaves (thus including augmented
        unisons, augmented octaves, augmented fifteenths, etc.), call this
        function with the keyword argument ``include_multiples`` set to
        ``True``.

        >>> container1 = abjad.Container(r"<c' cs''>1")
        >>> container2 = abjad.Container(r"<c' cs''>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals(
        ...     include_multiples=True,
        ... )
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <c' cs''>1
            }
            {
                <c' df''>1
            }
        }

        .. figure:: ../_images/respell_accidentals-8am8cu2rmgi.png

    ``respell_by_pitch_class``:
        By default, when this function changes the spelling of a pitch, it does
        not change the spelling of all other pitches with the same pitch-class.

        >>> container1 = abjad.Container(r"<c' cs' cs''>1")
        >>> container2 = abjad.Container(r"<c' cs' cs''>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals()
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <c' cs' cs''>1
            }
            {
                <c' df' cs''>1
            }
        }

        .. figure:: ../_images/respell_accidentals-47d16xk6gvs.png

        To alter all pitch-classes, call this function with the keyword
        argument ``respell_by_pitch_class`` set to ``True``.

        >>> container1 = abjad.Container(r"<c' cs' cs''>1")
        >>> container2 = abjad.Container(r"<c' cs' cs''>1")
        >>> auxjad.mutate(container2[:]).respell_accidentals(
        ...     respell_by_pitch_class=True,
        ... )
        >>> staff = abjad.Staff([container1, container2])
        >>> abjad.f(staff)
        \new Staff
        {
            {
                <c' cs' cs''>1
            }
            {
                <c' df' df''>1
            }
        }

        .. figure:: ../_images/respell_accidentals-kobft0oq9sl.png
    """
    if not isinstance(selection, abjad.Selection):
        raise TypeError("argument must be 'abjad.Selection'")
    if not isinstance(respell_by_pitch_class, bool):
        raise TypeError("'respell_by_pitch_class' must be 'bool'")
    if not isinstance(include_multiples, bool):
        raise TypeError("'include_multiples' must be 'bool'")

    for leaf in selection.leaves():
        if isinstance(leaf, abjad.Chord):
            accidentals = [pitch.accidental for pitch in leaf.written_pitches]
            if not include_multiples:
                # dealing only with intervals of size equal to 1 semitone
                for i in range(len(leaf.written_pitches) - 1):
                    p1 = leaf.written_pitches[i]
                    p2 = leaf.written_pitches[i + 1]
                    interval12 = p1 - p2
                    try:
                        p3 = leaf.written_pitches[i + 2]
                        interval23 = p2 - p3
                    except IndexError:
                        p3 = None
                        interval23 = None
                    if (interval12 == abjad.NamedInterval('+A1')
                            and interval23 != abjad.NamedInterval('+A1')
                            and interval23 != abjad.NamedInterval('+m2')):
                        if not respell_by_pitch_class:
                            # respelling only one single note
                            if p1.accidental == abjad.Accidental('f'):
                                accidentals[i] = abjad.Accidental('s')
                            elif p1.accidental == abjad.Accidental(''):
                                accidentals[i + 1] = abjad.Accidental('f')
                        else:
                            # respelling all matching pitch-classes
                            if p1.accidental == abjad.Accidental('f'):
                                for j, p in enumerate(leaf.written_pitches):
                                    if p.pitch_class == p1.pitch_class:
                                        accidentals[j] = abjad.Accidental('s')
                            elif p1.accidental == abjad.Accidental(''):
                                for j, p in enumerate(leaf.written_pitches):
                                    if p.pitch_class == p2.pitch_class:
                                        accidentals[j] = abjad.Accidental('f')
            else:
                # dealing with augmented unisons as well as augmented 8as,
                # 15ths, etc.
                for i in range(len(leaf.written_pitches) - 1):
                    for j in range(i + 1, len(leaf.written_pitches)):
                        # no p3 this time since p1 and p2 are not necessary
                        # consecutive pitches in the chord
                        p1 = leaf.written_pitches[i]
                        p2 = leaf.written_pitches[j]
                        interval = abjad.NamedIntervalClass(p1 - p2)
                        if (interval in (
                                abjad.NamedIntervalClass('+A1'),
                                abjad.NamedIntervalClass('-d1'),
                        )):
                            # no need for respell_by_pitch_class since this
                            # will go through all notes in the chord anyway
                            if p1.accidental == abjad.Accidental('f'):
                                accidentals[i] = abjad.Accidental('s')
                            if p1.accidental == abjad.Accidental('s'):
                                accidentals[i] = abjad.Accidental('f')
                            elif p1.accidental == abjad.Accidental(''):
                                if p2.accidental == abjad.Accidental('s'):
                                    accidentals[j] = abjad.Accidental('f')
                                elif p2.accidental == abjad.Accidental('f'):
                                    accidentals[j] = abjad.Accidental('s')
            # rewritting chord with new spelling
            respelt_pitches = []
            for pitch, accidental in zip(leaf.written_pitches, accidentals):
                if accidental == abjad.Accidental('f'):
                    respelt_pitches.append(pitch._respell_with_flats())
                elif accidental == abjad.Accidental('s'):
                    respelt_pitches.append(pitch._respell_with_sharps())
                else:
                    respelt_pitches.append(pitch)
            leaf.written_pitches = respelt_pitches
Example #11
0
def reposition_clefs(
        selection: abjad.Selection,
        *,
        shift_clef_to_notes: bool = True,
        implicit_clef: abjad.Clef = abjad.Clef('treble'),
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function removes all consecutive repeated clefs. It can also be used
    to shift clefs from rests to pitched leaves.

    Basic usage:
        When consecutive clefs are the same, the second one is removed:

        >>> staff = abjad.Staff(r"c'1 | d'1")
        >>> abjad.attach(abjad.Clef('treble'), staff[0])
        >>> abjad.attach(abjad.Clef('treble'), staff[1])
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "treble"
            c'1
            \clef "treble"
            d'1
        }

        .. figure:: ../_images/reposition_clefs-ve7c2iykuyb.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "treble"
            c'1
            d'1
        }

        .. figure:: ../_images/reposition_clefs-w6sbmg4iihr.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.mutate(staff[:]).reposition_clefs()

    LilyPond's fallback clef:
        As seen above, LilyPond automatically omits repeated clefs unless the
        first clef is omitted. In that case, it uses a treble clef as fallback,
        although it won't then remove a subsequent repeated treble clef:

        >>> staff = abjad.Staff(r"c'1 | d'1")
        >>> abjad.attach(abjad.Clef('treble'), staff[1])
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \clef "treble"
            d'1
        }

        .. figure:: ../_images/reposition_clefs-ozr2sz3jugc.png

        This function handles fallback clefs too:

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'1
        }

        .. figure:: ../_images/reposition_clefs-0620w7q00lsr.png

    Clef structure:
        The function also removes clefs that are separated by an arbitrary
        number of leaves without clefs:

        >>> staff = abjad.Staff(r"c'1 | d'2 e'4 r4 | f'1")
        >>> abjad.attach(abjad.Clef('treble'), staff[4])
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'2
            e'4
            r4
            \clef "treble"
            f'1
        }

        .. figure:: ../_images/reposition_clefs-1dwpu3agebe.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'2
            e'4
            r4
            f'1
        }

        .. figure:: ../_images/reposition_clefs-wjmmwbhtaq.png

    Inputs with optimal clef structure:
        The function will not alter the container if the clef changes are
        already optimal.

        >>> staff = abjad.Staff(r"c'1 | a,2 bf,4 r4 | f'1")
        >>> abjad.attach(abjad.Clef('bass'), staff[1])
        >>> abjad.attach(abjad.Clef('treble'), staff[4])
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \clef "bass"
            a,2
            bf,4
            r4
            \clef "treble"
            f'1
        }

        .. figure:: ../_images/reposition_clefs-ooacruvoibr.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \clef "bass"
            a,2
            bf,4
            r4
            \clef "treble"
            f'1
        }

        .. figure:: ../_images/reposition_clefs-8z0s96frl4x.png

    Multi-measure rests:
        The function handles rests and multi-measure rests.

        >>> staff = abjad.Staff(r"c'1 | d'2 r2 | R1 | e'1")
        >>> abjad.attach(abjad.Clef('treble'), staff[0])
        >>> abjad.attach(abjad.Clef('treble'), staff[4])
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'2
            r2
            R1
            \clef "treble"
            e'1
        }

        .. figure:: ../_images/reposition_clefs-wpuzqrszs7i.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'2
            r2
            R1
            e'1
        }

        .. figure:: ../_images/reposition_clefs-os7dqkh11vl.png

    ``shift_clef_to_notes``:
        By default, clefs attached to rests are shifted to the first pitched
        leaf.

        >>> staff = abjad.Staff(r"c'1 | d'2 r2 | fs1")
        >>> abjad.attach(abjad.Clef('treble'), staff[0])
        >>> abjad.attach(abjad.Clef('bass'), staff[2])
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "treble"
            c'1
            d'2
            \clef "bass"
            r2
            fs1
        }

        .. figure:: ../_images/reposition_clefs-jft5tljn0ni.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "treble"
            c'1
            d'2
            r2
            \clef "bass"
            fs1
        }

        .. figure:: ../_images/reposition_clefs-pirrrq3p6di.png

        Set ``shift_clef_to_notes`` to ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(r"c'1 | d'2 r2 | fs1")
        >>> abjad.attach(abjad.Clef('treble'), staff[0])
        >>> abjad.attach(abjad.Clef('bass'), staff[2])
        >>> auxjad.mutate(staff[:]).reposition_clefs(shift_clef_to_notes=False)
        >>> abjad.f(staff)
        \new Staff
        {
            \clef "treble"
            c'1
            d'2
            \clef "bass"
            r2
            fs1
        }

        .. figure:: ../_images/reposition_clefs-srrb69k33oe.png

    Multiple multi-measure rests:
        Clefs are shifted even if the container has multiple multi-measure
        rests.

        >>> staff = abjad.Staff(r"\time 3/4 c'2. | d'4 r2 | R1 * 3/4 | e'2.")
        >>> abjad.attach(abjad.Clef('treble'), staff[0])
        >>> abjad.attach(abjad.Clef('bass'), staff[2])
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            \clef "treble"
            c'2.
            d'4
            \clef "bass"
            r2
            R1 * 3/4
            e'2.
        }

        .. figure:: ../_images/reposition_clefs-1l1ws1tqqt5.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            \clef "treble"
            c'2.
            d'4
            r2
            R1 * 3/4
            \clef "bass"
            e'2.
        }

        .. figure:: ../_images/reposition_clefs-gmh7uqxjjrf.png

    Subcontainers:
        The container from which the selection is made can also have
        subcontainers, including cases in which the clefs are attached to
        leaves of subcontainers:

        >>> staff = abjad.Staff([abjad.Note("c'2"),
        ...                      abjad.Chord("<d' f'>2"),
        ...                      abjad.Tuplet((2, 3), "g'2 a'2 b'2"),
        ...                      ])
        >>> abjad.attach(abjad.Clef('treble'), staff[2][1])
        >>> abjad.f(staff)
        \new Staff
        {
            c'2
            <d' f'>2
            \times 2/3 {
                g'2
                \clef "treble"
                a'2
                b'2
            }
        }

        .. figure:: ../_images/reposition_clefs-vwygykrmjd.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'2
            <d' f'>2
            \times 2/3 {
                g'2
                a'2
                b'2
            }
        }

        .. figure:: ../_images/reposition_clefs-9gaqlf92kc.png

    ``implicit_clef``:
        By default, when the first leaf doesn't have a clef the function
        assumes that the music is written in treble clef (which is the default
        fallback clef in LilyPond).

        >>> staff = abjad.Staff(r"c'1 | d'1")
        >>> abjad.attach(abjad.Clef('treble'), staff[1])
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            \clef "treble"
            d'1
        }

        .. figure:: ../_images/reposition_clefs-tuxicnsglgk.png

        >>> auxjad.mutate(staff[:]).reposition_clefs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            d'1
        }

        .. figure:: ../_images/reposition_clefs-co27o4xxato.png

        Set the argument ``implicit_clef`` to a different |abjad.Clef| to
        change the implicit clef.

        >>> staff = abjad.Staff(r"c1 | d1")
        >>> abjad.attach(abjad.Clef('bass'), staff[1])
        >>> abjad.f(staff)
        \new Staff
        {
            c1
            \clef "bass"
            d1
        }

        .. figure:: ../_images/reposition_clefs-jyp5xd92vgi.png

        >>> auxjad.mutate(staff[:]).reposition_clefs(
        ...     implicit_clef=abjad.Clef('bass')
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c1
            d1
        }

        .. figure:: ../_images/reposition_clefs-o7bi4n2n58.png

        This can be useful when extending a container that already has a
        specific clef.

        >>> music = abjad.Staff(r"\clef bass c4 d4 e4 f4")
        >>> music.extend(staff)
        >>> abjad.f(music)
        \new Staff
        {
            \clef "bass"
            c4
            d4
            e4
            f4
            c1
            d1
        }

        .. figure:: ../_images/reposition_clefs-7y32wepotnf.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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(shift_clef_to_notes, bool):
        raise TypeError("'shift_clef_to_notes' must be 'bool'")
    if not isinstance(implicit_clef, abjad.Clef):
        raise TypeError("'implicit_clef' must be 'abjad.Clef'")

    leaves = selection.leaves()

    # shifting clefs from rests to notes
    if shift_clef_to_notes:
        shifted_clef = None
        for leaf in leaves[1:]:
            clef = abjad.inspect(leaf).indicator(abjad.Clef)
            if isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest)):
                if abjad.inspect(leaf).indicator(abjad.Clef) is not None:
                    shifted_clef = abjad.inspect(leaf).indicator(abjad.Clef)
                    abjad.detach(abjad.Clef, leaf)
            else:
                if (abjad.inspect(leaf).indicator(abjad.Clef) is None
                        and shifted_clef is not None):
                    abjad.attach(shifted_clef, leaf)
                shifted_clef = None

    # removing repeated clefs
    previous_clef = abjad.inspect(leaves[0]).indicator(abjad.Clef)
    if previous_clef is None:
        previous_clef = implicit_clef
    for leaf in leaves[1:]:
        clef = abjad.inspect(leaf).indicator(abjad.Clef)
        if clef == previous_clef:
            abjad.detach(abjad.Clef, leaf)
        elif clef is not None:
            previous_clef = clef
Example #12
0
def extract_trivial_tuplets(selection: abjad.Selection) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function looks for tuplets filled with rests or with tied notes or
    chords and replaces them with a single leaf.

    Basic usage:
        Usage is simple:

        >>> staff = abjad.Staff(
        ...     r"\times 2/3 {r4 r2} \times 2/3 {c'8 ~ c'8 ~ c'2}"
        ... )
        >>> abjad.f(container)
        {
            \times 2/3 {
                r4
                r2
            }
            \times 2/3 {
                c'8
                ~
                c'8
                ~
                c'2
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-4htz2xebxwf.png

        >>> auxjad.mutate(container[:]).extract_trivial_tuplets()
        >>> abjad.f(container)
        {
            r2
            c'2
        }

        .. figure:: ../_images/extract_trivial_tuplets-2dbuwo4erhb.png

        It also works with containers with tuplets within tuplets.

        >>> container = abjad.Container(r"\times 4/5 {r2. \times 2/3 {r2 r4}}")
        >>> abjad.f(container)
        {
            \times 4/5 {
                r2.
                \times 2/3 {
                    r2
                    r4
                }
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-8d5bcyxcmhc.png

        >>> auxjad.mutate(container[:]).extract_trivial_tuplets()
        >>> abjad.f(container)
        {
            r1
        }

        .. figure:: ../_images/extract_trivial_tuplets-2a2fvwimyrx.png

        >>> container = abjad.Container(
        ...     r"\times 4/5 {c'2. ~ \times 2/3 {c'2 ~ c'4}}"
        ... )
        >>> abjad.f(container)
        {
            \times 4/5 {
                c'2.
                ~
                \times 2/3 {
                    c'2
                    ~
                    c'4
                }
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-xka6r5iyo4l.png

        >>> auxjad.mutate(staff[:]).extract_trivial_tuplets()
        >>> abjad.f(container)
        {
            c'1
        }

        .. figure:: ../_images/extract_trivial_tuplets-f1qxi44xcsw.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).extract_trivial_tuplets()
        >>> abjad.mutate(staff[:]).extract_trivial_tuplets()

    Partial extraction:
        This function also extracts tuplets within tuplets.

        >>> container = abjad.Container(
        ...     r"r2 \times 2/3 {r2 r4} \times 4/5 {c'2. \times 2/3 {r2 r4}}"
        ... )
        >>> abjad.f(container)
        {
            r2
            \times 2/3 {
                r2
                r4
            }
            \times 4/5 {
                c'2.
                \times 2/3 {
                    r2
                    r4
                }
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-adibnkb1mbs.png

        >>> auxjad.mutate(container[:]).extract_trivial_tuplets()
        >>> abjad.f(container)
        {
            r2
            r2
            \times 4/5 {
                c'2.
                r2
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-xldohyedqs.png

    .. tip::

        Use |auxjad.mutate().rests_to_multimeasure_rest()| to replace measures
        filled with rests by a single multi-measure rest. That function makes
        use of |auxjad.mutate().extract_trivial_tuplets()|, so it is not
        necessary to flatten the empty tuplets beforehand.

    Time signature changes:
        Works with measures with any time signature.

        >>> container = abjad.Staff(r"\time 3/4 r2. \times 3/2 {r4 r4}")
        >>> auxjad.mutate(container[:]).extract_trivial_tuplets()
        >>> abjad.f(container)
        \new Staff
        {
            \time 3/4
            r2.
            r2.
        }

        .. figure:: ../_images/extract_trivial_tuplets-sa1tqmvtkx.png

    Non-assignable durations:
        This function also extracts tuplets which sum up to a non-assignable
        duration. In this case, it creates multiple leaves and substitutes them
        for the original tuplet. Indicators are passed on to the first leaf of
        the new leaves.

        >>> staff = abjad.Staff(r"\time 6/4 c'4\f \times 5/6 {g1.\p}")
        >>> abjad.f(staff)
        \new Staff
        {
            \time 6/4
            c'4
            \f
            \tweak text #tuplet-number::calc-fraction-text
            \times 5/6 {
                g1.
                \p
            }
        }

        .. figure:: ../_images/extract_trivial_tuplets-l4kp9g5v7m.png

        >>> abjad.mutate(staff[:]).extract_trivial_tuplets()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 6/4
            c'4
            \f
            g1
            \p
            ~
            g4
        }

        .. figure:: ../_images/extract_trivial_tuplets-8r40ndemvpn.png

    .. 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.f(container)
        {
            %%% \time 3/4 %%%
            c'4
            d'4
            e'4
        }

        .. figure:: ../_images/extract_trivial_tuplets-6wymsb7z1n4.png

        >>> staff = abjad.Staff([container])
        >>> abjad.f(container)
        {
            \time 3/4
            c'4
            d'4
            e'4
        }

        .. figure:: ../_images/extract_trivial_tuplets-moavfyqtxza.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'")

    tuplets = selection.tuplets()
    if len(tuplets) == 0:
        return

    for tuplet in tuplets:
        leaves = abjad.select(tuplet).leaves()
        if (all(isinstance(leaf, abjad.Rest) for leaf in leaves)
                and len(leaves) > 1):
            duration = tuplet.multiplied_duration
            rests = abjad.LeafMaker()(None, duration)
            time_signature = abjad.inspect(leaves[0]).indicator(
                abjad.TimeSignature)
            if time_signature is not None:
                abjad.attach(time_signature, rests[0])
            abjad.mutate(tuplet).replace(rests)
        if tuplet.sustained():
            duration = tuplet.multiplied_duration
            n_elements = len(tuplet)
            after_tie = abjad.inspect(leaves[-1]).indicator(abjad.Tie)
            for _ in range(n_elements - 1):
                tuplet.pop(-1)
            if not after_tie:
                abjad.detach(abjad.Tie(), leaves[0])
            if duration.is_assignable:
                leaves[0].written_duration = duration
                abjad.mutate(tuplet).extract()
            elif duration.has_power_of_two_denominator:
                if isinstance(leaves[0], abjad.Note):
                    pitch = leaves[0].written_pitch
                elif isinstance(leaves[0], abjad.Chord):
                    pitch = leaves[0].written_pitches
                else:
                    pitch = None
                notes = abjad.LeafMaker()(pitch, duration)
                indicators = abjad.inspect(leaves[0]).indicators()
                for indicator in indicators:
                    abjad.attach(indicator, notes[0])
                abjad.mutate(leaves[0]).replace(notes)
                abjad.mutate(tuplet).extract()
            else:
                continue
    for tuplet in tuplets:
        if tuplet.trivializable():
            tuplet.trivialize()
            abjad.mutate(tuplet).extract()
Example #13
0
def underfull_duration(selection: abjad.Selection) -> abjad.Duration:
    r"""Returns a |abjad.Duration| representing the duration missing in the
    last measure of an input |abjad.Selection| which is not fully filled in.

    Basic usage:
        Returns the missing duration of the last measure of an
        |abjad.Selection|. If no time signature is encountered, it uses
        LilyPond's fallback time signature of ``4/4``.

        >>> container1 = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> container2 = abjad.Container(r"c'4 d'4 e'4")
        >>> container3 = abjad.Container(r"c'4 d'4 e'4 f'4 | c'4")
        >>> container4 = abjad.Container(r"c'4 d'4 e'4 f'4 | c'4 d'4 e'4 f'4")
        >>> auxjad.inspect(container1[:]).underfull_duration()
        0
        >>> auxjad.inspect(container2[:]).underfull_duration()
        1/4
        >>> auxjad.inspect(container3[:]).underfull_duration()
        3/4
        >>> auxjad.inspect(container4[:]).underfull_duration()
        0

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.inspect()|. Therefore it can be used from either
        :func:`auxjad.inspect()` or |abjad.inspect()|, as shown below:

        >>> container = abjad.Container(r"c'4 d'4 e'4")
        >>> auxjad.inspect(container[:]).underfull_duration()
        1/4
        >>> abjad.inspect(container[:]).underfull_duration()
        1/4

    Time signature changes:
        Handles any time signatures as well as changes of time signature.

        >>> container1 = abjad.Container(r"\time 4/4 c'4 d'4 e'4 f'4")
        >>> container2 = abjad.Container(r"\time 3/4 a2. \time 2/4 r2")
        >>> container3 = abjad.Container(r"\time 5/4 g1 ~ g4 \time 4/4 af'2")
        >>> container4 = abjad.Container(r"\time 6/8 c'2 ~ c'8")
        >>> auxjad.inspect(container1[:]).underfull_duration()
        0
        >>> auxjad.inspect(container2[:]).underfull_duration()
        0
        >>> auxjad.inspect(container3[:]).underfull_duration()
        1/2
        >>> auxjad.inspect(container4[:]).underfull_duration()
        1/8

    Partial time signatures:
        Correctly handles partial time signatures.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> time_signature = abjad.TimeSignature((3, 4), partial=(1, 4))
        >>> abjad.attach(time_signature, container[0])
        >>> auxjad.inspect(container[:]).underfull_duration()
        0

    Multi-measure rests:
        It also handles multi-measure rests.

        >>> container1 = abjad.Container(r"R1")
        >>> container2 = abjad.Container(r"\time 3/4 R1*3/4 \time 2/4 r2")
        >>> container3 = abjad.Container(r"\time 5/4 R1*5/4 \time 4/4 g''4")
        >>> container4 = abjad.Container(r"\time 6/8 R1*1/2")
        >>> auxjad.inspect(container1[:]).underfull_duration()
        0
        >>> auxjad.inspect(container2[:]).underfull_duration()
        0
        >>> auxjad.inspect(container3[:]).underfull_duration()
        3/4
        >>> auxjad.inspect(container4[:]).underfull_duration()
        1/4

    .. error::

        If a selection is malformed, i.e. it has an underfilled measure before
        a time signature change, the function raises a :exc:`ValueError`
        exception. This is also the case when a selection starts in the middle
        of a measure.

        >>> container = abjad.Container(r"\time 5/4 g''1 \time 4/4 f'1")
        >>> auxjad.inspect(container[:]).underfull_duration()
        ValueError: 'selection' is malformed, with an underfull measure
        preceding a time signature change

    .. warning::

        The input container 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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    leaves = selection.leaves()
    # handling first leaf
    time_signature = abjad.inspect(leaves[0]).effective(abjad.TimeSignature)
    if time_signature is not None:
        effective_time_signature = time_signature
    else:
        effective_time_signature = abjad.TimeSignature((4, 4))
    duration = abjad.inspect(leaves[0]).duration()
    # handling partial time signatures
    if effective_time_signature.partial is not None:
        duration += effective_time_signature.duration
        duration -= effective_time_signature.partial
    # all other leaves
    for leaf in leaves[1:]:
        time_signature = abjad.inspect(leaf).effective(abjad.TimeSignature)
        if (time_signature is not None
                and time_signature != effective_time_signature):
            if duration % effective_time_signature.duration != 0:
                raise ValueError("'selection' is malformed, with an underfull "
                                 "measure preceding a time signature change")
            effective_time_signature = time_signature
            duration = abjad.Duration(0)
        duration += abjad.inspect(leaf).duration()
    duration_last_bar = duration % effective_time_signature.duration
    duration_left = duration_last_bar
    if duration_last_bar > abjad.Duration(0):
        duration_left = effective_time_signature.duration - duration_last_bar
    return duration_left
def double_barlines_before_time_signatures(selection: abjad.Selection,
                                           *,
                                           context: Optional[str] = None,
                                           ) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function adds double bar lines before all time signatures.

    Basic usage:
        Whenever a new time signature appears, the function adds a double bar
        line before it:

        >>> staff = abjad.Staff(
        ...     r"\time 3/4 c'2. \time 4/4 d'1 e'1 \time 6/4 f'2. g'2."
        ... )
        >>> auxjad.mutate(staff[:]).double_barlines_before_time_signatures()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            c'2.
            \bar "||"
            \time 4/4
            d'1
            e'1
            \bar "||"
            \time 6/4
            f'2.
            g'2.
        }

        .. figure:: ../_images/remove_repeated_time_signatures-2O7JyxN1CS.png

    Multi-measure rests:
        This function can handle multi-measure rests too.

        >>> staff = abjad.Staff(
        ...     r"\time 3/4 R1 * 3/4 "
        ...     r"\time 4/4 R1 * 2 "
        ...     r"\time 6/4 R1 * 6/4 "
        ...     r"\time 4/4 R1"
        ... )
        >>> auxjad.mutate(staff[:]).double_barlines_before_time_signatures()
        >>> abjad.f(staff)
        \new Staff
        {
            \time 3/4
            R1 * 3/4
            \bar "||"
            \time 4/4
            R1 * 2
            \bar "||"
            \time 6/4
            R1 * 3/2
            \bar "||"
            \time 4/4
            R1
        }

        .. figure:: ../_images/remove_repeated_time_signatures-aYmnnFDdRh.png

    Input with bar lines:
        If the input selection already contains bar lines at poinst where a
        time signature change, the function will only replace those of type
        ``"|"`` or ``""``, keeping all others as they were.

        >>> staff = abjad.Staff(
        ...     r"R1 "
        ...     r"\time 3/4 c'2. "
        ...     r"\time 4/4 d'1 "
        ...     r"e'1 "
        ...     r"\time 6/4 f'2. g'2. "
        ...     r"\time 2/4 a'2"
        ... )
        >>> abjad.attach(abjad.BarLine('.|:'), staff[0])
        >>> abjad.attach(abjad.BarLine(':|.'), staff[1])
        >>> abjad.attach(abjad.BarLine('|'), staff[3])
        >>> abjad.attach(abjad.BarLine('!'), staff[5])
        >>> auxjad.mutate(staff[:]).double_barlines_before_time_signatures()
        >>> abjad.f(staff)
        \new Staff
        {
            R1
            \bar ".|:"
            \time 3/4
            c'2.
            \bar ":|."
            \time 4/4
            d'1
            e'1
            \bar "||"
            \time 6/4
            f'2.
            g'2.
            \bar "!"
            \time 2/4
            a'2
        }

        .. figure:: ../_images/remove_repeated_time_signatures-jUymJWLdR7.png

    .. warning::

        Attempting to add barlines to multiple staves in an |abjad.Score| at
        the same point in the score will raise an exception:

        .. code::

            >>> up = abjad.Staff(r"\time 4/4 c'1 d'1 \time 6/4 e'1.")
            >>> down = abjad.Staff(
            ...     r"\time 4/4 \clef bass c1 d1 \time 6/4 e1."
            ... )
            >>> score = abjad.Score([up, down])
            >>> auxjad.mutate(up[:]).double_barlines_before_time_signatures()
            >>> auxjad.mutate(down[:]).double_barlines_before_time_signatures()
            abjad.exceptions.PersistentIndicatorError:

            Can not attach ...

            abjad.Wrapper(
                context='Score',
                indicator=abjad.BarLine('||', format_slot='after', ),
                tag=abjad.Tag(),
                )

            ... to Note('d1') in None because ...

            abjad.Wrapper(
                context='Score',
                indicator=abjad.BarLine('||', format_slot='after', ),
                tag=abjad.Tag(),
                )

            ... is already attached to Note("d'1") in None.

        This is because, by default, bar lines belong to the score scope. In
        order to have bar lines on both staves (e.g. for easier part
        extraction), invoke this mutation with the argument ``context`` set to
        ``'Staff'`` so that the double bar lines become scoped to the staff
        instead of the score:

        >>> up = abjad.Staff(r"\time 4/4 c'1 d'1 \time 6/4 e'1.")
        >>> down = abjad.Staff(r"\time 4/4 \clef bass c1 d1 \time 6/4 e1.")
        >>> score = abjad.Score([up, down])
        >>> auxjad.mutate(up[:]).double_barlines_before_time_signatures(
        ...     context='Staff',
        ... )
        >>> auxjad.mutate(down[:]).double_barlines_before_time_signatures(
        ...     context='Staff',
        ... )
        >>> abjad.f(score)
        \new Score
        <<
            \new Staff
            {
                \time 4/4
                c'1
                d'1
                \bar "||"
                \time 6/4
                e'1.
            }
            \new Staff
            {
                \time 4/4
                \clef "bass"
                c1
                d1
                \bar "||"
                \time 6/4
                e1.
            }
        >>

        .. figure:: ../_images/remove_repeated_time_signatures-yD6KL6xbrV.png

        In this case, both individual staves will also have the bar lines:

        >>> abjad.f(up)
        \new Staff
        {
            \time 4/4
            c'1
            d'1
            \bar "||"
            \time 6/4
            e'1.
        }

        .. figure:: ../_images/remove_repeated_time_signatures-Zs1hSq2uwY.png

        >>> abjad.f(down)
        \new Staff
        {
            \time 4/4
            \clef "bass"
            c1
            d1
            \bar "||"
            \time 6/4
            e1.
        }

        .. figure:: ../_images/remove_repeated_time_signatures-QYOgyhLJ2f.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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")

    leaves = selection.leaves()

    for i, leaf in enumerate(leaves[1:], 1):
        time_signature = abjad.inspect(leaf).indicator(abjad.TimeSignature)
        if time_signature is not None:
            inspector = abjad.inspect(leaves[i - 1])
            barline = inspector.indicator(abjad.BarLine)
            if barline is not None and barline.abbreviation in ('|', ''):
                abjad.detach(abjad.BarLine, leaves[i - 1])
                barline = None
            if barline is None:
                if context is None:
                    abjad.attach(abjad.BarLine("||"), leaves[i - 1])
                else:
                    abjad.attach(abjad.BarLine("||"),
                                 leaves[i - 1],
                                 context=context,
                                 )
Example #15
0
def remove_repeated_time_signatures(selection: abjad.Selection) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function removes all consecutive repeated time signatures.

    Basic usage:
        When two consecutive measures have identical time signatures, the
        second one is removed:

        >>> staff = abjad.Staff(r"c'4 d'8 | c'4 d'8")
        >>> abjad.attach(abjad.TimeSignature((3, 8)), staff[0])
        >>> abjad.attach(abjad.TimeSignature((3, 8)), staff[2])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/8
                c'4
                d'8
                \time 3/8
                c'4
                d'8
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-feZSi4Trsg.png

        >>> auxjad.mutate.remove_repeated_time_signatures(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/8
                c'4
                d'8
                c'4
                d'8
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-ImmpJOWn5U.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.remove_repeated_time_signatures(staff[:])
        >>> abjad.mutate.remove_repeated_time_signatures(staff[:])

    Time signature structure:
        The function also removes time signatures that are separated by an
        arbitrary number of measures without one:

        >>> staff = abjad.Staff(r"c'4 d'8 e'4. c'4 d'8")
        >>> abjad.attach(abjad.TimeSignature((3, 8)), staff[0])
        >>> abjad.attach(abjad.TimeSignature((3, 8)), staff[3])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/8
                c'4
                d'8
                e'4.
                \time 3/8
                c'4
                d'8
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-ihs4kU1dMe.png

        >>> auxjad.mutate.remove_repeated_time_signatures(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/8
                c'4
                d'8
                e'4.
                c'4
                d'8
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-Ans1RrG5ZW.png

    Subcontainers:
        The container from which the selection is made can also have
        subcontainers, including cases in which the time signatures are
        attached to leaves of subcontainers:

        >>> staff = abjad.Staff([abjad.Note("c'2"),
        ...                      abjad.Chord("<d' f'>2"),
        ...                      abjad.Tuplet((2, 3), "g2 a2 b2"),
        ...                      ])
        >>> abjad.attach(abjad.TimeSignature((2, 2)), staff[0])
        >>> abjad.attach(abjad.TimeSignature((2, 2)), staff[2][0])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 2/2
                c'2
                <d' f'>2
                \times 2/3
                {
                    \time 2/2
                    g2
                    a2
                    b2
                }
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-Nybwh816FT.png

        >>> auxjad.mutate.remove_repeated_time_signatures(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 2/2
                c'2
                <d' f'>2
                \times 2/3
                {
                    g2
                    a2
                    b2
                }
            }

        ..  figure:: ../_images/remove_repeated_time_signatures-PNCfPcnTtj.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 selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")

    measures = selection.group_by_measure()
    head = selection.leaf(0)
    previous_time_signature = abjad.get.indicator(
        head,
        abjad.TimeSignature,
    )
    if previous_time_signature is None:
        previous_time_signature = abjad.TimeSignature((4, 4))
    for measure in measures[1:]:
        head = abjad.select(measure).leaf(0)
        time_signature = abjad.get.indicator(head, abjad.TimeSignature)
        if time_signature == previous_time_signature:
            abjad.detach(abjad.TimeSignature, head)
        elif time_signature is not None:
            previous_time_signature = time_signature
Example #16
0
def reposition_slurs(
    selection: abjad.Selection,
    *,
    allow_slurs_under_rests: bool = False,
    close_unterminated_final_slur: bool = True,
) -> None:
    r"""Mutates an input |abjad.Selection| in place and has no return value;
    this function repositions all slurs that starts or ends on rests.

    Basic usage:
        This function will shift slurs that ends on rests to the previous
        pitched leaf.

        >>> staff = abjad.Staff(r"c'1( d'2 r2) r1 e'1")
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            r2
            )
            r1
            e'1
        }

        .. figure:: ../_images/reposition_slurs-uxji4xx6ftk.png

        >>> staff = abjad.Staff(r"c'1( d'2 r2) r1 e'1")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            r1
            e'1
        }

        .. figure:: ../_images/reposition_slurs-7nnp5cttm4y.png

    .. note::

        Auxjad automatically adds this function as an extension method to
        |abjad.mutate()|. It can thus be used from either
        :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines
        below are equivalent:

        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.mutate(staff[:]).reposition_slurs()

    Rests:
        Slurs starting on rests are shifted to the next pitched leaf.

        >>> staff = abjad.Staff(r"c'1 r2( d'2 e'1)")
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            r2
            (
            d'2
            e'1
            )
        }

        .. figure:: ../_images/reposition_slurs-2j7hgqd7bt1.png

        >>> staff = abjad.Staff(r"c'1 r2( d'2 e'1)")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            r2
            d'2
            (
            e'1
            )
        }

        .. figure:: ../_images/reposition_slurs-i957u1wt30m.png

    Multiple rests:
        This function also works when multiple rests are present.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 r1) e'1")
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            r2
            r1
            )
            e'1
        }

        .. figure:: ../_images/reposition_slurs-v76u42x7idk.png

        >>> staff = abjad.Staff(r"c'1( d'2 r2 r1) e'1")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            r1
            e'1
        }

        .. figure:: ../_images/reposition_slurs-burs1t0daid.png

    ``allow_slurs_under_rests``:
        By default, a slur crossing a rest is broken into two.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 e'1 f'1)")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            e'1
            (
            f'1
            )
        }

        .. figure:: ../_images/reposition_slurs-8wb7orpt285.png

        Set the optional keyword argument ``allow_slurs_under_rests`` to
        ``True`` to allow slurs under rests.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 e'1 f'1)")
        >>> auxjad.mutate(staff[:]).reposition_slurs(
        ...     allow_slurs_under_rests=True,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            r2
            e'1
            f'1
            )
        }

        .. figure:: ../_images/reposition_slurs-ftb59kz6u8j.png

    ``close_unterminated_final_slur``:
        By default, unterminated slurs at the end of the selection are closed
        when possible or removed when not.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 e'2 f'2) g'1( a'1")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            e'2
            (
            f'2
            )
            g'1
            (
            a'1
            )
        }

        .. figure:: ../_images/reposition_slurs-70dli8e0kqr.png

        Set the optional keyword argument ``close_unterminated_final_slur`` to
        ``False`` to disable this behaviour.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 e'2 f'2) g'1( a'1")
        >>> auxjad.mutate(staff[:]).reposition_slurs(
        ...     close_unterminated_final_slur=False,
        ... )
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            e'2
            (
            f'2
            )
            g'1
            (
            a'1
        }

        .. figure:: ../_images/reposition_slurs-1usa2dezl45.png

        When there are no pitched leaves left after an unterminated open slur,
        it is removed.

        >>> staff = abjad.Staff(r"c'1( d'2 r2 e'2 f'2) g'1( r1")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            r2
            e'2
            (
            f'2
            )
            g'1
            r1
        }

        .. figure:: ../_images/reposition_slurs-a0uakfcltuf.png

    .. note::

        Duplicate slur starts or stops are removed. Note that the score output
        will not change, as LilyPond also ignores duplicate slurs, but the
        output of |abjad.f()| will be cleaner.

        >>> staff = abjad.Staff(r"c'1( d'2) e'2) f'2( g'2( a'1)")
        >>> auxjad.mutate(staff[:]).reposition_slurs()
        >>> abjad.f(staff)
        \new Staff
        {
            c'1
            (
            d'2
            )
            e'2
            f'2
            (
            g'2
            a'1
            )
        }

        .. figure:: ../_images/reposition_slurs-0ugn322x3tr.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.Container' or child class")
    if not selection.leaves().are_contiguous_logical_voice():
        raise ValueError("argument must be contiguous logical voice")
    if not isinstance(allow_slurs_under_rests, bool):
        raise TypeError("'allow_slurs_under_rests' must be 'bool'")
    if not isinstance(close_unterminated_final_slur, bool):
        raise TypeError("'close_unterminated_final_slur' must be 'bool'")

    leaves = selection.leaves()

    # checking for final unfinished slurs
    if close_unterminated_final_slur:
        for leaf in leaves[::-1]:
            inspector = abjad.inspect(leaf)
            if inspector.indicator(abjad.StartSlur) is not None:
                if leaf is leaves[-1]:
                    abjad.detach(abjad.StartSlur(), leaf)
                elif (abjad.inspect(leaves[-1]).indicator(abjad.StopSlur) is
                      None):
                    abjad.attach(abjad.StopSlur(), leaves[-1])
            if inspector.indicator(abjad.StopSlur) is not None:
                break

    # checking for duplicate open or close slurs
    start_slur_count = 0
    stop_slur_count = 0
    for leaf in leaves:
        inspector = abjad.inspect(leaf)
        if inspector.indicator(abjad.StartSlur) is not None:
            start_slur_count += 1
            stop_slur_count = 0
            if start_slur_count > 1:
                abjad.detach(abjad.StartSlur(), leaf)
        elif inspector.indicator(abjad.StopSlur) is not None:
            stop_slur_count += 1
            start_slur_count = 0
            if stop_slur_count > 1:
                abjad.detach(abjad.StopSlur(), leaf)

    # shifting slurs from rests to notes
    shifted_startslur = None
    for leaf in leaves:
        inspector = abjad.inspect(leaf)
        if isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest)):
            if inspector.indicator(abjad.StartSlur) is not None:
                shifted_startslur = inspector.indicator(abjad.StartSlur)
                abjad.detach(abjad.StartSlur, leaf)
        else:
            if inspector.indicator(abjad.StartSlur) is None:
                if shifted_startslur is not None:
                    abjad.attach(shifted_startslur, leaf)
                    shifted_startslur = None
    shifted_stopslur = None
    for leaf in leaves[::-1]:
        inspector = abjad.inspect(leaf)
        if isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest)):
            if inspector.indicator(abjad.StopSlur) is not None:
                shifted_stopslur = inspector.indicator(abjad.StopSlur)
                abjad.detach(abjad.StopSlur, leaf)
        else:
            if inspector.indicator(abjad.StopSlur) is None:
                if shifted_stopslur is not None:
                    abjad.attach(shifted_stopslur, leaf)
                    shifted_stopslur = None

    # splitting slurs under rests
    if not allow_slurs_under_rests:
        active_slur = False
        for index, leaf in enumerate(leaves):
            inspector = abjad.inspect(leaf)
            if inspector.indicator(abjad.StartSlur) is not None:
                active_slur = True
            elif inspector.indicator(abjad.StopSlur) is not None:
                if not active_slur:
                    abjad.detach(abjad.StopSlur, leaf)
                active_slur = False
            if (isinstance(leaf, (abjad.Rest, abjad.MultimeasureRest))
                    and active_slur):
                previous_leaf = abjad.select(leaf).with_previous_leaf()[0]
                if (abjad.inspect(previous_leaf).indicator(abjad.StopSlur) is
                        None):
                    abjad.attach(abjad.StopSlur(), previous_leaf)
                for next_leaf in leaves[index + 1:]:
                    if not isinstance(next_leaf,
                                      (abjad.Rest, abjad.MultimeasureRest)):
                        if (abjad.inspect(next_leaf).indicator(abjad.StartSlur)
                                is None):
                            abjad.attach(abjad.StartSlur(), next_leaf)
                        break
        for leaf in leaves:
            inspector = abjad.inspect(leaf)
            if (inspector.indicator(abjad.StartSlur) is not None
                    and inspector.indicator(abjad.StopSlur) is not None):
                abjad.detach(abjad.StartSlur, leaf)
                abjad.detach(abjad.StopSlur, leaf)

    # removing slurs spanning a single logical tie
    for logical_tie in selection.logical_ties():
        inspector_head = abjad.inspect(logical_tie[0])
        inspector_tail = abjad.inspect(logical_tie[-1])
        if (inspector_head.indicator(abjad.StartSlur) is not None
                and inspector_tail.indicator(abjad.StopSlur) is not None):
            abjad.detach(abjad.StartSlur, logical_tie[0])
            abjad.detach(abjad.StopSlur, logical_tie[-1])