Example #1
0
    def test_pentatonic_tonal_function(self):
        t_domain = Tonality.create(ModalityType.MajorPentatonic,
                                   DiatonicTone('C'))

        interval = Interval(3, IntervalType.Major)

        f = CrossTonalityShiftTonalFunction.create_shift(t_domain, interval)
        # C, D, E, G. A ==> E, F#, G#, B, C#

        assert 'E' == f['C'].diatonic_symbol
        assert 'F#' == f['D'].diatonic_symbol
        assert 'G#' == f['E'].diatonic_symbol
        assert 'B' == f['G'].diatonic_symbol
        assert 'C#' == f['A'].diatonic_symbol

        assert 'A' == f['F'].diatonic_symbol
        assert 'D#' == f['B'].diatonic_symbol

        assert 'A#' == f['F#'].diatonic_symbol
        assert 'D##' == f['B#'].diatonic_symbol
        assert 'Ab' == f['Fb'].diatonic_symbol
        assert 'D' == f['Bb'].diatonic_symbol

        t_range = Tonality.create(ModalityType.MajorPentatonic,
                                  interval.get_end_tone(DiatonicTone('C')))
        f = CrossTonalityShiftTonalFunction(t_domain, t_range.annotation[2], 2)
        # C, D, E, G. A ==> G#, B, C#, E, F#

        assert 'G#' == f['C'].diatonic_symbol
        assert 'B' == f['D'].diatonic_symbol
        assert 'C#' == f['E'].diatonic_symbol
        assert 'E' == f['G'].diatonic_symbol
        assert 'F#' == f['A'].diatonic_symbol

        TestCrossTonalityShiftTonalFunction.print_function(f)
Example #2
0
    def test_book_examples(self):
        # Interval Creation
        interval = Interval(5, IntervalType.Perfect)
        print(interval)
        interval = Interval.parse("m:10")
        print(interval)
        interval = Interval.create_interval(DiatonicPitch.parse("a:3"), DiatonicPitch.parse("f#:4"))
        print(interval)

        # Interval Addition/Subtraction
        i1 = Interval(5, IntervalType.Perfect)
        i2 = Interval.parse("M:3")
        interval = i1 + i2
        print(interval)
        interval = i1 - i2
        print(interval)
        interval += i2
        print(interval)
        interval -= i2
        print(interval)

        # compute end and start
        interval = Interval(5, IntervalType.Perfect)
        pitch = interval.get_end_pitch(DiatonicPitch.parse("F#:5"))
        print(pitch)
        pitch = interval.get_start_pitch(DiatonicPitch.parse("C:5"))
        print(pitch)
Example #3
0
    def test_additional_octaves(self):
        t_domain = Tonality.create(ModalityType.MinorPentatonic,
                                   DiatonicTone('C'))
        i = Interval(12, IntervalType.Perfect)
        r = PitchRange.create('E:3', 'E:7')

        f = CrossTonalityShiftPitchFunction(t_domain, r, i)

        print('f={0}'.format(f))

        assert 'P:12' == str(f.root_shift_interval)

        assert 'G:5' == str(f['C:4'])
        assert 'A:5' == str(f['D:4'])
        assert 'B:5' == str(f['E:4'])
        assert 'C:6' == str(f['F:4'])

        i = Interval(11, IntervalType.Perfect).negation()
        f = CrossTonalityShiftPitchFunction(t_domain, r, i)
        print('f={0}'.format(f))

        assert '-P:11' == str(f.root_shift_interval)

        assert 'G:2' == str(f['C:4'])
        assert 'A:2' == str(f['D:4'])
        assert 'B:2' == str(f['E:4'])
        assert 'C:3' == str(f['F:4'])
Example #4
0
    def __create_chord_on_diatonic_tonality(self, diatonic_tone,
                                            diatonic_tonality):
        if not diatonic_tonality:
            raise Exception(
                "Cannot base secundal chord on tone {0} without tonality.".
                format(diatonic_tone.diatonic_symbol))
        # The tonality must include this tone.
        tone_scale = diatonic_tonality.annotation
        found_index = -1
        for i in range(0, len(tone_scale)):
            if diatonic_tone == tone_scale[i]:
                found_index = i
                break
        if found_index == -1:
            raise Exception(
                "For secundal chord based on tone {0}, tone must be in given tonality {1}"
                .format(diatonic_tone.diatonic_symbol, diatonic_tonality))
        self.chord_basis = []
        basis_tone = tone_scale[found_index]
        for i in range(0, 3):
            tone = tone_scale[(found_index + i) % (len(tone_scale) - 1)]
            pitch_a = DiatonicPitch(1, basis_tone.diatonic_symbol)
            b_octave = 2 if basis_tone.diatonic_index > tone.diatonic_index else 1
            pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol)
            interval = Interval.create_interval(pitch_a, pitch_b)
            # If for any reason, the interval is not perfect or augmented (we know it is a 4th), just adjust tone upward
            #    It is unknown if this can happen in a diatonic scale in practice.
            if interval.interval_type.value == IntervalType.Diminished:
                tone = DiatonicTone.alter_tone_by_augmentation(tone, 1)
                pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol)
                interval = Interval.create_interval(pitch_a, pitch_b)
            self.chord_basis.append(interval)

            self.__tones.append((tone, interval))
            basis_tone = tone
