aboutsummaryrefslogtreecommitdiffstats
path: root/hex33board/scale.py
blob: cb6dc6b8270f073f1d2204d374f30ff1bf2b4687 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
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,
        )