Пример #1
0
    def values(self, p_map, v_note):
        if v_note != self.actor_note:
            raise Exception("Illegal v_note {0} for constraints".format(v_note))
        if p_map[v_note].note is not None:
            if p_map[v_note].note.diatonic_pitch.diatonic_tone == self.tone:
                return {p_map[v_note].note}
            raise Exception('Fixed Tone Policy Violated has {0} should be {1}'.format(
                p_map[v_note].note.diatonic_pitch.diatonic_tone, self.tone))

        contextual_note = p_map[self.actor_note]
        policy_context = contextual_note.policy_context
        pitch_range = contextual_note.policy_context.pitch_range
        start_partition = max(ChromaticScale.index_to_location(pitch_range.start_index)[0] - 1, 0)
        end_partition = min(ChromaticScale.index_to_location(pitch_range.end_index)[0] + 1,
                            ChromaticScale.CHROMATIC_END[0])

        # Try to find that tone in target's tonality/scale.
        tone = self.tone
        for t_str in self.tone.enharmonics():
            t = DiatonicToneCache.get_tone(t_str)
            for scale_tone in PitchScale(policy_context.harmonic_context.tonality,
                                         policy_context.pitch_range).tone_scale:
                if scale_tone == t:
                    tone = t
                    break

        valid_set = OrderedSet()
        for i in range(start_partition, end_partition + 1):
            pitch = DiatonicPitch(i, tone)
            if pitch_range.is_pitch_inbounds(pitch):
                note = Note(pitch, self.actor_note.base_duration, self.actor_note.num_dots)
                valid_set.add(note)

        return valid_set
Пример #2
0
    def verify(self, parameter_map):
        """
        Verify that the note pitch of the first proxy and the note pitch of the second proxy meet the constraints
        diatonic distance.
        :param parameter_map: 
        :return: 
        """
        first_contextual_note = parameter_map[self.note_one]
        second_contextual_note = parameter_map[self.note_two]
        if str(first_contextual_note.policy_context.harmonic_context.tonality) != \
                str(second_contextual_note.policy_context.harmonic_context.tonality):
            raise Exception(
                'Note one and two of tonal step constraints must match')

        pitch_scale = PitchScale(
            first_contextual_note.policy_context.harmonic_context.tonality,
            first_contextual_note.policy_context.pitch_range)
        scale = pitch_scale.pitch_scale

        if first_contextual_note.note is None or second_contextual_note.note is None:
            return False

        first_pitch = first_contextual_note.note.diatonic_pitch
        second_pitch = second_contextual_note.note.diatonic_pitch

        first_index = scale.index(
            first_pitch) if first_pitch in scale else None
        second_index = scale.index(
            second_pitch) if second_pitch in scale else None

        if first_index is None or second_index is None:
            return False

        return second_index - first_index == self.n_steps * (
            1 if self.up_down == PitchStepConstraint.UP else -1)
Пример #3
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)
Пример #4
0
    def _build_pitch_segment_dict(tonality, scale_origin_str):
        origin_pitch = DiatonicPitch.parse(scale_origin_str)
        pitch_range = PitchRange.create(
            '{0}:{1}'.format(origin_pitch.diatonic_tone.diatonic_symbol, origin_pitch.octave),
            '{0}:{1}'.format(origin_pitch.diatonic_tone.diatonic_symbol, origin_pitch.octave + 1))

        scale = PitchScale(tonality, pitch_range).pitch_scale[0: -1]

        d = OrderedDict()
        for p in scale:
            d[p.diatonic_tone] = p.octave

        return d
