def run(cycle: Cycle, note_duration, velocity, mode, channel): if mode is None: cycle.play_midi(note_duration=note_duration, velocity=velocity, channel=channel) elif mode == 'all': for i, m in enumerate(cycle.modes): if i != 0: time.sleep(1) m.play_midi(note_duration=note_duration, velocity=velocity, channel=channel) else: try: mode_number = int(mode) except ValueError: raise ScaleTheoryError( "Mode should be left blank, or be 'all' or an integer") try: m = cycle.modes[mode_number - 1] except ValueError: raise ScaleTheoryError( f"Mode number too high (max {len(cycle.modes)})") m.play_midi(note_duration=note_duration, velocity=velocity, channel=channel)
def scale_info(cycle: Cycle): print(cycle.name()) for mode in cycle.modes: print("unison ", end="") for jump, ivl in zip(mode.jumps, mode.intervals()): print(f"-{jump}-> ", ivl.name().ljust(10), end="") print(f"-{mode.jumps[-1]}-> ", "p8") for ivl in cycle.canon_mode.intervals(): print(ivl.cents(), end=", ") print() print( f"Consonant interval diversity: {count_present_consonances(cycle)}/{len(cycle.edo().consonances)}" ) print("Total dissonances:", count_dissonances(cycle)) print(cycle.interval_vector()) print( "Total chords:", *[ count_chords(cycle, [third])[third] for third in ["septimal m3", "m3", "n3", "maj3", "septimal maj3"] ]) print("Total tetrachords:") for name, degrees in TETRACHORDS.items(): if count_extensions(cycle, degrees) > 0: print(f" {name}: {count_extensions(cycle, degrees)}") print("Proper:", proper(cycle)) # TODO # print("MIDI notes:", ", ".join(map(midi_to_12edo_name, get_midi_numbers(cycle)))) print()
def count_chords(cycle: Cycle, third_names: List[str]): fifth = cycle.edo().approximate(JI.by_name("p5")) chords = dict([(name, 0) for name in third_names]) for mode in cycle.modes: if fifth in mode.interval_set(): for name, third in zip(third_names, intervals_in_edo(cycle.edo(), third_names)): if third in mode.interval_set(): chords[name] += 1 return chords
def count_distinct_chord_roots(cycle: Cycle, third_names: List[str]): fifth = cycle.edo().approximate(JI.by_name("p5")) total = 0 for mode in cycle.modes: if fifth in mode.interval_set(): for third in intervals_in_edo(cycle.edo(), third_names): if third in mode.interval_set(): total += 1 break return total
def resolve_cycle_name(edo_steps, cycle_name): cycle = Cycle.by_name(edo_steps, cycle_name) if cycle: return cycle else: try: jumps = [int(j) for j in cycle_name.replace(" ", "").split(",")] except ValueError: raise ScaleTheoryError(f"Not a known scale name or valid comma-separated list of integers: {cycle_name}") if sum(jumps) != edo_steps: raise ScaleTheoryError(f"Jumps should add to {edo_steps}, but actually add to {sum(jumps)}") return Cycle(jumps)
def print_family(parent: Cycle, lengths): subscales = parent.children() named_subscales = [c for c in subscales if c.name() is not None and c.size() in lengths] named_subscales.sort(key=lambda cycle: cycle.size()) for scale in named_subscales: scale_info(scale) scale_info(parent)
def proper(cycle: Cycle): degrees = [set() for _ in range(cycle.size() - 1)] for mode in cycle.modes: for i, ivl in enumerate(mode.intervals()): degrees[i].add(ivl) not_strict = False for deg1, deg2 in zip(degrees[:-1], degrees[1:]): x, y = max(deg1), min(deg2) if x > y: return Proper.IMPROPER elif x == y: not_strict = True return Proper.PROPER if not_strict else Proper.STRICTLY_PROPER
def run(length, edo_steps): t = Timer() computed_cycles = [] num_computed_modes = {} for method in METHODS: method.cache_clear() t.task(method.__name__) modes = list(method(length, edo_steps)) t.clear() computed_cycles.append(set([Cycle(mode) for mode in modes])) num_computed_modes[method.__name__] = len(modes) t.clear() for c in computed_cycles[1:]: # we want to make sure the algorithms produce the same end result assert c == computed_cycles[0] print(len(computed_cycles[0]), "cycles found.") print("Number of modes computed:") for method_name, num_modes in num_computed_modes.items(): print(method_name, num_modes) t.log()
def count_by_name(cycle: Cycle, name: str): return cycle.interval_counts()[cycle.edo().approximate(JI.by_name(name))]
def count_dissonances(cycle: Cycle): return -sum([cycle.interval_counts().get(d, 0) for d in cycle.edo().dissonances])
def count_present_consonances(cycle: Cycle): return len(set(cycle.edo().consonances) & cycle.interval_set())
def count_extensions(cycle: Cycle, ivl_names: List[str]): total = 0 for mode in cycle.modes: if all(ivl in mode.interval_set() for ivl in intervals_in_edo(cycle.edo(), ivl_names)): total += 1 return total
def interval_diversity(cycle: Cycle, important_intervals: List[str] = None): intervals = cycle.interval_set() if important_intervals: intervals = intervals & intervals_in_edo(cycle.edo(), important_intervals) return len(intervals)
def list_best_subcycles(cycle: Cycle, priorities, length): list_best_cycles_from(cycle.children(length), priorities)