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
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 _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))))