Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
    def __init__(self,
                 anchor_pitch=DiatonicPitch.parse('A:0'),
                 anchor_value=9,
                 pitch_unit=1):
        """
        Constructor,
        """
        self.__anchor_pitch = anchor_pitch
        if not isinstance(self.anchor_pitch, DiatonicPitch):
            raise Exception('Anchor is not a DiatonicPitch')

        self.__anchor_value = anchor_value
        self.__pitch_unit = pitch_unit

        anchor_index = self.anchor_pitch.chromatic_distance
        base_value = anchor_value - anchor_index * pitch_unit

        self.value_to_pitch = OrderedMap()
        self.pitch_to_value = dict()
        for i in range(ChromaticScale.chromatic_start_index(),
                       ChromaticScale.chromatic_end_index() + 1):
            pitch = DiatonicFoundation.map_to_diatonic_scale(i)[0]
            value = base_value + i * pitch_unit
            self.value_to_pitch.insert(value, pitch)
            self.pitch_to_value[pitch] = value

        PitchRangeInterpreter.__init__(self)
Ejemplo n.º 3
0
    def get_end_pitch(self, pitch):
        """
        Given a pitch and this interval, Assuming pitch is the starting pitch of the interval,
        compute the end pitch.
        
        Args:
          pitch: DiatonicPitch
          
        Returns:
          DiatonicPitch of end tone
        """
        diatonic_dist = pitch.diatonic_distance() + self.diatonic_distance
        tone_index = diatonic_dist % 7
        end_pitch_string = DiatonicTone.get_diatonic_letter(tone_index)
        end_pitch_octave = diatonic_dist // 7

        chromatic_dist = pitch.chromatic_distance + self.chromatic_distance

        normal_pitch = DiatonicPitch(
            end_pitch_octave, DiatonicFoundation.get_tone(end_pitch_string))

        alteration = chromatic_dist - normal_pitch.chromatic_distance

        end_pitch_string += DiatonicTone.augmentation(alteration)

        return DiatonicPitch.parse(end_pitch_string + ':' +
                                   str(end_pitch_octave))
 def _build_non_tonal_pitch_map(self, pmap):
     octave_start = self._domain_pitch_range.start_index // 12
     octave_end = self._domain_pitch_range.end_index // 12
     for octave in range(octave_start, octave_end + 1):
         for ltr in 'ABCDEFG':
             for aug in ['bb', 'b', '', '#', "##"]:
                 p = DiatonicPitch(octave, DiatonicFoundation.get_tone(ltr + aug))
                 if self._domain_pitch_range.is_pitch_inbounds(p) and p not in pmap.keys():
                     closest_p, closest_distance = self._find_closest_pitch(p)
                     if closest_p not in pmap.keys():
                         continue
                     closest_p_prime = pmap[closest_p]
                     t = self.tonal_function[p.diatonic_tone]
                     if closest_p > p:
                         o = closest_p_prime.octave + 1 if DiatonicPitch.crosses_c(closest_p_prime.diatonic_tone,
                                                                                   t,
                                                                                   True) else closest_p_prime.octave
                     else:
                         o = closest_p_prime.octave - 1 if DiatonicPitch.crosses_c(closest_p_prime.diatonic_tone,
                                                                                   t,
                                                                                   False) else closest_p_prime.octave
                     p_prime = DiatonicPitch(o, t)
                     pmap[p] = p_prime
                     # The mapping is not-symmetrical.
                     # e.g. E-pentatonic reflection_tests on G#, f(A#)->G and f(g)->G##
     return pmap