Пример #5
0
 def all_tonal_pitches(self, v_note):
     """
     Compute all tonal pitches for the target of a v_note in p_map.
     :param v_note:
     :return:
     """
     target = self[v_note]
     if target is None:
         raise Exception(
             'Internal construction error, v_note target is None.')
     policy_context = target.policy_context
     return PitchScale.compute_tonal_pitches(
         policy_context.harmonic_context.tonality,
         policy_context.pitch_range)
    def compute_result(self, arg_contextual_note, target_contextual_note,
                       down_steps, up_steps):
        arg_pitch = arg_contextual_note.note.diatonic_pitch

        pitches = PitchScale.compute_tonal_pitch_range(
            target_contextual_note.policy_context.harmonic_context.tonality,
            arg_pitch, down_steps, up_steps)

        result = OrderedSet()
        for pitch in pitches:
            result.add(
                Note(pitch, self.note_two.base_duration,
                     self.note_two.num_dots))

        return result
    def verify(self, parameter_map):
        """
        Verify that p_map has values satisfying the constraint.
        :param parameter_map: 
        :return: 
        """
        first_contextual_note = parameter_map[self.note_one]
        second_contextual_note = parameter_map[self.note_two]
        if first_contextual_note.note is None or second_contextual_note.note is None:
            return False

        pitches = PitchScale.compute_tonal_pitch_range(
            second_contextual_note.policy_context.harmonic_context.tonality,
            first_contextual_note.note.diatonic_pitch, self.lower_steps,
            self.upper_steps)

        return second_contextual_note.note.diatonic_pitch in pitches
Пример #8
0
    def __init__(self,
                 tonality,
                 anchor_pitch=None,
                 anchor_value=None,
                 pitch_unit=1):
        """
        Constructor.
        :param tonality: The tonality being mapped to.
        :param anchor_pitch: A DiatonicPitch, in combo with anchor_value is a sample of the mapping.
        :param anchor_value: A numeric value that maps to anchor_pitch.
        :param pitch_unit: In the linear map of value to pitches, pitch_unit is the distance between mapping values.
        """
        self.__tonality = tonality
        self.__pitch_scale = PitchScale(self.tonality,
                                        PitchRange.create('A:0',
                                                          'C:8')).pitch_scale

        self.anchor_pitch = self.pitch_scale[0] if anchor_pitch is None else \
            DiatonicPitch.parse(anchor_pitch) if isinstance(anchor_pitch, str) else anchor_pitch

        anchor_index = self.pitch_scale.index(self.anchor_pitch)
        if anchor_index == -1:
            raise Exception(
                'Anchor pitch \'{0}\' not found in pitch scale for tonality \'{1}\''
                .format(self.anchor_pitch, self.tonality))

        self.__pitch_unit = pitch_unit

        self.anchor_value = anchor_value if anchor_value is not None else anchor_index * self.pitch_unit

        # base value should map to beginning of pitch scale!
        # recall that pitch unit maps to each pitch, making the scalar scale linear in value!
        base_value = anchor_value - anchor_index * pitch_unit

        self.value_to_pitch = OrderedMap()
        self.pitch_to_value = dict()
        for i in range(0, len(self.pitch_scale)):
            pitch = self.pitch_scale[i]
            value = base_value + i * pitch_unit
            self.value_to_pitch.insert(value, pitch)
            self.pitch_to_value[pitch] = value

        PitchRangeInterpreter.__init__(self)
Пример #9
0
    def values(self, p_map, v_note):
        assigned = p_map.assigned_actors(self)
        unassigned = p_map.unassigned_actors(self)
        if v_note in unassigned:
            tonality = p_map[v_note].policy_context.harmonic_context.tonality
            pitches = PitchScale.compute_tonal_pitches(tonality,
                                                       self.pitch_range)
            answer = OrderedSet()
            for p in pitches:
                answer.add(Note(p, v_note.base_duration, v_note.num_dots))
            return answer
            # return {Note(p, v_note.base_duration, v_note.num_dots) for p in pitches}

        if v_note in assigned:
            return OrderedSet([p_map[v_note].note])

        raise Exception(
            '{0} is not in actor list for pitch range constraints.'.format(
                v_note.note))
