Source code for auxjad.mutate.extend_notes

import abjad

from .. import get, mutate


def _groupped_logical_ties(container) -> list:
    r'Selects logical ties with all consecutive rests.'
    groupped_logical_ties = []
    selection = abjad.Selection()
    for logical_tie in abjad.select(container).logical_ties():
        if not isinstance(logical_tie.head, (abjad.Rest,
                                             abjad.MultimeasureRest,
                                             )):
            if len(selection) > 0:
                groupped_logical_ties.append(selection)
            selection = abjad.Selection(logical_tie)
        else:
            selection += abjad.Selection(logical_tie)
    if len(selection) > 0:
        groupped_logical_ties.append(selection)
    return groupped_logical_ties


[docs]def extend_notes(container: abjad.Container, max_note_duration: abjad.Duration, *, gap: abjad.Duration = abjad.Duration((0, 1)), gap_before_end: bool = False, use_multimeasure_rests: bool = True, rewrite_meter: bool = True, ) -> None: r"""Mutates an input |abjad.Container| (or child class) in place and has no return value; this function extends all logical ties (notes and chords) up to a given maximum note duration. Basic usage: This function will extend the duration of each logical tie up until the allowed maximum duration. >>> staff = abjad.Staff(r"c'16 r2... d'8 r2.. e'8. r16 r2. f'4 r2.") >>> abjad.show(staff) .. docs:: \new Staff { c'16 r2... d'8 r2.. e'8. r16 r2. f'4 r2. } .. figure:: ../_images/extend_notes-gJGtjtm3fu.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((1, 4))) >>> abjad.show(staff) .. docs:: \new Staff { c'4 r2. d'4 r2. e'4 r2. f'4 r2. } .. figure:: ../_images/extend_notes-6pjHLREOap.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.extend_notes(staff) >>> abjad.mutate.extend_notes(staff) Longer notes: This function will not alter the length of logical ties that are already equal to or larger than the maximum extension duration. >>> staff = abjad.Staff(r"c'16 r2... d'2 r2 e'2. r4 f'1") >>> abjad.show(staff) .. docs:: \new Staff { c'16 r2... d'2 r2 e'2. r4 f'1 } .. figure:: ../_images/extend_notes-56rOAsxHPP.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((1, 4))) >>> abjad.show(staff) .. docs:: \new Staff { c'4 r2. d'2 r2 e'2. r4 f'1 } .. figure:: ../_images/extend_notes-egF496w5Bs.png Chords: This function works with chords: >>> staff = abjad.Staff( ... r"\time 3/4 c'8 r8 r2 r4 <d' e' f'>4 r4 r8 g'16 r16 r2" ... ) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 c'8 r8 r2 r4 <d' e' f'>4 r4 r8 g'16 r16 r2 } .. figure:: ../_images/extend_notes-2151PE127t.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((2, 4))) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 c'2 r4 r4 <d' e' f'>2 r8 g'8 ~ g'4. r8 } .. figure:: ../_images/extend_notes-GVM7SBRQ5z.png Second positional argument: The second positional argument must be an |abjad.Duration| or objects that can instantiate an |abjad.Duration|, such as :obj:`str`, :obj:`tuple`, :obj:`int`, and :obj:`float`. The notes will be extended up to that duration, or until the next note if the full duration is not possible: >>> staff = abjad.Staff( ... r"c'16 r4.. d'16 r4.. e'16 r2... f'16 r4.. g'16 r4.." ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'16 r4.. d'16 r4.. e'16 r2... f'16 r4.. g'16 r4.. } .. figure:: ../_images/extend_notes-GfrvgpqH2J.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((3, 4))) >>> abjad.show(staff) .. docs:: \new Staff { c'2 d'2 e'2. r4 f'2 g'2 } .. figure:: ../_images/extend_notes-1vKtKyK7up.png Dynamics: Dynamics are preserved: >>> staff = abjad.Staff( ... r"c'16\ppp r2... d'16\ff r2... e'16\f r2... f'16\mp r2..." ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'16 \ppp r2... d'16 \ff r2... e'16 \f r2... f'16 \mp r2... } .. figure:: ../_images/extend_notes-rAHtip59b2.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((1, 4))) >>> abjad.show(staff) .. docs:: \new Staff { c'2. \ppp r4 d'2. \ff r4 e'2. \f r4 f'2. \mp r4 } .. figure:: ../_images/extend_notes-TNbei46PhS.png Time signatures changes: This function handles time signature changes: >>> staff = abjad.Staff( ... r"\time 3/4 c'16 r8. r2 " ... r"\time 2/4 d'8 r8 e'8 r8 " ... r"\time 3/4 r2 f'16 r8." ... ) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 c'16 r8. r2 \time 2/4 d'8 r8 e'8 r8 \time 3/4 r2 f'16 r8. } .. figure:: ../_images/extend_notes-k8d4mKMaS7.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((3, 4))) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 c'2. \time 2/4 d'4 e'4 ~ \time 3/4 e'2 f'4 } .. figure:: ../_images/extend_notes-5eDO8ehSld.png ``gap``: Use the argument ``gap`` to ensure that there a minimum gap is left between leaves when extending them: >>> staff = abjad.Staff(r"c'4 r4 d'4 r4 e'4 r2.") >>> abjad.show(staff) .. docs:: \new Staff { c'4 r4 d'4 r4 e'4 r2. } .. figure:: ../_images/extend_notes-zpAx8NYXof.png >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((2, 4)), ... gap=abjad.Duration((1, 16)), ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'4.. r16 d'4.. r16 e'2 r2 } .. figure:: ../_images/extend_notes-t1HIPtyJkn.png Note that this function will not shorten leaves, so previous leaves that had no gaps will still have no gaps between them. ``gap`` will only affect leaves that are extended by the function. >>> staff = abjad.Staff(r"c'2 d'4 r4 e'4 r4 f'2 ~ f'2 r2") >>> abjad.show(staff) .. docs:: \new Staff { c'2 d'4 r4 e'4 r4 f'2 ~ f'2 r2 } .. figure:: ../_images/extend_notes-ZfrusN3f8T.png >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((2, 4)), ... gap=abjad.Duration((1, 16)), ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'2 d'4.. r16 e'4.. r16 f'2 ~ f'2 r2 } .. figure:: ../_images/extend_notes-5iteVkhz3l.png ``gap_before_end``: By default, a when ``gap`` is present it is not applied to the last pitched at the end of the container. >>> staff = abjad.Staff(r"c'16 r8. d'16 r8. e'16 r8. f'16 r8.") >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((1, 4)), ... gap=abjad.Duration((1, 16)), ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'8. r16 d'8. r16 e'8. r16 f'4 } .. figure:: ../_images/extend_notes-LuCN8pgjLH.png Set ``gap_before_end`` to ``True`` to ensure that there is also a minimum gap at the end of the container. >>> staff = abjad.Staff(r"c'16 r8. d'16 r8. e'16 r8. f'16 r8.") >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((1, 4)), ... gap=abjad.Duration((1, 16)), ... gap_before_end=True, ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'8. r16 d'8. r16 e'8. r16 f'8. r16 } .. figure:: ../_images/extend_notes-LppuegmkEP.png ``use_multimeasure_rests``: By default, this function uses multi-measure rests >>> staff = abjad.Staff(r"\time 3/4 r8 c'8 r4 c'4 r2. r8 c'8 r2 r2.") >>> auxjad.mutate.extend_notes(staff, abjad.Duration((2, 4))) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 r8 c'4. c'4 ~ c'4 r2 r8 c'8 ~ c'4. r8 R1 * 3/4 } .. figure:: ../_images/extend_notes-WaLYhL24UG.png Set the keyword argument ``use_multimeasure_rests`` to ``False`` to disable this behaviour. >>> staff = abjad.Staff(r"\time 3/4 r8 c'8 r4 c'4 r2. r8 c'8 r2 r2.") >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((2, 4)), ... use_multimeasure_rests=False, ... ) >>> abjad.show(staff) .. docs:: \new Staff { \time 3/4 r8 c'4. c'4 ~ c'4 r2 r8 c'8 ~ c'4. r8 r2. } .. figure:: ../_images/extend_notes-H1uDCqQ8iz.png ``rewrite_meter`` By default, this function applies |auxjad.mutate.auto_rewrite_meter()| at the end of its process. >>> staff = abjad.Staff(r"c'16 r4.. d'16 r4..") >>> abjad.show(staff) .. docs:: \new Staff { c'16 r4.. d'16 r4.. } .. figure:: ../_images/extend_notes-BcAYS4SvUL.png >>> auxjad.mutate.extend_notes(staff, abjad.Duration((1, 4))) >>> abjad.show(staff) .. docs:: \new Staff { c'4 r4 d'4 r4 } .. figure:: ../_images/extend_notes-QNEks8lUXB.png Set ``rewrite_meter`` to ``False`` to disable this. The main reason for doing this is performance: when multiple mutations are being applied, it is faster to rewrite the meter just once at the end. >>> staff = abjad.Staff(r"c'16 r4.. d'16 r4..") >>> auxjad.mutate.extend_notes(staff, ... abjad.Duration((1, 4)), ... rewrite_meter=False, ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'16 ~ c'8. r4 d'16 ~ d'8. r4 } .. figure:: ../_images/extend_notes-49jhNoSa9T.png .. warning:: This function does not support tuplets. Using a container with one or more tuplets will result in a :exc:`ValueError` exception being raised: >>> staff = abjad.Staff(r"c'4 r4 \times 2/3 {r4 d'4 r4} e'4 r2.") >>> auxjad.mutate.extend_notes(staff, abjad.Duration((1, 4))) ValueError: first positional argument contains one ore more tuplets, which are not currently supported """ if not isinstance(container, abjad.Container): raise TypeError("first positional argument must be 'abjad.Container' " "or child class") if len(abjad.select(container).tuplets()) > 0: raise ValueError("first positional argument contains one ore more " "tuplets, which are not currently supported") if not isinstance(max_note_duration, (abjad.Duration, str, tuple, int, float), ): raise TypeError("second positional argument must be 'abjad.Duration', " "'str', 'tuple', or a number") if not isinstance(max_note_duration, abjad.Duration): max_note_duration = abjad.Duration(max_note_duration) if gap is not None: if not isinstance(gap, (abjad.Duration, str, tuple, int, float)): raise TypeError("'gap' must be 'abjad.Duration', 'str', 'tuple', " "or a number") if not isinstance(gap, abjad.Duration): gap = abjad.Duration(gap) if not isinstance(gap_before_end, bool): raise TypeError("'gap_before_end' must be 'bool'") if not isinstance(use_multimeasure_rests, bool): raise TypeError("'use_multimeasure_rests' must be 'bool'") if not isinstance(rewrite_meter, bool): raise TypeError("'rewrite_meter' must be 'bool'") time_signatures = get.time_signature_list(container, implicit_common_time=False, ) groups = _groupped_logical_ties(container) for group_index, group in enumerate(groups): if not gap_before_end and group_index == len(groups) - 1: gap = abjad.Duration((0, 1)) if isinstance(group[0], (abjad.Rest, abjad.MultimeasureRest)): continue pitched_duration = 0 rest_duration = 0 for item in group: if isinstance(item, (abjad.Note, abjad.Chord)): pitched_duration += abjad.get.duration(item) else: rest_duration += abjad.get.duration(item) missing_duration = max_note_duration - pitched_duration available_duration = rest_duration - gap if missing_duration <= 0 or available_duration <= 0: continue else: extended_duration = min(missing_duration, available_duration) new_rest_duration = rest_duration - extended_duration for index, item in enumerate(group): if isinstance(item, (abjad.Rest, abjad.MultimeasureRest)): first_rest_index = index break if isinstance(group[first_rest_index - 1], abjad.Note): pitches = group[first_rest_index - 1].written_pitch elif isinstance(group[first_rest_index - 1], abjad.Chord): pitches = [pitch for pitch in group[first_rest_index - 1].written_pitches] if new_rest_duration > abjad.Duration((0, 1)): replacement_items = abjad.LeafMaker()( [pitches, None], [extended_duration, new_rest_duration], ) else: replacement_items = abjad.LeafMaker()([pitches], [extended_duration], ) abjad.mutate.replace(group[first_rest_index:], replacement_items) abjad.attach(abjad.Tie(), group[first_rest_index - 1]) mutate.enforce_time_signature(container, time_signatures, disable_rewrite_meter=True, ) if rewrite_meter: mutate.auto_rewrite_meter(container) if use_multimeasure_rests: mutate.rests_to_multimeasure_rest(container[:])