Example #5
0
    def test_inversion_pattern(self):
        ctxt = 'CMaj+9@(9)'
        template = TertianChordTemplate.parse(ctxt)
        if template:
            print('succeeded')
            chord = template.create_chord()
            print(chord)
        else:
            print("failed")

        # Another way to do the same
        from tonalmodel.interval import Interval, IntervalType
        from tonalmodel.diatonic_tone_cache import DiatonicToneCache
        template = TertianChordTemplate(DiatonicToneCache.get_cache().get_tone('C'),
                                        None,
                                        TertianChordType.to_type('Maj'),
                                        [Interval(9, IntervalType.Major)],
                                        None,
                                        Interval(9, IntervalType.Major))
        chord = template.create_chord()
        print(chord)

        # reuse
        template = TertianChordTemplate.parse('IVMin')
        diatonic_tonality = Tonality.create(ModalityType.Major, DiatonicToneCache.get_cache().get_tone("Db"))
        chord = template.create_chord(diatonic_tonality)
        print(chord)
        diatonic_tonality = Tonality.create(ModalityType.Major, DiatonicToneCache.get_cache().get_tone("F"))
        chord = template.create_chord(diatonic_tonality)
        print(chord)
        diatonic_tonality = Tonality.create(ModalityType.HarmonicMinor, DiatonicToneCache.get_cache().get_tone("C"))
        chord = template.create_chord(diatonic_tonality)
        print(chord)
    def test_across_tonalities(self):
        logging.debug('Start test_across_tonalities.')
        lower_policy_context_1 = TestRelativeDiatonicConstraint.policy_creator(
            ModalityType.Major, DiatonicTone('G'), 'tV', 'C:2', 'C:8')
        lower_policy_context_2 = TestRelativeDiatonicConstraint.policy_creator(
            ModalityType.Major, DiatonicTone('Ab'), 'tI', 'C:2', 'C:8')

        upper_note_1 = Note(DiatonicPitch.parse('C:5'), Duration(1, 8))
        upper_note_2 = Note(DiatonicPitch.parse('D:5'), Duration(1, 8))
        lower_note_1 = ContextualNote(
            lower_policy_context_1,
            Note(DiatonicPitch.parse('F#:5'), Duration(1, 8)))
        lower_note_2 = ContextualNote(lower_policy_context_2)

        p_map = dict([(upper_note_1, lower_note_1),
                      (upper_note_2, lower_note_2)])

        policy = RelativeDiatonicConstraint(upper_note_1, upper_note_2,
                                            Interval(3, IntervalType.Minor),
                                            Interval(3, IntervalType.Major))

        v_result = policy.values(p_map, upper_note_2)

        for note in v_result:
            logging.debug(note)

        pitches = [note.diatonic_pitch for note in v_result]
        assert {str(p) for p in pitches} == {'Eb:5', 'F:5', 'G:5', 'Ab:5'}

        logging.debug('End test_across_tonalities.')
Example #7
0
def shift_change_modal_index_modality_and_shift():
    print('----- Shift Change Modal Index, modality, shift Example -----')
    source_expression = '{<C-Major: I> iC:4 C qD E <:IV> iF G hA <:V> ig b qf g <:VI> ie e qd ic d <:i> h@c}'

    t_shift = TShift.create(source_expression)
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift to modal index 1 (dorian)')
    target_line, target_hct = t_shift.apply(root_shift_interval=TonalInterval.parse('M:2'), modal_index=1)

    print_line(target_line)
    print_hct(target_hct)
    print()

    t_shift = TShift(target_line, target_hct)

    print('Shift P:4 to modal index 2 (phrygian) of MelodicMinor')
    target_line, target_hct = t_shift.apply(root_shift_interval=TonalInterval.parse('P:4'),
                                            range_modality_type=ModalityType.MelodicMinor, modal_index=2)

    print_line(target_line)
    print_hct(target_hct)
    print()
Example #8
0
    def test_lower_pitch(self):

        pitch = DiatonicPitch(4, 'C')
        for i in range(1, 13):
            for interval_type in TestInterval.INTERVAL_TYPES:
                if i == 1 and interval_type == IntervalType(IntervalType.Diminished):
                    continue
                if i == 1 or i == 4 or i == 5 or i == 8 or i == 11 or i == 12:
                    if interval_type == IntervalType(IntervalType.Minor) or \
                       interval_type == IntervalType(IntervalType.Major):
                        continue 
                else:
                    if interval_type == IntervalType(IntervalType.Perfect):
                        continue
                try:
                    interval = Interval(i, interval_type)
                    p = interval.get_start_pitch(pitch)
                except Exception:
                    e = sys.exc_info()[0]
                    print('exception {0} for interval i={1} interval={2} pitch={3}'.format(e, i, interval_type, pitch))
                    raise Exception('exception {0} for creating interval i={1} type={2} pitch={3}'.format(e, i,
                                                                                                          interval_type,
                                                                                                          pitch))

                print(p)
                assert p.diatonic_distance() == 4 * 7 + 0 - (i - 1)