Пример #10
0
    def compute_result(self, arg_contextual_note, target_contextual_note,
                       up_intvl, down_intvl):
        """
        
        :param arg_contextual_note: 
        :param target_contextual_note: 
        :param up_intvl: 
        :param down_intvl: 
        :return: 
        """

        starting_pitch = arg_contextual_note.note.diatonic_pitch
        chromatic_distance_start = starting_pitch.chromatic_distance - down_intvl.chromatic_distance
        chromatic_distance_end = starting_pitch.chromatic_distance + up_intvl.chromatic_distance

        r_start = max(
            chromatic_distance_start,
            target_contextual_note.policy_context.pitch_range.start_index)
        r_end = min(
            chromatic_distance_end,
            target_contextual_note.policy_context.pitch_range.end_index)

        if r_start > r_end:
            return OrderedSet()

        pitch_range = PitchRange(r_start, r_end)
        pitch_scale = PitchScale(
            target_contextual_note.policy_context.harmonic_context.tonality,
            pitch_range)

        result = OrderedSet()
        for pitch in pitch_scale.pitch_scale:
            result.add(
                Note(pitch, self.note_two.base_duration,
                     self.note_two.num_dots))
        # v_result = {Note(pitch, self.note_two.base_duration, self.note_two.num_dots)
        #             for pitch in pitch_scale.pitch_scale}

        return result
Пример #11
0
    def compute_note(p_map, assigned_note, unassigned_note):
        """
        For an assigned note and an unassigned note, return for unassigned, a note the same as assigned, but with
        pitch enharmonic to its tonality.
        :param p_map: 
        :param assigned_note: 
        :param unassigned_note: 
        :return: 
        """

        # select a pitch representation closest to the tonality, if it exists.
        policy_context = p_map[unassigned_note].policy_context
        pitch = p_map[assigned_note].note.diatonic_pitch
        for p in pitch.enharmonics():
            for t in PitchScale(policy_context.harmonic_context.tonality,
                                policy_context.pitch_range).tone_scale:
                if p.diatonic_tone == t:
                    pitch = p
                    break

        actual_note = Note(pitch, unassigned_note.base_duration,
                           unassigned_note.num_dots)
        return OrderedSet([actual_note])
    def _find_closest_tone(self, tone):
        # determine if pitch ltr is in tonality, get that tone
        ll = [t for t in self.tones if t.diatonic_letter == tone.diatonic_letter]
        if len(ll) == 1:
            return ll[0], ll[0].augmentation_offset - tone.augmentation_offset

        # Do something if len(ll) > 1
        elif len(ll) > 1:
            ll.sort(key=lambda x: abs(x.augmentation_offset - tone.augmentation_offset))
            return ll[0], ll[0].augmentation_offset - tone.augmentation_offset

        first_pitch = DiatonicPitch(3, self.tones[0])
        last_pitch = DiatonicPitch(5, self.tones[-1])
        scale = PitchScale(self.tonality, PitchRange.create(first_pitch, last_pitch))

        pitch = DiatonicPitch(4, tone)
        after_pitch = last_pitch
        before_pitch = None
        for p in scale.pitch_scale:
            if pitch.chromatic_distance <= p.chromatic_distance:
                after_pitch = p
                break
            else:
                before_pitch = p

        # distance measured FROM pitch TO near pitch (e.g., -1 before, +1 after)
        before_distance = before_pitch.chromatic_distance - pitch.chromatic_distance
        after_distance = after_pitch.chromatic_distance - pitch.chromatic_distance

        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.diatonic_tone, closest_distance
