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 _apply_spanners(self, leaves): spanner_references = { spannertools.Beam: None, spannertools.Slur: None, } first_leaf = leaves[0] for leaf, next_leaf in \ sequencetools.iterate_sequence_pairwise_wrapped(leaves): span_events = self._get_span_events(leaf) for current_class, directions in span_events.iteritems(): starting, stopping = [], [] for direction in directions: if direction is Left: starting.append(Left) else: stopping.append(Right) # apply undirected events immediately, # and do not maintain a reference to them if current_class is spannertools.Tie: if next_leaf is first_leaf: message = 'unterminated {} at {}.' message = message.format(current_class.__name__, leaf) raise Exception(message) previous_tie = [ x for x in leaf._get_spanners() if isinstance(x, spannertools.Tie) ] if previous_tie: previous_tie[0]._append(next_leaf) else: tie = spannertools.Tie() attach(tie, (leaf, next_leaf)) elif current_class is spannertools.Beam: # A beam may begin and end on the same leaf # but only one beam spanner may cover any given leaf, # and starting events are processed before ending ones for _ in starting: if spanner_references[current_class] is not None: message = 'already have beam.' raise Exception(message) else: spanner_references[current_class] = current_class() for _ in stopping: if spanner_references[current_class] is not None: spanner_references[current_class]._append(leaf) spanner_references[current_class] = None elif current_class is spannertools.Slur: # Slurs process stop events before start events, # they must contain more than one leaf, # but they can stop on a leaf and start on the same leaf. for _ in stopping: if spanner_references[current_class] is not None: spanner_references[current_class]._append(leaf) spanner_references[current_class] = None else: message = 'can not end: {}.' message = message.format(current_class.__name) raise Exception(message) for _ in starting: if spanner_references[current_class] is None: spanner_references[current_class] = current_class() else: message = 'already have: {}.' message = message.format(current_class.__name) raise Exception(message) # append leaf to all tracked spanners, for current_class, instance in spanner_references.iteritems(): if instance is not None: instance._append(leaf) # check for unterminated spanners for current_class, instance in spanner_references.iteritems(): if instance is not None: message = 'unterminated {}.' message = message.format(current_class.__name__) raise Exception(message)
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
def _apply_spanners(self, music): # get local reference to methods _get_span_events = self._get_span_events _span_event_name_to_spanner_class = self._span_event_name_to_spanner_class # dictionary of spanner classes and instances all_spanners = {} # traverse all leaves leaves = music.select_leaves(allow_discontiguous_leaves=True) first_leaf = None if leaves: first_leaf = leaves[0] for leaf, next_leaf in sequencetools.iterate_sequence_pairwise_wrapped(leaves): span_events = _get_span_events(leaf) directed_events = {} # sort span events into directed and undirected groups for span_event in span_events: spanner_class = _span_event_name_to_spanner_class(span_event.name) # group directed span events by their Abjad spanner class if hasattr(span_event, "span_direction"): if spanner_class not in directed_events: directed_events[spanner_class] = [span_event] else: directed_events[spanner_class].append(span_event) if spanner_class not in all_spanners: all_spanners[spanner_class] = [] # or apply undirected event immediately (i.e. ties, glisses) elif next_leaf is not first_leaf: # so long as we are not wrapping yet previous_spanners = [x for x in leaf._get_spanners() if isinstance(x, spanner_class)] if previous_spanners: previous_spanners[0].append(next_leaf) else: if hasattr(span_event, "direction") and hasattr(spanner_class, "direction"): spanner = spanner_class(direction=span_event.direction) attach(spanner, [leaf, next_leaf]) else: spanner = spanner_class() attach(spanner, [leaf, next_leaf]) # otherwise throw an error else: raise Exception("Unterminated %s at %s." % (spanner_class.__name__, leaf)) # check for DynamicMarks, and terminate any hairpin dynamics = leaf._get_marks(contexttools.DynamicMark) if dynamics and spannertools.HairpinSpanner in all_spanners and all_spanners[spannertools.HairpinSpanner]: all_spanners[spannertools.HairpinSpanner][0].append(leaf) all_spanners[spannertools.HairpinSpanner].pop() # loop through directed events, handling each as necessary for spanner_class, events in directed_events.iteritems(): starting_events, stopping_events = [], [] for x in events: if x.span_direction == "start": starting_events.append(x) else: stopping_events.append(x) if spanner_class is spannertools.BeamSpanner: # A beam may begin and end on the same leaf # but only one beam spanner may cover any given leaf, # and starting events are processed before ending ones for event in starting_events: if all_spanners[spanner_class]: raise Exception("Already have beam.") if hasattr(event, "direction"): all_spanners[spanner_class].append(spanner_class(direction=event.direction)) else: all_spanners[spanner_class].append(spanner_class()) for _ in stopping_events: if all_spanners[spanner_class]: all_spanners[spanner_class][0].append(leaf) all_spanners[spanner_class].pop() elif spanner_class is spannertools.HairpinSpanner: # Dynamic events can be ended many times, # but only one may start on a given leaf, # and the event must start and end on separate leaves. # If a hairpin already exists and another starts, # the pre-existant spanner is ended. for _ in stopping_events: if all_spanners[spanner_class]: all_spanners[spanner_class][0].append(leaf) all_spanners[spanner_class].pop() if 1 == len(starting_events): if all_spanners[spanner_class]: all_spanners[spanner_class][0].append(leaf) all_spanners[spanner_class].pop() shape = "<" event = starting_events[0] if event.name == "DecrescendoEvent": shape = ">" if hasattr(event, "direction"): all_spanners[spanner_class].append(spanner_class([], shape, direction=event.direction)) else: all_spanners[spanner_class].append(spanner_class([], shape)) elif 1 < len(starting_events): raise Exception("Simultaneous dynamic-span events.") elif spanner_class in [ spannertools.SlurSpanner, spannertools.PhrasingSlurSpanner, spannertools.TextSpanner, spannertools.TrillSpanner, ]: # These engravers process stop events before start events, # they must contain more than one leaf, # however, they can stop on a leaf and start on the same leaf. for _ in stopping_events: if all_spanners[spanner_class]: all_spanners[spanner_class][0].append(leaf) all_spanners[spanner_class].pop() else: raise Exception("Cannot end %s." % spanner_class.__name__) for event in starting_events: if not all_spanners[spanner_class]: if hasattr(event, "direction") and hasattr(spanner_class, "direction"): all_spanners[spanner_class].append(spanner_class(direction=event.direction)) else: all_spanners[spanner_class].append(spanner_class()) else: raise Exception("Already have %s." % spanner_class.__name__) elif spanner_class is spannertools.HorizontalBracketSpanner: # Brackets can nest, meaning # multiple brackets can begin or end on a leaf # but cannot both begin and end on the same leaf # and therefore a bracket cannot cover a single leaf has_starting_events = bool(len(starting_events)) for _ in starting_events: all_spanners[spanner_class].append(spanner_class()) if stopping_events: if not has_starting_events: for _ in stopping_events: if all_spanners[spanner_class]: all_spanners[spanner_class][-1].append(leaf) all_spanners[spanner_class].pop() else: raise Exception("Do not have that many brackets.") else: raise Exception("Conflicting note group events.") # append leaf to all tracked spanners, for spanner_class, instances in all_spanners.iteritems(): for instance in instances: instance.append(leaf) # check for unterminated spanners for spanner_class, instances in all_spanners.iteritems(): if instances: raise Exception("Unterminated %s." % spanner_class.__name__)