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