import random
from typing import Optional, Union
import abjad
from .. import mutate
from .Fader import Fader
[docs]class CrossFader():
r"""Takes two |abjad.Container|'s' (or child class) and gradually
crossfades from one into the other, by fading out the first while fading in
the second. It makes use of two :class:`auxjad.Fader` for that.
Basic usage:
Calling the object will return a :obj:`tuple` of two
|abjad.Selection|'s generated by the fading process. Each call of the
object will apply the fading process to one of the two containers of
the previous results. That is, either a note of the first container is
removed or a note of the second container is added.
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d''1}")
>>> abjad.show(fade_out_container)
.. docs::
{
fs'4
g'2
bf'4
}
.. figure:: ../_images/CrossFader-7yWgnyhyA3.png
>>> abjad.show(fade_in_container)
.. docs::
{
\times 4/5 {
cs''4
d''1
}
}
.. figure:: ../_images/CrossFader-aS29mS1xIB.png
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
g'2
bf'4
}
\new Staff
{
\time 4/4
R1
}
>>
.. figure:: ../_images/CrossFader-gdofr6vtutv.png
>>> selection_a, selection_b = fader()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
g'2
bf'4
}
\new Staff
{
\times 4/5
{
\time 4/4
r4
d''1
}
}
>>
.. figure:: ../_images/CrossFader-ge0qzrwa7fr.png
>>> selection_a, selection_b = fader()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
r2
bf'4
}
\new Staff
{
\times 4/5
{
\time 4/4
r4
d''1
}
}
>>
.. figure:: ../_images/CrossFader-0iynx7xenvmp.png
The property :attr:`current_window` can be used to access the current
window without processing it.
>>> notes = fader.current_window()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
r2
bf'4
}
\new Staff
{
\times 4/5
{
\time 4/4
r4
d''1
}
}
>>
.. figure:: ../_images/CrossFader-kinei9zncl.png
:meth:`output_all`:
To run through the whole process and output it as a :obj:`tuple` of two
|abjad.Selection|'s, use the method :meth:`output_all`.
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d'1}")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
g'2
bf'4
fs'4
g'2
bf'4
fs'4
r2
bf'4
fs'4
r2
bf'4
r2.
bf'4
R1
}
\new Staff
{
\time 4/4
R1
\times 4/5
{
cs''4
r1
}
\times 4/5
{
cs''4
r1
}
\times 4/5
{
cs''4
d'1
}
\times 4/5
{
cs''4
d'1
}
\times 4/5
{
cs''4
d'1
}
}
>>
.. figure:: ../_images/CrossFader-0t206fb4ycne.png
:meth:`output_n`:
To run through just part of the process and output it as a :obj:`tuple`
of two |abjad.Selection|'s, use the method :meth:`output_n` and pass
the number of iterations as argument.
>>> fade_out_container = abjad.Container(r"e'8 fs'4. r2")
>>> fade_in_container = abjad.Container(r"c''2 ~ c''8 d''4.")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_n(3)
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
e'8
fs'4.
r2
e'8
fs'4.
r2
e'8
r2..
}
\new Staff
{
\time 4/4
R1
c''2
~
c''8
r4.
c''2
~
c''8
r4.
}
>>
.. figure:: ../_images/CrossFader-8o5fjalw1px.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.
>>> fade_out_container = abjad.Container(r"e'8 fs'4. r2")
>>> fade_in_container = abjad.Container(r"c''2 ~ c''8 d''4.")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> staff_a = abjad.Staff()
>>> staff_b = abjad.Staff()
>>> for selection_a, selection_b in fader:
... staff_a.extend(selection_a)
... staff_b.extend(selection_b)
>>> auxjad.mutate.remove_repeated_time_signatures(staff_a)
>>> auxjad.mutate.remove_repeated_time_signatures(staff_b)
>>> score = abjad.Score([staff_a, staff_b])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
e'8
fs'4.
r2
e'8
fs'4.
r2
e'8
r2..
e'8
r2..
R1
}
\new Staff
{
\time 4/4
R1
c''2
~
c''8
r4.
c''2
~
c''8
r4.
c''2
~
c''8
d''4.
c''2
~
c''8
d''4.
}
>>
.. figure:: ../_images/CrossFader-3g9fv4ew4h9.png
Arguments and properties:
This class can take many optional keyword arguments during its
creation. Setting :attr:`fade_in_first` to ``True`` will ensure that a
note from the second container must be faded in before any notes of the
first container are removed (default ``False``). Setting
:attr:`fade_out_last` to ``True`` will ensure that all notes from the
second container must be faded in before the very last note of the
first container is removed (default ``False``). The arguments
:attr:`initial_repetitions` and :attr:`final_repetitions` set the
number of repetitions of the initial and final measures (default ``1``
for both). :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). Setting :attr:`weighted_duration` to ``True`` will give
more weight to the larger container (considering both its number of
notes as well its length), making it more likely that both will fade in
and out at a similar rate (default value is ``False``).
: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. 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.
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d''1}")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_in_first=True,
... fade_out_last=True,
... initial_repetitions=3,
... final_repetitions=3,
... repetition_chance=0.7,
... weighted_duration=True,
... disable_rewrite_meter=True,
... omit_time_signatures=True,
... use_multimeasure_rests=True,
... boundary_depth=True,
... maximum_dot_count=True,
... rewrite_tuplets=True,
... )
>>> fader.fade_in_first
True
>>> fader.fade_out_last
True
>>> fader.initial_repetitions
3
>>> fader.final_repetitions
3
>>> fader.repetition_chance
0.7
>>> fader.weighted_duration
True
>>> fader.disable_rewrite_meter
True
>>> fader.omit_time_signatures
True
>>> fader.use_multimeasure_rests
True
>>> fader.boundary_depth
True
>>> fader.maximum_dot_count
True
>>> fader.rewrite_tuplets
True
Use the properties below to change these values after initialisation.
>>> fader.fade_in_first = False
>>> fader.fade_out_last = False
>>> fader.initial_repetitions = 4
>>> fader.final_repetitions = 7
>>> fader.repetition_chance = 0.23
>>> fader.weighted_duration = False
>>> fader.disable_rewrite_meter = False
>>> fader.omit_time_signatures = False
>>> fader.use_multimeasure_rests = False
>>> fader.boundary_depth = False
>>> fader.maximum_dot_count = False
>>> fader.rewrite_tuplets = False
>>> fader.fade_in_first
False
>>> fader.fade_out_last
False
>>> fader.initial_repetitions
4
>>> fader.final_repetitions
7
>>> fader.repetition_chance
0.23
>>> fader.weighted_duration
False
>>> fader.disable_rewrite_meter
False
>>> fader.omit_time_signatures
False
>>> fader.use_multimeasure_rests
False
>>> fader.boundary_depth
False
>>> fader.maximum_dot_count
False
>>> fader.rewrite_tuplets
False
:meth:`reset`:
Use the :meth:`reset` method to reset the fader to its initial state.
This can be used to restart the process at any time.
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d'1}")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> staff_a = abjad.Staff()
>>> staff_b = abjad.Staff()
>>> for _ in range(3):
... selection_a, selection_b = fader()
... staff_a.extend(selection_a)
... staff_b.extend(selection_b)
>>> fader.reset()
>>> selection_a, selection_b = fader()
>>> staff_a.extend(selection_a)
>>> staff_b.extend(selection_b)
>>> auxjad.mutate.remove_repeated_time_signatures(staff_a)
>>> auxjad.mutate.remove_repeated_time_signatures(staff_b)
>>> score = abjad.Score([staff_a, staff_b])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
fs'4
g'2
bf'4
fs'4
g'2
bf'4
fs'4
r2
bf'4
fs'4
g'2
bf'4
}
\new Staff
{
\time 4/4
R1
\times 4/5
{
cs''4
r1
}
\times 4/5
{
cs''4
r1
}
R1
}
>>
.. figure:: ../_images/CrossFader-pauqe26ke8e.png
Chords:
This class also support chords. Each of their individual notes are
removed or added one by one.
>>> fade_out_container = abjad.Container(r"\times 2/3 {<c' e'>2 g'1}")
>>> fade_in_container = abjad.Container(r"<d' ef'>2. <bf a'>4")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_in_first=True,
... fade_out_last=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\times 2/3
{
\time 4/4
<c' e'>2
g'1
}
\times 2/3
{
<c' e'>2
g'1
}
\times 2/3
{
<c' e'>2
g'1
}
\times 2/3
{
<c' e'>2
g'1
}
\times 2/3
{
c'2
g'1
}
\times 2/3
{
c'2
g'1
}
\times 2/3
{
c'2
r1
}
R1
}
\new Staff
{
\time 4/4
R1
ef'2.
r4
<d' ef'>2.
r4
<d' ef'>2.
bf4
<d' ef'>2.
bf4
<d' ef'>2.
<bf a'>4
<d' ef'>2.
<bf a'>4
<d' ef'>2.
<bf a'>4
}
>>
.. figure:: ../_images/CrossFader-lm8byjxoayn.png
:func:`len()`:
The function :func:`len()` returns the sum of the number of notes of
both :attr:`fade_in_contents` and :attr:`fade_out_contents`.
>>> fade_out_container = abjad.Container(r"c'4 d'4 ~ d'4 r4")
>>> fade_in_container = abjad.Container(r"r2 c''2")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> len(fader)
3
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d''1}")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> len(fader)
5
>>> fade_out_container = abjad.Container(r"c'4 d'4 ~ d'4 r4")
>>> fade_in_container = abjad.Container(r"r2 <c'' e'' g''>2")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> len(fader)
5
:attr:`fade_in_first` and :attr:`fade_out_last`:
Setting the property :attr:`fade_in_first` to ``True`` will ensure that
a note from second container must be faded in before any note of the
first container is removed (default ``False``). Setting
:attr:`fade_out_last` to ``True`` will ensure that all notes from the
second container must be faded in before the very last note of first
container is removed (default ``False``).
>>> fade_out_container = abjad.Container(r"\time 3/4 r4 c'4 d'4")
>>> fade_in_container = abjad.Container(r"\time 3/4 a''4 g''2")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 3/4
r4
c'4
d'4
r2
d'4
r2
d'4
R1 * 3/4
R1 * 3/4
}
\new Staff
{
\time 3/4
R1 * 3/4
R1 * 3/4
a''4
r2
a''4
r2
a''4
g''2
}
>>
.. figure:: ../_images/CrossFader-sm9qcruxat.png
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_out_last=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 3/4
r4
c'4
d'4
r2
d'4
r2
d'4
r2
d'4
R1 * 3/4
}
\new Staff
{
\time 3/4
R1 * 3/4
R1 * 3/4
a''4
r2
a''4
g''2
a''4
g''2
}
>>
.. figure:: ../_images/CrossFader-2a4cyxsyxrg.png
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_in_first=True,
... fade_out_last=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 3/4
r4
c'4
d'4
r4
c'4
d'4
r4
c'4
d'4
r2
d'4
R1 * 3/4
}
\new Staff
{
\time 3/4
R1 * 3/4
a''4
r2
a''4
g''2
a''4
g''2
a''4
g''2
}
>>
.. figure:: ../_images/CrossFader-oap1pnwrfqc.png
:attr:`weighted_duration`:
Setting :attr:`weighted_duration` to ``True`` will give more weight to
the larger container (considering both its number of notes as well its
length), making it more likely that both will fade in and out at a
similar rate (default value is ``False``).
>>> fade_out_container = abjad.Container(r"e'2 c'2")
>>> fade_in_container = abjad.Container(
... r"c''8 d''8 e''8 f''8 g''8 a''8 b''8 c'''8"
... )
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
e'2
c'2
e'2
c'2
r2
c'2
r2
c'2
r2
c'2
R1
R1
R1
R1
R1
R1
}
\new Staff
{
\time 4/4
R1
r4.
f''8
r2
r4.
f''8
r2
r4.
f''8
r8
a''8
r4
r4.
f''8
r8
a''8
b''8
r8
r4.
f''8
r8
a''8
b''8
r8
r4.
f''8
r8
a''8
b''8
c'''8
r4
e''8
f''8
r8
a''8
b''8
c'''8
r8
d''8
e''8
f''8
r8
a''8
b''8
c'''8
c''8
d''8
e''8
f''8
r8
a''8
b''8
c'''8
c''8
d''8
e''8
f''8
g''8
a''8
b''8
c'''8
}
>>
.. figure:: ../_images/CrossFader-o58h79ijsdf.png
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... weighted_duration=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
e'2
c'2
e'2
c'2
r2
c'2
r2
c'2
r2
c'2
r2
c'2
r2
c'2
r2
c'2
r2
c'2
r2
c'2
R1
}
\new Staff
{
\time 4/4
R1
r4.
f''8
r2
r4.
f''8
r2
r4.
f''8
r8
a''8
r4
r4.
f''8
r8
a''8
b''8
r8
r4
e''8
f''8
r8
a''8
b''8
r8
r4
e''8
f''8
g''8
a''8
b''8
r8
c''8
r8
e''8
f''8
g''8
a''8
b''8
r8
c''8
r8
e''8
f''8
g''8
a''8
b''8
c'''8
c''8
d''8
e''8
f''8
g''8
a''8
b''8
c'''8
c''8
d''8
e''8
f''8
g''8
a''8
b''8
c'''8
}
>>
.. figure:: ../_images/CrossFader-l3dc3vxfpdnsxdym4gql.png
:attr:`initial_repetitions` and :attr:`final_repetitions`:
The properties :attr:`initial_repetitions` and
:attr:`final_repetitions` set the number of repetitions of the initial
and final measures (default is ``1`` for both).
>>> fade_out_container = abjad.Container(r"a'4 bf'2 r4")
>>> fade_in_container = abjad.Container(r"c''2 d''2")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... initial_repetitions=2,
... final_repetitions=3,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
a'4
bf'2
r4
a'4
bf'2
r4
r4
bf'2
r4
r4
bf'2
r4
r4
bf'2
r4
R1
R1
R1
}
\new Staff
{
\time 4/4
R1
R1
R1
r2
d''2
c''2
d''2
c''2
d''2
c''2
d''2
c''2
d''2
}
>>
.. figure:: ../_images/CrossFader-e480tbrvwg.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).
>>> fade_out_container = abjad.Container(r"a'4 bf'2 r4")
>>> fade_in_container = abjad.Container(r"c''2 d''2")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... repetition_chance=0.8,
... )
>>> selection_a, selection_b = fader.output_n(4)
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
a'4
bf'2
r4
a'4
bf'2
r4
a'4
bf'2
r4
a'4
bf'2
r4
}
\new Staff
{
\time 4/4
R1
r2
d''2
r2
d''2
r2
d''2
}
>>
.. figure:: ../_images/CrossFader-743rxr2n02o.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.
>>> fade_out_container = abjad.Container(r"fs'4 g'2 bf'4")
>>> fade_in_container = abjad.Container(r"\times 4/5 {cs''4 d''1}")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... omit_time_signatures=True,
... )
>>> selection_a, selection_b = fader.output_n(3)
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
fs'4
g'2
bf'4
fs'4
g'2
r4
fs'4
g'2
r4
}
\new Staff
{
R1
R1
\times 4/5
{
cs''4
r1
}
}
>>
.. figure:: ../_images/CrossFader-aypzofmynwe.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 or disabling |abjad.Meter.rewrite_meter()|:
This function uses the default logical tie splitting algorithm from
|abjad.Meter.rewrite_meter()|. It can be disabled with the property
:attr:`disable_rewrite_meter`.
>>> fade_out_container = abjad.Container(r"c'8 d'4 e'8 ~ e'2")
>>> fade_in_container = abjad.Container(r"c'2 d'2")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... disable_rewrite_meter=True,
... )
>>> selection_a, selection_b = fader.output_n(3)
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
c'8
d'4
e'8
~
e'2
r8
d'4
e'8
~
e'2
r8
d'4
e'8
~
e'2
}
\new Staff
{
\time 4/4
R1
R1
r2
d'2
}
>>
.. figure:: ../_images/CrossFader-vprhoh84quk.png
Arguments available for tweaking the output of
|abjad.Meter.rewrite_meter()| are :attr:`boundary_depth`,
:attr:`maximum_dot_count` and :attr:`rewrite_tuplets`, which work
exactly as the identically named arguments of
|abjad.Meter.rewrite_meter()|.
:attr:`fade_in_contents` and :attr:`fade_out_contents`:
Containers can be switched after initialisation by overwriting the
properties :attr:`fade_in_contents` or :attr:`fade_out_contents`. This
will reset the process.
>>> fade_out_container = abjad.Container(
... r"\time 3/4 e2 \times 2/3 {fs8 gs4}"
... )
>>> fade_in_container = abjad.Container(
... r"\time 3/4 c'8 d' e' f' g' a'"
... )
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> fader.fade_out_contents = abjad.Container(r"\time 3/4 a4. bf4.")
>>> print(fader)
{
%%% \time 3/4 %%%
a4.
bf4.
}
{
%%% \time 3/4 %%%
c'8
d'8
e'8
f'8
g'8
a'8
}
Time signature changes:
This class can handle time signature changes.
>>> fade_out_container = abjad.Container(
... r"\time 3/4 a'4 bf'2 ~ \time 2/4 bf'4 f'4"
... )
>>> fade_in_container = abjad.Container(
... r"\time 3/4 r16 cs''4.. e''4 \time 2/4 d''2"
... )
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_n(3)
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 3/4
a'4
bf'2
~
\time 2/4
bf'4
f'4
\time 3/4
a'4
bf'2
~
\time 2/4
bf'4
f'4
\time 3/4
a'4
bf'2
~
\time 2/4
bf'4
r4
}
\new Staff
{
\time 3/4
R1 * 3/4
\time 2/4
R1 * 1/2
\time 3/4
r16
cs''4..
r4
\time 2/4
R1 * 1/2
\time 3/4
r16
cs''4..
r4
\time 2/4
R1 * 1/2
}
>>
.. figure:: ../_images/CrossFader-w99zcui5rj.png
Indicators:
This class can also handle dynamics, articulations, chords, and
tuplets. Hairpins might need manual tweaking if the leaf under which
they terminate is removed.
>>> fade_out_container = abjad.Container(r"c'4.\p e'8--\f ~ e'2")
>>> fade_in_container = abjad.Container(
... r"\times 2/3 {f'4-.\pp r4 d'4->\f ~ } d'2"
... )
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_in_first=True,
... fade_out_last=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> score = abjad.Score([
... abjad.Staff(selection_a),
... abjad.Staff(selection_b),
... ])
>>> abjad.show(score)
.. docs::
\new Score
<<
\new Staff
{
\time 4/4
c'4.
\p
e'8
\f
- \tenuto
~
e'2
c'4.
\p
e'8
\f
- \tenuto
~
e'2
r4.
e'8
- \tenuto
~
e'2
r4.
e'8
- \tenuto
~
e'2
R1
}
\new Staff
{
\time 4/4
R1
\times 2/3
{
f'4
\pp
- \staccato
r2
}
r2
\times 2/3
{
f'4
- \staccato
r2
}
r2
\times 2/3
{
f'4
- \staccato
r4
d'4
\f
- \accent
~
}
d'2
\times 2/3
{
f'4
\pp
- \staccato
r4
d'4
\f
- \accent
~
}
d'2
}
>>
.. figure:: ../_images/CrossFader-o0yetbq5wqj.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.
Using multiple voices:
The selections output by the fader can also be assigned to
|abjad.Voice| containers, which can then be displayed simultaneously on
a single |abjad.Staff|.
>>> fade_out_container = abjad.Container(r"b'8 c''8 e''2 g''4")
>>> fade_in_container = abjad.Container(r"\times 2/3 {e'2 d'2 c'2}")
>>> fader = auxjad.CrossFader(fade_out_container, fade_in_container)
>>> selection_a, selection_b = fader.output_all()
>>> literal_voice_one = abjad.LilyPondLiteral(r'\voiceOne')
>>> literal_voice_two = abjad.LilyPondLiteral(r'\voiceTwo')
>>> abjad.attach(literal_voice_one, selection_a[0])
>>> abjad.attach(literal_voice_two, selection_b[0])
>>> staff = abjad.Staff(
... [abjad.Voice(selection_a), abjad.Voice(selection_b)],
... simultaneous=True,
... )
>>> abjad.show(staff)
.. docs::
\new Staff
<<
\new Voice
{
\time 4/4
\voiceOne
b'8
c''8
e''2
g''4
b'8
c''8
e''2
r4
b'8
c''8
e''2
r4
b'8
c''8
e''2
r4
b'8
c''8
e''2
r4
b'8
c''8
r2.
b'8
r2..
R1
}
\new Voice
{
\time 4/4
\voiceTwo
R1
R1
\times 2/3
{
r1
c'2
}
\times 2/3
{
e'2
r2
c'2
}
\times 2/3
{
e'2
d'2
c'2
}
\times 2/3
{
e'2
d'2
c'2
}
\times 2/3
{
e'2
d'2
c'2
}
\times 2/3
{
e'2
d'2
c'2
}
}
>>
.. figure:: ../_images/CrossFader-rZLSa2kyqM.png
Using containers of different lengths:
It is possible to use this class with containers of different lengths
and time signatures, although this feature is not fully implemented and
should be considered experimental. LilyPond must be set up to allow
different simultaneous time signatures, and
|auxjad.mutate.sync_containers()| can be used to add rests to the end
of the shorter staff.
>>> fade_out_container = abjad.Container(r"\time 3/4 c'4 d'4 e'4")
>>> fade_in_container = abjad.Container(r"\time 4/4 g'2 a'2")
>>> fader = auxjad.CrossFader(fade_out_container,
... fade_in_container,
... fade_in_first=True,
... fade_out_last=True,
... weighted_duration=True,
... )
>>> selection_a, selection_b = fader.output_all()
>>> staff_a = abjad.Staff(selection_a)
>>> staff_b = abjad.Staff(selection_b)
>>> abjad.mutate.sync_containers([staff_a, staff_b])
>>> score = abjad.Score([staff_a, staff_b])
>>> lilypond_file = abjad.LilyPondFile.new()
>>> score_block = abjad.Block(name='score')
>>> layout_block = abjad.Block(name='layout')
>>> score_block.items.append(score)
>>> score_block.items.append(layout_block)
>>> lilypond_file.items.append(score_block)
>>> layout_block.items.append(
... r'''\context {
... \Score
... \remove "Timing_translator"
... \remove "Default_bar_line_engraver"
... }
... \context {
... \Staff
... \consists "Timing_translator"
... \consists "Default_bar_line_engraver"
... }'''
... )
>>> abjad.show(score)
.. docs::
\version "2.19.82" %! abjad.LilyPondFile._get_format_pieces()
\language "english" %! abjad.LilyPondFile._get_format_pieces()
\header { %! abjad.LilyPondFile._get_formatted_blocks()
tagline = ##f
} %! abjad.LilyPondFile._get_formatted_blocks()
\layout {}
\paper {}
\score { %! abjad.LilyPondFile._get_formatted_blocks()
\new Score
<<
\new Staff
{
\time 3/4
c'4
d'4
e'4
c'4
d'4
e'4
c'4
r4
e'4
c'4
r2
c'4
r2
R1 * 3/4
R1 * 3/4
R1 * 3/4
}
\new Staff
{
\time 4/4
R1
r2
a'2
r2
a'2
r2
a'2
g'2
a'2
g'2
a'2
}
>>
\layout {
\context {
\Score
\remove "Timing_translator"
\remove "Default_bar_line_engraver"
}
\context {
\Staff
\consists "Timing_translator"
\consists "Default_bar_line_engraver"
}
}
} %! abjad.LilyPondFile._get_formatted_blocks()
.. figure:: ../_images/CrossFader-5qvaan79w8p.png
"""
### CLASS VARIABLES ###
__slots__ = ('_fade_out_contents',
'_fade_in_contents',
'_fader_in',
'_fader_out',
'_faders',
'_weights',
'_weighted_duration',
'_is_first_window',
'_is_first_process',
'_repetition_chance',
'_initial_repetitions',
'_final_repetitions',
'_initial_repetitions_counter',
'_final_repetitions_counter',
'_disable_rewrite_meter',
'_omit_time_signatures',
'_use_multimeasure_rests',
'_boundary_depth',
'_maximum_dot_count',
'_rewrite_tuplets',
'_fade_in_first',
'_fade_out_last',
)
### INITIALISER ###
[docs] def __init__(self,
fade_out_contents: abjad.Container,
fade_in_contents: abjad.Container,
*,
fade_in_first: bool = False,
fade_out_last: bool = False,
initial_repetitions: int = 1,
final_repetitions: int = 1,
repetition_chance: float = 0.0,
weighted_duration: bool = False,
disable_rewrite_meter: bool = False,
omit_time_signatures: bool = False,
use_multimeasure_rests: bool = True,
boundary_depth: Optional[int] = None,
maximum_dot_count: Optional[int] = None,
rewrite_tuplets: bool = True,
) -> None:
r'Initialises self.'
if not isinstance(fade_out_contents, abjad.Container):
raise TypeError("'fade_out_contents' must be 'abjad.Container' or "
"child class")
if not isinstance(fade_in_contents, abjad.Container):
raise TypeError("'fade_in_contents' must be 'abjad.Container' or "
"child class")
self._fade_out_contents = fade_out_contents
self._fade_in_contents = fade_in_contents
self._fader_out = Fader(self._fade_out_contents, mode='out')
self._fader_in = Fader(self._fade_in_contents, mode='in')
self._faders = (self._fader_in, self._fader_out)
self._is_first_window = True
self._is_first_process = True
self._initial_repetitions_counter = 0
self._final_repetitions_counter = 0
self.fade_in_first = fade_in_first
self.fade_out_last = fade_out_last
self.disable_rewrite_meter = disable_rewrite_meter
self.omit_time_signatures = omit_time_signatures
self.use_multimeasure_rests = use_multimeasure_rests
self.boundary_depth = boundary_depth
self.maximum_dot_count = maximum_dot_count
self.rewrite_tuplets = rewrite_tuplets
self.initial_repetitions = initial_repetitions
self.final_repetitions = final_repetitions
self.repetition_chance = repetition_chance
self.weighted_duration = weighted_duration
### SPECIAL METHODS ###
[docs] def __repr__(self) -> str:
r'Returns interpreter representation of both contents.'
string = abjad.lilypond(self._fader_out)
string += '\n'
string += abjad.lilypond(self._fader_in)
return string
[docs] def __len__(self) -> int:
r'Returns the sum of the number of notes of both contents.'
return len(self._fader_in) + len(self._fader_out)
[docs] def __call__(self) -> tuple[abjad.Selection]:
r"""Calls the cross fading process, returning a :obj:`tuple` of
|abjad.Selection| objects.
"""
self._cross_fade_process()
return self.current_window
[docs] def __next__(self) -> tuple[abjad.Selection]:
r"""Calls the cross fading process for one iteration, returning a
:obj:`tuple` of |abjad.Selection| objects.
"""
try:
return self.__call__()
except RuntimeError:
raise StopIteration
[docs] def __iter__(self) -> None:
r'Returns an iterator, allowing instances to be used as iterators.'
return self
### PUBLIC METHODS ###
[docs] def output_all(self) -> tuple[abjad.Selection]:
r"""Goes through the whole fading process and outputs a tuple of two
|abjad.Selection| objects.
"""
self.reset()
dummy_container_a = abjad.Container()
dummy_container_b = abjad.Container()
while True:
try:
result_a, result_b = self.__call__()
dummy_container_a.extend(result_a)
dummy_container_b.extend(result_b)
except RuntimeError:
break
mutate.remove_repeated_time_signatures(dummy_container_a[:])
mutate.remove_repeated_time_signatures(dummy_container_b[:])
mutate.remove_repeated_dynamics(dummy_container_a[:])
mutate.remove_repeated_dynamics(dummy_container_b[:])
output = (dummy_container_a[:], dummy_container_b[:])
dummy_container_a[:] = []
dummy_container_b[:] = []
return output
[docs] def output_n(self,
n: int,
) -> tuple[abjad.Selection]:
r"""Goes through ``n`` iterations of the fading process and outputs a
tuple of two |abjad.Selection| objects.
"""
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'")
self.reset()
dummy_container_a = abjad.Container()
dummy_container_b = abjad.Container()
for _ in range(n):
try:
result_a, result_b = self.__call__()
dummy_container_a.extend(result_a)
dummy_container_b.extend(result_b)
except RuntimeError:
break
mutate.remove_repeated_time_signatures(dummy_container_a[:])
mutate.remove_repeated_time_signatures(dummy_container_b[:])
mutate.remove_repeated_dynamics(dummy_container_a[:])
mutate.remove_repeated_dynamics(dummy_container_b[:])
output = (dummy_container_a[:], dummy_container_b[:])
dummy_container_a[:] = []
dummy_container_b[:] = []
return output
[docs] def reset(self) -> None:
r'Resets to the initial state.'
self._is_first_window = True
self._is_first_process = True
self._initial_repetitions_counter = 0
self._final_repetitions_counter = 0
self._fader_in.reset_mask()
self._fader_out.reset_mask()
### PRIVATE METHODS ###
def _cross_fade_process(self) -> None:
r'Processes both faders according to the cross fade process.'
if self._is_first_window:
self._fader_out()
self._fader_in()
self._initial_repetitions_counter += 1
self._is_first_window = False
elif self._initial_repetitions_counter < self._initial_repetitions:
self._initial_repetitions_counter += 1
elif self._done:
if self._final_repetitions_counter < self._final_repetitions - 1:
self._final_repetitions_counter += 1
else:
raise RuntimeError("both faders have been exhausted")
else:
if (self._is_first_process or self._repetition_chance == 0.0
or random.random() > self._repetition_chance):
if self._fade_in_first and self._is_first_process:
self._fader_in()
self._is_first_process = False
elif (self._fade_out_last and sum(self._fader_out._mask) == 1
and not self._fader_in._done):
self._fader_in()
else:
try:
random_fader = random.choices(self._faders,
weights=self._weights,
)[0]
random_fader()
except RuntimeError:
other_fader = self._faders[0]
if other_fader is random_fader:
other_fader = self._faders[1]
other_fader()
finally:
self._is_first_process = False
def _get_lilypond_format(self) -> str:
r'Returns interpreter representation of :attr:`contents`.'
return self.__repr__()
### PUBLIC PROPERTIES ###
@property
def fade_out_contents(self) -> abjad.Container:
r'The |abjad.Container| to be faded out.'
return abjad.mutate.copy(self._fade_out_contents)
@fade_out_contents.setter
def fade_out_contents(self,
fade_out_contents: abjad.Container,
) -> None:
if not isinstance(fade_out_contents, abjad.Container):
raise TypeError("'fade_out_contents' must be 'abjad.Container' or "
"child class")
leaves = abjad.select(fade_out_contents).leaves()
if not leaves.are_contiguous_logical_voice():
raise ValueError("'fade_out_contents' must be contiguous logical "
"voice")
self._fade_out_contents = abjad.mutate.copy(fade_out_contents)
self._fader_out.contents = self._fade_out_contents
self.reset()
@property
def fade_in_contents(self) -> abjad.Container:
r'The |abjad.Container| to be faded in.'
return abjad.mutate.copy(self._fade_in_contents)
@fade_in_contents.setter
def fade_in_contents(self,
fade_in_contents: abjad.Container,
) -> None:
if not isinstance(fade_in_contents, abjad.Container):
raise TypeError("'fade_in_contents' must be 'abjad.Container' or "
"child class")
leaves = abjad.select(fade_in_contents).leaves()
if not leaves.are_contiguous_logical_voice():
raise ValueError("'fade_in_contents' must be contiguous logical "
"voice")
self._fade_in_contents = abjad.mutate.copy(fade_in_contents)
self._fader_in.contents = self._fade_in_contents
self.reset()
@property
def current_window(self) -> tuple[abjad.Selection]:
r"""Read-only property, returns the result of the last operation as a
:obj:`tuple` of |abjad.Selection| objects.
"""
return (self._fader_out.current_window, self._fader_in.current_window)
@property
def weighted_duration(self) -> bool:
r"""Weights the choice of fader according to its number of notes and
total duration.
"""
return self._weighted_duration
@weighted_duration.setter
def weighted_duration(self,
weighted_duration: bool,
) -> None:
if not isinstance(weighted_duration, bool):
raise TypeError("'weighted_duration' must be 'bool'")
self._weighted_duration = weighted_duration
if self._weighted_duration:
self._weights = []
for container in [self._fade_in_contents, self._fade_out_contents]:
notes = abjad.select(container).logical_ties(pitched=True)
duration = abjad.get.duration(container)
value = len(notes) * float(duration)
self._weights.append(value)
else:
self._weights = [1.0, 1.0]
@property
def repetition_chance(self) -> float:
r"""The chance of not processing neither :attr:`fade_in_contents` not
:attr:`fade_out_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
@property
def initial_repetitions(self) -> int:
r"""The number of times the initial containers are repeated before the
cross fade process starts.
"""
return self._initial_repetitions
@initial_repetitions.setter
def initial_repetitions(self,
initial_repetitions: int,
) -> None:
if not isinstance(initial_repetitions, int):
raise TypeError("'initial_repetitions' must be 'int'")
if initial_repetitions < 1:
raise ValueError("'initial_repetitions' must be greater than zero")
self._initial_repetitions = initial_repetitions
@property
def final_repetitions(self) -> int:
r"""The number of times the final containers are repeated after the
cross fade process ends.
"""
return self._final_repetitions
@final_repetitions.setter
def final_repetitions(self,
final_repetitions: int,
) -> None:
if not isinstance(final_repetitions, int):
raise TypeError("'final_repetitions' must be 'int'")
if final_repetitions < 1:
raise ValueError("'final_repetitions' must be greater than zero")
self._final_repetitions = final_repetitions
@property
def initial_repetitions_counter(self) -> int:
r"""Read-only property, returns the counter of
:attr:`initial_repetitions`.
"""
return self._initial_repetitions_counter
@property
def final_repetitions_counter(self) -> int:
r"""Read-only property, returns the counter of
:attr:`final_repetitions`.
"""
return self._final_repetitions_counter
@property
def fade_in_first(self) -> bool:
r"""When ``True``, the first note of the fade in content will be added
before a note from the fade out content is removed.
"""
return self._fade_in_first
@fade_in_first.setter
def fade_in_first(self,
fade_in_first: bool,
) -> None:
if not isinstance(fade_in_first, bool):
raise TypeError("'fade_in_first' must be 'bool'")
self._fade_in_first = fade_in_first
@property
def fade_out_last(self) -> bool:
r"""When ``True``, the last note of the fade out content will be
removed only after the full fade in content is added.
"""
return self._fade_out_last
@fade_out_last.setter
def fade_out_last(self,
fade_out_last: bool,
) -> None:
if not isinstance(fade_out_last, bool):
raise TypeError("'fade_out_last' must be 'bool'")
self._fade_out_last = fade_out_last
@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
self._fader_in.disable_rewrite_meter = disable_rewrite_meter
self._fader_out.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
self._fader_in.omit_time_signatures = omit_time_signatures
self._fader_out.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
self._fader_in.use_multimeasure_rests = use_multimeasure_rests
self._fader_out.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
self._fader_in.boundary_depth = boundary_depth
self._fader_out.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
self._fader_in.maximum_dot_count = maximum_dot_count
self._fader_out.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
self._fader_in.rewrite_tuplets = rewrite_tuplets
self._fader_out.rewrite_tuplets = rewrite_tuplets
### PRIVATE PROPERTIES ###
@property
def _done(self) -> bool:
r""":obj:`bool` indicating whether the process is done, which is when
both faders are done.
"""
return all(fader._done for fader in self._faders)