def _handles_antipenultimate_bar(self, note: Note, index: tuple) -> bool:
     if index == (self._length - 3, 2):
         if note.get_accidental(
         ) != ScaleOption.NATURAL or note.get_scale_degree(
         ) != self._mr.get_final():
             return False
     return True
 def _get_notes_from_interval(self, note: Note,
                              interval: int) -> list[Note]:
     sdg = note.get_scale_degree()
     octv = note.get_octave()
     adjustment_value = -1 if interval > 0 else 1
     new_sdg, new_octv = sdg + interval + adjustment_value, octv
     if new_sdg < 1:
         new_octv -= 1
         new_sdg += 7
     else:
         while new_sdg > 7:
             new_octv += 1
             new_sdg -= 7
     new_note = Note(new_sdg, new_octv, 8)
     valid_notes = [new_note]
     if (self._mode == ModeOption.DORIAN
             or self._mode == ModeOption.LYDIAN) and new_sdg == 7:
         valid_notes.append(
             Note(new_sdg, new_octv, 8, accidental=ScaleOption.FLAT))
     if self._mode == ModeOption.AEOLIAN and new_sdg == 2:
         valid_notes.append(
             Note(new_sdg, new_octv, 8, accidental=ScaleOption.SHARP))
     if new_sdg in [1, 4, 5]:
         valid_notes.append(
             Note(new_sdg, new_octv, 8, accidental=ScaleOption.SHARP))
     return valid_notes
Ejemplo n.º 3
0
 def _handles_final_leading_tone(self, note: Note, index: tuple,
                                 line: int) -> bool:
     if index != (self._length - 2, 0): return True
     final = self._mr[line].get_final()
     if line != 0 and self._counterpoint_lst[0][
             self._length - 1].get_scale_degree() != final:
         return True
     if (note.get_scale_degree() + 1) % 7 != final: return True
     if (final in [2, 5, 6] and note.get_accidental() != ScaleOption.SHARP
         ) or (final in [1, 3, 4]
               and note.get_accidental() != ScaleOption.NATURAL):
         return False
     if final == 3 and note.get_scale_degree() == 4 and note.get_accidental(
     ) == ScaleOption.SHARP:
         return False
     return True
