1class Tone:
2 _NUMBER_OF_TONES = 12
3
4 tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
5
6 def __init__(self, name):
7 if name in self.tones:
8 self.name = name
9
10 def __str__(self):
11 return self.name
12
13 def __add__(self, other):
14 if isinstance(other, Tone):
15 return Chord(self, other)
16 elif isinstance(other, Interval):
17 ind = self.tones.index(self.name)
18 new_ind = (ind+other.number_of_semitones) % self._NUMBER_OF_TONES
19 return Tone(self.tones[new_ind])
20
21 def __sub__(self, other):
22 if isinstance(other, Tone):
23 number_of_semitones = (Tone.tones.index(self.name)-Tone.tones.index(other.name)) % self._NUMBER_OF_TONES
24 return Interval(number_of_semitones)
25 elif isinstance(other, Interval):
26 ind = self.tones.index(self.name)
27 new_ind = (ind - other.number_of_semitones) % self._NUMBER_OF_TONES
28 return Tone(self.tones[new_ind])
29
30
31class Interval:
32 _NUMBER_OF_SEMITONES = 12
33 intervals = {
34 0: "unison", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd",
35 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th",
36 7: "perfect 5th", 8: "minor 6th", 9: "major 6th",
37 10: "minor 7th", 11: "major 7th"
38 }
39
40 def __init__(self, number_of_semitones):
41 self.number_of_semitones = number_of_semitones % self._NUMBER_OF_SEMITONES
42
43 def __str__(self):
44 return self.intervals[self.number_of_semitones]
45
46 def __add__(self, other):
47 if isinstance(other, Tone):
48 raise TypeError("Invalid operation")
49 elif isinstance(other, Interval):
50 new_num_of_semitones = self.number_of_semitones + other.number_of_semitones
51 return Interval(new_num_of_semitones)
52
53 def __sub__(self, other):
54 if isinstance(other, Tone):
55 raise TypeError("Invalid operation")
56
57 #used in the transpose function in order to add a negative interval to a tone
58 def __neg__(self):
59 """Convert an interval to the negative value."""
60 return Interval(-self.number_of_semitones)
61
62
63class Chord:
64 _NUMBER_OF_SEMITONES = 12
65 _MIN_UNIQUE_TONES_IN_CHORD = 2
66 _NUMBER_OF_SEMITONES_IN_MINOR = 3
67 _NUMBER_OF_SEMITONES_IN_MAJOR = 4
68
69 def __init__(self, main_tone, *other_tones):
70 unique_tone_names = {main_tone.name}
71 unique_tones = [main_tone]
72
73 for tone in other_tones:
74 if tone.name not in unique_tone_names:
75 unique_tone_names.add(tone.name)
76 unique_tones.append(tone)
77
78 if len(unique_tone_names) < self._MIN_UNIQUE_TONES_IN_CHORD:
79 raise TypeError("Cannot have a chord made of only 1 unique tone")
80
81 main_tone_ind = Tone.tones.index(main_tone.name)
82 self.main_tone_ind = main_tone_ind #so I can use it in _get_distance_from_main_tone
83 tones_left = list()
84
85 for tone in unique_tones:
86 if tone != main_tone:
87 tones_left.append(tone)
88
89 tones_left.sort(key = self._get_distance_from_main_tone)
90
91 self.tones = [main_tone]
92 for tone in tones_left:
93 self.tones.append(tone)
94
95 def _get_tones_diff(self, tone, main_tone_ind):
96 """Return the count of semitones between a tone and the main_tone_ind."""
97 return (Tone.tones.index(tone.name) - main_tone_ind) % self._NUMBER_OF_SEMITONES
98
99 def _get_distance_from_main_tone(self, tone):
100 """Calculate the difference between the current tone and the main tone of the chord in semitones."""
101 return self._get_tones_diff(tone, self.main_tone_ind)
102
103 def _get_tone_ind(self, tone_name):
104 """Return the index of a tone."""
105 return Tone.tones.index(tone_name)
106
107 def __str__(self):
108 return f"{'-'.join(str(tone) for tone in self.tones)}"
109
110 def _is_interval(self, semitones_count):
111 main_tone = self.tones[0]
112 main_tone_ind = self._get_tone_ind(main_tone.name)
113 tones_count = len(self.tones)
114
115 for t in range(1, tones_count):
116 tone = self.tones[t]
117 tone_ind = self._get_tone_ind(tone.name)
118 if((tone_ind - main_tone_ind) % self._NUMBER_OF_SEMITONES == semitones_count):
119 return True
120
121 return False
122
123 def is_minor(self):
124 return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MINOR)
125
126 def is_major(self):
127 return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MAJOR)
128
129 def is_power_chord(self):
130 if not self.is_minor() and not self.is_major():
131 return True
132
133 return False
134
135 def _create_chord(self, new_tones):
136 """Create a new chord."""
137 new_main_tone = new_tones[0]
138 new_other_tones = new_tones[1:]
139
140 return Chord(new_main_tone, *new_other_tones)
141
142 def __add__(self, other):
143 tones_in_new_chord = list(self.tones)
144
145 if isinstance(other, Tone):
146 if other not in tones_in_new_chord:
147 tones_in_new_chord.append(other)
148 return self._create_chord(tones_in_new_chord)
149 elif isinstance(other, Chord):
150 for tone in other.tones:
151 if tone not in tones_in_new_chord:
152 tones_in_new_chord.append(tone)
153
154 return self._create_chord(tones_in_new_chord)
155
156 def __sub__(self, other):
157 if isinstance(other, Tone):
158 if other.name not in (tone.name for tone in self.tones):
159 raise TypeError(f"Cannot remove tone {other} from chord {self}")
160
161 tones_in_new_chord = list()
162
163 for tone in self.tones:
164 if tone.name != other.name:
165 tones_in_new_chord.append(tone)
166
167 if len(tones_in_new_chord) < self._MIN_UNIQUE_TONES_IN_CHORD:
168 raise TypeError("Cannot have a chord made of only 1 unique tone")
169
170 return self._create_chord(tones_in_new_chord)
171
172 def transposed(self, interval):
173 if isinstance(interval, Interval):
174 tones_in_new_chord = list()
175 for tone in self.tones:
176 new_tone = tone + interval
177 tones_in_new_chord.append(new_tone)
178
179 return self._create_chord(tones_in_new_chord)
.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.001s
OK
f | 1 | class Tone: | f | 1 | class Tone: |
2 | _NUMBER_OF_TONES = 12 | 2 | _NUMBER_OF_TONES = 12 | ||
3 | 3 | ||||
4 | tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 4 | tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
5 | 5 | ||||
6 | def __init__(self, name): | 6 | def __init__(self, name): | ||
7 | if name in self.tones: | 7 | if name in self.tones: | ||
8 | self.name = name | 8 | self.name = name | ||
9 | 9 | ||||
10 | def __str__(self): | 10 | def __str__(self): | ||
11 | return self.name | 11 | return self.name | ||
12 | 12 | ||||
13 | def __add__(self, other): | 13 | def __add__(self, other): | ||
14 | if isinstance(other, Tone): | 14 | if isinstance(other, Tone): | ||
15 | return Chord(self, other) | 15 | return Chord(self, other) | ||
16 | elif isinstance(other, Interval): | 16 | elif isinstance(other, Interval): | ||
17 | ind = self.tones.index(self.name) | 17 | ind = self.tones.index(self.name) | ||
18 | new_ind = (ind+other.number_of_semitones) % self._NUMBER_OF_TONES | 18 | new_ind = (ind+other.number_of_semitones) % self._NUMBER_OF_TONES | ||
19 | return Tone(self.tones[new_ind]) | 19 | return Tone(self.tones[new_ind]) | ||
20 | 20 | ||||
21 | def __sub__(self, other): | 21 | def __sub__(self, other): | ||
22 | if isinstance(other, Tone): | 22 | if isinstance(other, Tone): | ||
23 | number_of_semitones = (Tone.tones.index(self.name)-Tone.tones.index(other.name)) % self._NUMBER_OF_TONES | 23 | number_of_semitones = (Tone.tones.index(self.name)-Tone.tones.index(other.name)) % self._NUMBER_OF_TONES | ||
24 | return Interval(number_of_semitones) | 24 | return Interval(number_of_semitones) | ||
n | 25 | n | |||
26 | elif isinstance(other, Interval): | 25 | elif isinstance(other, Interval): | ||
27 | ind = self.tones.index(self.name) | 26 | ind = self.tones.index(self.name) | ||
28 | new_ind = (ind - other.number_of_semitones) % self._NUMBER_OF_TONES | 27 | new_ind = (ind - other.number_of_semitones) % self._NUMBER_OF_TONES | ||
29 | return Tone(self.tones[new_ind]) | 28 | return Tone(self.tones[new_ind]) | ||
30 | 29 | ||||
31 | 30 | ||||
32 | class Interval: | 31 | class Interval: | ||
33 | _NUMBER_OF_SEMITONES = 12 | 32 | _NUMBER_OF_SEMITONES = 12 | ||
34 | intervals = { | 33 | intervals = { | ||
35 | 0: "unison", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 34 | 0: "unison", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | ||
36 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 35 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | ||
37 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 36 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | ||
38 | 10: "minor 7th", 11: "major 7th" | 37 | 10: "minor 7th", 11: "major 7th" | ||
39 | } | 38 | } | ||
40 | 39 | ||||
41 | def __init__(self, number_of_semitones): | 40 | def __init__(self, number_of_semitones): | ||
42 | self.number_of_semitones = number_of_semitones % self._NUMBER_OF_SEMITONES | 41 | self.number_of_semitones = number_of_semitones % self._NUMBER_OF_SEMITONES | ||
43 | 42 | ||||
44 | def __str__(self): | 43 | def __str__(self): | ||
45 | return self.intervals[self.number_of_semitones] | 44 | return self.intervals[self.number_of_semitones] | ||
46 | 45 | ||||
47 | def __add__(self, other): | 46 | def __add__(self, other): | ||
48 | if isinstance(other, Tone): | 47 | if isinstance(other, Tone): | ||
49 | raise TypeError("Invalid operation") | 48 | raise TypeError("Invalid operation") | ||
50 | elif isinstance(other, Interval): | 49 | elif isinstance(other, Interval): | ||
51 | new_num_of_semitones = self.number_of_semitones + other.number_of_semitones | 50 | new_num_of_semitones = self.number_of_semitones + other.number_of_semitones | ||
52 | return Interval(new_num_of_semitones) | 51 | return Interval(new_num_of_semitones) | ||
53 | 52 | ||||
54 | def __sub__(self, other): | 53 | def __sub__(self, other): | ||
55 | if isinstance(other, Tone): | 54 | if isinstance(other, Tone): | ||
56 | raise TypeError("Invalid operation") | 55 | raise TypeError("Invalid operation") | ||
57 | 56 | ||||
58 | #used in the transpose function in order to add a negative interval to a tone | 57 | #used in the transpose function in order to add a negative interval to a tone | ||
59 | def __neg__(self): | 58 | def __neg__(self): | ||
60 | """Convert an interval to the negative value.""" | 59 | """Convert an interval to the negative value.""" | ||
61 | return Interval(-self.number_of_semitones) | 60 | return Interval(-self.number_of_semitones) | ||
62 | 61 | ||||
63 | 62 | ||||
64 | class Chord: | 63 | class Chord: | ||
65 | _NUMBER_OF_SEMITONES = 12 | 64 | _NUMBER_OF_SEMITONES = 12 | ||
66 | _MIN_UNIQUE_TONES_IN_CHORD = 2 | 65 | _MIN_UNIQUE_TONES_IN_CHORD = 2 | ||
67 | _NUMBER_OF_SEMITONES_IN_MINOR = 3 | 66 | _NUMBER_OF_SEMITONES_IN_MINOR = 3 | ||
68 | _NUMBER_OF_SEMITONES_IN_MAJOR = 4 | 67 | _NUMBER_OF_SEMITONES_IN_MAJOR = 4 | ||
69 | 68 | ||||
70 | def __init__(self, main_tone, *other_tones): | 69 | def __init__(self, main_tone, *other_tones): | ||
71 | unique_tone_names = {main_tone.name} | 70 | unique_tone_names = {main_tone.name} | ||
72 | unique_tones = [main_tone] | 71 | unique_tones = [main_tone] | ||
73 | 72 | ||||
74 | for tone in other_tones: | 73 | for tone in other_tones: | ||
75 | if tone.name not in unique_tone_names: | 74 | if tone.name not in unique_tone_names: | ||
76 | unique_tone_names.add(tone.name) | 75 | unique_tone_names.add(tone.name) | ||
77 | unique_tones.append(tone) | 76 | unique_tones.append(tone) | ||
78 | 77 | ||||
79 | if len(unique_tone_names) < self._MIN_UNIQUE_TONES_IN_CHORD: | 78 | if len(unique_tone_names) < self._MIN_UNIQUE_TONES_IN_CHORD: | ||
80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 79 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
81 | 80 | ||||
82 | main_tone_ind = Tone.tones.index(main_tone.name) | 81 | main_tone_ind = Tone.tones.index(main_tone.name) | ||
83 | self.main_tone_ind = main_tone_ind #so I can use it in _get_distance_from_main_tone | 82 | self.main_tone_ind = main_tone_ind #so I can use it in _get_distance_from_main_tone | ||
84 | tones_left = list() | 83 | tones_left = list() | ||
n | n | 84 | |||
85 | for tone in unique_tones: | 85 | for tone in unique_tones: | ||
86 | if tone != main_tone: | 86 | if tone != main_tone: | ||
87 | tones_left.append(tone) | 87 | tones_left.append(tone) | ||
88 | 88 | ||||
89 | tones_left.sort(key = self._get_distance_from_main_tone) | 89 | tones_left.sort(key = self._get_distance_from_main_tone) | ||
90 | 90 | ||||
91 | self.tones = [main_tone] | 91 | self.tones = [main_tone] | ||
92 | for tone in tones_left: | 92 | for tone in tones_left: | ||
93 | self.tones.append(tone) | 93 | self.tones.append(tone) | ||
94 | 94 | ||||
95 | def _get_tones_diff(self, tone, main_tone_ind): | 95 | def _get_tones_diff(self, tone, main_tone_ind): | ||
96 | """Return the count of semitones between a tone and the main_tone_ind.""" | 96 | """Return the count of semitones between a tone and the main_tone_ind.""" | ||
97 | return (Tone.tones.index(tone.name) - main_tone_ind) % self._NUMBER_OF_SEMITONES | 97 | return (Tone.tones.index(tone.name) - main_tone_ind) % self._NUMBER_OF_SEMITONES | ||
98 | 98 | ||||
99 | def _get_distance_from_main_tone(self, tone): | 99 | def _get_distance_from_main_tone(self, tone): | ||
100 | """Calculate the difference between the current tone and the main tone of the chord in semitones.""" | 100 | """Calculate the difference between the current tone and the main tone of the chord in semitones.""" | ||
101 | return self._get_tones_diff(tone, self.main_tone_ind) | 101 | return self._get_tones_diff(tone, self.main_tone_ind) | ||
102 | 102 | ||||
103 | def _get_tone_ind(self, tone_name): | 103 | def _get_tone_ind(self, tone_name): | ||
104 | """Return the index of a tone.""" | 104 | """Return the index of a tone.""" | ||
105 | return Tone.tones.index(tone_name) | 105 | return Tone.tones.index(tone_name) | ||
106 | 106 | ||||
107 | def __str__(self): | 107 | def __str__(self): | ||
108 | return f"{'-'.join(str(tone) for tone in self.tones)}" | 108 | return f"{'-'.join(str(tone) for tone in self.tones)}" | ||
109 | 109 | ||||
110 | def _is_interval(self, semitones_count): | 110 | def _is_interval(self, semitones_count): | ||
111 | main_tone = self.tones[0] | 111 | main_tone = self.tones[0] | ||
112 | main_tone_ind = self._get_tone_ind(main_tone.name) | 112 | main_tone_ind = self._get_tone_ind(main_tone.name) | ||
n | 113 | n | |||
114 | tones_count = len(self.tones) | 113 | tones_count = len(self.tones) | ||
n | n | 114 | |||
115 | for t in range(1, tones_count): | 115 | for t in range(1, tones_count): | ||
116 | tone = self.tones[t] | 116 | tone = self.tones[t] | ||
117 | tone_ind = self._get_tone_ind(tone.name) | 117 | tone_ind = self._get_tone_ind(tone.name) | ||
118 | if((tone_ind - main_tone_ind) % self._NUMBER_OF_SEMITONES == semitones_count): | 118 | if((tone_ind - main_tone_ind) % self._NUMBER_OF_SEMITONES == semitones_count): | ||
119 | return True | 119 | return True | ||
120 | 120 | ||||
121 | return False | 121 | return False | ||
122 | 122 | ||||
123 | def is_minor(self): | 123 | def is_minor(self): | ||
124 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MINOR) | 124 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MINOR) | ||
125 | 125 | ||||
126 | def is_major(self): | 126 | def is_major(self): | ||
127 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MAJOR) | 127 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MAJOR) | ||
128 | 128 | ||||
129 | def is_power_chord(self): | 129 | def is_power_chord(self): | ||
130 | if not self.is_minor() and not self.is_major(): | 130 | if not self.is_minor() and not self.is_major(): | ||
131 | return True | 131 | return True | ||
132 | 132 | ||||
133 | return False | 133 | return False | ||
134 | 134 | ||||
135 | def _create_chord(self, new_tones): | 135 | def _create_chord(self, new_tones): | ||
136 | """Create a new chord.""" | 136 | """Create a new chord.""" | ||
137 | new_main_tone = new_tones[0] | 137 | new_main_tone = new_tones[0] | ||
138 | new_other_tones = new_tones[1:] | 138 | new_other_tones = new_tones[1:] | ||
n | n | 139 | |||
139 | return Chord(new_main_tone, *new_other_tones) | 140 | return Chord(new_main_tone, *new_other_tones) | ||
140 | 141 | ||||
141 | def __add__(self, other): | 142 | def __add__(self, other): | ||
142 | tones_in_new_chord = list(self.tones) | 143 | tones_in_new_chord = list(self.tones) | ||
n | n | 144 | |||
143 | if isinstance(other, Tone): | 145 | if isinstance(other, Tone): | ||
144 | if other not in tones_in_new_chord: | 146 | if other not in tones_in_new_chord: | ||
145 | tones_in_new_chord.append(other) | 147 | tones_in_new_chord.append(other) | ||
n | 146 | n | |||
147 | return self._create_chord(tones_in_new_chord) | 148 | return self._create_chord(tones_in_new_chord) | ||
n | 148 | n | |||
149 | elif isinstance(other, Chord): | 149 | elif isinstance(other, Chord): | ||
150 | for tone in other.tones: | 150 | for tone in other.tones: | ||
151 | if tone not in tones_in_new_chord: | 151 | if tone not in tones_in_new_chord: | ||
152 | tones_in_new_chord.append(tone) | 152 | tones_in_new_chord.append(tone) | ||
153 | 153 | ||||
154 | return self._create_chord(tones_in_new_chord) | 154 | return self._create_chord(tones_in_new_chord) | ||
155 | 155 | ||||
156 | def __sub__(self, other): | 156 | def __sub__(self, other): | ||
157 | if isinstance(other, Tone): | 157 | if isinstance(other, Tone): | ||
158 | if other.name not in (tone.name for tone in self.tones): | 158 | if other.name not in (tone.name for tone in self.tones): | ||
159 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 159 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
160 | 160 | ||||
161 | tones_in_new_chord = list() | 161 | tones_in_new_chord = list() | ||
t | t | 162 | |||
162 | for tone in self.tones: | 163 | for tone in self.tones: | ||
163 | if tone.name != other.name: | 164 | if tone.name != other.name: | ||
164 | tones_in_new_chord.append(tone) | 165 | tones_in_new_chord.append(tone) | ||
165 | 166 | ||||
166 | if len(tones_in_new_chord) < self._MIN_UNIQUE_TONES_IN_CHORD: | 167 | if len(tones_in_new_chord) < self._MIN_UNIQUE_TONES_IN_CHORD: | ||
167 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 168 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
168 | 169 | ||||
169 | return self._create_chord(tones_in_new_chord) | 170 | return self._create_chord(tones_in_new_chord) | ||
170 | 171 | ||||
171 | def transposed(self, interval): | 172 | def transposed(self, interval): | ||
172 | if isinstance(interval, Interval): | 173 | if isinstance(interval, Interval): | ||
173 | tones_in_new_chord = list() | 174 | tones_in_new_chord = list() | ||
174 | for tone in self.tones: | 175 | for tone in self.tones: | ||
175 | new_tone = tone + interval | 176 | new_tone = tone + interval | ||
176 | tones_in_new_chord.append(new_tone) | 177 | tones_in_new_chord.append(new_tone) | ||
177 | 178 | ||||
178 | return self._create_chord(tones_in_new_chord) | 179 | return self._create_chord(tones_in_new_chord) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | _NUMBER_OF_TONES = 12 | 2 | _NUMBER_OF_TONES = 12 | ||
3 | 3 | ||||
4 | tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 4 | tones = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
5 | 5 | ||||
6 | def __init__(self, name): | 6 | def __init__(self, name): | ||
7 | if name in self.tones: | 7 | if name in self.tones: | ||
8 | self.name = name | 8 | self.name = name | ||
9 | 9 | ||||
10 | def __str__(self): | 10 | def __str__(self): | ||
11 | return self.name | 11 | return self.name | ||
12 | 12 | ||||
13 | def __add__(self, other): | 13 | def __add__(self, other): | ||
14 | if isinstance(other, Tone): | 14 | if isinstance(other, Tone): | ||
15 | return Chord(self, other) | 15 | return Chord(self, other) | ||
16 | elif isinstance(other, Interval): | 16 | elif isinstance(other, Interval): | ||
17 | ind = self.tones.index(self.name) | 17 | ind = self.tones.index(self.name) | ||
18 | new_ind = (ind+other.number_of_semitones) % self._NUMBER_OF_TONES | 18 | new_ind = (ind+other.number_of_semitones) % self._NUMBER_OF_TONES | ||
19 | return Tone(self.tones[new_ind]) | 19 | return Tone(self.tones[new_ind]) | ||
20 | 20 | ||||
21 | def __sub__(self, other): | 21 | def __sub__(self, other): | ||
22 | if isinstance(other, Tone): | 22 | if isinstance(other, Tone): | ||
23 | number_of_semitones = (Tone.tones.index(self.name)-Tone.tones.index(other.name)) % self._NUMBER_OF_TONES | 23 | number_of_semitones = (Tone.tones.index(self.name)-Tone.tones.index(other.name)) % self._NUMBER_OF_TONES | ||
24 | return Interval(number_of_semitones) | 24 | return Interval(number_of_semitones) | ||
25 | 25 | ||||
26 | elif isinstance(other, Interval): | 26 | elif isinstance(other, Interval): | ||
27 | ind = self.tones.index(self.name) | 27 | ind = self.tones.index(self.name) | ||
28 | new_ind = (ind - other.number_of_semitones) % self._NUMBER_OF_TONES | 28 | new_ind = (ind - other.number_of_semitones) % self._NUMBER_OF_TONES | ||
29 | return Tone(self.tones[new_ind]) | 29 | return Tone(self.tones[new_ind]) | ||
30 | 30 | ||||
31 | 31 | ||||
32 | class Interval: | 32 | class Interval: | ||
33 | _NUMBER_OF_SEMITONES = 12 | 33 | _NUMBER_OF_SEMITONES = 12 | ||
34 | intervals = { | 34 | intervals = { | ||
35 | 0: "unison", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 35 | 0: "unison", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | ||
36 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 36 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | ||
37 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 37 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | ||
38 | 10: "minor 7th", 11: "major 7th" | 38 | 10: "minor 7th", 11: "major 7th" | ||
39 | } | 39 | } | ||
40 | 40 | ||||
41 | def __init__(self, number_of_semitones): | 41 | def __init__(self, number_of_semitones): | ||
42 | self.number_of_semitones = number_of_semitones % self._NUMBER_OF_SEMITONES | 42 | self.number_of_semitones = number_of_semitones % self._NUMBER_OF_SEMITONES | ||
43 | 43 | ||||
44 | def __str__(self): | 44 | def __str__(self): | ||
45 | return self.intervals[self.number_of_semitones] | 45 | return self.intervals[self.number_of_semitones] | ||
46 | 46 | ||||
47 | def __add__(self, other): | 47 | def __add__(self, other): | ||
48 | if isinstance(other, Tone): | 48 | if isinstance(other, Tone): | ||
49 | raise TypeError("Invalid operation") | 49 | raise TypeError("Invalid operation") | ||
50 | elif isinstance(other, Interval): | 50 | elif isinstance(other, Interval): | ||
51 | new_num_of_semitones = self.number_of_semitones + other.number_of_semitones | 51 | new_num_of_semitones = self.number_of_semitones + other.number_of_semitones | ||
52 | return Interval(new_num_of_semitones) | 52 | return Interval(new_num_of_semitones) | ||
53 | 53 | ||||
54 | def __sub__(self, other): | 54 | def __sub__(self, other): | ||
55 | if isinstance(other, Tone): | 55 | if isinstance(other, Tone): | ||
56 | raise TypeError("Invalid operation") | 56 | raise TypeError("Invalid operation") | ||
57 | 57 | ||||
58 | #used in the transpose function in order to add a negative interval to a tone | 58 | #used in the transpose function in order to add a negative interval to a tone | ||
59 | def __neg__(self): | 59 | def __neg__(self): | ||
60 | """Convert an interval to the negative value.""" | 60 | """Convert an interval to the negative value.""" | ||
61 | return Interval(-self.number_of_semitones) | 61 | return Interval(-self.number_of_semitones) | ||
62 | 62 | ||||
63 | 63 | ||||
64 | class Chord: | 64 | class Chord: | ||
65 | _NUMBER_OF_SEMITONES = 12 | 65 | _NUMBER_OF_SEMITONES = 12 | ||
66 | _MIN_UNIQUE_TONES_IN_CHORD = 2 | 66 | _MIN_UNIQUE_TONES_IN_CHORD = 2 | ||
67 | _NUMBER_OF_SEMITONES_IN_MINOR = 3 | 67 | _NUMBER_OF_SEMITONES_IN_MINOR = 3 | ||
68 | _NUMBER_OF_SEMITONES_IN_MAJOR = 4 | 68 | _NUMBER_OF_SEMITONES_IN_MAJOR = 4 | ||
69 | 69 | ||||
70 | def __init__(self, main_tone, *other_tones): | 70 | def __init__(self, main_tone, *other_tones): | ||
71 | unique_tone_names = {main_tone.name} | 71 | unique_tone_names = {main_tone.name} | ||
72 | unique_tones = [main_tone] | 72 | unique_tones = [main_tone] | ||
73 | 73 | ||||
74 | for tone in other_tones: | 74 | for tone in other_tones: | ||
75 | if tone.name not in unique_tone_names: | 75 | if tone.name not in unique_tone_names: | ||
76 | unique_tone_names.add(tone.name) | 76 | unique_tone_names.add(tone.name) | ||
77 | unique_tones.append(tone) | 77 | unique_tones.append(tone) | ||
78 | 78 | ||||
79 | if len(unique_tone_names) < self._MIN_UNIQUE_TONES_IN_CHORD: | 79 | if len(unique_tone_names) < self._MIN_UNIQUE_TONES_IN_CHORD: | ||
80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
81 | 81 | ||||
82 | main_tone_ind = Tone.tones.index(main_tone.name) | 82 | main_tone_ind = Tone.tones.index(main_tone.name) | ||
83 | self.main_tone_ind = main_tone_ind #so I can use it in _get_distance_from_main_tone | 83 | self.main_tone_ind = main_tone_ind #so I can use it in _get_distance_from_main_tone | ||
84 | tones_left = list() | 84 | tones_left = list() | ||
85 | for tone in unique_tones: | 85 | for tone in unique_tones: | ||
86 | if tone != main_tone: | 86 | if tone != main_tone: | ||
87 | tones_left.append(tone) | 87 | tones_left.append(tone) | ||
88 | 88 | ||||
89 | tones_left.sort(key = self._get_distance_from_main_tone) | 89 | tones_left.sort(key = self._get_distance_from_main_tone) | ||
90 | 90 | ||||
91 | self.tones = [main_tone] | 91 | self.tones = [main_tone] | ||
92 | for tone in tones_left: | 92 | for tone in tones_left: | ||
93 | self.tones.append(tone) | 93 | self.tones.append(tone) | ||
94 | 94 | ||||
95 | def _get_tones_diff(self, tone, main_tone_ind): | 95 | def _get_tones_diff(self, tone, main_tone_ind): | ||
96 | """Return the count of semitones between a tone and the main_tone_ind.""" | 96 | """Return the count of semitones between a tone and the main_tone_ind.""" | ||
97 | return (Tone.tones.index(tone.name) - main_tone_ind) % self._NUMBER_OF_SEMITONES | 97 | return (Tone.tones.index(tone.name) - main_tone_ind) % self._NUMBER_OF_SEMITONES | ||
98 | 98 | ||||
99 | def _get_distance_from_main_tone(self, tone): | 99 | def _get_distance_from_main_tone(self, tone): | ||
100 | """Calculate the difference between the current tone and the main tone of the chord in semitones.""" | 100 | """Calculate the difference between the current tone and the main tone of the chord in semitones.""" | ||
101 | return self._get_tones_diff(tone, self.main_tone_ind) | 101 | return self._get_tones_diff(tone, self.main_tone_ind) | ||
102 | 102 | ||||
103 | def _get_tone_ind(self, tone_name): | 103 | def _get_tone_ind(self, tone_name): | ||
104 | """Return the index of a tone.""" | 104 | """Return the index of a tone.""" | ||
105 | return Tone.tones.index(tone_name) | 105 | return Tone.tones.index(tone_name) | ||
106 | 106 | ||||
107 | def __str__(self): | 107 | def __str__(self): | ||
108 | return f"{'-'.join(str(tone) for tone in self.tones)}" | 108 | return f"{'-'.join(str(tone) for tone in self.tones)}" | ||
109 | 109 | ||||
110 | def _is_interval(self, semitones_count): | 110 | def _is_interval(self, semitones_count): | ||
111 | main_tone = self.tones[0] | 111 | main_tone = self.tones[0] | ||
112 | main_tone_ind = self._get_tone_ind(main_tone.name) | 112 | main_tone_ind = self._get_tone_ind(main_tone.name) | ||
113 | 113 | ||||
114 | tones_count = len(self.tones) | 114 | tones_count = len(self.tones) | ||
115 | for t in range(1, tones_count): | 115 | for t in range(1, tones_count): | ||
116 | tone = self.tones[t] | 116 | tone = self.tones[t] | ||
117 | tone_ind = self._get_tone_ind(tone.name) | 117 | tone_ind = self._get_tone_ind(tone.name) | ||
t | 118 | t | |||
119 | if((tone_ind - main_tone_ind) % self._NUMBER_OF_SEMITONES == semitones_count): | 118 | if((tone_ind - main_tone_ind) % self._NUMBER_OF_SEMITONES == semitones_count): | ||
120 | return True | 119 | return True | ||
121 | 120 | ||||
122 | return False | 121 | return False | ||
123 | 122 | ||||
124 | def is_minor(self): | 123 | def is_minor(self): | ||
125 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MINOR) | 124 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MINOR) | ||
126 | 125 | ||||
127 | def is_major(self): | 126 | def is_major(self): | ||
128 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MAJOR) | 127 | return self._is_interval(self._NUMBER_OF_SEMITONES_IN_MAJOR) | ||
129 | 128 | ||||
130 | def is_power_chord(self): | 129 | def is_power_chord(self): | ||
131 | if not self.is_minor() and not self.is_major(): | 130 | if not self.is_minor() and not self.is_major(): | ||
132 | return True | 131 | return True | ||
133 | 132 | ||||
134 | return False | 133 | return False | ||
135 | 134 | ||||
136 | def _create_chord(self, new_tones): | 135 | def _create_chord(self, new_tones): | ||
137 | """Create a new chord.""" | 136 | """Create a new chord.""" | ||
138 | new_main_tone = new_tones[0] | 137 | new_main_tone = new_tones[0] | ||
139 | new_other_tones = new_tones[1:] | 138 | new_other_tones = new_tones[1:] | ||
140 | return Chord(new_main_tone, *new_other_tones) | 139 | return Chord(new_main_tone, *new_other_tones) | ||
141 | 140 | ||||
142 | def __add__(self, other): | 141 | def __add__(self, other): | ||
143 | tones_in_new_chord = list(self.tones) | 142 | tones_in_new_chord = list(self.tones) | ||
144 | if isinstance(other, Tone): | 143 | if isinstance(other, Tone): | ||
145 | if other not in tones_in_new_chord: | 144 | if other not in tones_in_new_chord: | ||
146 | tones_in_new_chord.append(other) | 145 | tones_in_new_chord.append(other) | ||
147 | 146 | ||||
148 | return self._create_chord(tones_in_new_chord) | 147 | return self._create_chord(tones_in_new_chord) | ||
149 | 148 | ||||
150 | elif isinstance(other, Chord): | 149 | elif isinstance(other, Chord): | ||
151 | for tone in other.tones: | 150 | for tone in other.tones: | ||
152 | if tone not in tones_in_new_chord: | 151 | if tone not in tones_in_new_chord: | ||
153 | tones_in_new_chord.append(tone) | 152 | tones_in_new_chord.append(tone) | ||
154 | 153 | ||||
155 | return self._create_chord(tones_in_new_chord) | 154 | return self._create_chord(tones_in_new_chord) | ||
156 | 155 | ||||
157 | def __sub__(self, other): | 156 | def __sub__(self, other): | ||
158 | if isinstance(other, Tone): | 157 | if isinstance(other, Tone): | ||
159 | if other.name not in (tone.name for tone in self.tones): | 158 | if other.name not in (tone.name for tone in self.tones): | ||
160 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 159 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
161 | 160 | ||||
162 | tones_in_new_chord = list() | 161 | tones_in_new_chord = list() | ||
163 | for tone in self.tones: | 162 | for tone in self.tones: | ||
164 | if tone.name != other.name: | 163 | if tone.name != other.name: | ||
165 | tones_in_new_chord.append(tone) | 164 | tones_in_new_chord.append(tone) | ||
166 | 165 | ||||
167 | if len(tones_in_new_chord) < self._MIN_UNIQUE_TONES_IN_CHORD: | 166 | if len(tones_in_new_chord) < self._MIN_UNIQUE_TONES_IN_CHORD: | ||
168 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 167 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
169 | 168 | ||||
170 | return self._create_chord(tones_in_new_chord) | 169 | return self._create_chord(tones_in_new_chord) | ||
171 | 170 | ||||
172 | def transposed(self, interval): | 171 | def transposed(self, interval): | ||
173 | if isinstance(interval, Interval): | 172 | if isinstance(interval, Interval): | ||
174 | tones_in_new_chord = list() | 173 | tones_in_new_chord = list() | ||
175 | for tone in self.tones: | 174 | for tone in self.tones: | ||
176 | new_tone = tone + interval | 175 | new_tone = tone + interval | ||
177 | tones_in_new_chord.append(new_tone) | 176 | tones_in_new_chord.append(new_tone) | ||
178 | 177 | ||||
179 | return self._create_chord(tones_in_new_chord) | 178 | return self._create_chord(tones_in_new_chord) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
07.11.2024 09:47
07.11.2024 09:48
07.11.2024 09:48
07.11.2024 09:53
07.11.2024 09:50
07.11.2024 09:52
07.11.2024 09:53
07.11.2024 09:54
07.11.2024 09:55
07.11.2024 09:55
07.11.2024 09:57
07.11.2024 09:57