Ejemplo n.º 5
0
    def test_major_modality(self):
        print('Testing Major Modality: D-Major, cue=G:4')
        domain_tonality = Tonality.create(ModalityType.Major, DiatonicFoundation.get_tone('D'))
        cue_pitch = DiatonicPitch.parse('G:4')

        domain_pitch_range = PitchRange.create('D:3', 'C#:6')
        f = ChromaticPitchReflectionFunction(domain_tonality, cue_pitch, domain_pitch_range, FlipType.CenterTone)

        TestChromaticPitchReflectionFunction.print_function(f)

        # Ensure all domain keys are in the domain_pitch_range
        for pitch in f.domain:
            assert domain_pitch_range.is_pitch_inbounds(pitch), \
                'Pitch {0} is not in range {1}.'.format(pitch, domain_pitch_range)

        assert DiatonicPitch.parse('D:3') in f.domain
        assert DiatonicPitch.parse('C#:6') in f.domain

        # Test for octave coverage
        assert 6 == f['D:3'].octave
        assert 5 == f['C#:4'].octave
        assert 5 == f['D:4'].octave
        assert 4 == f['C#:5'].octave
        assert 4 == f['D:5'].octave
        assert 3 == f['C#:6'].octave
Ejemplo n.º 6
0
 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)
Ejemplo n.º 7
0
 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))
Ejemplo n.º 8
0
    def test_low_range(self):
        ranges = PitchRange.create("A:0", "C:5")
        for modality_type in SYSTEM_MODALITIES:
            for validTone in ModalityFactory.create_modality(modality_type).get_valid_root_tones():
                tonality = Tonality.create(modality_type, DiatonicFoundation.get_tone(validTone))
        
                pitch_scale = PitchScale(tonality, ranges)
                print('Scale {0} {1} on {2}: {3}'.format(validTone, modality_type.name, ranges,
                                                         ','.join(map(get_symbol, pitch_scale.pitch_scale))))

                scale_check(pitch_scale, tonality)
Ejemplo n.º 9
0
 def test_map_to_diatonic_scale(self):
     answers = DiatonicFoundation.map_to_diatonic_scale(46)
         
     assert DiatonicPitch.parse('Cbb:4') in answers
     assert DiatonicPitch.parse('A#:3') in answers
     assert DiatonicPitch.parse('Bb:3') in answers
     answers = DiatonicFoundation.map_to_diatonic_scale(47)
         
     assert DiatonicPitch.parse('Cb:4') in answers
     assert DiatonicPitch.parse('A##:3') in answers
     assert DiatonicPitch.parse('B:3') in answers
         
     answers = DiatonicFoundation.map_to_diatonic_scale(48)
         
     assert DiatonicPitch.parse('C:4') in answers
     assert DiatonicPitch.parse('B#:3') in answers
     assert DiatonicPitch.parse('Dbb:4') in answers
     
     answers = DiatonicFoundation.map_to_diatonic_scale(49)        
         
     assert DiatonicPitch.parse('C#:4') in answers
     assert DiatonicPitch.parse('B##:3') in answers
     assert DiatonicPitch.parse('Db:4') in answers
Ejemplo n.º 10
0
    def eval_as_accurate_chromatic_distance(self, v):
        floor_value = self.value_to_pitch.floor(v)
        if floor_value is None:
            raise ChromaticRangeInterpreterException(
                'Illegal chromatic pitch range paramger value {0}.'.format(v))
        low_pitch = self.value_to_pitch[floor_value]
        index = low_pitch.chromatic_distance

        if index >= ChromaticScale.chromatic_end_index() or math.isclose(
                v, floor_value):
            return low_pitch.chromatic_distance
        high_pitch = DiatonicFoundation.map_to_diatonic_scale(index + 1)[0]
        return low_pitch.chromatic_distance + \
            ((v - floor_value) / self.pitch_unit) * \
            (high_pitch.chromatic_distance - low_pitch.chromatic_distance)
Ejemplo n.º 11
0
    def test_melodic_minor_modality(self):
        print('Testing Melodic Minor Modality: C-MelodicMinor, cue=Eb:3')
        domain_tonality = Tonality.create(ModalityType.MelodicMinor, DiatonicFoundation.get_tone('C'))
        cue_pitch = DiatonicPitch.parse('Eb:3')

        domain_pitch_range = PitchRange.create('D:4', 'F:5')
        f = ChromaticPitchReflectionFunction(domain_tonality, cue_pitch, domain_pitch_range, FlipType.CenterTone)

        TestChromaticPitchReflectionFunction.print_function(f)

        assert 2 == f['D:4'].octave
        assert 2 == f['G:4'].octave
        assert 1 == f['A:4'].octave
        assert 1 == f['C:5'].octave
        assert 1 == f['f:5'].octave
