Пример #1
0
    def values(self, p_map, v_note):
        if v_note != self.actor_note:
            raise Exception('v_note {0} not in ScalarConstraint actors.'.format(v_note.note))

        policy_context = p_map[self.actor_note].policy_context
        tones = list(policy_context.harmonic_context.tonality.annotation)
        tones = tones[:-1]   # remove final note (same as first)
        if len(self.scalar_roles) != 0:
            tones = [tones[i] for i in self.scalar_roles]
        if p_map[v_note].note is not None:
            tone = p_map[v_note].note.diatonic_pitch.diatonic_tone
            return OrderedSet([self.actor_note]) if tone in tones else None

        pitch_range = 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])

        valid_set = OrderedSet()

        for tone in tones:
            for j in range(start_partition, end_partition + 1):
                pitch = DiatonicPitch(j, 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 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
Пример #3
0
    def test_basic_plf(self):
        array = [(0, 'A:0'), (Fraction(1, 2), 'C:5'), (Position(3, 4), 'G:4'),
                 (1, 'A:5')]
        f = PiecewiseLinearPitchFunction(array)

        assert DiatonicPitch.parse('A:0').chromatic_distance == f.eval(0)
        assert DiatonicPitch.parse('C:5').chromatic_distance == f.eval(0.5)
        assert DiatonicPitch.parse('G:4').chromatic_distance == f.eval(
            Fraction(3, 4))
        assert DiatonicPitch.parse('A:5').chromatic_distance == f.eval(
            Position(1))

        assert DiatonicPitch.parse(
            'A:0').chromatic_distance == f.eval_as_chromatic_distance(0)
        assert DiatonicPitch.parse(
            'C:5').chromatic_distance == f.eval_as_chromatic_distance(0.5)
        assert DiatonicPitch.parse(
            'G:4').chromatic_distance == f.eval_as_chromatic_distance(
                Fraction(3, 4))
        assert DiatonicPitch.parse(
            'A:5').chromatic_distance == f.eval_as_chromatic_distance(
                Position(1))

        print(f.eval_as_frequency(0))
        assert ChromaticScale.A0 == f.eval_as_frequency(0)

        print(
            ChromaticScale.index_to_location(
                DiatonicPitch.parse('C:5').chromatic_distance))
        print(
            ChromaticScale.get_frequency(
                ChromaticScale.index_to_location(
                    DiatonicPitch.parse('C:5').chromatic_distance)))
        print(f.eval_as_frequency(0.5))
        assert math.isclose(
            ChromaticScale.get_frequency(
                ChromaticScale.index_to_location(
                    DiatonicPitch.parse('C:5').chromatic_distance)),
            f.eval_as_frequency(0.5))

        print(f.eval(0.25))  # 34.5
        print(f.eval_as_nearest_pitch(0.25))
        assert math.isclose(
            ChromaticScale.A0 * math.pow(ChromaticScale.SEMITONE_RATIO,
                                         f.eval(0.25) - 9),
            f.eval_as_frequency(0.25))

        assert 'A#:2' == str(f.eval_as_nearest_pitch(0.25))

        pitches = f.eval_as_pitch(0.25)
        assert 'A#:2' == str(pitches[0])
        assert 'B:2' == str(pitches[1])
Пример #4
0
 def test_book_example(self):
     location = ChromaticScale.parse_notation("4:9")
     print(location)
     index = ChromaticScale.location_to_index(location)
     print(index)
     loc = ChromaticScale.index_to_location(index)
     print(loc)
Пример #5
0
    def __compute_pitch_scale(self):
        (tone_index, pitch_index) = self.__find_lowest_tone(
        )  # Determine the lowest tone in the range
        if tone_index == -1:
            return []
        scale = [
            DiatonicPitch(
                ChromaticScale.index_to_location(pitch_index)[0],
                self.tone_scale[tone_index].diatonic_symbol)
        ]

        # Given the first pitch, sync up with the incremental intervals on the tonality, and move forward, computing
        # each scale pitch until we are out of range.
        # Note: be sure to skip the first incremental interval which should be P:1
        prior_pitch = scale[0]
        while True:
            tone_index += 1
            if tone_index > len(self.tone_scale) - 1:
                tone_index = 1  # skip 0 as that should be P:1
            incremental_interval = self.tonality.modality.incremental_intervals[
                tone_index]
            current_pitch = incremental_interval.get_end_pitch(prior_pitch)
            if current_pitch.chromatic_distance > self.pitch_range.end_index:
                break
            scale.append(current_pitch)
            prior_pitch = current_pitch

        return scale
Пример #6
0
 def find_lowest_placement_in_range(self, placement):
     """
     For a given chromatic placement (0, ..., 11) find the lowest chromatic index 
     in the range for it.
     """
     if placement < 0 or placement >= 12:
         raise Exception(
             'Illegal placement value {0} must be between 0 and 11'.format(
                 placement))
     start_partition = ChromaticScale.index_to_location(self.start_index)[0]
     end_partition = ChromaticScale.index_to_location(self.end_index)[0]
     lowest_index = -1
     for partition in range(start_partition, end_partition + 1):
         if self.is_location_inbounds((partition, placement)):
             lowest_index = ChromaticScale.location_to_index(
                 (partition, placement))
             break
     return lowest_index
Пример #7
0
    def values(self, p_map, v_note):
        """
        Return a set of possible pitches that v_note can take that must be in p_map target's chord.
        :param p_map: 
        :param v_note: 
        :return: 
        """
        if v_note != self.actor_note:
            raise Exception(
                'v_note {0} not in ChordalToneConstraint actors.'.format(
                    v_note.note))

        policy_context = p_map[self.actor_note].policy_context
        tones = policy_context.harmonic_context.chord.tones
        if p_map[v_note].note is not None:
            note = p_map[v_note].note
            if note.diatonic_pitch.diatonic_tone in tones:
                return {note}
            raise Exception(
                'Chordal Pitch Policy Violated has {0} should be member of chord {1}'
                .format(p_map[v_note].note.diatonic_pitch,
                        policy_context.harmonic_context.chord))

        pitch_range = 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])

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

        return valid_set
