1class Tone:
2 TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
3
4 def __init__(self, tone):
5 self.tone = tone
6
7 def __str__(self):
8 return self.tone
9
10 def __add__(self, other):
11 if isinstance(other, Tone):
12 return Chord(self, other)
13 elif isinstance(other, Interval):
14 start_index = self.TONES_SCALE.index(self.tone)
15 new_index = (start_index + other.number_of_semitones) % 12
16
17 return Tone(self.TONES_SCALE[new_index])
18
19 def __sub__(self, other):
20 if isinstance(other, Tone):
21 self_index = self.TONES_SCALE.index(self.tone)
22 other_index = self.TONES_SCALE.index(other.tone)
23
24 semitones_count = (self_index - other_index) % 12
25
26 return Interval(semitones_count)
27 elif isinstance(other, Interval):
28 start_index = self.TONES_SCALE.index(self.tone)
29 new_index = (start_index - other.number_of_semitones) % 12
30
31 return Tone(self.TONES_SCALE[new_index])
32
33
34class Interval:
35 def __init__(self, number_of_semitones):
36 self.number_of_semitones = number_of_semitones
37 self.is_positive_direction = True
38
39 def __str__(self):
40 self.number_of_semitones %= 12
41
42 intervals = (
43 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd',
44 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th',
45 'minor 6th', 'major 6th', 'minor 7th', 'major 7th'
46 )
47
48 return intervals[self.number_of_semitones]
49
50 def __add__(self, other):
51 if isinstance(other, Tone):
52 raise TypeError("Invalid operation")
53 elif isinstance(other, Interval):
54 return Interval(self.number_of_semitones + other.number_of_semitones)
55
56 def __sub__(self, other):
57 if isinstance(other, Tone):
58 raise TypeError("Invalid operation")
59
60 def __neg__(self):
61 neg_interval = Interval(self.number_of_semitones)
62 neg_interval.is_positive_direction = not self.is_positive_direction
63 return neg_interval
64
65
66class Chord:
67 TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
68
69 def __init__(self, first_tone, *args, **kwargs):
70 all_tones = [str(first_tone)] + [str(tone) for tone in args] + [str(tone) for tone in kwargs.values()]
71 self.chord = []
72
73 for tone in all_tones:
74 if tone not in self.chord:
75 self.chord.append(tone)
76
77 if len(self.chord) == 1:
78 raise TypeError("Cannot have a chord made of only 1 unique tone")
79
80 self.root = self.chord[0]
81
82 def __str__(self):
83 root_index = self.TONES_SCALE.index(self.root)
84
85 #Order the other tones based on the root.
86 sorted_tones = sorted(self.chord, key=lambda tone: (self.TONES_SCALE.index(tone) - root_index) % 12)
87
88 return "-".join(sorted_tones)
89
90 def __add__(self, other):
91 if isinstance(other, Tone):
92 new_tones = self.chord[:]
93 new_tones.append(other.tone)
94 return Chord(self.root, *new_tones)
95 elif isinstance(other, Chord):
96 return Chord(self.root, *self.chord, *other.chord)
97
98 def __sub__(self, other):
99 if isinstance(other, Tone):
100 curr_tones = list(self.chord)
101
102 if other.tone in curr_tones:
103 curr_tones.remove(other.tone)
104 self.root = curr_tones[0]
105 else:
106 raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")
107
108 if len(curr_tones) >= 2:
109 return Chord(self.root, *curr_tones)
110 else:
111 raise TypeError("Cannot have a chord made of only 1 unique tone")
112
113 def _has_interval(self, interval):
114 """ A private helper method to check if any tone forms a specific interval with the root. """
115
116 root_index = self.TONES_SCALE.index(self.root)
117
118 for tone in self.chord[1:]:
119 tone_index = self.TONES_SCALE.index(tone)
120 curr_interval = (tone_index - root_index) % 12
121
122 if curr_interval == interval:
123 return True
124 return False
125
126 def is_minor(self):
127 return self._has_interval(3)
128
129 def is_major(self):
130 return self._has_interval(4)
131
132 def is_power_chord(self):
133 return not self.is_minor() and not self.is_major()
134
135 def transposed(self, interval):
136 if isinstance(interval, Interval):
137 transposed_tones = []
138
139 for tone in self.chord:
140 curr_index = self.TONES_SCALE.index(tone)
141
142 if interval.is_positive_direction:
143 new_index = (curr_index + interval.number_of_semitones) % 12
144 else:
145 new_index = (curr_index - interval.number_of_semitones) % 12
146 transposed_tones.append(self.TONES_SCALE[new_index])
147
148 return Chord(transposed_tones[0], *transposed_tones[1:])
...........F.........................
======================================================================
FAIL: test_interval_negative (test.TestBasicIntervalFunctionality.test_interval_negative)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 51, in test_interval_negative
self.assertEqual(str(minor_2nd), "minor 2nd")
AssertionError: 'major 7th' != 'minor 2nd'
- major 7th
+ minor 2nd
----------------------------------------------------------------------
Ran 37 tests in 0.002s
FAILED (failures=1)
f | 1 | class Tone: | f | 1 | class Tone: |
2 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
3 | 3 | ||||
4 | def __init__(self, tone): | 4 | def __init__(self, tone): | ||
5 | self.tone = tone | 5 | self.tone = tone | ||
6 | 6 | ||||
7 | def __str__(self): | 7 | def __str__(self): | ||
8 | return self.tone | 8 | return self.tone | ||
9 | 9 | ||||
10 | def __add__(self, other): | 10 | def __add__(self, other): | ||
11 | if isinstance(other, Tone): | 11 | if isinstance(other, Tone): | ||
12 | return Chord(self, other) | 12 | return Chord(self, other) | ||
13 | elif isinstance(other, Interval): | 13 | elif isinstance(other, Interval): | ||
14 | start_index = self.TONES_SCALE.index(self.tone) | 14 | start_index = self.TONES_SCALE.index(self.tone) | ||
15 | new_index = (start_index + other.number_of_semitones) % 12 | 15 | new_index = (start_index + other.number_of_semitones) % 12 | ||
16 | 16 | ||||
17 | return Tone(self.TONES_SCALE[new_index]) | 17 | return Tone(self.TONES_SCALE[new_index]) | ||
18 | 18 | ||||
19 | def __sub__(self, other): | 19 | def __sub__(self, other): | ||
20 | if isinstance(other, Tone): | 20 | if isinstance(other, Tone): | ||
21 | self_index = self.TONES_SCALE.index(self.tone) | 21 | self_index = self.TONES_SCALE.index(self.tone) | ||
22 | other_index = self.TONES_SCALE.index(other.tone) | 22 | other_index = self.TONES_SCALE.index(other.tone) | ||
23 | 23 | ||||
24 | semitones_count = (self_index - other_index) % 12 | 24 | semitones_count = (self_index - other_index) % 12 | ||
25 | 25 | ||||
26 | return Interval(semitones_count) | 26 | return Interval(semitones_count) | ||
27 | elif isinstance(other, Interval): | 27 | elif isinstance(other, Interval): | ||
28 | start_index = self.TONES_SCALE.index(self.tone) | 28 | start_index = self.TONES_SCALE.index(self.tone) | ||
29 | new_index = (start_index - other.number_of_semitones) % 12 | 29 | new_index = (start_index - other.number_of_semitones) % 12 | ||
30 | 30 | ||||
31 | return Tone(self.TONES_SCALE[new_index]) | 31 | return Tone(self.TONES_SCALE[new_index]) | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Interval: | 34 | class Interval: | ||
35 | def __init__(self, number_of_semitones): | 35 | def __init__(self, number_of_semitones): | ||
36 | self.number_of_semitones = number_of_semitones | 36 | self.number_of_semitones = number_of_semitones | ||
n | 37 | self.isPositiveDirection = True | n | 37 | self.is_positive_direction = True |
38 | 38 | ||||
39 | def __str__(self): | 39 | def __str__(self): | ||
40 | self.number_of_semitones %= 12 | 40 | self.number_of_semitones %= 12 | ||
41 | 41 | ||||
n | 42 | intervals = { | n | 42 | intervals = ( |
43 | 0: "unison", | 43 | 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', | ||
44 | 1: "minor 2nd", | 44 | 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', | ||
45 | 2: "major 2nd", | 45 | 'minor 6th', 'major 6th', 'minor 7th', 'major 7th' | ||
46 | 3: "minor 3rd", | ||||
47 | 4: "major 3rd", | ||||
48 | 5: "perfect 4th", | ||||
49 | 6: "diminished 5th", | ||||
50 | 7: "perfect 5th", | ||||
51 | 8: "minor 6th", | ||||
52 | 9: "major 6th", | ||||
53 | 10: "minor 7th", | ||||
54 | 11: "major 7th" | ||||
55 | } | 46 | ) | ||
56 | 47 | ||||
57 | return intervals[self.number_of_semitones] | 48 | return intervals[self.number_of_semitones] | ||
58 | 49 | ||||
59 | def __add__(self, other): | 50 | def __add__(self, other): | ||
60 | if isinstance(other, Tone): | 51 | if isinstance(other, Tone): | ||
61 | raise TypeError("Invalid operation") | 52 | raise TypeError("Invalid operation") | ||
62 | elif isinstance(other, Interval): | 53 | elif isinstance(other, Interval): | ||
63 | return Interval(self.number_of_semitones + other.number_of_semitones) | 54 | return Interval(self.number_of_semitones + other.number_of_semitones) | ||
64 | 55 | ||||
65 | def __sub__(self, other): | 56 | def __sub__(self, other): | ||
66 | if isinstance(other, Tone): | 57 | if isinstance(other, Tone): | ||
67 | raise TypeError("Invalid operation") | 58 | raise TypeError("Invalid operation") | ||
68 | 59 | ||||
69 | def __neg__(self): | 60 | def __neg__(self): | ||
70 | neg_interval = Interval(self.number_of_semitones) | 61 | neg_interval = Interval(self.number_of_semitones) | ||
n | 71 | neg_interval.isPositiveDirection = not self.isPositiveDirection | n | 62 | neg_interval.is_positive_direction = not self.is_positive_direction |
72 | return neg_interval | 63 | return neg_interval | ||
73 | 64 | ||||
74 | 65 | ||||
75 | class Chord: | 66 | class Chord: | ||
76 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 67 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
77 | 68 | ||||
78 | def __init__(self, first_tone, *args, **kwargs): | 69 | def __init__(self, first_tone, *args, **kwargs): | ||
79 | all_tones = [str(first_tone)] + [str(tone) for tone in args] + [str(tone) for tone in kwargs.values()] | 70 | all_tones = [str(first_tone)] + [str(tone) for tone in args] + [str(tone) for tone in kwargs.values()] | ||
n | 80 | unique_tones = [] | n | 71 | self.chord = [] |
81 | 72 | ||||
82 | for tone in all_tones: | 73 | for tone in all_tones: | ||
n | 83 | if tone not in unique_tones: | n | 74 | if tone not in self.chord: |
84 | unique_tones.append(tone) | 75 | self.chord.append(tone) | ||
85 | 76 | ||||
n | 86 | if len(unique_tones) == 1: | n | 77 | if len(self.chord) == 1: |
87 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 78 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
88 | 79 | ||||
n | 89 | self.chord = unique_tones | n | 80 | self.root = self.chord[0] |
90 | self.root = unique_tones[0] | ||||
91 | 81 | ||||
92 | def __str__(self): | 82 | def __str__(self): | ||
93 | root_index = self.TONES_SCALE.index(self.root) | 83 | root_index = self.TONES_SCALE.index(self.root) | ||
94 | 84 | ||||
n | 95 | """ Order the other tones based on the root. """ | n | 85 | #Order the other tones based on the root. |
96 | sorted_tones = sorted(self.chord, key=lambda tone: (self.TONES_SCALE.index(tone) - root_index) % 12) | 86 | sorted_tones = sorted(self.chord, key=lambda tone: (self.TONES_SCALE.index(tone) - root_index) % 12) | ||
97 | 87 | ||||
98 | return "-".join(sorted_tones) | 88 | return "-".join(sorted_tones) | ||
99 | 89 | ||||
100 | def __add__(self, other): | 90 | def __add__(self, other): | ||
101 | if isinstance(other, Tone): | 91 | if isinstance(other, Tone): | ||
n | 102 | new_tones = list(self.chord) | n | 92 | new_tones = self.chord[:] |
103 | |||||
104 | if not other.tone in new_tones: | ||||
105 | new_tones.append(other.tone) | 93 | new_tones.append(other.tone) | ||
106 | |||||
107 | return Chord(self.root, *new_tones) | 94 | return Chord(self.root, *new_tones) | ||
108 | elif isinstance(other, Chord): | 95 | elif isinstance(other, Chord): | ||
n | 109 | combined_tones = list(self.chord) + list(other.chord) | n | 96 | return Chord(self.root, *self.chord, *other.chord) |
110 | return Chord(self.root, *combined_tones) | ||||
111 | 97 | ||||
112 | def __sub__(self, other): | 98 | def __sub__(self, other): | ||
113 | if isinstance(other, Tone): | 99 | if isinstance(other, Tone): | ||
114 | curr_tones = list(self.chord) | 100 | curr_tones = list(self.chord) | ||
115 | 101 | ||||
116 | if other.tone in curr_tones: | 102 | if other.tone in curr_tones: | ||
117 | curr_tones.remove(other.tone) | 103 | curr_tones.remove(other.tone) | ||
118 | self.root = curr_tones[0] | 104 | self.root = curr_tones[0] | ||
119 | else: | 105 | else: | ||
n | 120 | raise TypeError(f"Cannot remove tone {other.tone} from chord {str(self)}") | n | 106 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") |
121 | 107 | ||||
122 | if len(curr_tones) >= 2: | 108 | if len(curr_tones) >= 2: | ||
123 | return Chord(self.root, *curr_tones) | 109 | return Chord(self.root, *curr_tones) | ||
124 | else: | 110 | else: | ||
125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 111 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
126 | 112 | ||||
127 | def _has_interval(self, interval): | 113 | def _has_interval(self, interval): | ||
128 | """ A private helper method to check if any tone forms a specific interval with the root. """ | 114 | """ A private helper method to check if any tone forms a specific interval with the root. """ | ||
129 | 115 | ||||
130 | root_index = self.TONES_SCALE.index(self.root) | 116 | root_index = self.TONES_SCALE.index(self.root) | ||
131 | 117 | ||||
n | 132 | for tone in self.chord: | n | 118 | for tone in self.chord[1:]: |
133 | |||||
134 | if tone == self.root: | ||||
135 | continue | ||||
136 | |||||
137 | tone_index = self.TONES_SCALE.index(tone) | 119 | tone_index = self.TONES_SCALE.index(tone) | ||
n | 138 | n | |||
139 | curr_interval = (tone_index - root_index) % 12 | 120 | curr_interval = (tone_index - root_index) % 12 | ||
140 | 121 | ||||
141 | if curr_interval == interval: | 122 | if curr_interval == interval: | ||
142 | return True | 123 | return True | ||
n | 143 | n | |||
144 | return False | 124 | return False | ||
145 | 125 | ||||
146 | def is_minor(self): | 126 | def is_minor(self): | ||
147 | return self._has_interval(3) | 127 | return self._has_interval(3) | ||
148 | 128 | ||||
149 | def is_major(self): | 129 | def is_major(self): | ||
150 | return self._has_interval(4) | 130 | return self._has_interval(4) | ||
151 | 131 | ||||
152 | def is_power_chord(self): | 132 | def is_power_chord(self): | ||
n | 153 | if not self.is_minor() and not self.is_major(): | n | 133 | return not self.is_minor() and not self.is_major() |
154 | return True | ||||
155 | return False | ||||
156 | 134 | ||||
157 | def transposed(self, interval): | 135 | def transposed(self, interval): | ||
158 | if isinstance(interval, Interval): | 136 | if isinstance(interval, Interval): | ||
159 | transposed_tones = [] | 137 | transposed_tones = [] | ||
160 | 138 | ||||
161 | for tone in self.chord: | 139 | for tone in self.chord: | ||
162 | curr_index = self.TONES_SCALE.index(tone) | 140 | curr_index = self.TONES_SCALE.index(tone) | ||
163 | 141 | ||||
t | 164 | if interval.isPositiveDirection: | t | 142 | if interval.is_positive_direction: |
165 | new_index = (curr_index + interval.number_of_semitones) % 12 | 143 | new_index = (curr_index + interval.number_of_semitones) % 12 | ||
166 | else: | 144 | else: | ||
167 | new_index = (curr_index - interval.number_of_semitones) % 12 | 145 | new_index = (curr_index - interval.number_of_semitones) % 12 | ||
168 | transposed_tones.append(self.TONES_SCALE[new_index]) | 146 | transposed_tones.append(self.TONES_SCALE[new_index]) | ||
169 | 147 | ||||
170 | return Chord(transposed_tones[0], *transposed_tones[1:]) | 148 | return Chord(transposed_tones[0], *transposed_tones[1:]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
3 | 3 | ||||
4 | def __init__(self, tone): | 4 | def __init__(self, tone): | ||
5 | self.tone = tone | 5 | self.tone = tone | ||
6 | 6 | ||||
7 | def __str__(self): | 7 | def __str__(self): | ||
8 | return self.tone | 8 | return self.tone | ||
9 | 9 | ||||
10 | def __add__(self, other): | 10 | def __add__(self, other): | ||
11 | if isinstance(other, Tone): | 11 | if isinstance(other, Tone): | ||
12 | return Chord(self, other) | 12 | return Chord(self, other) | ||
13 | elif isinstance(other, Interval): | 13 | elif isinstance(other, Interval): | ||
14 | start_index = self.TONES_SCALE.index(self.tone) | 14 | start_index = self.TONES_SCALE.index(self.tone) | ||
15 | new_index = (start_index + other.number_of_semitones) % 12 | 15 | new_index = (start_index + other.number_of_semitones) % 12 | ||
16 | 16 | ||||
17 | return Tone(self.TONES_SCALE[new_index]) | 17 | return Tone(self.TONES_SCALE[new_index]) | ||
18 | 18 | ||||
19 | def __sub__(self, other): | 19 | def __sub__(self, other): | ||
20 | if isinstance(other, Tone): | 20 | if isinstance(other, Tone): | ||
21 | self_index = self.TONES_SCALE.index(self.tone) | 21 | self_index = self.TONES_SCALE.index(self.tone) | ||
22 | other_index = self.TONES_SCALE.index(other.tone) | 22 | other_index = self.TONES_SCALE.index(other.tone) | ||
23 | 23 | ||||
24 | semitones_count = (self_index - other_index) % 12 | 24 | semitones_count = (self_index - other_index) % 12 | ||
25 | 25 | ||||
26 | return Interval(semitones_count) | 26 | return Interval(semitones_count) | ||
27 | elif isinstance(other, Interval): | 27 | elif isinstance(other, Interval): | ||
28 | start_index = self.TONES_SCALE.index(self.tone) | 28 | start_index = self.TONES_SCALE.index(self.tone) | ||
29 | new_index = (start_index - other.number_of_semitones) % 12 | 29 | new_index = (start_index - other.number_of_semitones) % 12 | ||
30 | 30 | ||||
31 | return Tone(self.TONES_SCALE[new_index]) | 31 | return Tone(self.TONES_SCALE[new_index]) | ||
32 | 32 | ||||
33 | 33 | ||||
34 | class Interval: | 34 | class Interval: | ||
35 | def __init__(self, number_of_semitones): | 35 | def __init__(self, number_of_semitones): | ||
36 | self.number_of_semitones = number_of_semitones | 36 | self.number_of_semitones = number_of_semitones | ||
37 | self.isPositiveDirection = True | 37 | self.isPositiveDirection = True | ||
38 | 38 | ||||
39 | def __str__(self): | 39 | def __str__(self): | ||
40 | self.number_of_semitones %= 12 | 40 | self.number_of_semitones %= 12 | ||
41 | 41 | ||||
42 | intervals = { | 42 | intervals = { | ||
43 | 0: "unison", | 43 | 0: "unison", | ||
44 | 1: "minor 2nd", | 44 | 1: "minor 2nd", | ||
45 | 2: "major 2nd", | 45 | 2: "major 2nd", | ||
46 | 3: "minor 3rd", | 46 | 3: "minor 3rd", | ||
47 | 4: "major 3rd", | 47 | 4: "major 3rd", | ||
48 | 5: "perfect 4th", | 48 | 5: "perfect 4th", | ||
49 | 6: "diminished 5th", | 49 | 6: "diminished 5th", | ||
50 | 7: "perfect 5th", | 50 | 7: "perfect 5th", | ||
51 | 8: "minor 6th", | 51 | 8: "minor 6th", | ||
52 | 9: "major 6th", | 52 | 9: "major 6th", | ||
53 | 10: "minor 7th", | 53 | 10: "minor 7th", | ||
54 | 11: "major 7th" | 54 | 11: "major 7th" | ||
55 | } | 55 | } | ||
56 | 56 | ||||
57 | return intervals[self.number_of_semitones] | 57 | return intervals[self.number_of_semitones] | ||
58 | 58 | ||||
59 | def __add__(self, other): | 59 | def __add__(self, other): | ||
60 | if isinstance(other, Tone): | 60 | if isinstance(other, Tone): | ||
61 | raise TypeError("Invalid operation") | 61 | raise TypeError("Invalid operation") | ||
62 | elif isinstance(other, Interval): | 62 | elif isinstance(other, Interval): | ||
63 | return Interval(self.number_of_semitones + other.number_of_semitones) | 63 | return Interval(self.number_of_semitones + other.number_of_semitones) | ||
64 | 64 | ||||
65 | def __sub__(self, other): | 65 | def __sub__(self, other): | ||
66 | if isinstance(other, Tone): | 66 | if isinstance(other, Tone): | ||
67 | raise TypeError("Invalid operation") | 67 | raise TypeError("Invalid operation") | ||
68 | 68 | ||||
69 | def __neg__(self): | 69 | def __neg__(self): | ||
70 | neg_interval = Interval(self.number_of_semitones) | 70 | neg_interval = Interval(self.number_of_semitones) | ||
71 | neg_interval.isPositiveDirection = not self.isPositiveDirection | 71 | neg_interval.isPositiveDirection = not self.isPositiveDirection | ||
72 | return neg_interval | 72 | return neg_interval | ||
t | t | 73 | |||
73 | 74 | ||||
74 | class Chord: | 75 | class Chord: | ||
75 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 76 | TONES_SCALE = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
76 | 77 | ||||
77 | def __init__(self, first_tone, *args, **kwargs): | 78 | def __init__(self, first_tone, *args, **kwargs): | ||
78 | all_tones = [str(first_tone)] + [str(tone) for tone in args] + [str(tone) for tone in kwargs.values()] | 79 | all_tones = [str(first_tone)] + [str(tone) for tone in args] + [str(tone) for tone in kwargs.values()] | ||
79 | unique_tones = [] | 80 | unique_tones = [] | ||
80 | 81 | ||||
81 | for tone in all_tones: | 82 | for tone in all_tones: | ||
82 | if tone not in unique_tones: | 83 | if tone not in unique_tones: | ||
83 | unique_tones.append(tone) | 84 | unique_tones.append(tone) | ||
84 | 85 | ||||
85 | if len(unique_tones) == 1: | 86 | if len(unique_tones) == 1: | ||
86 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 87 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
87 | 88 | ||||
88 | self.chord = unique_tones | 89 | self.chord = unique_tones | ||
89 | self.root = unique_tones[0] | 90 | self.root = unique_tones[0] | ||
90 | 91 | ||||
91 | def __str__(self): | 92 | def __str__(self): | ||
92 | root_index = self.TONES_SCALE.index(self.root) | 93 | root_index = self.TONES_SCALE.index(self.root) | ||
93 | 94 | ||||
94 | """ Order the other tones based on the root. """ | 95 | """ Order the other tones based on the root. """ | ||
95 | sorted_tones = sorted(self.chord, key=lambda tone: (self.TONES_SCALE.index(tone) - root_index) % 12) | 96 | sorted_tones = sorted(self.chord, key=lambda tone: (self.TONES_SCALE.index(tone) - root_index) % 12) | ||
96 | 97 | ||||
97 | return "-".join(sorted_tones) | 98 | return "-".join(sorted_tones) | ||
98 | 99 | ||||
99 | def __add__(self, other): | 100 | def __add__(self, other): | ||
100 | if isinstance(other, Tone): | 101 | if isinstance(other, Tone): | ||
101 | new_tones = list(self.chord) | 102 | new_tones = list(self.chord) | ||
102 | 103 | ||||
103 | if not other.tone in new_tones: | 104 | if not other.tone in new_tones: | ||
104 | new_tones.append(other.tone) | 105 | new_tones.append(other.tone) | ||
105 | 106 | ||||
106 | return Chord(self.root, *new_tones) | 107 | return Chord(self.root, *new_tones) | ||
107 | elif isinstance(other, Chord): | 108 | elif isinstance(other, Chord): | ||
108 | combined_tones = list(self.chord) + list(other.chord) | 109 | combined_tones = list(self.chord) + list(other.chord) | ||
109 | return Chord(self.root, *combined_tones) | 110 | return Chord(self.root, *combined_tones) | ||
110 | 111 | ||||
111 | def __sub__(self, other): | 112 | def __sub__(self, other): | ||
112 | if isinstance(other, Tone): | 113 | if isinstance(other, Tone): | ||
113 | curr_tones = list(self.chord) | 114 | curr_tones = list(self.chord) | ||
114 | 115 | ||||
115 | if other.tone in curr_tones: | 116 | if other.tone in curr_tones: | ||
116 | curr_tones.remove(other.tone) | 117 | curr_tones.remove(other.tone) | ||
117 | self.root = curr_tones[0] | 118 | self.root = curr_tones[0] | ||
118 | else: | 119 | else: | ||
119 | raise TypeError(f"Cannot remove tone {other.tone} from chord {str(self)}") | 120 | raise TypeError(f"Cannot remove tone {other.tone} from chord {str(self)}") | ||
120 | 121 | ||||
121 | if len(curr_tones) >= 2: | 122 | if len(curr_tones) >= 2: | ||
122 | return Chord(self.root, *curr_tones) | 123 | return Chord(self.root, *curr_tones) | ||
123 | else: | 124 | else: | ||
124 | 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") | ||
125 | 126 | ||||
126 | def _has_interval(self, interval): | 127 | def _has_interval(self, interval): | ||
127 | """ A private helper method to check if any tone forms a specific interval with the root. """ | 128 | """ A private helper method to check if any tone forms a specific interval with the root. """ | ||
128 | 129 | ||||
129 | root_index = self.TONES_SCALE.index(self.root) | 130 | root_index = self.TONES_SCALE.index(self.root) | ||
130 | 131 | ||||
131 | for tone in self.chord: | 132 | for tone in self.chord: | ||
132 | 133 | ||||
133 | if tone == self.root: | 134 | if tone == self.root: | ||
134 | continue | 135 | continue | ||
135 | 136 | ||||
136 | tone_index = self.TONES_SCALE.index(tone) | 137 | tone_index = self.TONES_SCALE.index(tone) | ||
137 | 138 | ||||
138 | curr_interval = (tone_index - root_index) % 12 | 139 | curr_interval = (tone_index - root_index) % 12 | ||
139 | 140 | ||||
140 | if curr_interval == interval: | 141 | if curr_interval == interval: | ||
141 | return True | 142 | return True | ||
142 | 143 | ||||
143 | return False | 144 | return False | ||
144 | 145 | ||||
145 | def is_minor(self): | 146 | def is_minor(self): | ||
146 | return self._has_interval(3) | 147 | return self._has_interval(3) | ||
147 | 148 | ||||
148 | def is_major(self): | 149 | def is_major(self): | ||
149 | return self._has_interval(4) | 150 | return self._has_interval(4) | ||
150 | 151 | ||||
151 | def is_power_chord(self): | 152 | def is_power_chord(self): | ||
152 | if not self.is_minor() and not self.is_major(): | 153 | if not self.is_minor() and not self.is_major(): | ||
153 | return True | 154 | return True | ||
154 | return False | 155 | return False | ||
155 | 156 | ||||
156 | def transposed(self, interval): | 157 | def transposed(self, interval): | ||
157 | if isinstance(interval, Interval): | 158 | if isinstance(interval, Interval): | ||
158 | transposed_tones = [] | 159 | transposed_tones = [] | ||
159 | 160 | ||||
160 | for tone in self.chord: | 161 | for tone in self.chord: | ||
161 | curr_index = self.TONES_SCALE.index(tone) | 162 | curr_index = self.TONES_SCALE.index(tone) | ||
162 | 163 | ||||
163 | if interval.isPositiveDirection: | 164 | if interval.isPositiveDirection: | ||
164 | new_index = (curr_index + interval.number_of_semitones) % 12 | 165 | new_index = (curr_index + interval.number_of_semitones) % 12 | ||
165 | else: | 166 | else: | ||
166 | new_index = (curr_index - interval.number_of_semitones) % 12 | 167 | new_index = (curr_index - interval.number_of_semitones) % 12 | ||
167 | transposed_tones.append(self.TONES_SCALE[new_index]) | 168 | transposed_tones.append(self.TONES_SCALE[new_index]) | ||
168 | 169 | ||||
169 | return Chord(transposed_tones[0], *transposed_tones[1:]) | 170 | return Chord(transposed_tones[0], *transposed_tones[1:]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|