Example #9
0
    def test_for_book_example_2(self):
        print('----- test for book example 2 -----')

        source_instance_expression = '{<A-Major:i> [sA:4 A A A] qA:4 [iA:4 A] <:iv> qA:4 [sA:4 A A A] qA:4}'
        target_instance_expression = '{<G-Major:i> wA:4 <:iv> wA:4}'
        lge = LineGrammarExecutor()

        source_instance_line, source_instance_hct = lge.parse(
            source_instance_expression)

        actors = source_instance_line.get_all_notes()
        for a in actors:
            print("{0}".format(a))

        target_instance_line, target_instance_hct = lge.parse(
            target_instance_expression)
        target_hcs = target_instance_hct.hc_list()
        for hc in target_hcs:
            print("{0}".format(hc))

        pitch_range = PitchRange(
            DiatonicPitch.parse('C:4').chromatic_distance,
            DiatonicPitch.parse('C:6').chromatic_distance)

        p_map = PMap.create(source_instance_expression, pitch_range,
                            [('G-Major:I', Duration(3, 4)),
                             ('G-Major:IV', Duration(3, 4))])

        actors = p_map.actors

        policies = OrderedSet()
        policies.add(
            StepSequenceConstraint(
                [actors[0], actors[1], actors[2], actors[3]], [1, 1, 1]))
        policies.add(ChordalPitchConstraint(actors[0]))
        policies.add(ChordalPitchConstraint(actors[4]))
        policies.add(ChordalPitchConstraint(actors[8]))
        policies.add(
            StepSequenceConstraint(
                [actors[8], actors[9], actors[10], actors[11]], [1, -1, -1]))
        policies.add(EqualPitchConstraint([actors[0], actors[12]]))
        policies.add(EqualPitchConstraint([actors[4], actors[7]]))
        policies.add(
            RelativeDiatonicConstraint(actors[4], actors[5],
                                       Interval(3, IntervalType.Major),
                                       Interval(1, IntervalType.Perfect)))
        policies.add(StepSequenceConstraint([actors[5], actors[6]], [-1]))

        # policies.add(ChordalPitchConstraint(actors[7]))

        solver = PitchConstraintSolver(policies)

        full_results, partial_results = solver.solve(p_map)
        print('Results has {0} results.'.format(len(full_results)))

        for pm in full_results:
            if str(pm[actors[7]].note.diatonic_pitch) == 'D:4' and str(
                    pm[actors[0]].note.diatonic_pitch) == 'G:4':
                print("{0}".format(pm))
Example #10
0
    def test_inversion(self):
        interval_strs = ['d:1', 'P:1', 'A:1', 'd:2', 'm:2', 'M:2', 'A:2', 'd:3', 'm:3', 'M:3', 'A:3',
                         'd:4', 'P:4', 'A:4', 'd:5', 'P:5', 'A:5',
                         'd:6', 'm:6', 'M:6', 'A:6', 'd:7', 'm:7', 'M:7', 'A:7', 'd:8', 'P:8', 'A:8']
        answers = ['A:8', 'P:8', 'd:8', 'A:7', 'M:7', 'm:7', 'd:7', 'A:6', 'M:6', 'm:6', 'd:6',
                   'A:5', 'P:5', 'd:5', 'A:4', 'P:4', 'd:4',
                   'A:3', 'M:3', 'm:3', 'd:3', 'A:2', 'M:2', 'm:2', 'd:2', 'A:1', 'P:1', 'd:1']
        intervals = [Interval.parse(s) for s in interval_strs]
        print('+++++')
        for interval, answer in zip(intervals, answers):
            print('{0} --> {1}'.format(interval, interval.inversion()))
            assert str(interval.inversion()) == answer
        print('-----')

        interval = Interval.parse('A:12')
        inversion = interval.inversion()
        print('int={0} inv={1}'.format(interval, inversion))
        
        # Test augmented and negative intervals
        interval_strs = ['A:15', 'P:15', 'd:15', 'A:14', 'M:14', 'm:14', 'd:14', 'A:13', 'M:13', 'm:13', 'd:13',
                         'A:12', 'P:12', 'd:12',
                         'A:11', 'P:11', 'd:11', 'A:10', 'M:10', 'm:10', 'd:10', 'A:9', 'M:9', 'm:9', 'd:9',
                         ]
        answers = ['d:1', 'P:1', 'A:1', 'd:2', 'm:2', 'M:2', 'A:2', 'd:3', 'm:3', 'M:3', 'A:3', 'd:4', 'P:4', 'A:4',
                   'd:5', 'P:5', 'A:5', 'd:6', 'm:6', 'M:6', 'A:6', 'd:7', 'm:7', 'M:7', 'A:7']
        intervals = [Interval.parse(s) for s in interval_strs]
        print('+++++')
        for interval, answer in zip(intervals, answers):
            print('{0} --> {1}  {2}'.format(interval, interval.inversion(), answer))
            assert str(interval.inversion()) == answer
        print('-----')
        
        interval_strs = ['-d:1', '-P:1', '-A:1', '-d:2', '-m:2', '-M:2', '-A:2', '-d:3', '-m:3', '-M:3', '-A:3',
                         '-d:4', '-P:4', '-A:4', '-d:5', '-P:5', '-A:5',
                         '-d:6', '-m:6', '-M:6', '-A:6', '-d:7', '-m:7', '-M:7', '-A:7', '-d:8', '-P:8', '-A:8']
        answers = ['d:8', 'P:8', 'A:8', '-A:7', '-M:7', '-m:7', '-d:7', '-A:6', '-M:6', '-m:6', '-d:6', '-A:5',
                   '-P:5', '-d:5', '-A:4', '-P:4', '-d:4',
                   '-A:3', '-M:3', '-m:3', '-d:3', '-A:2', '-M:2', '-m:2', '-d:2', 'd:1', 'P:1', 'A:1']
        intervals = [Interval.parse(s) for s in interval_strs]
        print('+++++')
        for interval, answer in zip(intervals, answers):
            print('{0} --> {1}'.format(interval, interval.inversion()))
            assert str(interval.inversion()) == answer
        print('-----')
        
        interval_strs = ['-A:15', '-P:15', '-d:15', '-A:14', '-M:14', '-m:14', '-d:14',
                         '-A:13', '-M:13', '-m:13', '-d:13', '-A:12', '-P:12', '-d:12',
                         '-A:11', '-P:11', '-d:11', '-A:10', '-M:10', '-m:10', '-d:10', '-A:9', '-M:9', '-m:9', '-d:9',
                         ]
        answers = ['A:1', 'P:1', 'd:1', 'd:2', 'm:2', 'M:2', 'A:2', 'd:3', 'm:3', 'M:3', 'A:3', 'd:4', 'P:4', 'A:4',
                   'd:5', 'P:5', 'A:5', 'd:6', 'm:6', 'M:6', 'A:6', 'd:7', 'm:7', 'M:7', 'A:7']
        intervals = [Interval.parse(s) for s in interval_strs]
        print('+++++')
        for interval, answer in zip(intervals, answers):
            print('{0} --> {1}  {2}'.format(interval, interval.inversion(), answer))
        print('-----')
