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
Exemplo n.º 2
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
Exemplo 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))
Exemplo n.º 4
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)
Exemplo n.º 5
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
Exemplo n.º 6
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
Exemplo n.º 7
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)))
Exemplo n.º 8
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))
Exemplo n.º 9
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
Exemplo n.º 10
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)
Exemplo n.º 11
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