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)
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