Пример #13
0
    def compute_result(self, arg_contextual_note, target_contextual_note,
                       up_down):
        """
        Compute the target note from the arg note basecd on up_down and self.n_steps.
        
        :param arg_contextual_note: Given note
        :param target_contextual_note: Note to find based on constraints.
        :param up_down: 
        :return: 
        """
        if str(arg_contextual_note.policy_context.harmonic_context.tonality) != \
                str(target_contextual_note.policy_context.harmonic_context.tonality):
            raise Exception(
                'Note one and two of tonal step constraints must match on tonality'
            )

        starting_pitch = arg_contextual_note.note.diatonic_pitch

        pitch_scale = PitchScale(
            target_contextual_note.policy_context.harmonic_context.tonality,
            target_contextual_note.policy_context.pitch_range)
        scale = pitch_scale.pitch_scale
        pitch_index = scale.index(
            starting_pitch) if starting_pitch in scale else None
        if pitch_index is None:
            return None

        end_index = pitch_index + (
            self.n_steps * (1 if up_down == PitchStepConstraint.UP else -1))
        if end_index not in range(0, len(scale)):
            return None

        end_pitch = scale[end_index]
        return OrderedSet([
            Note(end_pitch, self.note_two.base_duration,
                 self.note_two.num_dots)
        ])