Example #11
0
 def test_plus_equals(self):
     a = Interval(3, IntervalType(IntervalType.Major))
     
     a += Interval(3, IntervalType(IntervalType.Minor))
     assert a == (Interval(5, IntervalType(IntervalType.Perfect)))
     
     a = Interval(2, IntervalType(IntervalType.Diminished))  
     
     with self.assertRaises(Exception):
         a += Interval(3, IntervalType(IntervalType.Minor))
Example #12
0
 def test_interval_exception(self):
     i1 = Interval.parse('d:4')
     i2 = Interval.parse('d:3')
     try:
         i = i1 + i2
     except IntervalException as e:
         print('caught exception ' + str(e))
         assert e
     else:
         assert i is not None
Example #13
0
    def parse(chord_string):
        """
        Parse an input string into a TertialChordTemplate.
        
        Args:
          chord_string: string input representing chord
        Returns:
          TertianChordTemplate       
        """
        if not chord_string:
            raise TertianChordException('Unable to parse chord string to completion: {0}'.format(chord_string))
        m = TertianChordTemplate.TERTIAN_PATTERN.match(chord_string)
        if not m:
            raise TertianChordException('Unable to parse chord string to completion: {0}'.format(chord_string))

        scale_degree = m.group(TertianChordTemplate.GROUP_SCALE_DEGREE)
        if scale_degree:
            scale_degree = ChordTemplate.SCALE_DEGREE_MAP[scale_degree]
        if m.group(TertianChordTemplate.GROUP_DIATONIC_TONE) is not None:
            diatonic_basis = DiatonicTone(m.group(TertianChordTemplate.GROUP_DIATONIC_TONE))
        else:
            diatonic_basis = None
        chord_name = m.group(TertianChordTemplate.GROUP_CHORD)
        chord_type = None
        if chord_name:
            chord_type = TertianChordType.to_type(chord_name)
        inversion_text = m.group(TertianChordTemplate.GROUP_INVERSION)
        inversion_tension = m.group(TertianChordTemplate.INVERSION_TENSION)
        inversion_interval = None
        inversion = None
        if inversion_tension:
            tensions_parse = TertianChordTemplate.INVERSE_TENSION_PATTERN.findall(inversion_tension)
            for tension in tensions_parse:  # should only be 1
                aug = DiatonicTone.AUGMENTATION_OFFSET_MAPPING[tension[0]]
                interval_type = Interval.available_types(int(tension[1]))[aug]
                inversion_interval = Interval(int(tension[1]), interval_type)
                logging.info('inversion_interval = {0}'.format(str(inversion_interval)))
        elif inversion_text:
            inversion = int(inversion_text)
        else:
            inversion = 1

        tensions = []
        if m.group(TertianChordTemplate.GROUP_TENSIONS):
            tensions_parse = TertianChordTemplate.TENSION_PATTERN.findall(m.group(TertianChordTemplate.GROUP_TENSIONS))
            for tension in tensions_parse:
                aug = DiatonicTone.AUGMENTATION_OFFSET_MAPPING[tension[2]]
                if aug not in Interval.available_types(int(tension[3])):
                    raise TertianChordException('Invalid interval specification for tension \'{0}\''.format(tension[0]))
                interval_type = Interval.available_types(int(tension[3]))[aug]
                interval = Interval(int(tension[3]), interval_type)
                tensions.append(interval)

        return TertianChordTemplate(diatonic_basis, scale_degree, chord_type, tensions, inversion, inversion_interval)
