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 _valid_harmonic_insertion(self, note: Note, index: tuple) -> bool:
     (i, j) = index
     cf_note = self._cantus[i]
     if self._is_valid_harmonically(note, cf_note):
         if j == 0:
             prev_note, cf_prev = self._counterpoint[(i - 1,
                                                      2)], self._cantus[i -
                                                                        1]
             if prev_note is not None and not self._is_valid_harmonically(
                     prev_note, cf_prev):
                 if (i - 1, 0) in self._counterpoint and self._counterpoint[
                     (i - 1, 0)].get_scale_degree_interval(
                         prev_note) != prev_note.get_scale_degree_interval(
                             note):
                     return False
         return True
     if j == 0: return False
     if cf_note.get_chromatic_interval(note) == 0: return True
     prev_note = self._counterpoint[(i, 0)]
     if prev_note is None:
         return False  #the highest or lowest note cannot be a passing tone
     if abs(prev_note.get_scale_degree_interval(note)) != 2: return False
     next_note = self._counterpoint[(i + 1, 0)]
     if next_note is None: return True
     return note.get_scale_degree_interval(
         next_note) == prev_note.get_scale_degree_interval(note)
Пример #3
0
 def _add_highest(self, note: Note) -> None:
     def remove_center_position(index: int) -> bool:
         if self._length % 2 == 1 and index == math.floor(self._length / 2):
             return False 
         return True
     possible_indices = list(filter(remove_center_position, [num for num in self._remaining_indices]))
     shuffle(possible_indices)
     correct_index = None
     for index in possible_indices:
         correct_index = index
         prev_note = self._cf.get_note(index - 1)
         next_note = self._cf.get_note(index + 1)
         if prev_note is None and next_note is None:
             break
         if prev_note is not None and self._valid_melodic_interval(prev_note, note):
             break 
         if prev_note is not None and not self._valid_melodic_interval(prev_note, note):
             continue 
         if next_note is not None and not self._valid_melodic_interval(note, next_note):
             continue
         if next_note is not None and self._valid_melodic_interval(note, next_note):
             #we need to check if the final three notes are handled correctly
             if note.get_scale_degree_interval(next_note) == -2:
                 break 
             #if we leap down, this can't be followed by a downward step
             final = self._cf.get_note(index + 2)
             if next_note.get_scale_degree_interval(final) == -2:
                 continue
             #otherwise we're legal
             break 
     self._cf.insert_note(note, correct_index)
     self._remaining_indices.remove(correct_index)
 def _no_large_parallel_leaps(self, note: Note, index: tuple) -> bool:
     (i, j) = index
     cf_prev, cf_note, cf_next = self._cantus[
         i - 1], self._cantus[i], self._cantus[i + 1]
     if j == 2:
         next_note = self._counterpoint[(i + 1, 0)]
         if next_note is not None:
             next_interval, cf_next_interval = note.get_scale_degree_interval(
                 next_note), cf_note.get_scale_degree_interval(cf_next)
             if ((abs(next_interval) > 2 and abs(cf_next_interval) > 2
                  and (abs(next_interval) > 4 or abs(cf_next_interval) > 4)
                  and ((next_interval > 0 and cf_next_interval > 0) or
                       (next_interval < 0 and cf_next_interval < 0)))):
                 return False
     else:
         prev_note = self._counterpoint[(
             i - 1, 2)]  #this index will always exist when we check this
         if prev_note is not None:
             prev_interval, cf_prev_interval = prev_note.get_scale_degree_interval(
                 note), cf_prev.get_scale_degree_interval(cf_note)
             if ((abs(prev_interval) > 2 and abs(cf_prev_interval) > 2
                  and (abs(prev_interval) > 4 or abs(cf_prev_interval) > 4)
                  and ((prev_interval > 0 and cf_prev_interval > 0) or
                       (prev_interval < 0 and cf_prev_interval < 0)))):
                 return False
     return True
 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_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 _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 _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 _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 _no_octave_leap_with_perfect_harmonic_interval(self, note: Note,
                                                    index: tuple) -> bool:
     (i, j) = index
     if i not in self._divided_measures or abs(
             self._cantus[i].get_scale_degree_interval(note)) not in [
                 1, 5, 8, 12
             ]:
         return True
     other_note = self._counterpoint[(i, 0 if j == 2 else 2)]
     if other_note is not None and abs(
             note.get_scale_degree_interval(other_note)) == 8:
         return False
     return True