Пример #14
0
    def test_compute_tonal_pitch_range(self):
        logging.debug('Start test_compute_tonal_pitch_range')

        tonality = Tonality.create(ModalityType.Major, DiatonicTone('Ab'))

        pitch = DiatonicPitch(4, 'B#')
        pitches = PitchScale.compute_tonal_pitch_range(tonality, pitch, 5, 7)
        for p in reversed(pitches):
            print(p)

        test_pitches = [
            'A:4', 'B:4', 'C:4', 'D:4', 'E:4', 'F:4', 'G:4', 'Ab:4', 'Bb:4',
            'Db:4', 'Eb:4', 'C#:4', 'D#:4', 'G#:4', 'A#:4', 'B#:4', 'Cb:4',
            'B##:4', 'Cbb:4'
        ]

        ranges = [[0, 5], [-5, 0], [-4, 2], [-5, -3], [5, 7]]

        answers = [
            '[Ab:4,Bb:4,C:5,Db:5,Eb:5,F:5,G:5]',
            '[C:4,Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[C:4,Db:4,Eb:4]',
            '[G:5,Ab:5,Bb:5]',
            '[Bb:4,C:5,Db:5,Eb:5,F:5,G:5,Ab:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5,Db:5,Eb:5]',
            '[Db:4,Eb:4,F:4]',
            '[Ab:5,Bb:5,C:6]',
            '[C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[Eb:3,F:3,G:3,Ab:3,Bb:3,C:4]',
            '[F:3,G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4]',
            '[Eb:3,F:3,G:3]',
            '[Ab:4,Bb:4,C:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[F:3,G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4,G:4]',
            '[F:3,G:3,Ab:3]',
            '[C:5,Db:5,Eb:5]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4]',
            '[Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[G:3,Ab:3,Bb:3]',
            '[Db:5,Eb:5,F:5]',
            '[F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4]',
            '[Bb:3,C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[Ab:3,Bb:3,C:4]',
            '[Db:5,Eb:5,F:5]',
            '[G:4,Ab:4,Bb:4,C:5,Db:5,Eb:5]',
            '[Bb:3,C:4,Db:4,Eb:4,F:4,G:4]',
            '[C:4,Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[Bb:3,C:4,Db:4]',
            '[Eb:5,F:5,G:5]',
            '[Ab:4,Bb:4,C:5,Db:5,Eb:5,F:5]',
            '[C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[C:4,Db:4,Eb:4]',
            '[F:5,G:5,Ab:5]',
            '[Bb:4,C:5,Db:5,Eb:5,F:5,G:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[Db:4,Eb:4,F:4]',
            '[G:5,Ab:5,Bb:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[F:3,G:3,Ab:3,Bb:3,C:4,Db:4]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4]',
            '[F:3,G:3,Ab:3]',
            '[Bb:4,C:5,Db:5]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4]',
            '[Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4,G:4]',
            '[G:3,Ab:3,Bb:3]',
            '[C:5,Db:5,Eb:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[F:3,G:3,Ab:3,Bb:3,C:4,Db:4]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4]',
            '[F:3,G:3,Ab:3]',
            '[Bb:4,C:5,Db:5]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4]',
            '[Ab:3,Bb:3,C:4,Db:4,Eb:4,F:4,G:4]',
            '[G:3,Ab:3,Bb:3]',
            '[C:5,Db:5,Eb:5]',
            '[Ab:4,Bb:4,C:5,Db:5,Eb:5,F:5]',
            '[C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[C:4,Db:4,Eb:4]',
            '[F:5,G:5,Ab:5]',
            '[Bb:4,C:5,Db:5,Eb:5,F:5,G:5]',
            '[Db:4,Eb:4,F:4,G:4,Ab:4,Bb:4]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[Db:4,Eb:4,F:4]',
            '[G:5,Ab:5,Bb:5]',
            '[C:5,Db:5,Eb:5,F:5,G:5,Ab:5]',
            '[Eb:4,F:4,G:4,Ab:4,Bb:4,C:5]',
            '[F:4,G:4,Ab:4,Bb:4,C:5,Db:5,Eb:5]',
            '[Eb:4,F:4,G:4]',
            '[Ab:5,Bb:5,C:6]',
            '[Bb:3,C:4,Db:4,Eb:4,F:4,G:4,Ab:4]',
            '[Db:3,Eb:3,F:3,G:3,Ab:3,Bb:3,C:4]',
            '[Eb:3,F:3,G:3,Ab:3,Bb:3,C:4,Db:4,Eb:4]',
            '[Db:3,Eb:3,F:3]',
            '[Ab:4,Bb:4,C:5]',
            '[Db:5,Eb:5,F:5,G:5,Ab:5,Bb:5]',
            '[F:4,G:4,Ab:4,Bb:4,C:5,Db:5]',
            '[G:4,Ab:4,Bb:4,C:5,Db:5,Eb:5,F:5]',
            '[F:4,G:4,Ab:4]',
            '[Bb:5,C:6,Db:6]',
            '[Bb:3,C:4,Db:4,Eb:4,F:4,G:4]',
            '[Db:3,Eb:3,F:3,G:3,Ab:3,Bb:3]',
            '[Eb:3,F:3,G:3,Ab:3,Bb:3,C:4,Db:4]',
            '[Db:3,Eb:3,F:3]',
            '[G:4,Ab:4,Bb:4]',
        ]

        answer_idx = 0
        for pitch_str in test_pitches:
            for r in ranges:
                pitch = DiatonicPitch.parse(pitch_str)
                pitches = PitchScale.compute_tonal_pitch_range(
                    tonality, pitch, r[0], r[1])
                answer_str = '[' + (','.join(str(p) for p in pitches)) + ']'
                # print '\'[' + (','.join(str(p) for p in pitches)) + ']\','
                answer = answers[answer_idx]
                answer_idx = answer_idx + 1
                print('{0} [{1}, {2}]: {3}'.format(pitch, r[0], r[1],
                                                   answer_str))

                assert answer == answer_str

        logging.debug('End test_compute_tonal_pitch_range')