Ejemplo n.º 12
0
    def test_natural_minor_modality(self):
        print('Testing Natural Minor Modality: C-Major, cue=Eb:3')
        domain_tonality = Tonality.create(ModalityType.NaturalMinor, DiatonicFoundation.get_tone('C'))
        cue_pitch = DiatonicPitch.parse('Eb:3')

        domain_pitch_range = PitchRange.create('D:2', 'F:4')
        f = ChromaticPitchReflectionFunction(domain_tonality, cue_pitch, domain_pitch_range, FlipType.CenterTone)

        TestChromaticPitchReflectionFunction.print_function(f)

        # Test for octave coverage
        assert 4 == f['D:2'].octave
        assert 3 == f['Bb:2'].octave
        assert 3 == f['C:3'].octave
        assert 2 == f['Bb:3'].octave
        assert 2 == f['F:4'].octave
Ejemplo n.º 13
0
    def test_book_examples(self):
        modality = ModalityFactory.create_modality(ModalityType.Major)
        assert modality is not None

        modality = ModalityFactory.create_modality(
            ModalityType.MajorPentatonic, 1)
        assert modality is not None

        my_modality = 'my_modality'
        modality_type = ModalityType(my_modality)
        incremental_interval_strs = [
            'P:1', 'm:2', 'M:3', 'm:2', 'm:2', 'M:2', 'A:2'
        ]
        modality_spec = ModalitySpec(modality_type, incremental_interval_strs)
        ModalityFactory.register_modality(modality_type, modality_spec)
        modality = ModalityFactory.create_modality(ModalityType(my_modality))
        assert modality is not None

        tones = modality.get_tonal_scale(DiatonicFoundation.get_tone('Eb'))
        print('[{0}]'.format(','.join(
            str(tone.diatonic_symbol) for tone in tones)))
Ejemplo n.º 14
0
    def print_function(f):
        domain_tonality = f.domain_tonality
        tones = domain_tonality.annotation[:len(domain_tonality.annotation) -
                                           1]

        for t in tones:
            print('{0} --> {1}'.format(t.diatonic_symbol,
                                       f[t].diatonic_symbol))

        print('------------------------')
        # print for domain tone letters not in the domain tonality
        for letter in 'ABCDEFG':
            found = False
            for t in tones:
                if t.diatonic_letter == letter:
                    found = True
                    break
            if not found:
                ltr_tone = DiatonicFoundation.get_tone(letter)
                print('{0} --> {1}'.format(ltr_tone.diatonic_symbol,
                                           f[ltr_tone].diatonic_symbol))
Ejemplo n.º 15
0
    def __init__(self, octave, diatonic_tone):
        """
        Constructor
      
        Args:
          octave:  integer >=0
          diatonic_tone: tone or letter representation of the diatonic tone, e.g. D#
          
          Note: 
            The tone is relative to the partition based on tonal_offset.  
            So, Cb:4 is really B:3 - however we retain 4 as the partition as Cb is relative to the 4th.
                Same with B#4 which is really C:5, we retain the 4.
            So the partition is not the actual partition, but the relative partition number.
        """
        self.__octave = octave

        if isinstance(diatonic_tone, DiatonicTone):
            self.__diatonic_tone = diatonic_tone
        else:
            self.__diatonic_tone = DiatonicFoundation.get_tone(diatonic_tone)
        self.__chromatic_distance = 12 * octave + self.diatonic_tone.tonal_offset
