def _check_starting_pitch(self, note: Note) -> bool:
     if note.get_accidental() == ScaleOption.REST:
         return True
     if self._cantus[0].get_scale_degree_interval(note) not in [
             -8, 1, 5, 8
     ]:
         return False
     return note.get_accidental() == ScaleOption.NATURAL
 def _valid_adjacent(self, note1: Note, note2: Note) -> bool:
     chro_interval = note1.get_chromatic_interval(note2)
     sdg_interval = note1.get_scale_degree_interval(note2)
     if chro_interval in VALID_MELODIC_INTERVALS_CHROMATIC and (
             abs(sdg_interval),
             abs(chro_interval)) not in FORBIDDEN_INTERVAL_COMBINATIONS:
         if note1.get_accidental(
         ) == ScaleOption.NATURAL or note2.get_accidental(
         ) == ScaleOption.NATURAL or abs(chro_interval) == 2:
             return True
     return False
 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
Пример #4
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
 def _no_cross_relations_with_cantus_firmus(self, note: Note,
                                            index: tuple) -> bool:
     (i, j) = index
     cf_note = self._cantus[i - 1 if j == 0 else i + 1]
     if abs(cf_note.get_scale_degree_interval(note)) in [1, 8]:
         return cf_note.get_accidental() == note.get_accidental()
     return True
Пример #6
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 _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))
Пример #9
0
 def _get_valid_durations(self, note: Note, index: tuple) -> set:
     (bar, beat) = index
     if bar == self._length - 1: return {16}
     if note.get_accidental() == ScaleOption.REST: return {4}
     if bar == 0 and beat == 0: return {12, 8, 6, 4}
     durs = self._get_durations_from_beat(beat)
     prev_length = len(durs)
     for check in self._rhythm_filters:
         durs = check(note, index, durs)
         if len(durs) == 0: break
     return durs
    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
 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))
Пример #12
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
Пример #13
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