Домашни > Pitches love the D > Решения > Решението на Йоан Байчев

Резултати
10 точки от тестове
0 точки от учител

10 точки общо

36 успешни теста
1 неуспешни теста
Код

  1class Tone:
  2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
  3
  4    def __init__(self, input_name):
  5        if input_name not in self.TONES:
  6            raise ValueError("Invalid tone name")
  7        self.name_of_tone = input_name
  8
  9    def __str__(self):
 10        return self.name_of_tone
 11
 12    def __hash__(self):  # this time i didn't use it
 13        return hash(self.name_of_tone)
 14
 15    def __eq__(self, other):
 16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
 17
 18    def __add__(self, other):
 19        if isinstance(other, Tone):
 20            return self._calculate_tone(other, "+")
 21        elif isinstance(other, Interval):
 22            return self._calculate_interval(other, "+")
 23        raise TypeError("Invalid operation")
 24
 25    def __sub__(self, other):
 26        if isinstance(other, Tone):
 27            return self._calculate_tone(other, "-")
 28        elif isinstance(other, Interval):
 29            return self._calculate_interval(other, "-")
 30        raise TypeError("Invalid operation")
 31
 32    def get_index(self):
 33        return Tone.TONES.index(self.name_of_tone)
 34
 35    def _calculate_tone(self, other, symbol):
 36        if symbol == "+":
 37            return Chord(self, other)
 38        elif symbol == "-":
 39            distance = (self.TONES.index(self.name_of_tone) -
 40                        self.TONES.index(other.name_of_tone)) % 12
 41            return Interval(distance)
 42        else:
 43            raise ValueError("Invalid symbol")
 44
 45    def _calculate_interval(self, other, symbol):
 46        if symbol == "+":
 47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
 48            return Tone(self.TONES[index])
 49        elif symbol == "-":
 50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
 51            return Tone(self.TONES[index])
 52        else:
 53            raise ValueError("Invalid symbol")
 54
 55
 56class Interval:
 57    INTERVAL_NAMES = [
 58        "unison", "minor 2nd", "major 2nd", "minor 3rd",
 59        "major 3rd", "perfect 4th", "diminished 5th",
 60        "perfect 5th", "minor 6th", "major 6th",
 61        "minor 7th", "major 7th"
 62    ]
 63
 64    def __init__(self, number):
 65        if isinstance(number, int):
 66            self.semitones = number % 12
 67        else:
 68            raise TypeError("Invalid input")
 69
 70    def __str__(self):
 71        return self.INTERVAL_NAMES[self.semitones]
 72
 73    def __add__(self, other):
 74        if isinstance(other, Interval):
 75            return Interval(self.semitones + other.semitones)
 76        raise TypeError("Invalid operation")
 77
 78    def __neg__(self):
 79        return Interval(-self.semitones)
 80
 81
 82class Chord:
 83    def __init__(self, root, *tones):
 84        if not isinstance(root, Tone):
 85            raise ValueError("Root must be a Tone instance")
 86
 87        for tone in tones:
 88            if not isinstance(tone, Tone):
 89                raise ValueError("All tones must be Tone instances")
 90
 91        unique_tones = [root]
 92        for tone in tones:
 93            if tone not in unique_tones: #using __eq__
 94                unique_tones.append(tone)
 95
 96        if len(unique_tones) < 2:
 97            raise TypeError("Cannot have a chord made of only 1 unique tone")
 98
 99        self.root = root
100        self.tones = unique_tones
101
102    def __str__(self):
103        root_index = Tone.TONES.index(self.root.name_of_tone)
104        other_tones = [tone for tone in self.tones if tone != self.root]
105        sorted_tones = sorted(
106            other_tones,
107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12
108        )
109        sorted_tones.insert(0, self.root)
110        return "-".join(str(tone) for tone in sorted_tones)
111
112    def __add__(self, other):
113        if isinstance(other, Tone):
114            return self._add_tone(other)
115        elif isinstance(other, Chord):
116            return self._add_chord(other)
117        raise TypeError("Invalid operation")
118
119    def __sub__(self, other):
120        if isinstance(other, Tone):
121            if other not in self.tones:  # using __eq__
122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
123            new_tones = [tone for tone in self.tones if tone != other]
124            if len(set(new_tones)) < 2:
125                raise TypeError("Cannot have a chord made of only 1 unique tone")
126            return Chord(*new_tones)
127        raise TypeError("Invalid operation")
128
129    def _validate_distance_to_root(self, target):
130        root_index = self.root.get_index()
131        for tone in self.tones:
132            distance = (tone.get_index() - root_index) % 12
133            if distance == target:
134                return True
135        return False
136
137    def is_minor(self):
138        return self._validate_distance_to_root(3)
139
140    def is_major(self):
141        return self._validate_distance_to_root(4)
142
143    def is_power_chord(self):
144        return not self.is_minor() and not self.is_major()
145
146    def _add_tone(self, other):
147        if other not in self.tones:  # using __eq__
148            new_tones = list(self.tones)
149            new_tones.append(other)
150            return Chord(*new_tones)
151        else:
152            return self
153
154    def _add_chord(self, other):
155        new_tones = list(self.tones)
156        for tone in other.tones:
157            if tone not in self.tones:  # using __eq__
158                new_tones.append(tone)
159        return Chord(*new_tones)
160
161    def transposed(self, input_interval):
162        if not isinstance(input_interval, Interval):
163            raise ValueError("Interval must be an instance of Interval")
164
165        transposed_tones = [tone + input_interval for tone in self.tones]
166        return Chord(*transposed_tones)

...........................F.........
======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation

----------------------------------------------------------------------
Ran 37 tests in 0.001s

FAILED (failures=1)

Дискусия
Виктор Бечев
02.11.2024 22:03

Отвъд забележките по-горе - good job.
История