Example #14
0
 def test_various(self):
     dta = DiatonicPitch(2, 'B#')
     dtb = DiatonicPitch(3, 'C')
     interval = Interval.create_interval(dta, dtb)
     semitones = interval.semitones()
     print('Interval "{0}" based on {1} and {2} has {3} semitones'.format(interval, dta, dtb, semitones))
     
     # get a major 3rd pitch
     interval = Interval.create_interval(DiatonicPitch(2, 'E'), DiatonicPitch(2, 'G#'))
     end_pitch = interval.get_end_pitch(DiatonicPitch(4, 'E'))
     print(end_pitch)
     assert str(end_pitch) == 'G#:4'
Example #15
0
    def find_tertian_chords(self):
        results = list()
        for chord_type, interval_list in TertianChordTemplate.TERTIAN_CHORD_TYPE_MAP.items(
        ):
            chord_tones = list()
            for interval in interval_list:
                chord_tones.append(interval.get_end_tone(self.root_tone))
            if set(chord_tones) <= set(self.chord_tones):
                if self.chord_tones[0] not in chord_tones:
                    continue
                ct = TertianChordType(chord_type)
                results.append((ct, chord_tones))

        results.sort(key=lambda x: len(x[1]), reverse=True)
        rr = [x for x in results if len(x[1]) == len(results[0][1])]
        chords = list()
        if len(rr):
            for answer in rr:
                # inversion computed - must be a chordal tone
                # if self.chord_tones[0] not in answer[1]:
                #    raise Exception('Inversion tone \'{0}\' must be a chord tone in [{1}]'.format(
                #        self.chord_tones[0], ', '.join(v.diatonic_symbol for v in answer[1])))
                inversion = answer[1].index(self.chord_tones[0]) + 1
                tensions = list()
                remainder = set(self.chord_tones) - set(answer[1])
                for r in remainder:
                    p1 = DiatonicPitch(4, self.root_tone)
                    p2 = DiatonicPitch(
                        5 if DiatonicPitch.crosses_c(self.root_tone, r) else 4,
                        r)
                    interval = Interval.create_interval(p1, p2)
                    if interval.diatonic_distance < 5:  # We don't want M:13 nor M:14
                        interval = Interval(interval.diatonic_distance + 8,
                                            interval.interval_type)
                    tensions.append(interval)

                if self.tonality is not None:
                    index = self.tonality.annotation.index(self.root_tone) \
                        if self.root_tone in self.tonality.annotation else None
                    if index is None:
                        continue
                        # raise Exception('Root tone {0} is not in tonality {1}'.format(self.root_tone, self.tonality))
                    template = TertianChordTemplate(None, index + 1, answer[0],
                                                    tensions, inversion)
                else:
                    template = TertianChordTemplate(self.root_tone, None,
                                                    answer[0], tensions,
                                                    inversion)
                chords.append(TertianChord(template, self.tonality))

        return chords
Example #16
0
 def __create_chord_on_root_no_base_intervals(self, diatonic_tone):
     # Assume MM or MajMaj
     self.chord_basis = []
     current_tone = diatonic_tone
     intervals = [
         Interval(1, IntervalType.Perfect),
         Interval(4, IntervalType.Perfect),
         Interval(4, IntervalType.Perfect)
     ]
     for i in range(0, 3):
         tone = intervals[i].get_end_tone(current_tone)
         self.__tones.append((tone, intervals[i]))
         self.chord_basis.append(intervals[i])
         current_tone = tone
Example #17
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 #18
0
 def test_parse(self):
     interval = Interval.parse('P:5')
     assert str(interval) == 'P:5'
     
     assert str(Interval.parse('A:8')) == 'A:8'
     assert str(Interval.parse('d:8')) == 'd:8'
     assert str(Interval.parse('M:3')) == 'M:3'
     assert str(Interval.parse('m:6')) == 'm:6'
     
     assert Interval.parse('-d:1') == Interval.parse('A:1')
     assert Interval.parse('-A:1') == Interval.parse('d:1')
