import random
from typing import Any, Optional, Union

import abjad

from .. import get, mutate
from ..score.ArtificialHarmonic import ArtificialHarmonic


class Fader:
    r"""Takes an |abjad.Container| (or child class) as input and, using it as
    reference, gradually removes or adds notes one by one to an output
    |abjad.Selection|.

    Basic usage:
        Calling the object will return an |abjad.Selection| generated by the
        fading process. Each call of the object will apply the fading process
        to the previous result. By default, the container will be faded out
        (that is, its notes will be gradually removed one by one). Note that,
        by default, the first call in fade out mode outputs the initial
        container, with subsequent calls replacing leaves for rests.

        >>> container = abjad.Container(r"c'4 ~ c'16 d'8. e'8 f'4.")
        >>> abjad.show(container)

        ..  docs::

            {
                c'4
                ~
                c'16
                d'8.
                e'8
                f'8
                ~
                f'4
            }

        ..  figure:: ../_images/Fader-cQstYYC7CZ.png

        >>> fader = auxjad.Fader(container)
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                ~
                c'16
                d'8.
                e'8
                f'4.
            }

        ..  figure:: ../_images/Fader-pvlbt0azuqj.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                ~
                c'16
                r8.
                e'8
                f'4.
            }

        ..  figure:: ../_images/Fader-4bebflmnff9.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r2
                e'8
                f'4.
            }

        ..  figure:: ../_images/Fader-64h0bb02goc.png

        The property :attr:`current_window` can be used to access the current
        window without processing it.

        >>> notes = fader.current_window()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r2
                e'8
                f'4.
            }

        ..  figure:: ../_images/Fader-ruetb1tzhtn.png

    :attr:`process_on_first_call`:
        The very first call will output the input container without processing
        it. To disable this behaviour and apply the fading process on the very
        first call, initialise the class with the keyword argument
        :attr:`process_on_first_call` set to ``True``.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> fader = auxjad.Fader(container,
        ...                      process_on_first_call=True,
        ...                      )
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                r4
            }

        ..  figure:: ../_images/Fader-rbnsf64kjoq.png

    :attr:`mode`:
        This class has two modes, set by the keyword argument :attr:`mode`. The
        default mode is ``'out'`` (i.e. 'fade out mode'), in which the fader
        will gradually remove notes one by one (see examples above). When set
        to ``'in'`` (i.e. 'fade in mode'), the fader will start with an empty
        container with the same length and time signature  structure as the
        input music and will gradually add the original notes one by one.

        >>> container = abjad.Container(r"c'4 ~ c'16 d'8. e'8 f'4.")
        >>> fader = auxjad.Fader(container,
        ...                      mode='in',
        ...                      )
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                R1
            }

        ..  figure:: ../_images/Fader-dzjqv7lsdis.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r2
                r8
                f'4.
            }

        ..  figure:: ../_images/Fader-f33perr6lfo.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                ~
                c'16
                r8.
                r8
                f'4.
            }

        ..  figure:: ../_images/Fader-qxvj8lkfph.png

    Changing :attr:`mode` after initialisation:
        The property :attr:`mode` can also be changed after initialisation, as
        shown below.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/Fader-3ldjnprohuo.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                r4
                f'4
            }

        ..  figure:: ../_images/Fader-wzeaqjgouz8.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                r2
            }

        ..  figure:: ../_images/Fader-aqq1docvezb.png

        >>> fader.mode = 'in'
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                r4
            }

        ..  figure:: ../_images/Fader-3jt6kto85h1.png

        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/Fader-2i22so5t5pf.png

    :attr:`include_empty_measures`:
        When :attr:`mode` is set to ``'out'``, the process will end on an empty
        measure and when it is set to ``'in'``, it will start on an empty
        measure. Set :attr:`include_empty_measures` to ``False`` to exclude the
        empty measures (default is ``True``). This can be used in conjunction
        with  :attr:`process_on_first_call`.

        >>> container = abjad.Container(r"c'4 d'4 e'2")
        >>> fader = auxjad.Fader(container,
        ...                      mode='in',
        ...                      include_empty_measures=False,
        ...                      )
        >>> staff = abjad.Staff(fader.output_all())
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                r2.
                c'4
                d'4
                r2
                c'4
                d'4
                e'2
            }

        ..  figure:: ../_images/Fader-pg0bejhb7ke.png

        >>> container = abjad.Container(r"c'4 d'4 e'2")
        >>> fader = auxjad.Fader(container,
        ...                      mode='out',
        ...                      include_empty_measures=False,
        ...                      )
        >>> staff = abjad.Staff(fader.output_all())
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'2
                r4
                d'4
                e'2
                r2
                e'2
            }

        ..  figure:: ../_images/Fader-t1ir3ezcg6a.png

    Using as iterator:
        The instances of this class can also be used as an iterator, which can
        then be used in a for loop to run through the whole process. Note that
        unlike the methods :meth:`output_n` and :meth:`output_all`, time
        signatures are added to each window returned by the fader. Use the
        function |auxjad.mutate.remove_repeated_time_signatures()| to clean
        the output when using this class in this way.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> fader = auxjad.Fader(container)
        >>> staff = abjad.Staff()
        >>> for window in fader:
        ...     staff.append(window)
        >>> auxjad.mutate.remove_repeated_time_signatures(staff)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                f'4
                c'4
                r4
                e'4
                f'4
                c'4
                r4
                e'4
                r4
                c'4
                r2.
                R1
            }

        ..  figure:: ../_images/Fader-qyve2exm08p.png

    Arguments and properties:
        This class can take many optional keyword arguments during its
        creation, besides :attr:`mode`. By default, calling the object in fade
        out mode will return the original container, and calling it in fade in
        mode will return a container filled with rests; set
        :attr:`process_on_first_call` to ``True`` and the fade process will be
        applied on the very first call. :attr:`max_steps` sets the maximum
        number of note that can be faded in/out at each iteration, ranging
        between ``1`` and the input value (default is also ``1``).
        :attr:`repetition_chance` sets the chance of a window repeating itself,
        from ``0.0`` to ``1.0`` (default is ``0.0``, i.e. no repetitions).
        :attr:`disable_rewrite_meter` disables the
        |abjad.Meter.rewrite_meter()| mutation which is applied to the
        container after every call, and :attr:`omit_time_signatures` will
        remove all time signatures from the output (both are ``False`` by
        default). Any measure filled with rests will be rewritten using a
        multi-measure rest; set the :attr:`use_multimeasure_rests` to ``False``
        to disable this behaviour. It is possible to set an initial mask for
        the notes using :attr:`mask`, which should be a :obj:`list` of the same
        length as the number of notes in the input container. When :attr:`mode`
        is set to ``'out'``, the mask is initialised with ``1``'s, and when it
        is set to ``'in'``, it is initialised with ``0``'s. Change it to a mix
        of ``1``'s and ``0``'s to start the process with some specific notes
        already hidden or present. The properties :attr:`boundary_depth`,
        :attr:`maximum_dot_count`, and :attr:`rewrite_tuplets` are passed as
        arguments to |abjad.Meter.rewrite_meter()|, see its documentation for
        more information.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> fader = auxjad.Fader(container,
        ...                      mode='in',
        ...                      max_steps=2,
        ...                      repetition_chance=0.7,
        ...                      disable_rewrite_meter=True,
        ...                      omit_time_signatures=True,
        ...                      use_multimeasure_rests=False,
        ...                      mask=[1, 0, 1, 1, 0],
        ...                      boundary_depth=0,
        ...                      maximum_dot_count=1,
        ...                      rewrite_tuplets=False,
        ...                      process_on_first_call=True,
        ...                      include_empty_measures=False,
        ...                      )
        >>> fader.mode
        'in'
        >>> fader.max_steps
        2
        >>> fader.repetition_chance
        0.7
        >>> fader.disable_rewrite_meter
        True
        >>> fader.omit_time_signatures
        True
        >>> fader.use_multimeasure_rests
        False
        >>> fader.mask
        [1, 0, 1, 1, 0]
        >>> fader.boundary_depth
        0
        >>> fader.maximum_dot_count
        1
        >>> fader.rewrite_tuplets
        False
        >>> fader.process_on_first_call
        True
        >>> fader.include_empty_measures
        False

        Use the properties below to change these values after initialisation.

        >>> fader.mode = 'out'
        >>> fader.max_steps = 1
        >>> fader.repetition_chance = 0.23
        >>> fader.disable_rewrite_meter = False
        >>> fader.omit_time_signatures = False
        >>> fader.use_multimeasure_rests = True
        >>> fader.mask = [0, 1, 1, 0, 1]
        >>> fader.boundary_depth = 1
        >>> fader.maximum_dot_count = 2
        >>> fader.rewrite_tuplets = True
        >>> fader.process_on_first_call = False
        >>> fader.include_empty_measures = True
        >>> fader.mode
        'out'
        >>> fader.max_steps
        1
        >>> fader.repetition_chance
        0.23
        >>> fader.disable_rewrite_meter
        False
        >>> fader.omit_time_signatures
        False
        >>> fader.use_multimeasure_rests
        True
        >>> fader.mask
        [0, 1, 1, 0, 1]
        >>> fader.boundary_depth
        1
        >>> fader.maximum_dot_count
        2
        >>> fader.rewrite_tuplets
        True
        >>> fader.process_on_first_call
        False
        >>> fader.include_empty_measures
        True

    :attr:`contents`:
        Use the :attr:`contents` property to read as well as overwrite the
        contents of the fader. Notice that :attr:`mask` will also be reset at
        that point.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader()
        >>> fader.mask
        [1, 1, 1, 1]
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/Fader-nv5f76rv7f.png

        >>> notes = fader()
        >>> fader.mask
        [0, 1, 1, 1]
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/Fader-6fr4wrb8god.png

        >>> fader.contents = abjad.Container(r"c'16 d'16 e'16 f'16 g'2.")
        >>> fader.mask
        [1, 1, 1, 1, 1]
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'16
                d'16
                e'16
                f'16
                g'2.
            }

        ..  figure:: ../_images/Fader-p8q5x8ti2d.png

        >>> notes = fader()
        >>> fader.mask
        [1, 1, 1, 1, 1]
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'16
                d'16
                r16
                f'16
                g'2.
            }

        ..  figure:: ../_images/Fader-mky4pulzf4i.png

    :meth:`output_all`:
        To run through the whole process and output it as a single container,
        use the method :meth:`output_all`.

        >>> container = abjad.Container(r"c'4. d'8 e'2")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4.
                d'8
                e'2
                r4.
                d'8
                e'2
                r2
                e'2
                R1
            }

        ..  figure:: ../_images/Fader-kiqwdhyx9vk.png

    :meth:`output_n`:
        To run through just part of the process and output it as a single
        container, use the method :meth:`output_n` and pass the number of
        iterations as argument.

        >>> container = abjad.Container(r"c'4. d'8 e'16 f'16 g'4.")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4.
                d'8
                e'16
                f'16
                g'4.
                c'4.
                r8
                e'16
                f'16
                g'4.
                c'4.
                r8
                e'16
                f'16
                r4.
            }

        ..  figure:: ../_images/Fader-6mqxj9b5f13.png

    Chords:
        This class also support chords. Each of their individual notes are
        removed or added one by one.

        >>> container = abjad.Container(
        ...     r"<c' e'>4 ~ <c' e'>16 d'8. <gs e'>8 <bf f' a'>8 ~ <bf f' a'>4"
        ... )
        >>> fader = auxjad.Fader(container)
        >>> staff = abjad.Staff(fader.output_all())
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                <c' e'>4
                ~
                <c' e'>16
                d'8.
                <gs e'>8
                <bf f' a'>4.
                <c' e'>4
                ~
                <c' e'>16
                d'8.
                gs8
                <bf f' a'>4.
                <c' e'>4
                ~
                <c' e'>16
                d'8.
                gs8
                <bf a'>4.
                c'4
                ~
                c'16
                d'8.
                gs8
                <bf a'>4.
                r4
                r16
                d'8.
                gs8
                <bf a'>4.
                r4
                r16
                d'8.
                gs8
                bf4.
                r2
                gs8
                bf4.
                r2
                r8
                bf4.
                R1
            }

        ..  figure:: ../_images/Fader-wjphyrz750d.png

    :func:`len()`:
        The function :func:`len()` returns the total number of notes in
        :attr:`contents`.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        4
        >>> container = abjad.Container(r"c'4 ~ c'8 d'8 e'4 ~ e'8 f'8")
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        4
        >>> container = abjad.Container(
        ...     r"c'4 ~ c'16 r16 d'8 e'4 ~ e'8 f'16 r16"
        ... )
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        4

        Note that each individual note in a chord will count as one note.

        >>> container = abjad.Container(r"<c' e' g'>2 <d' f'>2")
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        5
        >>> container = abjad.Container(
        ...     r"<c' e' g'>4 ~ <c' e' g'>16 r8. <d' f'>2"
        ... )
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        5
        >>> container = abjad.Container(r"<c' e' g'>4 d'4 <e' g' b'>4 r4")
        >>> fader = auxjad.Fader(container)
        >>> len(fader)
        7

    :attr:`max_steps`:
        Setting the keyword argument :attr:`max_steps` to a value larger than
        ``1`` will result in a random number of steps (between ``1`` and
        :attr:`max_steps`) being applied at each call.

        >>> container = abjad.Container(r"c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8")
        >>> fader = auxjad.Fader(container,
        ...                      max_steps=3,
        ...                      process_on_first_call=True,
        ...                      )
        >>> notes = fader.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8
                d'8
                r8
                f'8
                g'8
                a'8
                b'8
                c''8
                r4.
                f'8
                g'8
                a'8
                b'8
                c''8
                r4.
                f'8
                r8
                a'8
                b'8
                r8
            }

        ..  figure:: ../_images/Fader-72wpb0iqtes.png

    :attr:`repetition_chance`:
        Use :attr:`repetition_chance` to set the chance of a measure repeating
        itself, ranging from ``0.0`` to ``1.0`` (default is ``0.0``,
        i.e. no repetitions).

        >>> container = abjad.Container(r"c'4. d'8 e'4.. f'16")
        >>> fader = auxjad.Fader(container,
        ...                      repetition_chance=0.5,
        ...                      )
        >>> notes = fader.output_n(5)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4.
                d'8
                e'4..
                f'16
                c'4.
                d'8
                e'4..
                r16
                c'4.
                d'8
                e'4..
                r16
                c'4.
                d'8
                r2
                c'4.
                d'8
                r2
            }

        ..  figure:: ../_images/Fader-1t2yh8imiu8.png

    :attr:`mask` and :meth:`reset`:
        The property :attr:`mask` is used to represent whether each note is
        hidden or present. It is a :obj:`list` of the same length as the number
        of notes in the input container (use the :func:`len()` function to read
        that value). When :attr:`mode` is set to ``'out'``, the mask is
        initialised with ``1``'s, and when it is set to ``'in'``, it is
        initialised with ``0``'s. Change it to a mix of ``1``'s and ``0``'s to
        start the process with some notes already hidden or present. Use the
        method :meth:`reset` to reset it back to its default value (depending
        on :attr:`mode`).

        >>> container = abjad.Container(r"c'4 d'8 e'8 f'4 ~ f'8. g'16")
        >>> fader = auxjad.Fader(container)
        >>> fader.mask
        [1, 1, 1, 1, 1]
        >>> fader = auxjad.Fader(container,
        ...                      mode='in',
        ...                      )
        >>> fader.mask
        [0, 0, 0, 0, 0]
        >>> for _ in range(3):
        ...     fader()
        ...     fader.mask
        [0, 0, 0, 0, 0]
        [0, 1, 0, 0, 0]
        [0, 1, 1, 0, 0]
        >>> staff = abjad.Staff(fader.current_window)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r4
                d'8
                e'8
                r2
            }

        ..  figure:: ../_images/Fader-52bwgmr7rks.png

        >>> fader.mask = [1, 0, 1, 1, 0]
        >>> fader.mask
        [1, 0, 1, 1, 0]
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                r8
                e'8
                f'4..
                r16
            }

        ..  figure:: ../_images/Fader-fa199pggrp.png

        >>> fader.reset()
        >>> fader.mask
        [0, 0, 0, 0, 0]
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                R1
            }

        ..  figure:: ../_images/Fader-xq3g5bd8djr.png

    .. note::

        When a container has chords, each of their notes will be represented by
        an index in the mask, from the lowest pitched one to the highest
        pitched one. For instance, the container ``c'2 <d' e' f' g'>2`` has
        five notes in total (a single note plus a 4-note chord), and applying
        the mask ``[1, 0, 1, 1, 0]`` to it will result in the first, third, and
        fourth notes to be shown, in this case ``c'2 <e' f'>2``.

        >>> container = abjad.Container(r"c'2 <d' e' f' g'>2")
        >>> fader = auxjad.Fader(container, mask=[1, 0, 1, 1, 0])
        >>> staff = abjad.Staff(fader())
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'2
                <e' f'>2
            }

        ..  figure:: ../_images/Fader-5n18nhr5qq2.png

    :meth:`random_mask`:
        The mask can also be randomised at any point using the method
        :meth:`random_mask`.

        >>> container = abjad.Container(r"c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8")
        >>> fader = auxjad.Fader(container)
        >>> fader.random_mask()
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r8
                d'8
                r4
                g'8
                a'8
                r4
            }

        ..  figure:: ../_images/Fader-r9z77w5l6vp.png

        >>> fader.random_mask()
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r8
                d'8
                r4
                g'8
                a'8
                b'8
                r8
            }

        ..  figure:: ../_images/Fader-t7p8rjahkdh.png

    :meth:`shuffle_mask`:
        Use :meth:`shuffle_mask` to shuffle the current mask. This method will
        shuffle the mask while keeping the total number of shown notes constant
        (that is, it will shuffle the contents of the mask while keeping the
        total number of ``1``'s and ``0``'s).

        >>> container = abjad.Container(r"c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8")
        >>> fader = auxjad.Fader(container,
        ...                      mask=[0, 0, 1, 1, 1, 1, 1, 1],
        ...                      )
        >>> fader.shuffle_mask()
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                r8
                d'8
                e'8
                f'8
                g'8
                a'8
                b'8
                r8
            }

        ..  figure:: ../_images/Fader-6bghb0g2wcx.png

        >>> fader.shuffle_mask()
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8
                d'8
                e'8
                r8
                g'8
                r8
                b'8
                c''8
            }

        ..  figure:: ../_images/Fader-lvf1fqvizjn.png

    :attr:`use_multimeasure_rests` and :attr:`disable_rewrite_meter`:
        By default, all rests in a measure filled only with rests will be
        converted into a multi-measure rest. Set :attr:`use_multimeasure_rests`
        to ``False`` to disable this. Also, by default, all output is mutated
        through |abjad.Meter.rewrite_meter()|. To disable it, set
        :attr:`disable_rewrite_meter` to ``True``.

        >>> container = abjad.Container(r"c'8 d'8 e'2.")
        >>> fader = auxjad.Fader(container,
        ...                      disable_rewrite_meter=True,
        ...                      use_multimeasure_rests=False,
        ...                      )
        >>> notes = fader.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8
                d'8
                e'2.
                c'8
                r8
                e'2.
                r8
                r8
                e'2.
                r8
                r8
                r2.
            }

        ..  figure:: ../_images/Fader-lax06gkb3ap.png

    :attr:`omit_time_signatures`:
        To disable time signatures altogether, initialise this class with the
        keyword argument :attr:`omit_time_signatures` set to ``True`` (default
        is ``False``), or use the :attr:`omit_time_signatures` property after
        initialisation.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> fader = auxjad.Fader(container,
        ...                      omit_time_signatures=True,
        ...                      )
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                d'2
                e'4
            }

        ..  figure:: ../_images/Fader-pcq5ecwz7ff.png

    ..  tip::

        All methods that return an |abjad.Selection| will add an initial time
        signature to it. The :meth:`output_n` and :meth:`output_all` methods
        automatically remove repeated time signatures. When joining selections
        output by multiple method calls, use
        |auxjad.mutate.remove_repeated_time_signatures()| on the whole
        container after fusing the selections to remove any unecessary time
        signature changes.

    Tweaking |abjad.Meter.rewrite_meter()|:
        This function uses the default logical tie splitting algorithm from
        |abjad.Meter.rewrite_meter()|.

        >>> container = abjad.Container(r"c'4. d'8 e'2")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

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

        ..  figure:: ../_images/Fader-93hcv2prkua.png

        Set :attr:`boundary_depth` to a different number to change its
        behaviour.

        >>> fader = auxjad.Fader(container,
        ...                      boundary_depth=1,
        ...                      )
        >>> notes = fader()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                ~
                c'8
                d'8
                e'2
            }

        ..  figure:: ../_images/Fader-cq661zyctf.png

        Other arguments available for tweaking the output of
        |abjad.Meter.rewrite_meter()| are :attr:`maximum_dot_count` and
        :attr:`rewrite_tuplets`, which work exactly as the identically named
        arguments of |abjad.Meter.rewrite_meter()|.

        This class also accepts the arguments ``fuse_across_groups_of_beats``,
        ``fuse_quadruple_meter``, ``fuse_triple_meter``, and
        ``extract_trivial_tuplets``, which are passed on to
        |auxjad.mutate.prettify_rewrite_meter()| (the latter can be disabled
        by setting ``prettify_rewrite_meter`` to ``False``). See the
        documentation of this function for more details on these arguments.

    Indicators:
        This class can handle dynamics and articulations.

        >>> container = abjad.Container(
        ...     r"\time 3/4 c'8->\f d'8\p ~ d'4 e'8..-- f'32-."
        ... )
        >>> fader = auxjad.Fader(container)
        >>> notes = fader.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'8
                \f
                - \accent
                d'4.
                \p
                e'8..
                - \tenuto
                f'32
                - \staccato
                c'8
                \f
                - \accent
                d'4.
                \p
                r8..
                f'32
                - \staccato
                c'8
                \f
                - \accent
                d'4.
                \p
                r4
                c'8
                \f
                - \accent
                r8
                r2
                R1 * 3/4
            }

        ..  figure:: ../_images/Fader-ox08wd3ljps.png

    Slurs and hairpins:
        Slurs and hairpins are also supported. Slurs are split when rests
        appear in the middle of a slurred phrase, while hairpins are shortened
        and adjusted as required.

        >>> container = abjad.Container(
        ...     r"\times 2/3 {c'2(\p\< d'2 e'2\f} f'4\p\> g'2 a'4\pp)"
        ... )
        >>> fader = auxjad.Fader(container)
        >>> notes = fader.output_n(5)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \times 2/3
                {
                    \time 4/4
                    c'2
                    \p
                    \<
                    (
                    d'2
                    e'2
                    \f
                }
                f'4
                \p
                \>
                g'2
                a'4
                \pp
                )
                \times 2/3
                {
                    c'2
                    \p
                    \<
                    (
                    d'2
                    e'2
                    \f
                    )
                }
                r4
                g'2
                \p
                \>
                (
                a'4
                \pp
                )
                \times 2/3
                {
                    r2
                    d'2
                    \p
                    \<
                    (
                    e'2
                    \f
                    )
                }
                r4
                g'2
                \p
                \>
                (
                a'4
                \pp
                )
                \times 2/3
                {
                    r2
                    d'2
                    \p
                    \<
                    r2
                    \f
                }
                r4
                g'2
                \p
                \>
                (
                a'4
                \pp
                )
                R1
                r4
                g'2
                \p
                \>
                (
                a'4
                \pp
                )
            }

        ..  figure:: ../_images/Fader-jprjps4zxej.png

    ..  tip::

        The functions |auxjad.mutate.remove_repeated_dynamics()| and
        |auxjad.mutate.reposition_clefs()| can be used to clean the output
        and remove repeated dynamics and unnecessary clef changes.

    ..  warning::

        Do note that some elements that span multiple notes (such as ottava
        indicators, manual beams, etc.) can become problematic when notes
        containing them are split into two. As a rule of thumb, it is always
        better to attach those to the music after the fading process has ended.

    Tuplets:
        This class can handle tuplets.

        >>> container = abjad.Container(r"\times 2/3 {c'8 d'8 e'8} d'2.")
        >>> fader = auxjad.Fader(container)
        >>> notes = fader.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \times 2/3
                {
                    \time 4/4
                    c'8
                    d'8
                    e'8
                }
                d'2.
                \times 2/3
                {
                    r8
                    d'8
                    e'8
                }
                d'2.
                \times 2/3
                {
                    r8
                    d'8
                    r8
                }
                d'2.
                r4
                d'2.
                R1
            }

        ..  figure:: ../_images/Fader-888tqk73kw3.png

    Time signature changes:
        This class can handle time signature changes.

        >>> container = abjad.Container(r"\time 4/4 c'2( d'2 \time 3/4 e'2.)")
        >>> fader = auxjad.Fader(container, mode='in')
        >>> notes = fader.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                R1
                \time 3/4
                R1 * 3/4
                \time 4/4
                c'2
                r2
                )
                \time 3/4
                R1 * 3/4
                \time 4/4
                c'2
                (
                d'2
                )
                \time 3/4
                R1 * 3/4
                \time 4/4
                c'2
                (
                d'2
                \time 3/4
                e'2.
                )
            }

        ..  figure:: ../_images/Fader-lkhKFVuUgx.png
    """

    ### CLASS VARIABLES ###

    __slots__ = (
        "_contents",
        "_current_window",
        "_mode",
        "_max_steps",
        "_disable_rewrite_meter",
        "_mask",
        "_is_first_window",
        "_time_signatures",
        "_omit_time_signatures",
        "_use_multimeasure_rests",
        "_boundary_depth",
        "_maximum_dot_count",
        "_rewrite_tuplets",
        "_process_on_first_call",
        "_include_empty_measures",
        "_repetition_chance",
        "_prettify_rewrite_meter",
        "_extract_trivial_tuplets",
        "_fuse_across_groups_of_beats",
        "_fuse_quadruple_meter",
        "_fuse_triple_meter",
    )

    ### INITIALISER ###

    def __init__(
        self,
        contents: abjad.Container,
        *,
        mode: str = "out",
        max_steps: int = 1,
        repetition_chance: float = 0.0,
        process_on_first_call: bool = False,
        disable_rewrite_meter: bool = False,
        omit_time_signatures: bool = False,
        use_multimeasure_rests: bool = True,
        mask: Optional[list] = None,
        boundary_depth: Optional[int] = None,
        maximum_dot_count: Optional[int] = None,
        rewrite_tuplets: bool = True,
        include_empty_measures: bool = True,
        prettify_rewrite_meter: bool = True,
        extract_trivial_tuplets: bool = True,
        fuse_across_groups_of_beats: bool = True,
        fuse_quadruple_meter: bool = True,
        fuse_triple_meter: bool = True,
    ) -> None:
        self.mode = mode
        self.max_steps = max_steps
        self.contents = contents
        self.disable_rewrite_meter = disable_rewrite_meter
        self.omit_time_signatures = omit_time_signatures
        self.use_multimeasure_rests = use_multimeasure_rests
        if mask:
            self.mask = mask
        self.boundary_depth = boundary_depth
        self.maximum_dot_count = maximum_dot_count
        self.rewrite_tuplets = rewrite_tuplets
        self.prettify_rewrite_meter = prettify_rewrite_meter
        self.extract_trivial_tuplets = extract_trivial_tuplets
        self.fuse_across_groups_of_beats = fuse_across_groups_of_beats
        self.fuse_quadruple_meter = fuse_quadruple_meter
        self.fuse_triple_meter = fuse_triple_meter
        self.process_on_first_call = process_on_first_call
        self.include_empty_measures = include_empty_measures
        self.repetition_chance = repetition_chance
        self._is_first_window = True

    ### SPECIAL METHODS ###

    def __repr__(self) -> str:
        r"""Returns interpreter representation of  :attr:`contents`."""
        return abjad.lilypond(self._contents)

    def __len__(self) -> int:
        r"""Returns the number of notes of :attr:`contents`."""
        length = 0
        logical_ties = abjad.select(self._contents).logical_ties(pitched=True)
        for logical_tie in logical_ties:
            if isinstance(logical_tie.head, ArtificialHarmonic):
                length += 1
            elif isinstance(logical_tie.head, abjad.Chord):
                length += len(logical_tie.head.written_pitches)
            else:
                length += 1
        return length

    def __call__(self) -> abjad.Selection:
        r"""Calls the fading process for one iteration, returning an
        |abjad.Selection|.
        """
        if self._repetition_chance == 0.0 or random.random() > self._repetition_chance:
            if not self._is_first_window or self._process_on_first_call:
                if self._mode == "out":
                    self._remove_element()
                else:
                    self._add_element()
            elif not self._include_empty_measures and self._mode == "in":
                self._add_element()
        self._mask_to_selection()
        return self.current_window

    def __next__(self) -> abjad.Selection:
        r"""Calls the fading process for one iteration, returning an
        |abjad.Selection|.
        """
        if self._done:
            raise StopIteration
        return self.__call__()

    def __iter__(self) -> None:
        r"""Returns an iterator, allowing instances to be used as iterators."""
        return self

    ### PUBLIC METHODS ###

    def output_all(self) -> abjad.Selection:
        r"""Goes through the whole fading process and outputs a single
        |abjad.Selection|.
        """
        dummy_container = abjad.Container()
        while True:
            dummy_container.append(self.__call__())
            if self._done:
                break
        mutate.remove_repeated_time_signatures(dummy_container[:])
        mutate.remove_repeated_dynamics(dummy_container[:])
        output = dummy_container[:]
        dummy_container[:] = []
        return output

    def output_n(
        self,
        n: int,
    ) -> abjad.Selection:
        r"""Goes through ``n`` iterations of the fading process and outputs a
        single |abjad.Selection|.
        """
        if not isinstance(n, int):
            raise TypeError("first positional argument must be 'int'")
        if n < 1:
            raise ValueError("first positional argument must be a positive 'int'")
        dummy_container = abjad.Container()
        for _ in range(n):
            dummy_container.append(self.__call__())
        mutate.remove_repeated_time_signatures(dummy_container[:])
        mutate.remove_repeated_dynamics(dummy_container[:])
        output = dummy_container[:]
        dummy_container[:] = []
        return output

    def reset(self) -> None:
        r"""Resets mask, filling it with a default value depending on
        :attr:`mode`.
        """
        self._is_first_window = True
        if self._mode == "out":
            self._mask = [1 for _ in range(self.__len__())]
        else:
            self._mask = [0 for _ in range(self.__len__())]

    def random_mask(self) -> None:
        r"""Creates a mask randomly filled with ``1``'s and ``0``'s."""
        self._is_first_window = True
        self._mask = [random.randint(0, 1) for _ in range(self.__len__())]

    def shuffle_mask(self) -> None:
        r"""Shuffles the current mask."""
        self._is_first_window = True
        random.shuffle(self._mask)

    ### PRIVATE METHODS ###

    def _remove_element(self) -> None:
        r"""Sets a random element of the mask to ``0``."""
        for n in range(random.randint(1, self._max_steps)):
            if 1 in self._mask:
                total_count = sum(self._mask)
                random_count = random.randint(0, total_count - 1)
                index = self._get_index_of_nth_occurrence(
                    self._mask,
                    element=1,
                    count=random_count,
                )
                self._mask[index] = 0
            elif n == 0:
                raise RuntimeError("'current_window' is already empty")

    def _add_element(self) -> None:
        r"""Sets a random element of the mask to ``1``."""
        for n in range(random.randint(1, self._max_steps)):
            if 0 in self._mask:
                total_count = self.__len__() - sum(self._mask)
                random_count = random.randint(0, total_count - 1)
                index = self._get_index_of_nth_occurrence(
                    self._mask,
                    element=0,
                    count=random_count,
                )
                self._mask[index] = 1
            elif n == 0:
                raise RuntimeError("'current_window' is already full")

    def _mask_to_selection(self) -> None:
        r"""Applies the mask to :attr:`contents`."""
        dummy_container = abjad.mutate.copy(self._contents)
        logical_ties = abjad.select(dummy_container).logical_ties(pitched=True)
        mask_index = 0
        for logical_tie in logical_ties:
            if isinstance(logical_tie.head, abjad.Chord) and not isinstance(
                logical_tie.head, ArtificialHarmonic
            ):
                chord_len = len(logical_tie.head.written_pitches)
                if 1 not in self._mask[mask_index : mask_index + chord_len]:
                    self._convert_pitched_logical_tie_to_rest(logical_tie)
                    mask_index += chord_len
                else:
                    new_written_pitches = []
                    for written_pitch in logical_tie.head.written_pitches:
                        if self._mask[mask_index] != 0:
                            new_written_pitches.append(written_pitch)
                        mask_index += 1
                    if len(new_written_pitches) > 1:
                        for leaf in logical_tie:
                            leaf.written_pitches = new_written_pitches
                    else:
                        for leaf in logical_tie:
                            note = abjad.Note(
                                new_written_pitches[0],
                                leaf.written_duration,
                            )
                            for indicator in abjad.get.indicators(leaf):
                                abjad.attach(indicator, note)
                            abjad.mutate.replace(leaf, note)
            elif self._mask[mask_index] == 0:
                self._convert_pitched_logical_tie_to_rest(logical_tie)
                mask_index += 1
            else:
                mask_index += 1
        # handling dynamics and slurs and empty tuplets
        mutate.reposition_dynamics(dummy_container[:])
        mutate.reposition_slurs(dummy_container[:])
        mutate.extract_trivial_tuplets(dummy_container[:])
        # applying rewrite meter
        if not self._disable_rewrite_meter:
            mutate.auto_rewrite_meter(
                dummy_container,
                boundary_depth=self._boundary_depth,
                maximum_dot_count=self._maximum_dot_count,
                rewrite_tuplets=self._rewrite_tuplets,
                prettify_rewrite_meter=self._prettify_rewrite_meter,
                extract_trivial_tuplets=self._extract_trivial_tuplets,
                fuse_across_groups_of_beats=self._fuse_across_groups_of_beats,
                fuse_quadruple_meter=self._fuse_quadruple_meter,
                fuse_triple_meter=self._fuse_triple_meter,
            )
        if self._use_multimeasure_rests:
            mutate.rests_to_multimeasure_rest(dummy_container[:])
        # output
        self._current_window = dummy_container[:]
        dummy_container[:] = []
        self._is_first_window = False

    def _get_lilypond_format(self) -> str:
        r"""Returns interpreter representation of  :attr:`contents`."""
        return self.__repr__()

    @staticmethod
    def _convert_pitched_logical_tie_to_rest(
        logical_tie: abjad.LogicalTie,
    ) -> None:
        r"""Converts all leaves of a pitched logical tie into rests."""
        indicators_tuple = (
            abjad.BarLine,
            abjad.Clef,
            abjad.Dynamic,
            abjad.Fermata,
            abjad.KeySignature,
            abjad.LilyPondLiteral,
            abjad.MetronomeMark,
            abjad.Ottava,
            abjad.RehearsalMark,
            abjad.Repeat,
            abjad.StaffChange,
            abjad.StartHairpin,
            abjad.StartMarkup,
            abjad.StartPhrasingSlur,
            abjad.StartSlur,
            abjad.StartTextSpan,
            abjad.StopHairpin,
            abjad.StopPhrasingSlur,
            abjad.StopSlur,
            abjad.StopTextSpan,
            abjad.TimeSignature,
        )
        for leaf in logical_tie:
            rest = abjad.Rest(leaf.written_duration)
            for indicator in abjad.get.indicators(leaf):
                if isinstance(indicator, indicators_tuple):
                    abjad.attach(indicator, rest)
            abjad.mutate.replace(leaf, rest)

    @staticmethod
    def _remove_all_time_signatures(
        container: abjad.Container,
    ) -> None:
        r"""Removes all time signatures of an |abjad.Container|."""
        for leaf in abjad.select(container).leaves():
            if abjad.get.effective(leaf, abjad.TimeSignature):
                abjad.detach(abjad.TimeSignature, leaf)

    @staticmethod
    def _get_index_of_nth_occurrence(
        input_list: list[Any],
        *,
        element: Any,
        count: int,
    ) -> int:
        r"""Returns the index of the nth occurence of an element in a
        :obj:`list`.
        """
        return tuple(index for index, item in enumerate(input_list) if item == element)[count]

    ### PUBLIC PROPERTIES ###

    @property
    def contents(self) -> abjad.Container:
        r"""The |abjad.Container| to be faded."""
        return abjad.mutate.copy(self._contents)

    @contents.setter
    def contents(
        self,
        contents: abjad.Container,
    ) -> None:
        if not isinstance(contents, abjad.Container):
            raise TypeError("'contents' must be 'abjad.Container' or child class")
        if not abjad.select(contents).leaves().are_contiguous_logical_voice():
            raise ValueError("'contents' must be contiguous logical voice")
        if isinstance(contents, abjad.Score):
            self._contents = abjad.mutate.copy(contents[0])
        elif isinstance(contents, abjad.Tuplet):
            self._contents = abjad.Container([abjad.mutate.copy(contents)])
        else:
            self._contents = abjad.mutate.copy(contents)
        time_signatures = get.time_signature_list(
            self._contents,
            do_not_use_none=True,
        )
        mutate.enforce_time_signature(self._contents, time_signatures)
        dummy_container = abjad.mutate.copy(contents)
        self._current_window = dummy_container[:]
        dummy_container[:] = []
        self.reset()
        self._is_first_window = True

    @property
    def current_window(self) -> abjad.Selection:
        r"""Read-only property, returns the previously output selection."""
        current_window = abjad.mutate.copy(self._current_window)
        if self._omit_time_signatures:
            self._remove_all_time_signatures(current_window)
        return current_window

    @property
    def mode(self) -> str:
        r"""Mode of fading, must be either ``'in'`` or ``'out'``."""
        return self._mode

    @mode.setter
    def mode(
        self,
        mode: str,
    ) -> None:
        if not isinstance(mode, str):
            raise TypeError("'mode' must be 'str'")
        if mode not in ("in", "out"):
            raise ValueError("'mode' must be either 'in' or 'out'")
        self._mode = mode

    @property
    def max_steps(self) -> int:
        r"""The maximum number of steps per operation."""
        return self._max_steps

    @max_steps.setter
    def max_steps(
        self,
        max_steps: int,
    ) -> None:
        if not isinstance(max_steps, int):
            raise TypeError("'max_steps' must be 'int'")
        if max_steps < 1:
            raise ValueError("'max_steps' must be greater than zero")
        self._max_steps = max_steps

    @property
    def mask(self) -> list[int]:
        r"""Mask with ``1``'s and ``0``'s representing the notes of
        :attr:`contents`.
        """
        return self._mask

    @mask.setter
    def mask(
        self,
        mask: list[int],
    ) -> None:
        if not isinstance(mask, list):
            raise TypeError("'mask' must be 'list'")
        if any(element not in (0, 1) for element in mask):
            raise ValueError("'mask' must contain only 1's and 0's")
        if len(mask) != self.__len__():
            raise ValueError(
                "'mask' must have the same length as the number of logical ties in 'contents'"
            )
        self._mask = mask
        self._is_first_window = True

    @property
    def disable_rewrite_meter(self) -> bool:
        r"""When ``True``, the durations of the notes in the output will not be
        rewritten by the |abjad.Meter.rewrite_meter()| mutation.
        """
        return self._disable_rewrite_meter

    @disable_rewrite_meter.setter
    def disable_rewrite_meter(
        self,
        disable_rewrite_meter: bool,
    ) -> None:
        if not isinstance(disable_rewrite_meter, bool):
            raise TypeError("'disable_rewrite_meter' must be 'bool'")
        self._disable_rewrite_meter = disable_rewrite_meter

    @property
    def omit_time_signatures(self) -> bool:
        r"""When ``True``, all time signatures will be omitted from the
        output.
        """
        return self._omit_time_signatures

    @omit_time_signatures.setter
    def omit_time_signatures(
        self,
        omit_time_signatures: bool,
    ) -> None:
        if not isinstance(omit_time_signatures, bool):
            raise TypeError("'omit_time_signatures' must be 'bool'")
        self._omit_time_signatures = omit_time_signatures

    @property
    def use_multimeasure_rests(self) -> bool:
        r"""When ``True``, multi-measure rests will be used for silent
        measures.
        """
        return self._use_multimeasure_rests

    @use_multimeasure_rests.setter
    def use_multimeasure_rests(
        self,
        use_multimeasure_rests: bool,
    ) -> None:
        if not isinstance(use_multimeasure_rests, bool):
            raise TypeError("'use_multimeasure_rests' must be 'bool'")
        self._use_multimeasure_rests = use_multimeasure_rests

    @property
    def boundary_depth(self) -> Union[int, None]:
        r"""Sets the argument ``boundary_depth`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._boundary_depth

    @boundary_depth.setter
    def boundary_depth(
        self,
        boundary_depth: Optional[int],
    ) -> None:
        if boundary_depth is not None:
            if not isinstance(boundary_depth, int):
                raise TypeError("'boundary_depth' must be 'int'")
        self._boundary_depth = boundary_depth

    @property
    def maximum_dot_count(self) -> Union[int, None]:
        r"""Sets the argument ``maximum_dot_count`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._maximum_dot_count

    @maximum_dot_count.setter
    def maximum_dot_count(
        self,
        maximum_dot_count: Optional[int],
    ) -> None:
        if maximum_dot_count is not None:
            if not isinstance(maximum_dot_count, int):
                raise TypeError("'maximum_dot_count' must be 'int'")
        self._maximum_dot_count = maximum_dot_count

    @property
    def rewrite_tuplets(self) -> bool:
        r"""Sets the argument ``rewrite_tuplets`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._rewrite_tuplets

    @rewrite_tuplets.setter
    def rewrite_tuplets(
        self,
        rewrite_tuplets: bool,
    ) -> None:
        if not isinstance(rewrite_tuplets, bool):
            raise TypeError("'rewrite_tuplets' must be 'bool'")
        self._rewrite_tuplets = rewrite_tuplets

    @property
    def prettify_rewrite_meter(self) -> bool:
        r"""Used to enable or disable the mutation
        |auxjad.mutate.prettify_rewrite_meter()| (default ``True``).
        """
        return self._prettify_rewrite_meter

    @prettify_rewrite_meter.setter
    def prettify_rewrite_meter(
        self,
        prettify_rewrite_meter: bool,
    ) -> None:
        if not isinstance(prettify_rewrite_meter, bool):
            raise TypeError("'prettify_rewrite_meter' must be 'bool'")
        self._prettify_rewrite_meter = prettify_rewrite_meter

    @property
    def extract_trivial_tuplets(self) -> bool:
        r"""Sets the argument ``extract_trivial_tuplets`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._extract_trivial_tuplets

    @extract_trivial_tuplets.setter
    def extract_trivial_tuplets(
        self,
        extract_trivial_tuplets: bool,
    ) -> None:
        if not isinstance(extract_trivial_tuplets, bool):
            raise TypeError("'extract_trivial_tuplets' must be 'bool'")
        self._extract_trivial_tuplets = extract_trivial_tuplets

    @property
    def fuse_across_groups_of_beats(self) -> bool:
        r"""Sets the argument ``fuse_across_groups_of_beats`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_across_groups_of_beats

    @fuse_across_groups_of_beats.setter
    def fuse_across_groups_of_beats(
        self,
        fuse_across_groups_of_beats: bool,
    ) -> None:
        if not isinstance(fuse_across_groups_of_beats, bool):
            raise TypeError("'fuse_across_groups_of_beats' must be 'bool'")
        self._fuse_across_groups_of_beats = fuse_across_groups_of_beats

    @property
    def fuse_quadruple_meter(self) -> bool:
        r"""Sets the argument ``fuse_quadruple_meter`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_quadruple_meter

    @fuse_quadruple_meter.setter
    def fuse_quadruple_meter(
        self,
        fuse_quadruple_meter: bool,
    ) -> None:
        if not isinstance(fuse_quadruple_meter, bool):
            raise TypeError("'fuse_quadruple_meter' must be 'bool'")
        self._fuse_quadruple_meter = fuse_quadruple_meter

    @property
    def fuse_triple_meter(self) -> bool:
        r"""Sets the argument ``fuse_triple_meter`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_triple_meter

    @fuse_triple_meter.setter
    def fuse_triple_meter(
        self,
        fuse_triple_meter: bool,
    ) -> None:
        if not isinstance(fuse_triple_meter, bool):
            raise TypeError("'fuse_triple_meter' must be 'bool'")
        self._fuse_triple_meter = fuse_triple_meter

    @property
    def process_on_first_call(self) -> bool:
        r"""If ``True`` then :attr:`contents` will be processed in the very
        first call.
        """
        return self._process_on_first_call

    @process_on_first_call.setter
    def process_on_first_call(
        self,
        process_on_first_call: bool,
    ) -> None:
        if not isinstance(process_on_first_call, bool):
            raise TypeError("'process_on_first_call' must be 'bool'")
        self._process_on_first_call = process_on_first_call

    @property
    def include_empty_measures(self) -> bool:
        r"""If ``True`` then an initial or final empty measures will be used,
        otherwise the process starts/ends with a single logical tie.
        """
        return self._include_empty_measures

    @include_empty_measures.setter
    def include_empty_measures(
        self,
        include_empty_measures: bool,
    ) -> None:
        if not isinstance(include_empty_measures, bool):
            raise TypeError("'include_empty_measures' must be 'bool'")
        self._include_empty_measures = include_empty_measures

    @property
    def repetition_chance(self) -> float:
        r"""The chance of not processing :attr:`contents` on a call, thus
        repeating the previous output.
        """
        return self._repetition_chance

    @repetition_chance.setter
    def repetition_chance(
        self,
        repetition_chance: float,
    ) -> None:
        if not isinstance(repetition_chance, float):
            raise TypeError("'repetition_chance' must be 'float'")
        if repetition_chance < 0.0 or repetition_chance > 1.0:
            raise ValueError("'repetition_chance' must be between 0.0 and 1.0")
        self._repetition_chance = repetition_chance

    ### PRIVATE PROPERTIES ###

    @property
    def _done(self) -> bool:
        r""":obj:`bool` indicating whether the process is done, which is when
        the mask is filled with ``1``'s with :attr:`mode` set to ``'in'`` or
        when the mask is filled with ``0``'s with :attr:`mode` set to
        ``'out'``.
        """
        if self._mode == "out":
            if self._include_empty_measures:
                return 1 not in self._mask
            else:
                return sum(self._mask) <= 1
        else:
            return 0 not in self._mask
