def _doesnt_create_hiddens_or_parallels(self, note: Note, index: int) -> bool: chro_interval = note.get_chromatic_interval(self._cf.get_note(index)) if abs(chro_interval) not in [7, 12]: return True prev_note = self._counterpoint[index - 1] next_note = self._counterpoint[index + 1] if prev_note is not None: prev_interval = prev_note.get_scale_degree_interval(note) cf_prev_interval = self._cf.get_note(index - 1).get_scale_degree_interval( self._cf.get_note(index)) if (prev_interval > 0 and cf_prev_interval > 0) or (prev_interval < 0 and cf_prev_interval < 0): return False if next_note is not None: next_interval = note.get_scale_degree_interval(next_note) cf_next_interval = self._cf.get_note( index).get_scale_degree_interval(self._cf.get_note(index + 1)) if (next_interval > 0 and cf_next_interval > 0) or (next_interval < 0 and cf_next_interval < 0): next_chro_interval = next_note.get_chromatic_interval( self._cf.get_note(index + 1)) if abs(next_chro_interval) in [7, 12]: return False return True
def _is_valid_harmonically(self, note1: Note, note2: Note) -> bool: sdg_interval = note1.get_scale_degree_interval(note2) chro_interval = note1.get_chromatic_interval(note2) if ( sdg_interval in LegalIntervalsFourthSpecies["harmonic_scalar"] and chro_interval in LegalIntervalsFourthSpecies["harmonic_chromatic"] and (sdg_interval, chro_interval) not in LegalIntervalsFourthSpecies["forbidden_combinations"] ): return True return False
def _valid_outline(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 CONSONANT_MELODIC_INTERVALS_CHROMATIC and ( abs(sdg_interval), abs(chro_interval)) not in FORBIDDEN_INTERVAL_COMBINATIONS: return True return False
def _is_valid_adjacent(self, note1: Note, note2: Note) -> bool: sdg_interval = note1.get_scale_degree_interval(note2) chro_interval = note1.get_chromatic_interval(note2) if (self._mr.is_leading_tone(note1) or self._mr.is_leading_tone(note2)) and abs(sdg_interval) > 3: return False if ( sdg_interval in LegalIntervalsFourthSpecies["adjacent_melodic_scalar"] and chro_interval in LegalIntervalsFourthSpecies["adjacent_melodic_chromatic"] and (sdg_interval, chro_interval) not in LegalIntervalsFourthSpecies["forbidden_combinations"] ): return True return False
def _is_valid_outline(self, note1: Note, note2: Note) -> bool: sdg_interval = note1.get_scale_degree_interval(note2) chro_interval = note1.get_chromatic_interval(note2) if (sdg_interval in LegalIntervals["outline_melodic_scalar"] and chro_interval in LegalIntervals["outline_melodic_chromatic"] and (sdg_interval, chro_interval) not in LegalIntervals["forbidden_combinations"]): return True return False
def _valid_harmonically(self, note1: Note, note2: Note) -> bool: chro_interval = note1.get_chromatic_interval(note2) if chro_interval == 0: return False sdg_interval = note1.get_scale_degree_interval(note2) if sdg_interval in CONSONANT_HARMONIC_INTERVALS_SCALE_DEGREES and abs( chro_interval) % 12 in CONSONANT_HARMONIC_INTERVALS_CHROMATIC: combo = (abs(sdg_interval if sdg_interval <= 8 else sdg_interval - 7), abs(chro_interval) % 12) if combo not in FORBIDDEN_INTERVAL_COMBINATIONS: return True return False
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 _doesnt_create_parallels(self, note: Note, index: tuple) -> bool: (i, j) = index cf_note, next_note, cf_next = self._cantus[i], self._counterpoint[( i + 1, 0)], self._cantus[i + 1] if next_note is not None and abs( next_note.get_chromatic_interval(cf_next)) in [0, 7, 12, 19]: #next measure is a perfect interval. check for parallels first if note.get_chromatic_interval( cf_note) == next_note.get_chromatic_interval(cf_next): return False #check for hidden intervals if (j == 2 and ((note.get_scale_degree_interval(next_note) > 0 and cf_note.get_scale_degree_interval(cf_next) > 0) or (note.get_scale_degree_interval(next_note) < 0 and cf_note.get_scale_degree_interval(cf_next) < 0))): return False #if j is 2 we don't have to check what comes before if j == 0 and abs( note.get_chromatic_interval(cf_note)) in [0, 7, 12, 19]: cf_prev = self._cantus[i - 1] #check previous downbeat if it exists if i - 1 != 0 or self._start_on_beat: prev_downbeat = self._counterpoint[(i - 1, 0)] if prev_downbeat is not None and note.get_chromatic_interval( cf_note) == prev_downbeat.get_chromatic_interval( cf_prev): return False #previous weak beat will always exist when we check an insertion prev_note = self._counterpoint[(i - 1, 2)] if prev_note is not None and note.get_chromatic_interval( cf_note) == prev_note.get_chromatic_interval(cf_prev): return False #check for hiddens if (prev_note is not None and ((prev_note.get_scale_degree_interval(note) > 0 and cf_prev.get_scale_degree_interval(cf_note) > 0) or (prev_note.get_scale_degree_interval(note) < 0 and cf_prev.get_scale_degree_interval(cf_note) < 0))): return False return True
def _handles_first_note(self, note: Note, index: tuple, line: int) -> bool: if index[0] != 0: return True intvl = self._counterpoint_obj[0][(0, 0)].get_chromatic_interval( note) if line != 0 else note.get_chromatic_interval( self._counterpoint_obj[self._cf_index][(0, 0)]) if intvl < 0 or intvl % 12 not in [0, 4, 7]: return False if line != self._height - 1 and (line != self._height - 2 or self._cf_index != self._height - 1): return True has_fifth, has_third = False, False for i in range(1, self._height): low_note, high_note = self._counterpoint_obj[0][( 0, 0)], self._counterpoint_obj[i][(0, 0)] if low_note is None or high_note is None: continue intvl_to_check = low_note.get_chromatic_interval(high_note) % 12 if intvl_to_check == 4: has_third = True if intvl_to_check == 7: has_fifth = True if not has_third or not has_fifth: 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
def _valid_melodic_interval(self, first_note: Note, second_note: Note) -> bool: scale_interval = first_note.get_scale_degree_interval(second_note) chro_interval = first_note.get_chromatic_interval(second_note) if scale_interval not in VALID_MELODIC_INTERVALS_SCALE_DEGREES: return False if chro_interval not in VALID_MELODIC_INTERVALS_CHROMATIC: return False return True
def _is_unison(self, note1: Note, note2: Note) -> bool: return note1.get_scale_degree_interval( note2) == 1 and note1.get_chromatic_interval(note2) == 0