Source code for auxjad.utilities.staff_splitter

from typing import Union

import abjad

from .. import mutate


def _make_rest_from_leaf(leaf: abjad.Leaf,
                         *,
                         ignore_dynamics: bool = False,
                         ) -> abjad.Rest:
    r"""Creates a rest of the same duration as a leaf and passes the
    relevant leaf indicators to the rest (such as time signature or bar
    lines, but not slurs or articulations)
    """
    rest = abjad.Rest(leaf.written_duration)
    for indicator in abjad.get.indicators(leaf):
        if isinstance(indicator, (abjad.BarLine,
                                  abjad.Clef,
                                  abjad.Dynamic,
                                  abjad.KeySignature,
                                  abjad.LilyPondLiteral,
                                  abjad.MetronomeMark,
                                  abjad.RehearsalMark,
                                  abjad.Repeat,
                                  abjad.StaffChange,
                                  abjad.StartHairpin,
                                  abjad.StartSlur,
                                  abjad.StopHairpin,
                                  abjad.StopSlur,
                                  abjad.TimeSignature,
                                  )):
            abjad.attach(indicator, rest)
    return rest


[docs]def staff_splitter(staff: Union[abjad.Staff, abjad.Selection], *, threshold: Union[int, float, str, abjad.Pitch, ] = abjad.NamedPitch("c'"), upper_clef: Union[abjad.Clef, str] = abjad.Clef('treble'), lower_clef: Union[abjad.Clef, str] = abjad.Clef('bass'), add_clefs: bool = True, dynamics_only_on_upper_staff: bool = False, reposition_dynamics: bool = True, reposition_slurs: bool = True, use_multimeasure_rests: bool = True, rewrite_meter: bool = True, ) -> tuple: r"""Takes an |abjad.Staff| or |abjad.Selection| and splits it into two staves using a reference pitch as threshold. Returns a tuple of |abjad.Staff|'s. Basic usage: By default, this function splits notes using ``C4`` as the pitch threshold (any pitches equal to or higher than the threhold will be added to the upper staff, and any pitches lower than it will be added to the lower staff). It also automatically adds clefs to both staves. This function returns a tuple of two |abjad.Staff|'s, which can be input directly into an |abjad.Score|: >>> staff = abjad.Staff(r"a4 b4 c'4 d'4") >>> abjad.show(staff) .. docs:: \new Staff { a4 b4 c'4 d'4 } .. figure:: ../_images/staff_splitter-h3b7QkOtTm.png >>> staves = auxjad.staff_splitter(staff) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" r2 c'4 d'4 } \new Staff { \clef "bass" a4 b4 r2 } >> .. figure:: ../_images/staff_splitter-WQoeWAkkmV.png Chords: Chords are split according to the threhold pitch: >>> staff = abjad.Staff(r"<g b>4 <a c'>4 <b d' f'>4 <a f c' e' g'>4") >>> abjad.show(staff) .. docs:: \new Staff { <g b>4 <a c'>4 <b d' f'>4 <a f c' e' g'>4 } .. figure:: ../_images/staff_splitter-ICrujezDfk.png >>> staves = auxjad.staff_splitter(staff) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" r4 c'4 <d' f'>4 <c' e' g'>4 } \new Staff { \clef "bass" <g b>4 a4 b4 <f a>4 } >> .. figure:: ../_images/staff_splitter-KASqlXv1Fh.png ``threshold``: Set the keword argument ``threshold`` to a pitch to specify the threhold pitch. Notes lower than this pitch will be added to the lower staff whereas notes higher than or equal to this pitch will be added to the upper one. It can take an |abjad.NamedPitch|, an |abjad.NumberedPitch|, or a :obj:`str`, :obj:`int`, or :obj:`float`. >>> staff = abjad.Staff(r"c'4 d'4 e'4 <d' f' a'>4") >>> abjad.show(staff) .. docs:: \new Staff { c'4 d'4 e'4 <d' f' a'>4 } .. figure:: ../_images/staff_splitter-nbVARRMPpG.png >>> staves = auxjad.staff_splitter(staff, threshold="e'") >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" r2 e'4 <f' a'>4 } \new Staff { \clef "bass" c'4 d'4 r4 d'4 } >> .. figure:: ../_images/staff_splitter-hwD9HkFusx.png ``lower_clef`` and ``upper_clef``: By default, the clefs of the lower and upper staves are set to bass and treble clefs, respectively. Use the keyword arguments ``lower_clef`` and ``upper_clef`` to change this. They can take either an |abjad.Clef| or an :obj:`str`. >>> staff = abjad.Staff(r"c'4 d'4 e'4 <d' f' a'>4") >>> abjad.show(staff) .. docs:: \new Staff { c'4 d'4 e'4 <d' f' a'>4 } .. figure:: ../_images/staff_splitter-eJBqwmhPsj.png >>> staves = auxjad.staff_splitter(staff, ... threshold="e'", ... lower_clef='treble', ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" r2 e'4 <f' a'>4 } \new Staff { \clef "treble" c'4 d'4 r4 d'4 } >> .. figure:: ../_images/staff_splitter-87mg2UBDPn.png >>> staff = abjad.Staff(r"e4 f4 g4 <f a c'>4") >>> abjad.show(staff) .. docs:: \new Staff { e4 f4 g4 <f a c'>4 } .. figure:: ../_images/staff_splitter-Nu9dNMM9gy.png >>> staves = auxjad.staff_splitter(staff, ... threshold='g', ... upper_clef='bass', ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "bass" r2 g4 <a c'>4 } \new Staff { \clef "bass" e4 f4 r4 f4 } >> .. figure:: ../_images/staff_splitter-68Qhb2RuTj.png ``add_clefs``: To not add clefs to the output staves, set ``add_clefs`` to ``False``: >>> staff = abjad.Staff(r"c'4 d'4 e'4 <d' f' a'>4") >>> abjad.show(staff) .. docs:: \new Staff { c'4 d'4 e'4 <d' f' a'>4 } .. figure:: ../_images/staff_splitter-uoSjmalLzc.png >>> staves = auxjad.staff_splitter(staff, ... threshold="e'", ... add_clefs=False, ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { r2 e'4 <f' a'>4 } \new Staff { c'4 d'4 r4 d'4 } >> .. figure:: ../_images/staff_splitter-roBNy3fRus.png .. note:: Note how there are no clefs added to the LilyPond output. Note that in the image above that LilyPond automatically fallbacks to treble clefs when no clef is present in the input code. >>> staff = abjad.Staff(r"c'4 d'4 e'4 <d' f' a'>4") >>> staves = auxjad.staff_splitter(staff, ... threshold="e'", ... add_clefs=False, ... ) >>> string = abjad.lilypond(score) >>> print(string) \new Score << \new Staff { r2 e'4 <f' a'>4 } \new Staff { c'4 d'4 r4 d'4 } >> ``use_multimeasure_rests``: By default, rests are converted to multi-measure rests. >>> staff = abjad.Staff( ... r"\time 2/4 c'2 \times 2/3 {<g b d'>2 <e' f'>4}" ... r"\times 2/3 {a2 <g b>4}" ... ) >>> abjad.show(staff) .. docs:: \new Staff { \time 2/4 c'2 \times 2/3 { <g b d'>2 <e' f'>4 } \times 2/3 { a2 <g b>4 } } .. figure:: ../_images/staff_splitter-QkVo9lbpvB.png >>> staves = auxjad.staff_splitter(staff) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \time 2/4 \clef "treble" c'2 \times 2/3 { d'2 <e' f'>4 } R1 * 1/2 } \new Staff { \time 2/4 \clef "bass" R1 * 1/2 \times 2/3 { <g b>2 r4 } \times 2/3 { a2 <g b>4 } } >> .. figure:: ../_images/staff_splitter-gLP4jNpRXE.png Set ``use_multimeasure_rests`` to ``False`` to disable this behaviour. >>> staves = auxjad.staff_splitter(staff, ... use_multimeasure_rests=False, ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \time 2/4 \clef "treble" c'2 \times 2/3 { d'2 <e' f'>4 } r2 } \new Staff { \time 2/4 \clef "bass" r2 \times 2/3 { <g b>2 r4 } \times 2/3 { a2 <g b>4 } } >> .. figure:: ../_images/staff_splitter-s2Q7w1aVAC.png ``rewrite_meter``: By default, the |abjad.Meter.rewrite_meter()| mutation is applied to the output. Set ``rewrite_meter`` to ``False`` to disable it: >>> staff = abjad.Staff(r"a4 b4 c'4 d'4") >>> abjad.show(staff) .. docs:: \new Staff { a4 b4 c'4 d'4 } .. figure:: ../_images/staff_splitter-JyRf5fsouP.png >>> staves = auxjad.staff_splitter(staff, ... rewrite_meter=False, ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" r4 r4 c'4 d'4 } \new Staff { \clef "bass" a4 b4 r4 r4 } >> .. figure:: ../_images/staff_splitter-hinmixCe4v.png ``reposition_dynamics``: By default, dynamics are split among the staves and then are mutated using :func:`auxjad.mutate.reposition_dynamics()`. >>> staff = abjad.Staff( ... r"c'2\p <b d'>2\ff \times 2/3 {<g b d'>2\f <e' f'>1\mf}" ... r"\times 2/3 {a2\pp <g b>1\mp}" ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'2 \p <b d'>2 \ff \times 2/3 { <g b d'>2 \f <e' f'>1 \mf } \times 2/3 { a2 \pp <g b>1 \mp } } .. figure:: ../_images/staff_splitter-I42iFOOMEm.png >>> staves = auxjad.staff_splitter(staff) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" c'2 \p d'2 \ff \times 2/3 { d'2 \f <e' f'>1 \mf } R1 } \new Staff { \clef "bass" r2 b2 \ff \times 2/3 { <g b>2 \f r1 } \times 2/3 { a2 \pp <g b>1 \mp } } >> .. figure:: ../_images/staff_splitter-SYTT14sWJo.png Set ``reposition_dynamics`` to ``False`` to disable this behaviour: >>> staff = abjad.Staff( ... r"c'2\p <b d'>2\ff \times 2/3 {<g b d'>2\f <e' f'>1\mf}" ... r"\times 2/3 {a2\pp <g b>1\mp}" ... ) >>> staves = auxjad.staff_splitter(staff, ... reposition_dynamics=False, ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" c'2 \p d'2 \ff \times 2/3 { d'2 \f <e' f'>1 \mf } R1 \mp } \new Staff { \clef "bass" r2 \p b2 \ff \times 2/3 { <g b>2 \f r1 \mf } \times 2/3 { a2 \pp <g b>1 \mp } } >> .. figure:: ../_images/staff_splitter-sXrC86fy1J.png .. note:: It is important to note that dynamics can easily become problematic in the output when dealing with complex input staves. When possible, it is always best to handle containers without dynamics or those with very simple dynamics. ``dynamics_only_on_upper_staff``: Dynamics are distributed to both staves by default. To keep the dynamics only to the upper staff, set ``dynamics_only_on_upper_staff`` to ``True`` >>> staff = abjad.Staff( ... r"c'2\p <b d'>2\ff \times 2/3 {<g b d'>2\f <e' f'>1\mf}" ... r"\times 2/3 {a2\pp <g b>1\mp}" ... ) >>> abjad.show(staff) .. docs:: \new Staff { c'2 \p <b d'>2 \ff \times 2/3 { <g b d'>2 \f <e' f'>1 \mf } \times 2/3 { a2 \pp <g b>1 \mp } } .. figure:: ../_images/staff_splitter-f6E9w8Lpn7.png >>> staves = auxjad.staff_splitter(staff, ... dynamics_only_on_upper_staff=True, ... ) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \clef "treble" c'2 \p d'2 \ff \times 2/3 { d'2 \f <e' f'>1 \mf } R1 } \new Staff { \clef "bass" r2 b2 \times 2/3 { <g b>2 r1 } \times 2/3 { a2 <g b>1 } } >> .. figure:: ../_images/staff_splitter-bVcVinE5cG.png .. note:: Similarly to the previous note about dynamics, it is important to note that, once again, dynamics can easily become problematic in the output. If there are rests or multi-measure rests in the upper staff, the dynamics can get lost. The best approach is to add them after the split. Slurs and articulations: This function will automatically handle slurs and articulations: >>> staff = abjad.Staff( ... r"\time 2/4 a8( b c' d') \times 2/3 {<g b d'>2 <e' f'>4}" ... r"\time 3/4 <d a c' g'>4-- r8 <f a bf>4." ... ) >>> abjad.show(staff) .. docs:: \new Staff { \time 2/4 a8 ( b8 c'8 d'8 ) \times 2/3 { <g b d'>2 <e' f'>4 } \time 3/4 <d a c' g'>4 - \tenuto r8 <f a bf>4. } .. figure:: ../_images/staff_splitter-SZjT1IJCsn.png >>> staves = auxjad.staff_splitter(staff) >>> score = abjad.Score(staves) >>> abjad.show(score) .. docs:: \new Score << \new Staff { \time 2/4 \clef "treble" r4 c'8 ( d'8 ) \times 2/3 { d'2 <e' f'>4 } \time 3/4 <c' g'>4 - \tenuto r2 } \new Staff { \time 2/4 \clef "bass" a8 ( b8 ) r4 \times 2/3 { <g b>2 r4 } \time 3/4 <d a>4 - \tenuto r8 <f a bf>4. } >> .. figure:: ../_images/staff_splitter-RbPPaLUzjO.png """ if not isinstance(staff, (abjad.Staff, abjad.Selection)): raise TypeError("'staff' must be 'abjad.Staff' or 'abjad.Selection'") if isinstance(threshold, str): threshold = abjad.NamedPitch(threshold) elif isinstance(threshold, (int, float)): threshold = abjad.NumberedPitch(threshold) elif not isinstance(threshold, abjad.Pitch): raise TypeError("'threshold' must be 'abjad.Pitch' (or child class), " "'str', 'int', or 'float'") if isinstance(upper_clef, str): upper_clef = abjad.Clef(upper_clef) elif not isinstance(upper_clef, abjad.Clef): raise TypeError("'upper_clef' must be 'abjad.Clef' or 'str'") if isinstance(lower_clef, str): lower_clef = abjad.Clef(lower_clef) elif not isinstance(lower_clef, abjad.Clef): raise TypeError("'lower_clef' must be 'abjad.Clef' or 'str'") if not isinstance(add_clefs, bool): raise TypeError("'add_clefs' must be 'bool'") if not isinstance(dynamics_only_on_upper_staff, bool): raise TypeError("'dynamics_only_on_upper_staff' must be 'bool'") if not isinstance(reposition_dynamics, bool): raise TypeError("'reposition_dynamics' must be 'bool'") if not isinstance(reposition_slurs, bool): raise TypeError("'reposition_slurs' 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'") upper_staff = abjad.mutate.copy(staff) lower_staff = abjad.mutate.copy(staff) if isinstance(staff, abjad.Selection): upper_staff = abjad.Staff(upper_staff) lower_staff = abjad.Staff(lower_staff) for leaf in abjad.select(upper_staff).leaves(): if isinstance(leaf, abjad.Note) and leaf.written_pitch < threshold: rest = _make_rest_from_leaf(leaf) abjad.mutate.replace(leaf, rest) elif isinstance(leaf, abjad.Chord): if all([pitch < threshold for pitch in leaf.written_pitches]): rest = _make_rest_from_leaf(leaf) abjad.mutate.replace(leaf, rest) else: new_pitches = [] for pitch in leaf.written_pitches: if pitch >= threshold: new_pitches.append(pitch) if len(new_pitches) > 1: leaf.written_pitches = new_pitches else: new_leaf = abjad.Note(new_pitches[0], leaf.written_duration, ) for indicator in abjad.get.indicators(leaf): abjad.attach(indicator, new_leaf) abjad.mutate.replace(leaf, new_leaf) for leaf in abjad.select(lower_staff).leaves(): if isinstance(leaf, abjad.Note): if leaf.written_pitch >= threshold: rest = _make_rest_from_leaf(leaf) abjad.mutate.replace(leaf, rest) if isinstance(leaf, abjad.Chord): if all([pitch >= threshold for pitch in leaf.written_pitches]): rest = _make_rest_from_leaf(leaf) abjad.mutate.replace(leaf, rest) else: new_pitches = [] for pitch in leaf.written_pitches: if pitch < threshold: new_pitches.append(pitch) if len(new_pitches) > 1: leaf.written_pitches = new_pitches else: new_leaf = abjad.Note(new_pitches[0], leaf.written_duration, ) for indicator in abjad.get.indicators(leaf): abjad.attach(indicator, new_leaf) abjad.mutate.replace(leaf, new_leaf) if add_clefs: abjad.attach(upper_clef, abjad.select(upper_staff).leaf(0)) abjad.attach(lower_clef, abjad.select(lower_staff).leaf(0)) if dynamics_only_on_upper_staff: for leaf in abjad.select(lower_staff).leaves(): for indicator in abjad.get.indicators(leaf): if isinstance(indicator, (abjad.Dynamic, abjad.StartHairpin, abjad.StopHairpin, )): abjad.detach(indicator, leaf) if reposition_dynamics: mutate.reposition_dynamics(upper_staff[:]) if not dynamics_only_on_upper_staff: mutate.reposition_dynamics(lower_staff[:]) if reposition_slurs: mutate.reposition_slurs(upper_staff[:]) mutate.reposition_slurs(lower_staff[:]) if use_multimeasure_rests: mutate.rests_to_multimeasure_rest(upper_staff[:]) mutate.rests_to_multimeasure_rest(lower_staff[:]) if rewrite_meter: mutate.auto_rewrite_meter(upper_staff) mutate.auto_rewrite_meter(lower_staff) return upper_staff, lower_staff
### EXTENSION FUNCTIONS ### abjad.staff_splitter = staff_splitter