Source code for auxjad.core.CrossFader

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)