Ejemplo n.º 16
0
    def test_secondary_chord(self):
        print('----- test_secondary_tonality -----')
        diatonic_tonality = Tonality.create(ModalityType.Major,
                                            DiatonicFoundation.get_tone("C"))
        chort_t_i = TertianChordTemplate.parse('tI')
        chord_i = chort_t_i.create_chord(diatonic_tonality)

        chord_v_ii = SecondaryChordTemplate.parse('V/ii').create_chord(
            diatonic_tonality)
        chord_vi_v = SecondaryChordTemplate.parse('vi/V').create_chord(
            diatonic_tonality)

        chord_t_ii = TertianChordTemplate.parse('tii')
        chord_ii = chord_t_ii.create_chord(diatonic_tonality)

        hc_track = HarmonicContextTrack()
        hc_track.append(
            HarmonicContext(diatonic_tonality, chord_i, Duration(1)))
        hc_track.append(
            HarmonicContext(diatonic_tonality, chord_v_ii, Duration(1)))
        hc_track.append(
            HarmonicContext(diatonic_tonality, chord_vi_v, Duration(1)))
        hc_track.append(
            HarmonicContext(diatonic_tonality, chord_ii, Duration(1)))
        TestTChromaticFlip.print_hct(hc_track)

        tune = [('C:5', (1, 1)), ('E:5', (1, 1)), ('E:5', (1, 1)),
                ('G:5', (1, 1))]
        line = TestTChromaticFlip.build_line(tune)

        cue = DiatonicPitch(5, 'd')

        tflip = TChromaticReflection(line, hc_track, cue)

        score_line, score_hct = tflip.apply()
        TestTChromaticFlip.print_notes(score_line)
        TestTChromaticFlip.print_hct(score_hct)
Ejemplo n.º 17
0
 def __str__(self):
     return 'P-R({0}, {1})'.format(
         DiatonicFoundation.map_to_diatonic_scale(self.start_index)[0],
         DiatonicFoundation.map_to_diatonic_scale(self.end_index)[0])
    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
Ejemplo n.º 19
0
 def test_get_tones(self):
     tones = DiatonicFoundation.get_tones()
     
     assert tones is not None
     assert len(tones) > 12
     print(len(tones))
