def _notate(self, grace_handler, attack_point_optimizer, attach_tempo_marks): voice = scoretools.Voice() # generate the first beat = self.items[0] components = beat.q_grid(beat.beatspan) if attach_tempo_marks: attachment_target = components[0] if isinstance(attachment_target, containertools.Container): attachment_target = attachment_target.select_leaves()[0] tempo = copy.copy(beat.tempo) attach(tempo, attachment_target) voice.extend(components) # generate the rest pairwise, comparing tempi for beat_one, beat_two in \ sequencetools.iterate_sequence_pairwise_strict(self.items): components = beat_two.q_grid(beat_two.beatspan) if (beat_two.tempo != beat_one.tempo) and attach_tempo_marks: attachment_target = components[0] if isinstance(attachment_target, containertools.Container): attachment_target = attachment_target.select_leaves()[0] tempo = copy.copy(beat_two.tempo) attach(tempo, attachment_target) voice.extend(components) # apply tie chains, pitches, grace containers self._notate_leaves_pairwise(voice, grace_handler) # partition tie chains in voice attack_point_optimizer(voice) return voice
def _shift_downbeat_q_events_to_next_q_grid(self): beats = self.beats for one, two in sequencetools.iterate_sequence_pairwise_strict(beats): one_q_events = one.q_grid.next_downbeat.q_event_proxies two_q_events = two.q_grid.leaves[0].q_event_proxies while one_q_events: two_q_events.append(one_q_events.pop())
def _find_divisible_leaf_indices_and_subdivisions(self, q_grid): # TODO: This should actually check for all QEvents which fall # within the leaf's duration, # including QEvents attached to the next leaf # It may be prudent to actually store QEvents in two lists: # before_offset and after_offset indices, subdivisions = [], [] leaves = q_grid.leaves i = 0 for leaf_one, leaf_two in \ sequencetools.iterate_sequence_pairwise_strict(leaves): if (leaf_one.succeeding_q_event_proxies or leaf_two.preceding_q_event_proxies) and \ leaf_one.is_divisible: if len(leaf_one.q_event_proxies) == 1 and \ leaf_one.q_event_proxies[0].offset == \ leaf_one.start_offset: pass # perfect match, don't bother to continue subdivision else: parentage_ratios = leaf_one.parentage_ratios leaf_subdivisions = \ self._find_leaf_subdivisions(parentage_ratios) if leaf_subdivisions: indices.append(i) subdivisions.append(tuple(leaf_subdivisions)) i += 1 return indices, subdivisions
def has_voice_crossing(self): for upper, lower in \ sequencetools.iterate_sequence_pairwise_strict(self.cells): for lower_pitch in lower.pitches: for upper_pitch in upper.pitches: if upper_pitch.numbered_pitch < \ lower_pitch.numbered_pitch: return True return False
def list_nonspanning_subarrays(self): r'''Lists nonspanning subarrays of pitch array. :: >>> array = pitcharraytools.PitchArray([ ... [2, 2, 3, 1], ... [1, 2, 1, 1, 2, 1], ... [1, 1, 1, 1, 1, 1, 1, 1]]) >>> print array [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] :: >>> subarrays = array.list_nonspanning_subarrays() >>> len(subarrays) 3 :: >>> print subarrays[0] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] :: >>> print subarrays[1] [ ] [ ] [ ] [ ] [ ] [ ] :: >>> print subarrays[2] [ ] [ ] [ ] Returns list. ''' unspanned_indices = [] for i in range(self.width + 1): if not self.has_spanning_cell_over_index(i): unspanned_indices.append(i) array_depth = self.depth subarrays = [] for start_column, stop_column in \ sequencetools.iterate_sequence_pairwise_strict(unspanned_indices): upper_left_pair = (0, start_column) lower_right_pair = (array_depth, stop_column) subarray = self.copy_subarray(upper_left_pair, lower_right_pair) subarrays.append(subarray) return subarrays
def graph_order(self): r'''Graph order of tree node. Returns tuple. ''' from abjad.tools import sequencetools order = [] for parent, child in sequencetools.iterate_sequence_pairwise_strict( reversed(self.improper_parentage)): order.append(parent.index(child)) return tuple(order)
def prolations(self): r'''Prolations of rhythm tree node. Returns tuple. ''' prolations = [durationtools.Multiplier(1)] improper_parentage = self.improper_parentage for child, parent in \ sequencetools.iterate_sequence_pairwise_strict(improper_parentage): prolations.append(durationtools.Multiplier( parent.preprolated_duration, parent._contents_duration)) return tuple(prolations)
def get_offsets_at_depth(depth): if depth < len(offset_inventory): return offset_inventory[depth] while len(offset_inventory) <= depth: new_offsets = [] old_offsets = offset_inventory[-1] for first, second in \ sequencetools.iterate_sequence_pairwise_strict(old_offsets): new_offsets.append(first) new_offsets.append((first + second) / 2) new_offsets.append(old_offsets[-1]) offset_inventory.append(tuple(new_offsets)) return offset_inventory[depth]
def has_voice_crossing(self): r'''Is true when pitch array column has voice crossing. Otherwise false. Returns boolean. ''' for upper, lower in \ sequencetools.iterate_sequence_pairwise_strict(self.cells): for lower_pitch in lower.pitches: for upper_pitch in upper.pitches: if upper_pitch.numbered_pitch < \ lower_pitch.numbered_pitch: return True return False
def _get_intervals_in_subrun(subrun_source): from abjad.tools import pitchtools subrun_source = list(subrun_source) result = [0] for first, second in sequencetools.iterate_sequence_pairwise_strict( subrun_source): first_pitch = pitchtools.get_named_pitch_from_pitch_carrier(first) second_pitch = pitchtools.get_named_pitch_from_pitch_carrier(second) interval = abs(pitchtools.NumberedPitch(second_pitch)) - \ abs(pitchtools.NumberedPitch(first_pitch)) result.append(interval + result[-1]) result.pop(0) return result
def cumulative_sums_pairwise(sequence): r'''Lists pairwise cumulative sums of `sequence` from ``0``. :: >>> mathtools.cumulative_sums_pairwise([1, 2, 3, 4, 5, 6]) [(0, 1), (1, 3), (3, 6), (6, 10), (10, 15), (15, 21)] Returns list of pairs. ''' from abjad.tools import mathtools from abjad.tools import sequencetools return list(sequencetools.iterate_sequence_pairwise_strict(mathtools.cumulative_sums(sequence)))
def _interleave_source_with_code_blocks( self, tmp_directory, lines, code_blocks, output_format, ): #print 'INTERLEAVE SOURCE WITH CODE BLOCKS' image_file_names = [x for x in os.listdir(tmp_directory) if (x.endswith(output_format.image_format) and x.startswith(self.image_prefix))] image_dict = {} for image_file_name in image_file_names: suffix = os.path.splitext(image_file_name.partition('-')[2])[0] index, part, page = suffix.partition('-') index = int(index) if page: page = int(page.strip('page')) else: page = 0 if index not in image_dict: image_dict[index] = {} image_dict[index][page] = image_file_name interleaved = [] interleaved.append( '\n'.join(lines[:code_blocks[0].starting_line_number])) for pair in sequencetools.iterate_sequence_pairwise_strict( code_blocks): first_block, second_block = pair interleaved.extend(output_format(first_block, image_dict)) interleaved.append('\n'.join(lines[ first_block.ending_line_number+1: second_block.starting_line_number])) interleaved.extend(output_format(code_blocks[-1], image_dict)) interleaved.append('\n'.join( lines[code_blocks[-1].ending_line_number+1:])) return '\n'.join(interleaved)
def _notate( self, grace_handler, attack_point_optimizer, attach_tempos, ): voice = scoretools.Voice() # generate the first q_target_measure = self.items[0] measure = scoretools.Measure(q_target_measure.time_signature) for beat in q_target_measure.beats: measure.extend(beat.q_grid(beat.beatspan)) if attach_tempos: tempo = copy.copy(q_target_measure.tempo) attach(tempo, measure) voice.append(measure) # generate the rest pairwise, comparing tempi for q_target_measure_one, q_target_measure_two in \ sequencetools.iterate_sequence_pairwise_strict(self.items): measure = scoretools.Measure(q_target_measure_two.time_signature) for beat in q_target_measure_two.beats: measure.extend(beat.q_grid(beat.beatspan)) if (q_target_measure_two.tempo != q_target_measure_one.tempo) \ and attach_tempos: tempo = copy.copy(q_target_measure_two.tempo) attach(tempo, measure) voice.append(measure) # apply logical ties, pitches, grace containers self._notate_leaves_pairwise(voice, grace_handler) # partition logical ties in each measure for measure in voice: attack_point_optimizer(measure) return voice
def iterate_leaf_pairs_in_expr(expr): r'''Iterate leaf pairs forward in `expr`: :: >>> score = Score([]) >>> notes = [Note("c'8"), Note("d'8"), Note("e'8"), Note("f'8"), Note("g'4")] >>> score.append(Staff(notes)) >>> notes = [Note(x, (1, 4)) for x in [-12, -15, -17]] >>> score.append(Staff(notes)) >>> clef = contexttools.ClefMark('bass') >>> attach(clef, score[1]) ClefMark('bass')(Staff{3}) >>> show(score) # doctest: +SKIP .. doctest:: >>> f(score) \new Score << \new Staff { c'8 d'8 e'8 f'8 g'4 } \new Staff { \clef "bass" c4 a,4 g,4 } >> :: >>> for pair in iterationtools.iterate_leaf_pairs_in_expr(score): ... pair (Note("c'8"), Note('c4')) (Note("c'8"), Note("d'8")) (Note('c4'), Note("d'8")) (Note("d'8"), Note("e'8")) (Note("d'8"), Note('a,4')) (Note('c4'), Note("e'8")) (Note('c4'), Note('a,4')) (Note("e'8"), Note('a,4')) (Note("e'8"), Note("f'8")) (Note('a,4'), Note("f'8")) (Note("f'8"), Note("g'4")) (Note("f'8"), Note('g,4')) (Note('a,4'), Note("g'4")) (Note('a,4'), Note('g,4')) (Note("g'4"), Note('g,4')) Iterate leaf pairs left-to-right and top-to-bottom. Returns generator. ''' from abjad.tools import iterationtools vertical_moments = iterationtools.iterate_vertical_moments_in_expr(expr) for moment_1, moment_2 in \ sequencetools.iterate_sequence_pairwise_strict(vertical_moments): for pair in sequencetools.yield_all_unordered_pairs_of_sequence( moment_1.start_leaves): yield pair pairs = sequencetools.yield_all_pairs_between_sequences( moment_1.leaves, moment_2.start_leaves) for pair in pairs: yield pair else: for pair in sequencetools.yield_all_unordered_pairs_of_sequence( moment_2.start_leaves): yield pair
def generate_offset_kernel_to_denominator( self, denominator, normalize=True, ): r'''Generate a dictionary of all offsets in a meter up to `denominator`, where the keys are the offsets and the values are the normalized weights of those offsets: :: >>> meter = \ ... metertools.Meter((4, 4)) >>> kernel = \ ... meter.generate_offset_kernel_to_denominator(8) >>> for offset, weight in sorted(kernel.kernel.iteritems()): ... print '{!s}\t{!s}'.format(offset, weight) ... 0 3/16 1/8 1/16 1/4 1/8 3/8 1/16 1/2 1/8 5/8 1/16 3/4 1/8 7/8 1/16 1 3/16 This is useful for testing how strongly a collection of offsets responds to a given meter. Returns dictionary. ''' from abjad.tools import metertools assert mathtools.is_positive_integer_power_of_two( denominator / self.denominator) inventory = list(self.depthwise_offset_inventory) old_flag_count = durationtools.Duration(1, self.denominator).flag_count new_flag_count = durationtools.Duration(1, denominator).flag_count extra_depth = new_flag_count - old_flag_count for _ in range(extra_depth): old_offsets = inventory[-1] new_offsets = [] for first, second in \ sequencetools.iterate_sequence_pairwise_strict(old_offsets): new_offsets.append(first) new_offsets.append((first + second) / 2) new_offsets.append(old_offsets[-1]) inventory.append(tuple(new_offsets)) total = 0 kernel = {} for offsets in inventory: for offset in offsets: if offset not in kernel: kernel[offset] = 0 kernel[offset] += 1 total += 1 if normalize: for offset, response in kernel.iteritems(): kernel[offset] = durationtools.Multiplier(response, total) return metertools.MetricAccentKernel(kernel)
def list_numbered_interval_numbers_pairwise(pitch_carriers, wrap=False): r'''List numbered interval numbers pairwise between `pitch_carriers`: :: >>> staff = Staff("c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8") .. doctest:: >>> print format(staff) \new Staff { c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8 } :: >>> pitchtools.list_numbered_interval_numbers_pairwise( ... staff) [2, 2, 1, 2, 2, 2, 1] :: >>> pitchtools.list_numbered_interval_numbers_pairwise( ... staff, wrap=True) [2, 2, 1, 2, 2, 2, 1, -12] :: >>> notes = [ ... Note("c'8"), Note("d'8"), Note("e'8"), Note("f'8"), ... Note("g'8"), Note("a'8"), Note("b'8"), Note("c''8")] :: >>> notes.reverse() :: >>> pitchtools.list_numbered_interval_numbers_pairwise( ... notes) [-1, -2, -2, -2, -1, -2, -2] :: >>> pitchtools.list_numbered_interval_numbers_pairwise( ... notes, wrap=True) [-1, -2, -2, -2, -1, -2, -2, 12] When ``wrap = False`` do not return ``pitch_carriers[-1] - pitch_carriers[0]`` as last in series. When ``wrap = True`` do return ``pitch_carriers[-1] - pitch_carriers[0]`` as last in series. Returns list. ''' from abjad.tools import pitchtools result = [] if len(pitch_carriers) == 0: return result elif len(pitch_carriers) == 1: if pitchtools.Pitch.is_pitch_carrier(pitch_carriers[0]): return result else: message = 'must be pitch, not, note-head or chord.' raise TypeError(message) if wrap: pairs = sequencetools.iterate_sequence_pairwise_wrapped(pitch_carriers) else: pairs = sequencetools.iterate_sequence_pairwise_strict(pitch_carriers) for first_carrier, second_carrier in pairs: first_pitch = pitchtools.get_named_pitch_from_pitch_carrier(first_carrier) second_pitch = pitchtools.get_named_pitch_from_pitch_carrier(second_carrier) signed_interval = abs(pitchtools.NumberedPitch(second_pitch)) - \ abs(pitchtools.NumberedPitch(first_pitch)) result.append(signed_interval) return result
def graph_order(self): order = [] for parent, child in sequencetools.iterate_sequence_pairwise_strict( reversed(self.improper_parentage)): order.append(parent.index(child)) return tuple(order)
def compute_depth(self, bounding_interval=None): r"""Compute a tree whose intervals represent the level of overlap of the time interval aggregate: :: >>> a = timeintervaltools.TimeInterval(0, 3) >>> b = timeintervaltools.TimeInterval(6, 12) >>> c = timeintervaltools.TimeInterval(9, 15) >>> tree = timeintervaltools.TimeIntervalTree([a, b, c]) >>> tree.compute_depth() TimeIntervalTree([ TimeInterval(Offset(0, 1), Offset(3, 1), {'depth': 1}), TimeInterval(Offset(3, 1), Offset(6, 1), {'depth': 0}), TimeInterval(Offset(6, 1), Offset(9, 1), {'depth': 1}), TimeInterval(Offset(9, 1), Offset(12, 1), {'depth': 2}), TimeInterval(Offset(12, 1), Offset(15, 1), {'depth': 1}) ]) If `bounding_interval` is not none, only consider the depth of time intervals which intersect that time interval: :: >>> a = timeintervaltools.TimeInterval(0, 3) >>> b = timeintervaltools.TimeInterval(6, 12) >>> c = timeintervaltools.TimeInterval(9, 15) >>> tree = timeintervaltools.TimeIntervalTree([a, b, c]) >>> d = timeintervaltools.TimeInterval(-1, 16) >>> tree.compute_depth(bounding_interval=d) TimeIntervalTree([ TimeInterval(Offset(-1, 1), Offset(0, 1), {'depth': 0}), TimeInterval(Offset(0, 1), Offset(3, 1), {'depth': 1}), TimeInterval(Offset(3, 1), Offset(6, 1), {'depth': 0}), TimeInterval(Offset(6, 1), Offset(9, 1), {'depth': 1}), TimeInterval(Offset(9, 1), Offset(12, 1), {'depth': 2}), TimeInterval(Offset(12, 1), Offset(15, 1), {'depth': 1}), TimeInterval(Offset(15, 1), Offset(16, 1), {'depth': 0}) ]) Returns interval tree. """ from abjad.tools import sequencetools from abjad.tools import timeintervaltools if bounding_interval is not None: bounded_tree = self.find_intervals_intersecting_or_tangent_to_interval(bounding_interval) if not bounded_tree: return timeintervaltools.TimeIntervalTree( [ timeintervaltools.TimeInterval( bounding_interval.start_offset, bounding_interval.stop_offset, {"depth": 0} ) ] ) all_bounds = list(bounded_tree.all_unique_bounds) while all_bounds[0] < bounding_interval.start_offset: all_bounds.pop(0) while bounding_interval.stop_offset < all_bounds[-1]: all_bounds.pop() if bounding_interval.start_offset < all_bounds[0]: all_bounds.insert(0, bounding_interval.start_offset) if all_bounds[-1] < bounding_interval.stop_offset: all_bounds.append(bounding_interval.stop_offset) else: all_bounds = list(self.all_unique_bounds) depth_intervals = [] for start_offset, stop_offset in sequencetools.iterate_sequence_pairwise_strict(all_bounds): current_interval = timeintervaltools.TimeInterval(start_offset, stop_offset, {}) found = self.find_intervals_intersecting_or_tangent_to_interval(current_interval) depth = 0 if found: depth = len( [ x for x in found if ( not x.start_offset == current_interval.stop_offset and not x.stop_offset == current_interval.start_offset ) ] ) current_interval["depth"] = depth depth_intervals.append(current_interval) return timeintervaltools.TimeIntervalTree(depth_intervals)
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 list_numbered_inversion_equivalent_interval_classes_pairwise(pitch_carriers, wrap=False): r'''List numbered inversion-equivalent interval-classes pairwise between `pitch_carriers`: :: >>> staff = Staff("c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8") .. doctest:: >>> f(staff) \new Staff { c'8 d'8 e'8 f'8 g'8 a'8 b'8 c''8 } :: >>> result = pitchtools.list_numbered_inversion_equivalent_interval_classes_pairwise( ... staff, wrap=False) :: >>> for x in result: x ... NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) :: >>> result = pitchtools.list_numbered_inversion_equivalent_interval_classes_pairwise( ... staff, wrap=True) :: >>> for x in result: x NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(0) :: >>> notes = staff.select_leaves() >>> notes = list(reversed(notes)) :: >>> result = pitchtools.list_numbered_inversion_equivalent_interval_classes_pairwise( ... notes, wrap=False) :: >>> for x in result: x ... NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) :: >>> result = pitchtools.list_numbered_inversion_equivalent_interval_classes_pairwise( ... notes, wrap=True) :: >>> for x in result: x ... NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(1) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(2) NumberedInversionEquivalentIntervalClass(0) When ``wrap=False`` do not return ``pitch_carriers[-1] - pitch_carriers[0]`` as last in series. When ``wrap=True`` do return ``pitch_carriers[-1] - pitch_carriers[0]`` as last in series. Returns list. ''' from abjad.tools import pitchtools result = [] if len(pitch_carriers) == 0: return result elif len(pitch_carriers) == 1: if pitchtools.Pitch.is_pitch_carrier(pitch_carriers[0]): return result else: raise TypeError('must be Abjad pitch, note, note head or chord.') if wrap: pairs = sequencetools.iterate_sequence_pairwise_wrapped(pitch_carriers) else: pairs = sequencetools.iterate_sequence_pairwise_strict(pitch_carriers) for first_carrier, second_carrier in pairs: first_pitch = pitchtools.get_named_pitch_from_pitch_carrier(first_carrier) second_pitch = pitchtools.get_named_pitch_from_pitch_carrier(second_carrier) mdi = second_pitch - first_pitch iecic = pitchtools.NumberedInversionEquivalentIntervalClass(mdi) result.append(iecic) return result