Example #19
0
    def remap_chord(self, hc):
        from tonalmodel.interval import Interval as TonalInterval
        chord = hc.chord

        if not isinstance(chord, SecondaryChord):
            f = self.__hc_flip_map[hc] if hc in self.__hc_flip_map.keys() else \
                ChromaticPitchReflectionFunction(hc.tonality, self.cue_pitch, self.domain_pitch_range)
            # FlipOnTonality(hc.tonality, self.cue_pitch, self.domain_pitch_range)
            new_chord_tones = [f.tonal_function[t[0]] for t in chord.tones]
            chords = ChordClassifier.classify_all_roots(
                new_chord_tones, f.range_tonality)
            if chords is not None and len(chords) > 0:
                return chords[0]
            else:
                raise Exception(
                    'Cannot remap/classify chord {0} based on chord.'.format(
                        ', '.join(
                            str(t.diatonic_symbol) for t in new_chord_tones)))
        else:
            if hc in self.__hc_flip_map.keys():
                secondary_function = self.__hc_flip_map[hc].tonal_function
            else:
                secondary_function = self._build_secondary_flip_function(
                    hc).tonal_function

            base_f = self._build_chromatic_reflection(hc)
            root_mapped_tonality = base_f.range_tonality
            mapped_denominator = TonalInterval.calculate_tone_interval(
                root_mapped_tonality.root_tone, secondary_function.
                range_tonality.root_tone).diatonic_distance + 1

            # Alternatively, in the else part, we could have done:
            #   secondary_function = f.tonal_function.create_adapted_function(secondary_tonality, secondary_tonality)
            # but to be consistent within the logic, we go for the reflection_tests constructiobn of
            # the secondary function
            # as embodied in tFlip._build_secondary_flip_function()

            new_chord_tones = [secondary_function[t[0]] for t in chord.tones]
            secondary_tonality = secondary_function.range_tonality
            chords = ChordClassifier.classify_all_roots(
                new_chord_tones, secondary_tonality)

            if chords is not None and len(chords) > 0:
                new_chord = chords[0]
            else:
                raise Exception(
                    'Cannot remap/classify chord {0} based on chord.'.format(
                        ', '.join(
                            str(t.diatonic_symbol) for t in new_chord_tones)))

            # mapped_numerator = TonalInterval.calculate_tone_interval(
            #    new_chord.root_tone,
            #    secondary_function.range_tonality.root_tone).diatonic_distance + 1
            secondary_chord_template = SecondaryChordTemplate(
                new_chord.chord_template, mapped_denominator,
                secondary_tonality.modality.modality_type)
            secondary_chord = SecondaryChord(secondary_chord_template,
                                             root_mapped_tonality,
                                             secondary_function.range_tonality)
            return secondary_chord
Example #20
0
    def create_instrument(inst_node, parent):
        low = high = ''
        up_down = None
        transpose_interval = None
        articulations = []
        for c in inst_node:
            if c.tag == 'Range':
                for lh in c:
                    if lh.tag == 'Low':
                        low = lh.text
                    elif lh.tag == 'High':
                        high = lh.text
            elif c.tag == 'Transpose':
                updown_txt = c.get('direction')
                if updown_txt != 'up' and updown_txt != 'down':
                    raise Exception(
                        'Illegal transpose up/down must be \'up\' or \'down\'  now \'{0}\''
                        .format(updown_txt))
                up_down = updown_txt == 'up'
                transpose_interval = Interval.parse(c.get('interval'))
            elif c.tag == 'Articulations':
                articulations = InstrumentCatalog._parse_articulations(c)

        instrument = Instrument(inst_node.get('name'), inst_node.get('key'),
                                low, high, up_down, transpose_interval, parent)
        instrument.extend_articulations(articulations)
        return instrument
Example #21
0
    def __create_chord_on_diatonic_without_type(self, diatonic_tone):
        from tonalmodel.tonality import Tonality
        from tonalmodel.modality import ModalityType
        from harmonicmodel.tertian_chord_template import TertianChordTemplate
        diatonic_tonality = Tonality.create(ModalityType.Major, diatonic_tone)
        tone_scale = diatonic_tonality.annotation

        self.chord_basis = []
        base_tone = None
        for i in range(0, 3):
            tone = tone_scale[(2 * i) % (len(tone_scale) - 1)]
            if i == 0:
                base_tone = tone

            pitch_a = DiatonicPitch(1, diatonic_tone)
            b_octave = 2 if base_tone.diatonic_index > tone.diatonic_index else 1
            pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol)
            interval = Interval.create_interval(pitch_a, pitch_b)
            self.chord_basis.append(interval)

            self.__tones.append((tone, interval))
        self.__set_inversion()

        self.__chord_type = TertianChordTemplate.get_chord_type(
            self.chord_basis)
Example #22
0
def cue_examples():
    print('--------------  Cue examples  --------------------------')
    source_expression = '{<Bb-Major: I> sBb:4 A G F qEb D sF g iA i@Bb sF <:IVMaj7> ' \
                        'ir Eb sEb F G A iBb sEb:5 F i@Eb C ' \
                        '<:IIIMin7> sR F:5 Eb D C Bb:4 C:5 D i@Eb sC sr G:4 A G <:I> sG:5 F Eb D D C Bb:4 A ir q@G}'

    t_flip = TDiatonicReflection.create(source_expression,
                                        DiatonicPitch.parse('Eb:4'))
    print('Flip examples based on:')
    print_line(t_flip.source_line)
    print_hct(t_flip.source_hct)
    print()

    print('Flip on Eb:4 (Figure 16.12)')
    target_line, target_hct = t_flip.apply()

    print_line(target_line)
    print_hct(target_hct)
    print()

    print('Shift up an octave (Figure 16.13)')
    t_shift = TShift(target_line, target_hct, TonalInterval.parse('P:8'))
    final_line, final_hct = t_shift.apply()
    print_line(final_line)
    print_hct(final_hct)