Ejemplo n.º 4
0
 def _check_for_highest_and_second_note(self, note: Note,
                                        index: tuple) -> None:
     if self._mr.is_unison(note, self._attempt_params["highest"]):
         self._attempt_params["highest_has_been_placed"] = True
     if note.get_accidental(
     ) == ScaleOption.NATURAL and note.get_scale_degree(
     ) == self._attempt_params["second_note"]:
         self._attempt_params["second_note_has_been_placed"] = True
 def _map_solution_onto_counterpoint_dict(self,
                                          solution: list[Note]) -> None:
     for i, note in enumerate(solution):
         (measure, beat) = self._all_indices[i]
         if measure in self._divided_measures:
             note = Note(note.get_scale_degree(), note.get_octave(), 4,
                         note.get_accidental())
         self._counterpoint[(measure, beat)] = note
 def _backtrack(self) -> None:
     if (self._num_backtracks > 2000 and self._solutions_this_attempt == 0) or self._num_backtracks > 20000:
         return 
     if self._solutions_this_attempt >= 100:
         return 
     self._num_backtracks += 1
     if len(self._remaining_indices) == 0:
         # print("found possible solution!")
         sol = []
         for i in range(len(self._all_indices)):
             note_to_add = self._counterpoint[self._all_indices[i]]
             note_to_add_copy = Note(note_to_add.get_scale_degree(), note_to_add.get_octave(), note_to_add.get_duration(), note_to_add.get_accidental())
             sol.append(note_to_add_copy)
         if self._passes_final_checks(sol):
             # print("FOUND SOLUTION!")
             self._solutions.append(sol)
             self._solutions_this_attempt += 1
         return 
     (bar, beat) = self._remaining_indices.pop() 
     candidates = None
     if bar == 0 and (beat == 0 or self._counterpoint[(0, 0)].get_accidental() == ScaleOption.REST):
         candidates = list(filter(lambda n: self._cantus[0].get_chromatic_interval(n) in [-12, 0, 7, 12], self._valid_pitches))
     else:
         candidates = list(filter(lambda n: self._passes_insertion_checks(n, (bar, beat)), self._valid_pitches)) 
     if bar == 0 and beat == 0:
         candidates.append(Note(1, 0, 4, accidental = ScaleOption.REST))
         shuffle(candidates)
     if bar == self._length - 1:
         candidates = list(filter(lambda n: self._valid_last_note(n), candidates))
     for candidate in candidates:
         #start by making a copy of the note
         candidate = Note(candidate.get_scale_degree(), candidate.get_octave(), 8, candidate.get_accidental())
         temp_highest_placed, temp_lowest_placed = self._highest_has_been_placed, self._lowest_has_been_placed
         if self._mr.is_unison(candidate, self._highest): self._highest_has_been_placed = True  
         if self._mr.is_unison(candidate, self._lowest): self._lowest_has_been_placed = True 
         (may_be_tied, must_be_tied) = self._get_tied_options(candidate, (bar, beat))
         if not must_be_tied:
             candidate.set_duration(4 if bar != self._length - 1 else 16)
             self._counterpoint[(bar, beat)] = candidate 
             if self._current_chain_is_legal((bar, beat)):
                 self._backtrack()
         if may_be_tied or (bar == self._length - 2 and beat == 0):
             candidate.set_duration(8)
             index_to_remove = (bar, 2) if beat == 0 else (bar + 1, 0)
             index_position_all, index_position_remaining = self._all_indices.index(index_to_remove), self._remaining_indices.index(index_to_remove)
             self._all_indices.remove(index_to_remove)
             self._remaining_indices.remove(index_to_remove)
             del self._counterpoint[index_to_remove]
             self._counterpoint[(bar, beat)] = candidate 
             if self._current_chain_is_legal((bar, beat)):
                 self._backtrack()
             self._all_indices.insert(index_position_all, index_to_remove)
             self._remaining_indices.insert(index_position_remaining, index_to_remove)
             self._counterpoint[index_to_remove] = None 
         self._highest_has_been_placed, self._lowest_has_been_placed = temp_highest_placed, temp_lowest_placed
     self._counterpoint[(bar, beat)] = None 
     self._remaining_indices.append((bar, beat))
 def _handles_doubled_leading_tone(self, note: Note, index: tuple) -> bool:
     (bar, beat) = index
     if beat != 0 or self._cantus[bar].get_chromatic_interval(note) not in [
             -12, 0, 12
     ]:
         return True
     if (note.get_scale_degree() + 1) % 7 == self._mr.get_final():
         return False
     return True
 def _check_last_pitch(self, note: Note) -> bool:
     if self._mr.get_final() != note.get_scale_degree(
     ) or note.get_accidental() != ScaleOption.NATURAL:
         return False
     if self._counterpoint_lst[-1].get_scale_degree_interval(note) == 2:
         return self._mr.is_unison(self._mr.get_leading_tone_of_note(note),
                                   self._counterpoint_lst[-1])
     if self._counterpoint_lst[-1] != -2: return False
     return self._mr.is_unison(self._counterpoint_lst[-1],
                               self._mr.get_leading_tone_of_note(note))
Ejemplo n.º 9
0
 def _handles_repetition(self, note: Note, index: tuple, line: int) -> bool:
     if line == 0 and index[0] != 0 and self._counterpoint_lst[line][
             -1].get_scale_degree() == note.get_scale_degree():
         return False
     if len(self._counterpoint_lst[line]) < 2: return True
     if self._mr[line].is_unison(self._counterpoint_lst[line][-1],
                                 note) and self._mr[line].is_unison(
                                     self._counterpoint_lst[line][-2],
                                     self._counterpoint_lst[line][-1]):
         return False
     return True
