def create(start_spn, end_spn): """ Create PitchRange based on start and end scientific pitch notation. Args: start_spn: start spn (pitch string). end_spn: end spn (pitch string). Returns: PitchRange based on inputs. """ start = DiatonicFoundation.get_chromatic_distance( DiatonicPitch.parse(start_spn) if isinstance(start_spn, str ) else start_spn) end = DiatonicFoundation.get_chromatic_distance( DiatonicPitch.parse(end_spn) if isinstance(end_spn, str ) else end_spn) return PitchRange(start, end)
def is_pitch_inbounds(self, pitch): """ Determines if given chromatic location is in bounds of range. Args: pitch: spn text for pitch, e.g. 'c:4' or DiatonticPitch object. Returns: boolean indicating if in bounds. """ p = DiatonicPitch.parse(pitch) if isinstance(pitch, str) else pitch return self.is_inbounds(DiatonicFoundation.get_chromatic_distance(p))
def test_semitone_difference(self): assert DiatonicFoundation.get_chromatic_distance(DiatonicPitch.parse('C:4')) == 48 equi_list = DiatonicFoundation.add_semitones(DiatonicPitch.parse('eb:4'), 13) assert equi_list is not None assert len(equi_list) == 3 assert DiatonicPitch.parse('E:5') in equi_list assert DiatonicPitch.parse('D##:5') in equi_list assert DiatonicPitch.parse('Fb:5') in equi_list a = DiatonicPitch.parse('C:5') b = DiatonicPitch.parse('F#:4') assert DiatonicFoundation.semitone_difference(a, b) == 6 assert DiatonicFoundation.semitone_difference(b, a) == -6
def _find_closest_pitch(self, pitch): """ Given a pitch, find the scale pitch closest to it in chromatic distance. :param pitch: :return: 1) closest in half-steps pitch in tonality. 2) chromatic distance measured from closest pitch to given pitch. Note: algorithm looks for tonal pitches with same letter as pitch first, otherwise nearest non-tonal pitch. """ start_octave = max(pitch.octave - 1, ChromaticScale.CHROMATIC_START[0]) end_octave = min(pitch.octave + 1, ChromaticScale.CHROMATIC_END[0]) # Compute the first and last pitches within the start/end octave range. To build a PitchScale first_pitch = None for t in self.tonality.annotation: first_pitch = DiatonicPitch(start_octave, t) if DiatonicFoundation.get_chromatic_distance(first_pitch) >= ChromaticScale.chromatic_start_index(): break last_pitch = None loop_finished = False for o in range(end_octave, end_octave - 2, -1): if loop_finished: break for t in reversed(self.tonality.annotation): last_pitch = DiatonicPitch(o, t) if DiatonicFoundation.get_chromatic_distance(last_pitch) <= ChromaticScale.chromatic_end_index(): loop_finished = True break scale = PitchScale(self.tonality, PitchRange.create(first_pitch, last_pitch)) # determine if pitch ltr is in tonality, get that tone ll = [t for t in self.tones if t.diatonic_letter == pitch.diatonic_tone.diatonic_letter] if len(ll) == 1: pp = DiatonicPitch(pitch.octave, ll[0]) return pp, pitch.chromatic_distance - pp.chromatic_distance # Do something if len(ll) > 1 elif len(ll) > 1: ll.sort(key=lambda x: abs(x.augmentation_offset - pitch.diatonic_tone.augmentation_offset)) pp = DiatonicPitch(pitch.octave, ll[0]) return pp, pitch.chromatic_distance - pp.chromatic_distance before_pitch = first_pitch after_pitch = last_pitch for p in scale.pitch_scale: if pitch.chromatic_distance <= p.chromatic_distance: after_pitch = p break else: before_pitch = p before_distance = pitch.chromatic_distance - before_pitch.chromatic_distance after_distance = pitch.chromatic_distance - after_pitch.chromatic_distance if pitch.diatonic_tone.diatonic_letter == before_pitch.diatonic_tone.diatonic_letter: closest_distance = before_distance closest_pitch = before_pitch elif pitch.diatonic_tone.diatonic_letter == after_pitch.diatonic_tone.diatonic_letter: closest_distance = after_distance closest_pitch = after_pitch else: if abs(before_distance) < abs(after_distance): closest_distance = before_distance closest_pitch = before_pitch else: closest_distance = after_distance closest_pitch = after_pitch return closest_pitch, closest_distance