def select(expr=None, contiguous=False): r'''Selects `expr`. Returns selection. ''' from abjad.tools import scoretools from abjad.tools import selectiontools from abjad.tools import spannertools Selection = selectiontools.Selection if contiguous: if isinstance(expr, (list, tuple)): assert Selection._all_are_contiguous_components_in_same_logical_voice( expr) return selectiontools.ContiguousSelection(expr) elif isinstance(expr, scoretools.Component): return selectiontools.ContiguousSelection(expr) elif hasattr(expr, '_music'): music = expr._music return selectiontools.Selection(music) elif isinstance(expr, spannertools.Spanner): music = expr._components return selectiontools.Selection(music) elif expr is None: return selectiontools.Selection() else: return selectiontools.Selection(expr)
def leaves_grouped_by_immediate_parents(self): r'''Leaves in logical tie grouped by immediate parents of leaves. Returns list of lists. ''' from abjad.tools import selectiontools result = [] pairs_generator = itertools.groupby(self, lambda x: id(x._parent)) for key, values_generator in pairs_generator: group = selectiontools.ContiguousSelection(list(values_generator)) result.append(group) return result
def _split_by_duration( self, duration, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import indicatortools from abjad.tools import pitchtools from abjad.tools import selectiontools # check input duration = durationtools.Duration(duration) # calculate durations leaf_multiplied_duration = self._multiplied_duration prolation = self._get_parentage(include_self=False).prolation preprolated_duration = duration / prolation # handle boundary cases if preprolated_duration <= 0: return ([], [self]) if leaf_multiplied_duration <= preprolated_duration: return ([self], []) # create new leaf new_leaf = copy.copy(self) self._splice([new_leaf], grow_spanners=True) # adjust leaf self._detach_grace_containers(kind='after') # adjust new leaf new_leaf._detach_grace_containers(kind='grace') left_leaf_list = self._set_duration( preprolated_duration, use_messiaen_style_ties=use_messiaen_style_ties, ) right_preprolated_duration = \ leaf_multiplied_duration - preprolated_duration right_leaf_list = new_leaf._set_duration( right_preprolated_duration, use_messiaen_style_ties=use_messiaen_style_ties, ) leaf_left_of_split = left_leaf_list[-1] leaf_right_of_split = right_leaf_list[0] leaves_around_split = (leaf_left_of_split, leaf_right_of_split) if fracture_spanners: for spanner in leaf_left_of_split._get_spanners(): index = spanner._index(leaf_left_of_split) spanner._fracture(index, direction=Right) # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: selection = selectiontools.ContiguousSelection(leaves_around_split) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) return left_leaf_list, right_leaf_list
def select_leaves( self, start=0, stop=None, leaf_classes=None, recurse=True, allow_discontiguous_leaves=False, ): r'''Selects leaves in container. .. container:: example **Example 1.** Selects leaves from container: :: >>> container = Container("c'8 d'8 r8 e'8") :: >>> container.select_leaves() ContiguousSelection(Note("c'8"), Note("d'8"), Rest('r8'), Note("e'8")) Returns contiguous leaf selection or free leaf selection. ''' from abjad.tools import scoretools from abjad.tools import selectiontools Selection = selectiontools.Selection leaf_classes = leaf_classes or (scoretools.Leaf, ) expr = self if recurse: expr = iterate(expr).by_class(scoretools.Leaf) music = [ component for component in expr if isinstance(component, leaf_classes) ] music = music[start:stop] if allow_discontiguous_leaves: selection = selectiontools.Selection(music=music) else: assert Selection._all_are_contiguous_components_in_same_logical_voice( music) selection = selectiontools.ContiguousSelection(music=music) return selection
def _split( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import pitchtools from abjad.tools import selectiontools from abjad.tools import scoretools from abjad.tools import spannertools durations = [durationtools.Duration(x) for x in durations] if cyclic: durations = sequencetools.repeat_sequence_to_weight( durations, self._get_duration()) durations = [durationtools.Duration(x) for x in durations] if sum(durations) < self._get_duration(): last_duration = self._get_duration() - sum(durations) durations.append(last_duration) sequencetools.truncate_sequence( durations, weight=self._get_duration(), ) result = [] leaf_prolation = self._get_parentage(include_self=False).prolation timespan = self._get_timespan() start_offset = timespan.start_offset for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation shard = new_leaf._set_duration( preprolated_duration, use_messiaen_style_ties=use_messiaen_style_ties, ) for x in shard: if isinstance(x, scoretools.Leaf): x_duration = x.written_duration * leaf_prolation else: x_duration = x.multiplied_duration * leaf_prolation stop_offset = x_duration + start_offset x._start_offset = start_offset x._stop_offset = stop_offset x._timespan = timespantools.Timespan( start_offset=start_offset, stop_offset=stop_offset, ) start_offset = stop_offset shard = [x._get_parentage().root for x in shard] result.append(shard) flattened_result = sequencetools.flatten_sequence(result) flattened_result = selectiontools.SliceSelection(flattened_result) prototype = (spannertools.Tie,) parentage = self._get_parentage() if parentage._get_spanners(prototype=prototype): selection = select(flattened_result) for component in selection: # TODO: make top-level detach() work here for spanner in component._get_spanners(prototype): spanner._sever_all_components() #detach(prototype, component) # replace leaf with flattened result selection = selectiontools.SliceSelection(self) parent, start, stop = selection._get_parent_and_start_stop_indices() if parent: parent.__setitem__(slice(start, stop + 1), flattened_result) else: selection._give_dominant_spanners(flattened_result) selection._withdraw_from_crossing_spanners() # fracture spanners if fracture_spanners: first_shard = result[0] for spanner in first_shard[-1]._get_spanners(): index = spanner._index(first_shard[-1]) spanner._fracture(index, direction=Right) last_shard = result[-1] for spanner in last_shard[0]._get_spanners(): index = spanner._index(last_shard[0]) spanner._fracture(index, direction=Left) for middle_shard in result[1:-1]: for spanner in middle_shard[0]._get_spanners(): index = spanner._index(middle_shard[0]) spanner._fracture(index, direction=Left) for spanner in middle_shard[-1]._get_spanners(): index = spanner._index(middle_shard[-1]) spanner._fracture(index, direction=Right) # adjust first leaf first_leaf = flattened_result[0] self._detach_grace_containers(kind='after') # adjust any middle leaves for middle_leaf in flattened_result[1:-1]: middle_leaf._detach_grace_containers(kind='grace') self._detach_grace_containers(kind='after') detach(object, middle_leaf) # adjust last leaf last_leaf = flattened_result[-1] last_leaf._detach_grace_containers(kind='grace') detach(object, last_leaf) # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: flattened_result_leaves = iterate(flattened_result).by_class( scoretools.Leaf) # TODO: implement SliceSelection._attach_tie_spanner_to_leaves() for leaf_pair in sequencetools.iterate_sequence_nwise( flattened_result_leaves): selection = selectiontools.ContiguousSelection(leaf_pair) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return result return result
def _split_by_duration( self, duration, fracture_spanners=False, tie_split_notes=True, use_messiaen_style_ties=False, ): from abjad.tools import scoretools from abjad.tools import selectiontools # check input duration = durationtools.Duration(duration) assert 0 <= duration, repr(duration) # if zero duration then return empty list and self if duration == 0: return [], self # get split point score offset global_split_point = self._get_timespan().start_offset + duration # get any duration-crossing descendents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) # get any duration-crossing measure descendents measures = [ x for x in duration_crossing_descendants if isinstance(x, scoretools.Measure) ] # if we must split a power-of-two measure at non-power-of-two # split point then go ahead and transform the power-of-two measure # to non-power-of-two equivalent now; # code that crawls and splits later on will be happier if len(measures) == 1: measure = measures[0] split_point_in_measure = \ global_split_point - measure._get_timespan().start_offset if measure.has_non_power_of_two_denominator: if not measure.implied_prolation == \ split_point_in_measure.implied_prolation: raise NotImplementedError elif not mathtools.is_nonnegative_integer_power_of_two( split_point_in_measure.denominator): non_power_of_two_factors = mathtools.remove_powers_of_two( split_point_in_measure.denominator) non_power_of_two_factors = mathtools.factors( non_power_of_two_factors) non_power_of_two_product = 1 for non_power_of_two_factor in non_power_of_two_factors: non_power_of_two_product *= non_power_of_two_factor scoretools.scale_measure_denominator_and_adjust_measure_contents( measure, non_power_of_two_product) # rederive duration crosses with possibly new measure contents cross_offset = self._get_timespan().start_offset + duration duration_crossing_descendants = [] for descendant in self._get_descendants(): start_offset = descendant._get_timespan().start_offset stop_offset = descendant._get_timespan().stop_offset if start_offset < cross_offset < stop_offset: duration_crossing_descendants.append(descendant) elif 1 < len(measures): message = 'measures can not nest.' raise Exception(message) # 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, scoretools.Leaf): assert isinstance(bottom, scoretools.Leaf) did_split_leaf = True split_point_in_bottom = \ global_split_point - bottom._get_timespan().start_offset left_list, right_list = bottom._split_by_duration( split_point_in_bottom, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, use_messiaen_style_ties=use_messiaen_style_ties, ) right = right_list[0] leaf_right_of_split = right leaf_left_of_split = left_list[-1] 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).by_class(scoretools.Leaf): if leaf._get_timespan().start_offset == global_split_point: leaf_right_of_split = leaf leaf_left_of_split = leaf_right_of_split._get_leaf(-1) break else: message = 'can not split empty container {!r}.' message = message.format(bottom) raise Exception(message) # find component to right of split that is also immediate child of # last duration-crossing container for component in \ leaf_right_of_split._get_parentage(include_self=True): if component._parent is duration_crossing_containers[-1]: highest_level_component_right_of_split = component break else: message = 'should we be able to get here?' raise ValueError(message) # crawl back up through duration-crossing containers and # fracture spanners if requested if fracture_spanners: start_offset = leaf_right_of_split._get_timespan().start_offset for parent in leaf_right_of_split._get_parentage(): if parent._get_timespan().start_offset == start_offset: for spanner in parent._get_spanners(): index = spanner._index(parent) spanner._fracture(index, direction=Left) if parent is component: break # crawl back up through duration-crossing containers and split each previous = highest_level_component_right_of_split for duration_crossing_container in \ reversed(duration_crossing_containers): assert isinstance(duration_crossing_container, scoretools.Container) i = duration_crossing_container.index(previous) left, right = duration_crossing_container._split_at_index( i, fracture_spanners=fracture_spanners, ) previous = right # NOTE: If logical tie here is convenience, then fusing is good. # If logical tie here is user-given, then fusing is less good. # Maybe later model difference between user logical ties and not. left_logical_tie = leaf_left_of_split._get_logical_tie() right_logical_tie = leaf_right_of_split._get_logical_tie() left_logical_tie._fuse_leaves_by_immediate_parent() right_logical_tie._fuse_leaves_by_immediate_parent() # 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, scoretools.Note)): if (leaf_left_of_split._get_parentage().root is leaf_right_of_split._get_parentage().root): leaves_around_split = \ (leaf_left_of_split, leaf_right_of_split) selection = selectiontools.ContiguousSelection( leaves_around_split) selection._attach_tie_spanner_to_leaf_pair( use_messiaen_style_ties=use_messiaen_style_ties, ) # return pair of left and right list-wrapped halves of container return ([left], [right])
def apply_full_measure_tuplets_to_contents_of_measures_in_expr( expr, supplement=None): r'''Applies full-measure tuplets to contents of measures in `expr`: :: >>> staff = Staff([ ... Measure((2, 8), "c'8 d'8"), ... Measure((3, 8), "e'8 f'8 g'8")]) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { { \time 2/8 c'8 d'8 } { \time 3/8 e'8 f'8 g'8 } } :: >>> scoretools.apply_full_measure_tuplets_to_contents_of_measures_in_expr(staff) >>> show(staff) # doctest: +SKIP .. doctest:: >>> print(format(staff)) \new Staff { { \time 2/8 { c'8 d'8 } } { \time 3/8 { e'8 f'8 g'8 } } } Returns none. ''' from abjad.tools import selectiontools from abjad.tools import scoretools supplement = selectiontools.ContiguousSelection(supplement) assert isinstance(supplement, selectiontools.ContiguousSelection) for measure in iterate(expr).by_class(scoretools.Measure): target_duration = measure._preprolated_duration tuplet = scoretools.FixedDurationTuplet(target_duration, measure[:]) if supplement: new_supplement = mutate(supplement).copy() tuplet.extend(new_supplement)
def _set_item( self, i, expr, withdraw_components_in_expr_from_crossing_spanners=True, ): r'''This method exists beacuse __setitem__ can not accept keywords. Note that setting withdraw_components_in_expr_from_crossing_spanners=False constitutes a composer-unsafe use of this method. Only private methods should set this keyword. ''' from abjad.tools import indicatortools from abjad.tools import scoretools from abjad.tools import selectiontools from abjad.tools import spannertools # cache indicators attached to components in expr expr_indicators = [] for component in iterate(expr).by_class(): indicators = component._get_indicators(unwrap=False) expr_indicators.extend(indicators) # item assignment if isinstance(i, int): if isinstance(expr, str): expr = self._parse_string(expr)[:] assert len(expr) == 1, repr(expr) expr = expr[0] assert all(isinstance(x, scoretools.Component) for x in [expr]) if any(isinstance(x, scoretools.GraceContainer) for x in [expr]): message = 'must attach grace container to note or chord.' raise Exception(message) old = self[i] selection = selectiontools.ContiguousSelection(old) spanners_receipt = selection._get_dominant_spanners() for child in iterate([old]).by_class(): for spanner in child._get_spanners(): spanner._remove(child) if i < 0: i = len(self) + i del(self[i]) # must withdraw from spanners before withdrawing from parentage! # otherwise begin / end assessments don't work! if withdraw_components_in_expr_from_crossing_spanners: selection = selectiontools.SliceSelection([expr]) selection._withdraw_from_crossing_spanners() expr._set_parent(self) self._music.insert(i, expr) for spanner, index in spanners_receipt: spanner._insert(index, expr) expr._spanners.add(spanner) # slice assignment else: if isinstance(expr, str): expr = self._parse_string(expr)[:] elif isinstance(expr, list) and \ len(expr) == 1 and \ isinstance(expr[0], str): expr = self._parse_string(expr[0])[:] prototype = (scoretools.Component, selectiontools.Selection) assert all(isinstance(x, prototype) for x in expr) new_expr = [] for item in expr: if isinstance(item, selectiontools.Selection): new_expr.extend(item) else: new_expr.append(item) expr = new_expr assert all(isinstance(x, scoretools.Component) for x in expr) if any(isinstance(x, scoretools.GraceContainer) for x in expr): message = 'must attach grace container to note or chord.' raise Exception(message) 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 = self[start:stop] spanners_receipt = self._get_spanners_that_dominate_slice( start, stop) for component in old: for child in iterate([component]).by_class(): for spanner in child._get_spanners(): spanner._remove(child) del(self[start:stop]) # must withdraw before setting in self! # otherwise circular withdraw ensues! if withdraw_components_in_expr_from_crossing_spanners: selection = selectiontools.SliceSelection(expr) if selection._all_are_contiguous_components_in_same_logical_voice( selection): selection._withdraw_from_crossing_spanners() self._music.__setitem__(slice(start, start), expr) for component in expr: component._set_parent(self) for spanner, index in spanners_receipt: for component in reversed(expr): spanner._insert(index, component) component._spanners.add(spanner) for indicator in expr_indicators: if hasattr(indicator, '_update_effective_context'): indicator._update_effective_context()
def _splice( self, components, direction=Right, grow_spanners=True, ): from abjad.tools import scoretools from abjad.tools import selectiontools assert all(isinstance(x, scoretools.Component) for x in components) selection = selectiontools.ContiguousSelection(self) if direction == Right: if grow_spanners: insert_offset = self._get_timespan().stop_offset receipt = selection._get_dominant_spanners() for spanner, index in receipt: insert_component = None for component in spanner: start_offset = component._get_timespan().start_offset if start_offset == insert_offset: insert_component = component break if insert_component is not None: insert_index = spanner._index(insert_component) else: insert_index = len(spanner) for component in reversed(components): spanner._insert(insert_index, component) component._spanners.add(spanner) selection = selectiontools.SliceSelection(self) parent, start, stop = \ selection._get_parent_and_start_stop_indices() if parent is not None: if grow_spanners: for component in reversed(components): component._set_parent(parent) parent._music.insert(start + 1, component) else: after = stop + 1 parent.__setitem__(slice(after, after), components) return [self] + components else: if grow_spanners: offset = self._get_timespan().start_offset receipt = selection._get_dominant_spanners() for spanner, x in receipt: for component in spanner: if component._get_timespan().start_offset == offset: index = spanner._index(component) break else: message = 'no component in spanner at offset.' raise ValueError(message) for component in reversed(components): spanner._insert(index, component) component._spanners.add(spanner) selection = selectiontools.SliceSelection(self) parent, start, stop = \ selection._get_parent_and_start_stop_indices() if parent is not None: if grow_spanners: for component in reversed(components): component._set_parent(parent) parent._music.insert(start, component) else: parent.__setitem__(slice(start, start), components) return components + [self]