Example #23
0
 def calculate_interval(tone_1, tone_2, near_interval):
     """
     The purpose of this method is to find a transform interval close to 'near_interval'.  This is used
     to determine the transform interval for secondary chords, wherein the obvious jump does not match
     transform interval (e.g. major to minor scale), and as well must be adjusted for the number and sign of
     octaves nearest interval may have.  See test cases: test_modal_secondary_tonality where best_interval == d:4
     and near_interval == P:4 E-MM to Ab-MM using V/III chord.
     :param tone_1:
     :param tone_2:
     :param near_interval:
     :return:
     """
     sign = TShift._sign(near_interval.chromatic_distance)
     start, increment = (1, 1) if sign == 1 else (7, -1)
     p1 = DiatonicPitch(start, tone_1)
     oct_2 = start
     best = 100000
     best_interval = None
     while abs(oct_2) < 7:
         p2 = DiatonicPitch(oct_2, tone_2)
         i = TonalInterval.create_interval(p1, p2)
         if TShift._sign(i.chromatic_distance) != sign:
             oct_2 = oct_2 + sign
             continue
         diff = abs(near_interval.chromatic_distance - i.chromatic_distance)
         if diff < best:
             best = diff
             best_interval = i
         else:
             break
         oct_2 = oct_2 + sign
     return best_interval
Example #24
0
    def test_hct_simple_shift(self):
        print('----- test_hct_simple_shift -----')

        line_str = '{<C-Major: I> C:4 E F D <:IV> F A <:V> G D <:VI> a c b a}'
        lge = LineGrammarExecutor()
        target_line, target_hct = lge.parse(line_str)

        root_shift_interval = TonalInterval.create_interval('C:4', 'G:4')

        tshift = TShift(target_line, target_hct, root_shift_interval)

        temporal_extent = Interval(Fraction(1, 1), Fraction(2, 1))
        tshift.apply(temporal_extent, as_copy=False)
        TestTShift.print_notes(target_line)
        TestTShift.print_hct(target_hct)

        notes = target_line.get_all_notes()
        assert 12 == len(notes)
        assert 'C:5' == str(notes[4].diatonic_pitch)
        assert 'E:5' == str(notes[5].diatonic_pitch)
        assert 'D:5' == str(notes[6].diatonic_pitch)
        assert 'A:4' == str(notes[7].diatonic_pitch)

        hc_list = target_hct.hc_list()
        assert len(hc_list) == 4
        assert hc_list[1].chord.chord_template.scale_degree == 4
        assert {t[0].diatonic_symbol
                for t in hc_list[1].chord.tones} == {'C', 'E', 'G'}
        assert hc_list[1].chord.chord_template.inversion == 1
        assert hc_list[1].tonality.modal_index == 0
        assert hc_list[1].tonality.basis_tone.diatonic_symbol == 'G'
        assert hc_list[1].tonality.root_tone.diatonic_symbol == 'G'
        assert hc_list[1].tonality.modality_type == ModalityType.Major
        assert hc_list[1].chord.chord_type.value == TertianChordType.Maj
Example #25
0
    def test_diatonic_modal_indexed_function(self):
        t_domain = Tonality.create(ModalityType.Major, DiatonicTone('C'))
        i = Interval(2, IntervalType.Major)
        r = PitchRange.create('E:3', 'E:7')

        f = CrossTonalityShiftPitchFunction(t_domain, r, i, modal_index=4)

        print('f={0}'.format(f))

        TestCrossTonalityShiftPitchFunction.print_map('test_diatonic_function',
                                                      f)

        t = f.tonal_function
        print(t)

        # test diatonic maps
        assert 'D:4' == str(f['C:4'])
        assert 'E:4' == str(f['D:4'])
        assert 'F#:4' == str(f['E:4'])
        assert 'G:4' == str(f['F:4'])
        assert 'A:4' == str(f['G:4'])
        assert 'B:4' == str(f['A:4'])
        assert 'C:5' == str(f['B:4'])
        assert 'D:5' == str(f['C:5'])

        assert 'M:2' == str(f.root_shift_interval)
Example #26
0
 def test_all_g_same_octave_itervals(self):
     pitches = list('GABCDEF')
     octaves = [5, 5, 5, 6, 6, 6, 6]
     augs = ('bb', 'b', '',  '#', '##')
     example_count = 0
     exception_items = (1, 2, 5, 10, 15, 16, 20, 21, 25, 30, 31, 35)
     for i in range(0, 7):
         pitch = pitches[i]
         octave = octaves[i]
         for aug in augs:
             example_count += 1
             a = DiatonicPitch(5, 'G')
             b = DiatonicPitch(octave, pitch + aug)
             try:
                 interval = Interval.create_interval(a, b)
                 print('({0}): {1}, {2}) --> {3}'.format(example_count, a, b, interval))
                 
                 dist = b.diatonic_tone.diatonic_index - a.diatonic_tone.diatonic_index
                 if dist < 0:
                     dist += 7
                 assert dist == interval.diatonic_distance
                 assert b.chromatic_distance - a.chromatic_distance == interval.chromatic_distance
             except Exception as e:
                 print('Exception ({0}): ({1}, {2}) : {3}'.format(example_count, a, b, e))
                 assert example_count in exception_items, \
                     'ASSERT ERROR ({0}): ({1}, {2}) : {3}'.format(example_count, a, b, e)