Ejemplo n.º 20
0
    def _build_extension_map(self):
        ltrs = 'CDEFGAB'
        extension = dict()

        domain_scale = self.domain_tonality.annotation[:-1]
        domain_start_index = ltrs.index(domain_scale[0].diatonic_letter)
        domain_index_list = list(ltrs[domain_start_index:] + ltrs[:domain_start_index])

        # One time calculations based on lower upper
        if self.reflect_type != FlipType.CenterTone:
            if self.reflect_type == FlipType.LowerNeighborOfPair:
                lower_domain_index = domain_scale.index(self.cue_tone)
                upper_domain_index = (lower_domain_index + 1) % len(domain_scale)
            else:
                upper_domain_index = domain_scale.index(self.cue_tone)
                lower_domain_index = (upper_domain_index - 1) % len(domain_scale)
            lower_tone = domain_scale[lower_domain_index]
            upper_tone = domain_scale[upper_domain_index]
            lower_ltr_index = domain_index_list.index(lower_tone.diatonic_letter)
            lower_augmentation = lower_tone.augmentation_offset
            upper_ltr_index = domain_index_list.index(upper_tone.diatonic_letter)
            upper_augmentation = upper_tone.augmentation_offset
        else:
            lower_tone = None
            upper_tone = None
            lower_ltr_index = None
            lower_augmentation = None
            upper_ltr_index = None
            upper_augmentation = None

        for ltr in 'CDEFGAB':
            for aug in ['bb', 'b', '', '#', "##"]:
                tone = DiatonicFoundation.get_tone(ltr + aug)
                if tone not in self.tonal_map.keys():
                    if self.reflect_type == FlipType.CenterTone:
                        interval = Interval.calculate_tone_interval(tone, self.cue_tone)
                        if interval:  # Some intervals are illegal, eg Cbb --> C, for now ignore
                            end_tone = interval.get_end_tone(self.cue_tone)
                            extension[tone] = end_tone
                    else:

                        tone_ltr_index = domain_index_list.index(tone.diatonic_letter)
                        tone_augmentation = tone.augmentation_offset
                        if tone_ltr_index >= 0 and (tone_ltr_index < lower_ltr_index or
                                                    (tone_ltr_index == lower_ltr_index and
                                                     tone_augmentation <= lower_augmentation)):
                            interval = Interval.calculate_tone_interval(tone, lower_tone)
                            if interval:
                                upper = interval.get_end_tone(upper_tone)
                                extension[tone] = upper
                        elif tone_ltr_index < len(domain_index_list) and (tone_ltr_index > upper_ltr_index or
                                                                          (tone_ltr_index == upper_ltr_index and
                                                                           tone_augmentation >= upper_augmentation)):
                            interval = Interval.calculate_tone_interval(tone, upper_tone)
                            if interval:
                                new_lower = interval.get_end_tone(lower_tone)
                                extension[tone] = new_lower
                        else:   # Between the two limits
                            upper_interval = Interval.calculate_tone_interval(tone, upper_tone)
                            lower_interval = Interval.calculate_tone_interval(lower_tone, tone)
                            if upper_interval is None and lower_interval is None:
                                continue
                            elif upper_interval is None:
                                extension[tone] = upper_tone
                            elif lower_interval is None:
                                extension[tone] = lower_tone
                            else:
                                if abs(lower_interval.chromatic_distance) <= abs(upper_interval.chromatic_distance):
                                    extension[tone] = lower_interval.negation().get_end_tone(upper_tone)
                                else:
                                    extension[tone] = upper_interval.negation().get_end_tone(lower_tone)

        return extension
    def _build_pitch_map1(self):
        pitch_map = dict()

        lo_reg = self.domain_pitch_range.start_index // 12
        hi_reg = self.domain_pitch_range.end_index // 12

        domain_start_tone = CrossTonalityShiftPitchFunction.compute_lowest_letter(
            self.domain_pitch_range.start_index %
            12)  # self.domain_tonality.annotation[0]
        range_start_tone = CrossTonalityShiftPitchFunction.compute_highest_letter(
            self.domain_pitch_range.end_index %
            12)  # self.range_tonality.annotation[0]

        # note letter template should start with letter for domain_start_tone
        note_letters = 'ABCDEFG'
        i = note_letters.index(domain_start_tone.diatonic_letter)
        note_letters = note_letters[i:] + note_letters[:i]

        domain_tones = list()
        for ltr in note_letters:
            for aug in ['bb', 'b', '', '#', '##']:
                domain_tones.append(DiatonicFoundation.get_tone(ltr + aug))

        first_domain_pitch = None
        first_range_pitch = None
        last_domain_pitch = None
        last_range_pitch = None
        for register in range(lo_reg, hi_reg + 1):
            domain_reg = register
            target_pitch = self.root_shift_interval.get_end_pitch(
                DiatonicPitch(domain_reg, domain_start_tone))
            range_reg = target_pitch.octave
            domain_reg_upped = False
            range_reg_upped = False
            for tone in domain_tones:
                if not domain_reg_upped and tone.diatonic_letter == 'C' and domain_start_tone.diatonic_letter != 'C':
                    domain_reg = domain_reg + 1
                    domain_reg_upped = True
                domain_pitch = DiatonicPitch(domain_reg, tone)
                if domain_pitch.chromatic_distance < ChromaticScale.chromatic_start_index() or \
                        domain_pitch.chromatic_distance > ChromaticScale.chromatic_end_index():
                    continue
                if self.domain_pitch_range.is_pitch_inbounds(domain_pitch):
                    range_tone = self.tonal_function[tone]
                    if not range_reg_upped and range_tone.diatonic_letter == 'C' and \
                            range_start_tone.diatonic_letter != 'C':
                        range_reg = range_reg + 1
                        range_reg_upped = True
                    target_pitch = DiatonicPitch(range_reg, range_tone)
                    if target_pitch.chromatic_distance < ChromaticScale.chromatic_start_index() or \
                            target_pitch.chromatic_distance > ChromaticScale.chromatic_end_index():
                        continue
                    pitch_map[domain_pitch] = target_pitch
                    if first_domain_pitch is None:
                        first_domain_pitch = domain_pitch
                        first_range_pitch = target_pitch
                    last_domain_pitch = domain_pitch
                    last_range_pitch = target_pitch

        if first_domain_pitch.chromatic_distance > last_domain_pitch.chromatic_distance:
            self.__domain_pitch_range = PitchRange.create(
                last_domain_pitch, first_domain_pitch)
        else:
            self.__domain_pitch_range = PitchRange.create(
                first_domain_pitch, last_domain_pitch)

        if first_range_pitch.chromatic_distance > last_range_pitch.chromatic_distance:
            self.__range_pitch_range = PitchRange.create(
                last_range_pitch, first_range_pitch)
        else:
            self.__range_pitch_range = PitchRange.create(
                first_range_pitch, last_range_pitch)
        return pitch_map
    def _build_extension_map(self):
        extension = dict()

        # Map domain tone augmentations to similarly augmented range tonality tones.
        #   e.g. if m[Db] == G, then m[D == Db#] == G#
        key_ltr_map = {d.diatonic_letter: d for d in self.domain_tonality.annotation}
        for ltr_item, tone_item in key_ltr_map.items():
            target_tone = self.constr_primary_map[tone_item]
            for aug in ['bb', 'b', '', '#', "##"]:
                tone = DiatonicFoundation.get_tone(ltr_item + aug)
                if tone not in self.constr_primary_map.keys():
                    aug_difference = tone.augmentation_offset - tone_item.augmentation_offset
                    new_target = DiatonicTone.alter_tone_by_augmentation(target_tone, aug_difference)
                    extension[tone] = new_target

        # Map all the other cases, e.g. tones outside pentatonic tonal scale for example.
        tone_list = self.domain_tonality.annotation[:-1]
        for ltr_item in 'ABCDEFG':
            if ltr_item not in key_ltr_map.keys():
                t = DiatonicFoundation.get_tone(ltr_item)
                lo_tone, hi_tone = CrossTonalityShiftTonalFunction._compute_neighbor_tones(t, tone_list)
                lo_target = self.constr_primary_map[lo_tone] if lo_tone in self.constr_primary_map.keys() else \
                    extension[lo_tone]
                hi_target = self.constr_primary_map[hi_tone] if hi_tone in self.constr_primary_map.keys() else \
                    extension[hi_tone]

                # d_diff is half-step diff between lo_tone and hi_tone for domain.
                d_diff = hi_tone.placement - lo_tone.placement if lo_tone.placement <= hi_tone.placement else \
                    hi_tone.placement - lo_tone.placement + 12
                # r_diff is half-step diff between lo_tone and hi_tone for target.
                r_diff = hi_target.placement - lo_target.placement if lo_target.placement <= hi_target.placement else\
                    hi_target.placement - lo_target.placement + 12

                range_factor = r_diff / d_diff
                # tone_domain_distance is chromatic distance from lo_tone to t  (domain).
                tone_domain_distance = t.placement - lo_tone.placement if lo_tone.placement <= t.placement else \
                    t.placement - lo_tone.placement + 12
                # range_chrom_dist is conversion of tone_domain_distance to range chromatic distance (interpolation).
                #     i.e. distance from lo_target to ideal target (corresponding to t in domain).
                range_chrom_dist = int(round(tone_domain_distance * range_factor))

                # Tonal interpolation:
                # 1) Need to use a diatonic dist from lo_target
                # 2) Adjusted so that from lo_target is range_chrom_dist
                diatonic_distance = DiatonicTone.calculate_diatonic_distance(lo_tone, t)
                # target_tone is end tone of interval based on diatonic_distance and range_chrom_dist
                target_tone = Interval.end_tone_from_pure_distance(lo_target, diatonic_distance, range_chrom_dist)

                extension[t] = target_tone
                tone_list.insert(tone_list.index(lo_tone) + 1, t)
                key_ltr_map[t] = target_tone

                # Now that t is mappec, map all the augmentations.
                for aug in ['bb', 'b', '#', "##"]:
                    aug_tone = DiatonicFoundation.get_tone(ltr_item + aug)
                    if aug_tone not in self.constr_primary_map.keys() and aug_tone not in extension.keys():
                        aug_difference = aug_tone.augmentation_offset - t.augmentation_offset
                        new_target = DiatonicTone.alter_tone_by_augmentation(target_tone, aug_difference)
                        extension[aug_tone] = new_target

        return extension
Ejemplo n.º 23
0
 def enharmonics(self):
     return DiatonicFoundation.map_to_diatonic_scale(
         self.chromatic_distance)