Пример #15
0
    def test_compute_closest_scale_tones(self):
        logging.debug('Start test_compute_closest_scale_tones')

        tonality = Tonality.create(ModalityType.Major, DiatonicTone('Ab'))
        test_pitches = [
            'A:4', 'B:4', 'C:4', 'D:4', 'E:4', 'F:4', 'G:4', 'Ab:4', 'Bb:4',
            'Db:4', 'Eb:4', 'C#:4', 'D#:4', 'G#:4', 'A#:4', 'B#:4', 'Cb:4',
            'B##:4', 'Cbb:4'
        ]
        answers = [
            '[Ab:4,Bb:4]',
            '[Bb:4,C:5]',
            '[C:4]',
            '[Db:4,Eb:4]',
            '[Eb:4,F:4]',
            '[F:4]',
            '[G:4]',
            '[Ab:4]',
            '[Bb:4]',
            '[Db:4]',
            '[Eb:4]',
            '[Db:4]',
            '[Eb:4]',
            '[Ab:4]',
            '[Bb:4]',
            '[C:5]',
            '[Bb:3,C:4]',
            '[Db:5]',
            '[Bb:3]',
        ]
        for pitch_str, answer in zip(test_pitches, answers):
            pitch = DiatonicPitch.parse(pitch_str)
            closest = PitchScale.compute_closest_scale_tones(tonality, pitch)
            # print('{0} ==> [{1}]'.format(pitch, ','.join(str(p) for p in closest)))
            test_answer = '[' + (','.join(str(p) for p in closest)) + ']'
            assert answer == test_answer

        #  Do again for C major
        tonality = Tonality.create(ModalityType.HarmonicMinor,
                                   DiatonicTone('C'))
        test_pitches = [
            'C:4', 'D:4', 'E:4', 'F:4', 'G:4', 'A:4', 'B:4', 'Eb:4', 'Ab:4',
            'Db:4', 'Gb:4', 'Bb:4', 'Cb:4', 'B#:4'
        ]
        answers = [
            '[C:4]',
            '[D:4]',
            '[Eb:4,F:4]',
            '[F:4]',
            '[G:4]',
            '[Ab:4,B:4]',
            '[B:4]',
            '[Eb:4]',
            '[Ab:4]',
            '[C:4,D:4]',
            '[F:4,G:4]',
            '[Ab:4,B:4]',
            '[B:3]',
            '[C:5]',
        ]
        for pitch_str, answer in zip(test_pitches, answers):
            pitch = DiatonicPitch.parse(pitch_str)
            closest = PitchScale.compute_closest_scale_tones(tonality, pitch)
            test_answer = '[' + (','.join(str(p) for p in closest)) + ']'
            assert answer == test_answer

        logging.debug('End test_compute_closest_scale_tones')
Пример #16
0
 def test_book_example(self):
     pitch_range = PitchRange.create("c:4", "c:6")
     tonality = Tonality.create(ModalityType.MelodicMinor, 'D')
     pitch_scale = PitchScale(tonality, pitch_range)
     print('Scale = [{0}]'.format(', '.join(str(pitch) for pitch in  pitch_scale.pitch_scale)))
    def values(self, p_map, v_note):
        """
        Compute possible values for v_note's target.
        :param p_map: note-->contextual_note
        :param v_note: Note
        :return: Candidate Notes.

        Note: Here is why the intervals are reversed for solving for note_one:
              Suppose x --> [x-a, x + b].  Then for some value y, 
              for t with y-b<=t<=y+a, we have t -->[t-a, t+b], but
              from the inequalities, t-a<=y<t+b - so the reverse map is
              [y-b, y+a] <-- y, which is exactly what happens below.
        """
        if v_note == self.note_two:
            source = self.note_one
            target = self.note_two
            comparative = self.comparative
        elif v_note == self.note_one:
            source = self.note_two
            target = self.note_one
            comparative = 4 - self.comparative
        else:
            raise Exception(
                'v_note specification does not match any v_note in constraints.'
            )

        if p_map[target].note is not None:
            return {p_map[target].note}

        if p_map[source].note is None:
            answer_range = p_map[target].policy_context.pitch_range
            source_pitch = None
        else:
            # Establish a pitch range commensurate with comparative.
            qrange = p_map[target].policy_context.pitch_range
            source_pitch = p_map[source].note.diatonic_pitch

            if comparative > 2:
                answer_range = PitchRange(qrange.start_index,
                                          source_pitch.chromatic_distance)
            elif comparative < 2:
                answer_range = PitchRange(source_pitch.chromatic_distance,
                                          qrange.end_index)
            else:
                answer_range = PitchRange(source_pitch.chromatic_distance,
                                          source_pitch.chromatic_distance)

        pitches = PitchScale.compute_tonal_pitches(
            p_map[target].policy_context.harmonic_context.tonality,
            answer_range)
        if comparative == 4 and len(pitches) > 0 and source_pitch is not None and \
                source_pitch.chromatic_distance == pitches[-1].chromatic_distance:
            pitches.pop(-1)
        if comparative == 0 and len(pitches) > 0 and source_pitch is not None and \
                source_pitch.chromatic_distance == pitches[0].chromatic_distance:
            del pitches[0]

        answer = OrderedSet()
        for pitch in pitches:
            answer.add(Note(pitch, target.base_duration, target.num_dots))
        return answer
    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