Example #27
0
    def __create_chord_on_diatonic_tonality(self, diatonic_tone,
                                            diatonic_tonality):
        if not diatonic_tonality:
            raise Exception(
                "Cannot base quartal chord on tone {0} without tonality.".
                format(diatonic_tone.diatonic_symbol))
        # The tonality must include this tone.
        tone_scale = diatonic_tonality.annotation
        found_index = -1
        for i in range(0, len(tone_scale)):
            if diatonic_tone == tone_scale[i]:
                found_index = i
                break
        if found_index == -1:
            raise Exception(
                "For quartal chord based on tone {0}, tone must be in given tonality {1}"
                .format(diatonic_tone.diatonic_symbol, diatonic_tonality))
        self.chord_basis = []
        basis_tone = tone_scale[found_index]
        for i in range(0, 3):
            tone = tone_scale[(found_index + 3 * i) % (len(tone_scale) - 1)]
            pitch_a = DiatonicPitch(1, basis_tone.diatonic_symbol)
            b_octave = 2 if basis_tone.diatonic_index > tone.diatonic_index else 1
            pitch_b = DiatonicPitch(b_octave, tone.diatonic_symbol)
            interval = Interval.create_interval(pitch_a, pitch_b)
            self.chord_basis.append(interval)

            self.__tones.append((tone, interval))
            basis_tone = tone
Example #28
0
def example2():
    print('----- Debug meaning of modal index change and hct -----')

    # example tonality with modal index
    # Create a harmonic minor tonality of some basis root which as Mixolydian has F as the root.
    #       The answer is Bb-HarmonicMinor F(4)

    source_expression = '{<C-Major: I> iC:4}'

    t_shift = TShift.create(source_expression)
    print('Shift examples based on:')
    print_line(t_shift.source_line)
    print()

    print('Shift to modal index 4 (Mixolydian)')
    # This makes C the Mixolydian of F-Major
    target_line, target_hct = t_shift.apply(modal_index=4)
    print_line(target_line)
    print_hct(target_hct)
    print()

    # if you wanted G Mixolydian based on C
    print('Shift as if moving to modal index 4 (Mixolydian) in C')
    target_line, target_hct = t_shift.apply(
           root_shift_interval=TonalInterval.parse('P:5'), modal_index=4)

    print_line(target_line)
    print_hct(target_hct)
    print()
    def _build_tunnel_constraints(self, source_to_target, tag_map):
        if tag_map is None or len(tag_map) == 0:
            return []

        one_id = next(iter(tag_map.keys()))
        source_note = self.source_line.get_all_notes()[one_id]
        target_pitch = tag_map[one_id]

        mvmt_interval = Interval.create_interval(source_note.diatonic_pitch,
                                                 target_pitch)

        constraints = list()
        note_annotations = self.source_analysis.note_annotation
        for annotation in note_annotations:
            if annotation.note.diatonic_pitch is None:
                continue
            target_note = source_to_target[annotation.note]

            dest_ctr_pitch = mvmt_interval.get_end_pitch(
                annotation.note.diatonic_pitch)
            low_pitch = self.tunnel_half_interval.get_start_pitch(
                dest_ctr_pitch)
            high_pitch = self.tunnel_half_interval.get_end_pitch(
                dest_ctr_pitch)
            p_range = PitchRange.create(low_pitch, high_pitch)

            constraint = PitchRangeConstraint([target_note], p_range)
            constraints.append(constraint)

        return constraints
Example #30
0
    def _build_primary_map(self):
        domain_scale = self.domain_tonality.annotation[:-1]

        tonal_map = dict()
        if self.reflect_type == FlipType.CenterTone:
            for tone in domain_scale:
                interval = Interval.calculate_tone_interval(tone, self.cue_tone)
                end_tone = interval.get_end_tone(self.cue_tone)
                tonal_map[tone] = end_tone
        else:
            if self.reflect_type == FlipType.LowerNeighborOfPair:
                lower_index = domain_scale.index(self.cue_tone)
                upper_index = (lower_index + 1) % len(domain_scale)
            else:
                upper_index = domain_scale.index(self.cue_tone)
                lower_index = (upper_index - 1) % len(domain_scale)
            tonal_map[domain_scale[upper_index]] = domain_scale[lower_index]
            tonal_map[domain_scale[lower_index]] = domain_scale[upper_index]

            last_lower = domain_scale[lower_index]
            last_upper = domain_scale[upper_index]
            for i in list(reversed(range(0, lower_index))):
                new_lower = domain_scale[i]
                interval = Interval.calculate_tone_interval(new_lower, last_lower)
                new_upper = interval.get_end_tone(last_upper)
                tonal_map[new_lower] = new_upper
                last_lower = new_lower
                last_upper = new_upper

            last_lower = domain_scale[lower_index]
            last_upper = domain_scale[upper_index]
            for i in list(range((upper_index + 1), len(domain_scale))):
                new_upper = domain_scale[i]
                interval = Interval.calculate_tone_interval(last_upper, new_upper)
                new_lower = interval.negation().get_end_tone(last_lower)
                tonal_map[new_upper] = new_lower
                last_lower = new_lower
                last_upper = new_upper

        range_tones = list(reversed([tonal_map[tone] for tone in domain_scale]))
        first_tone = range_tones[-1]
        range_tones = [first_tone] + range_tones[:-1]

        # Determine the tonality of the range
        range_tonality = Tonality.find_tonality(range_tones)

        return tonal_map, range_tonality