Beispiel #1
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)
    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
Beispiel #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)
Beispiel #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
    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)
Beispiel #6
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
Beispiel #7
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
Beispiel #9
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)
        ])
Beispiel #10
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 _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