Source code for mutwo.abjad_parameters.abc

from __future__ import annotations
import abc
import dataclasses
import typing

import abjad

from mutwo import core_events
from mutwo import core_utilities
from mutwo import music_parameters


[docs]@dataclasses.dataclass() class AbjadAttachment(abc.ABC): """Abstract base class for all Abjad attachments.""" indicator: typing.Optional[ music_parameters.abc.PlayingIndicator | music_parameters.abc.NotationIndicator ] = None is_chronon_rest: typing.Optional[ typing.Callable[[core_events.SimpleEvent], bool] ] = None mutwo_pitch_to_abjad_pitch: typing.Optional[ typing.Callable[[music_parameters.abc.Pitch], abjad.Pitch] ] = None mutwo_pitch_to_abjad_pitch: typing.Optional[ typing.Callable[[music_parameters.abc.Pitch], abjad.Pitch] ] = None mutwo_volume_to_abjad_attachment_dynamic: typing.Optional[ typing.Callable[[music_parameters.abc.Volume], abjad.Dynamic] ] = None mutwo_lyric_to_abjad_string: typing.Optional[ typing.Callable[[music_parameters.abc.Lyric], str] ] = None with_duration_line: bool = False @classmethod @property def replace_leaf_by_leaf(cls) -> bool: """Communicate expected return type of process_leaf_tuple Set to ``True`` if 'process_leaf_tuple' returns a sequence of abjad leaves with an equal length to the provided leaf_tuple and where each new leaf replaces the respective old leaf at the same position. Set to ``True`` if 'process_leaf_tuple' returns a new abjad object which is set at the position of the old first leaf and if all other old leaves are supposed to be deleted. """ return True
[docs] @classmethod def get_class_name(cls): return core_utilities.camel_case_to_snake_case(cls.__name__)
[docs] @classmethod def from_indicator_collection( cls, indicator_collection: music_parameters.abc.IndicatorCollection, **kwargs ) -> typing.Optional[AbjadAttachment]: """Initialize :class:`AbjadAttachment` from :class:`~mutwo.music_parameters.abc.IndicatorCollection`. If no suitable :class:`~mutwo.music_parameters.abc.Indicator` could be found in the collection the method will simply return None. """ class_name = cls.get_class_name() try: indicator = getattr(indicator_collection, class_name) except AttributeError: return None return cls(indicator, **kwargs)
[docs] @abc.abstractmethod def process_leaf_tuple( self, leaf_tuple: tuple[abjad.Leaf, ...], previous_attachment: typing.Optional[AbjadAttachment], ) -> tuple[abjad.Leaf, ...]: ...
@property def is_active(self) -> bool: return self.indicator.is_active
[docs]class ToggleAttachment(AbjadAttachment): """Abstract base class for Abjad attachments which behave like a toggle. In Western notation one can differentiate between elements which only get notated if they change (for instance dynamics, tempo) and elements which have to be notated again and again (for instance arpeggi or tremolo). Attachments that inherit from :class:`ToggleAttachment` represent elements which only get notated if their value changes. """
[docs] @abc.abstractmethod def process_leaf( self, leaf: abjad.Leaf, previous_attachment: typing.Optional[AbjadAttachment] ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: ...
[docs] def process_leaf_tuple( self, leaf_tuple: tuple[abjad.Leaf, ...], previous_attachment: typing.Optional[AbjadAttachment], ) -> tuple[abjad.Leaf, ...]: if previous_attachment != self: return ( self.process_leaf(leaf_tuple[0], previous_attachment), ) + leaf_tuple[1:] else: return leaf_tuple
[docs]class BangAttachment(AbjadAttachment): """Abstract base class for Abjad attachments which behave like a bang. In Western notation one can differentiate between elements which only get notated if they change (for instance dynamics, tempo) and elements which have to be notated again and again to be effective (for instance arpeggi or tremolo). Attachments that inherit from :class:`BangAttachment` represent elements which have to be notated again and again to be effective. """
[docs] @abc.abstractmethod def process_first_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: ...
[docs] @abc.abstractmethod def process_last_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: ...
[docs] @abc.abstractmethod def process_central_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: ...
[docs] def process_leaf_tuple( self, leaf_tuple: tuple[abjad.Leaf, ...], previous_attachment: typing.Optional[AbjadAttachment], ) -> tuple[abjad.Leaf, ...]: new_leaf_list = [] if (leaf_count := len(leaf_tuple)) > 0: new_leaf_list.append(self.process_first_leaf(leaf_tuple[0])) if leaf_count > 2: for leaf in leaf_tuple[1:-1]: new_leaf_list.append(self.process_central_leaf(leaf)) if leaf_count > 1: new_leaf_list.append(self.process_last_leaf(leaf_tuple[-1])) return tuple(new_leaf_list)
[docs]class BangEachAttachment(BangAttachment):
[docs] @abc.abstractmethod def process_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: ...
[docs] def process_first_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return self.process_leaf(leaf)
[docs] def process_last_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return self.process_leaf(leaf)
[docs] def process_central_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return self.process_leaf(leaf)
[docs]class BangFirstAttachment(BangAttachment):
[docs] @abc.abstractmethod def process_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: ...
[docs] def process_first_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return self.process_leaf(leaf)
[docs] def process_last_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return leaf
[docs] def process_central_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: return leaf
[docs]class BangLastAttachment(BangAttachment):
[docs] @abc.abstractmethod def process_leaf( self, leaf: abjad.Leaf ) -> abjad.Leaf | typing.Sequence[abjad.Leaf]: ...
[docs] def process_first_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: return leaf
[docs] def process_last_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: return self.process_leaf(leaf)
[docs] def process_central_leaf(self, leaf: abjad.Leaf) -> abjad.Leaf: return leaf
[docs] def process_leaf_tuple( self, leaf_tuple: tuple[abjad.Leaf, ...], previous_attachment: typing.Optional[AbjadAttachment], ) -> tuple[abjad.Leaf, ...]: if len(leaf_tuple) > 0: return leaf_tuple[:-1] + (self.process_last_leaf(leaf_tuple[-1]),) else: return leaf_tuple