def _add_container_identifiers(self): if self.environment == "docs" and not getattr( self, "test_container_identifiers", False ): return segment_name = self.segment_name or "" segment_name = String(segment_name).to_segment_lilypond_identifier() contexts = [] try: context = self.score["Global_Skips"] contexts.append(context) except ValueError: pass try: context = self.score["Global_Rests"] contexts.append(context) except ValueError: pass for voice in iterate(self.score).components(Voice): if inspect(voice).annotation("INTERMITTENT") is True: continue contexts.append(voice) container_to_part_assignment = OrderedDict() for context in contexts: if segment_name: context_identifier = f"{segment_name}_{context.name}" else: context_identifier = context.name context.identifier = f"%*% {context_identifier}" part_container_count = 0 for container in iterate(context).components(Container): if not container.identifier: continue if container.identifier.startswith("%*% Part"): part_container_count += 1 globals_ = globals() part = container.identifier.strip("%*% ") part = eval(part, globals_) suffix = String().base_26(part_container_count).lower() container_identifier = f"{context_identifier}_{suffix}" container_identifier = String(container_identifier) assert container_identifier.is_lilypond_identifier() assert ( container_identifier not in container_to_part_assignment ) timespan = inspect(container).timespan() pair = (part, timespan) container_to_part_assignment[container_identifier] = pair container.identifier = f"%*% {container_identifier}" for staff in iterate(self.score).components(Staff): if segment_name: context_identifier = f"{segment_name}_{staff.name}" else: context_identifier = staff.name staff.identifier = f"%*% {context_identifier}" self._container_to_part_assignment = container_to_part_assignment
def _add_container_identifiers(self): if (self.environment == 'docs' and not getattr(self, 'test_container_identifiers', False)): return segment_name = self.segment_name or '' segment_name = String(segment_name).to_segment_lilypond_identifier() contexts = [] try: context = self.score['Global_Skips'] contexts.append(context) except ValueError: pass try: context = self.score['Global_Rests'] contexts.append(context) except ValueError: pass for voice in iterate(self.score).components(Voice): if inspect(voice).annotation('INTERMITTENT') is True: continue contexts.append(voice) container_to_part_assignment = OrderedDict() for context in contexts: if segment_name: context_identifier = f'{segment_name}_{context.name}' else: context_identifier = context.name context.identifier = f'%*% {context_identifier}' part_container_count = 0 for container in iterate(context).components(Container): if not container.identifier: continue if container.identifier.startswith('%*% Part'): part_container_count += 1 globals_ = globals() part = container.identifier.strip('%*% ') part = eval(part, globals_) suffix = String().base_26(part_container_count).lower() container_identifier = f'{context_identifier}_{suffix}' container_identifier = String(container_identifier) assert container_identifier.is_lilypond_identifier() assert container_identifier not in \ container_to_part_assignment timespan = inspect(container).timespan() pair = (part, timespan) container_to_part_assignment[container_identifier] = pair container.identifier = f'%*% {container_identifier}' for staff in iterate(self.score).components(Staff): if segment_name: context_identifier = f'{segment_name}_{staff.name}' else: context_identifier = staff.name staff.identifier = f'%*% {context_identifier}' self._container_to_part_assignment = container_to_part_assignment
def _update_all_leaf_indices_and_measure_numbers(score_root): """ Call only when updating offsets. No separate state flags exist for leaf indices or measure numbers. """ from abjad.core.Context import Context if isinstance(score_root, Context): contexts = iterate(score_root).components(Context) for context in contexts: for leaf_index, leaf in enumerate(iterate(context).leaves()): leaf._leaf_index = leaf_index else: for leaf_index, leaf in enumerate(iterate(score_root).leaves()): leaf._leaf_index = leaf_index
def check_misrepresented_flags( self, argument=None ) -> typing.Tuple[typing.List, int]: """ Checks misrepresented flags. """ violators: typing.List[Leaf] = [] total = set() for leaf in iterate(argument).leaves(): total.add(leaf) flags = leaf.written_duration.flag_count left = getattr(setting(leaf), "stem_left_beam_count", None) right = getattr(setting(leaf), "stem_right_beam_count", None) if left is not None: if flags < left or ( left < flags and right not in (flags, None) ): if leaf not in violators: violators.append(leaf) if right is not None: if flags < right or ( right < flags and left not in (flags, None) ): if leaf not in violators: violators.append(leaf) return violators, len(total)
def check_empty_containers( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks empty containers. .. container:: example >>> staff = abjad.Staff("c'4 d' e' f'") >>> staff.append(abjad.Container()) >>> abjad.f(staff) \new Staff { c'4 d'4 e'4 f'4 { } } >>> wellformedness = abjad.Wellformedness() >>> violators, total = wellformedness.check_empty_containers(staff) >>> violators [Container()] """ violators, containers = [], set() for container in iterate(argument).components(Container): containers.add(container) if len(container) == 0: violators.append(container) return violators, len(containers)
def _get_duration_in_seconds(self): if self.simultaneous: return max([Duration(0)] + [x._get_duration(in_seconds=True) for x in self]) else: duration = Duration(0) for leaf in iterate(self).leaves(): duration += leaf._get_duration(in_seconds=True) return duration
def _get_duration_in_seconds(self): if self.is_simultaneous: return max( [Duration(0)] + [x._get_duration(in_seconds=True) for x in self] ) else: duration = Duration(0) for leaf in iterate(self).leaves(): duration += leaf._get_duration(in_seconds=True) return duration
def _aggregate_context_wrappers(self, argument): """ Special_Voice may contain other instances of Special_Voice. This currently happens with OnBeatGraceContainer. This method aggregates all Special_Voice wrappers for checks. """ name_to_wrappers: typing.Dict = {} for context in iterate(argument).components(Context): if context.name not in name_to_wrappers: name_to_wrappers[context.name] = [] wrappers = context._dependent_wrappers[:] name_to_wrappers[context.name].extend(wrappers) return name_to_wrappers
def check_missing_parents( self, argument=None ) -> typing.Tuple[typing.List, int]: """ Checks missing parents. """ violators, total = [], set() components = iterate(argument).components() for i, component in enumerate(components): total.add(component) if 0 < i: parentage = inspect(component).parentage(grace_notes=True) if parentage.parent is None: violators.append(component) return violators, len(total)
def check_duplicate_ids( self, argument=None ) -> typing.Tuple[typing.List, int]: """ Checks duplicate IDs. """ violators = [] components = iterate(argument).components() total_ids = [id(_) for _ in components] unique_ids = Sequence(total_ids).remove_repeats() if len(unique_ids) < len(total_ids): for current_id in unique_ids: if 1 < total_ids.count(current_id): violators.extend( [_ for _ in components if id(_) == current_id] ) return violators, len(total_ids)
def __illustrate__( self, default_paper_size=None, global_staff_size=None, includes=None ): """ Illustrates score template. """ score: Score = self() for voice in iterate(score).components(Voice): skip = Skip(1, tag="abjad.ScoreTemplate.__illustrate__") voice.append(skip) self.attach_defaults(score) lilypond_file: LilyPondFile = score.__illustrate__() lilypond_file = new( lilypond_file, default_paper_size=default_paper_size, global_staff_size=global_staff_size, includes=includes, ) return lilypond_file
def _set_item(self, i, argument): from .GraceContainer import GraceContainer argument_indicators = [] for component in iterate(argument).components(): wrappers = inspect(component).wrappers() argument_indicators.extend(wrappers) if isinstance(i, int): argument = [argument] if i < 0: i = len(self) + i i = slice(i, i + 1) prototype = (Component, Selection) assert all(isinstance(_, prototype) for _ in argument) new_argument = [] for item in argument: if isinstance(item, Selection): new_argument.extend(item) else: new_argument.append(item) argument = new_argument assert all(isinstance(_, Component) for _ in argument) if any(isinstance(_, GraceContainer) for _ in argument): raise Exception("must attach grace container to note or chord.") if self._check_for_cycles(argument): raise exceptions.ParentageError("attempted to induce cycles.") if ( i.start == i.stop and i.start is not None and i.stop is not None and i.start <= -len(self) ): start, stop = 0, 0 else: start, stop, stride = i.indices(len(self)) old_components = self[start:stop] del self[start:stop] self._components.__setitem__(slice(start, start), argument) for component in argument: component._set_parent(self) for indicator in argument_indicators: if hasattr(indicator, "_update_effective_context"): indicator._update_effective_context()
def _set_item(self, i, argument): from .BeforeGraceContainer import BeforeGraceContainer argument_indicators = [] for component in iterate(argument).components(): wrappers = inspect(component).wrappers() argument_indicators.extend(wrappers) if isinstance(i, int): argument = [argument] if i < 0: i = len(self) + i i = slice(i, i + 1) prototype = (Component, Selection) assert all(isinstance(_, prototype) for _ in argument) new_argument = [] for item in argument: if isinstance(item, Selection): new_argument.extend(item) else: new_argument.append(item) argument = new_argument assert all(isinstance(_, Component) for _ in argument) if any(isinstance(_, BeforeGraceContainer) for _ in argument): raise Exception("must attach grace container to note or chord.") if self._check_for_cycles(argument): raise exceptions.ParentageError("attempted to induce cycles.") if ( i.start == i.stop and i.start is not None and i.stop is not None and i.start <= -len(self) ): start, stop = 0, 0 else: start, stop, stride = i.indices(len(self)) del self[start:stop] self._components.__setitem__(slice(start, start), argument) for component in argument: component._set_parent(self) for indicator in argument_indicators: if hasattr(indicator, "_update_effective_context"): indicator._update_effective_context()
def from_selection(selection) -> "Clef": """ Makes clef from ``selection``. .. container:: example >>> maker = abjad.NoteMaker() >>> notes = maker(range(-12, -6), [(1, 4)]) >>> staff = abjad.Staff(notes) >>> abjad.Clef.from_selection(staff) Clef('bass') Choses between treble and bass based on minimal number of ledger lines. """ pitches = iterate(selection).pitches() diatonic_pitch_numbers = [ pitch._get_diatonic_pitch_number() for pitch in pitches ] max_diatonic_pitch_number = max(diatonic_pitch_numbers) min_diatonic_pitch_number = min(diatonic_pitch_numbers) lowest_treble_line_pitch = NamedPitch("E4") lowest_treble_line_diatonic_pitch_number = ( lowest_treble_line_pitch._get_diatonic_pitch_number() ) candidate_steps_below_treble = ( lowest_treble_line_diatonic_pitch_number - min_diatonic_pitch_number ) highest_bass_line_pitch = NamedPitch("A3") highest_bass_line_diatonic_pitch_number = ( highest_bass_line_pitch._get_diatonic_pitch_number() ) candidate_steps_above_bass = ( max_diatonic_pitch_number - highest_bass_line_diatonic_pitch_number ) if candidate_steps_above_bass < candidate_steps_below_treble: return Clef("bass") else: return Clef("treble")
def check_beamed_long_notes(self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks beamed long notes. .. container:: example Beamed quarter notes are not wellformed: >>> voice = abjad.Voice("c'4 d'4 e'4 f'4") >>> abjad.attach(abjad.StartBeam(), voice[0]) >>> abjad.attach(abjad.StopBeam(), voice[1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'4 [ d'4 ] e'4 f'4 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 2 / 4 beamed long notes 0 / 5 duplicate ids 0 / 1 empty containers 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners Beamed eighth notes are wellformed: >>> voice = abjad.Voice("c'8 d'8 e'8 f'8") >>> abjad.attach(abjad.StartBeam(), voice[0]) >>> abjad.attach(abjad.StopBeam(), voice[1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'8 [ d'8 ] e'8 f'8 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 4 beamed long notes 0 / 5 duplicate ids 0 / 1 empty containers 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners .. container:: example REGRESSION. Matching start- and stop-beam indicators work correctly: >>> voice = abjad.Voice("c'8 d'8 e'4 f'2") >>> abjad.attach(abjad.StartBeam(), voice[0]) >>> abjad.attach(abjad.StopBeam(), voice[1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'8 [ d'8 ] e'4 f'2 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 4 beamed long notes 0 / 5 duplicate ids 0 / 1 empty containers 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners The examples above feature Abjad voice containers because beams are voice-persistent. """ violators, total = [], 0 for leaf in iterate(argument).leaves(): total += 1 if leaf.written_duration < Duration((1, 4)): continue start_wrapper = inspect(leaf).effective_wrapper(StartBeam) if start_wrapper is None: continue stop_wrapper = inspect(leaf).effective_wrapper(StopBeam) if stop_wrapper is None: violators.append(leaf) continue if stop_wrapper.leaked_start_offset < start_wrapper.leaked_start_offset: violators.append(leaf) continue leaf_start_offset = inspect(leaf).timespan().start_offset if stop_wrapper.leaked_start_offset == leaf_start_offset: violators.append(leaf) return violators, total
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, 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 check_unterminated_text_spanners( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks unterminated text spanners. .. container:: example Unterminated text spanner is not wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> start_text_span = abjad.StartTextSpan() >>> abjad.attach(start_text_span, voice[0]) >>> abjad.f(voice) \new Voice { c'4 \startTextSpan c'4 c'4 c'4 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 1 overlapping text spanners 0 / 1 unmatched stop text spans 0 / 0 unterminated hairpins 1 / 1 unterminated text spanners Terminated text span is wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> start_text_span = abjad.StartTextSpan() >>> abjad.attach(start_text_span, voice[0]) >>> stop_text_span = abjad.StopTextSpan() >>> abjad.attach(stop_text_span, voice[-1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'4 \startTextSpan c'4 c'4 c'4 \stopTextSpan } >>> abjad.inspect(voice).wellformed() True """ violators, total = [], 0 for context in iterate(argument).components(Context): wrappers = context._dependent_wrappers[:] wrappers.sort(key=lambda _: _.start_offset) open_spanners: typing.Dict = {} for wrapper in wrappers: if isinstance(wrapper.indicator, StartTextSpan): total += 1 command = wrapper.indicator.command command = command.replace("start", "") command = command.replace("Start", "") if command not in open_spanners: open_spanners[command] = [] open_spanners[command].append(wrapper.component) elif isinstance(wrapper.indicator, StopTextSpan): command = wrapper.indicator.command command = command.replace("stop", "") command = command.replace("Stop", "") if ( command not in open_spanners or not open_spanners[command] ): # unmatched stop text span pass else: open_spanners[command].pop() for command, list_ in open_spanners.items(): for component in list_: violators.append(component) return violators, total
def check_unterminated_hairpins( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks unterminated hairpins. .. container:: example Unterminated crescendo is not wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> start_hairpin = abjad.StartHairpin('<') >>> abjad.attach(start_hairpin, voice[0]) >>> abjad.f(voice) \new Voice { c'4 \< c'4 c'4 c'4 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 1 / 1 unterminated hairpins 0 / 0 unterminated text spanners Even with start dynamic: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> dynamic = abjad.Dynamic('f') >>> abjad.attach(dynamic, voice[0]) >>> start_hairpin = abjad.StartHairpin('<') >>> abjad.attach(start_hairpin, voice[0]) >>> abjad.f(voice) \new Voice { c'4 \f \< c'4 c'4 c'4 } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 1 / 1 unterminated hairpins 0 / 0 unterminated text spanners Terminated crescendo is wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> start_hairpin = abjad.StartHairpin('<') >>> abjad.attach(start_hairpin, voice[0]) >>> dynamic = abjad.Dynamic('f') >>> abjad.attach(dynamic, voice[-1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'4 \< c'4 c'4 c'4 \f } >>> abjad.inspect(voice).wellformed() True .. container:: example Bang-terminated crescendo is wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> start_hairpin = abjad.StartHairpin('<') >>> abjad.attach(start_hairpin, voice[0]) >>> stop_hairpin = abjad.StopHairpin() >>> abjad.attach(stop_hairpin, voice[-1]) >>> abjad.show(voice) # doctest: +SKIP .. docs:: >>> abjad.f(voice) \new Voice { c'4 \< c'4 c'4 c'4 \! } >>> abjad.inspect(voice).wellformed() True """ violators, total = [], 0 for context in iterate(argument).components(Context): last_dynamic = None last_tag = None wrappers = context._dependent_wrappers[:] wrappers.sort(key=lambda _: _.start_offset) for wrapper in wrappers: parameter = getattr(wrapper.indicator, "parameter", None) if parameter == "DYNAMIC" or isinstance( wrapper.indicator, StopHairpin ): last_dynamic = wrapper.indicator last_tag = wrapper.tag if isinstance(wrapper.indicator, StartHairpin): total += 1 if isinstance( last_dynamic, StartHairpin ) and "right_broken" not in str(last_tag): violators.append(wrapper.component) return violators, total
def check_overlapping_text_spanners( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks overlapping text spanners. .. container:: example Overlapping text spanners are not wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> abjad.text_spanner(voice) >>> abjad.text_spanner(voice[1:3]) >>> abjad.f(voice) \new Voice { c'4 \startTextSpan c'4 \startTextSpan c'4 \stopTextSpan c'4 \stopTextSpan } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 1 / 2 overlapping text spanners 0 / 2 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 2 unterminated text spanners .. container:: example Overlapping text spanners with different IDs are wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> abjad.text_spanner(voice) >>> command = r'\startTextSpanOne' >>> start_text_span = abjad.StartTextSpan(command=command) >>> abjad.attach(start_text_span, voice[1]) >>> command = r'\stopTextSpanOne' >>> stop_text_span = abjad.StopTextSpan(command=command) >>> abjad.attach(stop_text_span, voice[2]) >>> abjad.f(voice) \new Voice { c'4 \startTextSpan c'4 \startTextSpanOne c'4 \stopTextSpanOne c'4 \stopTextSpan } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 2 overlapping text spanners 0 / 2 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 2 unterminated text spanners .. container:: example Enchained text spanners are wellformed: >>> voice = abjad.Voice("c'4 c'4 c'4 c'4") >>> abjad.text_spanner(voice[:3]) >>> abjad.text_spanner(voice[-2:]) >>> abjad.f(voice) \new Voice { c'4 \startTextSpan c'4 c'4 \stopTextSpan \startTextSpan c'4 \stopTextSpan } >>> agent = abjad.inspect(voice) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 2 overlapping text spanners 0 / 2 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 2 unterminated text spanners """ violators, total = [], 0 def key(wrapper): if isinstance(wrapper.indicator, StartTextSpan): priority = 1 else: priority = 0 return (wrapper.start_offset, priority) for context in iterate(argument).components(Context): wrappers = context._dependent_wrappers[:] wrappers.sort(key=key) open_spanners: typing.Dict = {} for wrapper in wrappers: if isinstance(wrapper.indicator, StartTextSpan): # print(wrapper.indicator) total += 1 command = wrapper.indicator.command command = command.replace("start", "") command = command.replace("Start", "") # print(command, 'START', wrapper.start_offset) if command not in open_spanners: open_spanners[command] = [] if open_spanners[command]: violators.append(wrapper.component) open_spanners[command].append(wrapper.component) elif isinstance(wrapper.indicator, StopTextSpan): # print(wrapper.indicator) command = wrapper.indicator.command command = command.replace("stop", "") command = command.replace("Stop", "") # print(command, 'STOP', wrapper.start_offset) if command in open_spanners and open_spanners[command]: open_spanners[command].pop() return violators, total
def check_out_of_range_pitches( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks out-of-range notes. .. container:: example Out of range: >>> staff = abjad.Staff("c'8 r8 <d fs>8 r8") >>> violin = abjad.Violin() >>> abjad.attach(violin, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'8 r8 <d fs>8 r8 } >>> agent = abjad.inspect(staff) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 1 / 2 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners .. container:: example Allows out-of-range pitches: >>> staff = abjad.Staff("c'8 r8 <d fs>8 r8") >>> violin = abjad.Violin() >>> abjad.attach(violin, staff[0]) >>> abjad.attach(abjad.const.ALLOW_OUT_OF_RANGE, staff[2]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { c'8 r8 <d fs>8 r8 } >>> agent = abjad.inspect(staff) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 2 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners """ violators, total = [], set() for leaf in iterate(argument).leaves(pitched=True): total.add(leaf) if inspect(leaf).has_indicator(const.ALLOW_OUT_OF_RANGE): continue # TODO: change to has_indicator(const.HIDDEN): if inspect(leaf).annotation(const.HIDDEN) is True: continue instrument = inspect(leaf).effective(Instrument) if instrument is None: continue if leaf not in instrument.pitch_range: violators.append(leaf) return violators, len(total)
def check_notes_on_wrong_clef( self, argument=None ) -> typing.Tuple[typing.List, int]: r""" Checks notes and chords on wrong clef. .. container:: example >>> staff = abjad.Staff("c'8 d'8 e'8 f'8") >>> clef = abjad.Clef('alto') >>> abjad.attach(clef, staff[0]) >>> violin = abjad.Violin() >>> abjad.attach(violin, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \clef "alto" c'8 d'8 e'8 f'8 } >>> agent = abjad.inspect(staff) >>> print(agent.tabulate_wellformedness()) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 4 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners .. container:: example Allows percussion clef: >>> staff = abjad.Staff("c'8 d'8 e'8 f'8") >>> clef = abjad.Clef('percussion') >>> abjad.attach(clef, staff[0]) >>> violin = abjad.Violin() >>> abjad.attach(violin, staff[0]) >>> abjad.show(staff) # doctest: +SKIP .. docs:: >>> abjad.f(staff) \new Staff { \clef "percussion" c'8 d'8 e'8 f'8 } >>> agent = abjad.inspect(staff) >>> print(agent.tabulate_wellformedness(allow_percussion_clef=True)) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 0 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners .. container:: example Forbids percussion clef: >>> agent = abjad.inspect(staff) >>> print(agent.tabulate_wellformedness(allow_percussion_clef=False)) 0 / 5 duplicate ids 0 / 1 empty containers 0 / 4 misrepresented flags 0 / 5 missing parents 4 / 4 notes on wrong clef 0 / 4 out of range pitches 0 / 0 overlapping text spanners 0 / 0 unmatched stop text spans 0 / 0 unterminated hairpins 0 / 0 unterminated text spanners """ violators, total = [], set() for leaf in iterate(argument).leaves(): total.add(leaf) instrument = inspect(leaf).effective(Instrument) if instrument is None: continue clef = inspect(leaf).effective(Clef) if clef is None: continue allowable_clefs = [Clef(_) for _ in instrument.allowable_clefs] if self.allow_percussion_clef: allowable_clefs.append(Clef("percussion")) if clef not in allowable_clefs: violators.append(leaf) return violators, len(total)
def _before_attach(self, argument): if self._ignore_before_attach: return for leaf in iterate(argument).leaves(): detach(Tie, leaf)
def __getitem__(self, name): r""" Gets item with ``name``. .. container:: example Gets header block: >>> lilypond_file = abjad.LilyPondFile.new() >>> lilypond_file['header'] <Block(name='header')> .. container:: example Searches score: >>> voice_1 = abjad.Voice("c''4 b' a' g'", name='Custom_Voice_1') >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceOne'), voice_1) >>> voice_2 = abjad.Voice("c'4 d' e' f'", name='Custom_Voice_2') >>> abjad.attach(abjad.LilyPondLiteral(r'\voiceTwo'), voice_2) >>> staff = abjad.Staff( ... [voice_1, voice_2], ... is_simultaneous=True, ... name='Custom_Staff', ... ) >>> score = abjad.Score([staff], name='Custom_Score') >>> lilypond_file = abjad.LilyPondFile.new(score) >>> abjad.show(score) # doctest: +SKIP .. docs:: >>> abjad.f(score) \context Score = "Custom_Score" << \context Staff = "Custom_Staff" << \context Voice = "Custom_Voice_1" { \voiceOne c''4 b'4 a'4 g'4 } \context Voice = "Custom_Voice_2" { \voiceTwo c'4 d'4 e'4 f'4 } >> >> >>> lilypond_file['score'] <Block(name='score')> >>> lilypond_file['Custom_Score'] <Score-"Custom_Score"<<1>>> >>> lilypond_file[abjad.Score] <Score-"Custom_Score"<<1>>> >>> lilypond_file['Custom_Staff'] <Staff-"Custom_Staff"<<2>>> >>> lilypond_file[abjad.Staff] <Staff-"Custom_Staff"<<2>>> >>> lilypond_file['Custom_Voice_1'] Voice("c''4 b'4 a'4 g'4", name='Custom_Voice_1') >>> lilypond_file['Custom_Voice_2'] Voice("c'4 d'4 e'4 f'4", name='Custom_Voice_2') >>> lilypond_file[abjad.Voice] Voice("c''4 b'4 a'4 g'4", name='Custom_Voice_1') .. container:: example REGRESSION. Works when score block contains parallel container: >>> include_container = abjad.Container() >>> string = r'\include "layout.ly"' >>> literal = abjad.LilyPondLiteral(string, 'opening') >>> abjad.attach(literal, include_container) >>> staff = abjad.Staff("c'4 d' e' f'", name='Custom_Staff') >>> container = abjad.Container( ... [include_container, staff], ... is_simultaneous=True, ... ) >>> lilypond_file = abjad.LilyPondFile.new( ... container, ... lilypond_language_token=False, ... lilypond_version_token=False, ... ) >>> del(lilypond_file.items[:3]) >>> abjad.f(lilypond_file) \score { %! abjad.LilyPondFile << { \include "layout.ly" } \context Staff = "Custom_Staff" { c'4 d'4 e'4 f'4 } >> } %! abjad.LilyPondFile >>> lilypond_file[abjad.Staff] Staff("c'4 d'4 e'4 f'4", name='Custom_Staff') >>> lilypond_file['Custom_Staff'] Staff("c'4 d'4 e'4 f'4", name='Custom_Staff') Returns item. Raises key error when no item with ``name`` is found. """ if not isinstance(name, str): if inspect.isclass(name): assert issubclass(name, Component), repr(name) else: assert isinstance(name, Component), repr(name) score = None if self.score_block and self.score_block.items: items = self.score_block.items for container in iterate(items).components(Container): if isinstance(container, Context): score = container break if isinstance(name, str): for item in self.items: if getattr(item, "name", None) == name: return item if score is not None: if score.name == name: return score context = score[name] return context raise KeyError(f"can not find item with name {name!r}.") elif isinstance(name, Component): for item in self.items: if item is name: return item if score is not None: if score is name: return score prototype = Context for context in iterate(score).components(prototype): if context is name: return context raise KeyError(f"can not find {name}.") elif inspect.isclass(name) and issubclass(name, Component): for item in self.items: if isinstance(item, name): return item if score is not None: if isinstance(score, name): return score prototype = Context for context in iterate(score).components(prototype): if isinstance(context, name): return context raise KeyError(f"can not find item of class {name}.") else: raise TypeError(name)
def _split_by_duration( self, duration, tie_split_notes=True, repeat_ties=False ): if self.is_simultaneous: return self._split_simultaneous_by_duration( duration=duration, tie_split_notes=tie_split_notes, repeat_ties=repeat_ties, ) duration = Duration(duration) assert 0 <= duration, repr(duration) if duration == 0: return [], self # get split point score offset timespan = inspect(self).timespan() global_split_point = timespan.start_offset + duration # get any duration-crossing descendents cross_offset = timespan.start_offset + duration duration_crossing_descendants = [] for descendant in inspect(self).descendants(): timespan = inspect(descendant).timespan() start_offset = timespan.start_offset stop_offset = timespan.stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) # any duration-crossing leaf will be at end of list bottom = duration_crossing_descendants[-1] did_split_leaf = False # if split point necessitates leaf split if isinstance(bottom, Leaf): assert isinstance(bottom, Leaf) did_split_leaf = True timespan = inspect(bottom).timespan() start_offset = timespan.start_offset split_point_in_bottom = global_split_point - start_offset new_leaves = bottom._split_by_durations( [split_point_in_bottom], tie_split_notes=tie_split_notes, repeat_ties=repeat_ties, ) for leaf in new_leaves: timespan = inspect(leaf).timespan() if timespan.stop_offset == global_split_point: leaf_left_of_split = leaf if timespan.start_offset == global_split_point: leaf_right_of_split = leaf duration_crossing_containers = duration_crossing_descendants[:-1] if not len(duration_crossing_containers): return left_list, right_list # if split point falls between leaves # then find leaf to immediate right of split point # in order to start upward crawl through duration-crossing containers else: duration_crossing_containers = duration_crossing_descendants[:] for leaf in iterate(bottom).leaves(): timespan = inspect(leaf).timespan() if timespan.start_offset == global_split_point: leaf_right_of_split = leaf leaf_left_of_split = inspect(leaf).leaf(-1) break else: raise Exception("can not split empty container {bottom!r}.") assert leaf_left_of_split is not None assert leaf_right_of_split is not None # find component to right of split # that is also immediate child of last duration-crossing container for component in inspect(leaf_right_of_split).parentage(): parent = inspect(component).parentage().parent if parent is duration_crossing_containers[-1]: highest_level_component_right_of_split = component break else: raise ValueError("should not be able to get here.") # crawl back up through duration-crossing containers and split each previous = highest_level_component_right_of_split for container in reversed(duration_crossing_containers): assert isinstance(container, Container) index = container.index(previous) left, right = container._split_at_index(index) previous = right # reapply tie here if crawl above killed tie applied to leaves if did_split_leaf: if tie_split_notes and isinstance(leaf_left_of_split, Note): if ( inspect(leaf_left_of_split).parentage().root is inspect(leaf_right_of_split).parentage().root ): leaves_around_split = ( leaf_left_of_split, leaf_right_of_split, ) selection = select(leaves_around_split) selection._attach_tie_to_leaves(repeat_ties=repeat_ties) # return list-wrapped halves of container return [left], [right]
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