Exemplo n.º 1
0
def test_unnested_climaxes(obj):
    section1 = Voice.merge_lists(*obj.nested_scale_degrees[:4])
    section2 = Voice.merge_lists(*obj.nested_scale_degrees[4:8])
    section3 = Voice.merge_lists(*obj.nested_scale_degrees[8:12])
    section4 = Voice.merge_lists(*obj.nested_scale_degrees[12:])

    if max(section1) == max(section2):
        return False
    if max(section3) <= max(section4):
        return False
    return True
Exemplo n.º 2
0
def test_proper_leaps(obj):
    # breaking this rule is too advanced for you
    unnested_antecedent = Voice.merge_lists(*obj.nested_scale_degrees[:8])
    unnested_consequent = Voice.merge_lists(*obj.nested_scale_degrees[8:])
    proper_antecedent_leaps = Voice.has_proper_leaps(unnested_antecedent)
    proper_consequent_leaps = Voice.has_proper_leaps(unnested_consequent)
    if not (proper_antecedent_leaps and proper_consequent_leaps):
        return False

    ante_cons_transition = Voice.merge_lists(*obj.nested_scale_degrees[5:10])
    if (not Voice.has_proper_leaps(ante_cons_transition)
            and obj.nested_scale_degrees[0] != obj.nested_scale_degrees[8]):
        return False

    return True
Exemplo n.º 3
0
def test_bounds(obj):
    if min(obj.unnested_scale_degrees) < -3:
        return False
    if max(obj.unnested_scale_degrees) > 7:
        return False
    if max(obj.unnested_scale_degrees) < 5:
        return False
    section1 = Voice.merge_lists(*obj.nested_scale_degrees[:3])
    if min(section1) < 0:
        return False
    return True
Exemplo n.º 4
0
    def has_melody_figure(self):
        """Check specific melody against figuration options"""

        last_rhythm_symbol = self.rhythm_symbols[self.chord_index - 1]
        if last_rhythm_symbol == -1:
            if self.chord_index == 15:
                section3 = Voice.merge_lists(*self.nested_scale_degrees[8:12])
                section4 = Voice.merge_lists(*self.nested_scale_degrees[12:14])

                if max(section3) <= max(section4):
                    return False
            self.nested_scale_degrees[self.chord_index -
                                      1] = [self.previous_degree_choice]
            return True
        if last_rhythm_symbol == -2:
            self.melody_figure_options[self.chord_index -
                                       1] = (self.get_pickup_sequences(
                                           self.current_degree_choice))
            return self.add_valid_figure()

        remaining_figures = self.melody_figure_options[self.chord_index - 1]
        if remaining_figures:
            return self.add_valid_figure()

        degree_mvmt = self.current_degree_choice - self.previous_degree_choice
        melody_slope = Voice.calculate_slope(degree_mvmt)
        degree_mvmt = abs(degree_mvmt)

        embellish_amount = len(self.finalized_rhythms[self.chord_index - 1])
        if embellish_amount == 2:
            all_figurations = self.all_single_figurations
        elif embellish_amount == 3:
            all_figurations = self.all_double_figurations
        possible_scale_degrees = all_figurations[degree_mvmt](
            self.previous_degree_choice, self.current_degree_choice,
            melody_slope)

        self.melody_figure_options[self.chord_index -
                                   1] = possible_scale_degrees
        return self.add_valid_figure()
Exemplo n.º 5
0
    def add_valid_figure(self):
        """Find and add specific figuration of base melody using idioms"""
        valid_figure = None
        remaining_figures = self.melody_figure_options[self.chord_index - 1]

        random.shuffle(remaining_figures)
        # alias has side effect but allows easier referencing
        while remaining_figures:
            inbetween, fig_type = remaining_figures.pop()
            if min(inbetween) < -3 or max(inbetween) > 7:
                continue
            if self.chord_index - 1 < 3 and min(inbetween) < 0:
                continue

            unnested_scalar_melody = self.unnested_scale_degrees[:]
            unnested_scalar_melody.extend(inbetween)
            """
			chord 8 and 12 are short-circuited
			only need to evaluate once going forward
			so, use the next indices for testing at divisible checkpoints
			"""
            if self.chord_index == 13 and max(unnested_scalar_melody) < 5:
                continue
            if self.chord_index == 9:
                section1 = Voice.merge_lists(*self.nested_scale_degrees[:4])
                section2 = Voice.merge_lists(*self.nested_scale_degrees[4:8])

                if max(section1) == max(section2):
                    continue

            valid_figure = inbetween
            break

        if valid_figure is None:
            return False
        self.nested_scale_degrees[self.chord_index - 1] = [
            self.previous_degree_choice, *valid_figure
        ]
        self.chosen_figurations[self.chord_index - 1] = fig_type
        return True
Exemplo n.º 6
0
	def test_list_merger(self):
		self.assertEqual(Voice.merge_lists([]), [])
		self.assertEqual(Voice.merge_lists([], [], []), [])
		self.assertEqual(Voice.merge_lists([], [0], []), [0])
		self.assertEqual(Voice.merge_lists([5], [], []), [5])
		
		self.assertEqual(Voice.merge_lists([], [], [2, 3]), [2, 3])
		self.assertEqual(Voice.merge_lists([4], [], [9, 1, 3], []), [4, 9, 1, 3])
		self.assertEqual(Voice.merge_lists([1, 2], [3], [4, 5]), [1, 2, 3, 4, 5])

		list1 = [-5, -4]
		list2 = [-3, -2]
		list3 = Voice.merge_lists(list1, list2)
		list3.append(0)
		self.assertFalse(list1[-1] == 0)
		self.assertFalse(list2[-1] == 0)

		list4 = Voice.merge_lists(list1, [])
		self.assertTrue(list1 is not list4)
