def __copy__(self, *arguments): """ Shallow copies component. Copies indicators. Does not copy spanners. Does not copy children. Returns new component. """ new_component = type(self)(*self.__getnewargs__()) if getattr(self, "_overrides", None) is not None: manager = copy.copy(override(self)) new_component._overrides = manager if getattr(self, "_lilypond_setting_name_manager", None) is not None: manager = copy.copy(setting(self)) new_component._lilypond_setting_name_manager = manager for wrapper in inspect(self).annotation_wrappers(): new_wrapper = copy.copy(wrapper) attach(new_wrapper, new_component) for wrapper in inspect(self).wrappers(): new_wrapper = copy.copy(wrapper) attach(new_wrapper, new_component) return new_component
def _process_post_events(self, leaf, post_events): nonspanner_post_event_types = ( abjad_indicators.Articulation, abjad_indicators.BarLine, abjad_indicators.Dynamic, abjad_indicators.GlissandoIndicator, abjad_indicators.StartHairpin, abjad_indicators.LilyPondLiteral, abjad_indicators.StartBeam, abjad_indicators.StartGroup, abjad_indicators.StartPhrasingSlur, abjad_indicators.StartSlur, abjad_indicators.StartTextSpan, abjad_indicators.StartTrillSpan, abjad_indicators.StopBeam, abjad_indicators.StopGroup, abjad_indicators.StopHairpin, abjad_indicators.StopSlur, abjad_indicators.StopPhrasingSlur, abjad_indicators.StopTextSpan, abjad_indicators.StopTrillSpan, abjad_indicators.StemTremolo, abjad_indicators.TieIndicator, abjad_markups.Markup, ) for post_event in post_events: if isinstance(post_event, nonspanner_post_event_types): attach(post_event, leaf)
def _process_post_events(self, leaf, post_events): nonspanner_post_event_types = ( abjad_indicators.Articulation, abjad_indicators.BarLine, abjad_indicators.Dynamic, abjad_indicators.GlissandoIndicator, abjad_indicators.StartHairpin, abjad_indicators.LilyPondLiteral, abjad_indicators.StartBeam, abjad_indicators.StartGroup, abjad_indicators.StartPhrasingSlur, abjad_indicators.StartSlur, abjad_indicators.StartTextSpan, abjad_indicators.StartTrillSpan, abjad_indicators.StopBeam, abjad_indicators.StopGroup, abjad_indicators.StopHairpin, abjad_indicators.StopSlur, abjad_indicators.StopPhrasingSlur, abjad_indicators.StopTextSpan, abjad_indicators.StopTrillSpan, abjad_indicators.StemTremolo, abjad_indicators.Tie, abjad_markups.Markup, ) for post_event in post_events: if isinstance(post_event, nonspanner_post_event_types): attach(post_event, leaf)
def _construct_context_specced_music(self, context, optional_id, optional_context_mod, music): known_contexts = { "ChoirStaff": core.StaffGroup, "GrandStaff": core.StaffGroup, "PianoStaff": core.StaffGroup, "Score": core.Score, "Staff": core.Staff, "StaffGroup": core.StaffGroup, "Voice": core.Voice, } lilypond_type = context if context in known_contexts: context = known_contexts[context]([]) else: message = "context type {!r} not supported." message = message.format(context) raise Exception(message) if lilypond_type in ("GrandStaff", "PianoStaff"): context.lilypond_type = lilypond_type if optional_id is not None: context.name = optional_id if optional_context_mod is not None: for x in optional_context_mod: print(x) # TODO: impelement context mods on contexts pass context.simultaneous = music.simultaneous # add children while len(music): component = music.pop(0) context.append(component) for wrapper in music._wrappers: attach(wrapper, context) return context
def _copy_override_and_set_from_leaf(self, leaf): if getattr(leaf, "_overrides", None) is not None: self._overrides = copy.copy(override(leaf)) if getattr(leaf, "_lilypond_setting_name_manager", None) is not None: self._lilypond_setting_name_manager = copy.copy(setting(leaf)) new_wrappers = [] for wrapper in leaf._wrappers: new_wrapper = copy.copy(wrapper) new_wrappers.append(new_wrapper) for new_wrapper in new_wrappers: attach(new_wrapper, self)
def __illustrate__(self): """ Illustrates articulation. Returns LilyPond file. """ note = abjad.Note("c'4") articulation = copy.copy(self) attach(articulation, note) lilypond_file = abjad.LilyPondFile.new(note) return lilypond_file
def _copy_override_and_set_from_leaf(self, leaf): if getattr(leaf, '_overrides', None) is not None: self._overrides = copy.copy(override(leaf)) if getattr(leaf, '_lilypond_setting_name_manager', None) is not None: self._lilypond_setting_name_manager = copy.copy(setting(leaf)) new_wrappers = [] for wrapper in leaf._wrappers: new_wrapper = copy.copy(wrapper) new_wrappers.append(new_wrapper) for new_wrapper in new_wrappers: attach(new_wrapper, self)
def _attach_lilypond_one_voice(self): from .Voice import Voice anchor_leaf = self._get_on_beat_anchor_leaf() anchor_voice = abjad_inspect(anchor_leaf).parentage().get(Voice) final_anchor_leaf = abjad_inspect(anchor_voice).leaf(-1) next_leaf = abjad_inspect(final_anchor_leaf).leaf(1) literal = LilyPondLiteral(r"\oneVoice") if abjad_inspect(next_leaf).has_indicator(literal): return site = "abjad.OnBeatGraceContainer._attach_lilypond_one_voice()" tag = Tag(site) attach(literal, next_leaf, tag=tag)
def __copy__(self, *arguments): """ Shallow copies leaf. """ new = Component.__copy__(self, *arguments) new.multiplier = self.multiplier before_grace_container = self._before_grace_container if before_grace_container is not None: new_grace_container = before_grace_container._copy_with_children() attach(new_grace_container, new) after_grace_container = self._after_grace_container if after_grace_container is not None: new_after_grace_container = after_grace_container._copy_with_children( ) attach(new_after_grace_container, new) return new
def __copy__(self, *arguments): """ Shallow copies leaf. Returns new leaf. """ new = Component.__copy__(self, *arguments) grace_container = self._grace_container if grace_container is not None: new_grace_container = grace_container._copy_with_children() attach(new_grace_container, new) after_grace_container = self._after_grace_container if after_grace_container is not None: new_after_grace_container = \ after_grace_container._copy_with_children() attach(new_after_grace_container, new) return new
def _attach_lilypond_one_voice(self): from .Voice import Voice anchor_leaf = self._get_on_beat_anchor_leaf() anchor_voice = abjad_inspect(anchor_leaf).parentage().get(Voice) final_anchor_leaf = abjad_inspect(anchor_voice).leaf(-1) next_leaf = abjad_inspect(final_anchor_leaf).leaf(1) literal = LilyPondLiteral(r"\oneVoice", format_slot="absolute_before") if abjad_inspect(next_leaf).has_indicator(literal): return if isinstance(next_leaf._parent, OnBeatGraceContainer): return if next_leaf._parent._is_on_beat_anchor_voice(): return site = "abjad.OnBeatGraceContainer._attach_lilypond_one_voice()" tag = Tag(site) tag = tag.append(abjad_tags.ONE_VOICE_COMMAND) attach(literal, next_leaf, tag=tag)
def __copy__(self, *arguments): """ Shallow copies leaf. Returns new leaf. """ new = Component.__copy__(self, *arguments) new.multiplier = self.multiplier grace_container = self._grace_container if grace_container is not None: new_grace_container = grace_container._copy_with_children() attach(new_grace_container, new) after_grace_container = self._after_grace_container if after_grace_container is not None: new_after_grace_container = ( after_grace_container._copy_with_children() ) attach(new_after_grace_container, new) return new
def _construct_context_specced_music( self, context, optional_id, optional_context_mod, music ): known_contexts = { "ChoirStaff": core.StaffGroup, "GrandStaff": core.StaffGroup, "PianoStaff": core.StaffGroup, "Score": core.Score, "Staff": core.Staff, "StaffGroup": core.StaffGroup, "Voice": core.Voice, } lilypond_type = context if context in known_contexts: context = known_contexts[context]([]) else: message = "context type {!r} not supported." message = message.format(context) raise Exception(message) if lilypond_type in ("GrandStaff", "PianoStaff"): context.lilypond_type = lilypond_type if optional_id is not None: context.name = optional_id if optional_context_mod is not None: for x in optional_context_mod: print(x) # TODO: impelement context mods on contexts pass context.is_simultaneous = music.is_simultaneous # add children while len(music): component = music.pop(0) context.append(component) for wrapper in music._wrappers: attach(wrapper, context) return context
def _set_duration(self, new_duration, repeat_ties=False): import abjad new_duration = Duration(new_duration) # change LilyPond multiplier if leaf already has LilyPond multiplier if self._get_indicators(Multiplier): detach(Multiplier, self) multiplier = new_duration.__div__(self.written_duration) attach(multiplier, self) return select(self) # change written duration if new duration is assignable try: self.written_duration = new_duration return select(self) except exceptions.AssignabilityError: pass # make new notes or tuplets if new duration is nonassignable maker = abjad.NoteMaker(repeat_ties=repeat_ties, ) components = maker(0, new_duration) if isinstance(components[0], Leaf): tied_leaf_count = len(components) - 1 tied_leaves = tied_leaf_count * self all_leaves = [self] + tied_leaves for leaf, component in zip(all_leaves, components): leaf.written_duration = component.written_duration self._splice(tied_leaves, grow_spanners=True) if not inspect(self).has_spanner(abjad.Tie): tie = abjad.Tie() if tie._attachment_test(self): tie = abjad.Tie(repeat=repeat_ties) attach(tie, all_leaves) return select(all_leaves) else: assert isinstance(components[0], abjad.Tuplet) tuplet = components[0] components = tuplet[:] tied_leaf_count = len(components) - 1 tied_leaves = tied_leaf_count * self all_leaves = [self] + tied_leaves for leaf, component in zip(all_leaves, components): leaf.written_duration = component.written_duration self._splice(tied_leaves, grow_spanners=True) if not inspect(self).has_spanner(abjad.Tie): tie = abjad.Tie() if tie._attachment_test(self): tie = abjad.Tie(repeat=repeat_ties) attach(tie, all_leaves) multiplier = tuplet.multiplier tuplet = abjad.Tuplet(multiplier, []) abjad.mutate(all_leaves).wrap(tuplet) return select(tuplet)
def hairpin( descriptor: str, argument: typing.Union[Component, Selection], *, selector: typings.Selector = 'abjad.select().leaves()', ) -> None: r""" Attaches hairpin indicators. .. container:: example With three-part string descriptor: >>> staff = abjad.Staff("c'4 d' e' f'") >>> abjad.hairpin('p < f', staff[:]) >>> abjad.override(staff[0]).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \once \override DynamicLineSpanner.staff-padding = #4 c'4 \p \< d'4 e'4 f'4 \f } .. container:: example With dynamic objects: >>> staff = abjad.Staff("c'4 d' e' f'") >>> start = abjad.Dynamic('niente', command=r'\!') >>> trend = abjad.DynamicTrend('o<|') >>> abjad.tweak(trend).color = 'blue' >>> stop = abjad.Dynamic('"f"') >>> abjad.hairpin([start, trend, stop], staff[:]) >>> abjad.override(staff[0]).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \once \override DynamicLineSpanner.staff-padding = #4 c'4 \! - \tweak color #blue - \tweak circled-tip ##t - \tweak stencil #abjad-flared-hairpin \< d'4 e'4 f'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 "”" ) ) ) } """ import abjad indicators: typing.List = [] start_dynamic: typing.Optional[Dynamic] dynamic_trend: typing.Optional[DynamicTrend] stop_dynamic: typing.Optional[Dynamic] known_shapes = DynamicTrend('<').known_shapes if isinstance(descriptor, str): for string in descriptor.split(): if string in known_shapes: dynamic_trend = DynamicTrend(string) indicators.append(dynamic_trend) else: dynamic = Dynamic(string) indicators.append(dynamic) else: assert isinstance(descriptor, list), repr(descriptor) indicators = descriptor start_dynamic, dynamic_trend, stop_dynamic = None, None, None if len(indicators) == 1: if isinstance(indicators[0], Dynamic): start_dynamic = indicators[0] else: dynamic_trend = indicators[0] elif len(indicators) == 2: if isinstance(indicators[0], Dynamic): start_dynamic = indicators[0] dynamic_trend = indicators[1] else: dynamic_trend = indicators[0] stop_dynamic = indicators[1] elif len(indicators) == 3: start_dynamic, dynamic_trend, stop_dynamic = indicators else: raise Exception(indicators) assert isinstance(start_dynamic, Dynamic), repr(start_dynamic) if isinstance(selector, str): selector = eval(selector) assert isinstance(selector, Expression) argument = selector(argument) leaves = select(argument).leaves() start_leaf = leaves[0] stop_leaf = leaves[-1] if start_dynamic is not None: attach(start_dynamic, start_leaf) if dynamic_trend is not None: attach(dynamic_trend, start_leaf) if stop_dynamic is not None: attach(stop_dynamic, stop_leaf)
def attach_defaults(self, argument) -> typing.List: """ Attaches defaults to all staff and staff group contexts in ``argument`` when ``argument`` is a score. Attaches defaults to ``argument`` (without iterating ``argument``) when ``argument`` is a staff or staff group. Returns list of one wrapper for every indicator attached. """ assert isinstance(argument, (Score, Staff, StaffGroup)), repr(argument) wrappers: typing.List[Wrapper] = [] tag = const.REMOVE_ALL_EMPTY_STAVES empty_prototype = (MultimeasureRest, Skip) prototype = (Staff, StaffGroup) if isinstance(argument, Score): staff__groups = select(argument).components(prototype) staves = select(argument).components(Staff) elif isinstance(argument, Staff): staff__groups = [argument] staves = [argument] else: assert isinstance(argument, StaffGroup), repr(argument) staff__groups = [argument] staves = [] for staff__group in staff__groups: leaf = None voices = select(staff__group).components(Voice) # find leaf 0 in first nonempty voice for voice in voices: leaves = [] for leaf_ in select(voice).leaves(): if inspect(leaf_).has_indicator(const.HIDDEN): leaves.append(leaf_) if not all(isinstance(_, empty_prototype) for _ in leaves): leaf = inspect(voice).leaf(0) break # otherwise, find first leaf in voice in non-removable staff if leaf is None: for voice in voices: voice_might_vanish = False for component in inspect(voice).parentage(): if inspect(component).annotation(tag) is True: voice_might_vanish = True if not voice_might_vanish: leaf = inspect(voice).leaf(0) if leaf is not None: break # otherwise, as last resort find first leaf in first voice if leaf is None: leaf = inspect(voices[0]).leaf(0) if leaf is None: continue instrument = inspect(leaf).indicator(instruments.Instrument) if instrument is None: string = "default_instrument" instrument = inspect(staff__group).annotation(string) if instrument is not None: wrapper = attach( instrument, leaf, context=staff__group.lilypond_type, tag=Tag("abjad.ScoreTemplate.attach_defaults(1)"), wrapper=True, ) wrappers.append(wrapper) margin_markup = inspect(leaf).indicator(MarginMarkup) if margin_markup is None: string = "default_margin_markup" margin_markup = inspect(staff__group).annotation(string) if margin_markup is not None: wrapper = attach( margin_markup, leaf, tag=abjad_tags.NOT_PARTS.append( Tag("abjad.ScoreTemplate.attach_defaults(2)")), wrapper=True, ) wrappers.append(wrapper) for staff in staves: leaf = inspect(staff).leaf(0) clef = inspect(leaf).indicator(Clef) if clef is not None: continue clef = inspect(staff).annotation("default_clef") if clef is not None: wrapper = attach( clef, leaf, tag=Tag("abjad.ScoreTemplate.attach_defaults(3)"), wrapper=True, ) wrappers.append(wrapper) return wrappers
def _split_by_durations( self, durations, cyclic=False, tie_split_notes=True, repeat_ties=False ): from .AfterGraceContainer import AfterGraceContainer from .Chord import Chord from .GraceContainer import GraceContainer from .Note import Note from .Selection import Selection from .Tuplet import Tuplet durations = [Duration(_) for _ in durations] durations = Sequence(durations) leaf_duration = inspect(self).duration() if cyclic: durations = durations.repeat_to_weight(leaf_duration) if sum(durations) < leaf_duration: last_duration = leaf_duration - sum(durations) durations = list(durations) durations.append(last_duration) durations = Sequence(durations) durations = durations.truncate(weight=leaf_duration) originally_tied = inspect(self).has_indicator(TieIndicator) originally_repeat_tied = inspect(self).has_indicator(RepeatTie) result_selections = [] grace_container = self._detach_grace_container() after_grace_container = self._detach_after_grace_container() leaf_prolation = inspect(self).parentage().prolation for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation selection = new_leaf._set_duration( preprolated_duration, repeat_ties=repeat_ties ) result_selections.append(selection) result_components = Sequence(result_selections).flatten(depth=-1) result_components = select(result_components) result_leaves = select(result_components).leaves(grace_notes=False) assert all(isinstance(_, Selection) for _ in result_selections) assert all(isinstance(_, Component) for _ in result_components) assert result_leaves.are_leaves() # for leaf in result_leaves: # detach(Tie, leaf) # strip result leaves of all indicators for leaf in result_leaves: detach(object, leaf) # replace leaf with flattened result if self._parent is not None: mutate(self).replace(result_components) # move indicators first_result_leaf = result_leaves[0] last_result_leaf = result_leaves[-1] for indicator in inspect(self).indicators(): detach(indicator, self) direction = getattr(indicator, "_time_orientation", enums.Left) if direction is enums.Left: attach(indicator, first_result_leaf) elif direction == enums.Right: attach(indicator, last_result_leaf) else: raise ValueError(direction) # move grace containers if grace_container is not None: container = grace_container[0] assert isinstance(container, GraceContainer), repr(container) attach(container, first_result_leaf) if after_grace_container is not None: container = after_grace_container[0] prototype = AfterGraceContainer assert isinstance(container, prototype), repr(container) attach(container, last_result_leaf) if isinstance(result_components[0], Tuplet): mutate(result_components).fuse() # tie split notes if ( tie_split_notes and isinstance(self, (Note, Chord)) and 1 < len(result_leaves) ): result_leaves._attach_tie_to_leaves(repeat_ties=repeat_ties) # assert not inspect(result_leaves[0]).has_indicator(RepeatTie) detach(RepeatTie, result_leaves[0]) # assert not inspect(result_leaves[-1]).has_indicator(TieIndicator) detach(TieIndicator, result_leaves[-1]) if originally_repeat_tied: tie = RepeatTie() attach(tie, result_leaves[0]) if originally_tied: tie = TieIndicator() attach(tie, result_leaves[-1]) assert isinstance(result_leaves, Selection) assert all(isinstance(_, Leaf) for _ in result_leaves) return result_leaves
def on_beat_grace_container( contents, anchor_voice_selection, *, anchor_voice_number=2, do_not_beam=None, do_not_slash=None, do_not_slur=None, do_not_stop_polyphony=None, font_size=-3, grace_voice_number=1, leaf_duration=None, ): r""" Makes on-beat grace container and wraps around ``selection``. .. container:: example GRACE NOTES ABOVE. Note-to-note anchor: >>> music_voice = abjad.Voice("c'4 d' e' f'", name="Music_Voice") >>> string = "g'8 a' b' c'' d'' c'' b' a' b' c'' d''" >>> result = abjad.on_beat_grace_container( ... string, music_voice[1:3], leaf_duration=(1, 30) ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { c'4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceOne %! abjad.on_beat_grace_container(3) < \tweak font-size #0 \tweak transparent ##t d' g' >8 * 4/15 [ ( a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 c''8 * 4/15 b'8 * 4/15 a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceTwo %! abjad.on_beat_grace_container(4) d'4 e'4 } >> \oneVoice %! abjad.on_beat_grace_container(5) f'4 } } Note-to-chord anchor: >>> music_voice = abjad.Voice( ... "<a c'>4 <b d'> <c' e'> <d' f'>", name="Music_Voice" ... ) >>> string = "g'8 a' b' c'' d'' c'' b' a' b' c'' d''" >>> result = abjad.on_beat_grace_container( ... string, music_voice[1:3], leaf_duration=(1, 30) ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { <a c'>4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceOne %! abjad.on_beat_grace_container(3) < \tweak font-size #0 \tweak transparent ##t d' g' >8 * 4/15 [ ( a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 c''8 * 4/15 b'8 * 4/15 a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceTwo %! abjad.on_beat_grace_container(4) <b d'>4 <c' e'>4 } >> \oneVoice %! abjad.on_beat_grace_container(5) <d' f'>4 } } Chord-to-note anchor: >>> music_voice = abjad.Voice("c'4 d' e' f'", name="Music_Voice") >>> string = "<g' b'>8 a' b' c'' d'' c'' b' a' b' c'' d''" >>> result = abjad.on_beat_grace_container( ... string, music_voice[1:3], leaf_duration=(1, 30) ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { c'4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceOne %! abjad.on_beat_grace_container(3) < \tweak font-size #0 \tweak transparent ##t d' g' b' >8 * 4/15 [ ( a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 c''8 * 4/15 b'8 * 4/15 a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceTwo %! abjad.on_beat_grace_container(4) d'4 e'4 } >> \oneVoice %! abjad.on_beat_grace_container(5) f'4 } } Chord-to-chord anchor: >>> music_voice = abjad.Voice( ... "<a c'>4 <b d'> <c' e'> <d' f'>", name="Music_Voice" ... ) >>> string = "<g' b'>8 a' b' c'' d'' c'' b' a' b' c'' d''" >>> result = abjad.on_beat_grace_container( ... string, music_voice[1:3], leaf_duration=(1, 30) ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { <a c'>4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceOne %! abjad.on_beat_grace_container(3) < \tweak font-size #0 \tweak transparent ##t d' g' b' >8 * 4/15 [ ( a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 c''8 * 4/15 b'8 * 4/15 a'8 * 4/15 b'8 * 4/15 c''8 * 4/15 d''8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceTwo %! abjad.on_beat_grace_container(4) <b d'>4 <c' e'>4 } >> \oneVoice %! abjad.on_beat_grace_container(5) <d' f'>4 } } .. container:: example GRACE NOTES BELOW. Note-to-note anchor: >>> music_voice = abjad.Voice("c'4 d' e' f'", name="Music_Voice") >>> string = "g8 a b c' d' c' b a b c' d'" >>> result = abjad.on_beat_grace_container( ... string, ... music_voice[1:3], ... anchor_voice_number=1, ... grace_voice_number=2, ... leaf_duration=(1, 30), ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { c'4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceTwo %! abjad.on_beat_grace_container(3) < g \tweak font-size #0 \tweak transparent ##t d' >8 * 4/15 [ ( a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 c'8 * 4/15 b8 * 4/15 a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceOne %! abjad.on_beat_grace_container(4) d'4 e'4 } >> \oneVoice %! abjad.on_beat_grace_container(5) f'4 } } Note-to-chord anchor: >>> music_voice = abjad.Voice( ... "<c' e'>4 <d' f'> <e' g'> <f' a'>", name="Music_Voice" ... ) >>> string = "g8 a b c' d' c' b a b c' d'" >>> result = abjad.on_beat_grace_container( ... string, ... music_voice[1:3], ... anchor_voice_number=1, ... grace_voice_number=2, ... leaf_duration=(1, 30), ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { <c' e'>4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceTwo %! abjad.on_beat_grace_container(3) < g \tweak font-size #0 \tweak transparent ##t f' >8 * 4/15 [ ( a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 c'8 * 4/15 b8 * 4/15 a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceOne %! abjad.on_beat_grace_container(4) <d' f'>4 <e' g'>4 } >> \oneVoice %! abjad.on_beat_grace_container(5) <f' a'>4 } } Chord-to-note anchor: >>> music_voice = abjad.Voice("c'4 d' e' f'", name="Music_Voice") >>> string = "<e g>8 a b c' d' c' b a b c' d'" >>> result = abjad.on_beat_grace_container( ... string, ... music_voice[1:3], ... anchor_voice_number=1, ... grace_voice_number=2, ... leaf_duration=(1, 30), ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { c'4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceTwo %! abjad.on_beat_grace_container(3) < e g \tweak font-size #0 \tweak transparent ##t d' >8 * 4/15 [ ( a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 c'8 * 4/15 b8 * 4/15 a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceOne %! abjad.on_beat_grace_container(4) d'4 e'4 } >> \oneVoice %! abjad.on_beat_grace_container(5) f'4 } } Chord-to-chord anchor: >>> music_voice = abjad.Voice( ... "<c' e'>4 <d' f'> <e' g'> <f' a'>", name="Music_Voice" ... ) >>> string = "<e g>8 a b c' d' c' b a b c' d'" >>> result = abjad.on_beat_grace_container( ... string, ... music_voice[1:3], ... anchor_voice_number=1, ... grace_voice_number=2, ... leaf_duration=(1, 30), ... ) >>> staff = abjad.Staff([music_voice]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \context Voice = "Music_Voice" { <c' e'>4 << \context Voice = "On_Beat_Grace_Container" { \set fontSize = #-3 %! abjad.on_beat_grace_container(1) \slash %! abjad.on_beat_grace_container(2) \voiceTwo %! abjad.on_beat_grace_container(3) < e g \tweak font-size #0 \tweak transparent ##t f' >8 * 4/15 [ ( a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 c'8 * 4/15 b8 * 4/15 a8 * 4/15 b8 * 4/15 c'8 * 4/15 d'8 * 4/15 ) ] } \context Voice = "Music_Voice" { \voiceOne %! abjad.on_beat_grace_container(4) <d' f'>4 <e' g'>4 } >> \oneVoice %! abjad.on_beat_grace_container(5) <f' a'>4 } } .. container:: example Raises exception when duration of on-beat grace container exceeds duration of anchor container: >>> music_voice = abjad.Voice("c'4 d' e' f'", name="Music_Voice") >>> string = "g'8 a' b' c'' d'' c'' b' a' b' c'' d''" >>> result = abjad.on_beat_grace_container( ... string, music_voice[1:2], leaf_duration=(1, 8) ... ) Traceback (most recent call last): ... Exception: grace Duration(11, 8) exceeds anchor Duration(1, 4). """ from .Container import Container from .Selection import Selection from .Voice import Voice from abjad.spanners import beam from abjad.spanners import slur def _site(n): return Tag(f"abjad.on_beat_grace_container({n})") assert isinstance(anchor_voice_selection, Selection) if not anchor_voice_selection.are_contiguous_same_parent( ignore_before_after_grace=True): message = "selection must be contiguous in same parent:\n" message += f" {repr(anchor_voice_selection)}" raise Exception(message) on_beat_grace_container = OnBeatGraceContainer(contents, leaf_duration=leaf_duration) if not isinstance(anchor_voice_selection, Selection): raise Exception(f"must be selection:\n {repr(anchor_voice_selection)}") anchor_leaf = abjad_inspect(anchor_voice_selection).leaf(0) anchor_voice = abjad_inspect(anchor_leaf).parentage().get(Voice) if anchor_voice.name is None: raise Exception( f"anchor voice must be named:\n {repr(anchor_voice)}") anchor_voice_insert = Voice(name=anchor_voice.name) mutate(anchor_voice_selection).wrap(anchor_voice_insert) container = Container(simultaneous=True) mutate(anchor_voice_insert).wrap(container) container.insert(0, on_beat_grace_container) on_beat_grace_container._match_anchor_leaf() on_beat_grace_container._set_leaf_durations() insert_duration = abjad_inspect(anchor_voice_insert).duration() grace_container_duration = abjad_inspect( on_beat_grace_container).duration() if insert_duration < grace_container_duration: message = f"grace {repr(grace_container_duration)}" message += f" exceeds anchor {repr(insert_duration)}." raise Exception(message) if font_size is not None: string = rf"\set fontSize = #{font_size}" literal = LilyPondLiteral(string) attach(literal, on_beat_grace_container, tag=_site(1)) if not do_not_beam: beam(on_beat_grace_container[:]) if not do_not_slash: literal = LilyPondLiteral(r"\slash") attach(literal, on_beat_grace_container[0], tag=_site(2)) if not do_not_slur: slur(on_beat_grace_container[:]) voice_number_to_string = { 1: r"\voiceOne", 2: r"\voiceTwo", 3: r"\voiceThree", 4: r"\voiceFour", } first_grace = abjad_inspect(on_beat_grace_container).leaf(0) one_voice_literal = LilyPondLiteral(r"\oneVoice", format_slot="absolute_before") string = voice_number_to_string.get(grace_voice_number, None) if string is not None: literal detach(one_voice_literal, anchor_leaf) attach(LilyPondLiteral(string), first_grace, tag=_site(3)) string = voice_number_to_string.get(anchor_voice_number, None) if string is not None: detach(one_voice_literal, anchor_leaf) attach(LilyPondLiteral(string), anchor_leaf, tag=_site(4)) if not do_not_stop_polyphony: last_anchor_leaf = abjad_inspect(anchor_voice_selection).leaf(-1) next_leaf = abjad_inspect(last_anchor_leaf).leaf(1) if next_leaf is not None: literal = LilyPondLiteral(r"\oneVoice", format_slot="absolute_before") attach(literal, next_leaf, tag=_site(5)) return on_beat_grace_container
def rhythm( class_, selections, divisions=None, attach_lilypond_voice_commands=None, implicit_scaling=None, pitched_staff=None, simultaneous_selections=None, time_signatures=None, ): r""" Makes rhythm-styled LilyPond file. .. container:: example Makes rhythmic staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.attach(abjad.Beam(), selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { { % measure \time 3/4 s1 * 3/4 } % measure { % measure \time 4/8 s1 * 1/2 } % measure { % measure \time 1/4 s1 * 1/4 } % measure } \new RhythmicStaff { { % measure \time 3/4 c'8 [ c'8 c'8 c'8 c'8 c'8 ] } % measure { % measure \time 4/8 c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] } % measure { % measure \time 1/4 c'8 [ c'8 ] } % measure } >> .. container:: example Set time signatures explicitly: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.attach(abjad.Beam(), selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... [(6, 8), (4, 8), (2, 8)], ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { { % measure \time 6/8 s1 * 3/4 } % measure { % measure \time 4/8 s1 * 1/2 } % measure { % measure \time 2/8 s1 * 1/4 } % measure } \new RhythmicStaff { { % measure \time 6/8 c'8 [ c'8 c'8 c'8 c'8 c'8 ] } % measure { % measure \time 4/8 c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] } % measure { % measure \time 2/8 c'8 [ c'8 ] } % measure } >> .. container:: example Makes pitched staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.attach(abjad.Beam(), selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... pitched_staff=True, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { { % measure \time 3/4 s1 * 3/4 } % measure { % measure \time 4/8 s1 * 1/2 } % measure { % measure \time 1/4 s1 * 1/4 } % measure } \new Staff { { % measure \time 3/4 c'8 [ c'8 c'8 c'8 c'8 c'8 ] } % measure { % measure \time 4/8 c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] } % measure { % measure \time 1/4 c'8 [ c'8 ] } % measure } >> .. container:: example Makes simultaneous voices: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.attach(abjad.Beam(), selection[:]) ... >>> for note in abjad.iterate(selections).components(abjad.Note): ... note.written_pitch = abjad.NamedPitch("e'") ... >>> selection_1 = selections[0] + selections[1] + selections[2] >>> selections = [ ... maker(12 * [0], [(1, 16)]), ... maker(16 * [0], [(1, 32)]), ... maker(4 * [0], [(1, 16)]), ... ] >>> for selection in selections: ... abjad.attach(abjad.Beam(), selection[:]) ... >>> selection_2 = selections[0] + selections[1] + selections[2] >>> selections = { ... 'Voice 1': selection_1, ... 'Voice 2': selection_2, ... } >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> voice_1 = lilypond_file['Voice 1'] >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceOne'), voice_1) >>> voice_2 = lilypond_file['Voice 2'] >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceTwo'), voice_2) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { { % measure \time 3/4 s1 * 3/4 } % measure { % measure \time 4/8 s1 * 1/2 } % measure { % measure \time 1/4 s1 * 1/4 } % measure } \new Staff << \context Voice = "Voice 1" { \voiceOne e'8 [ e'8 e'8 e'8 e'8 e'8 ] e'16 [ e'16 e'16 e'16 e'16 e'16 e'16 e'16 ] e'8 [ e'8 ] } \context Voice = "Voice 2" { \voiceTwo c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'32 [ c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 ] c'16 [ c'16 c'16 c'16 ] } >> >> Returns LilyPond file. """ if isinstance(selections, list): for selection in selections: if not isinstance(selection, Selection): message = f'must be selection: {selection!r}.' raise TypeError(message) elif isinstance(selections, dict): for selection in selections.values(): if not isinstance(selection, Selection): message = f'must be selection: {selection!r}.' raise TypeError(message) else: message = f'must be list or dictionary: {selections!r}.' raise TypeError(message) score = Score() lilypond_file = LilyPondFile.new( score, includes=['default.ily', 'rhythm-maker-docs.ily'], ) if pitched_staff is None: if isinstance(selections, list): selections_ = selections elif isinstance(selections, dict): selections_ = selections.values() else: raise TypeError(selections) for note in iterate(selections_).leaves(Note): if note.written_pitch != NamedPitch("c'"): pitched_staff = True break if isinstance(selections, list): if divisions is None: duration = abjad_inspect(selections).duration() divisions = [duration] time_signatures = time_signatures or divisions maker = MeasureMaker(implicit_scaling=implicit_scaling) measures = maker(time_signatures) if pitched_staff: staff = Staff(measures) else: staff = Staff(measures, lilypond_type='RhythmicStaff') selections = sequence(selections).flatten(depth=-1) selections_ = copy.deepcopy(selections) try: agent = mutate(staff) measures = agent.replace_measure_contents(selections) except StopIteration: if pitched_staff: staff = Staff(selections_) else: staff = Staff( selections_, lilypond_type='RhythmicStaff', ) elif isinstance(selections, dict): voices = [] for voice_name in sorted(selections): selections_ = selections[voice_name] selections_ = sequence(selections_).flatten(depth=-1) selections_ = copy.deepcopy(selections_) voice = Voice(selections_, name=voice_name) if attach_lilypond_voice_commands: voice_name_to_command_string = { 'Voice 1': 'voiceOne', 'Voice 2': 'voiceTwo', 'Voice 3': 'voiceThree', 'Voice 4': 'voiceFour', } command_string = voice_name_to_command_string.get( voice_name, ) if command_string: command = LilyPondLiteral('\\' + command_string) attach(command, voice) voices.append(voice) staff = Staff(voices, is_simultaneous=True) if divisions is None: duration = abjad_inspect(staff).duration() divisions = [duration] else: message = 'must be list or dictionary of selections:' message += f' {selections!r}.' raise TypeError(message) score.append(staff) assert isinstance(divisions, collections.Sequence), repr(divisions) time_signatures = time_signatures or divisions context = Context(lilypond_type='GlobalContext') maker = MeasureMaker(implicit_scaling=implicit_scaling) measures = maker(time_signatures) context.extend(measures) score.insert(0, context) return lilypond_file
def _construct_sequential_music(self, music): # indicator sorting could be rewritten into a single list using tuplets # with t[0] being 'forward' or 'backward' and t[1] being the indicator # as this better preserves attachment order. Not clear if we need it. container = core.Container() previous_leaf = None apply_forward = [] apply_backward = [] # sort events into forward or backwards attaching # and attach them to the proper leaf for x in music: if isinstance(x, core.Component) and not isinstance( x, core.GraceContainer ): for indicator in apply_forward: attach(indicator, x) if previous_leaf: for indicator in apply_backward: attach(indicator, previous_leaf) else: for indicator in apply_backward: attach(indicator, x) apply_forward[:] = [] apply_backward[:] = [] previous_leaf = x container.append(x) else: if isinstance(x, (abjad_indicators.BarLine,)): apply_backward.append(x) elif isinstance( x, abjad_indicators.LilyPondLiteral ) and x.name in (r"\break", r"\breathe", r"\pageBreak"): apply_backward.append(x) else: apply_forward.append(x) # attach remaining events to last leaf # or to the container itself if there were no leaves if previous_leaf: for indicator in apply_forward: attach(indicator, previous_leaf) for indicator in apply_backward: attach(indicator, previous_leaf) else: for indicator in apply_forward: attach(indicator, container) for indicator in apply_backward: attach(indicator, container) return container
def rhythm( class_, selections, divisions=None, attach_lilypond_voice_commands=None, implicit_scaling=None, pitched_staff=None, simultaneous_selections=None, time_signatures=None, ): r""" Makes rhythm-styled LilyPond file. .. container:: example Makes rhythmic staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { \time 3/4 s1 * 3/4 \time 4/8 s1 * 1/2 \time 1/4 s1 * 1/4 } \new RhythmicStaff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Set time signatures explicitly: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... [(6, 8), (4, 8), (2, 8)], ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { \time 6/8 s1 * 3/4 \time 4/8 s1 * 1/2 \time 2/8 s1 * 1/4 } \new RhythmicStaff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Makes pitched staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... pitched_staff=True, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { \time 3/4 s1 * 3/4 \time 4/8 s1 * 1/2 \time 1/4 s1 * 1/4 } \new Staff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Makes simultaneous voices: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> for note in abjad.iterate(selections).components(abjad.Note): ... note.written_pitch = abjad.NamedPitch("e'") ... >>> selection_1 = selections[0] + selections[1] + selections[2] >>> selections = [ ... maker(12 * [0], [(1, 16)]), ... maker(16 * [0], [(1, 32)]), ... maker(4 * [0], [(1, 16)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> selection_2 = selections[0] + selections[1] + selections[2] >>> selections = { ... 'Voice_1': selection_1, ... 'Voice_2': selection_2, ... } >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> voice_1 = lilypond_file['Voice_1'] >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceOne'), voice_1) >>> voice_2 = lilypond_file['Voice_2'] >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceTwo'), voice_2) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { s1 * 3/4 s1 * 1/2 s1 * 1/4 } \new Staff << \context Voice = "Voice_1" { \voiceOne e'8 [ e'8 e'8 e'8 e'8 e'8 ] e'16 [ e'16 e'16 e'16 e'16 e'16 e'16 e'16 ] e'8 [ e'8 ] } \context Voice = "Voice_2" { \voiceTwo c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'32 [ c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 ] c'16 [ c'16 c'16 c'16 ] } >> >> Returns LilyPond file. """ if isinstance(selections, list): for selection in selections: if not isinstance(selection, Selection): raise TypeError(f"must be selection: {selection!r}.") elif isinstance(selections, dict): for selection in selections.values(): if not isinstance(selection, Selection): raise TypeError(f"must be selection: {selection!r}.") else: raise TypeError(f"must be list or dictionary: {selections!r}.") score = Score() lilypond_file = LilyPondFile.new( score, includes=["default.ily", "rhythm-maker-docs.ily"] ) if pitched_staff is None: if isinstance(selections, list): selections_ = selections elif isinstance(selections, dict): selections_ = selections.values() else: raise TypeError(selections) for note in iterate(selections_).leaves(Note): if note.written_pitch != NamedPitch("c'"): pitched_staff = True break if isinstance(selections, list): if divisions is None: duration = abjad_inspect(selections).duration() divisions = [duration] time_signatures = time_signatures or divisions time_signatures = [TimeSignature(_) for _ in time_signatures] if pitched_staff: staff = Staff() else: staff = Staff(lilypond_type="RhythmicStaff") staff.extend(selections) elif isinstance(selections, dict): voices = [] for voice_name in sorted(selections): selections_ = selections[voice_name] selections_ = sequence(selections_).flatten(depth=-1) selections_ = copy.deepcopy(selections_) voice = Voice(selections_, name=voice_name) if attach_lilypond_voice_commands: voice_name_to_command_string = { "Voice_1": "voiceOne", "Voice_2": "voiceTwo", "Voice_3": "voiceThree", "Voice_4": "voiceFour", } command_string = voice_name_to_command_string.get( voice_name ) if command_string: command = LilyPondLiteral("\\" + command_string) attach(command, voice) voices.append(voice) staff = Staff(voices, is_simultaneous=True) if divisions is None: duration = abjad_inspect(staff).duration() divisions = [duration] else: message = "must be list or dictionary of selections:" message += f" {selections!r}." raise TypeError(message) score.append(staff) assert isinstance(divisions, collections.abc.Sequence), repr(divisions) time_signatures = time_signatures or divisions context = Context(lilypond_type="GlobalContext") skips = [] for time_signature in time_signatures: skip = Skip(1) skip.multiplier = time_signature attach(time_signature, skip, context="Score") skips.append(skip) context.extend(skips) score.insert(0, context) return lilypond_file
def _split_by_durations( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, repeat_ties=False, ): import abjad durations = [Duration(_) for _ in durations] durations = sequence(durations) leaf_duration = inspect(self).duration() if cyclic: durations = durations.repeat_to_weight(leaf_duration) if sum(durations) < leaf_duration: last_duration = leaf_duration - sum(durations) durations = list(durations) durations.append(last_duration) durations = sequence(durations) durations = durations.truncate(weight=leaf_duration) result_selections = [] # detach grace containers grace_container = self._detach_grace_container() after_grace_container = self._detach_after_grace_container() leaf_prolation = inspect(self).parentage().prolation for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation selection = new_leaf._set_duration( preprolated_duration, repeat_ties=repeat_ties, ) result_selections.append(selection) result_components = sequence(result_selections).flatten(depth=-1) result_components = select(result_components) result_leaves = select(result_components).leaves() assert all(isinstance(_, abjad.Selection) for _ in result_selections) assert all(isinstance(_, Component) for _ in result_components) assert result_leaves.are_leaves() if inspect(self).has_spanner(abjad.Tie): for leaf in result_leaves: detach(abjad.Tie, leaf) # strip result leaves of indicators (other than multipliers) for leaf in result_leaves: multiplier = inspect(leaf).indicator(Multiplier) detach(object, leaf) if multiplier is not None: attach(multiplier, leaf) # replace leaf with flattened result selection = select(self) parent, start, stop = selection._get_parent_and_start_stop_indices() if parent: parent.__setitem__(slice(start, stop + 1), result_components) else: selection._give_dominant_spanners(result_components) selection._withdraw_from_crossing_spanners() # fracture spanners if fracture_spanners: first_selection = result_selections[0] for spanner in inspect(first_selection[-1]).spanners(): index = spanner._index(first_selection[-1]) spanner._fracture(index, direction=enums.Right) last_selection = result_selections[-1] for spanner in inspect(last_selection[0]).spanners(): index = spanner._index(last_selection[0]) spanner._fracture(index, direction=enums.Left) for middle_selection in result_selections[1:-1]: spanners = inspect(middle_selection[0]).spanners() for spanner in spanners: index = spanner._index(middle_selection[0]) spanner._fracture(index, direction=enums.Left) spanners = inspect(middle_selection[-1]).spanners() for spanner in spanners: index = spanner._index(middle_selection[-1]) spanner._fracture(index, direction=enums.Right) # move indicators first_result_leaf = result_leaves[0] last_result_leaf = result_leaves[-1] for indicator in inspect(self).indicators(): if isinstance(indicator, Multiplier): continue detach(indicator, self) direction = getattr(indicator, '_time_orientation', enums.Left) if direction is enums.Left: attach(indicator, first_result_leaf) elif direction == enums.Right: attach(indicator, last_result_leaf) else: raise ValueError(direction) # move grace containers if grace_container is not None: container = grace_container[0] assert isinstance(container, abjad.GraceContainer), repr(container) attach(container, first_result_leaf) if after_grace_container is not None: container = after_grace_container[0] prototype = abjad.AfterGraceContainer assert isinstance(container, prototype), repr(container) attach(container, last_result_leaf) if isinstance(result_components[0], abjad.Tuplet): abjad.mutate(result_components).fuse() # tie split notes if isinstance(self, (abjad.Note, abjad.Chord)) and tie_split_notes: result_leaves._attach_tie_to_leaves(repeat_ties=repeat_ties, ) assert isinstance(result_selections, list), repr(result_selections) assert all(isinstance(_, abjad.Selection) for _ in result_selections) return result_selections
def rhythm( class_, selections, divisions=None, attach_lilypond_voice_commands=None, implicit_scaling=None, pitched_staff=None, simultaneous_selections=None, time_signatures=None, ): r""" Makes rhythm-styled LilyPond file. .. container:: example Makes rhythmic staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { \time 3/4 s1 * 3/4 \time 4/8 s1 * 1/2 \time 1/4 s1 * 1/4 } \new RhythmicStaff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Set time signatures explicitly: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... [(6, 8), (4, 8), (2, 8)], ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> score = lilypond_file[abjad.Score] >>> abjad.f(score) \new Score << \new GlobalContext { \time 6/8 s1 * 3/4 \time 4/8 s1 * 1/2 \time 2/8 s1 * 1/4 } \new RhythmicStaff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Makes pitched staff: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... pitched_staff=True, ... ) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { \time 3/4 s1 * 3/4 \time 4/8 s1 * 1/2 \time 1/4 s1 * 1/4 } \new Staff { c'8 [ c'8 c'8 c'8 c'8 c'8 ] c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'8 [ c'8 ] } >> .. container:: example Makes simultaneous voices: >>> divisions = [(3, 4), (4, 8), (1, 4)] >>> maker = abjad.NoteMaker() >>> selections = [ ... maker(6 * [0], [(1, 8)]), ... maker(8 * [0], [(1, 16)]), ... maker(2 * [0], [(1, 8)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> for note in abjad.iterate(selections).components(abjad.Note): ... note.written_pitch = abjad.NamedPitch("e'") ... >>> selection_1 = selections[0] + selections[1] + selections[2] >>> selections = [ ... maker(12 * [0], [(1, 16)]), ... maker(16 * [0], [(1, 32)]), ... maker(4 * [0], [(1, 16)]), ... ] >>> for selection in selections: ... abjad.beam(selection[:]) ... >>> selection_2 = selections[0] + selections[1] + selections[2] >>> selections = { ... 'Voice_1': selection_1, ... 'Voice_2': selection_2, ... } >>> lilypond_file = abjad.LilyPondFile.rhythm( ... selections, ... divisions, ... ) >>> voice_1 = lilypond_file['Voice_1'] >>> literal = abjad.LilyPondLiteral(r'\voiceOne', "opening") >>> abjad.attach(literal, voice_1) >>> voice_2 = lilypond_file['Voice_2'] >>> literal = abjad.LilyPondLiteral(r'\voiceTwo', "opening") >>> abjad.attach(literal, voice_2) >>> abjad.show(lilypond_file) # doctest: +SKIP .. docs:: >>> abjad.f(lilypond_file[abjad.Score]) \new Score << \new GlobalContext { s1 * 3/4 s1 * 1/2 s1 * 1/4 } \new Staff << \context Voice = "Voice_1" { \voiceOne e'8 [ e'8 e'8 e'8 e'8 e'8 ] e'16 [ e'16 e'16 e'16 e'16 e'16 e'16 e'16 ] e'8 [ e'8 ] } \context Voice = "Voice_2" { \voiceTwo c'16 [ c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 c'16 ] c'32 [ c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 c'32 ] c'16 [ c'16 c'16 c'16 ] } >> >> Returns LilyPond file. """ if isinstance(selections, Selection): pass elif isinstance(selections, list): for selection in selections: if not isinstance(selection, Selection): raise TypeError(f"must be selection: {selection!r}.") elif isinstance(selections, dict): for selection in selections.values(): if not isinstance(selection, Selection): raise TypeError(f"must be selection: {selection!r}.") else: raise TypeError(f"must be list or dictionary: {selections!r}.") score = Score() lilypond_file = LilyPondFile.new( score, includes=["default.ily", "rhythm-maker-docs.ily"] ) if pitched_staff is None: if isinstance(selections, (list, Selection)): selections_ = selections elif isinstance(selections, dict): selections_ = selections.values() else: raise TypeError(selections) for note in select(selections_).notes(): if note.written_pitch != NamedPitch("c'"): pitched_staff = True break chords = select(selections_).chords() if chords: pitched_staff = True if isinstance(selections, (list, Selection)): if divisions is None: duration = abjad_inspect(selections).duration() divisions = [duration] time_signatures = time_signatures or divisions time_signatures = [TimeSignature(_) for _ in time_signatures] if pitched_staff: staff = Staff() else: staff = Staff(lilypond_type="RhythmicStaff") staff.extend(selections) elif isinstance(selections, dict): voices = [] for voice_name in sorted(selections): selections_ = selections[voice_name] selections_ = sequence(selections_).flatten(depth=-1) selections_ = copy.deepcopy(selections_) voice = Voice(selections_, name=voice_name) if attach_lilypond_voice_commands: voice_name_to_command_string = { "Voice_1": "voiceOne", "Voice_2": "voiceTwo", "Voice_3": "voiceThree", "Voice_4": "voiceFour", } command_string = voice_name_to_command_string.get(voice_name) if command_string: command = LilyPondLiteral("\\" + command_string) attach(command, voice) voices.append(voice) staff = Staff(voices, simultaneous=True) if divisions is None: duration = abjad_inspect(staff).duration() divisions = [duration] else: message = "must be list or dictionary of selections:" message += f" {selections!r}." raise TypeError(message) score.append(staff) assert isinstance(divisions, collections.abc.Sequence), repr(divisions) time_signatures = time_signatures or divisions context = Context(lilypond_type="GlobalContext") skips = [] for time_signature in time_signatures: skip = Skip(1) skip.multiplier = time_signature attach(time_signature, skip, context="Score") skips.append(skip) context.extend(skips) score.insert(0, context) return lilypond_file
def _split_by_durations( self, durations, cyclic=False, tie_split_notes=True, repeat_ties=False, ): from .AfterGraceContainer import AfterGraceContainer from .Chord import Chord from .GraceContainer import GraceContainer from .Note import Note from .Selection import Selection from .Tuplet import Tuplet durations = [Duration(_) for _ in durations] durations = Sequence(durations) leaf_duration = inspect(self).duration() if cyclic: durations = durations.repeat_to_weight(leaf_duration) if sum(durations) < leaf_duration: last_duration = leaf_duration - sum(durations) durations = list(durations) durations.append(last_duration) durations = Sequence(durations) durations = durations.truncate(weight=leaf_duration) originally_tied = inspect(self).has_indicator(TieIndicator) originally_repeat_tied = inspect(self).has_indicator(RepeatTie) result_selections = [] grace_container = self._detach_grace_container() after_grace_container = self._detach_after_grace_container() leaf_prolation = inspect(self).parentage().prolation for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation selection = new_leaf._set_duration( preprolated_duration, repeat_ties=repeat_ties, ) result_selections.append(selection) result_components = Sequence(result_selections).flatten(depth=-1) result_components = select(result_components) result_leaves = select(result_components).leaves(grace_notes=False) assert all(isinstance(_, Selection) for _ in result_selections) assert all(isinstance(_, Component) for _ in result_components) assert result_leaves.are_leaves() # for leaf in result_leaves: # detach(Tie, leaf) # strip result leaves of all indicators for leaf in result_leaves: detach(object, leaf) # replace leaf with flattened result if self._parent is not None: mutate(self).replace(result_components) # move indicators first_result_leaf = result_leaves[0] last_result_leaf = result_leaves[-1] for indicator in inspect(self).indicators(): detach(indicator, self) direction = getattr(indicator, '_time_orientation', enums.Left) if direction is enums.Left: attach(indicator, first_result_leaf) elif direction == enums.Right: attach(indicator, last_result_leaf) else: raise ValueError(direction) # move grace containers if grace_container is not None: container = grace_container[0] assert isinstance(container, GraceContainer), repr(container) attach(container, first_result_leaf) if after_grace_container is not None: container = after_grace_container[0] prototype = AfterGraceContainer assert isinstance(container, prototype), repr(container) attach(container, last_result_leaf) if isinstance(result_components[0], Tuplet): mutate(result_components).fuse() # tie split notes if (tie_split_notes and isinstance(self, (Note, Chord)) and 1 < len(result_leaves)): result_leaves._attach_tie_to_leaves(repeat_ties=repeat_ties) #assert not inspect(result_leaves[0]).has_indicator(RepeatTie) detach(RepeatTie, result_leaves[0]) #assert not inspect(result_leaves[-1]).has_indicator(TieIndicator) detach(TieIndicator, result_leaves[-1]) if originally_repeat_tied: tie = RepeatTie() attach(tie, result_leaves[0]) if originally_tied: tie = TieIndicator() attach(tie, result_leaves[-1]) assert isinstance(result_leaves, Selection) assert all(isinstance(_, Leaf) for _ in result_leaves) return result_leaves
def _move_indicators(self, recipient_component): for wrapper in inspect(self).wrappers(): detach(wrapper, self) attach(wrapper, recipient_component)
def attach_defaults(self, argument) -> typing.List: """ Attaches defaults to all staff and staff group contexts in ``argument`` when ``argument`` is a score. Attaches defaults to ``argument`` (without iterating ``argument``) when ``argument`` is a staff or staff group. Returns list of one wrapper for every indicator attached. """ assert isinstance(argument, (Score, Staff, StaffGroup)), repr(argument) wrappers: typing.List[Wrapper] = [] tag = const.REMOVE_ALL_EMPTY_STAVES empty_prototype = (MultimeasureRest, Skip) prototype = (Staff, StaffGroup) if isinstance(argument, Score): staff__groups = select(argument).components(prototype) staves = select(argument).components(Staff) elif isinstance(argument, Staff): staff__groups = [argument] staves = [argument] else: assert isinstance(argument, StaffGroup), repr(argument) staff__groups = [argument] staves = [] for staff__group in staff__groups: leaf = None voices = select(staff__group).components(Voice) # find leaf 0 in first nonempty voice for voice in voices: leaves = [] for leaf_ in select(voice).leaves(): if inspect(leaf_).annotation(const.HIDDEN) is not True: leaves.append(leaf_) if not all(isinstance(_, empty_prototype) for _ in leaves): leaf = inspect(voice).leaf(0) break # otherwise, find first leaf in voice in non-removable staff if leaf is None: for voice in voices: voice_might_vanish = False for component in inspect(voice).parentage(): if inspect(component).annotation(tag) is True: voice_might_vanish = True if not voice_might_vanish: leaf = inspect(voice).leaf(0) if leaf is not None: break # otherwise, as last resort find first leaf in first voice if leaf is None: leaf = inspect(voices[0]).leaf(0) if leaf is None: continue instrument = inspect(leaf).indicator(instruments.Instrument) if instrument is None: string = "default_instrument" instrument = inspect(staff__group).annotation(string) if instrument is not None: wrapper = attach( instrument, leaf, context=staff__group.lilypond_type, tag="abjad.ScoreTemplate.attach_defaults", wrapper=True, ) wrappers.append(wrapper) margin_markup = inspect(leaf).indicator(MarginMarkup) if margin_markup is None: string = "default_margin_markup" margin_markup = inspect(staff__group).annotation(string) if margin_markup is not None: wrapper = attach( margin_markup, leaf, tag=Tag("-PARTS").prepend( "abjad.ScoreTemplate.attach_defaults" ), wrapper=True, ) wrappers.append(wrapper) for staff in staves: leaf = inspect(staff).leaf(0) clef = inspect(leaf).indicator(Clef) if clef is not None: continue clef = inspect(staff).annotation("default_clef") if clef is not None: wrapper = attach( clef, leaf, tag="abjad.ScoreTemplate.attach_defaults", wrapper=True, ) wrappers.append(wrapper) return wrappers
def _split_by_durations(self, durations, cyclic=False): from .Chord import Chord from .Note import Note from .Selection import Selection from .Tuplet import Tuplet durations = [Duration(_) for _ in durations] durations = Sequence(durations) leaf_duration = inspect(self).duration() if cyclic: durations = durations.repeat_to_weight(leaf_duration) if sum(durations) < leaf_duration: last_duration = leaf_duration - sum(durations) durations = list(durations) durations.append(last_duration) durations = Sequence(durations) durations = durations.truncate(weight=leaf_duration) originally_tied = inspect(self).has_indicator(Tie) originally_repeat_tied = inspect(self).has_indicator(RepeatTie) result_selections = [] # detach grace containers before_grace_container = self._before_grace_container if before_grace_container is not None: detach(before_grace_container, self) after_grace_container = self._after_grace_container if after_grace_container is not None: detach(after_grace_container, self) # do other things leaf_prolation = inspect(self).parentage().prolation for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation selection = new_leaf._set_duration(preprolated_duration) result_selections.append(selection) result_components = Sequence(result_selections).flatten(depth=-1) result_components = select(result_components) result_leaves = select(result_components).leaves(grace=False) assert all(isinstance(_, Selection) for _ in result_selections) assert all(isinstance(_, Component) for _ in result_components) assert result_leaves.are_leaves() # strip result leaves of all indicators for leaf in result_leaves: detach(object, leaf) # replace leaf with flattened result if inspect(self).parentage().parent is not None: mutate(self).replace(result_components) # move indicators first_result_leaf = result_leaves[0] last_result_leaf = result_leaves[-1] for indicator in inspect(self).indicators(): detach(indicator, self) direction = getattr(indicator, "_time_orientation", enums.Left) if direction is enums.Left: attach(indicator, first_result_leaf) elif direction == enums.Right: attach(indicator, last_result_leaf) else: raise ValueError(direction) # reattach grace containers if before_grace_container is not None: attach(before_grace_container, first_result_leaf) if after_grace_container is not None: attach(after_grace_container, last_result_leaf) # fuse tuplets if isinstance(result_components[0], Tuplet): mutate(result_components).fuse() # tie split notes if isinstance(self, (Note, Chord)) and 1 < len(result_leaves): result_leaves._attach_tie_to_leaves() if originally_repeat_tied and not inspect( result_leaves[0]).has_indicator(RepeatTie): attach(RepeatTie(), result_leaves[0]) if originally_tied and not inspect( result_leaves[-1]).has_indicator(Tie): attach(Tie(), result_leaves[-1]) assert isinstance(result_leaves, Selection) assert all(isinstance(_, Leaf) for _ in result_leaves) return result_leaves
def _construct_sequential_music(self, music): # indicator sorting could be rewritten into a single list using tuplets # with t[0] being 'forward' or 'backward' and t[1] being the indicator # as this better preserves attachment order. Not clear if we need it. container = core.Container() previous_leaf = None apply_forward = [] apply_backward = [] # sort events into forward or backwards attaching # and attach them to the proper leaf for x in music: if isinstance(x, core.Component) and not isinstance( x, core.BeforeGraceContainer ): for indicator in apply_forward: attach(indicator, x) if previous_leaf: for indicator in apply_backward: attach(indicator, previous_leaf) else: for indicator in apply_backward: attach(indicator, x) apply_forward[:] = [] apply_backward[:] = [] previous_leaf = x container.append(x) else: if isinstance(x, (abjad_indicators.BarLine,)): apply_backward.append(x) elif isinstance(x, abjad_indicators.LilyPondLiteral) and x.argument in ( r"\break", r"\breathe", r"\pageBreak", ): apply_backward.append(x) else: apply_forward.append(x) # attach remaining events to last leaf # or to the container itself if there were no leaves if previous_leaf: for indicator in apply_forward: attach(indicator, previous_leaf) for indicator in apply_backward: attach(indicator, previous_leaf) else: for indicator in apply_forward: attach(indicator, container) for indicator in apply_backward: attach(indicator, container) return container
def text_spanner( argument: typing.Union[Component, Selection], *, selector: typings.Selector = 'abjad.select().leaves()', start_text_span: StartTextSpan = None, stop_text_span: StopTextSpan = None, ) -> None: r""" Attaches text span indicators. .. container:: example Single spanner: >>> staff = abjad.Staff("c'4 d' e' f'") >>> start_text_span = abjad.StartTextSpan( ... left_text=abjad.Markup('pont.').upright(), ... right_text=abjad.Markup('tasto').upright(), ... style='solid_line_with_arrow', ... ) >>> abjad.text_spanner(staff[:], start_text_span=start_text_span) >>> abjad.override(staff[0]).text_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \once \override TextSpanner.staff-padding = #4 c'4 - \abjad_solid_line_with_arrow - \tweak bound-details.left.text \markup { \concat { \upright pont. \hspace #0.5 } } - \tweak bound-details.right.text \markup { \upright tasto } \startTextSpan d'4 e'4 f'4 \stopTextSpan } .. container:: example Enchained spanners: >>> staff = abjad.Staff("c'4 d' e' f' r") >>> start_text_span = abjad.StartTextSpan( ... left_text=abjad.Markup('pont.').upright(), ... style='dashed_line_with_arrow', ... ) >>> abjad.text_spanner(staff[:3], start_text_span=start_text_span) >>> start_text_span = abjad.StartTextSpan( ... left_text=abjad.Markup('tasto').upright(), ... right_text=abjad.Markup('pont.').upright(), ... style='dashed_line_with_arrow', ... ) >>> abjad.text_spanner(staff[-3:], start_text_span=start_text_span) >>> abjad.override(staff).text_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override TextSpanner.staff-padding = #4 } { c'4 - \abjad_dashed_line_with_arrow - \tweak bound-details.left.text \markup { \concat { \upright pont. \hspace #0.5 } } \startTextSpan d'4 e'4 \stopTextSpan - \abjad_dashed_line_with_arrow - \tweak bound-details.left.text \markup { \concat { \upright tasto \hspace #0.5 } } - \tweak bound-details.right.text \markup { \upright pont. } \startTextSpan f'4 r4 \stopTextSpan } >>> staff = abjad.Staff("c'4 d' e' f' r") >>> start_text_span = abjad.StartTextSpan( ... left_text=abjad.Markup('pont.').upright(), ... style='dashed_line_with_arrow', ... ) >>> abjad.text_spanner(staff[:3], start_text_span=start_text_span) >>> start_text_span = abjad.StartTextSpan( ... left_text=abjad.Markup('tasto').upright(), ... style='solid_line_with_hook', ... ) >>> abjad.text_spanner(staff[-3:], start_text_span=start_text_span) >>> abjad.override(staff).text_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override TextSpanner.staff-padding = #4 } { c'4 - \abjad_dashed_line_with_arrow - \tweak bound-details.left.text \markup { \concat { \upright pont. \hspace #0.5 } } \startTextSpan d'4 e'4 \stopTextSpan - \abjad_solid_line_with_hook - \tweak bound-details.left.text \markup { \concat { \upright tasto \hspace #0.5 } } \startTextSpan f'4 r4 \stopTextSpan } """ import abjad start_text_span = start_text_span or StartTextSpan() stop_text_span = stop_text_span or StopTextSpan() if isinstance(selector, str): selector = eval(selector) assert isinstance(selector, Expression) argument = selector(argument) leaves = select(argument).leaves() start_leaf = leaves[0] stop_leaf = leaves[-1] attach(start_text_span, start_leaf) attach(stop_text_span, stop_leaf)