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 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|