Пример #19
0
    def values(self, p_map, v_note):
        """

        :param p_map:
        :param v_note:
        :return:
        """

        index = self.actors.index(v_note) if v_note in self.actors else None
        if index is None:
            raise Exception('Cannot find v_note in constraints actors')

        if p_map[v_note].note is not None:
            return OrderedSet([p_map[v_note].note])

        # find the first assigned note
        assigned_index = None
        for i in range(0, len(self.actors)):
            if p_map[self.actors[i]].note is not None:
                assigned_index = i
                break

        if assigned_index is None:
            pitches = p_map.all_tonal_pitches(v_note)
            return OrderedSet([Note(p, v_note.base_duration, v_note.num_dots) for p in pitches])

        known_note = p_map[self.actors[assigned_index]].note
        if assigned_index < index:
            for i in range(assigned_index + 1, index + 1):
                unknown_contextual_note = p_map[self.actors[i]]
                unknown_note = unknown_contextual_note.note
                if unknown_note is not None:
                    known_note = unknown_note
                    continue

                lower_index = self.variance_list[i - 1] if self.variance_list[i - 1] < 0 else 0
                upper_index = self.variance_list[i - 1] if self.variance_list[i - 1] > 0 else 0
                pitches = PitchScale.compute_tonal_pitch_range(
                    unknown_contextual_note.policy_context.harmonic_context.tonality,
                    known_note.diatonic_pitch, lower_index, upper_index)
                pitch_index = self.variance_list[i - 1] + (len(pitches) - 1 if self.variance_list[i - 1] < 0 else 0)
                if pitch_index < 0 or pitch_index >= len(pitches):
                    if pitches is None or len(pitches) == 0:
                       return OrderedSet()
                    pitch = pitches[0] if pitch_index < 0 else pitches[len(pitches) - 1]
                else:
                    pitch = pitches[pitch_index]
                known_note = Note(pitch, self.actors[i].base_duration, self.actors[i].num_dots)
            return OrderedSet([known_note])

        for i in range(assigned_index - 1, index - 1, -1):
            unknown_contextual_note = p_map[self.actors[i]]
            unknown_note = unknown_contextual_note.note
            if unknown_note is not None:
                known_note = unknown_note
                continue

            upper_index = -self.variance_list[i] if self.variance_list[i] < 0 else 0
            lower_index = -self.variance_list[i] if self.variance_list[i] > 0 else 0
            pitches = PitchScale.compute_tonal_pitch_range(
                unknown_contextual_note.policy_context.harmonic_context.tonality,
                known_note.diatonic_pitch, lower_index, upper_index)
            pitch_index = -self.variance_list[i] + (len(pitches) - 1 if self.variance_list[i] > 0 else 0)
            if pitch_index < 0:
                pitch_index = 0
            elif pitch_index >= len(pitches):
                pitch_index = len(pitches) - 1
            pitch = pitches[pitch_index]
            known_note = Note(pitch, self.actors[i].base_duration, self.actors[i].num_dots)
        return OrderedSet([known_note])