"""
Handler classes.
"""
import statistics
import abjad
import quicktions
from abjadext import microtones
from . import sequence
from .pitch import JIPitch, return_cent_markup, tune_to_ratio
[docs]class Handler:
"""
Handler Base Class
"""
[docs] def __repr__(self):
return abjad.StorageFormatManager(self).get_repr_format()
[docs]class ArticulationHandler(Handler):
r"""
Articulation Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 r4 c'4 c'4 c'4 c'4 c'4")
>>> art_lst = [
... "staccato",
... "tenuto",
... "staccatissimo",
... "open",
... "halfopen",
... "stopped",
... "portato",
... "tremolo"
... ]
>>> handler = evans.ArticulationHandler(
... articulation_list=art_lst,
... articulation_boolean_vector=[1],
... vector_forget=False,
... forget=False,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
- \staccato
c'4
- \tenuto
c'4
- \staccatissimo
r4
c'4
- \open
c'4
- \halfopen
c'4
- \stopped
c'4
- \portato
c'4
:32
}
"""
def __init__(
self,
articulation_list=None,
articulation_boolean_vector=(1,),
vector_forget=False,
forget=True,
count=-1,
vector_count=-1,
name="Articulation Handler",
):
self.articulation_list = articulation_list
self.vector_forget = vector_forget
self.forget = forget
self._count = count
self._vector_count = vector_count
self.articulation_boolean_vector = sequence.CyclicList(
articulation_boolean_vector, self.vector_forget, self._vector_count
)
self._cyc_articulations = sequence.CyclicList(
lst=articulation_list, forget=self.forget, count=self._count
)
self.name = name
[docs] def __call__(self, selections):
self.add_articulations(selections)
[docs] def add_articulations(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
articulations = self._cyc_articulations(r=len(ties))
vector = self.articulation_boolean_vector(r=len(ties))
for tie, articulation, bool in zip(ties, articulations, vector):
if bool == 1:
if self.articulation_list is not None:
if articulation == "tremolo":
for leaf in tie:
if abjad.get.duration(leaf) <= abjad.Duration(1, 32):
continue
else:
abjad.attach(abjad.StemTremolo(32), leaf)
elif articulation == "default":
continue
else:
abjad.attach(abjad.Articulation(articulation), tie[0])
else:
continue
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("count", self._cyc_articulations.state()),
("vector_count", self.articulation_boolean_vector.state()),
]
)
[docs]class BendHandler(Handler):
r"""
Bend Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.BendHandler(
... bend_amounts=[1, 1.5],
... bend_forget=False,
... boolean_vector=[1, 1, 0, 1],
... vector_forget=False,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
- \bendAfter #'1
c'4
- \bendAfter #'1.5
c'4
c'4
- \bendAfter #'1.5
}
"""
def __init__(
self,
bend_amounts=(1,),
bend_forget=False,
boolean_vector=(1,),
vector_forget=False,
bend_count=-1,
vector_count=-1,
name="Bend Handler",
):
self._bend_count = bend_count
self._vector_count = vector_count
self.bend_forget = bend_forget
self.vector_forget = vector_forget
self.bend_amounts = sequence.CyclicList(
bend_amounts, self.bend_forget, self._bend_count
)
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.vector_forget, self._vector_count
)
self.name = name
[docs] def __call__(self, selections):
self.add_bend(selections)
[docs] def add_bend(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
vector = self.boolean_vector(r=len(ties))
amounts = self.bend_amounts(r=len(ties))
for tie, bool, amount in zip(ties, vector, amounts):
if bool == 1:
abjad.attach(abjad.BendAfter(amount), tie[-1])
else:
continue
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("bend_count", self.bend_amounts.state()),
("vector_count", self.boolean_vector.state()),
]
)
[docs]class BisbigliandoHandler(Handler):
r"""
Bisbigliando Handler
.. container:: example
>>> s = abjad.Staff("c''4 c''4 c''4 c''4")
>>> m = [
... r"\markup {",
... r"\lower #1.5",
... r"\override #'(graphical . #t)",
... r"\override #'(size . 0.4)",
... r"\override #'(thickness . 0.25)",
... r"\woodwind-diagram",
... r"#'flute",
... r"#'((cc . (one two three four five six)) (lh . (bes b gis)) (rh . (bes d dis ees cis c gz)))",
... r"}",
... ]
>>> handler = evans.BisbigliandoHandler(
... fingering_list=[None, m],
... boolean_vector=[1],
... staff_padding=2,
... forget=False,
... )
>>> handler(s[:-1])
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
c''4
- \tweak padding #2
- \tweak staff-padding #2
- \tweak bound-details.right.padding #2
- \tweak bound-details.left.text
\markup{ \raise #1 \teeny \musicglyph #"scripts.halfopenvertical" }
\startTrillSpan
c''4
\stopTrillSpan
- \tweak padding #2
- \tweak staff-padding #2
- \tweak bound-details.right.padding #2
- \tweak bound-details.left.text
\markup {
\lower #1.5
\override #'(graphical . #t)
\override #'(size . 0.4)
\override #'(thickness . 0.25)
\woodwind-diagram
#'flute
#'((cc . (one two three four five six)) (lh . (bes b gis)) (rh . (bes d dis ees cis c gz)))
}
\startTrillSpan
c''4
\stopTrillSpan
- \tweak padding #2
- \tweak staff-padding #2
- \tweak bound-details.right.padding #2
- \tweak bound-details.left.text
\markup{ \raise #1 \teeny \musicglyph #"scripts.halfopenvertical" }
\startTrillSpan
c''4
\stopTrillSpan
}
"""
def __init__(
self,
*,
bis_count=-1,
bool_count=-1,
boolean_vector=None,
forget=False, # forget=None
fingering_list=None,
name=None,
padding=2,
right_padding=2,
staff_padding=2,
):
self.bis_count = bis_count
self.bool_count = bool_count
if boolean_vector is None:
boolean_vector = [1]
self.boolean_vector = sequence.CyclicList(
boolean_vector,
forget=forget,
count=bool_count,
)
self.forget = forget
if fingering_list is None:
fingering_list = [None]
self.fingering_list = sequence.CyclicList(
fingering_list,
forget=forget,
count=bis_count,
)
self.name = name
self.padding = padding
self.right_padding = right_padding
self.staff_padding = staff_padding
[docs] def __call__(self, selections):
self.add_spanner(selections)
def _make_start_literal(self):
markup = r'\markup{ \raise #1 \teeny \musicglyph #"scripts.halfopenvertical" }'
start_literal = abjad.LilyPondLiteral(
[
fr"- \tweak padding #{self.padding}",
fr"- \tweak staff-padding #{self.staff_padding}",
fr"- \tweak bound-details.right.padding #{self.right_padding}",
fr"- \tweak bound-details.left.text {markup}",
r"\startTrillSpan",
],
format_slot="after",
)
return start_literal
def _make_start_literal_pre(self):
start_literal_pre = abjad.LilyPondLiteral(
[
fr"- \tweak padding #{self.padding}",
fr"- \tweak staff-padding #{self.staff_padding}",
fr"- \tweak bound-details.right.padding #{self.right_padding}",
r"- \tweak bound-details.left.text",
],
format_slot="after",
)
return start_literal_pre
def _treat_tie(self, value, tie):
if value != 1:
return
fingering = self.fingering_list(r=1)[0]
if fingering is None:
start_literal = self._make_start_literal()
stop_literal = abjad.LilyPondLiteral(r"\stopTrillSpan", format_slot="after")
abjad.attach(start_literal, tie[0])
abjad.attach(stop_literal, abjad.get.leaf(tie[-1], 1))
return
start_literal_pre = self._make_start_literal_pre()
start_literal = abjad.LilyPondLiteral(fingering, format_slot="after")
start_literal_post = abjad.LilyPondLiteral(
r"\startTrillSpan", format_slot="after"
)
stop_literal = abjad.LilyPondLiteral(r"\stopTrillSpan", format_slot="after")
abjad.attach(start_literal_pre, tie[0])
abjad.attach(start_literal, tie[0])
abjad.attach(start_literal_post, tie[0])
abjad.attach(stop_literal, abjad.get.leaf(tie[-1], 1))
[docs] def add_spanner(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
values = self.boolean_vector(r=len(ties))
for value, tie in zip(values, ties):
self._treat_tie(value, tie)
[docs] def state(self):
state_dict = abjad.OrderedDict()
state_dict["boolean_vector_count"] = self.boolean_vector.count
state_dict["fingering_list_count"] = self.fingering_list.count
return state_dict
[docs]class BowAngleHandler(Handler):
r"""
Bow Angle Handler: In Progress
.. container:: example
>>> s = abjad.Staff("c'2 c'2 c'2 c'2 r2 r2")
>>> handler = evans.BowAngleHandler([0, 45, 0, -45])
>>> handler(s)
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=[
... "abjad.ily",
... "/Users/evansdsg2/evans/lilypond/evans-markups.ily",
... "/Users/evansdsg2/evans/lilypond/evans-spanners.ily",
... ],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
c'2
- \abjad-solid-line-with-arrow
- \evans-clockwise-BAD-spanner-left-text #0
- \tweak bound-details.right.padding 1.4
- \tweak staff-padding #2
\evansStartTextSpanBAD
c'2
\evansStopTextSpanBAD
- \abjad-solid-line-with-arrow
- \evans-counterclockwise-BAD-spanner-left-text #45
- \tweak bound-details.right.padding 1.4
- \tweak staff-padding #2
\evansStartTextSpanBAD
c'2
\evansStopTextSpanBAD
- \abjad-solid-line-with-arrow
- \evans-counterclockwise-BAD-spanner-left-text #0
- \tweak bound-details.right.padding 1.4
- \tweak staff-padding #2
\evansStartTextSpanBAD
c'2
\evansStopTextSpanBAD
- \abjad-solid-line-with-arrow
- \evans-clockwise-BAD-spanner-left-text #-45
- \evans-BAD-spanner-right-text #0
- \tweak bound-details.right.padding 1.4
- \tweak staff-padding #2
\evansStartTextSpanBAD
r2
\evansStopTextSpanBAD
r2
}
"""
def __init__(
self,
angles=(0, 45),
):
self.agles = angles
self._cyc_angles = sequence.CyclicList(angles, forget=False)
[docs] def __call__(self, selections):
self._add_spanners(selections)
def _return_arc_direction(self, left_number, right_number):
if left_number < right_number:
return "clockwise"
else:
return "counterclockwise"
def _add_spanners(self, selections):
for run in abjad.select(selections).runs():
numbers = self._cyc_angles(r=len(run) + 1)
first_leaf = abjad.select(run).leaf(0)
start_literal = abjad.LilyPondLiteral(
[
r"- \abjad-solid-line-with-arrow",
rf"- \evans-{self._return_arc_direction(numbers[0], numbers[1])}-BAD-spanner-left-text #{numbers[0]}",
r"- \tweak bound-details.right.padding 1.4",
r"- \tweak staff-padding #2",
r"\evansStartTextSpanBAD",
],
format_slot="absolute_after",
)
abjad.attach(start_literal, first_leaf)
for i, tie in enumerate(abjad.select(run).logical_ties()[1:-1]):
literal = abjad.LilyPondLiteral(
[
r"\evansStopTextSpanBAD",
r"- \abjad-solid-line-with-arrow",
rf"- \evans-{self._return_arc_direction(numbers[i + 1], numbers[i + 2])}-BAD-spanner-left-text #{numbers[i + 1]}",
r"- \tweak bound-details.right.padding 1.4",
r"- \tweak staff-padding #2",
r"\evansStartTextSpanBAD",
],
format_slot="absolute_after",
)
abjad.attach(literal, tie[0])
terminating_literal = abjad.LilyPondLiteral(
[
r"\evansStopTextSpanBAD",
r"- \abjad-solid-line-with-arrow",
rf"- \evans-{self._return_arc_direction(numbers[-2], numbers[-1])}-BAD-spanner-left-text #{numbers[-2]}",
rf"- \evans-BAD-spanner-right-text #{numbers[-1]}",
r"- \tweak bound-details.right.padding 1.4",
r"- \tweak staff-padding #2",
r"\evansStartTextSpanBAD",
],
format_slot="absolute_after",
)
abjad.attach(terminating_literal, abjad.select(run).leaf(-1))
last_leaf = abjad.get.leaf(abjad.select(run).leaf(-1), 1)
stop_literal = abjad.LilyPondLiteral(
r"\evansStopTextSpanBAD", format_slot="absolute_after"
)
abjad.attach(stop_literal, last_leaf)
[docs] def state(self):
return "State not yet maintained."
# add shelf for ottava to ensure that no notes in the bracket are illegible
[docs]class ClefHandler(Handler):
r"""
Clef Handler
.. container:: example
>>> s = abjad.Staff("c,4 c'4 c4 c''4 c''''8 r8")
>>> handler = evans.ClefHandler(
... clef="bass",
... add_extended_clefs=True,
... add_ottavas=True,
... )
>>> handler(s)
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs
>>> print(abjad.lilypond(s))
\new Staff
{
\clef "bass"
c,4
c'4
c4
\clef "tenorvarC"
c''4
\ottava 1
\clef "treble"
c''''8
\ottava 0
r8
}
"""
_clef_groups_up = {
"bass": ("bass", "tenorvarC", "treble"), # "treble^8", "treble^15"),
"tenor": ("tenorvarC", "treble"), # "treble^8", "treble^15"),
"alto": ("varC", "treble"), # "treble^8", "treble^15"),
"treble": ("treble",), # "treble^8", "treble^15"),
}
_clef_groups_down = {
"bass": ("bass", "bass_8", "bass_15"),
"tenor": ("tenorvarC", "bass", "bass_8"),
"alto": ("varC", "bass", "bass_8"),
"treble": ("treble", "treble_8", "bass"),
}
_default_clef_shelves = {
"bass": (-28, 6),
"tenor": (-10, 12),
"tenorvarC": (-10, 12),
"alto": (-12, 18),
"varC": (-12, 18),
"treble": (-5, 24),
"treble^8": (7, 36),
"treble^15": (19, 48),
}
def __init__(
self,
clef=None,
clef_shelf=None,
allowable_clefs=None,
add_extended_clefs=False,
ottava_shelf=None,
add_ottavas=False,
extend_in_direction="up",
):
self.clef = clef
self.clef_shelf = clef_shelf
self.allowable_clefs = allowable_clefs
self.add_extended_clefs = add_extended_clefs
self.ottava_shelf = ottava_shelf
self.add_ottavas = add_ottavas
self.extend_in_direction = extend_in_direction
if self.clef_shelf is not None:
self._default_clef_shelves[self.clef] = self.clef_shelf
[docs] def __call__(self, voice):
self._add_clefs(voice)
self._add_ottavas(voice)
def _extended_range_clefs(self, clef):
if self.extend_in_direction == "down":
return self._clef_groups_down[clef]
else:
return self._clef_groups_up[clef]
def _extended_range_ottavas(self, clef):
return self._default_clef_shelves[clef]
def _add_clefs(self, voice): # allow the beginning of a run to ignore active clef
clef = self.clef
if clef is not None:
base_clef = self.clef
first_clef_name = self._extended_range_clefs(clef)[0]
clef_list = [abjad.Clef(first_clef_name)]
abjad.attach(clef_list[0], abjad.select(voice).leaves()[0])
if self.add_extended_clefs is True:
allowable_clefs = None
if self.allowable_clefs is not None:
allowable_clefs = self.allowable_clefs
else:
allowable_clefs = self._extended_range_clefs(base_clef)
for tie in abjad.select(voice).logical_ties(pitched=True):
pitches = []
for pitch in abjad.get.pitches(tie[0]):
pitches.append(pitch.number)
pitch = statistics.mean(pitches)
value = None
for count, allowed_clef in enumerate(allowable_clefs):
if clef_list[-1] == abjad.Clef(allowed_clef):
value = count
else:
continue
active_clef_in_list = clef_list[-1]
range_ = self._extended_range_ottavas(active_clef_in_list.name)
active_clef_in_list_shelf = range_
if pitch > active_clef_in_list_shelf[1]:
test_value = value + 1
if test_value < len(allowable_clefs):
temp_clef = allowable_clefs[test_value]
clef = abjad.Clef(temp_clef)
if clef == clef_list[-1]:
continue
if abjad.get.indicator(tie[0], abjad.Clef) is not None:
indicator = abjad.get.indicator(tie[0], abjad.Clef)
abjad.detach(indicator, tie[0])
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
abjad.attach(clef, tie[0])
clef_list.append(clef)
if pitch > self._extended_range_ottavas(temp_clef)[1]:
test_value = value + 2
if test_value < len(allowable_clefs):
temp_clef = allowable_clefs[test_value]
clef = abjad.Clef(temp_clef)
if clef == clef_list[-1]:
continue
indicator = abjad.get.indicator(tie[0], abjad.Clef)
if indicator is not None:
abjad.detach(indicator, tie[0])
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
continue
else:
continue
else:
continue
elif pitch < active_clef_in_list_shelf[0]:
test_value = value - 2
if test_value > -1:
temp_clef = allowable_clefs[test_value]
clef = abjad.Clef(temp_clef)
if clef == clef_list[-1]:
continue
indicator = abjad.get.indicator(tie[0], abjad.Clef)
if indicator is not None:
abjad.detach(indicator, tie[0])
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
abjad.attach(clef, tie[0])
clef_list.append(clef)
if pitch > self._extended_range_ottavas(temp_clef)[1]:
test_value = value - 1
if test_value > -1:
temp_clef = allowable_clefs[test_value]
clef = abjad.Clef(temp_clef)
if clef == clef_list[-1]:
continue
indicator = abjad.get.indicator(tie[0], abjad.Clef)
if indicator is not None:
abjad.detach(indicator, tie[0])
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
continue
else:
continue
else:
test_value = value - 1
if test_value > -1:
temp_clef = allowable_clefs[test_value]
clef = abjad.Clef(temp_clef)
if clef == clef_list[-1]:
continue
indicator = abjad.get.indicator(tie[0], abjad.Clef)
if indicator is not None:
abjad.detach(indicator, tie[0])
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
abjad.attach(clef, tie[0])
clef_list.append(clef)
else:
continue
else:
continue
else:
converted_clef = self._extended_range_clefs(clef)[0]
clef = abjad.Clef(converted_clef)
first_leaf = abjad.select(voice).leaves()[0]
indicator = abjad.get.indicator(first_leaf, abjad.Clef)
if indicator is not None:
abjad.detach(indicator, first_leaf)
abjad.attach(clef, first_leaf)
else:
abjad.attach(clef, first_leaf)
else:
clef = abjad.Clef("treble")
first_leaf = abjad.select(voice).leaves()[0]
abjad.attach(clef, first_leaf)
def _add_ottavas(self, voice):
if self.add_ottavas is True:
if self.allowable_clefs is not None:
active_clef = self.allowable_clefs[-1]
else:
active_clef = self._extended_range_clefs(self.clef)[-1]
for tie in abjad.select(voice).logical_ties(pitched=True):
current_clef = active_clef
if self.ottava_shelf is not None:
shelf = self.ottava_shelf
if self.extend_in_direction == "down":
for pitch in abjad.get.pitches(tie[0]):
if pitch < shelf[0]:
start = abjad.Ottava(n=-1)
stop = abjad.Ottava(n=0)
ottava_indicator = abjad.get.indicator(
tie[0], abjad.Ottava
)
if ottava_indicator is not None:
abjad.detach(ottava_indicator, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
abjad.attach(start, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
for pitch in abjad.get.pitches(tie[0]):
if pitch > shelf[1]:
start = abjad.Ottava(n=1)
stop = abjad.Ottava(n=0)
ottava_indicator = abjad.get.indicator(
tie[0], abjad.Ottava
)
if ottava_indicator is not None:
abjad.detach(ottava_indicator, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
abjad.attach(start, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
shelf = self._extended_range_ottavas(current_clef)
if self.extend_in_direction == "down":
for pitch in abjad.get.pitches(tie[0]):
if pitch < shelf[0]:
start = abjad.Ottava(n=-1)
stop = abjad.Ottava(n=0)
ottava_indicator = abjad.get.indicator(
tie[0], abjad.Ottava
)
if ottava_indicator is not None:
abjad.detach(ottava_indicator, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
abjad.attach(start, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
for pitch in abjad.get.pitches(tie[0]):
if pitch > shelf[1]:
start = abjad.Ottava(n=1)
stop = abjad.Ottava(n=0)
ottava_indicator = abjad.get.indicator(
tie[0], abjad.Ottava
)
if ottava_indicator is not None:
abjad.detach(ottava_indicator, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
abjad.attach(start, tie[0])
abjad.attach(stop, abjad.get.leaf(tie[-1], 1))
else:
pass
[docs]class CompositeHandler(Handler):
r"""
Composite Handler
.. container:: example
>>> durs = [abjad.Duration((4, 4))]
>>> rh = evans.RhythmHandler(evans.RTMMaker(["(1 (1 1 1 1))"]))
>>> ph = evans.PitchHandler([0, 1, 2, 3])
>>> ah = evans.ArticulationHandler(["staccato", "tenuto"])
>>> comp = evans.CompositeHandler(rhythm_handler=rh, attachment_handlers=[ph, ah])
>>> n = comp(durations=durs)
>>> st = abjad.Staff()
>>> st.extend(n)
>>> score = abjad.Score([st])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs
>>> print(abjad.lilypond(st))
\new Staff
{
c'4
- \staccato
cs'4
- \tenuto
d'4
- \staccato
ef'4
- \tenuto
}
"""
def __init__(
self,
rhythm_handler=None,
attachment_handlers=(None,),
name="Composite Handler",
):
self.rhythm_handler = rhythm_handler
self.attachment_handlers = attachment_handlers
self.name = name
[docs] def __call__(
self,
durations=(None,),
selections=None,
):
if self.rhythm_handler is not None:
selections = self._make_container(self.rhythm_handler, durations)
for handler in self.attachment_handlers:
handler(selections)
return selections[:]
def _make_container(self, handler, durations):
selections = handler(durations)
container = abjad.Container([])
container.extend(selections)
return container
[docs] def return_state(self):
return self.rhythm_handler.return_state()
[docs] def state(self):
state_dict = abjad.OrderedDict()
for _ in self.attachment_handlers:
state_dict[_.name] = _.state()
return state_dict
# incorporate spanner anchors
[docs]class DynamicHandler(Handler):
r"""
Dynamic Handler
.. container:: example
>>> staff = abjad.Staff("c'4 d'4 e'4 f'4 r4 g'4 r2")
>>> handler = evans.DynamicHandler(
... dynamic_list=['f', 'niente', 'p', 'mf'],
... flare_boolean_vector=[0, 0, 0, 1],
... flare_forget=False,
... hold_first_boolean_vector=[1, 0, 0,],
... hold_first_forget=False,
... hold_last_boolean_vector=[0, 1],
... hold_last_forget=False,
... effort_boolean_vector=[1, 0],
... effort_forget=False,
... forget=False,
... )
>>> first_group = staff[0:3]
>>> second_group = staff[2:]
>>> handler(first_group)
>>> handler(second_group)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
_ #(make-dynamic-script
(markup
#:whiteout
#:line (
#:general-align Y -2 #:normal-text #:larger "“"
#:hspace -0.4
#:dynamic "f"
#:hspace -0.2
#:general-align Y -2 #:normal-text #:larger "”"
)
)
)
- \tweak stencil #constante-hairpin
\<
d'4
e'4
- \tweak circled-tip ##t
\<
f'4
<>
_ #(make-dynamic-script
(markup
#:whiteout
#:line (
#:general-align Y -2 #:normal-text #:larger "“"
#:hspace -0.1
#:dynamic "p"
#:hspace -0.25
#:general-align Y -2 #:normal-text #:larger "”"
)
)
)
r4
g'4
\mf
- \tweak stencil #constante-hairpin
\<
r2
\!
}
"""
def __init__(
self,
dynamic_list=None,
flare_boolean_vector=[0],
flare_forget=False,
hold_first_boolean_vector=[0],
hold_first_forget=False,
hold_last_boolean_vector=[0],
hold_last_forget=False,
effort_boolean_vector=[0],
effort_forget=False,
with_constante_hairpins=True,
forget=False,
terminating_dynamic_markup=False,
terminating_dynamic_markup_boolean_vector=[1],
count_1=-1,
count_2=-1,
count_3=-1,
count_4=-1,
count_5=-1,
name="Dynamic Handler",
):
self.dynamic_list = dynamic_list
self.flare_boolean_vector = flare_boolean_vector
self.flare_forget = flare_forget
self.hold_first_boolean_vector = hold_first_boolean_vector
self.hold_first_forget = hold_first_forget
self.hold_last_boolean_vector = hold_last_boolean_vector
self.hold_last_forget = hold_last_forget
self.effort_boolean_vector = effort_boolean_vector
self.effort_forget = effort_forget
self.with_constante_hairpins = with_constante_hairpins
self.forget = forget
self.terminating_dynamic_markup = terminating_dynamic_markup
self.terminating_dynamic_markup_boolean_vector = (
terminating_dynamic_markup_boolean_vector
)
self._count_1 = count_1
self._count_2 = count_2
self._count_3 = count_3
self._count_4 = count_4
self._count_5 = count_5
self._cyc_dynamics = sequence.CyclicList(
dynamic_list,
self.forget,
self._count_1,
)
self._cyc_flare_boolean_vector = sequence.CyclicList(
flare_boolean_vector,
self.flare_forget,
self._count_2,
)
self._cyc_hold_first_boolean_vector = sequence.CyclicList(
hold_first_boolean_vector,
self.hold_first_forget,
self._count_3,
)
self._cyc_hold_last_boolean_vector = sequence.CyclicList(
hold_last_boolean_vector,
self.hold_last_forget,
self._count_4,
)
self._cyc_effort_boolean_vector = sequence.CyclicList(
effort_boolean_vector,
self.effort_forget,
self._count_5,
)
self._terminating_dynamic_boolean_vector = sequence.CyclicList(
terminating_dynamic_markup_boolean_vector,
False,
-1,
)
self.name = name
[docs] def __call__(self, selections):
self._apply_dynamics(selections)
def _calculate_hairpin(self, start, stop, flared=0):
if isinstance(start, str):
start = abjad.Dynamic(start)
elif isinstance(start, int):
ord_to_name = abjad.Dynamic.dynamic_ordinal_to_dynamic_name(start)
start = abjad.Dynamic(ord_to_name)
else:
pass
if isinstance(stop, str):
stop = abjad.Dynamic(stop)
elif isinstance(stop, int):
ord_to_name = abjad.Dynamic.dynamic_ordinal_to_dynamic_name(stop)
stop = abjad.Dynamic(ord_to_name)
else:
pass
if flared == 1:
if start.ordinal < stop.ordinal:
if start.name == "niente": # carry these through instead?
start = abjad.Dynamic("niente", hide=True)
hairpin = "o<|"
else:
hairpin = "<|"
else:
if stop.name == "niente":
stop = abjad.Dynamic("niente", command=r"\!")
hairpin = "|>o"
else:
hairpin = "|>"
else:
if start.ordinal < stop.ordinal:
if start.name == "niente":
start = abjad.Dynamic("niente", hide=True)
hairpin = "o<"
else:
hairpin = "<"
else:
if stop.name == "niente":
stop = abjad.Dynamic("niente", command=r"\!")
hairpin = ">o"
else:
hairpin = ">"
return hairpin # , start, stop?
def _make_effort_dynamics(self, dyn):
conversion = {
"ppppp": '"ppppp"',
"pppp": '"pppp"',
"ppp": '"ppp"',
"pp": '"pp"',
"p": '"p"',
"mp": '"mp"',
"mf": '"mf"',
"f": '"f"',
"ff": '"ff"',
"fff": '"fff"',
"ffff": '"ffff"',
"fffff": '"fffff"',
"fp": '"fp"',
"sf": '"sf"',
"sff": '"sff"',
"sp": '"sp"',
"spp": '"spp"',
"sfz": '"sfz"',
"sffz": '"sffz"',
"sfffz": '"sfffz"',
"sffp": '"sffp"',
"sffpp": '"sffpp"',
"sfp": '"sfp"',
"sfpp": '"sfpp"',
"rfz": '"rfz"',
"niente": "niente",
}
return conversion[dyn]
def _apply_dynamics(self, selections):
for run in abjad.select(selections).runs():
hold_first = self._cyc_hold_first_boolean_vector(r=1)[0]
if hold_first == 0:
if len(run) > 1:
if abjad.get.has_indicator(run[0], abjad.Dynamic):
current_dynamic = abjad.get.indicator(run[0], abjad.Dynamic)
start = abjad.Dynamic(current_dynamic, hide=True)
stop = self._cyc_dynamics(r=1)[0]
else:
items = self._cyc_dynamics(r=2)
start = items[0]
stop = items[1]
flare_value = self._cyc_flare_boolean_vector(r=1)[0]
calculated_hairpin = self._calculate_hairpin(
start, stop, flared=flare_value
)
hairpin = abjad.StartHairpin(calculated_hairpin)
hold_last = self._cyc_hold_last_boolean_vector(r=1)[0]
effort_bools = self._cyc_effort_boolean_vector(r=2)
if isinstance(start, str):
if effort_bools[0] == 0:
start = start
else:
start = self._make_effort_dynamics(start)
start = abjad.Dynamic(start)
elif isinstance(start, int):
start = abjad.Dynamic.dynamic_ordinal_to_dynamic_name(start)
if effort_bools[0] == 0:
start = start
else:
start = self._make_effort_dynamics(start)
start = abjad.Dynamic(start)
else:
pass
if isinstance(stop, str):
if effort_bools[1] == 0:
stop = stop
else:
stop = self._make_effort_dynamics(stop)
stop = abjad.Dynamic(stop)
elif isinstance(stop, int):
stop = abjad.Dynamic.dynamic_ordinal_to_dynamic_name(stop)
if effort_bools[1] == 0:
stop = stop
else:
stop = self._make_effort_dynamics(stop)
stop = abjad.Dynamic(stop)
else:
pass
if start.name == "niente":
start = abjad.Dynamic("niente", hide=True)
if stop.name == "niente":
stop = abjad.Dynamic("niente", command=r"\!")
if hold_last == 1:
if stop.name != "niente":
if self.with_constante_hairpins is True:
abjad.attach(abjad.StartHairpin("--"), run[-1])
next_tie_leaf = abjad.get.leaf(run[-1], 1)
abjad.attach(abjad.StopHairpin(), next_tie_leaf)
else: # attach to anchor?
if isinstance(abjad.get.leaf(run[-1], 1), abjad.Rest):
stop = abjad.Dynamic(stop, command=r"\!", leak=True)
else:
pass
else:
if isinstance(abjad.get.leaf(run[-1], 1), abjad.Rest):
stop = abjad.Dynamic(stop, leak=True) # attach to anchor
else:
pass
if abjad.get.has_indicator(run[0], abjad.Dynamic):
abjad.attach(abjad.StopHairpin(), run[0])
abjad.attach(hairpin, run[0])
abjad.attach(stop, run[-1])
else:
abjad.hairpin([start, hairpin, stop], run)
if self.terminating_dynamic_markup is True: # NEW
markup_val = self._terminating_dynamic_boolean_vector(r=1)[
0
]
if markup_val == 1:
if start.ordinal < stop.ordinal:
mark_text = abjad.Markup(
fr"""\markup {{ \override #'(style . "box") \override #'(box-padding . 0.5) \italic \box \whiteout \small "cresc. a {stop.name}" }}""",
direction=abjad.Down,
literal=True,
)
else:
mark_text = abjad.Markup(
fr"""\markup {{ \override #'(style . "box") \override #'(box-padding . 0.5) \italic \box \whiteout \small "dim. a {stop.name}" }}""",
direction=abjad.Down,
literal=True,
)
abjad.attach(mark_text, abjad.select(run).leaf(0))
else:
hold_last = self._cyc_hold_last_boolean_vector(r=1)[0]
if hold_last == 1:
start = self._cyc_dynamics(r=1)[0]
if start == "niente":
start = self._cyc_dynamics(r=1)[0]
else:
pass
if self._cyc_effort_boolean_vector(r=1)[0] == 0:
start = abjad.Dynamic(start)
else:
start_string = self._make_effort_dynamics(start)
start = abjad.Dynamic(start_string)
sustain = abjad.StartHairpin("--")
next_leaf = abjad.get.leaf(run[-1], 1)
abjad.attach(start, run[0])
if self.with_constante_hairpins is True:
abjad.attach(sustain, run[0])
if isinstance(
next_leaf, (abjad.Rest, abjad.MultimeasureRest)
):
abjad.attach(abjad.StopHairpin(), next_leaf)
else:
items = self._cyc_dynamics(r=2)
effort_bools = self._cyc_effort_boolean_vector(r=2)
start = items[0]
stop = items[1]
if effort_bools[0] == 0:
if start == "niente":
start = abjad.Dynamic(start, hide=True)
else:
start = abjad.Dynamic(start)
else:
start_string = self._make_effort_dynamics(start)
if start_string == "niente":
start = abjad.Dynamic(start_string, hide=True)
else:
start = abjad.Dynamic(start_string)
if effort_bools[1] == 0:
if stop == "niente":
stop = abjad.Dynamic(
stop, command=r"\!", leak=True
) # attach to anchor
else:
stop = abjad.Dynamic(
stop, leak=True
) # attach to anchor
else:
stop_string = self._make_effort_dynamics(stop)
if stop_string == "niente":
stop = abjad.Dynamic(
stop_string,
command=r"\!",
leak=True, # attach to anchor
)
else:
stop = abjad.Dynamic(
stop_string, leak=True
) # attach to anchor
flare_value = self._cyc_flare_boolean_vector(r=1)[0]
calculated_hairpin = self._calculate_hairpin(
start,
stop,
flared=flare_value,
)
hairpin = abjad.StartHairpin(calculated_hairpin)
abjad.hairpin([start, hairpin, stop], run)
if self.terminating_dynamic_markup is True: # NEW
markup_val = self._terminating_dynamic_boolean_vector(r=1)[
0
]
if markup_val == 1:
if start.ordinal < stop.ordinal:
mark_text = abjad.Markup(
fr"""\markup {{ \override #'(style . "box") \override #'(box-padding . 0.5) \italic \box \whiteout \small "cresc. a {stop.name}" }}""",
direction=abjad.Down,
literal=True,
)
else:
mark_text = abjad.Markup(
fr"""\markup {{ \override #'(style . "box") \override #'(box-padding . 0.5) \italic \box \whiteout \small "dim. a {stop.name}" }}""",
direction=abjad.Down,
literal=True,
)
abjad.attach(mark_text, abjad.select(run).leaf(0))
else:
start = self._cyc_dynamics(r=1)[0]
if start == "niente":
start = self._cyc_dynamics(r=1)[0]
effort_bool = self._cyc_effort_boolean_vector(r=1)[0]
if effort_bool == 1:
start_string = self._make_effort_dynamics(start)
start = abjad.Dynamic(start_string)
else:
start = abjad.Dynamic(start)
hairpin = abjad.StartHairpin("--")
stopper = abjad.StopHairpin()
next_leaf = abjad.get.leaf(run[-1], 1)
abjad.attach(start, run[0])
if self.with_constante_hairpins is True:
abjad.attach(hairpin, run[0])
if isinstance(next_leaf, (abjad.Rest, abjad.MultimeasureRest)):
abjad.attach(stopper, next_leaf)
else:
pass
self._remove_niente(selections)
# attach to anchor?
# maybe just continue instead of replacing?
def _remove_niente(self, selections):
for leaf in abjad.select(selections).leaves():
for dynamic in abjad.get.indicators(leaf, abjad.Dynamic):
if dynamic.name == "niente":
if dynamic.command == r"\!":
abjad.detach(dynamic, leaf)
abjad.attach(
abjad.Dynamic(dynamic, command=r"\!", leak=True), leaf
)
elif dynamic.leak is True:
abjad.detach(dynamic, leaf)
abjad.attach(
abjad.Dynamic(dynamic, command=r"\!", leak=True), leaf
)
else:
abjad.detach(dynamic, leaf)
abjad.attach(abjad.Dynamic(dynamic, hide=True), leaf)
else:
continue
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("count_1", self._cyc_dynamics.state()),
("count_2", self._cyc_flare_boolean_vector.state()),
("count_3", self._cyc_hold_first_boolean_vector.state()),
("count_4", self._cyc_hold_last_boolean_vector.state()),
("count_5", self._cyc_effort_boolean_vector.state()),
]
)
[docs]class GettatoHandler(Handler):
r"""
Gettato Handler
.. container:: example
>>> staff = abjad.Voice("c'4 fs'4 c''4 gqs''4", name="Voice 1")
>>> handler = evans.GettatoHandler(
... number_of_attacks=[4, 5, 6],
... actions=["throw", "drop"],
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\context Voice = "Voice 1"
{
<<
\context Voice = "On_Beat_Grace_Container"
{
\set fontSize = #-4
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
\once \override Beam.grow-direction = #left
\slash
\voiceOne
<
\tweak font-size 0
\tweak transparent ##t
c'
>32 * 4/3
^ \markup { \hspace #1 throw (4)}
[
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c'32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c'32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c'32 * 4/3
]
}
\context Voice = "Voice 1"
{
\voiceTwo
c'4
}
>>
<<
\context Voice = "On_Beat_Grace_Container"
{
\set fontSize = #-4
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
\once \override Beam.grow-direction = #right
\slash
\voiceOne
<
\tweak font-size 0
\tweak transparent ##t
fs'
>32 * 4/3
^ \markup { \hspace #1 drop (5)}
[
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
fs'32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
fs'32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
fs'32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
fs'32 * 4/3
]
}
\context Voice = "Voice 1"
{
\voiceTwo
fs'4
}
>>
<<
\context Voice = "On_Beat_Grace_Container"
{
\set fontSize = #-4
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
\once \override Beam.grow-direction = #left
\slash
\voiceOne
<
\tweak font-size 0
\tweak transparent ##t
c''
>32 * 4/3
^ \markup { \hspace #1 throw (6)}
[
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
c''32 * 4/3
]
}
\context Voice = "Voice 1"
{
\voiceTwo
c''4
}
>>
<<
\context Voice = "On_Beat_Grace_Container"
{
\set fontSize = #-4
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
\once \override Beam.grow-direction = #right
\slash
\voiceOne
<
\tweak font-size 0
\tweak transparent ##t
gqs''
>32 * 4/3
^ \markup { \hspace #1 drop (4)}
[
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
gqs''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
gqs''32 * 4/3
\once \override NoteHead.no-ledgers = ##t
\once \override Accidental.transparent = ##t
\tweak transparent ##t
gqs''32 * 4/3
]
}
\context Voice = "Voice 1"
{
\voiceTwo
gqs''4
}
>>
}
"""
def __init__(
self,
number_of_attacks=[4, 5, 6],
attack_number_forget=False,
actions=["throw", "drop"],
action_forget=False,
boolean_vector=[1],
vector_forget=False,
attack_count=-1,
action_count=-1,
vector_count=-1,
name="Gettato Handler",
):
self._attack_count = attack_count
self._action_count = action_count
self._vector_count = vector_count
self.attack_number_forget = attack_number_forget
self.action_forget = action_forget
self.vector_forget = vector_forget
self.attacks = sequence.CyclicList(
number_of_attacks, self.attack_number_forget, self._attack_count
)
self.actions = sequence.CyclicList(
actions, self.action_forget, self._action_count
)
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.vector_forget, self._vector_count
)
self.name = name
[docs] def __call__(self, selections):
self.add_gettato(selections)
[docs] def add_gettato(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
vector = self.boolean_vector(r=len(ties))
for value, tie in zip(vector, ties):
if value == 1:
repetitions = self.attacks(r=1)[0]
pitches = [_ for _ in abjad.get.pitches(tie[0])]
repeated_pitch = pitches[-1]
list_ = []
list_.append(abjad.Chord([repeated_pitch], (1, 32)))
for _ in range(repetitions - 1):
list_.append(abjad.Note(repeated_pitch, (1, 32)))
sel = abjad.Selection(list_)
abjad.beam(sel)
t = abjad.LilyPondLiteral(
[
r"\once \override NoteHead.no-ledgers = ##t",
r"\once \override Accidental.transparent = ##t",
r"\tweak transparent ##t",
],
format_slot="before",
)
for leaf in abjad.select(sel).leaves():
abjad.attach(t, leaf)
a = self.actions(r=1)[0]
if a == "throw":
literal = abjad.LilyPondLiteral(
r"\once \override Beam.grow-direction = #left",
format_slot="before",
)
abjad.attach(literal, sel[0])
mark = abjad.Markup(
fr"\markup {{ \hspace #1 throw ({repetitions})}}",
direction=abjad.Up,
literal=True,
)
abjad.attach(mark, sel[0])
elif a == "drop":
literal = abjad.LilyPondLiteral(
r"\once \override Beam.grow-direction = #right",
format_slot="before",
)
abjad.attach(literal, sel[0])
mark = abjad.Markup(
fr"\markup {{ \hspace #1 drop ({repetitions})}}",
direction=abjad.Up,
literal=True,
)
abjad.attach(mark, sel[0])
else:
pass
abjad.on_beat_grace_container(
sel,
tie[:],
leaf_duration=(1, 24),
do_not_slur=True,
do_not_beam=True,
font_size=-4,
)
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("attack_count", self.attacks.state()),
("action_count", self.actions.state()),
("vector_count", self.boolean_vector.state()),
]
)
[docs]class GlissandoHandler(Handler):
r"""
Glissando Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.GlissandoHandler(
... line_style="dotted-line",
... boolean_vector=[1],
... forget=False,
... apply_to="runs",
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
- \tweak style #'dotted-line
\glissando
c'4
- \tweak style #'dotted-line
\glissando
c'4
- \tweak style #'dotted-line
\glissando
c'4
}
"""
def __init__(
self,
glissando_style=None,
line_style=None,
boolean_vector=[0],
forget=False,
apply_to="runs",
count=-1,
name="Glissando Handler",
):
self.glissando_style = glissando_style
self.line_style = f"#'{line_style}"
self._count = count
self.forget = forget
self.apply_to = apply_to
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.forget, self._count
)
self.name = name
[docs] def __call__(self, selections):
self.add_glissando(selections)
[docs] def add_glissando(self, selections):
if self.apply_to == "runs":
runs = abjad.select(selections).runs()
if self.glissando_style == "hide_middle_note_heads":
if self.line_style is not None:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
t = abjad.tweak(self.line_style).style
abjad.glissando(run[:], t, hide_middle_note_heads=True)
else:
continue
else:
continue
else:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
abjad.glissando(
run[:],
hide_middle_note_heads=True,
allow_repeats=True,
)
else:
continue
else:
continue
elif self.glissando_style == "hide_middle_stems":
if self.line_style is not None:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
t = abjad.tweak(self.line_style).style
abjad.glissando(
run[:],
t,
hide_middle_note_heads=True,
hide_middle_stems=True,
)
else:
continue
else:
continue
else:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
abjad.glissando(
run[:],
hide_middle_note_heads=True,
hide_middle_stems=True,
)
else:
continue
else:
continue
else:
if self.line_style is not None:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
t = abjad.tweak(self.line_style).style
abjad.glissando(
run[:],
t,
allow_repeats=True,
allow_ties=False,
)
else:
continue
else:
continue
else:
for run in runs:
if self.boolean_vector(r=1)[0] == 1:
if len(run) > 1:
abjad.glissando(
run[:],
allow_repeats=True,
allow_ties=False,
)
else:
continue
else:
continue
else:
ties = abjad.select(selections).logical_ties(pitched=True)
values = self.boolean_vector(r=len(ties))
for value, tie in zip(values, ties):
if value == 1:
abjad.attach(abjad.Glissando(), tie[-1])
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict([("count", self.boolean_vector.state())])
[docs]class GraceHandler(Handler):
r"""
Grace Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.GraceHandler(
... boolean_vector=[0, 1, 0, 1],
... gesture_lengths=[1, 2],
... forget=False,
... )
>>> handler(staff[:])
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
\scaleDurations #'(1 . 1) {
\slashedGrace {
c'16
s8..
s2
}
}
c'4
c'4
\scaleDurations #'(1 . 1) {
\slashedGrace {
\slash
\override Stem.direction = #UP
\override Staff.Stem.stemlet-length = 0
c'16
[
s8..
c'16
\revert Stem.direction
s8..
\revert Staff.Stem.stemlet-length
s2
]
}
}
c'4
}
"""
def __init__(
self,
boolean_vector=None,
gesture_lengths=None,
forget=True,
remove_skips=False,
vector_count=-1,
gesture_count=-1,
name="Grace Handler",
):
self.forget = forget
self.remove_skips = remove_skips
self._vector_count = vector_count
self._gesture_count = gesture_count
self.boolean_vector = boolean_vector
self.gesture_lengths = gesture_lengths
self._cyc_boolean_vector = sequence.CyclicList(
boolean_vector, self.forget, self._vector_count
)
self._cyc_gesture_lengths = sequence.CyclicList(
gesture_lengths, self.forget, self._gesture_count
)
self.name = name
[docs] def __call__(self, selections):
self._add_grace_notes(selections)
def _add_grace_notes(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
vectors = self._cyc_boolean_vector(r=len(ties))
if self.boolean_vector is not None:
for value, tie in zip(vectors, ties):
if value == 1:
grace_list = ""
if self.gesture_lengths is not None:
grace_length = self._cyc_gesture_lengths(r=1)[0]
for x in range(grace_length):
s = "c'16"
grace_list = grace_list + s
grace_list = grace_list + " "
if self.remove_skips is False:
grace_list = grace_list + "s8.."
grace_list = grace_list + " "
if self.remove_skips is False:
grace_list = grace_list + "s2"
grace = abjad.BeforeGraceContainer(
grace_list, command=r"\slashedGrace"
)
if 1 < len(abjad.select(grace).leaves(pitched=True)):
abjad.beam(
grace,
beam_rests=True,
beam_lone_notes=True,
stemlet_length=0,
)
literal_slash = abjad.LilyPondLiteral(
r"\slash", format_slot="before"
)
abjad.attach(
literal_slash,
abjad.select(grace).leaves(pitched=True)[0],
)
direction_override = abjad.LilyPondLiteral(
r"\override Stem.direction = #UP", format_slot="before"
)
direction_revert = abjad.LilyPondLiteral(
r"\revert Stem.direction", format_slot="after"
)
abjad.attach(
direction_override,
abjad.select(grace).leaves(pitched=True)[0],
)
abjad.attach(
direction_revert,
abjad.select(grace).leaves(pitched=True)[-1],
)
open_literal = abjad.LilyPondLiteral(
r"\scaleDurations #'(1 . 1) {", format_slot="before"
)
close_literal = abjad.LilyPondLiteral("}", format_slot="after")
abjad.attach(open_literal, grace)
abjad.attach(close_literal, grace)
abjad.attach(grace, tie[0])
else:
grace = abjad.BeforeGraceContainer(
"c'16", command=r"\slashedGrace"
)
open_literal = abjad.LilyPondLiteral(
r"\scaleDurations #'(1 . 1) {", format_slot="before"
)
close_literal = abjad.LilyPondLiteral("}", format_slot="after")
abjad.attach(open_literal, grace)
abjad.attach(close_literal, grace)
abjad.attach(grace, tie[0])
else:
continue
else:
pass
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("vector_count", self._cyc_boolean_vector.state()),
("gesture_count", self._cyc_gesture_lengths.state()),
]
)
[docs]class IntermittentVoiceHandler(Handler):
r"""
IntermittentVoiceHandler
.. container:: example
>>> ph_up = evans.PitchHandler([8, 8.5, 9, 9.5, 9, 8.5], forget=False)
>>> ph_down = evans.PitchHandler([0, 1, 2, 3, 4, 5], forget=False)
>>> s = abjad.Staff([abjad.Voice("c'4 c'8 c'8 c'8 c'4.", name="Voice1")], name="Staff1")
>>> ph_down(s)
>>> h = evans.RhythmHandler(
... rmakers.stack(
... rmakers.talea(
... [1, 2, 3, 4],
... 8,
... extra_counts=[1, 0, -1],
... ),
... rmakers.trivialize(abjad.select().tuplets()),
... rmakers.extract_trivial(abjad.select().tuplets()),
... rmakers.rewrite_rest_filled(abjad.select().tuplets()),
... rmakers.rewrite_sustained(abjad.select().tuplets()),
... ),
... forget=False,
... )
...
>>> ivh = evans.IntermittentVoiceHandler(h, direction=abjad.Up)
>>> sel1 = abjad.select(s["Voice1"]).leaf(0)
>>> sel2 = abjad.select(s["Voice1"]).leaf(2)
>>> sel3 = abjad.select(s["Voice1"]).leaves().get([3, 4])
>>> ivh(sel1)
>>> ivh(sel2)
>>> ivh(sel3)
>>> ph_up = evans.PitchHandler([8, 8.5, 9, 9.5, 9, 8.5], forget=False)
>>> for voice in abjad.select(s).components(abjad.Voice):
... if voice.name == "intermittent_voice":
... ph_up(voice)
...
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\context Staff = "Staff1"
{
\context Voice = "Voice1"
{
<<
\context Voice = "Voice1"
{
\voiceTwo
c'4
}
\context Voice = "intermittent_voice"
{
\times 2/3 {
\voiceOne
af'8
aqf'4
}
}
>>
\oneVoice
cs'8
<<
\context Voice = "Voice1"
{
\voiceTwo
d'8
}
\context Voice = "intermittent_voice"
{
\voiceOne
a'8
}
>>
\oneVoice
<<
\context Voice = "Voice1"
{
\voiceTwo
ef'8
e'4.
}
\context Voice = "intermittent_voice"
{
\tweak text #tuplet-number::calc-fraction-text
\times 4/3 {
\voiceOne
aqs'4
a'8
}
}
>>
\oneVoice
}
}
"""
def __init__(
self,
rhythm_handler,
direction=abjad.Up,
):
self.rhythm_handler = rhythm_handler
self.direction = direction
[docs] def __call__(
self,
selections,
):
selections = abjad.select(selections)
self._add_voice(selections)
def _add_voice(self, selections):
if self.direction == abjad.Up:
literal1 = abjad.LilyPondLiteral(r"\voiceTwo")
literal2 = abjad.LilyPondLiteral(r"\voiceOne")
else:
literal1 = abjad.LilyPondLiteral(r"\voiceOne")
literal2 = abjad.LilyPondLiteral(r"\voiceTwo")
closing_literal = abjad.LilyPondLiteral(r"\oneVoice", format_slot="after")
duration = [abjad.get.duration(selections[:])]
container = abjad.Container(simultaneous=True)
original_voice = abjad.Voice(name=self._find_parent(selections))
intermittent_voice = abjad.Voice(name="intermittent_voice")
intermittent_voice.append(self._make_components(duration)[:])
abjad.mutate.wrap(selections, original_voice)
abjad.mutate.wrap(original_voice, container)
container.append(intermittent_voice)
abjad.attach(literal1, abjad.select(original_voice).leaf(0))
abjad.attach(literal2, abjad.select(intermittent_voice).leaf(0))
abjad.attach(closing_literal, container)
def _find_parent(self, selections):
first_leaf = abjad.select(selections).leaf(0)
parentage = abjad.get.parentage(first_leaf)
parent_voice = abjad.select(parentage).components(abjad.Voice)
return parent_voice[0].name
def _make_components(self, duration):
return self.rhythm_handler(duration)
[docs]class NoteheadHandler(Handler):
r"""
Notehead Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.NoteheadHandler(
... notehead_list=["default", "harmonic", "triangle", "slash"],
... transition=True,
... head_boolean_vector=[1],
... head_vector_forget=False,
... transition_boolean_vector=[0, 1],
... transition_vector_forget=False,
... forget=True,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
\tweak NoteHead.style #'default
c'4
\tweak NoteHead.style #'harmonic
c'4
- \tweak arrow-length #2
- \tweak arrow-width #0.5
- \tweak bound-details.right.arrow ##t
- \tweak thickness #2.5
\glissando
\tweak NoteHead.style #'triangle
c'4
\tweak NoteHead.style #'slash
c'4
}
"""
def __init__(
self,
notehead_list=None,
transition=False,
head_boolean_vector=[0],
head_vector_forget=False,
transition_boolean_vector=[0],
transition_vector_forget=False,
forget=True,
count=-1,
name="Notehead Handler",
):
self.notehead_list = notehead_list
self.transition = transition
self.head_vector_forget = head_vector_forget
self._head_vector_count = -1
self.head_boolean_vector = sequence.CyclicList(
head_boolean_vector, self.head_vector_forget, self._head_vector_count
)
self.transition_vector_forget = transition_vector_forget
self._transition_vector_count = -1
self.transition_boolean_vector = sequence.CyclicList(
transition_boolean_vector,
self.transition_vector_forget,
self._transition_vector_count,
)
self.forget = forget
self._count = count
self._cyc_noteheads = sequence.CyclicList(
notehead_list, self.forget, self._count
)
self.name = name
[docs] def __call__(self, selections):
self.add_noteheads(selections)
[docs] def add_noteheads(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
heads = self._cyc_noteheads(r=len(ties))
head_vector = self.head_boolean_vector(r=len(ties))
trans_vector = self.transition_boolean_vector(r=len(ties))
if self.notehead_list is not None:
for tie, head, bool in zip(ties, heads, head_vector):
string = str(r"""\tweak NoteHead.style #'""")
full_string = string + head
style = abjad.LilyPondLiteral(full_string, format_slot="opening")
if bool == 1:
for leaf in abjad.select(tie).leaves(pitched=True):
abjad.attach(style, leaf)
else:
continue
if self.transition is True:
transition_arrow = abjad.LilyPondLiteral(
r"""
- \tweak arrow-length #2
- \tweak arrow-width #0.5
- \tweak bound-details.right.arrow ##t
- \tweak thickness #2.5
\glissando
""",
"absolute_after",
) # verify that heads are different?
for tie, bool1, bool2 in zip(ties, head_vector, trans_vector):
if bool1 == 1:
if bool2 == 1:
abjad.attach(transition_arrow, tie[-1])
else:
continue
else:
continue
for run in abjad.select(selections).runs():
last_tie = abjad.select(run).logical_ties(pitched=True)[-1]
abjad.detach(transition_arrow, last_tie[-1])
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("count", self._cyc_noteheads.state()),
("head_vector_count", self.head_boolean_vector.state()),
("transition_vector_count", self.transition_boolean_vector.state()),
]
)
[docs]class OnBeatGraceHandler(Handler):
r"""
On Beat Grace Handler
.. container:: example
>>> grace_handler = evans.OnBeatGraceHandler(
... number_of_attacks=[
... 4,
... 3,
... 4,
... 5,
... 6,
... 3,
... 4,
... 3,
... 4,
... 3,
... 4,
... 5,
... 5,
... 3,
... 4,
... 3,
... ],
... durations=[
... 2,
... 1,
... 1,
... 1,
... 2,
... 1,
... 2,
... 1,
... 1,
... ],
... font_size=-4,
... leaf_duration=(1, 100),
... attack_number_forget=False,
... durations_forget=False,
... boolean_vector=[1],
... vector_forget=False,
... name="On Beat Grace Handler",
... )
...
>>> head_handler = evans.NoteheadHandler(
... ["harmonic"],
... head_boolean_vector=[1],
... forget=False,
... )
...
>>> pitch_handler = evans.PitchHandler(
... [
... 30,
... 32,
... 29.5,
... 31,
... 31.5,
... 33,
... 30,
... 29,
... 32.5,
... ],
... forget=False,
... )
...
>>> s = abjad.Staff([abjad.Voice("e''4 e''2 e''4", name="Voice1")], name="Staff1")
>>> grace_handler(abjad.select(s).leaf(1))
>>> pitch_handler(abjad.select(s).logical_ties(grace=True))
>>> head_handler(abjad.select(s).logical_ties(grace=True))
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\context Staff = "Staff1"
{
\context Voice = "Voice1"
{
e''4
<<
\context Voice = "On_Beat_Grace_Container"
{
\set fontSize = #-4
\slash
\voiceOne
\tweak NoteHead.style #'harmonic
fs'''8
[
(
\tweak NoteHead.style #'harmonic
af'''16
\tweak NoteHead.style #'harmonic
fqs'''16
\tweak NoteHead.style #'harmonic
g'''16
)
]
}
\context Voice = "Voice1"
{
\voiceTwo
e''2
}
>>
\oneVoice
e''4
}
}
"""
def __init__(
self,
number_of_attacks=[4, 5, 6],
durations=[
2,
1,
1,
1,
2,
1,
2,
1,
1,
],
attack_number_forget=False,
durations_forget=False,
font_size=(-4),
leaf_duration=(1, 28),
boolean_vector=[1],
vector_forget=False,
attack_count=-1,
durations_count=-1,
vector_count=-1,
name="On Beat Grace Handler",
):
self.font_size = font_size
self.leaf_duration = leaf_duration
self._attack_count = attack_count
self._durations_count = durations_count
self._vector_count = vector_count
self.attack_number_forget = attack_number_forget
self.durations_forget = durations_forget
self.vector_forget = vector_forget
self.attacks = sequence.CyclicList(
number_of_attacks, self.attack_number_forget, self._attack_count
)
self.durations = sequence.CyclicList(
durations, self.durations_forget, self._durations_count
)
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.vector_forget, self._vector_count
)
self.name = name
[docs] def __call__(self, selections):
self.add_grace(selections)
[docs] def add_grace(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
vector = self.boolean_vector(r=len(ties))
for value, tie in zip(vector, ties):
if value == 1:
repetitions = self.attacks(r=1)[0]
list_ = []
durs = self.durations(r=repetitions)
for _ in durs:
list_.append(abjad.Note("c'", (_, 16)))
sel = abjad.Selection(list_)
abjad.on_beat_grace_container(
sel,
tie[:],
leaf_duration=self.leaf_duration,
do_not_slur=False,
do_not_beam=False,
font_size=self.font_size,
)
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("attack_count", self.attacks.state()),
("vector_count", self.boolean_vector.state()),
]
)
[docs]class PitchHandler(Handler):
r"""
Pitch Handler
.. container:: example
>>> import fractions
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[1, 2, 3, 4],
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
cs'4
d'4
ef'4
e'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[1, [2, 3], 4],
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
cs'4
<d' ef'>4
e'4
cs'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[0, 1, 2.5, 3, 4, 5.5],
... chord_boolean_vector=[0, 1, 1],
... chord_groups=[2, 4],
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
c'4
<cs' dqs'>4
<c' ef' e' fqs'>4
cs'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[0, 1, 1, 3, 4, 5.5],
... allow_chord_duplicates=True,
... chord_boolean_vector=[0, 1, 1],
... chord_groups=[2, 4],
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
c'4
<cs' cs'>4
<c' ef' e' fqs'>4
cs'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[1, fractions.Fraction(9, 4), 3, 4],
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
cs'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-eighth-sharp-markup
d'4
ef'4
e'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[1, [fractions.Fraction(4, 3), 4], 5, fractions.Fraction(37, 6)],
... allow_chord_duplicates=True,
... forget=False,
... )
>>> handler(abjad.select(s).logical_ties())
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
cs'4
<
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-third-flat-markup
df'
e'
>4
f'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \five-twelfths-flat-markup
gf'4
}
.. container:: example
>>> s = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.PitchHandler(
... pitch_list=[
... fractions.Fraction(3, 4),
... [
... fractions.Fraction(25, 2),
... fractions.Fraction(4, 3),
... ],
... 2,
... fractions.Fraction(23, 4),
... ],
... forget=False,
... allow_chord_duplicates=True,
... )
>>> handler(s)
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
c'4
<
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-third-flat-markup
df'
cqs''
>4
d'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
f'4
}
.. container:: example
>>> pitch_set = microtones.PitchSegment([0, Fraction(3, 2), 7, Fraction(19, 4)])
>>> pitch_set = pitch_set + pitch_set.invert(2).multiply(Fraction(5, 4))
>>> pitch_set = pitch_set + pitch_set.retrograde().rotate(3).transpose(Fraction(13, 2))
>>> pitch_set = microtones.PitchSegment([evans.to_nearest_eighth_tone(_) for _ in pitch_set])
>>> notes = [abjad.Note() for _ in pitch_set]
>>> staff = abjad.Staff(notes)
>>> handler = evans.PitchHandler(
... pitch_list=[_ for _ in pitch_set],
... forget=False,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
dqf'4
g'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
e'4
f'4
ef'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-flat-markup
a4
b4
bqs'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-eighth-sharp-markup
b'4
dqf''4
af'4
gqf'4
fqs'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
d'4
aqs'4
}
.. container:: example
>>> pitch_segment = microtones.PitchSegment([0, Fraction(3, 2), 7, Fraction(19, 4)])
>>> pitch_segment = pitch_segment + pitch_segment.invert(2).multiply(Fraction(5, 4))
>>> pitch_segment = pitch_segment + pitch_segment.retrograde().rotate(3).transpose(Fraction(13, 2))
>>> pitch_segment = microtones.PitchSegment([evans.to_nearest_eighth_tone(_) for _ in pitch_segment])
>>> notes = [abjad.Note() for _ in pitch_segment]
>>> staff = abjad.Staff(notes)
>>> handler = evans.PitchHandler(
... pitch_list=[_ for _ in pitch_set],
... apply_all=True,
... forget=False,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-natural-markup
c'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-flat-markup
df'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-natural-markup
g'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
e'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-natural-markup
f'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-flat-markup
ef'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-flat-markup
a4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-natural-markup
b4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-sharp-markup
b'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-eighth-sharp-markup
b'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-flat-markup
df''4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \abjad-flat-markup
af'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-flat-markup
gf'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-sharp-markup
f'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \three-eighths-sharp-markup
d'4
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \one-quarter-sharp-markup
a'4
}
.. container:: example
>>> ratio_segment = microtones.RatioSegment([1, Fraction(3, 2), Fraction(5, 4)])
>>> ratio_segment = ratio_segment + ratio_segment.invert(2).multiply(Fraction(5, 4))
>>> ratio_segment = ratio_segment + ratio_segment.retrograde().rotate(3).transpose(1)
>>> notes = [abjad.Note() for _ in ratio_segment]
>>> staff = abjad.Staff(notes)
>>> handler = evans.PitchHandler(
... pitch_list=[_ for _ in ratio_segment],
... as_ratios=True,
... forget=False,
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score, abjad.Block(name="layout")],
... includes=["ekmelos-ji-accidental-markups.ily"],
... global_staff_size=16,
... )
>>> style = '"dodecaphonic"'
>>> file.layout_block.items.append(fr"\accidentalStyle {style}")
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
c'4
^ \markup \center-align { +0 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
g'4
^ \markup \center-align { +2 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \natural-one-syntonic-comma-down }
e'4
^ \markup \center-align { -14 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \natural-one-syntonic-comma-down }
e'''4
^ \markup \center-align { -14 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \natural-one-syntonic-comma-down }
a''4
^ \markup \center-align { -16 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
c'''4
^ \markup \center-align { +0 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
d''4
^ \markup \center-align { +4 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \natural-one-syntonic-comma-down }
e''4
^ \markup \center-align { -14 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
c''4
^ \markup \center-align { +0 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \natural-one-syntonic-comma-down }
e'''4
^ \markup \center-align { -14 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \one-tridecimal-third-tone-down }
d'''4
^ \markup \center-align { C♯+39 }
\tweak Accidental.stencil #ly:text-interface::print
\tweak Accidental.text \markup { \abjad-natural }
g'''4
^ \markup \center-align { +2 }
}
"""
def __init__( # for apply all add a sharp/flat/none keyword
self,
pitch_list=None,
allow_chord_duplicates=False,
apply_all=False,
apply_all_spelling=None,
as_ratios=False,
chord_boolean_vector=[0],
chord_groups=None,
forget=True,
to_ties=False,
pitch_count=-1,
state=None,
chord_boolean_count=-1,
chord_groups_count=-1,
name="Pitch Handler",
):
self.pitch_list = pitch_list
self.allow_chord_duplicates = allow_chord_duplicates
self.apply_all = apply_all
self.apply_all_spelling = apply_all_spelling
self.as_ratios = as_ratios
self.chord_boolean_vector = chord_boolean_vector
self.chord_groups = chord_groups
self.forget = forget
self.to_ties = to_ties
self.name = name
self._state = state
self._pitch_count = pitch_count
self._chord_boolean_count = chord_boolean_count
self._chord_groups_count = chord_groups_count
if self._state is not None:
self._pitch_count = state["pitch_count"]
self._chord_boolean_count = state["chord_boolean_count"]
self._chord_groups_count = state["chord_groups_count"]
self._cyc_pitches = sequence.CyclicList(
self.pitch_list,
self.forget,
self._pitch_count,
)
self._cyc_chord_boolean_vector = sequence.CyclicList(
self.chord_boolean_vector,
self.forget,
self._chord_boolean_count,
)
self._cyc_chord_groups = sequence.CyclicList(
self.chord_groups,
self.forget,
self._chord_groups_count,
)
[docs] def __call__(self, selections):
if self.to_ties is True:
for tie in selections:
self._apply_pitches(tie)
else:
self._apply_pitches(selections)
def _collect_pitches_durations_leaves(self, logical_ties):
pitches, durations, leaves = [[], [], []]
ties_ = logical_ties
if self.chord_groups is not None:
pitches_ = []
bools = self._cyc_chord_boolean_vector(r=len(ties_))
for tie, bool in zip(ties_, bools):
if 0 < bool:
group_size = self._cyc_chord_groups(r=1)[0]
pitches_.append(self._cyc_pitches(r=group_size))
else:
pitches_.append(self._cyc_pitches(r=1)[0])
else:
pitches_ = self._cyc_pitches(r=len(ties_))
for tie, pitch in zip(ties_, pitches_):
for leaf in tie:
if isinstance(pitch, list):
if self.allow_chord_duplicates is False:
pitch = list(set(pitch))
pitches.append(pitch)
durations.append(leaf.written_duration)
leaves.append(leaf)
return pitches, durations, leaves
def _apply_pitches(self, selections): # get apply all to handler chords
if self.as_ratios is True:
self.apply_all = True
leaf_maker = abjad.LeafMaker()
old_ties = [tie for tie in abjad.iterate(selections).logical_ties(pitched=True)]
if len(old_ties) > 0:
collect = self._collect_pitches_durations_leaves(old_ties)
pitches, durations, old_leaves = collect
microtonal_indices_to_pitch = abjad.OrderedDict()
for i, _ in enumerate(pitches):
if isinstance(_, list):
_.sort()
nested_indices_to_pitch = abjad.OrderedDict()
for i_, sub_ in enumerate(_):
if self.apply_all is False:
if isinstance(sub_, str):
val = abjad.NumberedPitch(sub_).number
else:
val = sub_
if 0 < val % quicktions.Fraction(1, 2):
nested_indices_to_pitch[str(i_)] = sub_
pitches[i][i_] = 0
microtonal_indices_to_pitch[
str(i)
] = nested_indices_to_pitch
else:
nested_indices_to_pitch[str(i_)] = sub_
pitches[i][i_] = 0
microtonal_indices_to_pitch[
str(i)
] = nested_indices_to_pitch
else:
if self.apply_all is False:
if isinstance(_, str):
val = abjad.NumberedPitch(_).number
else:
val = _
if 0 < val % quicktions.Fraction(1, 2):
microtonal_indices_to_pitch[str(i)] = _
pitches[i] = 0
else:
microtonal_indices_to_pitch[str(i)] = _
pitches[i] = 0
for pitch_index, pitch_value in enumerate(
pitches
): # find way to add cent when 1/4 is false
if isinstance(pitch_value, JIPitch):
pitches[pitch_index] = pitch_value.pitch
if self.apply_all is False:
new_leaves = [leaf for leaf in leaf_maker(pitches, durations)]
else:
new_leaves = old_leaves
for i, pair in enumerate(
zip(new_leaves, microtonal_indices_to_pitch.values())
):
leaf, pitch = pair
if isinstance(pitch, abjad.OrderedDict):
replacement_chord = abjad.Chord()
replacement_chord.written_duration = leaf.written_duration
replacement_chord.note_heads = abjad.NoteHeadList(
[abjad.NoteHead(leaf.written_pitch) for _ in pair[1]]
)
indicators = abjad.get.indicators(leaf)
before_grace = abjad.get.before_grace_container(leaf)
for indicator in indicators:
abjad.attach(indicator, replacement_chord)
if before_grace is not None:
abjad.attach(before_grace, replacement_chord)
abjad.mutate.replace(leaf, replacement_chord)
new_leaves[i] = replacement_chord
for index in microtonal_indices_to_pitch:
for leaf in abjad.select(new_leaves[int(index)]).leaves():
leaf_annotation_pitch = [_.hertz for _ in abjad.get.pitches(leaf)]
leaf_annotation_ratio = []
if isinstance(leaf, abjad.Chord):
marks = []
heads = leaf.note_heads
for sub_index in microtonal_indices_to_pitch[index]:
head = heads[int(sub_index)]
if self.as_ratios is False:
microtones.apply_alteration(
head,
microtonal_indices_to_pitch[index][sub_index],
spell=self.apply_all_spelling,
)
else:
ratio = microtonal_indices_to_pitch[index][sub_index]
factors = []
for _ in microtones.ji._prime_factors(
quicktions.Fraction(ratio).numerator
):
factors.append(_)
for _ in microtones.ji._prime_factors(
quicktions.Fraction(ratio).denominator
):
factors.append(_)
over_23 = 0
if 0 < len(factors):
over_23 = max(factors)
if 23 < over_23:
marks.append(return_cent_markup(head, ratio))
tune_to_ratio(head, ratio)
leaf_annotation_ratio.append(ratio)
else:
marks.append(
microtones.return_cent_deviation_markup(
ratio, head.written_pitch
)
)
microtones.tune_to_ratio(head, ratio)
leaf_annotation_ratio.append(ratio)
if 0 < len(marks):
marks_strings = r""
for marks_string in marks[::-1]:
marks_strings += fr"{marks_string.contents[0][24:-1]}"
column = abjad.Markup(
fr"\center-column {{ {marks_strings} }}",
literal=True,
)
m = abjad.Markup(
fr"\markup \center-align {column}",
direction=abjad.Up,
literal=True,
)
if leaf is abjad.get.logical_tie(leaf).head:
abjad.attach(m, leaf)
else:
if self.as_ratios is False:
temp = microtonal_indices_to_pitch[index]
if isinstance(temp, abjad.OrderedDict):
temp = microtonal_indices_to_pitch[index]["1"]
microtones.apply_alteration(
leaf.note_head,
temp,
spell=self.apply_all_spelling,
)
else:
temp = microtonal_indices_to_pitch[index]
if isinstance(temp, abjad.OrderedDict):
temp = microtonal_indices_to_pitch[index]["1"]
factors = []
for _ in microtones.ji._prime_factors(
quicktions.Fraction(temp).numerator
):
factors.append(_)
for _ in microtones.ji._prime_factors(
quicktions.Fraction(temp).denominator
):
factors.append(_)
over_23 = 0
if 0 < len(factors):
over_23 = max(factors)
if 23 < over_23:
m = return_cent_markup(leaf.note_head, temp)
tune_to_ratio(leaf.note_head, temp)
leaf_annotation_ratio.append(temp)
else:
m = microtones.return_cent_deviation_markup(
temp,
leaf.note_head.written_pitch,
)
microtones.tune_to_ratio(
leaf.note_head,
temp,
)
leaf_annotation_ratio.append(temp)
if leaf is abjad.get.logical_tie(leaf).head:
abjad.attach(m, leaf)
annotation_string = [
f"{x} * {y}"
for x, y in zip(leaf_annotation_pitch, leaf_annotation_ratio)
]
abjad.annotate(leaf, "ratio", annotation_string)
if self.apply_all is False:
for old_leaf, new_leaf in zip(old_leaves, new_leaves):
indicators = abjad.get.indicators(old_leaf)
before_grace = abjad.get.before_grace_container(old_leaf)
for indicator in indicators:
abjad.attach(indicator, new_leaf)
if before_grace is not None:
abjad.attach(before_grace, new_leaf)
abjad.mutate.replace(old_leaf, new_leaf)
[docs] def make_persistent_copy(self, state_dict):
copied_handler = type(self)(
pitch_list=self.pitch_list,
allow_chord_duplicates=self.allow_chord_duplicates,
apply_all=self.apply_all,
apply_all_spelling=self.apply_all_spelling,
as_ratios=self.as_ratios,
chord_boolean_vector=self.chord_boolean_vector,
chord_groups=self.chord_groups,
forget=self.forget,
to_ties=self.to_ties,
state=state_dict,
name=self.name,
)
return copied_handler
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("pitch_count", self._cyc_pitches.state()),
("chord_boolean_count", self._cyc_chord_boolean_vector.state()),
("chord_groups_count", self._cyc_chord_groups.state()),
]
)
[docs]class RhythmHandler(Handler):
r"""
Rhythm Handler
.. container:: example
>>> spans = abjad.TimespanList(
... [
... abjad.Timespan(0, 1),
... abjad.Timespan(1, 2),
... ]
... )
>>> maker = rmakers.stack(
... rmakers.NoteRhythmMaker()
... )
>>> handler = evans.RhythmHandler(rmaker=maker)
>>> staff = abjad.Staff()
>>> for span in spans:
... selections = maker([span.duration])
... staff.extend(selections)
...
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'1
c'1
}
"""
def __init__(self, rmaker, forget=True, state=None, name="Rhythm Handler"):
self.rmaker = rmaker
self.forget = forget
self._input_state = state
self.state = self.rmaker.state
self.name = name
[docs] def __call__(self, durations):
return self._make_music(durations)
def _make_basic_rhythm(self, durations):
if self.forget is False:
if self._input_state is not None:
self.state = self._input_state
selections = self.rmaker(durations, previous_state=self.state)
self.state = self.rmaker.state
self._input_state = None
else:
selections = self.rmaker(durations, previous_state=self.rmaker.state)
self.state = self.rmaker.state
else:
selections = self.rmaker(durations)
return selections
def _make_music(self, durations):
selections = self._make_basic_rhythm(durations)
return selections
[docs] def make_persistent_copy(self, state_dict):
new_handler = type(self)(
rmaker=self.rmaker,
forget=self.forget,
state=state_dict,
name=self.name,
)
return new_handler
[docs] def name(self):
return self.name
[docs] def return_state(self):
return abjad.OrderedDict([("state", self.rmaker.state)])
# add style option for \slurDotted and \slurDashed and \slurSolid
[docs]class SlurHandler(Handler):
r"""
Slur Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 c'4")
>>> handler = evans.SlurHandler()
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
(
c'4
c'4
c'4
)
}
"""
def __init__(
self,
apply_slur_to="runs",
boolean_vector=[1],
forget=False,
count=-1,
name="Slur Handler",
):
self.apply_slur_to = apply_slur_to
self._count = count
self.forget = forget
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.forget, self._count
)
self.name = name
[docs] def __call__(self, selections):
self.add_slurs(selections)
[docs] def add_slurs(self, selections):
if self.apply_slur_to == "selections":
if len(abjad.select(selections).logical_ties(pitched=True)) < 2:
pass
if self.boolean_vector(r=1)[0] == 1:
abjad.slur(selections[:])
else:
pass
elif self.apply_slur_to == "runs":
for run in abjad.select(selections).runs():
if len(abjad.select(run).logical_ties()) < 2:
continue
if self.boolean_vector(r=1)[0] == 1:
abjad.slur(run[:])
else:
continue
else:
pass
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict([("count", self.boolean_vector.state())])
[docs]class TempoSpannerHandler(Handler):
r"""
Tempo Spanner Handler
.. container:: example
>>> s = abjad.Staff("s4 s4 s4 s4")
>>> handler = evans.TempoSpannerHandler(
... tempo_list=[(3, 0, 1, "87"), (3, 0, 1, "95")],
... boolean_vector=[1],
... padding=4,
... staff_padding=2,
... forget=False,
... )
>>> handler(s[:-1])
>>> score = abjad.Score([s])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=[
... "abjad.ily",
... "/Users/evansdsg2/evans/lilypond/evans-spanners.ily",
... ],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(s))
\new Staff
{
s4
- \abjad-dashed-line-with-arrow
- \baca-metronome-mark-spanner-left-text 3 0 1 "87"
- \tweak padding #4
- \tweak staff-padding #2
- \tweak font-size #2
\bacaStartTextSpanMM
s4
s4
\bacaStopTextSpanMM
- \abjad-invisible-line
- \baca-metronome-mark-spanner-left-text 3 0 1 "95"
- \tweak padding #4
- \tweak staff-padding #2
- \tweak font-size #2
\bacaStartTextSpanMM
s4
\bacaStopTextSpanMM
}
"""
def __init__(
self,
tempo_list=[(3, 0, 1, "87"), (3, 0, 1, "95")],
boolean_vector=[1],
padding=4,
staff_padding=2,
font_size=2,
forget=False,
tempo_count=-1,
bool_count=-1,
name="Tempo Spanner Handler",
):
self._tempo_count = tempo_count
self._bool_count = bool_count
self.padding = padding
self.forget = forget
self.staff_padding = staff_padding
self.font_size = font_size
self.tempo_list = sequence.CyclicList(
tempo_list, self.forget, self._tempo_count
)
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.forget, self._bool_count
)
self.name = name
[docs] def __call__(self, selections):
self.add_spanner(selections)
[docs] def add_spanner(self, selections):
ties = abjad.select(selections).logical_ties()
value = self.boolean_vector(r=1)[0]
if value == 1:
start_temp = self.tempo_list(r=1)[0]
stop_temp = self.tempo_list(r=1)[0]
start_literal = abjad.LilyPondLiteral(
[
r"- \abjad-dashed-line-with-arrow",
r"- \baca-metronome-mark-spanner-left-text "
+ f"{start_temp[0]} {start_temp[1]} {start_temp[2]}"
+ f' "{start_temp[3]}"',
r"- \tweak padding #" + f"{self.padding}",
r"- \tweak staff-padding #" + f"{self.staff_padding}",
r"- \tweak font-size #" + f"{self.font_size}",
r"\bacaStartTextSpanMM",
],
format_slot="after",
)
stop_literal = abjad.LilyPondLiteral(
[
r"\bacaStopTextSpanMM",
r"- \abjad-invisible-line",
r"- \baca-metronome-mark-spanner-left-text "
+ f'{stop_temp[0]} {stop_temp[1]} {stop_temp[2]} "{stop_temp[3]}"',
r"- \tweak padding #" + f"{self.padding}",
r"- \tweak staff-padding #" + f"{self.staff_padding}",
r"- \tweak font-size #" + f"{self.font_size}",
r"\bacaStartTextSpanMM",
],
format_slot="after",
)
stopper = abjad.LilyPondLiteral(r"\bacaStopTextSpanMM", format_slot="after")
abjad.attach(start_literal, abjad.select(ties).leaves()[0])
abjad.attach(stop_literal, abjad.select(ties).leaves()[-1])
abjad.attach(stopper, abjad.get.leaf(abjad.select(ties).leaves()[-1], 1))
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict([("count", self.boolean_vector.state())])
[docs]class TextSpanHandler(Handler):
r"""
Text Span Handler
.. container:: example
>>> staff = abjad.Staff("c'4 c'4 c'4 r4")
>>> handler = evans.TextSpanHandler(
... span_one_positions=["pont.", "tast."],
... span_one_style="dashed-line",
... span_one_padding=1.5,
... attach_span_one_to="bounds",
... )
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
c'4
- \abjad-dashed-line-with-arrow
- \tweak bound-details.left.text \markup \concat { \upright pont. \hspace #0.5 }
- \tweak bound-details.right.padding 1.4
- \tweak staff-padding #1.5
\startTextSpanOne
c'4
c'4
\stopTextSpanOne
- \abjad-dashed-line-with-hook
- \tweak bound-details.left.text \markup \concat { \upright pont. \hspace #0.5 }
- \tweak bound-details.right.padding 3
- \tweak staff-padding #1.5
\startTextSpanOne
r4
\stopTextSpanOne
}
"""
def __init__(
self,
span_one_positions=None,
span_one_style=None,
span_one_padding=None,
attach_span_one_to=None,
span_two_positions=None,
span_two_style=None,
span_two_padding=None,
attach_span_two_to=None,
span_three_positions=None,
span_three_style=None,
span_three_padding=None,
attach_span_three_to=None,
hooks=True,
forget=True,
count_1=-1,
count_2=-1,
count_3=-1,
name="TextSpan Handler",
):
self.span_one_positions = span_one_positions
self.span_one_style = span_one_style
self.span_one_padding = span_one_padding
self.attach_span_one_to = attach_span_one_to
self.span_two_positions = span_two_positions
self.span_two_style = span_two_style
self.span_two_padding = span_two_padding
self.attach_span_two_to = attach_span_two_to
self.span_three_positions = span_three_positions
self.span_three_style = span_three_style
self.span_three_padding = span_three_padding
self.attach_span_three_to = attach_span_three_to
self.hooks = hooks
self.forget = forget
self._count_1 = count_1
self._count_2 = count_2
self._count_3 = count_3
self._cyc_span_one_positions = sequence.CyclicList(
span_one_positions, self.forget, self._count_1
)
self._cyc_span_two_positions = sequence.CyclicList(
span_two_positions, self.forget, self._count_2
)
self._cyc_span_three_positions = sequence.CyclicList(
span_three_positions, self.forget, self._count_3
)
self.name = name
[docs] def __call__(self, selections):
self._add_spanners(selections)
def _add_spanners(self, selections):
if self.attach_span_one_to == "bounds":
self._apply_position_and_span_to_bounds(
selections,
self._cyc_span_one_positions,
self.span_one_style,
r"One",
self.span_one_padding,
)
elif self.attach_span_one_to == "leaves":
self._apply_position_and_span_to_leaves(
selections,
self._cyc_span_one_positions,
self.span_one_style,
r"One",
self.span_one_padding,
)
elif self.attach_span_one_to == "left":
self._apply_position_and_span_to_left(
selections,
self._cyc_span_one_positions,
self.span_one_style,
r"One",
self.span_one_padding,
)
else:
pass
if self.attach_span_two_to == "bounds":
self._apply_position_and_span_to_bounds(
selections,
self._cyc_span_two_positions,
self.span_two_style,
r"Two",
self.span_two_padding,
)
elif self.attach_span_two_to == "leaves":
self._apply_position_and_span_to_leaves(
selections,
self._cyc_span_two_positions,
self.span_two_style,
r"Two",
self.span_two_padding,
)
elif self.attach_span_two_to == "left":
self._apply_position_and_span_to_left(
selections,
self._cyc_span_two_positions,
self.span_two_style,
r"Two",
self.span_two_padding,
)
else:
pass
if self.attach_span_three_to == "bounds":
self._apply_position_and_span_to_bounds(
selections,
self._cyc_span_three_positions,
self.span_three_style,
r"Three",
self.span_three_padding,
)
elif self.attach_span_three_to == "leaves":
self._apply_position_and_span_to_leaves(
selections,
self._cyc_span_three_positions,
self.span_three_style,
r"Three",
self.span_three_padding,
)
elif self.attach_span_three_to == "left":
self._apply_position_and_span_to_left(
selections,
self._cyc_span_three_positions,
self.span_three_style,
r"Three",
self.span_three_padding,
)
else:
pass
def _apply_empty_spanner(self, selections, span_command):
first_leaf = abjad.select(selections).leaves()[0]
stop_indicator = abjad.StopTextSpan(command=r"\stopTextSpan" + span_command)
abjad.attach(stop_indicator, first_leaf)
def _apply_position_and_span_to_bounds(
self, selections, positions, style, span_command, span_padding
):
for run in abjad.select(selections).runs():
if len(run) < 2:
start_span = abjad.StartTextSpan(
left_text=abjad.Markup(
fr"\upright {positions(r=1)[0]}", literal=True
),
style=style + "-with-hook",
command=r"\startTextSpan" + span_command,
)
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command),
abjad.get.leaf(run[-1], 1),
)
abjad.attach(start_span, run[0])
abjad.tweak(start_span).staff_padding = f"#{span_padding}"
else:
start_span = abjad.StartTextSpan(
left_text=abjad.Markup(
fr"\upright {positions(r=1)[0]}", literal=True
),
style=style + "-with-arrow",
command=r"\startTextSpan" + span_command,
right_padding=1.4,
)
if self.hooks is True:
stop_span = abjad.StartTextSpan(
left_text=abjad.Markup(
rf"\upright {positions(r=1)[0]}", literal=True
),
style=style + "-with-hook",
command=r"\startTextSpan" + span_command,
right_padding=3,
)
else:
stop_span = abjad.StartTextSpan(
left_text=abjad.Markup(
fr"\upright {positions(r=1)[0]}", literal=True
),
style="invisible-line",
command=r"\startTextSpan" + span_command,
right_padding=3,
)
abjad.attach(start_span, run[0])
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command), run[-1]
)
abjad.attach(stop_span, run[-1])
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command),
abjad.get.leaf(run[-1], 1),
)
abjad.tweak(start_span).staff_padding = f"#{span_padding}"
abjad.tweak(stop_span).staff_padding = f"#{span_padding}"
def _apply_position_and_span_to_leaves(
self, selections, positions, style, span_command, span_padding
):
for run in abjad.select(selections).runs():
ties = abjad.select(run).logical_ties(pitched=True)
following_leaf = abjad.get.leaf(ties[-1][-1], 1)
distance = len(ties) + 1
start_strings = [positions(r=1)[0] for _ in range(distance)]
for i, start_string in enumerate(start_strings[:-1]):
if all(start_string[_].isdigit() for _ in (0, -1)):
if quicktions.Fraction(
int(start_strings[i][0]), int(start_strings[i][-1])
) > quicktions.Fraction(
int(start_strings[i + 1][0]), int(start_strings[i + 1][-1])
):
start_strings[
i
] = fr"""\center-column {{ \center-align \vcenter \musicglyph \evans-upbow \vspace #0.2 \upright \fraction {start_string[0]} {start_string[-1]} }}"""
elif quicktions.Fraction(
int(start_strings[i][0]), int(start_strings[i][-1])
) < quicktions.Fraction(
int(start_strings[i + 1][0]), int(start_strings[i + 1][-1])
):
start_strings[
i
] = fr"""\center-column {{ \center-align \vcenter \musicglyph \evans-downbow \vspace #0.2 \upright \fraction {start_string[0]} {start_string[-1]} }}"""
else:
start_strings[
i
] = fr"""\center-column {{ \center-align \vcenter \upright \fraction {start_string[0]} {start_string[-1]} }}"""
else:
start_strings[
i
] = fr"""\center-column {{ \upright \center-align \vcenter {start_string} }}"""
start_indicators = [
abjad.StartTextSpan(
left_text=abjad.Markup(f"{start_string}", literal=True),
style=fr"{style}-with-arrow",
command=r"\startTextSpan" + span_command,
right_padding=1.4,
)
for start_string in start_strings
]
final_indicator = abjad.StartTextSpan()
if all(start_string[-1].isdigit() for _ in (0, -1)):
final_indicator = abjad.StartTextSpan(
left_text=abjad.Markup(
fr"""\center-column {{ \center-align \vcenter \upright \fraction {start_strings[-1][0]} {start_strings[-1][-1]} }}""",
literal=True,
),
style=r"invisible-line",
command=r"\startTextSpan" + span_command,
right_padding=3,
)
else:
final_indicator = abjad.StartTextSpan(
left_text=abjad.Markup(
fr"""\center-column {{ \center-align \upright \vcenter {start_strings[-1]} }}""",
literal=True,
),
style=r"invisible-line",
command=r"\startTextSpan" + span_command,
right_padding=3,
)
for indicator in start_indicators:
abjad.tweak(indicator).staff_padding = f"#{span_padding}"
abjad.tweak(final_indicator).staff_padding = f"#{span_padding}"
abjad.attach(start_indicators[0], ties[0][0])
for pair in zip(ties[1:], start_indicators[1:]):
tie, start_indicator = pair
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command), tie[0]
)
abjad.attach(start_indicator, tie[0])
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command),
following_leaf,
)
abjad.attach(final_indicator, following_leaf)
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command),
abjad.get.leaf(following_leaf, 1),
)
def _apply_position_and_span_to_left(
self, selections, positions, style, span_command, span_padding
):
runs = abjad.select(selections).runs()
start_strings = [positions(r=1)[0] for _ in runs]
start_indicators = [
abjad.StartTextSpan(
left_text=abjad.Markup(fr"\upright {start_string}", literal=True),
style=fr"{style}-with-hook",
command=r"\startTextSpan" + span_command,
right_padding=3,
)
for start_string in start_strings
]
for indicator in start_indicators:
abjad.tweak(indicator).staff_padding = f"#{span_padding}"
for i, pair in enumerate(zip(runs, start_indicators)):
run, start_indicator = pair
abjad.attach(start_indicator, run[0])
abjad.attach(
abjad.StopTextSpan(command=r"\stopTextSpan" + span_command),
abjad.get.leaf(run[-1], 1),
)
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict(
[
("count_1", self._cyc_span_one_positions.state()),
("count_2", self._cyc_span_two_positions.state()),
("count_3", self._cyc_span_three_positions.state()),
]
)
[docs]class TrillHandler(Handler):
r"""
Trill Handler
.. container:: example
>>> staff = abjad.Staff("<c' d'>4 c'4 c'4 <c' d'>4 c'4 c'4 c'4 c'4 ")
>>> handler = evans.TrillHandler(boolean_vector=[0, 1], forget=False)
>>> handler(staff)
>>> score = abjad.Score([staff])
>>> moment = "#(ly:make-moment 1 25)"
>>> abjad.setting(score).proportional_notation_duration = moment
>>> file = abjad.LilyPondFile(
... items=[score],
... includes=["abjad.ily"],
... global_staff_size=16,
... )
...
>>> abjad.show(file) # doctest: +SKIP
.. docs::
>>> print(abjad.lilypond(staff))
\new Staff
{
<c' d'>4
c'4
c'4
\pitchedTrill
c'4
\startTrillSpan d'
c'4
\stopTrillSpan
c'4
c'4
c'4
}
"""
def __init__(
self,
boolean_vector=[0],
forget=False,
count=-1,
name="Trill Handler",
only_chords=False,
):
self.forget = forget
self._count = count
self.boolean_vector = sequence.CyclicList(
boolean_vector, self.forget, self._count
)
self.name = name
self.only_chords = only_chords
[docs] def __call__(self, selections):
self._apply_trills(selections)
def _apply_trills(self, selections):
ties = abjad.select(selections).logical_ties(pitched=True)
if self.only_chords:
chords = abjad.select(selections).components(abjad.Chord)
ties = abjad.select(chords).logical_ties(pitched=True)
vector = self.boolean_vector
for tie, bool in zip(ties, vector(r=len(ties))):
if bool == 1:
if all(
isinstance(leaf, abjad.Chord)
for leaf in abjad.iterate(tie).leaves()
):
old_chord = tie[0]
base_pitch = old_chord.written_pitches[0]
trill_pitch = old_chord.written_pitches[-1]
new_leaf = abjad.Note(base_pitch, old_chord.written_duration)
trill_start = abjad.LilyPondLiteral(
r"\pitchedTrill", format_slot="before"
)
trill_literal = abjad.LilyPondLiteral(
fr"\startTrillSpan {trill_pitch}", format_slot="after"
)
trill_stop = abjad.LilyPondLiteral(
r"\stopTrillSpan", format_slot="after"
)
abjad.attach(trill_start, new_leaf)
abjad.attach(trill_literal, new_leaf)
last_leaf = tie[-1]
next_leaf = abjad.get.leaf(last_leaf, 1)
if next_leaf is not None:
abjad.attach(trill_stop, next_leaf)
else:
continue
indicators = abjad.get.indicators(old_chord)
for indicator in indicators:
abjad.attach(indicator, new_leaf)
parent = abjad.get.parentage(old_chord).parent
parent[parent.index(old_chord)] = new_leaf
tail = abjad.select(tie).leaves()[1:]
for leaf in tail:
new_tail = abjad.Note(base_pitch, leaf.written_duration)
indicators = abjad.get.indicators(leaf)
for indicator in indicators:
abjad.attach(indicator, new_tail)
before_grace = abjad.get.before_grace_container(leaf)
if before_grace is not None:
abjad.attach(before_grace, new_tail)
parent = abjad.get.parentage(leaf).parent
parent[parent.index(leaf)] = new_tail
[docs] def name(self):
return self.name
[docs] def state(self):
return abjad.OrderedDict([("count", self.boolean_vector.state())])