Example #1
0
    def create_adapted_function(self, domain_tonality, range_tonality):
        """
        Generate a tonal function from this template using:

        :param domain_tonality: Replacement domain tonality.
        :param range_tonality: Replacement range tnality.
        :return:
        """

        # Tonalities must match original domain and range in cardinality.
        if domain_tonality.cardinality != self.domain_cardinality:
            raise Exception(
                'Cardinalities of domains don\'t match: given {0} versus {1}.'.
                format(domain_tonality.cardinality, self.domain_cardinality))
        if range_tonality.cardinality != self.range_cardinality:
            raise Exception(
                'Cardinalities of ranges don\'t match: given {0} versus {1}.'.
                format(range_tonality.cardinality, self.range_cardinality))

        domain_tones = domain_tonality.annotation[:len(domain_tonality.
                                                       annotation) - 1]
        range_tones = range_tonality.annotation[:len(range_tonality.annotation
                                                     ) - 1]

        primary_map = dict()
        for t, i in zip(domain_tones, self.tonal_order):
            primary_map[t] = None if i is None else range_tones[i]

        # Build the extension map
        extension_map = dict()
        for k, v in self.extension_interval_map.items():
            key_tone = Interval.end_tone_from_pure_distance(
                domain_tones[0], k[0], k[1])
            if key_tone not in domain_tones:
                value_tone = Interval.end_tone_from_pure_distance(
                    range_tones[0], v[0], v[1])
                extension_map[key_tone] = value_tone

        # Examine the primary intervals, as some may not be primary due to modality change.
        for k, v in self.tonal_interval_map.items():
            key_tone = Interval.end_tone_from_pure_distance(
                domain_tones[0], k[0], k[1])
            if key_tone not in domain_tones:
                value_tone = Interval.end_tone_from_pure_distance(
                    range_tones[0], v[0], v[1])
                extension_map[key_tone] = value_tone

        return TonalFunction(domain_tonality, range_tonality, primary_map,
                             extension_map)
Example #2
0
    def test_pure_distances(self):
        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('E'),
                                                  DiatonicToneCache.get_tone('C'))
        assert dd == 5
        assert cc == 8

        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('Ab'),
                                                  DiatonicToneCache.get_tone('B'))
        assert dd == 1
        assert cc == 3

        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('C'),
                                                  DiatonicToneCache.get_tone('B'))
        assert dd == 6
        assert cc == 11

        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('B'),
                                                  DiatonicToneCache.get_tone('C'))
        assert dd == 1
        assert cc == 1

        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('Ebb'),
                                                  DiatonicToneCache.get_tone('A#'))
        assert dd == 3
        assert cc == 8

        dd, cc = Interval.calculate_pure_distance(DiatonicToneCache.get_tone('A#'),
                                                  DiatonicToneCache.get_tone('Ebb'))
        assert dd == 4
        assert cc == 4

        end_tone = Interval.end_tone_from_pure_distance(DiatonicToneCache.get_tone('C'), 4, 6)
        assert 'Gb' == DiatonicTone.to_upper(end_tone.diatonic_symbol)

        end_tone = Interval.end_tone_from_pure_distance(DiatonicToneCache.get_tone('G'), 2, 5)
        assert 'B#' == DiatonicTone.to_upper(end_tone.diatonic_symbol)

        end_tone = Interval.end_tone_from_pure_distance(DiatonicToneCache.get_tone('Gb'), 4, 6, False)
        assert 'C' == DiatonicTone.to_upper(end_tone.diatonic_symbol)

        end_tone = Interval.end_tone_from_pure_distance(DiatonicToneCache.get_tone('B#'), 2, 5, False)
        assert 'G' == DiatonicTone.to_upper(end_tone.diatonic_symbol)
    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