def test_init(input_, semitones, name): class_ = abjad.NamedInterval if isinstance(semitones, type) and issubclass(semitones, Exception): with pytest.raises(semitones): class_(input_) return instance = class_(input_) assert float(instance) == semitones assert instance.name == name abjad.NamedInterval(instance) abjad.NumberedInterval(instance) abjad.NamedIntervalClass(instance) abjad.NumberedIntervalClass(instance) if isinstance(input_, str): group_dict = abjad.pitch._lib._interval_name_abbreviation_regex.match( input_).groupdict() inflected_up = class_("{}{}{}{}".format( group_dict["direction"] or "", group_dict["quality"], "+", group_dict["number"], )) inflected_down = class_("{}{}{}{}".format( group_dict["direction"] or "", group_dict["quality"], "~", group_dict["number"], )) if (math.sign(float(instance)) == instance.direction_number) and abs(instance.number) != 1: direction = math.sign(float(instance)) assert float(inflected_up) == (abs(float(instance)) + 0.5) * direction assert float(inflected_down) == (abs(float(instance)) - 0.5) * direction
def test_init(input_, semitones, name): class_ = abjad.NamedIntervalClass if isinstance(semitones, type) and issubclass(semitones, Exception): with pytest.raises(semitones): class_(input_) return instance = class_(input_) assert float(instance) == semitones assert instance.name == name abjad.NamedInterval(instance) abjad.NamedIntervalClass(instance) abjad.NumberedInterval(instance) abjad.NumberedIntervalClass(instance)
def _from_named_parts(self, direction, quality, diatonic_number): import abjad octaves = 0 diatonic_pc_number = abs(diatonic_number) while diatonic_pc_number > 7: octaves += 1 diatonic_pc_number -= 7 if diatonic_pc_number == 1 and quality == "P" and diatonic_number >= 8: octaves -= 1 diatonic_pc_number = 8 self._octaves = octaves if direction: diatonic_pc_number *= direction self._interval_class = abjad.NamedIntervalClass( (quality, diatonic_pc_number))
def respell_augmented_unisons( selection: abjad.Selection, *, include_multiples: bool = False, respell_by_pitch_class: bool = False, ) -> None: r"""Mutates an input |abjad.Selection| in place and has no return value; this function changes the accidentals of individual pitches of all chords in a container in order to avoid augmented unisons. Basic usage: To use this function, apply it to a selection that contains chords that have augmented unisons. >>> container = abjad.Container(r"c'4 r4 <ef' e'>4 g'4 <c' cs'>4 r2.") >>> abjad.show(container) .. docs:: { c'4 r4 <ef' e'>4 g'4 <c' cs'>4 r2. } .. figure:: ../_images/respell_augmented_unisons-OXnGvQzGT2.png >>> auxjad.mutate.respell_augmented_unisons(container[:]) >>> abjad.show(container) .. docs:: { c'4 r4 <ds' e'>4 g'4 <c' df'>4 r2. } .. figure:: ../_images/respell_augmented_unisons-x33afbbamt.png This can be useful when using tuples of integers to create chords that contain minor seconds: >>> pitches = [(0, 1), (8, 9, 12), (0, 4, 5, 6), (-1, 5, 6)] >>> durations = [(1, 8), (3, 8), (7, 16), (1, 16)] >>> maker = abjad.LeafMaker() >>> chords = maker(pitches, durations) >>> staff = abjad.Staff(chords) >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic') >>> abjad.attach(literal, staff) >>> abjad.show(staff) .. docs:: \new Staff { \accidentalStyle dodecaphonic <c' cs'>8 <af' a' c''>4. <c' e' f' fs'>4.. <b f' fs'>16 } .. figure:: ../_images/respell_augmented_unisons-mWVWwV9uBx.png >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.show(staff) .. docs:: \new Staff { \accidentalStyle dodecaphonic <c' df'>8 <gs' a' c''>4. <c' e' f' gf'>4.. <b f' gf'>16 } .. figure:: ../_images/respell_augmented_unisons-IfzaseW4oS.png .. note:: Auxjad automatically adds this function as an extension function to |abjad.mutate|. It can thus be used from either |auxjad.mutate|_ or |abjad.mutate| namespaces. Therefore, the two lines below are equivalent: >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.mutate.respell_augmented_unisons(staff[:]) 2-note chords: The example below shows first the default spelling of 2-note chords in Abjad followed by the respelt 2-note chords. >>> staff = abjad.Staff() >>> for pitch in range(12): ... staff.append(abjad.Chord([pitch, pitch + 1], (1, 16))) >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic') >>> abjad.attach(literal, staff) >>> abjad.show(staff) .. docs:: \new Staff { \accidentalStyle dodecaphonic <c' cs'>16 <cs' d'>16 <d' ef'>16 <ef' e'>16 <e' f'>16 <f' fs'>16 <fs' g'>16 <g' af'>16 <af' a'>16 <a' bf'>16 <bf' b'>16 <b' c''>16 } .. figure:: ../_images/respell_augmented_unisons-qQduIqCRpz.png >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.show(staff) .. docs:: \new Staff { \accidentalStyle dodecaphonic <c' df'>16 <cs' d'>16 <d' ef'>16 <ds' e'>16 <e' f'>16 <f' gf'>16 <fs' g'>16 <g' af'>16 <gs' a'>16 <a' bf'>16 <as' b'>16 <b' c''>16 } .. figure:: ../_images/respell_augmented_unisons-jvg032q24il.png augmented unissons in chords with 3 or more pitches: The function looks for all augmented unissons in chords of 3 or more pitches: >>> staff = abjad.Staff(r"<a c' cs' f'>1") >>> abjad.show(staff) .. docs:: \new Staff { <a c' cs' f'>1 } .. figure:: ../_images/respell_augmented_unisons-IklJO81q1E.png >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.show(staff) .. docs:: \new Staff { <a c' df' f'>1 } .. figure:: ../_images/respell_augmented_unisons-gyficck05p.png ``include_multiples``: By default, this function only changes spelling for pitches that are 1 semitone apart. >>> staff = abjad.Staff(r"<c' cs''>1") >>> abjad.show(staff) .. docs:: \new Staff { <c' cs''>1 } .. figure:: ../_images/respell_augmented_unisons-HPqFrADjeh.png >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.show(staff) .. docs:: \new Staff { <c' cs''>1 } .. figure:: ../_images/respell_augmented_unisons-uszf11qb72d.png To consider pitches in different octaves (thus including augmented unisons, augmented octaves, augmented fifteenths, etc.), call this function with the keyword argument ``include_multiples`` set to ``True``. >>> staff = abjad.Staff(r"<c' cs''>1") >>> auxjad.mutate.respell_augmented_unisons( ... staff[:], ... include_multiples=True, ... ) .. docs:: \new Staff { <c' df''>1 } .. figure:: ../_images/respell_augmented_unisons-8am8cu2rmgi.png ``respell_by_pitch_class``: By default, when this function changes the spelling of a pitch, it does not change the spelling of all other pitches with the same pitch-class. >>> staff = abjad.Staff(r"<c' cs' cs''>1") >>> abjad.show(staff) .. docs:: \new Staff { <c' cs' cs''>1 } .. figure:: ../_images/respell_augmented_unisons-eWixL7iCEq.png >>> auxjad.mutate.respell_augmented_unisons(staff[:]) >>> abjad.show(staff) .. docs:: \new Staff { <c' df' cs''>1 } .. figure:: ../_images/respell_augmented_unisons-47d16xk6gvs.png To alter all pitch-classes, call this function with the keyword argument ``respell_by_pitch_class`` set to ``True``. >>> staff = abjad.Staff(r"<c' cs' cs''>1") >>> auxjad.mutate.respell_augmented_unisons( ... staff[:], ... respell_by_pitch_class=True, ... ) >>> abjad.show(staff) .. docs:: \new Staff { <c' df' df''>1 } .. figure:: ../_images/respell_augmented_unisons-kobft0oq9sl.png """ if not isinstance(selection, abjad.Selection): raise TypeError("argument must be 'abjad.Selection'") if not isinstance(respell_by_pitch_class, bool): raise TypeError("'respell_by_pitch_class' must be 'bool'") if not isinstance(include_multiples, bool): raise TypeError("'include_multiples' must be 'bool'") for leaf in selection.leaves(): if isinstance(leaf, abjad.Chord): accidentals = [pitch.accidental for pitch in leaf.written_pitches] original_accidentals = accidentals[:] if not include_multiples: # dealing only with intervals of size equal to 1 semitone for i in range(len(leaf.written_pitches) - 1): p1 = leaf.written_pitches[i] p2 = leaf.written_pitches[i + 1] interval12 = p1 - p2 try: p3 = leaf.written_pitches[i + 2] interval23 = p2 - p3 except IndexError: p3 = None interval23 = None if (interval12 == abjad.NamedInterval('+A1') and interval23 != abjad.NamedInterval('+A1') and interval23 != abjad.NamedInterval('+m2')): if not respell_by_pitch_class: # respelling only one single note if p1.accidental == abjad.Accidental('f'): accidentals[i] = abjad.Accidental('s') elif p1.accidental == abjad.Accidental(''): accidentals[i + 1] = abjad.Accidental('f') else: # respelling all matching pitch-classes if p1.accidental == abjad.Accidental('f'): for j, p in enumerate(leaf.written_pitches): if p.pitch_class == p1.pitch_class: accidentals[j] = abjad.Accidental('s') elif p1.accidental == abjad.Accidental(''): for j, p in enumerate(leaf.written_pitches): if p.pitch_class == p2.pitch_class: accidentals[j] = abjad.Accidental('f') else: # dealing with augmented unisons as well as augmented 8as, # 15ths, etc. for i in range(len(leaf.written_pitches) - 1): for j in range(i + 1, len(leaf.written_pitches)): # no p3 this time since p1 and p2 are not necessary # consecutive pitches in the chord p1 = leaf.written_pitches[i] p2 = leaf.written_pitches[j] interval = abjad.NamedIntervalClass(p1 - p2) if (interval in ( abjad.NamedIntervalClass('+A1'), abjad.NamedIntervalClass('-d1'), )): # no need for respell_by_pitch_class since this # will go through all notes in the chord anyway if p1.accidental == abjad.Accidental('f'): accidentals[i] = abjad.Accidental('s') if p1.accidental == abjad.Accidental('s'): accidentals[i] = abjad.Accidental('f') elif p1.accidental == abjad.Accidental(''): if p2.accidental == abjad.Accidental('s'): accidentals[j] = abjad.Accidental('f') elif p2.accidental == abjad.Accidental('f'): accidentals[j] = abjad.Accidental('s') # rewritting chord with new spelling respelt_pitches = [] for pitch, accidental, original_accidental in zip( leaf.written_pitches, accidentals, original_accidentals, ): if (accidental != original_accidental and accidental == abjad.Accidental('f')): respelt_pitches.append(pitch._respell(accidental='flats')) elif (accidental != original_accidental and accidental == abjad.Accidental('s')): respelt_pitches.append(pitch._respell(accidental='sharps')) else: respelt_pitches.append(pitch) leaf.written_pitches = respelt_pitches
def respell_accidentals( selection: abjad.Selection, *, include_multiples: bool = False, respell_by_pitch_class: bool = False, ) -> None: r"""Mutates an input |abjad.Selection| in place and has no return value; this function changes the accidentals of individual pitches of all chords in a container in order to avoid augmented unisons. Basic usage: To use this function, apply it to a selection that contains chords that have augmented unisons. >>> container = abjad.Container(r"c'4 r4 <ef' e'>4 g'4 <c' cs'>4 r2.") >>> auxjad.mutate(container[:]).respell_accidentals() >>> abjad.f(container) { c'4 r4 <ds' e'>4 g'4 <c' df'>4 r2. } .. figure:: ../_images/respell_accidentals-x33afbbamt.png .. note:: Auxjad automatically adds this function as an extension method to |abjad.mutate()|. It can thus be used from either :func:`auxjad.mutate()` or |abjad.mutate()|. Therefore, the two lines below are equivalent: >>> auxjad.mutate(staff[:]).respell_accidentals() >>> abjad.mutate(staff[:]).respell_accidentals() 2-note chords: The example below shows the default spelling of 2-note chords in Abjad in the upper staff, and the respelt 2-note chords in the bottom staff. >>> staff1 = abjad.Staff() >>> staff2 = abjad.Staff() >>> for pitch in range(12): ... staff1.append(abjad.Chord([pitch, pitch + 1], (1, 16))) ... staff2.append(abjad.Chord([pitch, pitch + 1], (1, 16))) >>> auxjad.mutate(staff2[:]).respell_accidentals() >>> literal = abjad.LilyPondLiteral(r'\accidentalStyle dodecaphonic') >>> abjad.attach(literal, staff1) >>> abjad.attach(literal, staff2) >>> score = abjad.Score([staff1, staff2]) >>> abjad.f(score) \new Score << \new Staff { \accidentalStyle dodecaphonic <c' cs'>16 <cs' d'>16 <d' ef'>16 <ef' e'>16 <e' f'>16 <f' fs'>16 <fs' g'>16 <g' af'>16 <af' a'>16 <a' bf'>16 <bf' b'>16 <b' c''>16 } \new Staff { \accidentalStyle dodecaphonic <c' df'>16 <cs' d'>16 <d' ef'>16 <ds' e'>16 <e' f'>16 <f' gf'>16 <fs' g'>16 <g' af'>16 <gs' a'>16 <a' bf'>16 <as' b'>16 <b' c''>16 } >> .. figure:: ../_images/respell_accidentals-jvg032q24il.png augmented unissons in larger chords: The function looks for all augmented unissons in chords of 3 or more pitches: >>> container1 = abjad.Container(r"<a c' cs' f'>1") >>> container2 = abjad.Container(r"<a c' cs' f'>1") >>> auxjad.mutate(container2[:]).respell_accidentals() >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <a c' cs' f'>1 } { <a c' df' f'>1 } } .. figure:: ../_images/respell_accidentals-gyficck05p.png It is not a problem if the pitches are input out of order. >>> container1 = abjad.Container(r"<e' cs' g' ef'>1") >>> container2 = abjad.Container(r"<e' cs' g' ef'>1") >>> auxjad.mutate(container2[:]).respell_accidentals() >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <cs' ef' e' g'>1 } { <cs' ds' e' g'>1 } } .. figure:: ../_images/respell_accidentals-xbu6u6mu6qo.png ``include_multiples``: By default, this function only changes spelling for pitches that are 1 semitone apart. >>> container1 = abjad.Container(r"<c' cs''>1") >>> container2 = abjad.Container(r"<c' cs''>1") >>> auxjad.mutate(container2[:]).respell_accidentals() >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <c' cs''>1 } { <c' cs''>1 } } .. figure:: ../_images/respell_accidentals-uszf11qb72d.png To consider pitches in different octaves (thus including augmented unisons, augmented octaves, augmented fifteenths, etc.), call this function with the keyword argument ``include_multiples`` set to ``True``. >>> container1 = abjad.Container(r"<c' cs''>1") >>> container2 = abjad.Container(r"<c' cs''>1") >>> auxjad.mutate(container2[:]).respell_accidentals( ... include_multiples=True, ... ) >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <c' cs''>1 } { <c' df''>1 } } .. figure:: ../_images/respell_accidentals-8am8cu2rmgi.png ``respell_by_pitch_class``: By default, when this function changes the spelling of a pitch, it does not change the spelling of all other pitches with the same pitch-class. >>> container1 = abjad.Container(r"<c' cs' cs''>1") >>> container2 = abjad.Container(r"<c' cs' cs''>1") >>> auxjad.mutate(container2[:]).respell_accidentals() >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <c' cs' cs''>1 } { <c' df' cs''>1 } } .. figure:: ../_images/respell_accidentals-47d16xk6gvs.png To alter all pitch-classes, call this function with the keyword argument ``respell_by_pitch_class`` set to ``True``. >>> container1 = abjad.Container(r"<c' cs' cs''>1") >>> container2 = abjad.Container(r"<c' cs' cs''>1") >>> auxjad.mutate(container2[:]).respell_accidentals( ... respell_by_pitch_class=True, ... ) >>> staff = abjad.Staff([container1, container2]) >>> abjad.f(staff) \new Staff { { <c' cs' cs''>1 } { <c' df' df''>1 } } .. figure:: ../_images/respell_accidentals-kobft0oq9sl.png """ if not isinstance(selection, abjad.Selection): raise TypeError("argument must be 'abjad.Selection'") if not isinstance(respell_by_pitch_class, bool): raise TypeError("'respell_by_pitch_class' must be 'bool'") if not isinstance(include_multiples, bool): raise TypeError("'include_multiples' must be 'bool'") for leaf in selection.leaves(): if isinstance(leaf, abjad.Chord): accidentals = [pitch.accidental for pitch in leaf.written_pitches] if not include_multiples: # dealing only with intervals of size equal to 1 semitone for i in range(len(leaf.written_pitches) - 1): p1 = leaf.written_pitches[i] p2 = leaf.written_pitches[i + 1] interval12 = p1 - p2 try: p3 = leaf.written_pitches[i + 2] interval23 = p2 - p3 except IndexError: p3 = None interval23 = None if (interval12 == abjad.NamedInterval('+A1') and interval23 != abjad.NamedInterval('+A1') and interval23 != abjad.NamedInterval('+m2')): if not respell_by_pitch_class: # respelling only one single note if p1.accidental == abjad.Accidental('f'): accidentals[i] = abjad.Accidental('s') elif p1.accidental == abjad.Accidental(''): accidentals[i + 1] = abjad.Accidental('f') else: # respelling all matching pitch-classes if p1.accidental == abjad.Accidental('f'): for j, p in enumerate(leaf.written_pitches): if p.pitch_class == p1.pitch_class: accidentals[j] = abjad.Accidental('s') elif p1.accidental == abjad.Accidental(''): for j, p in enumerate(leaf.written_pitches): if p.pitch_class == p2.pitch_class: accidentals[j] = abjad.Accidental('f') else: # dealing with augmented unisons as well as augmented 8as, # 15ths, etc. for i in range(len(leaf.written_pitches) - 1): for j in range(i + 1, len(leaf.written_pitches)): # no p3 this time since p1 and p2 are not necessary # consecutive pitches in the chord p1 = leaf.written_pitches[i] p2 = leaf.written_pitches[j] interval = abjad.NamedIntervalClass(p1 - p2) if (interval in ( abjad.NamedIntervalClass('+A1'), abjad.NamedIntervalClass('-d1'), )): # no need for respell_by_pitch_class since this # will go through all notes in the chord anyway if p1.accidental == abjad.Accidental('f'): accidentals[i] = abjad.Accidental('s') if p1.accidental == abjad.Accidental('s'): accidentals[i] = abjad.Accidental('f') elif p1.accidental == abjad.Accidental(''): if p2.accidental == abjad.Accidental('s'): accidentals[j] = abjad.Accidental('f') elif p2.accidental == abjad.Accidental('f'): accidentals[j] = abjad.Accidental('s') # rewritting chord with new spelling respelt_pitches = [] for pitch, accidental in zip(leaf.written_pitches, accidentals): if accidental == abjad.Accidental('f'): respelt_pitches.append(pitch._respell_with_flats()) elif accidental == abjad.Accidental('s'): respelt_pitches.append(pitch._respell_with_sharps()) else: respelt_pitches.append(pitch) leaf.written_pitches = respelt_pitches