"""Nightlight composition for 2023/04/25-28."""
import itertools import ranges from mutwo import common_generators from mutwo import core_events from mutwo import core_parameters from mutwo import music_events from mutwo import project_generators from mutwo import timeline_interfaces def is_supported(context, **kwargs): try: assert int(context.moon_phase_index) in (24, 25, 26, 27) assert hasattr(context.orchestration, "AEOLIAN_HARP") except AssertionError: return False return True def main(context, random, **kwargs) -> timeline_interfaces.EventPlacement: scale = context.scale aeolian_harp = context.orchestration.AEOLIAN_HARP duration = context.end - context.start string_tuple, activity_level_tuple = find_main_chord(scale, aeolian_harp) string_and_activity_list = list(zip(string_tuple, activity_level_tuple)) for string in aeolian_harp.string_tuple: if string not in string_tuple: string_and_activity_list.append((string, ACTIVITY_LEVEL_TUPLE[-1])) assert len(string_and_activity_list) == aeolian_harp.TOTAL_STRING_COUNT sim = core_events.TaggedSimultaneousEvent( tag=aeolian_harp.name, ) string_and_activity_list.sort( key=lambda string_and_activity: string_and_activity[0].index ) for string, activity in string_and_activity_list: sim.append(make_sequential_event(string, activity, duration, random)) return timeline_interfaces.EventPlacement( core_events.SimultaneousEvent([sim]), context.start, context.end, ) def find_main_chord(scale, aeolian_harp, minp=3, maxp=4): main_pitch = scale.scale_position_to_pitch((0, 0)) main_pitch, *_ = aeolian_harp.get_pitch_variant_tuple(main_pitch) pitch_to_choose_from_tuple = tuple( s.tuning for s in aeolian_harp.string_tuple if s.tuning != main_pitch and s.tuning in scale.pitch_tuple ) chord_tuple = project_generators.find_chord_tuple( (main_pitch,), pitch_to_choose_from_tuple, pitch_count_range=ranges.Range(minp, maxp), min_harmonicity=None, max_harmonicity=None, ) chord = max(chord_tuple, key=lambda c: c.harmonicity) p0, p1 = (p for p in chord.pitch_tuple if p != main_pitch) h0, h1 = ((p - main_pitch).harmonicity_simplified_barlow for p in (p0, p1)) if h0 > h1: level0, level1 = ACTIVITY_LEVEL_TUPLE[1], ACTIVITY_LEVEL_TUPLE[2] else: level1, level0 = ACTIVITY_LEVEL_TUPLE[1], ACTIVITY_LEVEL_TUPLE[2] string_tuple = chord_to_string_tuple(chord.pitch_tuple, aeolian_harp) activity_level_list = [] # stupid pattern matching language design # https://stackoverflow.com/questions/67525257/capture-makes-remaining-patterns-unreachable class N(object): p_main = main_pitch p_0 = p0 p_1 = p1 for string in string_tuple: match string.tuning: case N.p_main: activity_level_list.append(ACTIVITY_LEVEL_TUPLE[0]) case N.p_0: activity_level_list.append(level0) case N.p_1: activity_level_list.append(level1) case _: raise RuntimeError(str(string.tuning)) return string_tuple, tuple(activity_level_list) ACTIVITY_LEVEL_TUPLE = (9, 7, 5, 1) def chord_to_string_tuple(chord, aeolian_harp): return tuple(s for s in aeolian_harp.string_tuple if s.tuning in chord) def make_sequential_event(string, level, duration, random): activity_level = common_generators.ActivityLevel( next(LEVEL_TO_START_AT_CYCLE[level]) ) match level: case 1: tone_duration_range = ranges.Range(25, 45) case 5: tone_duration_range = ranges.Range(10, 15) case 7: tone_duration_range = ranges.Range(8, 15) case 9: tone_duration_range = ranges.Range(4, 10) case _: raise NotImplementedError(level) duration_range_tuple = ( # rest ranges.Range(100, 220), # tone tone_duration_range, ) sequential_event = core_events.SequentialEvent([]) seq_duration = core_parameters.DirectDuration(0) while seq_duration < duration: note_like = music_events.NoteLike() if is_active := activity_level(level): note_like.pitch_list = [string.tuning] note_like.envelope = random.choice( ["BASIC", "BASIC_QUIET", "BASIC_LOUD", "PLUCK_0", "PLUCK_1"] ) note_like.frequency_factor = random.choice([1, 0.5, 0.25, 0.125, 2]) duration_range = duration_range_tuple[is_active] note_like.duration = random.uniform(duration_range.start, duration_range.end) sequential_event.append(note_like) seq_duration += note_like.duration diff = seq_duration - duration sequential_event[-1].duration -= diff return sequential_event LEVEL_TO_START_AT_CYCLE = {lv: itertools.cycle((0, 1, 2)) for lv in range(11)}
"""Nightlight composition for 2023/04/09, 2023/04/08 & 2023/04/07."""
import ranges from mutwo import core_events from mutwo import core_parameters from mutwo import music_events from mutwo import timeline_interfaces def is_supported(context, **kwargs): try: assert int(context.moon_phase_index) in (6, 7, 8) assert hasattr(context.orchestration, "AEOLIAN_HARP") except AssertionError: return False return True def main( context, activity_level, random, **kwargs ) -> timeline_interfaces.EventPlacement: aeolian_harp = context.orchestration.AEOLIAN_HARP duration = context.end - context.start simultaneous_event = core_events.TaggedSimultaneousEvent(tag=aeolian_harp.name) for string in aeolian_harp.string_tuple: simultaneous_event.append( make_sequential_event( string, duration, random.choice((6, 7, 8)), activity_level, random ) ) return timeline_interfaces.EventPlacement( core_events.SimultaneousEvent([simultaneous_event]), context.start, context.end, ) def make_sequential_event(string, duration, level, activity_level, random): duration_range_tuple = (ranges.Range(6, 15), ranges.Range(1.2, 4)) sequential_event = core_events.SequentialEvent([]) seqd = core_parameters.DirectDuration(0) while seqd < duration: note_like = music_events.NoteLike() if is_active := activity_level(level): note_like.pitch_list = [string.tuning] note_like.envelope = random.choice( ["PLUCK_0", "PLUCK_1"] ) note_like.frequency_factor = random.uniform(0.015, 0.02) duration_range = duration_range_tuple[is_active] note_like.duration = random.uniform(duration_range.start, duration_range.end) sequential_event.append(note_like) seqd += note_like.duration return sequential_event.set("duration", duration)
"""Nightlight composition for 2023/04/29."""
import ranges from mutwo import core_events from mutwo import core_parameters from mutwo import music_events from mutwo import timeline_interfaces def is_supported(context, **kwargs): try: assert int(context.moon_phase_index) in (28,) assert hasattr(context.orchestration, "AEOLIAN_HARP") except AssertionError: return False return True def main( context, activity_level, random, **kwargs ) -> timeline_interfaces.EventPlacement: aeolian_harp = context.orchestration.AEOLIAN_HARP duration = context.end - context.start simultaneous_event = core_events.TaggedSimultaneousEvent(tag=aeolian_harp.name) for string in aeolian_harp.string_tuple: simultaneous_event.append( make_sequential_event( string, duration, random.choice((7, 8, 9)), activity_level, random ) ) return timeline_interfaces.EventPlacement( core_events.SimultaneousEvent([simultaneous_event]), context.start, context.end, ) def make_sequential_event(string, duration, level, activity_level, random): duration_range_tuple = (ranges.Range(5, 10), ranges.Range(2, 5)) sequential_event = core_events.SequentialEvent([]) seqd = core_parameters.DirectDuration(0) while seqd < duration: note_like = music_events.NoteLike() if is_active := activity_level(level): note_like.pitch_list = [string.tuning] note_like.envelope = random.choice( ["BASIC", "BASIC_QUIET", "PLUCK_0", "PLUCK_1"] ) note_like.frequency_factor = random.uniform(0.105, 0.2) duration_range = duration_range_tuple[is_active] note_like.duration = random.uniform(duration_range.start, duration_range.end) sequential_event.append(note_like) seqd += note_like.duration return sequential_event.set("duration", duration)
"""Nightlight composition for 2023/04/22."""
import ranges from mutwo import common_generators from mutwo import core_events from mutwo import music_events from mutwo import project_generators from mutwo import timeline_interfaces def is_supported(context, **kwargs): try: assert int(context.moon_phase_index) in (21,) assert hasattr(context.orchestration, "AEOLIAN_HARP") except AssertionError: return False return True def main(context, random, **kwargs) -> timeline_interfaces.EventPlacement: scale = context.scale aeolian_harp = context.orchestration.AEOLIAN_HARP duration = context.end - context.start main_chord = find_main_chord(scale, aeolian_harp) side_chord = find_side_chord(scale, aeolian_harp, main_chord) chord_tuple = (side_chord, main_chord) sim = core_events.TaggedSimultaneousEvent( [ core_events.SequentialEvent([core_events.SimpleEvent(duration)]) for _ in range(9) ], tag=aeolian_harp.name, ) # We use local activity level :) activity_level = common_generators.ActivityLevel() # True for main chord, # False for low chord chord_activity_distribution = 7 # True for tone # False for rest tone_rest_activity_level = 6 absolute_time = 0 duration_range_tuple = ( # rest ranges.Range(45, 100), # tone ranges.Range(11, 22), ) while absolute_time < duration: is_tone = activity_level(tone_rest_activity_level) duration_range = duration_range_tuple[is_tone] min_duration, max_duration = duration_range.start, duration_range.end d = random.uniform(min_duration, max_duration) new_absolute_time = absolute_time + d if new_absolute_time > duration: new_absolute_time = duration d = new_absolute_time - absolute_time if is_tone and d > min_duration: chord = chord_tuple[activity_level(chord_activity_distribution)] for string in chord: n = music_events.NoteLike(string.tuning, d) n.envelope = random.choice(["BASIC_QUIET", "PLUCK_0", "PLUCK_1"]) n.frequency_factor = random.choice([0.5, 0.25]) sim[string.index].squash_in(absolute_time, n) absolute_time = new_absolute_time print(absolute_time) print("FINISHED ENTRY") return timeline_interfaces.EventPlacement( core_events.SimultaneousEvent([sim]), context.start, context.end, ) def find_main_chord(scale, aeolian_harp): main_pitch = scale.scale_position_to_pitch((0, 0)) main_pitch, *_ = aeolian_harp.get_pitch_variant_tuple(main_pitch) pitch_to_choose_from_tuple = tuple( s.tuning for s in aeolian_harp.string_tuple if s.tuning != main_pitch and s.tuning in scale.pitch_tuple ) chord_tuple = project_generators.find_chord_tuple( (main_pitch,), pitch_to_choose_from_tuple, pitch_count_range=ranges.Range(2, 3), min_harmonicity=None, max_harmonicity=None, ) chord = max(chord_tuple, key=lambda c: c.harmonicity) return chord_to_string_tuple(chord.pitch_tuple, aeolian_harp) def find_side_chord(scale, aeolian_harp, main_chord): pitch_to_choose_from_tuple = tuple( s.tuning for s in aeolian_harp.string_tuple if s.tuning not in main_chord ) chord_tuple = project_generators.find_chord_tuple( tuple([]), pitch_to_choose_from_tuple, pitch_count_range=ranges.Range(2, 3), min_harmonicity=None, max_harmonicity=None, ) chord = max(chord_tuple, key=lambda c: c.harmonicity) return chord_to_string_tuple(chord.pitch_tuple, aeolian_harp) def chord_to_string_tuple(chord, aeolian_harp): return tuple(s for s in aeolian_harp.string_tuple if s.tuning in chord)