from __future__ import annotations class Scale: STEPS = { "major": [2, 2, 1, 2, 2, 2, 1], "min nat": [2, 1, 2, 2, 1, 2, 2], "min harm": [2, 1, 2, 2, 1, 3, 1], "min mel": [2, 1, 2, 2, 2, 2, 1], "min hung": [2, 1, 3, 1, 1, 3, 1], "whole": [2, 2, 2, 2, 2, 2], "penta": [2, 2, 3, 2, 3], } # LABELS = { # 'english': 'C C# D D# E F F# G G# A A# B'.split(' '), # 'german': 'C C# D D# E F F# G G# A A# H'.split(' '), # 'sol': 'do do# re re# mi fa fa# sol sol# la la# si'.split(' '), # } LABELS = "C C# D D# E F F# G G# A A# B".split(" ") root: int name: str notes: list[int] size: int def __init__(self, root: int, steps: list[int], name: str): self.root = root self.name = name self.notes = [sum(steps[:i]) for i in range(len(steps))] self.size = sum(steps) def is_in_scale(self, pitch: int) -> Union[bool, Literal["core"]]: relative_pitch = (pitch - self.root) % self.size if relative_pitch not in self.notes: return False if self.root <= pitch < self.root + self.size: return "core" return True def label(self, pitch, octave=False): off = pitch % self.size oct = "" if octave: oct = str(self.root // self.size) return self.LABELS[off] + oct def format(self): oct = self.root // self.size off = self.root % self.size return "SCALE: {} {} {}".format( self.LABELS[off], oct, self.name, )