def split(sequential_event: events.basic.SequentialEvent, nth_child: int, *durations: parameters.abc.DurationType): child = sequential_event[nth_child] difference = child.duration - sum(durations) assert difference >= 0 absolute_time = sequential_event.absolute_times[nth_child] for duration in durations: absolute_time += duration sequential_event.split_child_at(absolute_time)
def _split_concatenated_cengkok_chain_to_cengkok_chain( self, concatenated_cengkok_chain: events.basic.SequentialEvent ) -> typing.Tuple[events.basic.SequentialEvent, ...]: concatenated_cengkok_chain.cut_off(0, self._added_delay_size) cengkok_chain = [events.basic.SequentialEvent([])] for index, event in enumerate(concatenated_cengkok_chain): cengkok_chain[-1].append(event) if event.is_goal and index + 1 != len(concatenated_cengkok_chain): cengkok_chain.append(events.basic.SequentialEvent([])) return cengkok_chain
def delay( sequential_event: events.basic.SequentialEvent, nth: int, duration: parameters.abc.DurationType, ): previous_event_index = nth - 1 if previous_event_index >= 0 and is_rest( sequential_event[previous_event_index]): sequential_event[previous_event_index].duration += duration else: sequential_event.insert(nth, events.music.NoteLike([], duration)) sequential_event[nth].duration -= duration
def _prolong_notes_before_next_bar( self, sequential_event: events.basic.SequentialEvent ) -> events.basic.SequentialEvent: for absolute_time in self.absolute_times[1:]: item_index = sequential_event.get_event_index_at(absolute_time) responsible_event = sequential_event[item_index] if hasattr(responsible_event, "is_root") and responsible_event.is_root: previous_item = sequential_event[item_index - 1] if ( hasattr(previous_item, "pitch_or_pitches") and previous_item.pitch_or_pitches ): if not previous_item.playing_indicators.glissando.is_active: if ( responsible_event.duration.denominator % 3 != 0 and previous_item.duration.denominator % 3 != 0 ): if self.prolong_al_generator(self.prolong_al): if responsible_event.duration.denominator % 3 == 0: prolong_duration = fractions.Fraction(1, 12) else: prolong_duration = fractions.Fraction(1, 16) if responsible_event.duration > prolong_duration: responsible_event.duration -= prolong_duration previous_item.duration += prolong_duration return sequential_event
def _prolong_last_beat_of_previous_bar( self, restructured_phrase_parts: typing.Tuple[structure.RestructuredPhrasePart, ...], sequential_event: events.basic.SequentialEvent, ): is_first = True for absolute_time, restructured_phrase_part in zip( restructured_phrase_parts.absolute_times, restructured_phrase_parts ): event_index_to_shorten = sequential_event.get_event_index_at(absolute_time) event_to_prolong = sequential_event[event_index_to_shorten - 1] event_to_shorten = sequential_event[event_index_to_shorten] if ( restructured_phrase_part.bar_character not in (structure.END, structure.END_AND_START) and not is_first and hasattr(event_to_prolong, "pitch_or_pitches") and event_to_prolong.pitch_or_pitches and event_to_shorten.duration.denominator % 2 == 0 and event_to_shorten.duration.denominator % 3 != 0 and event_to_prolong.duration.denominator % 2 == 0 and event_to_prolong.duration.denominator % 3 != 0 ): if self.prolong_al_generator(self.prolong_al): event_to_shorten.duration /= 2 event_to_prolong.duration += event_to_shorten.duration is_first = False return sequential_event
def shorten( sequential_event: events.basic.SequentialEvent, nth: int, duration: parameters.abc.DurationType, add_rest: bool = True, ): sequential_event[nth].duration -= duration next_index = nth + 1 try: next_event = sequential_event[next_index] except IndexError: next_event = None if next_event and (is_rest(next_event) or not add_rest): sequential_event[next_index].duration += duration else: sequential_event.insert(nth + 1, events.music.NoteLike([], duration))
def _make_moving_part( self, nth_grid: int, n_grids: int, grid_data, restructured_phrase_part: structure.RestructuredPhrasePart, next_restructured_phrase_part: structure.RestructuredPhrasePart, restructured_phrase_parts: typing.Tuple[structure.RestructuredPhrasePart, ...], cengkok_line: events.basic.SequentialEvent, counter_melody: events.basic.SequentialEvent, ): grid_step_size, grid = grid_data if restructured_phrase_part.bar_character == structure.CADENZA: density = self.cadenza_density else: density = self._density_curve.value_at(nth_grid / n_grids) n_added_notes = int((len(grid) - 1) * density) positions = tuple( utilities.tools.accumulate_from_zero([grid_step_size for _ in grid]) )[1:-1] choosen_indices = tuple( sorted( self._random.choice( positions, p=utilities.tools.scale_sequence_to_sum(grid[1:], 1), size=n_added_notes, replace=False, ) ) ) rhythm = events.basic.SequentialEvent([]) for position0, position1 in zip( (0,) + choosen_indices, choosen_indices + (len(grid) * grid_step_size,), ): duration = position1 - position0 rhythm.append( events.music.NoteLike( [ restructured_phrase_part.phrase_event.root - parameters.pitches.JustIntonationPitch("2/1") ], duration=duration, ) ) cut_out_start = counter_melody.duration cengkok_extract = cengkok_line.cut_out( cut_out_start, cut_out_start + rhythm.duration, mutate=False ) self._assign_pitches( rhythm, restructured_phrase_parts[nth_grid], next_restructured_phrase_part, cengkok_extract, ) return rhythm
def __call__( self, bar_to_adapt: events.basic.SequentialEvent, nth_bar: int, modulation_pitch: parameters.pitches.JustIntonationPitch, ) -> events.basic.SequentialEvent: if self.gantungan_al(self.activity_level): return self._add_gantungan(bar_to_adapt, nth_bar, modulation_pitch) else: return bar_to_adapt.copy()
def __call__( self, bar_to_adapt: events.basic.SequentialEvent, nth_bar: int, modulation_pitch: parameters.pitches.JustIntonationPitch, ) -> events.basic.SequentialEvent: bar_to_adapt = bar_to_adapt.copy() bar_to_adapt[0].duration = bar_to_adapt.duration for _ in bar_to_adapt[1:]: del bar_to_adapt[1] bar_to_adapt[0].pitch_or_pitches[0].register(-1) bar_to_adapt[0].pitch_or_pitches[0].add(modulation_pitch) return bar_to_adapt
def _convert_sequential_event( self, sequential_event_to_convert: events.basic.SequentialEvent, absolute_entry_delay: parameters.abc.DurationType, ) -> events.basic.SequentialEvent: if isinstance(sequential_event_to_convert[0], events.basic.SimpleEvent): sequential_event_to_apply_pitches_to = sequential_event_to_convert.copy() self._apply_pitches_on_sequential_event( sequential_event_to_apply_pitches_to ) return sequential_event_to_apply_pitches_to else: return super()._convert_sequential_event( sequential_event_to_convert, absolute_entry_delay )
def _add_gantungan( self, bar_to_adapt: events.basic.SequentialEvent, nth_bar: int, modulation_pitch: parameters.pitches.JustIntonationPitch, ) -> events.basic.SequentialEvent: adapted_bar = bar_to_adapt.copy() to_remain = self.gantungan_pattern_duration absolute_times = adapted_bar.absolute_times items_to_remain = 1 for absolute_time in reversed(absolute_times): if absolute_time % to_remain == 0: break else: items_to_remain += 1 adapted_bar = adapted_bar[-items_to_remain:] free_space = bar_to_adapt.duration - adapted_bar.duration if free_space: n_gantungan_pattern = int(free_space // to_remain) gantungan = events.basic.SequentialEvent([]) for nth_gantungan_pattern in range(n_gantungan_pattern): nth_gantungan_pattern_percentage = nth_gantungan_pattern / ( n_gantungan_pattern ) gantungan_pattern = next(self.gantungan_pattern_cycle) gantungan.extend( self._convert_gantungan_pattern_to_sequential_event( gantungan_pattern, nth_bar, modulation_pitch, nth_gantungan_pattern_percentage, ) ) adapted_bar = gantungan + adapted_bar assert adapted_bar.duration == bar_to_adapt.duration return adapted_bar
def _get_pitches_by_backtracking( self, ambitus: ot2_parameters.ambitus.Ambitus, start_pitch: parameters.pitches.JustIntonationPitch, end_pitch: parameters.pitches.JustIntonationPitch, cengkok_extract: events.basic.SequentialEvent, rhythm: events.basic.SequentialEvent, restructured_phrase_part: structure.RestructuredPhrasePart, ) -> typing.Sequence[parameters.pitches.JustIntonationPitch]: def is_valid(choices, choice_indices) -> bool: elements = tuple( choice[index] for index, choice in zip(choice_indices, choices) ) are_in_ambitus = all(map(ambitus.is_member, elements)) is_close_enough_to_goal_pitch = True if len(elements) == len(rhythm): is_close_enough_to_goal_pitch = ( abs((elements[-1] - end_pitch).cents) < 280 ) return are_in_ambitus and is_close_enough_to_goal_pitch def find_choices(): last_pitch = choices[-1][choice_indices[-1]] available_pitches = restructured_phrase_part.phrase_event.all_pitches valid_ambitus = ot2_parameters.ambitus.Ambitus( last_pitch - parameters.pitches.JustIntonationPitch("5/4"), last_pitch + parameters.pitches.JustIntonationPitch("5/4"), ) legal_pitches = [] for pitch in available_pitches: variants = valid_ambitus.find_all_pitch_variants(pitch) for variant in variants: if variant != last_pitch: legal_pitches.append(variant) legal_pitches = RestructuredPhrasePartsAndCengkokLineToCounterVoiceConverter._sort_legal_pitches( cengkok_part_per_event, choices, legal_pitches, last_pitch ) return legal_pitches cengkok_part_per_event = tuple( cengkok_extract.cut_out(start, end, mutate=False) for start, end in rhythm.start_and_end_time_per_event ) choices = [(start_pitch,)] choice_indices = [0] # backtracking algorithm while True: if is_valid(choices, choice_indices): if len(choices) < len(rhythm): choices.append(find_choices()) choice_indices.append(0) else: break else: while choice_indices[-1] + 1 == len(choices[-1]): choice_indices = choice_indices[:-1] choices = choices[:-1] if len(choices) == 0: raise ValueError("No solution found") choice_indices[-1] += 1 pitches = tuple(choice[index] for index, choice in zip(choice_indices, choices)) return pitches
def __call__( self, bar_to_adapt: events.basic.SequentialEvent, nth_bar: int, modulation_pitch: parameters.pitches.JustIntonationPitch, ) -> events.basic.SequentialEvent: # return bar_to_adapt goal_pitch = self._restructured_phrase_parts[nth_bar].connection_pitch1 if not goal_pitch: try: goal_pitch = self._restructured_phrase_parts[nth_bar + 1].root except IndexError: goal_pitch = self._restructured_phrase_parts[nth_bar].root goal_pitch.register(-1) goal_pitch.add(modulation_pitch) start_pitch = bar_to_adapt[0].pitch_or_pitches[0] if start_pitch == goal_pitch: return bar_to_adapt.copy() sorted_goal_and_start_pitch = sorted((goal_pitch, start_pitch)) ambitus = ot2_parameters.ambitus.Ambitus(*sorted_goal_and_start_pitch) pitch_line = [start_pitch, goal_pitch] for pitch in self._restructured_phrase_parts[nth_bar].all_pitches: for pitch_variant in ambitus.find_all_pitch_variants(pitch): if pitch_variant not in pitch_line: pitch_line.append(pitch_variant) sorted_pitch_line = sorted( pitch_line, reverse=start_pitch == sorted_goal_and_start_pitch[1] )[:-1] n_pitches_in_pitch_line = len(sorted_pitch_line) advanced_pitch_line = functools.reduce( operator.add, zip( sorted_pitch_line, sorted_pitch_line[2:] + list(reversed(sorted_pitch_line[-2:])), ), ) advanced_pitch_line = sorted_pitch_line n_factor = 1 n_needed_beats = n_pitches_in_pitch_line * n_factor available_duration = bar_to_adapt.duration pulse_size = available_duration n_pulses = 1 while n_pulses < n_needed_beats: pulse_size /= 2 n_pulses = int(available_duration / pulse_size) rhythm = generators.toussaint.euclidean(n_pulses, n_needed_beats) cadential_bar = events.basic.SequentialEvent([]) for n_beats, pitch in zip(rhythm, advanced_pitch_line): note = events.music.NoteLike( [pitch], duration=n_beats * pulse_size, volume="p" ) cadential_bar.append(note) try: cadential_bar[-2].pitch_or_pitches = [goal_pitch] except IndexError: cadential_bar[-1].pitch_or_pitches = [goal_pitch] assert cadential_bar.duration == bar_to_adapt.duration return cadential_bar