def slices_to_melodic_intervals(first, second) -> Tuple[NamedInterval, NamedInterval]: first_lower = first.leaves[0] second_lower = second.leaves[0] first_upper = first.leaves[1] second_upper = second.leaves[1] lower = NamedInterval.from_pitch_carriers(first_lower, second_lower) upper = NamedInterval.from_pitch_carriers(first_upper, second_upper) return lower, upper
def reward(self, state, action, state_prime): harmonic_intervals, melodic_intervals = MusicStateFeatureExtractor.last_n_intervals( 1, self.domain.current_duration, self.domain.voices[0], self.domain.given_voices[0]) if len(harmonic_intervals) > 0: last_interval = harmonic_intervals[0] if last_interval.interval_class in { NamedInterval("P1").interval_class, NamedInterval("P8").interval_class }: return 10 return -1 return -1
def grade_composition(self, composition: CompositionEnvironment) -> int: penalties = 0 working_staff = StaffGroup() working_staff.append(composition.voices[0]) working_staff.append(composition.voices[1]) interval_tally = {} pitch_tally = {} melodic_interval_tally = {} vertical_moments = list(iterate(working_staff).by_vertical_moment()) intervals = [] degrees = [[], []] for moment in vertical_moments: pitches = moment.leaves harmonic = NamedInterval.from_pitch_carriers( pitches[1], pitches[0]) intervals.append(harmonic) degrees[0].append( composition.composition_parameters.scale. named_pitch_class_to_scale_degree(pitches[0])) degrees[1].append( composition.composition_parameters.scale. named_pitch_class_to_scale_degree(pitches[1])) count = pitch_tally.setdefault(pitches[0], 0) + 1 pitch_tally[pitches[0]] = count count = pitch_tally.setdefault(pitches[1], 0) + 1 pitch_tally[pitches[1]] = count count = interval_tally.setdefault(harmonic, 0) + 1 interval_tally[harmonic] = count for i in range(len(vertical_moments)): vertical_moment = vertical_moments[i] harmonic = intervals[i] # No voice crossing if harmonic.direction_string == "descending": penalties -= 1 if abs(harmonic.semitones) in constants.dissonant_intervals: penalties -= 5 # No harmonic intervals greater than a 12th if abs(harmonic.semitones) > NamedInterval("P12").semitones: penalties -= 5 # Tenth is pushing it elif abs(harmonic.semitones) > NamedInterval("M10").semitones: penalties -= 1 maximum_extent = vertical_moment.offset + vertical_moment.leaves[ 0].written_duration if maximum_extent == composition.composition_parameters.duration: last_top = degrees[0][-1] last_bottom = degrees[1][-1] prev_top = degrees[0][-2] prev_bottom = degrees[1][-2] # Use an authentic cadence if last_top != ScaleDegree(1): penalties -= 10 if last_bottom != ScaleDegree(1): penalties -= 10 if not ((prev_top == ScaleDegree(7) and prev_bottom == ScaleDegree(2)) or (prev_top == ScaleDegree(2) and prev_bottom == ScaleDegree(7))): penalties -= 10 elif i is 0: # First interval should be a tonic unison if degrees[0][0] != ScaleDegree(1): penalties -= 5 if degrees[1][0] != ScaleDegree(1): penalties -= 5 if i > 0: prev_harmonic = intervals[i - 1] prev_slice = vertical_moments[i - 1] lower_melodic, upper_melodic = slices_to_melodic_intervals( prev_slice, vertical_moment) count = melodic_interval_tally.get(lower_melodic, 0) + 1 melodic_interval_tally[lower_melodic] = count count = melodic_interval_tally.get(upper_melodic, 0) + 1 melodic_interval_tally[upper_melodic] = count motion = characterize_relative_motion(upper_melodic, lower_melodic) # Lines should always be moving if motion is RelativeMotion.none: penalties -= 5 # Contrary motion is preferred if motion is RelativeMotion.similar or motion is RelativeMotion.oblique: penalties -= 1 # Never have two perfect consonances in a row if prev_harmonic.interval_class == harmonic.interval_class and harmonic.quality_string == "perfect": penalties -= 5 # Steps are preferred to leaps if is_leap(lower_melodic): penalties -= 1 if is_leap(upper_melodic): penalties -= 1 if i > 2: prev_prev_harmonic = intervals[i - 2] prev_prev_prev_harmonic = intervals[i - 3] all_same = same_harmonic_quality(harmonic, prev_harmonic, prev_prev_harmonic, prev_prev_prev_harmonic) # Don't have the same interval more than three times in a row if all_same: penalties -= 5 prev_lower_melodic, prev_upper_harmonic = slices_to_melodic_intervals( vertical_moments[i - 2], prev_slice) # Encourage counterstepwise motion. # If the prev motion was a leap.. if is_leap(prev_lower_melodic): # It needs to be resolved by a step, and the step needs to be in the opposite direction if not is_step(lower_melodic) or ( prev_lower_melodic.semitones > 0 ^ lower_melodic.semitones > 0): penalties -= 5 else: () # print("we did it") if is_leap(prev_upper_harmonic): if not is_step(upper_melodic) or ( prev_upper_harmonic.semitones > 0 ^ upper_melodic.semitones > 0): penalties -= 5 else: () # print("we did it") """ for pitch, num in pitch_tally.items(): if num > 2: penalties -= 5 * num for interval, num in interval_tally.items(): if num > 5: penalties -= 5 * num for interval, num in melodic_interval_tally.items(): if num > 5: penalties -= 5 * num """ return penalties
def interval_or_none(first, second): if first is None or second is None: return None else: return NamedInterval.from_pitch_carriers(first, second)
from abjad.tools.pitchtools.NamedInterval import NamedInterval from abjad.tools.pitchtools.PitchRange import PitchRange soprano_range = PitchRange("[C4,G5]") alto_range = PitchRange("[G3, D5]") tenor_range = PitchRange("[C3, C5]") bass_range = PitchRange("[E2, C4]") dissonant_intervals = {NamedInterval("m2").semitones, NamedInterval("m7").semitones, NamedInterval("M7").semitones, NamedInterval("aug2").semitones, NamedInterval("aug4").semitones, NamedInterval("dim5").semitones} consonant_intervals = {NamedInterval("M2").semitones, NamedInterval("M6").semitones, NamedInterval("M3"), NamedInterval("m3")} perfect_intervals = {NamedInterval("P4").semitones, NamedInterval("P5").semitones, NamedInterval("P8").semitones, NamedInterval("P11").semitones}
def reward(self, state, action, state_prime): if NamedInterval.from_pitch_carriers( self.domain.voices[1][-1], self.domain.voices[0][-1]).semitones != 4: return -1 return 0