def estimate_best_meter(self, melody: old.Melody) -> tuple: """Return (Melody, TimeSignature) - pair.""" meter_fitness_pairs = [] for meter in self.potential_meters: n_potential_upbeats = int( fractions.Fraction(meter[0].numerator, meter[0].denominator) / fractions.Fraction(1, 8) ) for n_upbeats in range(n_potential_upbeats): adadpted_melody = melody.copy() if n_upbeats: adadpted_melody.insert( 0, old.Rest(n_upbeats * fractions.Fraction(1, 8)) ) metricity = self._calculate_metricity_of_melody(meter, adadpted_melody) meter_fitness_pairs.append((adadpted_melody, meter[0], metricity)) return max(meter_fitness_pairs, key=operator.itemgetter(2))[:2]
def __call__(self, melody: old.Melody) -> old.Melody: new_melody = melody.copy() melody_size = len(melody) for idx, tone in enumerate(new_melody): halved_duration = tone.duration * 0.5 # only add ornamentation if there isn't any glissando yet if (not tone.glissando and not tone.pitch.is_empty and halved_duration > self.__minima_gissando_duration): previous = None following = None previous_distance = None following_distance = None if idx != 0 and not melody[idx - 1].pitch.is_empty: previous = melody[idx - 1] previous_distance = previous.pitch.cents - tone.pitch.cents if idx + 1 != melody_size and not melody[idx + 1].pitch.is_empty: following = melody[idx + 1] following_distance = following.pitch.cents - tone.pitch.cents beginning_and_end_glissando = ( previous is not None and abs(previous_distance) > self.__minima_gissando_size, following is not None and abs(following_distance) > self.__minima_gissando_size, ) if any(beginning_and_end_glissando): if next(self.__al): if all(beginning_and_end_glissando): glissando_type = next( self.__glissando_type_generator) else: glissando_type = beginning_and_end_glissando.index( True) glissando_type = ((True, False), (False, True), (True, True))[glissando_type] glissando_line = [] is_first = True for is_allowed, distance in zip( glissando_type, (previous_distance, following_distance)): if is_allowed: data = self.get_glissando_values( halved_duration, distance) remaining_time = halved_duration - data[1] if is_first: data = ( old.PitchInterpolation( data[1], mel.SimplePitch(0, data[0])), old.PitchInterpolation( remaining_time, mel.SimplePitch(0, 0)), ) else: data = ( old.PitchInterpolation( remaining_time, mel.SimplePitch(0, 0)), old.PitchInterpolation( data[1], mel.SimplePitch(0, 0)), old.PitchInterpolation( 0, mel.SimplePitch(0, data[0])), ) else: data = [ old.PitchInterpolation( halved_duration, mel.SimplePitch(0, 0)) ] if not is_first: data.append( old.PitchInterpolation( 0, mel.SimplePitch(0, 0))) glissando_line.extend(data) is_first = False new_melody[idx].glissando = old.GlissandoLine( interpolations.InterpolationLine(glissando_line)) return new_melody
def transform_melody( self, melody: old.Melody, mapping: dict = { instr: prime for prime, instr in zip(globals_.METRICAL_PRIMES, ("violin", "viola", "cello")) }, ) -> tuple: prime_number_per_event = [] for tone in melody: if tone.pitch.is_empty: if prime_number_per_event: prime_number_per_event.append(prime_number_per_event[-1]) else: prime_number_per_event.append(None) else: prime_number_per_event.append( mapping[globals_.PITCH2INSTRUMENT[tone.pitch.normalize()]]) if prime_number_per_event[0] is None: prime_number_per_event[0] = int(prime_number_per_event[1]) # as high metricity as possible / as low deviation as possible hof = crosstrainer.MultiDimensionalRating(size=2, fitness=[1, -1]) n_possible_offsets = len( self._rhythm_and_metricity_per_prime[prime_number_per_event[0]][0]) for n_offsets in range(n_possible_offsets): if n_offsets > 0: adapted_melody = melody.copy() offset_duration = sum(self._rhythm_and_metricity_per_prime[ prime_number_per_event[0]][0][:n_offsets]) adapted_melody.insert( 0, old.Tone(mel.TheEmptyPitch, delay=offset_duration)) adapted_prime_number_per_event = ( prime_number_per_event[0], ) + tuple(prime_number_per_event) else: adapted_melody = melody.copy() adapted_prime_number_per_event = tuple(prime_number_per_event) expected_distances = tuple( fractions.Fraction(d) for d in adapted_melody.delay) positions = [ Point( adapted_prime_number_per_event[0], 0, 0, self._absolute_rhythm_and_metricity_per_prime, self.duration, ) ] for expected_distance, prime_number in zip( expected_distances, adapted_prime_number_per_event[1:] + (adapted_prime_number_per_event[-1], ), ): positions.append(positions[-1].find_next_position( prime_number, expected_distance)) absolute_rhythm = tuple(p.position for p in positions) complete_duration = (positions[-1].nth_loop + 1) * self.duration if absolute_rhythm[-1] != complete_duration: absolute_rhythm += ((positions[-1].nth_loop + 1) * self.duration, ) adapted_melody.append(old.Tone(mel.TheEmptyPitch, delay=1)) relative_rhythm = tuple( b - a for a, b in zip(absolute_rhythm, absolute_rhythm[1:])) new_melody = old.Melody([ old.Tone(pitch=t.pitch, volume=t.volume, delay=r, duration=r) for t, r in zip(adapted_melody, relative_rhythm) ]) summed_metricity = sum(p.metricity for p in positions[:-1]) / len(positions) summed_deviation = sum( abs(exp - real) for exp, real in zip(expected_distances, relative_rhythm)) hof.append( (new_melody, positions[-1].nth_loop + 1), summed_metricity, summed_deviation, ) best = hof.convert2list()[-1] return (best[0][0], lambda: self.spread(best[0][-1], mapping)), best[1]