def __init__(self, operator_string="<", length=None): Inequality.__init__(self, operator_string=operator_string) if length is None: length = mathtools.Infinity() assert 0 <= length infinities = (mathtools.Infinity(), mathtools.NegativeInfinity()) if length not in infinities: length = int(length) self._length = length
def __init__( self, start_offset=mathtools.NegativeInfinity(), stop_offset=mathtools.Infinity(), divisions=None, forbid_fusing=None, forbid_splitting=None, layer=None, minimum_duration=None, music=None, music_specifier=None, original_start_offset=None, original_stop_offset=None, voice_name=None, ): abjad.Timespan.__init__( self, start_offset=start_offset, stop_offset=stop_offset, ) if divisions is not None: divisions = tuple(abjad.Duration(_) for _ in divisions) assert sum(divisions) == self.duration self._divisions = divisions if forbid_fusing is not None: forbid_fusing = bool(forbid_fusing) self._forbid_fusing = forbid_fusing if forbid_splitting is not None: forbid_splitting = bool(forbid_splitting) self._forbid_splitting = forbid_splitting if layer is not None: layer = int(layer) self._layer = layer if minimum_duration is not None: minimum_duration = abjad.Duration(minimum_duration) self._minimum_duration = minimum_duration #if music is not None: # assert inspect(music).get_duration() == self.duration self._music = music #if music_specifier is not None: # assert isinstance(music_specifier, tsmakers.MusicSpecifier), \ # music_specifier self._music_specifier = music_specifier if original_start_offset is not None: original_start_offset = abjad.Offset(original_start_offset) else: original_start_offset = self.start_offset self._original_start_offset = original_start_offset if original_stop_offset is not None: original_stop_offset = abjad.Offset(original_stop_offset) else: original_stop_offset = self.stop_offset self._original_stop_offset = original_stop_offset self._voice_name = voice_name
def __init__( self, start_offset=mathtools.NegativeInfinity(), stop_offset=mathtools.Infinity(), layer=None, voice_name=None, ): abjad.Timespan.__init__( self, start_offset=start_offset, stop_offset=stop_offset, ) if layer is not None: layer = int(layer) self._layer = layer self._voice_name = voice_name
class Dynamic(object): r""" Dynamic. .. container:: example Initializes from dynamic name: >>> voice = abjad.Voice("c'8 d'8 e'8 f'8") >>> dynamic = abjad.Dynamic('f') >>> abjad.attach(dynamic, voice[0]) .. docs:: >>> abjad.f(voice) \new Voice { c'8 \f d'8 e'8 f'8 } >>> abjad.show(voice) # doctest: +SKIP .. container:: example Initializes from other dynamic: >>> dynamic_1 = abjad.Dynamic('f') >>> dynamic_2 = abjad.Dynamic(dynamic_1) >>> dynamic_1 Dynamic('f') >>> dynamic_2 Dynamic('f') .. container:: example Initializes niente: >>> abjad.Dynamic('niente') Dynamic('niente', direction=Down, name_is_textual=True) .. container:: example Simultaneous dynamics in a single staff: >>> voice_1 = abjad.Voice("e'8 g'8 f'8 a'8") >>> abjad.attach(abjad.Dynamic('f'), voice_1[0], context='Voice') >>> literal = abjad.LilyPondLiteral(r"\voiceOne", "opening") >>> abjad.attach(literal, voice_1) >>> abjad.override(voice_1).dynamic_line_spanner.direction = abjad.Up >>> voice_2 = abjad.Voice("c'2") >>> literal = abjad.LilyPondLiteral(r"\voiceTwo", "opening") >>> abjad.attach(literal, voice_2) >>> abjad.attach(abjad.Dynamic('mf'), voice_2[0], context='Voice') >>> staff = abjad.Staff([voice_1, voice_2], simultaneous=True) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff << \new Voice \with { \override DynamicLineSpanner.direction = #up } { \voiceOne e'8 \f g'8 f'8 a'8 } \new Voice { \voiceTwo c'2 \mf } >> >>> for leaf in abjad.select(staff).leaves(): ... leaf, abjad.inspect(leaf).effective(abjad.Dynamic) ... (Note("e'8"), Dynamic('f')) (Note("g'8"), Dynamic('f')) (Note("f'8"), Dynamic('f')) (Note("a'8"), Dynamic('f')) (Note("c'2"), Dynamic('mf')) .. container:: example exception Errors on nondynamic input: >>> abjad.Dynamic('text') Traceback (most recent call last): ... Exception: the letter 't' (in 'text') is not a dynamic. """ ### CLASS VARIABLES ### __slots__ = ( "_command", "_direction", "_format_hairpin_stop", "_hide", "_leak", "_name", "_name_is_textual", "_ordinal", "_sforzando", "_tweaks", ) _composite_dynamic_name_to_steady_state_dynamic_name = { "fp": "p", "sf": "f", "sff": "ff", "sfp": "p", "sfpp": "pp", "sffp": "p", "sffpp": "pp", "sfz": "f", "sp": "p", "spp": "pp", "rfz": "f", } _context = "Voice" _dynamic_name_to_dynamic_ordinal = { "ppppp": -6, "pppp": -5, "ppp": -4, "pp": -3, "p": -2, "niente": mathtools.NegativeInfinity(), "mp": -1, "mf": 1, "f": 2, "ff": 3, "fff": 4, "ffff": 5, "fffff": 6, } _dynamic_names = ( "ppppp", "pppp", "ppp", "pp", "p", "mp", "mf", "f", "ff", "fff", "ffff", "fffff", "fp", "sf", "sff", "sp", "spp", "sfz", "sffz", "sfffz", "sffp", "sffpp", "sfp", "sfpp", "rfz", "niente", ) _dynamic_ordinal_to_dynamic_name = { -6: "ppppp", -5: "pppp", -4: "ppp", -3: "pp", -2: "p", -1: "mp", mathtools.NegativeInfinity(): "niente", 1: "mf", 2: "f", 3: "ff", 4: "fff", 5: "ffff", 6: "fffff", } _format_slot = "after" _lilypond_dynamic_commands = [_ for _ in _dynamic_names if not _ == "niente"] _lilypond_dynamic_alphabet = "fmprsz" _parameter = "DYNAMIC" _persistent = True _to_width = {'"f"': 2, '"mf"': 3.5, '"mp"': 3.5, '"p"': 2, "sfz": 2.5} ### INITIALIZER ### def __init__( self, name: typing.Union[str, "Dynamic"] = "f", *, command: str = None, direction: enums.VerticalAlignment = None, format_hairpin_stop: bool = None, hide: bool = None, leak: bool = None, name_is_textual: bool = None, ordinal: typing.Union[int, Infinity, NegativeInfinity] = None, sforzando: bool = None, tweaks: LilyPondTweakManager = None, ) -> None: if name is not None: assert isinstance(name, (str, type(self))), repr(name) if isinstance(name, type(self)): name_ = name.name elif isinstance(name, str): name_ = name if name_ == "niente": if name_is_textual not in (None, True): raise Exception("niente dynamic name is always textual.") name_is_textual = True if not name_is_textual: for letter in name_.strip('"'): if letter not in self._lilypond_dynamic_alphabet: message = f"the letter {letter!r} (in {name!r})" message += " is not a dynamic." raise Exception(message) self._name = name_ if command is not None: assert isinstance(command, str), repr(command) assert command.startswith("\\"), repr(command) self._command = command if direction is not None: assert direction in (enums.Down, enums.Up), repr(direction) self._direction = direction if format_hairpin_stop is not None: format_hairpin_stop = bool(format_hairpin_stop) self._format_hairpin_stop = format_hairpin_stop if hide is not None: hide = bool(hide) self._hide = hide if leak is not None: leak = bool(leak) self._leak = leak if name_is_textual is not None: name_is_textual = bool(name_is_textual) self._name_is_textual = name_is_textual if ordinal is not None: assert isinstance(ordinal, (int, Infinity, NegativeInfinity)) self._ordinal = ordinal if sforzando is not None: sforzando = bool(sforzando) self._sforzando = sforzando if tweaks is not None: assert isinstance(tweaks, LilyPondTweakManager), repr(tweaks) self._tweaks = LilyPondTweakManager.set_tweaks(self, tweaks) ### SPECIAL METHODS ### def __eq__(self, argument) -> bool: """ Is true when ``argument`` equals dynamic. .. container:: example >>> dynamic_1 = abjad.Dynamic('p') >>> dynamic_2 = abjad.Dynamic('p') >>> dynamic_3 = abjad.Dynamic('f') >>> dynamic_1 == dynamic_1 True >>> dynamic_1 == dynamic_2 True >>> dynamic_1 == dynamic_3 False >>> dynamic_2 == dynamic_1 True >>> dynamic_2 == dynamic_2 True >>> dynamic_2 == dynamic_3 False >>> dynamic_3 == dynamic_1 False >>> dynamic_3 == dynamic_2 False >>> dynamic_3 == dynamic_3 True """ if not isinstance(argument, type(self)): return False if self.name == argument.name and self.ordinal == argument.ordinal: return True return False def __hash__(self) -> int: """ Hashes dynamic. Redefined in tandem with __eq__. """ hash_values = StorageFormatManager(self).get_hash_values() try: result = hash(hash_values) except TypeError: raise TypeError(f"unhashable type: {self}") return result def __repr__(self): """ Gets interpreter representation. """ return StorageFormatManager(self).get_repr_format() ### PRIVATE METHODS ### def _attachment_test_all(self, component_expression): import abjad if not isinstance(component_expression, abjad.Leaf): strings = [f"Must be leaf (not {component_expression})."] return strings return True def _format_effort_dynamic(self): name = self.name.strip('"') before = {"f": -0.4, "m": -0.1, "p": -0.1, "r": -0.1, "s": -0.3, "z": -0.2}[ name[0] ] after = {"f": -0.2, "m": -0.1, "p": -0.25, "r": 0, "s": 0, "z": -0.2}[name[-1]] direction = self.direction direction = String.to_tridirectional_lilypond_symbol(direction) strings = [] strings.append(f"{direction} #(make-dynamic-script") strings.append(" (markup") strings.append(" #:whiteout") strings.append(" #:line (") strings.append(' #:general-align Y -2 #:normal-text #:larger "“"') strings.append(f" #:hspace {before}") strings.append(f' #:dynamic "{name}"') strings.append(f" #:hspace {after}") strings.append(' #:general-align Y -2 #:normal-text #:larger "”"') strings.append(" )") strings.append(" )") strings.append(" )") string = "\n".join(strings) return string @staticmethod def _format_textual(direction, string): if direction is None: direction = enums.Down direction = String.to_tridirectional_lilypond_symbol(direction) assert isinstance(string, str), repr(string) string = f'(markup #:whiteout #:normal-text #:italic "{string}")' string = f"{direction} #(make-dynamic-script {string})" return string def _get_format_specification(self): keywords = ["command", "direction", "hide", "leak"] if self._ordinal is not None: keywords.append("ordinal") keywords.append("name_is_textual") if self._sforzando is not None: keywords.append("sforzando") return FormatSpecification( self, repr_is_indented=False, storage_format_args_values=[self.name], storage_format_is_indented=False, storage_format_kwargs_names=keywords, ) def _get_lilypond_format(self): if self.command: string = self.command elif self.effort: string = self._format_effort_dynamic() elif self.name_is_textual: string = self._format_textual(self.direction, self.name) else: string = rf"\{self.name}" if self.direction is not None: direction_ = self.direction direction = String.to_tridirectional_lilypond_symbol(direction_) string = f"{direction} {string}" return string def _get_lilypond_format_bundle(self, component=None): bundle = LilyPondFormatBundle() if self.leak: bundle.after.leaks.append("<>") if self.tweaks: tweaks = self.tweaks._list_format_contributions() if self.leak: bundle.after.leaks.extend(tweaks) else: bundle.after.articulations.extend(tweaks) if not self.hide: string = self._get_lilypond_format() if self.leak: bundle.after.leaks.append(string) else: bundle.after.articulations.append(string) return bundle ### PUBLIC PROPERTIES ### @property def command(self) -> typing.Optional[str]: r""" Gets explicit command. .. container:: example >>> abjad.Dynamic('f', command=r'\sub_f').command '\\sub_f' Use to override LilyPond output when a custom dynamic has been defined in an external stylesheet. (In the example above, ``\sub_f`` is a nonstandard LilyPond dynamic. LilyPond will interpret the output above only when the command ``\sub_f`` is defined somewhere in an external stylesheet.) """ return self._command @property def context(self) -> str: """ Returns (historically conventional) context ``'Voice'``. .. container:: example >>> abjad.Dynamic('f').context 'Voice' Class constant. Override with ``abjad.attach(..., context='...')``. """ return self._context @property def direction(self) -> typing.Optional[enums.VerticalAlignment]: r""" Gets direction for effort dynamics only. .. container:: example With ``direction`` unset: >>> staff = abjad.Staff("c'2 c''2") >>> abjad.attach(abjad.Dynamic("p"), staff[0]) >>> abjad.attach(abjad.Dynamic("f"), staff[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'2 \p c''2 \f } With ``direction=abjad.Up``: >>> staff = abjad.Staff("c'2 c''2") >>> abjad.attach(abjad.Dynamic("p", direction=abjad.Up), staff[0]) >>> abjad.attach(abjad.Dynamic("f", direction=abjad.Up), staff[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'2 ^ \p c''2 ^ \f } With ``direction=abjad.Down``: >>> staff = abjad.Staff("c'2 c''2") >>> abjad.attach(abjad.Dynamic("p", direction=abjad.Down), staff[0]) >>> abjad.attach(abjad.Dynamic("f", direction=abjad.Down), staff[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'2 _ \p c''2 _ \f } .. container:: example REGRESSION. Effort dynamics default to down: >>> staff = abjad.Staff("c'2 c''2") >>> abjad.attach(abjad.Dynamic('"p"'), staff[0]) >>> abjad.attach(abjad.Dynamic('"f"'), staff[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'2 _ #(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 "”" ) ) ) c''2 _ #(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 "”" ) ) ) } And may be overriden: >>> staff = abjad.Staff("c'2 c''2") >>> abjad.attach(abjad.Dynamic('"p"', direction=abjad.Up), staff[0]) >>> abjad.attach(abjad.Dynamic('"f"', direction=abjad.Up), staff[1]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'2 ^ #(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 "”" ) ) ) c''2 ^ #(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 "”" ) ) ) } """ if self._direction is not None: return self._direction elif self.name == "niente" or self.effort: result = enums.Down assert isinstance(result, enums.VerticalAlignment) return result else: return None @property def effort(self) -> typing.Optional[bool]: r""" Is true when double quotes enclose dynamic. .. container:: example >>> voice = abjad.Voice("c'4 r d' r e' r f' r") >>> abjad.attach(abjad.Dynamic('"pp"'), voice[0]) >>> abjad.attach(abjad.Dynamic('"mp"'), voice[2]) >>> abjad.attach(abjad.Dynamic('"mf"'), voice[4]) >>> abjad.attach(abjad.Dynamic('"ff"'), voice[6]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "pp" #:hspace -0.25 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 d'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "mp" #:hspace -0.25 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 e'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "mf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 f'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.4 #:dynamic "ff" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 } .. container:: example >>> voice = abjad.Voice("c'4 r d' r e' r f' r") >>> abjad.attach(abjad.Dynamic('"sf"'), voice[0]) >>> abjad.attach(abjad.Dynamic('"sfz"'), voice[2]) >>> abjad.attach(abjad.Dynamic('"rf"'), voice[4]) >>> abjad.attach(abjad.Dynamic('"rfz"'), voice[6]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.3 #:dynamic "sf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 d'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.3 #:dynamic "sfz" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 e'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "rf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 f'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "rfz" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 } """ return bool(self.name) and self.name[0] == '"' @property def format_hairpin_stop(self) -> typing.Optional[bool]: r""" Is true when dynamic formats LilyPond ``\!`` hairpin stop. """ return self._format_hairpin_stop @property def hide(self) -> typing.Optional[bool]: r""" Is true when dynamic should not appear in output (but should still determine effective dynamic). .. container:: example >>> voice = abjad.Voice("c'4 d' e' f'") >>> abjad.attach(abjad.Dynamic('f'), voice[0]) >>> abjad.attach(abjad.Dynamic('mf', hide=True), voice[2]) >>> abjad.show(voice) # doctest: +SKIP >>> abjad.f(voice) \new Voice { c'4 \f d'4 e'4 f'4 } >>> for leaf in abjad.iterate(voice).leaves(): ... leaf, abjad.inspect(leaf).effective(abjad.Dynamic) ... (Note("c'4"), Dynamic('f')) (Note("d'4"), Dynamic('f')) (Note("e'4"), Dynamic('mf', hide=True)) (Note("f'4"), Dynamic('mf', hide=True)) """ return self._hide @property def leak(self) -> typing.Optional[bool]: r""" Is true when dynamic formats LilyPond empty chord ``<>`` symbol. .. container:: example Without leaked stop dynamic: >>> staff = abjad.Staff("c'4 d' e' r") >>> start_dynamic = abjad.Dynamic('mf') >>> start_hairpin = abjad.StartHairpin('>') >>> stop_dynamic = abjad.Dynamic('pp') >>> abjad.attach(start_dynamic, staff[0]) >>> abjad.attach(start_hairpin, staff[0]) >>> abjad.attach(stop_dynamic, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \mf \> d'4 e'4 \pp r4 } With leaked stop dynamic: >>> staff = abjad.Staff("c'4 d' e' r") >>> start_dynamic = abjad.Dynamic('mf') >>> start_hairpin = abjad.StartHairpin('>') >>> stop_dynamic = abjad.Dynamic('pp', leak=True) >>> abjad.attach(start_dynamic, staff[0]) >>> abjad.attach(start_hairpin, staff[0]) >>> abjad.attach(stop_dynamic, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \mf \> d'4 e'4 <> \pp r4 } .. container:: example Leaks format after spanners: >>> staff = abjad.Staff("c'8 [ d' e' ] f'") >>> start_dynamic = abjad.Dynamic('mf') >>> start_hairpin = abjad.StartHairpin('>') >>> stop_dynamic = abjad.Dynamic('pp', leak=True) >>> abjad.attach(start_dynamic, staff[0]) >>> abjad.attach(start_hairpin, staff[0]) >>> abjad.attach(stop_dynamic, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'8 \mf \> [ d'8 e'8 ] <> \pp f'8 } .. container:: example Leaked and nonleaked dynamic may be attached to the same leaf: >>> staff = abjad.Staff("c'4 d' e' f'") >>> abjad.attach(abjad.Dynamic('f'), staff[0]) >>> abjad.attach(abjad.Dynamic('p', leak=True), staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'4 \f <> \p d'4 e'4 f'4 } .. container:: example Leaks and tweaks on the same dynamic format correctly; LilyPond empty chord ``<>`` symbol appears before postevents: >>> staff = abjad.Staff("r4 d' e' f'") >>> dynamic = abjad.Dynamic('f', leak=True) >>> abjad.tweak(dynamic).color = 'blue' >>> abjad.attach(dynamic, staff[0]) >>> abjad.show(staff) # doctest: +SKIP >>> abjad.f(staff) \new Staff { r4 <> - \tweak color #blue \f d'4 e'4 f'4 } .. container:: example Leak survives copy: >>> import copy >>> dynamic = abjad.Dynamic('pp', leak=True) >>> copy.copy(dynamic) Dynamic('pp', leak=True) """ return self._leak @property def name(self) -> str: r""" Gets name. .. container:: example >>> abjad.Dynamic('f').name 'f' >>> abjad.Dynamic('p').name 'p' >>> abjad.Dynamic('sffz').name 'sffz' >>> abjad.Dynamic('sffp').name 'sffp' >>> abjad.Dynamic('"f"').name '"f"' .. container:: example Niente dynamics format like this: >>> voice = abjad.Voice("c'4 r r c'4") >>> abjad.attach(abjad.Dynamic('p'), voice[0]) >>> abjad.attach(abjad.Dynamic('niente'), voice[1]) >>> abjad.attach(abjad.Dynamic('p'), voice[3]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \p r4 _ #(make-dynamic-script (markup #:whiteout #:normal-text #:italic "niente")) r4 c'4 \p } """ return self._name @property def name_is_textual(self) -> typing.Optional[bool]: r""" Is true when name is textual. .. container:: example >>> abjad.Dynamic('f').name_is_textual is None True >>> abjad.Dynamic('niente').name_is_textual True >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> dynamic.name_is_textual True .. container:: example Textual dynamics format like this when initialized without an explicit command: >>> voice = abjad.Voice("c'4 d' e' f'") >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> abjad.attach(dynamic, voice[0]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.override(voice).dynamic_text.X_extent = (0, 0) >>> abjad.override(voice).dynamic_text.self_alignment_X = abjad.Left >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 \override DynamicText.X-extent = #'(0 . 0) \override DynamicText.self-alignment-X = #left } { c'4 _ #(make-dynamic-script (markup #:whiteout #:normal-text #:italic "appena udibile")) d'4 e'4 f'4 } .. container:: example Textual dynamics format like this when initialized with an explicit command: >>> voice = abjad.Voice("c'4 d' e' f'") >>> dynamic = abjad.Dynamic( ... 'appena udibile', ... command=r'\appena_udibile', ... name_is_textual=True, ... ) >>> abjad.attach(dynamic, voice[0]) Only LilyPond output is shown here because dynamic commands (like ``\appena_udibile`` shown here) are meant to be user-defined (and not included in Abjad): >>> abjad.f(voice) \new Voice { c'4 \appena_udibile d'4 e'4 f'4 } .. container:: example REGRESSION. Textual names work with new: >>> dynamic = abjad.Dynamic('niente') >>> abjad.new(dynamic) Dynamic('niente', direction=Down, ordinal=NegativeInfinity, name_is_textual=True, sforzando=False) >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> abjad.new(dynamic) Dynamic('appena udibile', name_is_textual=True, sforzando=False) """ return self._name_is_textual @property # def ordinal(self) -> typing.Union[int, Infinity, NegativeInfinity]: def ordinal(self): """ Gets ordinal. .. container:: example >>> abjad.Dynamic('f').ordinal 2 >>> abjad.Dynamic('p').ordinal -2 .. container:: example >>> abjad.Dynamic('niente').ordinal NegativeInfinity .. container:: example >>> abjad.Dynamic('"f"').ordinal 2 >>> abjad.Dynamic('"p"').ordinal -2 .. container:: example User-defined ordinals: >>> barely_audible = abjad.Dynamic( ... 'barely audible', ... name_is_textual=True, ... ordinal=-99, ... ) >>> barely_audible.ordinal -99 >>> extremely_loud = abjad.Dynamic( ... 'extremely loud', ... name_is_textual=True, ... ordinal=99, ... ) >>> extremely_loud.ordinal 99 .. container:: example REGRESSION. Textual names without explicit ordinal return none: >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> dynamic.ordinal is None True """ if self._ordinal is not None: return self._ordinal name = None if self.name: name = self.name.strip('"') if name in self._composite_dynamic_name_to_steady_state_dynamic_name: name = self._composite_dynamic_name_to_steady_state_dynamic_name[name] ordinal = self._dynamic_name_to_dynamic_ordinal.get(name) return ordinal @property def parameter(self) -> str: """ Returns ``'DYNAMIC'``. .. container:: example >>> abjad.Dynamic('f').parameter 'DYNAMIC' """ return self._parameter @property def persistent(self) -> bool: """ Is true. .. container:: example >>> abjad.Dynamic('f').persistent True """ return self._persistent @property def sforzando(self) -> typing.Optional[bool]: """ Is true when dynamic name begins in s- and ends in -z. .. container:: example >>> abjad.Dynamic('f').sforzando False >>> abjad.Dynamic('sfz').sforzando True >>> abjad.Dynamic('sffz').sforzando True >>> abjad.Dynamic('sfp').sforzando False >>> abjad.Dynamic('sf').sforzando False >>> abjad.Dynamic('rfz').sforzando False """ if self._sforzando is not None: return self._sforzando if self.name and self.name.startswith("s") and self.name.endswith("z"): return True return False @property def regression(self): r""" Documents regressions. .. container:: example Duplicate dynamics raise exception on attach: >>> staff = abjad.Staff("c'4 d' e' f'") >>> dynamic = abjad.Dynamic('p') >>> abjad.attach(dynamic, staff[0]) >>> dynamic = abjad.Dynamic('p') >>> abjad.attach(dynamic, staff[0]) Traceback (most recent call last): ... abjad.exceptions.PersistentIndicatorError: <BLANKLINE> Can not attach ... <BLANKLINE> abjad.Wrapper( context='Voice', indicator=abjad.Dynamic('p'), tag=abjad.Tag(), ) <BLANKLINE> ... to Note("c'4") in None because ... <BLANKLINE> abjad.Wrapper( context='Voice', indicator=abjad.Dynamic('p'), tag=abjad.Tag(), ) <BLANKLINE> ... is already attached to the same leaf. """ pass ### PUBLIC METHODS ### @staticmethod def composite_dynamic_name_to_steady_state_dynamic_name(name) -> str: """ Changes composite ``name`` to steady state dynamic name. .. container:: example >>> abjad.Dynamic.composite_dynamic_name_to_steady_state_dynamic_name('sfp') 'p' >>> abjad.Dynamic.composite_dynamic_name_to_steady_state_dynamic_name('rfz') 'f' """ return Dynamic._composite_dynamic_name_to_steady_state_dynamic_name[name] @staticmethod # def dynamic_name_to_dynamic_ordinal(name) -> typing.Union[ # int, Infinity, NegativeInfinity, # ]: def dynamic_name_to_dynamic_ordinal(name): """ Changes ``name`` to dynamic ordinal. .. container:: example >>> abjad.Dynamic.dynamic_name_to_dynamic_ordinal('fff') 4 >>> abjad.Dynamic.dynamic_name_to_dynamic_ordinal('niente') NegativeInfinity """ try: return Dynamic._dynamic_name_to_dynamic_ordinal[name] except KeyError: name = Dynamic.composite_dynamic_name_to_steady_state_dynamic_name(name) return Dynamic._dynamic_name_to_dynamic_ordinal[name] @staticmethod def dynamic_ordinal_to_dynamic_name(dynamic_ordinal) -> str: """ Changes ``dynamic_ordinal`` to dynamic name. .. container:: example >>> abjad.Dynamic.dynamic_ordinal_to_dynamic_name(-5) 'pppp' >>> negative_infinity = abjad.mathtools.NegativeInfinity() >>> abjad.Dynamic.dynamic_ordinal_to_dynamic_name(negative_infinity) 'niente' """ if dynamic_ordinal == NegativeInfinity(): return "niente" else: return Dynamic._dynamic_ordinal_to_dynamic_name[dynamic_ordinal] @staticmethod def is_dynamic_name(argument) -> bool: """ Is true when ``argument`` is dynamic name. .. container:: example >>> abjad.Dynamic.is_dynamic_name('f') True >>> abjad.Dynamic.is_dynamic_name('sfz') True >>> abjad.Dynamic.is_dynamic_name('niente') True """ return argument in Dynamic._dynamic_names @property def spanner_stop(self) -> bool: """ Is true .. container:: example >>> abjad.Dynamic("p").spanner_stop True >>> abjad.Dynamic("niente").spanner_stop True """ return True @property def tweaks(self) -> typing.Optional[LilyPondTweakManager]: r""" Gets tweaks .. container:: example >>> note = abjad.Note("c'4") >>> dynamic = abjad.Dynamic('f') >>> abjad.tweak(dynamic).color = 'blue' >>> abjad.attach(dynamic, note) >>> abjad.show(note) # doctest: +SKIP .. docs:: >>> abjad.f(note) c'4 - \tweak color #blue \f """ return self._tweaks
""" Utility classes and functions. """ from abjad import mathtools # noqa Infinity = mathtools.Infinity() NegativeInfinity = mathtools.NegativeInfinity() del mathtools from .CyclicTuple import CyclicTuple from .Duration import Duration from .Enumerator import Enumerator from .Inequality import Inequality from .DurationInequality import DurationInequality from .Expression import Expression from .LengthInequality import LengthInequality from .Multiplier import Multiplier from .Offset import Offset from .Pattern import Pattern from .TypedCollection import TypedCollection from .TypedTuple import TypedTuple from .OrderedDict import OrderedDict from .PatternTuple import PatternTuple from .PitchInequality import PitchInequality from .Sequence import Sequence from .SortedCollection import SortedCollection from .String import String from .TypedCounter import TypedCounter from .TypedFrozenset import TypedFrozenset from .TypedList import TypedList
class Dynamic(AbjadValueObject): r""" Dynamic. .. container:: example Initializes from dynamic name: >>> voice = abjad.Voice("c'8 d'8 e'8 f'8") >>> dynamic = abjad.Dynamic('f') >>> abjad.attach(dynamic, voice[0]) .. docs:: >>> abjad.f(voice) \new Voice { c'8 \f d'8 e'8 f'8 } >>> abjad.show(voice) # doctest: +SKIP .. container:: example Initializes from other dynamic: >>> dynamic_1 = abjad.Dynamic('f') >>> dynamic_2 = abjad.Dynamic(dynamic_1) >>> dynamic_1 Dynamic('f') >>> dynamic_2 Dynamic('f') .. container:: example Initializes niente: >>> dynamic = abjad.Dynamic('niente') >>> format(dynamic, 'lilypond') '' .. container:: example Simultaneous dynamics in a single staff: >>> voice_1 = abjad.Voice("e'8 g'8 f'8 a'8") >>> abjad.attach(abjad.Dynamic('f'), voice_1[0], context='Voice') >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceOne'), voice_1) >>> abjad.override(voice_1).dynamic_line_spanner.direction = abjad.Up >>> voice_2 = abjad.Voice("c'2") >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceTwo'), voice_2) >>> abjad.attach(abjad.Dynamic('mf'), voice_2[0], context='Voice') >>> staff = abjad.Staff([voice_1, voice_2], is_simultaneous=True) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff << \new Voice \with { \override DynamicLineSpanner.direction = #up } { \voiceOne e'8 \f g'8 f'8 a'8 } \new Voice { \voiceTwo c'2 \mf } >> >>> for leaf in abjad.select(staff).leaves(): ... leaf, abjad.inspect(leaf).effective(abjad.Dynamic) ... (Note("e'8"), Dynamic('f')) (Note("g'8"), Dynamic('f')) (Note("f'8"), Dynamic('f')) (Note("a'8"), Dynamic('f')) (Note("c'2"), Dynamic('mf')) """ ### CLASS VARIABLES ### __slots__ = ( '_command', '_direction', '_format_hairpin_stop', '_hide', '_leak', '_name', '_name_is_textual', '_ordinal', '_sforzando', '_tweaks', ) _composite_dynamic_name_to_steady_state_dynamic_name = { 'fp': 'p', 'sf': 'f', 'sff': 'ff', 'sfp': 'p', 'sfpp': 'pp', 'sffp': 'p', 'sffpp': 'pp', 'sfz': 'f', 'sp': 'p', 'spp': 'pp', 'rfz': 'f', } _context = 'Voice' _dynamic_name_to_dynamic_ordinal = { 'ppppp': -6, 'pppp': -5, 'ppp': -4, 'pp': -3, 'p': -2, 'niente': mathtools.NegativeInfinity(), 'mp': -1, 'mf': 1, 'f': 2, 'ff': 3, 'fff': 4, 'ffff': 5, 'fffff': 6, } _dynamic_names = ( 'ppppp', 'pppp', 'ppp', 'pp', 'p', 'mp', 'mf', 'f', 'ff', 'fff', 'ffff', 'fffff', 'fp', 'sf', 'sff', 'sp', 'spp', 'sfz', 'sffz', 'sfffz', 'sffp', 'sffpp', 'sfp', 'sfpp', 'rfz', 'niente', ) _dynamic_ordinal_to_dynamic_name = { -6: 'ppppp', -5: 'pppp', -4: 'ppp', -3: 'pp', -2: 'p', -1: 'mp', mathtools.NegativeInfinity(): 'niente', 1: 'mf', 2: 'f', 3: 'ff', 4: 'fff', 5: 'ffff', 6: 'fffff', } _format_slot = 'after' _lilypond_dynamic_commands = [ _ for _ in _dynamic_names if not _ == 'niente' ] _lilypond_dynamic_alphabet = 'fmprsz' _parameter = True _to_width = { '"f"': 2, '"mf"': 3.5, '"mp"': 3.5, '"p"': 2, 'sfz': 2.5, } ### INITIALIZER ### def __init__( self, name: typing.Union[str, 'Dynamic'] = 'f', *, command: str = None, direction: enums.VerticalAlignment = None, format_hairpin_stop: bool = None, hide: bool = None, leak: bool = None, name_is_textual: bool = None, ordinal: typing.Union[int, Infinity, NegativeInfinity] = None, sforzando: bool = None, tweaks: typing.Union[typing.List[typing.Tuple], LilyPondTweakManager] = None, ) -> None: if name is not None: assert isinstance(name, (str, type(self))), repr(name) if isinstance(name, type(self)): name_ = name.name elif isinstance(name, str): name_ = name if name_ == 'niente': if name_is_textual not in (None, True): raise Exception('niente dynamic name is always textual.') name_is_textual = True if not name_is_textual: for letter in name_.strip('"'): assert letter in self._lilypond_dynamic_alphabet, repr(name_) self._name = name_ if command is not None: assert isinstance(command, str), repr(command) assert command.startswith('\\'), repr(command) self._command = command if direction is not None: assert direction in (enums.Down, enums.Up), repr(direction) self._direction = direction if format_hairpin_stop is not None: format_hairpin_stop = bool(format_hairpin_stop) self._format_hairpin_stop = format_hairpin_stop if hide is not None: hide = bool(hide) self._hide = hide if leak is not None: leak = bool(leak) self._leak = leak if name_is_textual is not None: name_is_textual = bool(name_is_textual) self._name_is_textual = name_is_textual if ordinal is not None: assert isinstance(ordinal, (int, Infinity, NegativeInfinity)) self._ordinal = ordinal if sforzando is not None: sforzando = bool(sforzando) self._sforzando = sforzando self._tweaks = None LilyPondTweakManager.set_tweaks(self, tweaks) ### SPECIAL METHODS ### def __eq__(self, argument) -> bool: """ Is true when ``argument`` equals dynamic. .. container:: example >>> dynamic_1 = abjad.Dynamic('p') >>> dynamic_2 = abjad.Dynamic('p') >>> dynamic_3 = abjad.Dynamic('f') >>> dynamic_1 == dynamic_1 True >>> dynamic_1 == dynamic_2 True >>> dynamic_1 == dynamic_3 False >>> dynamic_2 == dynamic_1 True >>> dynamic_2 == dynamic_2 True >>> dynamic_2 == dynamic_3 False >>> dynamic_3 == dynamic_1 False >>> dynamic_3 == dynamic_2 False >>> dynamic_3 == dynamic_3 True """ if not isinstance(argument, type(self)): return False if self.name == argument.name and self.ordinal == argument.ordinal: return True return False def __format__(self, format_specification='') -> str: r""" Formats dynamic. .. container:: example Gets storage format: >>> dynamic = abjad.Dynamic('f') >>> print(format(dynamic)) abjad.Dynamic('f') Gets LilyPond format: >>> dynamic = abjad.Dynamic('f') >>> print(format(dynamic, 'lilypond')) \f """ if format_specification == 'lilypond': if self.name == 'niente': return '' elif self.name.strip('"') not in self._lilypond_dynamic_commands: message = f'{self.name!r} is not a LilyPond dynamic command.' raise Exception(message) return self._get_lilypond_format() return super().__format__(format_specification=format_specification) def __hash__(self) -> int: """ Hashes dynamic. Redefined in tandem with __eq__. """ return super().__hash__() ### PRIVATE METHODS ### def _add_leak(self, string): if self.leak: string = f'<> {string}' return string def _attachment_test_all(self, component_expression): import abjad if not isinstance(component_expression, abjad.Leaf): return False return True def _format_effort_dynamic(self): name = self.name.strip('"') before = { 'f': -0.4, 'm': -0.1, 'p': -0.1, 'r': -0.1, 's': -0.3, 'z': -0.2, }[name[0]] after = { 'f': -0.2, 'm': -0.1, 'p': -0.25, 'r': 0, 's': 0, 'z': -0.2, }[name[-1]] direction = self.direction direction = String.to_tridirectional_lilypond_symbol(direction) strings = [] strings.append(f'{direction} #(make-dynamic-script') strings.append(' (markup') strings.append(' #:whiteout') strings.append(' #:line (') strings.append( ' #:general-align Y -2 #:normal-text #:larger "“"') strings.append(f' #:hspace {before}') strings.append(f' #:dynamic "{name}"') strings.append(f' #:hspace {after}') strings.append( ' #:general-align Y -2 #:normal-text #:larger "”"') strings.append(' )') strings.append(' )') strings.append(' )') string = '\n'.join(strings) return string @staticmethod def _format_textual(direction, string): if direction is None: direction = enums.Down direction = String.to_tridirectional_lilypond_symbol(direction) assert isinstance(string, str), repr(string) string = f'(markup #:whiteout #:normal-text #:italic "{string}")' string = f'{direction} #(make-dynamic-script {string})' return string def _get_format_specification(self): keywords = ['command', 'direction', 'hide', 'leak'] if self._ordinal is not None: keywords.append('ordinal') keywords.append('name_is_textual') if self._sforzando is not None: keywords.append('sforzando') return FormatSpecification( self, repr_is_indented=False, storage_format_args_values=[self.name], storage_format_is_indented=False, storage_format_kwargs_names=keywords, ) def _get_lilypond_format(self): if self.command: string = self.command elif self.effort: string = self._format_effort_dynamic() elif self.name_is_textual: string = self._format_textual(self.direction, self.name) else: string = rf'\{self.name}' string = self._add_leak(string) return string def _get_lilypond_format_bundle(self, component=None): bundle = LilyPondFormatBundle() if self.tweaks: tweaks = self.tweaks._list_format_contributions() if self.leak: bundle.after.leaks.extend(tweaks) else: bundle.after.articulations.extend(tweaks) if not self.hide: string = self._get_lilypond_format() if self.leak: bundle.after.leaks.append(string) else: bundle.after.articulations.append(string) return bundle @staticmethod def _tag_hide(strings): import abjad abjad_tags = abjad.Tags() return LilyPondFormatManager.tag( strings, deactivate=False, tag=abjad_tags.HIDE_TO_JOIN_BROKEN_SPANNERS, ) ### PUBLIC PROPERTIES ### @property def command(self) -> typing.Optional[str]: r""" Gets explicit command. .. container:: example >>> abjad.Dynamic('f', command=r'\sub_f').command '\\sub_f' Use to override LilyPond output when a custom dynamic has been defined in an external stylesheet. (In the example above, ``\sub_f`` is a nonstandard LilyPond dynamic. LilyPond will interpret the output above only when the command ``\sub_f`` is defined somewhere in an external stylesheet.) """ return self._command @property def context(self) -> str: """ Returns (historically conventional) context ``'Voice'``. .. container:: example >>> abjad.Dynamic('f').context 'Voice' Class constant. Override with ``abjad.attach(..., context='...')``. """ return self._context @property def direction(self) -> typing.Optional[enums.VerticalAlignment]: """ Gets direction for effort dynamics only. .. container:: example Effort dynamics default to down: >>> abjad.Dynamic('"f"').direction Down And may be overriden: >>> abjad.Dynamic('"f"', direction=abjad.Up).direction Up """ if self._direction is not None: return self._direction elif self.name == 'niente' or self.effort: return enums.Down else: return None @property def effort(self) -> typing.Optional[bool]: r""" Is true when double quotes enclose dynamic. .. container:: example >>> voice = abjad.Voice("c'4 r d' r e' r f' r") >>> abjad.attach(abjad.Dynamic('"pp"'), voice[0]) >>> abjad.attach(abjad.Dynamic('"mp"'), voice[2]) >>> abjad.attach(abjad.Dynamic('"mf"'), voice[4]) >>> abjad.attach(abjad.Dynamic('"ff"'), voice[6]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "pp" #:hspace -0.25 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 d'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "mp" #:hspace -0.25 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 e'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "mf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 f'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.4 #:dynamic "ff" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 } .. container:: example >>> voice = abjad.Voice("c'4 r d' r e' r f' r") >>> abjad.attach(abjad.Dynamic('"sf"'), voice[0]) >>> abjad.attach(abjad.Dynamic('"sfz"'), voice[2]) >>> abjad.attach(abjad.Dynamic('"rf"'), voice[4]) >>> abjad.attach(abjad.Dynamic('"rfz"'), voice[6]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.3 #:dynamic "sf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 d'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.3 #:dynamic "sfz" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 e'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "rf" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 f'4 _ #(make-dynamic-script (markup #:whiteout #:line ( #:general-align Y -2 #:normal-text #:larger "“" #:hspace -0.1 #:dynamic "rfz" #:hspace -0.2 #:general-align Y -2 #:normal-text #:larger "”" ) ) ) r4 } """ return bool(self.name) and self.name[0] == '"' @property def format_hairpin_stop(self) -> typing.Optional[bool]: r""" Is true when dynamic formats LilyPond ``\!`` hairpin stop. """ return self._format_hairpin_stop @property def hide(self) -> typing.Optional[bool]: r""" Is true when dynamic should not appear in output (but should still determine effective dynamic). .. container:: example >>> voice = abjad.Voice("c'4 d' e' f'") >>> abjad.attach(abjad.Dynamic('f'), voice[0]) >>> abjad.attach(abjad.Dynamic('mf', hide=True), voice[2]) >>> abjad.show(voice) # doctest: +SKIP >>> abjad.f(voice) \new Voice { c'4 \f d'4 e'4 f'4 } >>> for leaf in abjad.iterate(voice).leaves(): ... leaf, abjad.inspect(leaf).effective(abjad.Dynamic) ... (Note("c'4"), Dynamic('f')) (Note("d'4"), Dynamic('f')) (Note("e'4"), Dynamic('mf', hide=True)) (Note("f'4"), Dynamic('mf', hide=True)) """ return self._hide @property def leak(self) -> typing.Optional[bool]: r""" Is true when dynamic formats LilyPond empty chord ``<>`` symbol. .. container:: example Without leaked stop dynamic: >>> staff = abjad.Staff("c'4 d' e' r") >>> start = abjad.Dynamic('mf') >>> trend = abjad.DynamicTrend('>') >>> stop = abjad.Dynamic('pp') >>> abjad.attach(start, staff[0]) >>> abjad.attach(trend, staff[0]) >>> abjad.attach(stop, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \mf \> d'4 e'4 \pp r4 } With leaked stop dynamic: >>> staff = abjad.Staff("c'4 d' e' r") >>> start = abjad.Dynamic('mf') >>> trend = abjad.DynamicTrend('>') >>> stop = abjad.Dynamic('pp', leak=True) >>> abjad.attach(start, staff[0]) >>> abjad.attach(trend, staff[0]) >>> abjad.attach(stop, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \mf \> d'4 e'4 <> \pp r4 } .. container:: example Leaks format after spanners: >>> staff = abjad.Staff("c'8 [ d' e' ] f'") >>> start = abjad.Dynamic('mf') >>> trend = abjad.DynamicTrend('>') >>> stop = abjad.Dynamic('pp', leak=True) >>> abjad.attach(start, staff[0]) >>> abjad.attach(trend, staff[0]) >>> abjad.attach(stop, staff[-2]) >>> abjad.override(staff).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff \with { \override DynamicLineSpanner.staff-padding = #4 } { c'8 \mf \> [ d'8 e'8 ] <> \pp f'8 } .. container:: example Leak survives copy: >>> import copy >>> dynamic = abjad.Dynamic('pp', leak=True) >>> copy.copy(dynamic) Dynamic('pp', leak=True, ordinal=-3, sforzando=False) """ return self._leak @property def name(self) -> str: r""" Gets name. .. container:: example >>> abjad.Dynamic('f').name 'f' >>> abjad.Dynamic('p').name 'p' >>> abjad.Dynamic('sffz').name 'sffz' >>> abjad.Dynamic('sffp').name 'sffp' >>> abjad.Dynamic('"f"').name '"f"' .. container:: example Niente dynamics format like this: >>> voice = abjad.Voice("c'4 r r c'4") >>> abjad.attach(abjad.Dynamic('p'), voice[0]) >>> abjad.attach(abjad.Dynamic('niente'), voice[1]) >>> abjad.attach(abjad.Dynamic('p'), voice[3]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 } { c'4 \p r4 _ #(make-dynamic-script (markup #:whiteout #:normal-text #:italic "niente")) r4 c'4 \p } """ return self._name @property def name_is_textual(self) -> typing.Optional[bool]: r""" Is true when name is textual. .. container:: example >>> abjad.Dynamic('f').name_is_textual is None True >>> abjad.Dynamic('niente').name_is_textual True >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> dynamic.name_is_textual True .. container:: example Textual dynamics format like this when initialized without an explicit command: >>> voice = abjad.Voice("c'4 d' e' f'") >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> abjad.attach(dynamic, voice[0]) >>> abjad.override(voice).dynamic_line_spanner.staff_padding = 4 >>> abjad.override(voice).dynamic_text.X_extent = (0, 0) >>> abjad.override(voice).dynamic_text.self_alignment_X = abjad.Left >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice \with { \override DynamicLineSpanner.staff-padding = #4 \override DynamicText.X-extent = #'(0 . 0) \override DynamicText.self-alignment-X = #left } { c'4 _ #(make-dynamic-script (markup #:whiteout #:normal-text #:italic "appena udibile")) d'4 e'4 f'4 } .. container:: example Textual dynamics format like this when initialized with an explicit command: >>> voice = abjad.Voice("c'4 d' e' f'") >>> dynamic = abjad.Dynamic( ... 'appena udibile', ... command=r'\appena_udibile', ... name_is_textual=True, ... ) >>> abjad.attach(dynamic, voice[0]) Only LilyPond output is shown here because dynamic commands (like ``\appena_udibile`` shown here) are meant to be user-defined (and not included in Abjad): >>> abjad.f(voice) \new Voice { c'4 \appena_udibile d'4 e'4 f'4 } .. container:: example REGRESSION. Textual names work with new: >>> dynamic = abjad.Dynamic('niente') >>> abjad.new(dynamic) Dynamic('niente', direction=Down, ordinal=NegativeInfinity, name_is_textual=True, sforzando=False) >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> abjad.new(dynamic) Dynamic('appena udibile', name_is_textual=True, sforzando=False) """ return self._name_is_textual @property #def ordinal(self) -> typing.Union[int, Infinity, NegativeInfinity]: def ordinal(self): """ Gets ordinal. .. container:: example >>> abjad.Dynamic('f').ordinal 2 >>> abjad.Dynamic('p').ordinal -2 .. container:: example >>> abjad.Dynamic('niente').ordinal NegativeInfinity .. container:: example >>> abjad.Dynamic('"f"').ordinal 2 >>> abjad.Dynamic('"p"').ordinal -2 .. container:: example User-defined ordinals: >>> barely_audible = abjad.Dynamic( ... 'barely audible', ... name_is_textual=True, ... ordinal=-99, ... ) >>> barely_audible.ordinal -99 >>> extremely_loud = abjad.Dynamic( ... 'extremely loud', ... name_is_textual=True, ... ordinal=99, ... ) >>> extremely_loud.ordinal 99 .. container:: example REGRESSION. Textual names without explicit ordinal return none: >>> dynamic = abjad.Dynamic('appena udibile', name_is_textual=True) >>> dynamic.ordinal is None True """ if self._ordinal is not None: return self._ordinal name = None if self.name: name = self.name.strip('"') if name in self._composite_dynamic_name_to_steady_state_dynamic_name: name = self._composite_dynamic_name_to_steady_state_dynamic_name[ name] ordinal = self._dynamic_name_to_dynamic_ordinal.get(name) return ordinal @property def parameter(self) -> bool: """ Is true. .. container:: example >>> abjad.Dynamic('f').parameter True """ return self._parameter @property def sforzando(self) -> typing.Optional[bool]: """ Is true when dynamic name begins in s- and ends in -z. .. container:: example >>> abjad.Dynamic('f').sforzando False >>> abjad.Dynamic('sfz').sforzando True >>> abjad.Dynamic('sffz').sforzando True >>> abjad.Dynamic('sfp').sforzando False >>> abjad.Dynamic('sf').sforzando False >>> abjad.Dynamic('rfz').sforzando False """ if self._sforzando is not None: return self._sforzando if (self.name and self.name.startswith('s') and self.name.endswith('z')): return True return False ### PUBLIC METHODS ### @staticmethod def composite_dynamic_name_to_steady_state_dynamic_name(name) -> str: """ Changes composite ``name`` to steady state dynamic name. .. container:: example >>> abjad.Dynamic.composite_dynamic_name_to_steady_state_dynamic_name('sfp') 'p' >>> abjad.Dynamic.composite_dynamic_name_to_steady_state_dynamic_name('rfz') 'f' """ return Dynamic._composite_dynamic_name_to_steady_state_dynamic_name[ name] @staticmethod #def dynamic_name_to_dynamic_ordinal(name) -> typing.Union[ # int, Infinity, NegativeInfinity, # ]: def dynamic_name_to_dynamic_ordinal(name): """ Changes ``name`` to dynamic ordinal. .. container:: example >>> abjad.Dynamic.dynamic_name_to_dynamic_ordinal('fff') 4 >>> abjad.Dynamic.dynamic_name_to_dynamic_ordinal('niente') NegativeInfinity """ try: return Dynamic._dynamic_name_to_dynamic_ordinal[name] except KeyError: name = Dynamic.composite_dynamic_name_to_steady_state_dynamic_name( name) return Dynamic._dynamic_name_to_dynamic_ordinal[name] @staticmethod def dynamic_ordinal_to_dynamic_name(dynamic_ordinal) -> str: """ Changes ``dynamic_ordinal`` to dynamic name. .. container:: example >>> abjad.Dynamic.dynamic_ordinal_to_dynamic_name(-5) 'pppp' >>> negative_infinity = abjad.mathtools.NegativeInfinity() >>> abjad.Dynamic.dynamic_ordinal_to_dynamic_name(negative_infinity) 'niente' """ if dynamic_ordinal == NegativeInfinity(): return 'niente' else: return Dynamic._dynamic_ordinal_to_dynamic_name[dynamic_ordinal] @staticmethod def is_dynamic_name(argument) -> bool: """ Is true when ``argument`` is dynamic name. .. container:: example >>> abjad.Dynamic.is_dynamic_name('f') True >>> abjad.Dynamic.is_dynamic_name('sfz') True >>> abjad.Dynamic.is_dynamic_name('niente') True """ return argument in Dynamic._dynamic_names @property def tweaks(self) -> typing.Optional[LilyPondTweakManager]: r""" Gets tweaks .. container:: example >>> note = abjad.Note("c'4") >>> dynamic = abjad.Dynamic('f') >>> abjad.tweak(dynamic).color = 'blue' >>> abjad.attach(dynamic, note) >>> abjad.show(note) # doctest: +SKIP .. docs:: >>> abjad.f(note) c'4 - \tweak color #blue \f .. container:: example >>> note = abjad.Note("c'4") >>> dynamic = abjad.Dynamic( ... 'f', ... tweaks=[('color', 'blue')], ... ) >>> abjad.attach(dynamic, note) >>> abjad.show(note) # doctest: +SKIP .. docs:: >>> abjad.f(note) c'4 - \tweak color #blue \f """ return self._tweaks