f1class Tone:f1class Tone:
2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33
4    def __init__(self, input_name):4    def __init__(self, input_name):
5        if input_name not in self.TONES:5        if input_name not in self.TONES:
6            raise ValueError("Invalid tone name")6            raise ValueError("Invalid tone name")
7        self.name_of_tone = input_name7        self.name_of_tone = input_name
88
9    def __str__(self):9    def __str__(self):
10        return self.name_of_tone10        return self.name_of_tone
1111
12    def __hash__(self):  # this time i didn't use it12    def __hash__(self):  # this time i didn't use it
13        return hash(self.name_of_tone)13        return hash(self.name_of_tone)
1414
15    def __eq__(self, other):15    def __eq__(self, other):
16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
1717
18    def __add__(self, other):18    def __add__(self, other):
19        if isinstance(other, Tone):19        if isinstance(other, Tone):
20            return self._calculate_tone(other, "+")20            return self._calculate_tone(other, "+")
21        elif isinstance(other, Interval):21        elif isinstance(other, Interval):
22            return self._calculate_interval(other, "+")22            return self._calculate_interval(other, "+")
23        raise TypeError("Invalid operation")23        raise TypeError("Invalid operation")
2424
25    def __sub__(self, other):25    def __sub__(self, other):
26        if isinstance(other, Tone):26        if isinstance(other, Tone):
27            return self._calculate_tone(other, "-")27            return self._calculate_tone(other, "-")
28        elif isinstance(other, Interval):28        elif isinstance(other, Interval):
29            return self._calculate_interval(other, "-")29            return self._calculate_interval(other, "-")
30        raise TypeError("Invalid operation")30        raise TypeError("Invalid operation")
3131
32    def get_index(self):32    def get_index(self):
33        return Tone.TONES.index(self.name_of_tone)33        return Tone.TONES.index(self.name_of_tone)
3434
35    def _calculate_tone(self, other, symbol):35    def _calculate_tone(self, other, symbol):
36        if symbol == "+":36        if symbol == "+":
37            return Chord(self, other)37            return Chord(self, other)
38        elif symbol == "-":38        elif symbol == "-":
39            distance = (self.TONES.index(self.name_of_tone) -39            distance = (self.TONES.index(self.name_of_tone) -
40                        self.TONES.index(other.name_of_tone)) % 1240                        self.TONES.index(other.name_of_tone)) % 12
41            return Interval(distance)41            return Interval(distance)
42        else:42        else:
43            raise ValueError("Invalid symbol")43            raise ValueError("Invalid symbol")
4444
45    def _calculate_interval(self, other, symbol):45    def _calculate_interval(self, other, symbol):
46        if symbol == "+":46        if symbol == "+":
47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 1247            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
48            return Tone(self.TONES[index])48            return Tone(self.TONES[index])
49        elif symbol == "-":49        elif symbol == "-":
50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 1250            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
51            return Tone(self.TONES[index])51            return Tone(self.TONES[index])
52        else:52        else:
53            raise ValueError("Invalid symbol")53            raise ValueError("Invalid symbol")
5454
5555
56class Interval:56class Interval:
57    INTERVAL_NAMES = [57    INTERVAL_NAMES = [
t58        "octave", "minor 2nd", "major 2nd", "minor 3rd",t58        "unison", "minor 2nd", "major 2nd", "minor 3rd",
59        "major 3rd", "perfect 4th", "diminished 5th",59        "major 3rd", "perfect 4th", "diminished 5th",
60        "perfect 5th", "minor 6th", "major 6th",60        "perfect 5th", "minor 6th", "major 6th",
61        "minor 7th", "major 7th"61        "minor 7th", "major 7th"
62    ]62    ]
6363
64    def __init__(self, number):64    def __init__(self, number):
65        if isinstance(number, int):65        if isinstance(number, int):
66            self.semitones = number % 1266            self.semitones = number % 12
67        else:67        else:
68            raise TypeError("Invalid input")68            raise TypeError("Invalid input")
6969
70    def __str__(self):70    def __str__(self):
71        return self.INTERVAL_NAMES[self.semitones]71        return self.INTERVAL_NAMES[self.semitones]
7272
73    def __add__(self, other):73    def __add__(self, other):
74        if isinstance(other, Interval):74        if isinstance(other, Interval):
75            return Interval(self.semitones + other.semitones)75            return Interval(self.semitones + other.semitones)
76        raise TypeError("Invalid operation")76        raise TypeError("Invalid operation")
7777
78    def __neg__(self):78    def __neg__(self):
79        return Interval(-self.semitones)79        return Interval(-self.semitones)
8080
8181
82class Chord:82class Chord:
83    def __init__(self, root, *tones):83    def __init__(self, root, *tones):
84        if not isinstance(root, Tone):84        if not isinstance(root, Tone):
85            raise ValueError("Root must be a Tone instance")85            raise ValueError("Root must be a Tone instance")
8686
87        for tone in tones:87        for tone in tones:
88            if not isinstance(tone, Tone):88            if not isinstance(tone, Tone):
89                raise ValueError("All tones must be Tone instances")89                raise ValueError("All tones must be Tone instances")
9090
91        unique_tones = [root]91        unique_tones = [root]
92        for tone in tones:92        for tone in tones:
93            if tone not in unique_tones: #using __eq__93            if tone not in unique_tones: #using __eq__
94                unique_tones.append(tone)94                unique_tones.append(tone)
9595
96        if len(unique_tones) < 2:96        if len(unique_tones) < 2:
97            raise TypeError("Cannot have a chord made of only 1 unique tone")97            raise TypeError("Cannot have a chord made of only 1 unique tone")
9898
99        self.root = root99        self.root = root
100        self.tones = unique_tones100        self.tones = unique_tones
101101
102    def __str__(self):102    def __str__(self):
103        root_index = Tone.TONES.index(self.root.name_of_tone)103        root_index = Tone.TONES.index(self.root.name_of_tone)
104        other_tones = [tone for tone in self.tones if tone != self.root]104        other_tones = [tone for tone in self.tones if tone != self.root]
105        sorted_tones = sorted(105        sorted_tones = sorted(
106            other_tones,106            other_tones,
107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12
108        )108        )
109        sorted_tones.insert(0, self.root)109        sorted_tones.insert(0, self.root)
110        return "-".join(str(tone) for tone in sorted_tones)110        return "-".join(str(tone) for tone in sorted_tones)
111111
112    def __add__(self, other):112    def __add__(self, other):
113        if isinstance(other, Tone):113        if isinstance(other, Tone):
114            return self._add_tone(other)114            return self._add_tone(other)
115        elif isinstance(other, Chord):115        elif isinstance(other, Chord):
116            return self._add_chord(other)116            return self._add_chord(other)
117        raise TypeError("Invalid operation")117        raise TypeError("Invalid operation")
118118
119    def __sub__(self, other):119    def __sub__(self, other):
120        if isinstance(other, Tone):120        if isinstance(other, Tone):
121            if other not in self.tones:  # using __eq__121            if other not in self.tones:  # using __eq__
122                raise TypeError(f"Cannot remove tone {other} from chord {self}")122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
123            new_tones = [tone for tone in self.tones if tone != other]123            new_tones = [tone for tone in self.tones if tone != other]
124            if len(set(new_tones)) < 2:124            if len(set(new_tones)) < 2:
125                raise TypeError("Cannot have a chord made of only 1 unique tone")125                raise TypeError("Cannot have a chord made of only 1 unique tone")
126            return Chord(*new_tones)126            return Chord(*new_tones)
127        raise TypeError("Invalid operation")127        raise TypeError("Invalid operation")
128128
129    def _validate_distance_to_root(self, target):129    def _validate_distance_to_root(self, target):
130        root_index = self.root.get_index()130        root_index = self.root.get_index()
131        for tone in self.tones:131        for tone in self.tones:
132            distance = (tone.get_index() - root_index) % 12132            distance = (tone.get_index() - root_index) % 12
133            if distance == target:133            if distance == target:
134                return True134                return True
135        return False135        return False
136136
137    def is_minor(self):137    def is_minor(self):
138        return self._validate_distance_to_root(3)138        return self._validate_distance_to_root(3)
139139
140    def is_major(self):140    def is_major(self):
141        return self._validate_distance_to_root(4)141        return self._validate_distance_to_root(4)
142142
143    def is_power_chord(self):143    def is_power_chord(self):
144        return not self.is_minor() and not self.is_major()144        return not self.is_minor() and not self.is_major()
145145
146    def _add_tone(self, other):146    def _add_tone(self, other):
147        if other not in self.tones:  # using __eq__147        if other not in self.tones:  # using __eq__
148            new_tones = list(self.tones)148            new_tones = list(self.tones)
149            new_tones.append(other)149            new_tones.append(other)
150            return Chord(*new_tones)150            return Chord(*new_tones)
151        else:151        else:
152            return self152            return self
153153
154    def _add_chord(self, other):154    def _add_chord(self, other):
155        new_tones = list(self.tones)155        new_tones = list(self.tones)
156        for tone in other.tones:156        for tone in other.tones:
157            if tone not in self.tones:  # using __eq__157            if tone not in self.tones:  # using __eq__
158                new_tones.append(tone)158                new_tones.append(tone)
159        return Chord(*new_tones)159        return Chord(*new_tones)
160160
161    def transposed(self, input_interval):161    def transposed(self, input_interval):
162        if not isinstance(input_interval, Interval):162        if not isinstance(input_interval, Interval):
163            raise ValueError("Interval must be an instance of Interval")163            raise ValueError("Interval must be an instance of Interval")
164164
165        transposed_tones = [tone + input_interval for tone in self.tones]165        transposed_tones = [tone + input_interval for tone in self.tones]
166        return Chord(*transposed_tones)166        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Tone:f1class Tone:
2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33
4    def __init__(self, input_name):4    def __init__(self, input_name):
5        if input_name not in self.TONES:5        if input_name not in self.TONES:
6            raise ValueError("Invalid tone name")6            raise ValueError("Invalid tone name")
7        self.name_of_tone = input_name7        self.name_of_tone = input_name
88
9    def __str__(self):9    def __str__(self):
10        return self.name_of_tone10        return self.name_of_tone
1111
12    def __hash__(self):  # this time i didn't use it12    def __hash__(self):  # this time i didn't use it
13        return hash(self.name_of_tone)13        return hash(self.name_of_tone)
1414
15    def __eq__(self, other):15    def __eq__(self, other):
16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
1717
18    def __add__(self, other):18    def __add__(self, other):
19        if isinstance(other, Tone):19        if isinstance(other, Tone):
20            return self._calculate_tone(other, "+")20            return self._calculate_tone(other, "+")
21        elif isinstance(other, Interval):21        elif isinstance(other, Interval):
22            return self._calculate_interval(other, "+")22            return self._calculate_interval(other, "+")
23        raise TypeError("Invalid operation")23        raise TypeError("Invalid operation")
2424
25    def __sub__(self, other):25    def __sub__(self, other):
26        if isinstance(other, Tone):26        if isinstance(other, Tone):
27            return self._calculate_tone(other, "-")27            return self._calculate_tone(other, "-")
28        elif isinstance(other, Interval):28        elif isinstance(other, Interval):
29            return self._calculate_interval(other, "-")29            return self._calculate_interval(other, "-")
30        raise TypeError("Invalid operation")30        raise TypeError("Invalid operation")
3131
32    def get_index(self):32    def get_index(self):
33        return Tone.TONES.index(self.name_of_tone)33        return Tone.TONES.index(self.name_of_tone)
3434
35    def _calculate_tone(self, other, symbol):35    def _calculate_tone(self, other, symbol):
36        if symbol == "+":36        if symbol == "+":
37            return Chord(self, other)37            return Chord(self, other)
38        elif symbol == "-":38        elif symbol == "-":
39            distance = (self.TONES.index(self.name_of_tone) -39            distance = (self.TONES.index(self.name_of_tone) -
40                        self.TONES.index(other.name_of_tone)) % 1240                        self.TONES.index(other.name_of_tone)) % 12
41            return Interval(distance)41            return Interval(distance)
42        else:42        else:
43            raise ValueError("Invalid symbol")43            raise ValueError("Invalid symbol")
4444
45    def _calculate_interval(self, other, symbol):45    def _calculate_interval(self, other, symbol):
46        if symbol == "+":46        if symbol == "+":
47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 1247            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
48            return Tone(self.TONES[index])48            return Tone(self.TONES[index])
49        elif symbol == "-":49        elif symbol == "-":
50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 1250            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
51            return Tone(self.TONES[index])51            return Tone(self.TONES[index])
52        else:52        else:
53            raise ValueError("Invalid symbol")53            raise ValueError("Invalid symbol")
5454
5555
56class Interval:56class Interval:
t57    INTERVAL_NAMES = {t57    INTERVAL_NAMES = [
58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",58        "octave", "minor 2nd", "major 2nd", "minor 3rd",
59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",59        "major 3rd", "perfect 4th", "diminished 5th",
60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",60        "perfect 5th", "minor 6th", "major 6th",
61        10: "minor 7th", 11: "major 7th"61        "minor 7th", "major 7th"
62    }62    ]
6363
64    def __init__(self, number):64    def __init__(self, number):
65        if isinstance(number, int):65        if isinstance(number, int):
66            self.semitones = number % 1266            self.semitones = number % 12
67        else:67        else:
68            raise TypeError("Invalid input")68            raise TypeError("Invalid input")
6969
70    def __str__(self):70    def __str__(self):
71        return self.INTERVAL_NAMES[self.semitones]71        return self.INTERVAL_NAMES[self.semitones]
7272
73    def __add__(self, other):73    def __add__(self, other):
74        if isinstance(other, Interval):74        if isinstance(other, Interval):
75            return Interval(self.semitones + other.semitones)75            return Interval(self.semitones + other.semitones)
76        raise TypeError("Invalid operation")76        raise TypeError("Invalid operation")
7777
78    def __neg__(self):78    def __neg__(self):
79        return Interval(-self.semitones)79        return Interval(-self.semitones)
8080
8181
82class Chord:82class Chord:
83    def __init__(self, root, *tones):83    def __init__(self, root, *tones):
84        if not isinstance(root, Tone):84        if not isinstance(root, Tone):
85            raise ValueError("Root must be a Tone instance")85            raise ValueError("Root must be a Tone instance")
8686
87        for tone in tones:87        for tone in tones:
88            if not isinstance(tone, Tone):88            if not isinstance(tone, Tone):
89                raise ValueError("All tones must be Tone instances")89                raise ValueError("All tones must be Tone instances")
9090
91        unique_tones = [root]91        unique_tones = [root]
92        for tone in tones:92        for tone in tones:
93            if tone not in unique_tones: #using __eq__93            if tone not in unique_tones: #using __eq__
94                unique_tones.append(tone)94                unique_tones.append(tone)
9595
96        if len(unique_tones) < 2:96        if len(unique_tones) < 2:
97            raise TypeError("Cannot have a chord made of only 1 unique tone")97            raise TypeError("Cannot have a chord made of only 1 unique tone")
9898
99        self.root = root99        self.root = root
100        self.tones = unique_tones100        self.tones = unique_tones
101101
102    def __str__(self):102    def __str__(self):
103        root_index = Tone.TONES.index(self.root.name_of_tone)103        root_index = Tone.TONES.index(self.root.name_of_tone)
104        other_tones = [tone for tone in self.tones if tone != self.root]104        other_tones = [tone for tone in self.tones if tone != self.root]
105        sorted_tones = sorted(105        sorted_tones = sorted(
106            other_tones,106            other_tones,
107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12
108        )108        )
109        sorted_tones.insert(0, self.root)109        sorted_tones.insert(0, self.root)
110        return "-".join(str(tone) for tone in sorted_tones)110        return "-".join(str(tone) for tone in sorted_tones)
111111
112    def __add__(self, other):112    def __add__(self, other):
113        if isinstance(other, Tone):113        if isinstance(other, Tone):
114            return self._add_tone(other)114            return self._add_tone(other)
115        elif isinstance(other, Chord):115        elif isinstance(other, Chord):
116            return self._add_chord(other)116            return self._add_chord(other)
117        raise TypeError("Invalid operation")117        raise TypeError("Invalid operation")
118118
119    def __sub__(self, other):119    def __sub__(self, other):
120        if isinstance(other, Tone):120        if isinstance(other, Tone):
121            if other not in self.tones:  # using __eq__121            if other not in self.tones:  # using __eq__
122                raise TypeError(f"Cannot remove tone {other} from chord {self}")122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
123            new_tones = [tone for tone in self.tones if tone != other]123            new_tones = [tone for tone in self.tones if tone != other]
124            if len(set(new_tones)) < 2:124            if len(set(new_tones)) < 2:
125                raise TypeError("Cannot have a chord made of only 1 unique tone")125                raise TypeError("Cannot have a chord made of only 1 unique tone")
126            return Chord(*new_tones)126            return Chord(*new_tones)
127        raise TypeError("Invalid operation")127        raise TypeError("Invalid operation")
128128
129    def _validate_distance_to_root(self, target):129    def _validate_distance_to_root(self, target):
130        root_index = self.root.get_index()130        root_index = self.root.get_index()
131        for tone in self.tones:131        for tone in self.tones:
132            distance = (tone.get_index() - root_index) % 12132            distance = (tone.get_index() - root_index) % 12
133            if distance == target:133            if distance == target:
134                return True134                return True
135        return False135        return False
136136
137    def is_minor(self):137    def is_minor(self):
138        return self._validate_distance_to_root(3)138        return self._validate_distance_to_root(3)
139139
140    def is_major(self):140    def is_major(self):
141        return self._validate_distance_to_root(4)141        return self._validate_distance_to_root(4)
142142
143    def is_power_chord(self):143    def is_power_chord(self):
144        return not self.is_minor() and not self.is_major()144        return not self.is_minor() and not self.is_major()
145145
146    def _add_tone(self, other):146    def _add_tone(self, other):
147        if other not in self.tones:  # using __eq__147        if other not in self.tones:  # using __eq__
148            new_tones = list(self.tones)148            new_tones = list(self.tones)
149            new_tones.append(other)149            new_tones.append(other)
150            return Chord(*new_tones)150            return Chord(*new_tones)
151        else:151        else:
152            return self152            return self
153153
154    def _add_chord(self, other):154    def _add_chord(self, other):
155        new_tones = list(self.tones)155        new_tones = list(self.tones)
156        for tone in other.tones:156        for tone in other.tones:
157            if tone not in self.tones:  # using __eq__157            if tone not in self.tones:  # using __eq__
158                new_tones.append(tone)158                new_tones.append(tone)
159        return Chord(*new_tones)159        return Chord(*new_tones)
160160
161    def transposed(self, input_interval):161    def transposed(self, input_interval):
162        if not isinstance(input_interval, Interval):162        if not isinstance(input_interval, Interval):
163            raise ValueError("Interval must be an instance of Interval")163            raise ValueError("Interval must be an instance of Interval")
164164
165        transposed_tones = [tone + input_interval for tone in self.tones]165        transposed_tones = [tone + input_interval for tone in self.tones]
166        return Chord(*transposed_tones)166        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Tone:f1class Tone:
2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33
4    def __init__(self, input_name):4    def __init__(self, input_name):
5        if input_name not in self.TONES:5        if input_name not in self.TONES:
6            raise ValueError("Invalid tone name")6            raise ValueError("Invalid tone name")
7        self.name_of_tone = input_name7        self.name_of_tone = input_name
88
9    def __str__(self):9    def __str__(self):
10        return self.name_of_tone10        return self.name_of_tone
1111
12    def __hash__(self):  # this time i didn't use it12    def __hash__(self):  # this time i didn't use it
13        return hash(self.name_of_tone)13        return hash(self.name_of_tone)
1414
15    def __eq__(self, other):15    def __eq__(self, other):
16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
1717
18    def __add__(self, other):18    def __add__(self, other):
19        if isinstance(other, Tone):19        if isinstance(other, Tone):
20            return self._calculate_tone(other, "+")20            return self._calculate_tone(other, "+")
21        elif isinstance(other, Interval):21        elif isinstance(other, Interval):
22            return self._calculate_interval(other, "+")22            return self._calculate_interval(other, "+")
23        raise TypeError("Invalid operation")23        raise TypeError("Invalid operation")
2424
25    def __sub__(self, other):25    def __sub__(self, other):
26        if isinstance(other, Tone):26        if isinstance(other, Tone):
27            return self._calculate_tone(other, "-")27            return self._calculate_tone(other, "-")
28        elif isinstance(other, Interval):28        elif isinstance(other, Interval):
29            return self._calculate_interval(other, "-")29            return self._calculate_interval(other, "-")
30        raise TypeError("Invalid operation")30        raise TypeError("Invalid operation")
3131
32    def get_index(self):32    def get_index(self):
33        return Tone.TONES.index(self.name_of_tone)33        return Tone.TONES.index(self.name_of_tone)
3434
35    def _calculate_tone(self, other, symbol):35    def _calculate_tone(self, other, symbol):
36        if symbol == "+":36        if symbol == "+":
37            return Chord(self, other)37            return Chord(self, other)
38        elif symbol == "-":38        elif symbol == "-":
39            distance = (self.TONES.index(self.name_of_tone) -39            distance = (self.TONES.index(self.name_of_tone) -
40                        self.TONES.index(other.name_of_tone)) % 1240                        self.TONES.index(other.name_of_tone)) % 12
41            return Interval(distance)41            return Interval(distance)
42        else:42        else:
43            raise ValueError("Invalid symbol")43            raise ValueError("Invalid symbol")
4444
45    def _calculate_interval(self, other, symbol):45    def _calculate_interval(self, other, symbol):
46        if symbol == "+":46        if symbol == "+":
47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 1247            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
48            return Tone(self.TONES[index])48            return Tone(self.TONES[index])
49        elif symbol == "-":49        elif symbol == "-":
50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 1250            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
51            return Tone(self.TONES[index])51            return Tone(self.TONES[index])
52        else:52        else:
53            raise ValueError("Invalid symbol")53            raise ValueError("Invalid symbol")
5454
5555
56class Interval:56class Interval:
57    INTERVAL_NAMES = {57    INTERVAL_NAMES = {
58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",
59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",
60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",
61        10: "minor 7th", 11: "major 7th"61        10: "minor 7th", 11: "major 7th"
62    }62    }
6363
64    def __init__(self, number):64    def __init__(self, number):
65        if isinstance(number, int):65        if isinstance(number, int):
66            self.semitones = number % 1266            self.semitones = number % 12
67        else:67        else:
68            raise TypeError("Invalid input")68            raise TypeError("Invalid input")
6969
70    def __str__(self):70    def __str__(self):
71        return self.INTERVAL_NAMES[self.semitones]71        return self.INTERVAL_NAMES[self.semitones]
7272
73    def __add__(self, other):73    def __add__(self, other):
74        if isinstance(other, Interval):74        if isinstance(other, Interval):
75            return Interval(self.semitones + other.semitones)75            return Interval(self.semitones + other.semitones)
76        raise TypeError("Invalid operation")76        raise TypeError("Invalid operation")
7777
78    def __neg__(self):78    def __neg__(self):
79        return Interval(-self.semitones)79        return Interval(-self.semitones)
8080
8181
82class Chord:82class Chord:
83    def __init__(self, root, *tones):83    def __init__(self, root, *tones):
84        if not isinstance(root, Tone):84        if not isinstance(root, Tone):
85            raise ValueError("Root must be a Tone instance")85            raise ValueError("Root must be a Tone instance")
8686
87        for tone in tones:87        for tone in tones:
88            if not isinstance(tone, Tone):88            if not isinstance(tone, Tone):
89                raise ValueError("All tones must be Tone instances")89                raise ValueError("All tones must be Tone instances")
9090
91        unique_tones = [root]91        unique_tones = [root]
92        for tone in tones:92        for tone in tones:
93            if tone not in unique_tones: #using __eq__93            if tone not in unique_tones: #using __eq__
94                unique_tones.append(tone)94                unique_tones.append(tone)
9595
96        if len(unique_tones) < 2:96        if len(unique_tones) < 2:
97            raise TypeError("Cannot have a chord made of only 1 unique tone")97            raise TypeError("Cannot have a chord made of only 1 unique tone")
9898
99        self.root = root99        self.root = root
100        self.tones = unique_tones100        self.tones = unique_tones
101101
102    def __str__(self):102    def __str__(self):
nn103        root_index = Tone.TONES.index(self.root.name_of_tone)
103        other_tones = [tone for tone in self.tones if tone != self.root]104        other_tones = [tone for tone in self.tones if tone != self.root]
104        sorted_tones = sorted(105        sorted_tones = sorted(
tt106            other_tones,
105            other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone)107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12
106        )108        )
107        sorted_tones.insert(0, self.root)109        sorted_tones.insert(0, self.root)
108        return "-".join(str(tone) for tone in sorted_tones)110        return "-".join(str(tone) for tone in sorted_tones)
109111
110    def __add__(self, other):112    def __add__(self, other):
111        if isinstance(other, Tone):113        if isinstance(other, Tone):
112            return self._add_tone(other)114            return self._add_tone(other)
113        elif isinstance(other, Chord):115        elif isinstance(other, Chord):
114            return self._add_chord(other)116            return self._add_chord(other)
115        raise TypeError("Invalid operation")117        raise TypeError("Invalid operation")
116118
117    def __sub__(self, other):119    def __sub__(self, other):
118        if isinstance(other, Tone):120        if isinstance(other, Tone):
119            if other not in self.tones:  # using __eq__121            if other not in self.tones:  # using __eq__
120                raise TypeError(f"Cannot remove tone {other} from chord {self}")122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
121            new_tones = [tone for tone in self.tones if tone != other]123            new_tones = [tone for tone in self.tones if tone != other]
122            if len(set(new_tones)) < 2:124            if len(set(new_tones)) < 2:
123                raise TypeError("Cannot have a chord made of only 1 unique tone")125                raise TypeError("Cannot have a chord made of only 1 unique tone")
124            return Chord(*new_tones)126            return Chord(*new_tones)
125        raise TypeError("Invalid operation")127        raise TypeError("Invalid operation")
126128
127    def _validate_distance_to_root(self, target):129    def _validate_distance_to_root(self, target):
128        root_index = self.root.get_index()130        root_index = self.root.get_index()
129        for tone in self.tones:131        for tone in self.tones:
130            distance = (tone.get_index() - root_index) % 12132            distance = (tone.get_index() - root_index) % 12
131            if distance == target:133            if distance == target:
132                return True134                return True
133        return False135        return False
134136
135    def is_minor(self):137    def is_minor(self):
136        return self._validate_distance_to_root(3)138        return self._validate_distance_to_root(3)
137139
138    def is_major(self):140    def is_major(self):
139        return self._validate_distance_to_root(4)141        return self._validate_distance_to_root(4)
140142
141    def is_power_chord(self):143    def is_power_chord(self):
142        return not self.is_minor() and not self.is_major()144        return not self.is_minor() and not self.is_major()
143145
144    def _add_tone(self, other):146    def _add_tone(self, other):
145        if other not in self.tones:  # using __eq__147        if other not in self.tones:  # using __eq__
146            new_tones = list(self.tones)148            new_tones = list(self.tones)
147            new_tones.append(other)149            new_tones.append(other)
148            return Chord(*new_tones)150            return Chord(*new_tones)
149        else:151        else:
150            return self152            return self
151153
152    def _add_chord(self, other):154    def _add_chord(self, other):
153        new_tones = list(self.tones)155        new_tones = list(self.tones)
154        for tone in other.tones:156        for tone in other.tones:
155            if tone not in self.tones:  # using __eq__157            if tone not in self.tones:  # using __eq__
156                new_tones.append(tone)158                new_tones.append(tone)
157        return Chord(*new_tones)159        return Chord(*new_tones)
158160
159    def transposed(self, input_interval):161    def transposed(self, input_interval):
160        if not isinstance(input_interval, Interval):162        if not isinstance(input_interval, Interval):
161            raise ValueError("Interval must be an instance of Interval")163            raise ValueError("Interval must be an instance of Interval")
162164
163        transposed_tones = [tone + input_interval for tone in self.tones]165        transposed_tones = [tone + input_interval for tone in self.tones]
164        return Chord(*transposed_tones)166        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Tone:f1class Tone:
2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33
4    def __init__(self, input_name):4    def __init__(self, input_name):
5        if input_name not in self.TONES:5        if input_name not in self.TONES:
6            raise ValueError("Invalid tone name")6            raise ValueError("Invalid tone name")
7        self.name_of_tone = input_name7        self.name_of_tone = input_name
88
9    def __str__(self):9    def __str__(self):
10        return self.name_of_tone10        return self.name_of_tone
1111
n12    def __hash__(self): #this time i didn't use itn12    def __hash__(self):  # this time i didn't use it
13        return hash(self.name_of_tone)13        return hash(self.name_of_tone)
1414
15    def __eq__(self, other):15    def __eq__(self, other):
16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
1717
18    def __add__(self, other):18    def __add__(self, other):
19        if isinstance(other, Tone):19        if isinstance(other, Tone):
n20            return self.calculate_tone(other, "+")n20            return self._calculate_tone(other, "+")
21        elif isinstance(other, Interval):21        elif isinstance(other, Interval):
n22            return self.calculate_interval(other, "+")n22            return self._calculate_interval(other, "+")
23        raise TypeError("Invalid operation")23        raise TypeError("Invalid operation")
2424
25    def __sub__(self, other):25    def __sub__(self, other):
26        if isinstance(other, Tone):26        if isinstance(other, Tone):
n27            return self.calculate_tone(other, "-")n27            return self._calculate_tone(other, "-")
28        elif isinstance(other, Interval):28        elif isinstance(other, Interval):
n29            return self.calculate_interval(other, "-")n29            return self._calculate_interval(other, "-")
30        raise TypeError("Invalid operation")30        raise TypeError("Invalid operation")
3131
32    def get_index(self):32    def get_index(self):
33        return Tone.TONES.index(self.name_of_tone)33        return Tone.TONES.index(self.name_of_tone)
3434
n35    def calculate_tone(self, other, symbol):n35    def _calculate_tone(self, other, symbol):
36        if symbol == "+":36        if symbol == "+":
37            return Chord(self, other)37            return Chord(self, other)
38        elif symbol == "-":38        elif symbol == "-":
39            distance = (self.TONES.index(self.name_of_tone) -39            distance = (self.TONES.index(self.name_of_tone) -
40                        self.TONES.index(other.name_of_tone)) % 1240                        self.TONES.index(other.name_of_tone)) % 12
41            return Interval(distance)41            return Interval(distance)
42        else:42        else:
43            raise ValueError("Invalid symbol")43            raise ValueError("Invalid symbol")
4444
n45    def calculate_interval(self, other, symbol):n45    def _calculate_interval(self, other, symbol):
46        if symbol == "+":46        if symbol == "+":
47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 1247            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
48            return Tone(self.TONES[index])48            return Tone(self.TONES[index])
49        elif symbol == "-":49        elif symbol == "-":
50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 1250            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
51            return Tone(self.TONES[index])51            return Tone(self.TONES[index])
52        else:52        else:
53            raise ValueError("Invalid symbol")53            raise ValueError("Invalid symbol")
5454
5555
56class Interval:56class Interval:
57    INTERVAL_NAMES = {57    INTERVAL_NAMES = {
58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",
59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",
60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",
61        10: "minor 7th", 11: "major 7th"61        10: "minor 7th", 11: "major 7th"
62    }62    }
6363
64    def __init__(self, number):64    def __init__(self, number):
65        if isinstance(number, int):65        if isinstance(number, int):
66            self.semitones = number % 1266            self.semitones = number % 12
67        else:67        else:
68            raise TypeError("Invalid input")68            raise TypeError("Invalid input")
6969
70    def __str__(self):70    def __str__(self):
71        return self.INTERVAL_NAMES[self.semitones]71        return self.INTERVAL_NAMES[self.semitones]
7272
73    def __add__(self, other):73    def __add__(self, other):
74        if isinstance(other, Interval):74        if isinstance(other, Interval):
75            return Interval(self.semitones + other.semitones)75            return Interval(self.semitones + other.semitones)
76        raise TypeError("Invalid operation")76        raise TypeError("Invalid operation")
7777
78    def __neg__(self):78    def __neg__(self):
79        return Interval(-self.semitones)79        return Interval(-self.semitones)
8080
8181
82class Chord:82class Chord:
83    def __init__(self, root, *tones):83    def __init__(self, root, *tones):
84        if not isinstance(root, Tone):84        if not isinstance(root, Tone):
85            raise ValueError("Root must be a Tone instance")85            raise ValueError("Root must be a Tone instance")
8686
87        for tone in tones:87        for tone in tones:
88            if not isinstance(tone, Tone):88            if not isinstance(tone, Tone):
89                raise ValueError("All tones must be Tone instances")89                raise ValueError("All tones must be Tone instances")
9090
91        unique_tones = [root]91        unique_tones = [root]
92        for tone in tones:92        for tone in tones:
93            if tone not in unique_tones: #using __eq__93            if tone not in unique_tones: #using __eq__
94                unique_tones.append(tone)94                unique_tones.append(tone)
9595
96        if len(unique_tones) < 2:96        if len(unique_tones) < 2:
97            raise TypeError("Cannot have a chord made of only 1 unique tone")97            raise TypeError("Cannot have a chord made of only 1 unique tone")
9898
99        self.root = root99        self.root = root
100        self.tones = unique_tones100        self.tones = unique_tones
101101
102    def __str__(self):102    def __str__(self):
103        other_tones = [tone for tone in self.tones if tone != self.root]103        other_tones = [tone for tone in self.tones if tone != self.root]
104        sorted_tones = sorted(104        sorted_tones = sorted(
105            other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone)105            other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone)
106        )106        )
107        sorted_tones.insert(0, self.root)107        sorted_tones.insert(0, self.root)
108        return "-".join(str(tone) for tone in sorted_tones)108        return "-".join(str(tone) for tone in sorted_tones)
109109
110    def __add__(self, other):110    def __add__(self, other):
111        if isinstance(other, Tone):111        if isinstance(other, Tone):
n112            return self.add_tone(other)n112            return self._add_tone(other)
113        elif isinstance(other, Chord):113        elif isinstance(other, Chord):
n114            return self.add_chord(other)n114            return self._add_chord(other)
115        raise TypeError("Invalid operation")115        raise TypeError("Invalid operation")
116116
117    def __sub__(self, other):117    def __sub__(self, other):
118        if isinstance(other, Tone):118        if isinstance(other, Tone):
n119            if other not in self.tones: #using __eq__n119            if other not in self.tones:  # using __eq__
120                raise TypeError(f"Cannot remove tone {other} from chord {self}")120                raise TypeError(f"Cannot remove tone {other} from chord {self}")
121            new_tones = [tone for tone in self.tones if tone != other]121            new_tones = [tone for tone in self.tones if tone != other]
122            if len(set(new_tones)) < 2:122            if len(set(new_tones)) < 2:
123                raise TypeError("Cannot have a chord made of only 1 unique tone")123                raise TypeError("Cannot have a chord made of only 1 unique tone")
124            return Chord(*new_tones)124            return Chord(*new_tones)
125        raise TypeError("Invalid operation")125        raise TypeError("Invalid operation")
126126
n127    def validate_distance_to_root(self, target):n127    def _validate_distance_to_root(self, target):
128        root_index = self.root.get_index()128        root_index = self.root.get_index()
129        for tone in self.tones:129        for tone in self.tones:
130            distance = (tone.get_index() - root_index) % 12130            distance = (tone.get_index() - root_index) % 12
131            if distance == target:131            if distance == target:
132                return True132                return True
133        return False133        return False
134134
135    def is_minor(self):135    def is_minor(self):
n136        return self.validate_distance_to_root(3)n136        return self._validate_distance_to_root(3)
137137
138    def is_major(self):138    def is_major(self):
n139        return self.validate_distance_to_root(4)n139        return self._validate_distance_to_root(4)
140140
141    def is_power_chord(self):141    def is_power_chord(self):
142        return not self.is_minor() and not self.is_major()142        return not self.is_minor() and not self.is_major()
143143
n144    def add_tone(self, other):n144    def _add_tone(self, other):
145        if other not in self.tones: #using __eq__145        if other not in self.tones:  # using __eq__
146            new_tones = list(self.tones)146            new_tones = list(self.tones)
147            new_tones.append(other)147            new_tones.append(other)
148            return Chord(*new_tones)148            return Chord(*new_tones)
149        else:149        else:
150            return self150            return self
151151
n152    def add_chord(self, other):n152    def _add_chord(self, other):
153        new_tones = list(self.tones)153        new_tones = list(self.tones)
154        for tone in other.tones:154        for tone in other.tones:
t155            if tone not in self.tones: #using __eq__t155            if tone not in self.tones:  # using __eq__
156                new_tones.append(tone)156                new_tones.append(tone)
157        return Chord(*new_tones)157        return Chord(*new_tones)
158158
159    def transposed(self, input_interval):159    def transposed(self, input_interval):
160        if not isinstance(input_interval, Interval):160        if not isinstance(input_interval, Interval):
161            raise ValueError("Interval must be an instance of Interval")161            raise ValueError("Interval must be an instance of Interval")
162162
163        transposed_tones = [tone + input_interval for tone in self.tones]163        transposed_tones = [tone + input_interval for tone in self.tones]
164        return Chord(*transposed_tones)164        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1class Tone:f1class Tone:
2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
33
4    def __init__(self, input_name):4    def __init__(self, input_name):
5        if input_name not in self.TONES:5        if input_name not in self.TONES:
6            raise ValueError("Invalid tone name")6            raise ValueError("Invalid tone name")
7        self.name_of_tone = input_name7        self.name_of_tone = input_name
88
9    def __str__(self):9    def __str__(self):
10        return self.name_of_tone10        return self.name_of_tone
1111
n12    def __hash__(self):n12    def __hash__(self): #this time i didn't use it
13        return hash(self.name_of_tone)13        return hash(self.name_of_tone)
1414
15    def __eq__(self, other):15    def __eq__(self, other):
16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
1717
n18    def get_index(self):n
19        return Tone.TONES.index(self.name_of_tone)
20 
21    def __add__(self, other):18    def __add__(self, other):
22        if isinstance(other, Tone):19        if isinstance(other, Tone):
n23            return Chord(self, other)n20            return self.calculate_tone(other, "+")
24        elif isinstance(other, Interval):21        elif isinstance(other, Interval):
n25            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12n22            return self.calculate_interval(other, "+")
26            return Tone(self.TONES[index])
27        raise TypeError("Invalid operation")23        raise TypeError("Invalid operation")
2824
29    def __sub__(self, other):25    def __sub__(self, other):
30        if isinstance(other, Tone):26        if isinstance(other, Tone):
n31            distance = (self.TONES.index(self.name_of_tone) - self.TONES.index(other.name_of_tone)) % 12n27            return self.calculate_tone(other, "-")
28        elif isinstance(other, Interval):
29            return self.calculate_interval(other, "-")
30        raise TypeError("Invalid operation")
31 
32    def get_index(self):
33        return Tone.TONES.index(self.name_of_tone)
34 
35    def calculate_tone(self, other, symbol):
36        if symbol == "+":
37            return Chord(self, other)
38        elif symbol == "-":
39            distance = (self.TONES.index(self.name_of_tone) -
40                        self.TONES.index(other.name_of_tone)) % 12
32            return Interval(distance)41            return Interval(distance)
n33        elif isinstance(other, Interval):n42        else:
43            raise ValueError("Invalid symbol")
44 
45    def calculate_interval(self, other, symbol):
46        if symbol == "+":
47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
48            return Tone(self.TONES[index])
49        elif symbol == "-":
34            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 1250            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
35            return Tone(self.TONES[index])51            return Tone(self.TONES[index])
n36        raise TypeError("Invalid operation")n52        else:
53            raise ValueError("Invalid symbol")
3754
3855
39class Interval:56class Interval:
n40    interval_names = {n57    INTERVAL_NAMES = {
41        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",58        0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",
42        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",59        4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",
n43        7: "perfect 5th", 8: "minor 6th", 9: "major 6th", 10: "minor 7th", 11: "major 7th"n60        7: "perfect 5th", 8: "minor 6th", 9: "major 6th",
61        10: "minor 7th", 11: "major 7th"
44    }62    }
4563
46    def __init__(self, number):64    def __init__(self, number):
n47        if type(number) is int:n65        if isinstance(number, int):
48            self.semitones = number % 1266            self.semitones = number % 12
49        else:67        else:
n50            raise TypeError("Negative interval number")n68            raise TypeError("Invalid input")
5169
52    def __str__(self):70    def __str__(self):
n53        return self.interval_names[self.semitones]n71        return self.INTERVAL_NAMES[self.semitones]
5472
55    def __add__(self, other):73    def __add__(self, other):
56        if isinstance(other, Interval):74        if isinstance(other, Interval):
57            return Interval(self.semitones + other.semitones)75            return Interval(self.semitones + other.semitones)
58        raise TypeError("Invalid operation")76        raise TypeError("Invalid operation")
5977
60    def __neg__(self):78    def __neg__(self):
61        return Interval(-self.semitones)79        return Interval(-self.semitones)
6280
6381
64class Chord:82class Chord:
65    def __init__(self, root, *tones):83    def __init__(self, root, *tones):
66        if not isinstance(root, Tone):84        if not isinstance(root, Tone):
67            raise ValueError("Root must be a Tone instance")85            raise ValueError("Root must be a Tone instance")
6886
69        for tone in tones:87        for tone in tones:
70            if not isinstance(tone, Tone):88            if not isinstance(tone, Tone):
71                raise ValueError("All tones must be Tone instances")89                raise ValueError("All tones must be Tone instances")
7290
n73        unique_tones = [root] + list(tones)n91        unique_tones = [root]
92        for tone in tones:
93            if tone not in unique_tones: #using __eq__
94                unique_tones.append(tone)
7495
n75        if len(set(unique_tones)) < 2: #using __eq__ from Tonen96        if len(unique_tones) < 2:
76            raise TypeError("Cannot have a chord made of only 1 unique tone")97            raise TypeError("Cannot have a chord made of only 1 unique tone")
7798
78        self.root = root99        self.root = root
79        self.tones = unique_tones100        self.tones = unique_tones
80101
81    def __str__(self):102    def __str__(self):
82        other_tones = [tone for tone in self.tones if tone != self.root]103        other_tones = [tone for tone in self.tones if tone != self.root]
nn104        sorted_tones = sorted(
83        sorted_tones = sorted(other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone))105            other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone)
106        )
84        sorted_tones.insert(0, self.root)107        sorted_tones.insert(0, self.root)
n85 n
86        return "-".join(str(tone) for tone in sorted_tones)108        return "-".join(str(tone) for tone in sorted_tones)
87109
n88    def is_minor(self):n110    def __add__(self, other):
111        if isinstance(other, Tone):
112            return self.add_tone(other)
113        elif isinstance(other, Chord):
114            return self.add_chord(other)
115        raise TypeError("Invalid operation")
116 
117    def __sub__(self, other):
118        if isinstance(other, Tone):
119            if other not in self.tones: #using __eq__
120                raise TypeError(f"Cannot remove tone {other} from chord {self}")
121            new_tones = [tone for tone in self.tones if tone != other]
122            if len(set(new_tones)) < 2:
123                raise TypeError("Cannot have a chord made of only 1 unique tone")
124            return Chord(*new_tones)
125        raise TypeError("Invalid operation")
126 
127    def validate_distance_to_root(self, target):
89        root_index = self.root.get_index()128        root_index = self.root.get_index()
n90 n
91        for tone in self.tones:129        for tone in self.tones:
92            distance = (tone.get_index() - root_index) % 12130            distance = (tone.get_index() - root_index) % 12
n93            if distance == 3:n131            if distance == target:
94                return True132                return True
95        return False133        return False
96134
nn135    def is_minor(self):
136        return self.validate_distance_to_root(3)
137 
97    def is_major(self):138    def is_major(self):
n98        root_index = self.root.get_index()n139        return self.validate_distance_to_root(4)
99 
100        for tone in self.tones:
101            distance = (tone.get_index() - root_index) % 12
102            if distance == 4:
103                return True
104        return False
105140
106    def is_power_chord(self):141    def is_power_chord(self):
107        return not self.is_minor() and not self.is_major()142        return not self.is_minor() and not self.is_major()
108143
n109    def __add__(self, other):n144    def add_tone(self, other):
110        if isinstance(other, Tone):145        if other not in self.tones: #using __eq__
111            new_tones = list(self.tones)146            new_tones = list(self.tones)
n112            if other not in new_tones: #using __eq__ from Tonen
113                new_tones.append(other)147            new_tones.append(other)
114            return Chord(*new_tones)148            return Chord(*new_tones)
n115        elif isinstance(other, Chord):n149        else:
116            new_tones = list(self.tones)150            return self
117            for tone in other.tones:
118                if tone not in new_tones: #using __eq__ from Tone
119                    new_tones.append(tone)
120            return Chord(*new_tones)
121        raise TypeError("Invalid operation")
122151
n123    def __sub__(self, other):n152    def add_chord(self, other):
124        if isinstance(other, Tone):153        new_tones = list(self.tones)
154        for tone in other.tones:
125            if other not in self.tones:155            if tone not in self.tones: #using __eq__
126                raise TypeError(f"Cannot remove tone {other} from chord {self}")156                new_tones.append(tone)
127 
128            new_tones = [tone for tone in self.tones if tone != other]
129            if len(set(new_tones)) < 2:
130                raise TypeError("Cannot have a chord made of only 1 unique tone")
131 
132            return Chord(self.root, *new_tones)157        return Chord(*new_tones)
133        raise TypeError("Invalid operation")
134158
135    def transposed(self, input_interval):159    def transposed(self, input_interval):
136        if not isinstance(input_interval, Interval):160        if not isinstance(input_interval, Interval):
137            raise ValueError("Interval must be an instance of Interval")161            raise ValueError("Interval must be an instance of Interval")
138162
n139        transposed_tones = {tone + input_interval for tone in self.tones} #using __hash__ from Tonen163        transposed_tones = [tone + input_interval for tone in self.tones]
140        return Chord(*transposed_tones)164        return Chord(*transposed_tones)
t141 t
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op