Пример #8
0
    def _build_pitch_map(self):
        ltrs = 'CDEFGAB'
        index = ltrs.index(self.domain_tonality.diatonic_tone.diatonic_letter)
        key_ltrs = list(ltrs[index:] + ltrs[:index])
        c_index = key_ltrs.index('C')
        low_octave = self.cue_pitch.octave if key_ltrs.index(self.cue_pitch.diatonic_tone.diatonic_letter) < c_index \
            else self.cue_pitch.octave - 1
        high_octave = low_octave + 1

        range_ltrs = 'CBAGFED'
        index = range_ltrs.index(self.range_tonality.diatonic_tone.diatonic_letter)
        range_key_ltrs = list(range_ltrs[index:] + range_ltrs[:index])
        range_c_index = range_key_ltrs.index('C')
        range_low_octave = self.cue_pitch.octave if range_key_ltrs.index(self.cue_pitch.diatonic_tone.diatonic_letter) \
            > range_c_index else self.cue_pitch.octave - 1
        range_high_octave = range_low_octave + 1

        imap = dict()
        for tone in self.tonal_function.domain:
            domain_octave = low_octave if key_ltrs.index(tone.diatonic_letter) < c_index else high_octave
            value = self.tonal_function[tone]
            range_octave = range_high_octave if range_key_ltrs.index(value.diatonic_letter) <= range_c_index \
                else range_low_octave
            imap[DiatonicPitch(domain_octave, tone)] = DiatonicPitch(range_octave, value)

        full_map = dict()
        start = ChromaticScale.index_to_location(self.domain_pitch_range.start_index)[0]
        end = ChromaticScale.index_to_location(self.domain_pitch_range.end_index)[0] + 1

        for octave in range(start, end):
            if octave < ChromaticScale.CHROMATIC_START[0] or octave > ChromaticScale.CHROMATIC_END[0]:
                continue
            octave_delta = self.cue_pitch.octave - octave
            for pitch in imap.keys():
                rrange = imap[pitch]
                new_pitch = DiatonicPitch(pitch.octave - octave_delta, pitch.diatonic_tone)
                if self.domain_pitch_range.is_pitch_inbounds(new_pitch):
                    full_map[new_pitch] = DiatonicPitch(rrange.octave + octave_delta, rrange.diatonic_tone)

        return full_map
Пример #9
0
    def test_lowest_placement(self):
        pr = PitchRange.create('G:3', 'G:5')
        answers = [4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3]
        for i in range(0, 12):
            lowest = pr.find_lowest_placement_in_range(i)
            partition = ChromaticScale.index_to_location(lowest)[0]
            self.assertTrue(partition == answers[i], 'Assert failure {0} != {1}'.format(partition, answers[i]))
            print(i, partition)
            
        with self.assertRaises(Exception):
            pr.find_lowest_placement_in_range(-1)

        with self.assertRaises(Exception):
            pr.find_lowest_placement_in_range(12)
Пример #10
0
    def test_scale(self):
        scale = ChromaticScale.get_chromatic_scale(
            ChromaticScale.parse_notation("0:9"),
            ChromaticScale.parse_notation("8:0"))
        start = ChromaticScale.location_to_index((0, 9))
        end = ChromaticScale.location_to_index((8, 0)) + 1

        for i in range(start, end):
            logging.info('{0}{1}   {1}'.format(
                i, ChromaticScale.index_to_location(i), scale[i - start]))

        assert is_close(scale[ChromaticScale.location_to_index((4, 9)) - start], 440.0), \
            "Error A:4 = {0} should be 440.0".format(scale[ChromaticScale.location_to_index((4, 9)) - start])
        assert is_close(scale[ChromaticScale.location_to_index((4, 0)) - start], 261.625565301), \
            "Error C:4 = {0} should be 261.625565301".format(scale[ChromaticScale.location_to_index((4, 0)) - start])
