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.
|
| f | 1 | class Tone: | f | 1 | class 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"] | ||
| 3 | 3 | ||||
| 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_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 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") | ||
| 24 | 24 | ||||
| 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") | ||
| 31 | 31 | ||||
| 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) | ||
| 34 | 34 | ||||
| 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)) % 12 | 40 | 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") | ||
| 44 | 44 | ||||
| 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) % 12 | 47 | 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) % 12 | 50 | 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") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| 57 | INTERVAL_NAMES = [ | 57 | INTERVAL_NAMES = [ | ||
| t | 58 | "octave", "minor 2nd", "major 2nd", "minor 3rd", | t | 58 | "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 | ] | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 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") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class 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") | ||
| 86 | 86 | ||||
| 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") | ||
| 90 | 90 | ||||
| 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) | ||
| 95 | 95 | ||||
| 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") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 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) % 12 | 107 | 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) | ||
| 111 | 111 | ||||
| 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") | ||
| 118 | 118 | ||||
| 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") | ||
| 128 | 128 | ||||
| 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) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 133 | if distance == target: | 133 | if distance == target: | ||
| 134 | return True | 134 | return True | ||
| 135 | return False | 135 | return False | ||
| 136 | 136 | ||||
| 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) | ||
| 139 | 139 | ||||
| 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) | ||
| 142 | 142 | ||||
| 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() | ||
| 145 | 145 | ||||
| 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 self | 152 | return self | ||
| 153 | 153 | ||||
| 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) | ||
| 160 | 160 | ||||
| 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") | ||
| 164 | 164 | ||||
| 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 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class Tone: | f | 1 | class 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"] | ||
| 3 | 3 | ||||
| 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_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 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") | ||
| 24 | 24 | ||||
| 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") | ||
| 31 | 31 | ||||
| 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) | ||
| 34 | 34 | ||||
| 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)) % 12 | 40 | 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") | ||
| 44 | 44 | ||||
| 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) % 12 | 47 | 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) % 12 | 50 | 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") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| t | 57 | INTERVAL_NAMES = { | t | 57 | 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 | ] | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 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") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class 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") | ||
| 86 | 86 | ||||
| 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") | ||
| 90 | 90 | ||||
| 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) | ||
| 95 | 95 | ||||
| 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") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 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) % 12 | 107 | 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) | ||
| 111 | 111 | ||||
| 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") | ||
| 118 | 118 | ||||
| 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") | ||
| 128 | 128 | ||||
| 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) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 133 | if distance == target: | 133 | if distance == target: | ||
| 134 | return True | 134 | return True | ||
| 135 | return False | 135 | return False | ||
| 136 | 136 | ||||
| 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) | ||
| 139 | 139 | ||||
| 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) | ||
| 142 | 142 | ||||
| 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() | ||
| 145 | 145 | ||||
| 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 self | 152 | return self | ||
| 153 | 153 | ||||
| 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) | ||
| 160 | 160 | ||||
| 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") | ||
| 164 | 164 | ||||
| 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 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class Tone: | f | 1 | class 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"] | ||
| 3 | 3 | ||||
| 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_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 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") | ||
| 24 | 24 | ||||
| 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") | ||
| 31 | 31 | ||||
| 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) | ||
| 34 | 34 | ||||
| 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)) % 12 | 40 | 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") | ||
| 44 | 44 | ||||
| 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) % 12 | 47 | 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) % 12 | 50 | 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") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class 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 | } | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 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") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class 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") | ||
| 86 | 86 | ||||
| 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") | ||
| 90 | 90 | ||||
| 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) | ||
| 95 | 95 | ||||
| 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") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 102 | def __str__(self): | 102 | def __str__(self): | ||
| n | n | 103 | 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( | ||
| t | t | 106 | 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) | ||
| 109 | 111 | ||||
| 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") | ||
| 116 | 118 | ||||
| 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") | ||
| 126 | 128 | ||||
| 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) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 131 | if distance == target: | 133 | if distance == target: | ||
| 132 | return True | 134 | return True | ||
| 133 | return False | 135 | return False | ||
| 134 | 136 | ||||
| 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) | ||
| 137 | 139 | ||||
| 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) | ||
| 140 | 142 | ||||
| 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() | ||
| 143 | 145 | ||||
| 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 self | 152 | return self | ||
| 151 | 153 | ||||
| 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) | ||
| 158 | 160 | ||||
| 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") | ||
| 162 | 164 | ||||
| 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 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class Tone: | f | 1 | class 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"] | ||
| 3 | 3 | ||||
| 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_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| n | 12 | def __hash__(self): #this time i didn't use it | n | 12 | def __hash__(self): # this time i didn't use it |
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 18 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| n | 20 | return self.calculate_tone(other, "+") | n | 20 | return self._calculate_tone(other, "+") |
| 21 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| n | 22 | return self.calculate_interval(other, "+") | n | 22 | return self._calculate_interval(other, "+") |
| 23 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 24 | 24 | ||||
| 25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 26 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| n | 27 | return self.calculate_tone(other, "-") | n | 27 | return self._calculate_tone(other, "-") |
| 28 | elif isinstance(other, Interval): | 28 | elif isinstance(other, Interval): | ||
| n | 29 | return self.calculate_interval(other, "-") | n | 29 | return self._calculate_interval(other, "-") |
| 30 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 31 | 31 | ||||
| 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) | ||
| 34 | 34 | ||||
| n | 35 | def calculate_tone(self, other, symbol): | n | 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)) % 12 | 40 | 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") | ||
| 44 | 44 | ||||
| n | 45 | def calculate_interval(self, other, symbol): | n | 45 | def _calculate_interval(self, other, symbol): |
| 46 | if symbol == "+": | 46 | if symbol == "+": | ||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | 47 | 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) % 12 | 50 | 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") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class 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 | } | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 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") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class 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") | ||
| 86 | 86 | ||||
| 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") | ||
| 90 | 90 | ||||
| 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) | ||
| 95 | 95 | ||||
| 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") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 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) | ||
| 109 | 109 | ||||
| 110 | def __add__(self, other): | 110 | def __add__(self, other): | ||
| 111 | if isinstance(other, Tone): | 111 | if isinstance(other, Tone): | ||
| n | 112 | return self.add_tone(other) | n | 112 | return self._add_tone(other) |
| 113 | elif isinstance(other, Chord): | 113 | elif isinstance(other, Chord): | ||
| n | 114 | return self.add_chord(other) | n | 114 | return self._add_chord(other) |
| 115 | raise TypeError("Invalid operation") | 115 | raise TypeError("Invalid operation") | ||
| 116 | 116 | ||||
| 117 | def __sub__(self, other): | 117 | def __sub__(self, other): | ||
| 118 | if isinstance(other, Tone): | 118 | if isinstance(other, Tone): | ||
| n | 119 | if other not in self.tones: #using __eq__ | n | 119 | 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") | ||
| 126 | 126 | ||||
| n | 127 | def validate_distance_to_root(self, target): | n | 127 | 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) % 12 | 130 | distance = (tone.get_index() - root_index) % 12 | ||
| 131 | if distance == target: | 131 | if distance == target: | ||
| 132 | return True | 132 | return True | ||
| 133 | return False | 133 | return False | ||
| 134 | 134 | ||||
| 135 | def is_minor(self): | 135 | def is_minor(self): | ||
| n | 136 | return self.validate_distance_to_root(3) | n | 136 | return self._validate_distance_to_root(3) |
| 137 | 137 | ||||
| 138 | def is_major(self): | 138 | def is_major(self): | ||
| n | 139 | return self.validate_distance_to_root(4) | n | 139 | return self._validate_distance_to_root(4) |
| 140 | 140 | ||||
| 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() | ||
| 143 | 143 | ||||
| n | 144 | def add_tone(self, other): | n | 144 | 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 self | 150 | return self | ||
| 151 | 151 | ||||
| n | 152 | def add_chord(self, other): | n | 152 | 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: | ||
| t | 155 | if tone not in self.tones: #using __eq__ | t | 155 | 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) | ||
| 158 | 158 | ||||
| 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") | ||
| 162 | 162 | ||||
| 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 | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
| f | 1 | class Tone: | f | 1 | class 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"] | ||
| 3 | 3 | ||||
| 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_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| n | 12 | def __hash__(self): | n | 12 | def __hash__(self): #this time i didn't use it |
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| n | 18 | 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): | ||
| n | 23 | return Chord(self, other) | n | 20 | return self.calculate_tone(other, "+") |
| 24 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| n | 25 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | n | 22 | return self.calculate_interval(other, "+") |
| 26 | return Tone(self.TONES[index]) | ||||
| 27 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 28 | 24 | ||||
| 29 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 30 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| n | 31 | distance = (self.TONES.index(self.name_of_tone) - self.TONES.index(other.name_of_tone)) % 12 | n | 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 | ||||
| 32 | return Interval(distance) | 41 | return Interval(distance) | ||
| n | 33 | elif isinstance(other, Interval): | n | 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 == "-": | ||||
| 34 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 35 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| n | 36 | raise TypeError("Invalid operation") | n | 52 | else: |
| 53 | raise ValueError("Invalid symbol") | ||||
| 37 | 54 | ||||
| 38 | 55 | ||||
| 39 | class Interval: | 56 | class Interval: | ||
| n | 40 | interval_names = { | n | 57 | 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", | ||
| n | 43 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", 10: "minor 7th", 11: "major 7th" | n | 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", |
| 61 | 10: "minor 7th", 11: "major 7th" | ||||
| 44 | } | 62 | } | ||
| 45 | 63 | ||||
| 46 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| n | 47 | if type(number) is int: | n | 65 | if isinstance(number, int): |
| 48 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 49 | else: | 67 | else: | ||
| n | 50 | raise TypeError("Negative interval number") | n | 68 | raise TypeError("Invalid input") |
| 51 | 69 | ||||
| 52 | def __str__(self): | 70 | def __str__(self): | ||
| n | 53 | return self.interval_names[self.semitones] | n | 71 | return self.INTERVAL_NAMES[self.semitones] |
| 54 | 72 | ||||
| 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") | ||
| 59 | 77 | ||||
| 60 | def __neg__(self): | 78 | def __neg__(self): | ||
| 61 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 62 | 80 | ||||
| 63 | 81 | ||||
| 64 | class Chord: | 82 | class 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") | ||
| 68 | 86 | ||||
| 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") | ||
| 72 | 90 | ||||
| n | 73 | unique_tones = [root] + list(tones) | n | 91 | unique_tones = [root] |
| 92 | for tone in tones: | ||||
| 93 | if tone not in unique_tones: #using __eq__ | ||||
| 94 | unique_tones.append(tone) | ||||
| 74 | 95 | ||||
| n | 75 | if len(set(unique_tones)) < 2: #using __eq__ from Tone | n | 96 | 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") | ||
| 77 | 98 | ||||
| 78 | self.root = root | 99 | self.root = root | ||
| 79 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 80 | 101 | ||||
| 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] | ||
| n | n | 104 | 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) | ||
| n | 85 | n | |||
| 86 | return "-".join(str(tone) for tone in sorted_tones) | 108 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 87 | 109 | ||||
| n | 88 | def is_minor(self): | n | 110 | 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() | ||
| n | 90 | n | |||
| 91 | for tone in self.tones: | 129 | for tone in self.tones: | ||
| 92 | distance = (tone.get_index() - root_index) % 12 | 130 | distance = (tone.get_index() - root_index) % 12 | ||
| n | 93 | if distance == 3: | n | 131 | if distance == target: |
| 94 | return True | 132 | return True | ||
| 95 | return False | 133 | return False | ||
| 96 | 134 | ||||
| n | n | 135 | def is_minor(self): | ||
| 136 | return self.validate_distance_to_root(3) | ||||
| 137 | |||||
| 97 | def is_major(self): | 138 | def is_major(self): | ||
| n | 98 | root_index = self.root.get_index() | n | 139 | 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 | ||||
| 105 | 140 | ||||
| 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() | ||
| 108 | 143 | ||||
| n | 109 | def __add__(self, other): | n | 144 | 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) | ||
| n | 112 | if other not in new_tones: #using __eq__ from Tone | n | ||
| 113 | new_tones.append(other) | 147 | new_tones.append(other) | ||
| 114 | return Chord(*new_tones) | 148 | return Chord(*new_tones) | ||
| n | 115 | elif isinstance(other, Chord): | n | 149 | 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") | ||||
| 122 | 151 | ||||
| n | 123 | def __sub__(self, other): | n | 152 | 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") | ||||
| 134 | 158 | ||||
| 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") | ||
| 138 | 162 | ||||
| n | 139 | transposed_tones = {tone + input_interval for tone in self.tones} #using __hash__ from Tone | n | 163 | transposed_tones = [tone + input_interval for tone in self.tones] |
| 140 | return Chord(*transposed_tones) | 164 | return Chord(*transposed_tones) | ||
| t | 141 | t |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||