Ejemplo n.º 10
0
 def _stops_leaps_outside_of_hexachord(self, note: Note,
                                       index: tuple) -> bool:
     if index != (0, 0) and abs(self._counterpoint_lst[-1].
                                get_scale_degree_interval(note)) > 2:
         hex_notes = [
             self._attempt_params["first_note"],
             self._attempt_params["second_note"]
         ]
         if self._counterpoint_lst[-1].get_scale_degree(
         ) not in hex_notes and note.get_scale_degree() not in hex_notes:
             return False
     return True
Ejemplo n.º 11
0
 def _get_notes_from_interval(self, note: Note, interval: int) -> list[Note]: 
     sdg = note.get_scale_degree()
     octv = note.get_octave()
     adjustment_value = -1 if interval > 0 else 1
     new_sdg, new_octv = sdg + interval + adjustment_value, octv
     if new_sdg < 1:
         new_octv -= 1
         new_sdg += 7
     elif new_sdg > 7:
         new_octv += 1
         new_sdg -= 7
     new_note = Note(new_sdg, new_octv, 8)
     valid_notes = [new_note]
     if (self._mode == ModeOption.DORIAN or self._mode == ModeOption.LYDIAN) and new_sdg == 7:
         valid_notes.append(Note(new_sdg, new_octv, 8, accidental = ScaleOption.FLAT))
     def valid_interval(next_note: Note) -> bool:
         chro_interval = next_note.get_chromatic_with_octave() - note.get_chromatic_with_octave()
         return chro_interval in CONSONANT_MELODIC_INTERVALS_CHROMATIC
     return list(map(lambda n: (interval, n), list(filter(valid_interval, valid_notes))))
Ejemplo n.º 12
0
 def _handles_last_note(self, note: Note, index: tuple, line: int) -> bool:
     if index[0] != self._length - 1: return True
     if line == 0:
         return note.get_scale_degree() == self._mr[line].get_final()
     intvl = self._counterpoint_obj[0][(self._length - 1,
                                        0)].get_chromatic_interval(note)
     if intvl < 0 or intvl % 12 not in [0, 4, 7]: return False
     if self._height >= 4 and (line == self._height - 1 or
                               (line == self._height - 2
                                and self._cf_index == self._height - 1)):
         has_third = False
         bass_note = self._counterpoint_obj[0][index]
         for other_line in range(1, self._height):
             if other_line != line and bass_note.get_chromatic_interval(
                     self._counterpoint_obj[other_line][index]) % 12 == 4:
                 has_third = True
         if not has_third and bass_note.get_chromatic_interval(
                 note) % 12 != 4:
             return False
     return True
    def _is_valid_adjacent(self, note1: Note, note2: Note) -> bool:
        sdg_interval = note1.get_scale_degree_interval(note2)
        if (note1.get_accidental() == ScaleOption.SHARP
                or note2.get_accidental()
                == ScaleOption.SHARP) and abs(sdg_interval) > 3:
            return False
        #if a sharp is not followed by a step up, we'll give it an arbitrary 50% chance of passing
        is_leading_tone = note1.get_accidental == ScaleOption.SHARP or (
            note1.get_scale_degree() == 7
            and self._mode in [ModeOption.DORIAN, ModeOption.LYDIAN])
        if sdg_interval != 2 and is_leading_tone and random() > .5:
            return False

        chro_interval = note1.get_chromatic_interval(note2)
        if (sdg_interval in LegalIntervals["adjacent_melodic_scalar"] and
                chro_interval in LegalIntervals["adjacent_melodic_chromatic"]
                and (sdg_interval, chro_interval)
                not in LegalIntervals["forbidden_combinations"]):
            return True
        return False
Ejemplo n.º 14
0
 def _check_starting_pitch(self, note: Note) -> bool:
     if note.get_accidental() != ScaleOption.NATURAL: return False
     if note.get_scale_degree() != self._attempt_params["first_note"]:
         return False
     return True
Ejemplo n.º 15
0
 def _check_starting_pitch(self, note: Note) -> bool:
     if note.get_accidental() == ScaleOption.REST:
         return True
     if note.get_scale_degree() != self._mr.get_final(): return False
     return note.get_accidental() == ScaleOption.NATURAL