Exemplo n.º 7
0
    def validate_base_melody(self):
        """Check current base melody with idioms"""
        melodic_mvmt = "".join(
            str(slope)
            for slope in self.melodic_direction[:self.chord_index + 1])

        if "_" * 3 in melodic_mvmt:
            # Avoid long rests
            return False
        if "_" * 2 in melodic_mvmt:
            all_rest_indices = set()
            start_index = 0
            while True:
                rest_index = melodic_mvmt.find("__", start_index)
                if rest_index == -1:
                    break
                all_rest_indices.add(rest_index)
                start_index = rest_index + 1
            if all_rest_indices - self.good_double_rest_indices:
                # Avoid triple repeats only between phrases")
                return False

        start_index = 0
        while True:
            rest_index = melodic_mvmt.find("_", start_index)

            if rest_index in self.bad_single_rest_indices:
                return False
            if rest_index == -1:
                break
            start_index = rest_index + 1

        relevant_melodic_mvt = melodic_mvmt[1:]

        current_move_distance = self.current_degree_choice - self.previous_degree_choice
        abs_current_move_distance = abs(current_move_distance)
        if abs_current_move_distance > 7:
            # Keep leaps within octave
            return False
        if self.chord_index == 14:
            if abs_current_move_distance > 4:
                # Don't end with a large leap
                return False
            if relevant_melodic_mvt.count('>') > relevant_melodic_mvt.count(
                    '<'):
                # Descending motion should predominate
                return False
        if abs_current_move_distance > 4 and self.chord_index not in self.valid_leap_indices:
            # Large leap can only occur halfway through
            return False
        if len(self.unnested_scale_degrees) >= 3:
            if self.chord_index < 9:
                nested_part_half = self.nested_scale_degrees[:8]
            else:
                nested_part_half = self.nested_scale_degrees[8:]
            unnested_part_half = Voice.merge_lists(*nested_part_half)
            if not Voice.has_proper_leaps(unnested_part_half):
                # "Leap should be followed by contrary stepwise motion (full melody)"
                return False
        if self.chord_index == 11:
            ante_cons_transition = Voice.merge_lists(
                *self.nested_scale_degrees[5:10])
            if (not Voice.has_proper_leaps(ante_cons_transition)
                    and self.nested_scale_degrees[0] !=
                    self.nested_scale_degrees[8]):
                return False

        # score divides into 4 sections, 16 items
        # first 2 sections: antecedent
        # last 2 sections: consequent
        current_section = self.chord_index // 4
        section_start_index = current_section * 4
        section_scale_degrees = (
            self.chosen_scale_degrees[section_start_index:section_start_index +
                                      4])
        end_degree = section_scale_degrees[-1]
        while end_degree is None:
            section_scale_degrees.pop()
            end_degree = section_scale_degrees[-1]

        section_max_degree = max(section_scale_degrees)
        if current_section <= 2:
            if section_scale_degrees.count(section_max_degree) > 2:
                return False
            if section_scale_degrees.count(section_max_degree) == 2:
                for scale_degree0, scale_degree1 in zip(
                        section_scale_degrees, section_scale_degrees[1:]):
                    if (scale_degree0 == section_max_degree
                            and scale_degree0 == scale_degree1):
                        break
                else:
                    return False

        if self.chord_index == 2:
            if self.chosen_figurations[0] != "IPT":
                return False
        if self.chord_index >= 3:
            previous_melody_note = self.chosen_scale_degrees[self.chord_index -
                                                             3]
            for chord_index, melody_group in enumerate(
                    self.nested_scale_degrees[self.chord_index -
                                              3:self.chord_index - 1],
                    self.chord_index - 3):
                for fig_index, current_melody_note in enumerate(melody_group):
                    pitch_diff = current_melody_note - previous_melody_note
                    if (abs(pitch_diff) > 4 and pitch_diff < 0 and
                        (fig_index != 0
                         or chord_index not in self.valid_leap_indices)):
                        return False
                    previous_melody_note = current_melody_note
            if (self.chord_index not in self.quick_turn_indices
                    and melodic_mvmt[self.chord_index - 2:] in {"><>", "<><"}):
                # No late melodic jukes
                return False

        if self.chord_index == 8:
            section1 = self.chosen_scale_degrees[:4]
            section2 = self.chosen_scale_degrees[4:8]
            if max(section1) == max(section2):
                return False
        elif self.chord_index == 15:
            section3 = self.chosen_scale_degrees[8:12]
            section4 = self.chosen_scale_degrees[12:]
            if max(section3) <= max(section4):
                return False
            if abs(self.nested_scale_degrees[-3][-1]) > 1:
                return False
            if (self.chosen_figurations.count("OPT") > 2
                    and self.nested_scale_degrees[0:4] !=
                    self.nested_scale_degrees[8:12]):
                return False

        num_still_figures = self.chosen_figurations.count("CN")
        num_still_figures += self.chosen_figurations.count("DN")
        num_still_figures += self.chosen_figurations.count("DCN")

        if num_still_figures > 2:
            return False
        if self.chosen_figurations.count("OPT") > 4:
            return False
        if self.chosen_figurations.count("ANT") > 1:
            return False

        return True