Пример #11
0
 def map_to_diatonic_scale(chromatic_index):
     """
     Convert a chromatic index (int) to a diatonic pitch in string format.
     
     Args:
       chromatic_index: the chromatic index of the pitch (int)
     Return:
       all enharmonic diatonic pitches
     """
     from tonalmodel.diatonic_pitch import DiatonicPitch
     location = ChromaticScale.index_to_location(chromatic_index)
     enharmonics = DiatonicTone.DIATONIC_OFFSET_ENHARMONIC_MAPPING[
         location[1]]
     octave_adjustments = DiatonicFoundation.ENHARMONIC_OCTAVE_ADJUSTMENT_MAPPING[
         location[1]]
     answers = []
     for i in range(0, len(enharmonics)):
         enharmonic = enharmonics[i]
         answers.append(
             DiatonicPitch.parse(enharmonic + ':' +
                                 str(location[0] + octave_adjustments[i])))
     return answers
Пример #12
0
    def __init__(self, tonal_function, scale_origins, domain_pitch_range, reversal=False):
        """
        Constructor.
        :param tonal_function: TonalFunction upon which this function is based.
        :param scale_origins: Duplet of origin pitches for the domain and range respectively.
        :param domain_pitch_range: Domain pitch range (PitchRange)
        :param reversal: If during construction, domain and range octaves follow together forward, or if the range
                         follow backwards.
        """
        self.domain_tonality = tonal_function.domain_tonality
        self.range_tonality = tonal_function.range_tonality

        if domain_pitch_range is None or not isinstance(domain_pitch_range, PitchRange):
            raise Exception('domain pitch range cannot be None and must be PitchRange')

        dlex, dcom = TonalityPitchFunction._build_lex_dcom_maps(self.domain_tonality, scale_origins[0])
        rlex, rcom = TonalityPitchFunction._build_lex_dcom_maps(self.range_tonality, scale_origins[1])

        # Needed for getitem()
        self._dlex = dlex

        # These define how the mapping starts on the domain, in terms of octaves octaves
        # d_reg: The octave for which the domain tonality is centered.
        # d_min: The minimal octave in the octave coverage of the domain.
        # d_max: The maximal octave in the octave coverage of the domain.
        d_reg = DiatonicPitch.parse(scale_origins[0]).octave
        d_min = max(ChromaticScale.CHROMATIC_START[0],
                    ChromaticScale.index_to_location(domain_pitch_range.start_index)[0]) - 1
        d_max = min(ChromaticScale.CHROMATIC_END[0],
                    ChromaticScale.index_to_location(domain_pitch_range.start_index)[1]) + 1

        # Some aspects of building pitch_map.
        # Goal: pitch_map: (key from largest range of (tone, octave) in domain) --> (range tone, octave)
        # The following help:
        # d_lex: maps enharmonic representations of domain tone to the domain tones.
        #        the range is called the normalized range.
        # d_com: for domain pitch scale centered at scale_origin[0], maps normalized tone to octave.
        # r_lex and r_com are similar but for the range.
        #
        # The pitch map is based on the following dynamics:
        # For d_key in pitch domain, d_key --(d_lex)-->d_n_key--(d_com)-->d_octave
        #     d_key --(tonal_function)--> r_key
        #     r_key --(r_lex)-->r_n_key --(r_com)--> r_octave
        # then:
        #     pitch_map(DP(d_octave, d_key) = DP(r_octave, r_key)
        # with adjustments to d_octave, r_octave based on sliding over the octave ranges.
        pitch_map = OrderedDict()
        for d_octave in range(d_min, d_max + 1):
            for d_tone in tonal_function.map.keys():
                source_pitch = DiatonicPitch(d_octave + (dcom[dlex[d_tone]] - d_reg), d_tone)
                r_tone = tonal_function[d_tone]
                if r_tone is not None:
                    target_pitch = DiatonicPitch(rcom[rlex[r_tone]] -
                                                 (-1 if reversal else 1) * (d_reg - d_octave), r_tone)
                    if domain_pitch_range.is_pitch_inbounds(source_pitch) and \
                            TonalityPitchFunction.FULL_PITCH_RANGE.is_pitch_inbounds(target_pitch):
                        pitch_map[source_pitch] = target_pitch
                else:
                    pitch_map[source_pitch] = None

        GeneralPitchFunction.__init__(self, pitch_map)
Пример #13
0
 def test_index_to_location(self):
     for i in range(12, 47):
         location = ChromaticScale.index_to_location(i)
         logging.info(location)
         assert location[0] == i // 12 and location[1] == i % 12