def repeat_to_duration(self, duration): r'''Repeat start-positioned division payload expression to `duration`. :: >>> payload = [(6, 8), (6, 8), (3, 4)] >>> expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( ... payload, Offset(0)) :: >>> result = expression.repeat_to_duration(Duration(13, 4)) :: >>> print result.storage_format musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=musicexpressiontools.DivisionList( [Division('[6, 8]', start_offset=Offset(0, 1)), Division('[6, 8]', start_offset=Offset(3, 4)), Division('[3, 4]', start_offset=Offset(3, 2)), Division('[6, 8]', start_offset=Offset(9, 4)), Division('[2, 8]', start_offset=Offset(3, 1))], start_offset=durationtools.Offset(0, 1) ), start_offset=durationtools.Offset(0, 1) ) Returns newly constructed start-positioned division payload expression. ''' divisions = sequencetools.repeat_sequence_to_weight_exactly(self.payload, duration) result = type(self)(payload=divisions, voice_name=self.voice_name, start_offset=self.start_offset) return result
def repeat_to_duration(self, duration): r'''Repeat payload expression to duration. :: >>> result = \ ... payload_expression.repeat_to_duration(Duration(13, 16)) :: >>> print result.storage_format musicexpressiontools.IterablePayloadExpression( payload=(NonreducedFraction(4, 16), NonreducedFraction(2, 16), NonreducedFraction(4, 16), NonreducedFraction(2, 16), NonreducedFraction(1, 16)) ) Returns newly constructed payload expression. ''' if not sequencetools.all_are_numbers(self.payload): payload = [mathtools.NonreducedFraction(x) for x in self.payload] else: payload = self.payload payload = sequencetools.repeat_sequence_to_weight_exactly( payload, duration) result = self.new(payload=payload) return result
def test_sequencetools_repeat_sequence_to_weight_exactly_02(): r'''Works with nonreduced fractions. ''' sequence = [NonreducedFraction(3, 16)] sequence = sequencetools.repeat_sequence_to_weight_exactly(sequence, NonreducedFraction(5, 4)) assert sum(sequence) == NonreducedFraction(5, 4) assert [x.pair for x in sequence] == [(3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (3, 16), (2, 16)]
def evaluate(self): r'''Evaluate division region expression. Returns start-positioned division payload expression. ''' from experimental.tools import musicexpressiontools divisions = self.source_expression[:] divisions = [musicexpressiontools.Division(x) for x in divisions] divisions = sequencetools.repeat_sequence_to_weight_exactly( divisions, self.total_duration) expression = musicexpressiontools.StartPositionedDivisionPayloadExpression( payload=divisions, start_offset=self.start_offset, voice_name=self.voice_name, ) return expression
def _split( self, durations, cyclic=False, fracture_spanners=False, tie_split_notes=True, ): from abjad.tools import iterationtools from abjad.tools import pitchtools from abjad.tools import selectiontools from abjad.tools import spannertools durations = [durationtools.Duration(x) for x in durations] if cyclic: durations = sequencetools.repeat_sequence_to_weight_exactly( 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_to_weight( durations, self._get_duration()) result = [] leaf_prolation = self._get_parentage(include_self=False).prolation leaf_copy = copy.copy(self) for duration in durations: new_leaf = copy.copy(self) preprolated_duration = duration / leaf_prolation shard = new_leaf._set_duration(preprolated_duration) shard = [x._get_parentage().root for x in shard] result.append(shard) flattened_result = sequencetools.flatten_sequence(result) flattened_result = selectiontools.SliceSelection(flattened_result) spanner_classes = (spannertools.TieSpanner,) parentage = self._get_parentage() if parentage._get_spanners(spanner_classes=spanner_classes): selection = selectiontools.select(flattened_result) for component in selection: for mark in component._get_spanners( spanner_classes=spanner_classes): mark.detach() # 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') for mark in middle_leaf._get_marks(): mark.detach() # adjust last leaf last_leaf = flattened_result[-1] last_leaf._detach_grace_containers(kind='grace') for mark in last_leaf._get_marks(): mark.detach() # tie split notes, rests and chords as specified if pitchtools.Pitch.is_pitch_carrier(self) and tie_split_notes: flattened_result_leaves = iterationtools.iterate_leaves_in_expr( flattened_result) # TODO: implement SliceSelection._attach_tie_spanner_to_leaves() for leaf_pair in sequencetools.iterate_sequence_pairwise_strict( flattened_result_leaves): selection = selectiontools.ContiguousSelection(leaf_pair) selection._attach_tie_spanner_to_leaf_pair() # return result return result
def split( self, durations, fracture_spanners=False, cyclic=False, tie_split_notes=True, ): r'''Splits component or selection by `durations`. .. container:: example **Example 1.** Split leaves: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 e'16 d'8 f'32 f'16. c'8 e'8 d'8 f'8 \f } .. container:: example **Example 2.** Split leaves and fracture crossing spanners: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 \f e'16 \< \p d'8 f'32 \f f'16. \< \p c'8 e'8 d'8 f'8 \f } .. container:: example **Example 3.** Split leaves cyclically: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... cyclic=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 e'16 d'8 f'32 f'16. c'16. c'32 e'8 d'16 d'16 f'8 \f } .. container:: example **Example 4.** Split leaves cyclically and fracture spanners: :: >>> staff = Staff("c'8 e' d' f' c' e' d' f'") >>> leaves = staff.select_leaves() >>> hairpin = spannertools.HairpinSpanner(descriptor='p < f') >>> attach(hairpin, leaves) >>> staff.override.dynamic_line_spanner.staff_padding = 3 >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'8 d'8 f'8 c'8 e'8 d'8 f'8 \f } :: >>> durations = [Duration(3, 16), Duration(7, 32)] >>> result = mutate(leaves).split( ... durations, ... cyclic=True, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff \with { \override DynamicLineSpanner #'staff-padding = #3 } { c'8 \< \p e'16 \f e'16 \< \p d'8 f'32 \f f'16. \< \p c'16. \f c'32 \< \p e'8 d'16 \f d'16 \< \p f'8 \f } .. container:: example **Example 5.** Split tupletted leaves and fracture crossing spanners: :: >>> staff = Staff() >>> staff.append(Tuplet((2, 3), "c'4 d' e'")) >>> staff.append(Tuplet((2, 3), "c'4 d' e'")) >>> leaves = staff.select_leaves() >>> slur = spannertools.SlurSpanner() >>> attach(slur, leaves) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { c'4 ( d'4 e'4 } \times 2/3 { c'4 d'4 e'4 ) } } :: >>> durations = [Duration(1, 4)] >>> result = mutate(leaves).split( ... durations, ... fracture_spanners=True, ... tie_split_notes=False, ... ) >>> show(staff) # doctest: +SKIP .. doctest:: >>> f(staff) \new Staff { \times 2/3 { c'4 ( d'8 ) d'8 ( e'4 } \times 2/3 { c'4 d'4 e'4 ) } } Returns list of selections. ''' from abjad.tools import scoretools from abjad.tools import leaftools from abjad.tools import selectiontools # check input components = self._client single_component_input = False if isinstance(components, scoretools.Component): single_component_input = True components = selectiontools.Selection(components) assert all( isinstance(x, scoretools.Component) for x in components) if not isinstance(components, selectiontools.Selection): components = selectiontools.Selection(components) durations = [durationtools.Duration(x) for x in durations] # return if no split to be done if not durations: if single_component_input: return components else: return [], components # calculate total component duration total_component_duration = components.get_duration() total_split_duration = sum(durations) # calculate durations if cyclic: durations = sequencetools.repeat_sequence_to_weight_exactly( durations, total_component_duration) elif total_split_duration < total_component_duration: final_offset = total_component_duration - sum(durations) durations.append(final_offset) elif total_component_duration < total_split_duration: durations = sequencetools.truncate_sequence_to_weight( durations, total_component_duration) # keep copy of durations to partition result components durations_copy = durations[:] # calculate total split duration total_split_duration = sum(durations) assert total_split_duration == total_component_duration # initialize loop variables result, shard = [], [] offset_index, offset_count = 0, len(durations) current_shard_duration = durationtools.Duration(0) remaining_components = list(components[:]) advance_to_next_offset = True # loop and build shards by grabbing next component # and next duration each time through loop while True: # grab next split point if advance_to_next_offset: if durations: next_split_point = durations.pop(0) else: break advance_to_next_offset = True # grab next component from input stack of components if remaining_components: current_component = remaining_components.pop(0) else: break # find where current component endpoint will position us candidate_shard_duration = current_shard_duration + \ current_component._get_duration() # if current component would fill current shard exactly if candidate_shard_duration == next_split_point: shard.append(current_component) result.append(shard) shard = [] current_shard_duration = durationtools.Duration(0) offset_index += 1 # if current component would exceed current shard elif next_split_point < candidate_shard_duration: local_split_duration = \ next_split_point - current_shard_duration if isinstance(current_component, leaftools.Leaf): leaf_split_durations = [local_split_duration] current_duration = current_component._get_duration() additional_required_duration = \ current_duration - local_split_duration split_durations = sequencetools.split_sequence_by_weights( durations, [additional_required_duration], cyclic=False, overhang=True, ) additional_durations = split_durations[0] leaf_split_durations.extend(additional_durations) durations = split_durations[-1] leaf_shards = current_component._split( leaf_split_durations, cyclic=False, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, ) shard.extend(leaf_shards) result.append(shard) offset_index += len(additional_durations) else: left_list, right_list = \ current_component._split_by_duration( local_split_duration, fracture_spanners=fracture_spanners, tie_split_notes=tie_split_notes, ) shard.extend(left_list) result.append(shard) remaining_components.__setitem__(slice(0, 0), right_list) shard = [] offset_index += 1 current_shard_duration = durationtools.Duration(0) # if current component would not fill current shard elif candidate_shard_duration < next_split_point: shard.append(current_component) current_shard_duration += current_component._get_duration() advance_to_next_offset = False else: raise ValueError # append any stub shard if len(shard): result.append(shard) # append any unexamined components if len(remaining_components): result.append(remaining_components) # partition split components according to input durations result = sequencetools.flatten_sequence(result) result = selectiontools.ContiguousSelection(result) result = result.partition_by_durations_exactly(durations_copy) # return list of shards result = [selectiontools.Selection(x) for x in result] return result
def partition_sequence_by_counts(sequence, counts, cyclic=False, overhang=False, copy_elements=False): r"""Partition sequence by counts. .. container:: example **Example 1a.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(10), ... [3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2]] .. container:: example **Example 1b.** Partition sequence once by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(16), ... [4, 3], ... cyclic=False, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6]] .. container:: example **Example 2a.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(10), ... [3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8]] .. container:: example **Example 2b.** Partition sequence cyclically by counts without overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(16), ... [4, 3], ... cyclic=True, ... overhang=False, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13]] .. container:: example **Example 3a.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(10), ... [3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9]] .. container:: example **Example 3b.** Partition sequence once by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(16), ... [4, 3], ... cyclic=False, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15]] .. container:: example **Example 4a.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(10), ... [3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] .. container:: example **Example 4b.** Partition sequence cyclically by counts with overhang: :: >>> sequencetools.partition_sequence_by_counts( ... range(16), ... [4, 3], ... cyclic=True, ... overhang=True, ... ) [[0, 1, 2, 3], [4, 5, 6], [7, 8, 9, 10], [11, 12, 13], [14, 15]] Returns list of sequence objects. """ from abjad.tools import sequencetools result = [] if cyclic: if overhang: counts = sequencetools.repeat_sequence_to_weight_exactly(counts, len(sequence)) else: counts = sequencetools.repeat_sequence_to_weight_at_most(counts, len(sequence)) elif overhang: weight_counts = mathtools.weight(counts) len_sequence = len(sequence) if weight_counts < len_sequence: counts = list(counts) counts.append(len(sequence) - weight_counts) for start, stop in mathtools.cumulative_sums_pairwise(counts): result.append(type(sequence)(sequence[start:stop])) return result
def test_sequencetools_repeat_sequence_to_weight_exactly_01(): assert sequencetools.repeat_sequence_to_weight_exactly((5, -5, -5), 23) == (5, -5, -5, 5, -3)