Пример #13
0
 def _add_lowest(self, note: Note) -> None:
     possible_indices = [num for num in self._remaining_indices]
     shuffle(possible_indices)
     correct_index = None
     for index in possible_indices:
         #for each index, there are several scenarios:
         #index is preceded by a note (either the highest or the lowest)
         #index is followed by a note (either the highest followed by a blank, the penultimate, or the highest followed by penultimate)
         correct_index = index
         prev_note = self._cf.get_note(index - 1)
         next_note = self._cf.get_note(index + 1)
         note_after_next = self._cf.get_note(index + 2)
         if prev_note is None and next_note is None:
             break
         if prev_note is not None and not self._valid_melodic_interval(prev_note, note):
             continue 
         if next_note is not None and not self._valid_melodic_interval(note, next_note):
             continue
         if next_note is None or note_after_next is None:
             break
         if next_note is not None and note_after_next is not None: #we are in the third to last or fourth to last position
             if index == self._length - 3:
                 #if index is the antipenultimate position, there are no scenarios in which
                 #lowest note -> penultimate -> final have an improper sequence of intervals
                 break 
             #otherwise we need to make sure that a leap to the highest note is handled properly
             final = self._cf.get_note(index + 3)
             lowest_to_highest = note.get_scale_degree_interval(next_note)
             lowest_to_penult = note.get_scale_degree_interval(note_after_next)
             lowest_to_final = note.get_scale_degree_interval(final)
             if lowest_to_highest > 3 and lowest_to_penult != lowest_to_highest - 1 and lowest_to_final != lowest_to_highest - 1:
                 continue
             else:
                 break
     self._cf.insert_note(note, correct_index)
     self._remaining_indices.remove(correct_index)
    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 _no_large_parallel_leaps(self, note: Note, index: int) -> bool:
     prev_note = self._counterpoint[index - 1]
     next_note = self._counterpoint[index + 1]
     cf_note = self._cf.get_note(index)
     cf_prev_note = self._cf.get_note(index - 1)
     cf_next_note = self._cf.get_note(index + 1)
     if prev_note is not None:
         prev_interval = prev_note.get_scale_degree_interval(note)
         cf_prev_interval = cf_prev_note.get_scale_degree_interval(cf_note)
         if (prev_interval > 2
                 and cf_prev_interval > 2) or (prev_interval < -2
                                               and cf_prev_interval < -2):
             if abs(prev_interval) > 4 or abs(cf_prev_interval) > 4:
                 return False
     if next_note is not None:
         next_interval = note.get_scale_degree_interval(next_note)
         cf_next_interval = cf_note.get_scale_degree_interval(cf_next_note)
         if (next_interval > 2
                 and cf_next_interval > 2) or (next_interval < -2
                                               and cf_next_interval < -2):
             if abs(next_interval) > 4 or abs(cf_next_interval) > 4:
                 return False
     return True
 def _place_highest(self, note: Note) -> bool:
     possible_indices = self._remaining_indices[:]
     if self._length % 2 == 1:
         possible_indices.remove((math.floor(self._length / 2), 0))
     shuffle(possible_indices)
     index = None
     while len(possible_indices) > 0:
         index = possible_indices.pop()
         if not self._passes_insertion_check(note, index):
             continue
         #if it passes insertion checks, make sure last two intervals are not invalid
         next_note = self._get_next_note(index)
         if next_note is not None:
             last_note = self._counterpoint[(self._length - 1, 0)]
             if next_note.get_scale_degree_interval(
                     last_note
             ) == -2 and note.get_scale_degree_interval(next_note) < -2:
                 continue
         break
     if len(possible_indices) == 0:
         return False
     self._counterpoint[index] = note
     self._remaining_indices.remove(index)
     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
 def _valid_range(self, note1: Note, note2: Note) -> bool:
     if self._valid_outline(note1, note2): return True
     if note1.get_scale_degree_interval(note2) == 7: return True
     return False
Пример #19
0
 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