Source code for auxjad.mutate.reposition_dynamics

import abjad

from .remove_repeated_dynamics import (
    remove_repeated_dynamics as remove_repeated_dynamics_,
)


[docs]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)