1from operator import truediv
2
3CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#',
4 'E', 'F', 'F#', 'G',
5 'G#', 'A', 'A#', 'B']
6
7SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd',
8 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th',
9 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'}
10
11INTERVAL_LEN = len(CHROMATIC_SCALE)
12MINOR_IDX = 3
13MAJOR_IDX = 4
14
15class Tone:
16 def __init__(self, name):
17 self.name = name
18
19 def __str__(self):
20 return self.name
21
22 def __eq__(self, other):
23 return self.name == other.name
24
25 def __add__(self, other):
26 if isinstance(other, Interval):
27 new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % INTERVAL_LEN
28 return Tone(CHROMATIC_SCALE[new_tone_idx])
29 return Chord(self, other)
30
31 def __sub__(self, other):
32 first_idx = CHROMATIC_SCALE.index(self.name)
33 if isinstance(other, Interval):
34 second_idx = other.number_of_semitones
35 res_idx = (first_idx - second_idx) % INTERVAL_LEN
36 return Tone(CHROMATIC_SCALE[res_idx])
37 second_idx = CHROMATIC_SCALE.index(other.name)
38 res_idx = (first_idx - second_idx) % INTERVAL_LEN
39 return Interval(res_idx)
40
41
42class Interval:
43
44 def __init__(self, number_of_semitones):
45 self.number_of_semitones = number_of_semitones
46
47 def __str__(self):
48 return SEMITONES_TYPES[self.number_of_semitones % INTERVAL_LEN]
49
50 def __add__(self, other):
51 if isinstance(other, Tone):
52 raise TypeError('Invalid operation')
53 else:
54 semitones = (self.number_of_semitones + other.number_of_semitones) % INTERVAL_LEN
55 return Interval(semitones)
56
57 def __sub__(self, other):
58 if isinstance(other, Tone):
59 raise TypeError('Invalid operation')
60
61 def __neg__(self):
62 return Interval(-self.number_of_semitones)
63
64
65class Chord:
66 def __init__(self, root, *tones):
67 self.root = root
68 self.all_tones = [root, *tones]
69 self.uniques = self._unique_tones(self.all_tones)
70 if len(self.uniques) < 2:
71 raise TypeError("Cannot have a chord made of only 1 unique tone")
72 #calculates how far is every unique tone from the root and sorts(increase) by this parameter
73 self.sorted_unique_tones = sorted(self.uniques, key=lambda tone:
74 (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE))
75
76 @staticmethod
77 def _unique_tones(tones):
78 unique_list = []
79 for tone in tones:
80 if tone not in unique_list:
81 unique_list.append(tone)
82 return unique_list
83
84 def __str__(self):
85 return '-'.join(map(str, self.sorted_unique_tones))
86
87 def is_minor(self):
88 return self._minor_major(MINOR_IDX)
89
90 def is_major(self):
91 return self._minor_major(MAJOR_IDX)
92
93 def is_power_chord(self):
94 return not (self.is_minor() or self.is_major())
95
96 def _minor_major(self, interval):
97 index_of_root = CHROMATIC_SCALE.index(self.root.name)
98 for tone in self.sorted_unique_tones:
99 curr_tone = CHROMATIC_SCALE.index(tone.name)
100 diff = (curr_tone - index_of_root)
101 if diff == interval:
102 return True
103 elif diff < 0 and (12 + diff == interval):
104 return True
105 return False
106
107 def __add__(self, other):
108 if isinstance(other, Tone):
109 tones = self.all_tones + [other]
110 end_tones = tuple(tones)
111 return Chord(self.root, *end_tones)
112 all_tones = tuple(self.all_tones) + tuple(other.all_tones)
113 return Chord(self.root, *all_tones)
114
115 def __sub__(self, other):
116 if isinstance(other, Tone):
117 if other not in self.uniques:
118 raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}')
119 elif len(self.uniques) >= 3:
120 tones = []
121 for tone in self.all_tones:
122 if tone != other:
123 tones.append(tone)
124 new_root = tones[0]
125 return Chord(new_root, *(tuple(tones)))
126 else:
127 raise TypeError('Cannot have a chord made of only 1 unique tone')
128
129 def transposed(self, interval):
130 interval_num = interval.number_of_semitones
131 tones = []
132 for tone in self.all_tones:
133 curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % INTERVAL_LEN
134 tones.append(Tone(CHROMATIC_SCALE[curr_idx]))
135 new_root = tones[0]
136 return Chord(new_root, *(tuple(tones[1:])))
.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.001s
OK
Георги Кунчев
04.11.2024 18:18Да, има превъртане.
|
Елица Павлова
04.11.2024 17:41При функциите is_minor, is_major има ли превъртане, както при други функции, или този else, който правя във функцията _minor_major, е излишен?
|
n | n | 1 | from operator import truediv | ||
2 | |||||
1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', | 3 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', | ||
2 | 'E', 'F', 'F#', 'G', | 4 | 'E', 'F', 'F#', 'G', | ||
3 | 'G#', 'A', 'A#', 'B'] | 5 | 'G#', 'A', 'A#', 'B'] | ||
4 | 6 | ||||
5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | 7 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | ||
6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | 8 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | ||
7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 9 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||
8 | 10 | ||||
9 | INTERVAL_LEN = len(CHROMATIC_SCALE) | 11 | INTERVAL_LEN = len(CHROMATIC_SCALE) | ||
10 | MINOR_IDX = 3 | 12 | MINOR_IDX = 3 | ||
11 | MAJOR_IDX = 4 | 13 | MAJOR_IDX = 4 | ||
12 | 14 | ||||
13 | class Tone: | 15 | class Tone: | ||
14 | def __init__(self, name): | 16 | def __init__(self, name): | ||
15 | self.name = name | 17 | self.name = name | ||
16 | 18 | ||||
17 | def __str__(self): | 19 | def __str__(self): | ||
18 | return self.name | 20 | return self.name | ||
19 | 21 | ||||
20 | def __eq__(self, other): | 22 | def __eq__(self, other): | ||
21 | return self.name == other.name | 23 | return self.name == other.name | ||
22 | 24 | ||||
23 | def __add__(self, other): | 25 | def __add__(self, other): | ||
24 | if isinstance(other, Interval): | 26 | if isinstance(other, Interval): | ||
25 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % INTERVAL_LEN | 27 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % INTERVAL_LEN | ||
26 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | 28 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | ||
27 | return Chord(self, other) | 29 | return Chord(self, other) | ||
28 | 30 | ||||
29 | def __sub__(self, other): | 31 | def __sub__(self, other): | ||
30 | first_idx = CHROMATIC_SCALE.index(self.name) | 32 | first_idx = CHROMATIC_SCALE.index(self.name) | ||
31 | if isinstance(other, Interval): | 33 | if isinstance(other, Interval): | ||
32 | second_idx = other.number_of_semitones | 34 | second_idx = other.number_of_semitones | ||
33 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | 35 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | ||
34 | return Tone(CHROMATIC_SCALE[res_idx]) | 36 | return Tone(CHROMATIC_SCALE[res_idx]) | ||
35 | second_idx = CHROMATIC_SCALE.index(other.name) | 37 | second_idx = CHROMATIC_SCALE.index(other.name) | ||
36 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | 38 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | ||
37 | return Interval(res_idx) | 39 | return Interval(res_idx) | ||
38 | 40 | ||||
39 | 41 | ||||
40 | class Interval: | 42 | class Interval: | ||
41 | 43 | ||||
42 | def __init__(self, number_of_semitones): | 44 | def __init__(self, number_of_semitones): | ||
43 | self.number_of_semitones = number_of_semitones | 45 | self.number_of_semitones = number_of_semitones | ||
44 | 46 | ||||
45 | def __str__(self): | 47 | def __str__(self): | ||
46 | return SEMITONES_TYPES[self.number_of_semitones % INTERVAL_LEN] | 48 | return SEMITONES_TYPES[self.number_of_semitones % INTERVAL_LEN] | ||
47 | 49 | ||||
48 | def __add__(self, other): | 50 | def __add__(self, other): | ||
49 | if isinstance(other, Tone): | 51 | if isinstance(other, Tone): | ||
50 | raise TypeError('Invalid operation') | 52 | raise TypeError('Invalid operation') | ||
51 | else: | 53 | else: | ||
52 | semitones = (self.number_of_semitones + other.number_of_semitones) % INTERVAL_LEN | 54 | semitones = (self.number_of_semitones + other.number_of_semitones) % INTERVAL_LEN | ||
53 | return Interval(semitones) | 55 | return Interval(semitones) | ||
54 | 56 | ||||
55 | def __sub__(self, other): | 57 | def __sub__(self, other): | ||
56 | if isinstance(other, Tone): | 58 | if isinstance(other, Tone): | ||
57 | raise TypeError('Invalid operation') | 59 | raise TypeError('Invalid operation') | ||
58 | 60 | ||||
59 | def __neg__(self): | 61 | def __neg__(self): | ||
60 | return Interval(-self.number_of_semitones) | 62 | return Interval(-self.number_of_semitones) | ||
61 | 63 | ||||
62 | 64 | ||||
63 | class Chord: | 65 | class Chord: | ||
64 | def __init__(self, root, *tones): | 66 | def __init__(self, root, *tones): | ||
65 | self.root = root | 67 | self.root = root | ||
66 | self.all_tones = [root, *tones] | 68 | self.all_tones = [root, *tones] | ||
67 | self.uniques = self._unique_tones(self.all_tones) | 69 | self.uniques = self._unique_tones(self.all_tones) | ||
68 | if len(self.uniques) < 2: | 70 | if len(self.uniques) < 2: | ||
69 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
70 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | 72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | ||
71 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | 73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | ||
72 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | 74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | ||
73 | 75 | ||||
74 | @staticmethod | 76 | @staticmethod | ||
75 | def _unique_tones(tones): | 77 | def _unique_tones(tones): | ||
76 | unique_list = [] | 78 | unique_list = [] | ||
77 | for tone in tones: | 79 | for tone in tones: | ||
78 | if tone not in unique_list: | 80 | if tone not in unique_list: | ||
79 | unique_list.append(tone) | 81 | unique_list.append(tone) | ||
80 | return unique_list | 82 | return unique_list | ||
81 | 83 | ||||
82 | def __str__(self): | 84 | def __str__(self): | ||
83 | return '-'.join(map(str, self.sorted_unique_tones)) | 85 | return '-'.join(map(str, self.sorted_unique_tones)) | ||
84 | 86 | ||||
85 | def is_minor(self): | 87 | def is_minor(self): | ||
86 | return self._minor_major(MINOR_IDX) | 88 | return self._minor_major(MINOR_IDX) | ||
87 | 89 | ||||
88 | def is_major(self): | 90 | def is_major(self): | ||
89 | return self._minor_major(MAJOR_IDX) | 91 | return self._minor_major(MAJOR_IDX) | ||
90 | 92 | ||||
91 | def is_power_chord(self): | 93 | def is_power_chord(self): | ||
92 | return not (self.is_minor() or self.is_major()) | 94 | return not (self.is_minor() or self.is_major()) | ||
93 | 95 | ||||
94 | def _minor_major(self, interval): | 96 | def _minor_major(self, interval): | ||
95 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | 97 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | ||
96 | for tone in self.sorted_unique_tones: | 98 | for tone in self.sorted_unique_tones: | ||
97 | curr_tone = CHROMATIC_SCALE.index(tone.name) | 99 | curr_tone = CHROMATIC_SCALE.index(tone.name) | ||
n | 98 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | n | 100 | diff = (curr_tone - index_of_root) |
101 | if diff == interval: | ||||
99 | return True | 102 | return True | ||
n | 100 | elif (INTERVAL_LEN - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | n | 103 | elif diff < 0 and (12 + diff == interval): |
101 | return True | 104 | return True | ||
102 | return False | 105 | return False | ||
103 | 106 | ||||
104 | def __add__(self, other): | 107 | def __add__(self, other): | ||
105 | if isinstance(other, Tone): | 108 | if isinstance(other, Tone): | ||
106 | tones = self.all_tones + [other] | 109 | tones = self.all_tones + [other] | ||
107 | end_tones = tuple(tones) | 110 | end_tones = tuple(tones) | ||
108 | return Chord(self.root, *end_tones) | 111 | return Chord(self.root, *end_tones) | ||
109 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | 112 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | ||
110 | return Chord(self.root, *all_tones) | 113 | return Chord(self.root, *all_tones) | ||
111 | 114 | ||||
112 | def __sub__(self, other): | 115 | def __sub__(self, other): | ||
113 | if isinstance(other, Tone): | 116 | if isinstance(other, Tone): | ||
114 | if other not in self.uniques: | 117 | if other not in self.uniques: | ||
115 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | 118 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | ||
116 | elif len(self.uniques) >= 3: | 119 | elif len(self.uniques) >= 3: | ||
117 | tones = [] | 120 | tones = [] | ||
118 | for tone in self.all_tones: | 121 | for tone in self.all_tones: | ||
119 | if tone != other: | 122 | if tone != other: | ||
120 | tones.append(tone) | 123 | tones.append(tone) | ||
121 | new_root = tones[0] | 124 | new_root = tones[0] | ||
122 | return Chord(new_root, *(tuple(tones))) | 125 | return Chord(new_root, *(tuple(tones))) | ||
123 | else: | 126 | else: | ||
124 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 127 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
125 | 128 | ||||
126 | def transposed(self, interval): | 129 | def transposed(self, interval): | ||
127 | interval_num = interval.number_of_semitones | 130 | interval_num = interval.number_of_semitones | ||
128 | tones = [] | 131 | tones = [] | ||
129 | for tone in self.all_tones: | 132 | for tone in self.all_tones: | ||
130 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % INTERVAL_LEN | 133 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % INTERVAL_LEN | ||
131 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 134 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
132 | new_root = tones[0] | 135 | new_root = tones[0] | ||
133 | return Chord(new_root, *(tuple(tones[1:]))) | 136 | return Chord(new_root, *(tuple(tones[1:]))) | ||
134 | 137 | ||||
135 | 138 | ||||
t | 136 | t | |||
137 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', | f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', |
n | 2 | 'E', 'F', 'F#', 'G', | n | 2 | 'E', 'F', 'F#', 'G', |
3 | 'G#', 'A', 'A#', 'B'] | 3 | 'G#', 'A', 'A#', 'B'] | ||
4 | 4 | ||||
5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | 5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | ||
6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | 6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | ||
7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||
n | n | 8 | |||
9 | INTERVAL_LEN = len(CHROMATIC_SCALE) | ||||
10 | MINOR_IDX = 3 | ||||
11 | MAJOR_IDX = 4 | ||||
8 | 12 | ||||
9 | class Tone: | 13 | class Tone: | ||
10 | def __init__(self, name): | 14 | def __init__(self, name): | ||
11 | self.name = name | 15 | self.name = name | ||
12 | 16 | ||||
13 | def __str__(self): | 17 | def __str__(self): | ||
n | 14 | return f'{self.name}' | n | 18 | return self.name |
15 | 19 | ||||
16 | def __eq__(self, other): | 20 | def __eq__(self, other): | ||
17 | return self.name == other.name | 21 | return self.name == other.name | ||
18 | 22 | ||||
19 | def __add__(self, other): | 23 | def __add__(self, other): | ||
20 | if isinstance(other, Interval): | 24 | if isinstance(other, Interval): | ||
n | 21 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % 12 | n | 25 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % INTERVAL_LEN |
22 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | 26 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | ||
23 | return Chord(self, other) | 27 | return Chord(self, other) | ||
24 | 28 | ||||
25 | def __sub__(self, other): | 29 | def __sub__(self, other): | ||
26 | first_idx = CHROMATIC_SCALE.index(self.name) | 30 | first_idx = CHROMATIC_SCALE.index(self.name) | ||
27 | if isinstance(other, Interval): | 31 | if isinstance(other, Interval): | ||
28 | second_idx = other.number_of_semitones | 32 | second_idx = other.number_of_semitones | ||
n | 29 | if first_idx >= second_idx: | n | ||
30 | res_idx = first_idx - second_idx | 33 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | ||
31 | else: | ||||
32 | res_idx = 12 - abs(first_idx - second_idx) | ||||
33 | return Tone(CHROMATIC_SCALE[res_idx]) | 34 | return Tone(CHROMATIC_SCALE[res_idx]) | ||
34 | second_idx = CHROMATIC_SCALE.index(other.name) | 35 | second_idx = CHROMATIC_SCALE.index(other.name) | ||
n | 35 | if first_idx >= second_idx: | n | ||
36 | res_idx = first_idx - second_idx | 36 | res_idx = (first_idx - second_idx) % INTERVAL_LEN | ||
37 | else: | ||||
38 | res_idx = 12 - abs(first_idx - second_idx) | ||||
39 | return Interval(res_idx) | 37 | return Interval(res_idx) | ||
40 | 38 | ||||
41 | 39 | ||||
42 | class Interval: | 40 | class Interval: | ||
43 | 41 | ||||
44 | def __init__(self, number_of_semitones): | 42 | def __init__(self, number_of_semitones): | ||
45 | self.number_of_semitones = number_of_semitones | 43 | self.number_of_semitones = number_of_semitones | ||
46 | 44 | ||||
47 | def __str__(self): | 45 | def __str__(self): | ||
n | 48 | return f'{SEMITONES_TYPES[self.number_of_semitones % 12]}' | n | 46 | return SEMITONES_TYPES[self.number_of_semitones % INTERVAL_LEN] |
49 | 47 | ||||
50 | def __add__(self, other): | 48 | def __add__(self, other): | ||
51 | if isinstance(other, Tone): | 49 | if isinstance(other, Tone): | ||
52 | raise TypeError('Invalid operation') | 50 | raise TypeError('Invalid operation') | ||
53 | else: | 51 | else: | ||
n | 54 | semitones = (self.number_of_semitones + other.number_of_semitones) % 12 | n | 52 | semitones = (self.number_of_semitones + other.number_of_semitones) % INTERVAL_LEN |
55 | return Interval(semitones) | 53 | return Interval(semitones) | ||
56 | 54 | ||||
57 | def __sub__(self, other): | 55 | def __sub__(self, other): | ||
58 | if isinstance(other, Tone): | 56 | if isinstance(other, Tone): | ||
59 | raise TypeError('Invalid operation') | 57 | raise TypeError('Invalid operation') | ||
60 | 58 | ||||
61 | def __neg__(self): | 59 | def __neg__(self): | ||
62 | return Interval(-self.number_of_semitones) | 60 | return Interval(-self.number_of_semitones) | ||
63 | 61 | ||||
64 | 62 | ||||
65 | class Chord: | 63 | class Chord: | ||
66 | def __init__(self, root, *tones): | 64 | def __init__(self, root, *tones): | ||
67 | self.root = root | 65 | self.root = root | ||
68 | self.all_tones = [root, *tones] | 66 | self.all_tones = [root, *tones] | ||
69 | self.uniques = self._unique_tones(self.all_tones) | 67 | self.uniques = self._unique_tones(self.all_tones) | ||
70 | if len(self.uniques) < 2: | 68 | if len(self.uniques) < 2: | ||
71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 69 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | 70 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | ||
73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | 71 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | ||
74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | 72 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | ||
75 | 73 | ||||
76 | @staticmethod | 74 | @staticmethod | ||
77 | def _unique_tones(tones): | 75 | def _unique_tones(tones): | ||
78 | unique_list = [] | 76 | unique_list = [] | ||
79 | for tone in tones: | 77 | for tone in tones: | ||
80 | if tone not in unique_list: | 78 | if tone not in unique_list: | ||
81 | unique_list.append(tone) | 79 | unique_list.append(tone) | ||
82 | return unique_list | 80 | return unique_list | ||
83 | 81 | ||||
84 | def __str__(self): | 82 | def __str__(self): | ||
n | 85 | self.tones_to_print = '-'.join(str(unique) for unique in self.sorted_unique_tones) | n | 83 | return '-'.join(map(str, self.sorted_unique_tones)) |
86 | return self.tones_to_print | ||||
87 | 84 | ||||
88 | def is_minor(self): | 85 | def is_minor(self): | ||
n | 89 | return self._minor_major(3) | n | 86 | return self._minor_major(MINOR_IDX) |
90 | 87 | ||||
91 | def is_major(self): | 88 | def is_major(self): | ||
n | 92 | return self._minor_major(4) | n | 89 | return self._minor_major(MAJOR_IDX) |
93 | 90 | ||||
94 | def is_power_chord(self): | 91 | def is_power_chord(self): | ||
95 | return not (self.is_minor() or self.is_major()) | 92 | return not (self.is_minor() or self.is_major()) | ||
96 | 93 | ||||
97 | def _minor_major(self, interval): | 94 | def _minor_major(self, interval): | ||
98 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | 95 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | ||
99 | for tone in self.sorted_unique_tones: | 96 | for tone in self.sorted_unique_tones: | ||
100 | curr_tone = CHROMATIC_SCALE.index(tone.name) | 97 | curr_tone = CHROMATIC_SCALE.index(tone.name) | ||
101 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | 98 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | ||
102 | return True | 99 | return True | ||
n | 103 | elif (12 - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | n | 100 | elif (INTERVAL_LEN - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: |
104 | return True | 101 | return True | ||
105 | return False | 102 | return False | ||
106 | 103 | ||||
107 | def __add__(self, other): | 104 | def __add__(self, other): | ||
108 | if isinstance(other, Tone): | 105 | if isinstance(other, Tone): | ||
n | 109 | tones = [] | n | 106 | tones = self.all_tones + [other] |
110 | for tone in self.all_tones: | ||||
111 | tones.append(tone) | ||||
112 | tones.append(other) | ||||
113 | end_tones = tuple(tones) | 107 | end_tones = tuple(tones) | ||
114 | return Chord(self.root, *end_tones) | 108 | return Chord(self.root, *end_tones) | ||
115 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | 109 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | ||
116 | return Chord(self.root, *all_tones) | 110 | return Chord(self.root, *all_tones) | ||
117 | 111 | ||||
118 | def __sub__(self, other): | 112 | def __sub__(self, other): | ||
119 | if isinstance(other, Tone): | 113 | if isinstance(other, Tone): | ||
120 | if other not in self.uniques: | 114 | if other not in self.uniques: | ||
121 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | 115 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | ||
122 | elif len(self.uniques) >= 3: | 116 | elif len(self.uniques) >= 3: | ||
123 | tones = [] | 117 | tones = [] | ||
124 | for tone in self.all_tones: | 118 | for tone in self.all_tones: | ||
n | 125 | if not tone == other: | n | 119 | if tone != other: |
126 | tones.append(tone) | 120 | tones.append(tone) | ||
127 | new_root = tones[0] | 121 | new_root = tones[0] | ||
128 | return Chord(new_root, *(tuple(tones))) | 122 | return Chord(new_root, *(tuple(tones))) | ||
129 | else: | 123 | else: | ||
130 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 124 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
131 | 125 | ||||
132 | def transposed(self, interval): | 126 | def transposed(self, interval): | ||
133 | interval_num = interval.number_of_semitones | 127 | interval_num = interval.number_of_semitones | ||
134 | tones = [] | 128 | tones = [] | ||
n | 135 | if interval_num > 0: | n | ||
136 | for tone in self.all_tones: | 129 | for tone in self.all_tones: | ||
137 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | 130 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % INTERVAL_LEN | ||
138 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 131 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
139 | else: | ||||
140 | for tone in self.all_tones: | ||||
141 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | ||||
142 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||||
143 | new_root = tones[0] | 132 | new_root = tones[0] | ||
144 | return Chord(new_root, *(tuple(tones[1:]))) | 133 | return Chord(new_root, *(tuple(tones[1:]))) | ||
145 | 134 | ||||
146 | 135 | ||||
t | t | 136 | |||
137 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', | f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', |
2 | 'E', 'F', 'F#', 'G', | 2 | 'E', 'F', 'F#', 'G', | ||
3 | 'G#', 'A', 'A#', 'B'] | 3 | 'G#', 'A', 'A#', 'B'] | ||
4 | 4 | ||||
5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | 5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | ||
6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | 6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | ||
7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||
8 | 8 | ||||
9 | class Tone: | 9 | class Tone: | ||
10 | def __init__(self, name): | 10 | def __init__(self, name): | ||
11 | self.name = name | 11 | self.name = name | ||
12 | 12 | ||||
13 | def __str__(self): | 13 | def __str__(self): | ||
14 | return f'{self.name}' | 14 | return f'{self.name}' | ||
15 | 15 | ||||
16 | def __eq__(self, other): | 16 | def __eq__(self, other): | ||
17 | return self.name == other.name | 17 | return self.name == other.name | ||
18 | 18 | ||||
19 | def __add__(self, other): | 19 | def __add__(self, other): | ||
20 | if isinstance(other, Interval): | 20 | if isinstance(other, Interval): | ||
21 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % 12 | 21 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % 12 | ||
22 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | 22 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | ||
23 | return Chord(self, other) | 23 | return Chord(self, other) | ||
24 | 24 | ||||
25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
26 | first_idx = CHROMATIC_SCALE.index(self.name) | 26 | first_idx = CHROMATIC_SCALE.index(self.name) | ||
27 | if isinstance(other, Interval): | 27 | if isinstance(other, Interval): | ||
28 | second_idx = other.number_of_semitones | 28 | second_idx = other.number_of_semitones | ||
29 | if first_idx >= second_idx: | 29 | if first_idx >= second_idx: | ||
30 | res_idx = first_idx - second_idx | 30 | res_idx = first_idx - second_idx | ||
31 | else: | 31 | else: | ||
32 | res_idx = 12 - abs(first_idx - second_idx) | 32 | res_idx = 12 - abs(first_idx - second_idx) | ||
33 | return Tone(CHROMATIC_SCALE[res_idx]) | 33 | return Tone(CHROMATIC_SCALE[res_idx]) | ||
34 | second_idx = CHROMATIC_SCALE.index(other.name) | 34 | second_idx = CHROMATIC_SCALE.index(other.name) | ||
35 | if first_idx >= second_idx: | 35 | if first_idx >= second_idx: | ||
36 | res_idx = first_idx - second_idx | 36 | res_idx = first_idx - second_idx | ||
37 | else: | 37 | else: | ||
38 | res_idx = 12 - abs(first_idx - second_idx) | 38 | res_idx = 12 - abs(first_idx - second_idx) | ||
39 | return Interval(res_idx) | 39 | return Interval(res_idx) | ||
40 | 40 | ||||
41 | 41 | ||||
42 | class Interval: | 42 | class Interval: | ||
43 | 43 | ||||
44 | def __init__(self, number_of_semitones): | 44 | def __init__(self, number_of_semitones): | ||
45 | self.number_of_semitones = number_of_semitones | 45 | self.number_of_semitones = number_of_semitones | ||
46 | 46 | ||||
47 | def __str__(self): | 47 | def __str__(self): | ||
48 | return f'{SEMITONES_TYPES[self.number_of_semitones % 12]}' | 48 | return f'{SEMITONES_TYPES[self.number_of_semitones % 12]}' | ||
49 | 49 | ||||
50 | def __add__(self, other): | 50 | def __add__(self, other): | ||
51 | if isinstance(other, Tone): | 51 | if isinstance(other, Tone): | ||
52 | raise TypeError('Invalid operation') | 52 | raise TypeError('Invalid operation') | ||
53 | else: | 53 | else: | ||
54 | semitones = (self.number_of_semitones + other.number_of_semitones) % 12 | 54 | semitones = (self.number_of_semitones + other.number_of_semitones) % 12 | ||
55 | return Interval(semitones) | 55 | return Interval(semitones) | ||
56 | 56 | ||||
57 | def __sub__(self, other): | 57 | def __sub__(self, other): | ||
58 | if isinstance(other, Tone): | 58 | if isinstance(other, Tone): | ||
59 | raise TypeError('Invalid operation') | 59 | raise TypeError('Invalid operation') | ||
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self.number_of_semitones) | 62 | return Interval(-self.number_of_semitones) | ||
63 | 63 | ||||
64 | 64 | ||||
65 | class Chord: | 65 | class Chord: | ||
66 | def __init__(self, root, *tones): | 66 | def __init__(self, root, *tones): | ||
67 | self.root = root | 67 | self.root = root | ||
68 | self.all_tones = [root, *tones] | 68 | self.all_tones = [root, *tones] | ||
69 | self.uniques = self._unique_tones(self.all_tones) | 69 | self.uniques = self._unique_tones(self.all_tones) | ||
70 | if len(self.uniques) < 2: | 70 | if len(self.uniques) < 2: | ||
71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | 72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | ||
73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | 73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | ||
74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | 74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | ||
75 | 75 | ||||
76 | @staticmethod | 76 | @staticmethod | ||
77 | def _unique_tones(tones): | 77 | def _unique_tones(tones): | ||
78 | unique_list = [] | 78 | unique_list = [] | ||
79 | for tone in tones: | 79 | for tone in tones: | ||
80 | if tone not in unique_list: | 80 | if tone not in unique_list: | ||
81 | unique_list.append(tone) | 81 | unique_list.append(tone) | ||
82 | return unique_list | 82 | return unique_list | ||
83 | 83 | ||||
84 | def __str__(self): | 84 | def __str__(self): | ||
85 | self.tones_to_print = '-'.join(str(unique) for unique in self.sorted_unique_tones) | 85 | self.tones_to_print = '-'.join(str(unique) for unique in self.sorted_unique_tones) | ||
86 | return self.tones_to_print | 86 | return self.tones_to_print | ||
87 | 87 | ||||
88 | def is_minor(self): | 88 | def is_minor(self): | ||
89 | return self._minor_major(3) | 89 | return self._minor_major(3) | ||
90 | 90 | ||||
91 | def is_major(self): | 91 | def is_major(self): | ||
92 | return self._minor_major(4) | 92 | return self._minor_major(4) | ||
93 | 93 | ||||
94 | def is_power_chord(self): | 94 | def is_power_chord(self): | ||
95 | return not (self.is_minor() or self.is_major()) | 95 | return not (self.is_minor() or self.is_major()) | ||
96 | 96 | ||||
97 | def _minor_major(self, interval): | 97 | def _minor_major(self, interval): | ||
98 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | 98 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | ||
99 | for tone in self.sorted_unique_tones: | 99 | for tone in self.sorted_unique_tones: | ||
100 | curr_tone = CHROMATIC_SCALE.index(tone.name) | 100 | curr_tone = CHROMATIC_SCALE.index(tone.name) | ||
101 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | 101 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | ||
102 | return True | 102 | return True | ||
103 | elif (12 - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | 103 | elif (12 - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | ||
104 | return True | 104 | return True | ||
105 | return False | 105 | return False | ||
106 | 106 | ||||
107 | def __add__(self, other): | 107 | def __add__(self, other): | ||
108 | if isinstance(other, Tone): | 108 | if isinstance(other, Tone): | ||
109 | tones = [] | 109 | tones = [] | ||
110 | for tone in self.all_tones: | 110 | for tone in self.all_tones: | ||
111 | tones.append(tone) | 111 | tones.append(tone) | ||
112 | tones.append(other) | 112 | tones.append(other) | ||
113 | end_tones = tuple(tones) | 113 | end_tones = tuple(tones) | ||
114 | return Chord(self.root, *end_tones) | 114 | return Chord(self.root, *end_tones) | ||
115 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | 115 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | ||
116 | return Chord(self.root, *all_tones) | 116 | return Chord(self.root, *all_tones) | ||
117 | 117 | ||||
118 | def __sub__(self, other): | 118 | def __sub__(self, other): | ||
119 | if isinstance(other, Tone): | 119 | if isinstance(other, Tone): | ||
120 | if other not in self.uniques: | 120 | if other not in self.uniques: | ||
121 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | 121 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | ||
122 | elif len(self.uniques) >= 3: | 122 | elif len(self.uniques) >= 3: | ||
123 | tones = [] | 123 | tones = [] | ||
124 | for tone in self.all_tones: | 124 | for tone in self.all_tones: | ||
125 | if not tone == other: | 125 | if not tone == other: | ||
126 | tones.append(tone) | 126 | tones.append(tone) | ||
127 | new_root = tones[0] | 127 | new_root = tones[0] | ||
128 | return Chord(new_root, *(tuple(tones))) | 128 | return Chord(new_root, *(tuple(tones))) | ||
129 | else: | 129 | else: | ||
130 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 130 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
131 | 131 | ||||
132 | def transposed(self, interval): | 132 | def transposed(self, interval): | ||
133 | interval_num = interval.number_of_semitones | 133 | interval_num = interval.number_of_semitones | ||
134 | tones = [] | 134 | tones = [] | ||
135 | if interval_num > 0: | 135 | if interval_num > 0: | ||
136 | for tone in self.all_tones: | 136 | for tone in self.all_tones: | ||
137 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | 137 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | ||
138 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 138 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
139 | else: | 139 | else: | ||
140 | for tone in self.all_tones: | 140 | for tone in self.all_tones: | ||
141 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | 141 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | ||
142 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 142 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
143 | new_root = tones[0] | 143 | new_root = tones[0] | ||
144 | return Chord(new_root, *(tuple(tones[1:]))) | 144 | return Chord(new_root, *(tuple(tones[1:]))) | ||
145 | 145 | ||||
146 | 146 | ||||
t | 147 | c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G")) | t | ||
148 | result_chord = c_major_chord - Tone("C") | ||||
149 | print(result_chord) | ||||
150 | # C-G |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', | f | 1 | CHROMATIC_SCALE = ['C', 'C#', 'D', 'D#', |
2 | 'E', 'F', 'F#', 'G', | 2 | 'E', 'F', 'F#', 'G', | ||
3 | 'G#', 'A', 'A#', 'B'] | 3 | 'G#', 'A', 'A#', 'B'] | ||
4 | 4 | ||||
5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | 5 | SEMITONES_TYPES = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', | ||
6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | 6 | 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', | ||
7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 7 | 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||
8 | 8 | ||||
9 | class Tone: | 9 | class Tone: | ||
10 | def __init__(self, name): | 10 | def __init__(self, name): | ||
11 | self.name = name | 11 | self.name = name | ||
12 | 12 | ||||
13 | def __str__(self): | 13 | def __str__(self): | ||
14 | return f'{self.name}' | 14 | return f'{self.name}' | ||
15 | 15 | ||||
16 | def __eq__(self, other): | 16 | def __eq__(self, other): | ||
17 | return self.name == other.name | 17 | return self.name == other.name | ||
18 | 18 | ||||
19 | def __add__(self, other): | 19 | def __add__(self, other): | ||
20 | if isinstance(other, Interval): | 20 | if isinstance(other, Interval): | ||
21 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % 12 | 21 | new_tone_idx = (CHROMATIC_SCALE.index(self.name) + other.number_of_semitones) % 12 | ||
22 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | 22 | return Tone(CHROMATIC_SCALE[new_tone_idx]) | ||
23 | return Chord(self, other) | 23 | return Chord(self, other) | ||
24 | 24 | ||||
25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
26 | first_idx = CHROMATIC_SCALE.index(self.name) | 26 | first_idx = CHROMATIC_SCALE.index(self.name) | ||
27 | if isinstance(other, Interval): | 27 | if isinstance(other, Interval): | ||
28 | second_idx = other.number_of_semitones | 28 | second_idx = other.number_of_semitones | ||
29 | if first_idx >= second_idx: | 29 | if first_idx >= second_idx: | ||
30 | res_idx = first_idx - second_idx | 30 | res_idx = first_idx - second_idx | ||
31 | else: | 31 | else: | ||
32 | res_idx = 12 - abs(first_idx - second_idx) | 32 | res_idx = 12 - abs(first_idx - second_idx) | ||
33 | return Tone(CHROMATIC_SCALE[res_idx]) | 33 | return Tone(CHROMATIC_SCALE[res_idx]) | ||
34 | second_idx = CHROMATIC_SCALE.index(other.name) | 34 | second_idx = CHROMATIC_SCALE.index(other.name) | ||
35 | if first_idx >= second_idx: | 35 | if first_idx >= second_idx: | ||
36 | res_idx = first_idx - second_idx | 36 | res_idx = first_idx - second_idx | ||
37 | else: | 37 | else: | ||
38 | res_idx = 12 - abs(first_idx - second_idx) | 38 | res_idx = 12 - abs(first_idx - second_idx) | ||
39 | return Interval(res_idx) | 39 | return Interval(res_idx) | ||
40 | 40 | ||||
41 | 41 | ||||
42 | class Interval: | 42 | class Interval: | ||
43 | 43 | ||||
44 | def __init__(self, number_of_semitones): | 44 | def __init__(self, number_of_semitones): | ||
45 | self.number_of_semitones = number_of_semitones | 45 | self.number_of_semitones = number_of_semitones | ||
46 | 46 | ||||
47 | def __str__(self): | 47 | def __str__(self): | ||
48 | return f'{SEMITONES_TYPES[self.number_of_semitones % 12]}' | 48 | return f'{SEMITONES_TYPES[self.number_of_semitones % 12]}' | ||
49 | 49 | ||||
50 | def __add__(self, other): | 50 | def __add__(self, other): | ||
51 | if isinstance(other, Tone): | 51 | if isinstance(other, Tone): | ||
52 | raise TypeError('Invalid operation') | 52 | raise TypeError('Invalid operation') | ||
53 | else: | 53 | else: | ||
54 | semitones = (self.number_of_semitones + other.number_of_semitones) % 12 | 54 | semitones = (self.number_of_semitones + other.number_of_semitones) % 12 | ||
55 | return Interval(semitones) | 55 | return Interval(semitones) | ||
56 | 56 | ||||
57 | def __sub__(self, other): | 57 | def __sub__(self, other): | ||
58 | if isinstance(other, Tone): | 58 | if isinstance(other, Tone): | ||
59 | raise TypeError('Invalid operation') | 59 | raise TypeError('Invalid operation') | ||
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self.number_of_semitones) | 62 | return Interval(-self.number_of_semitones) | ||
63 | 63 | ||||
64 | 64 | ||||
65 | class Chord: | 65 | class Chord: | ||
66 | def __init__(self, root, *tones): | 66 | def __init__(self, root, *tones): | ||
67 | self.root = root | 67 | self.root = root | ||
68 | self.all_tones = [root, *tones] | 68 | self.all_tones = [root, *tones] | ||
69 | self.uniques = self._unique_tones(self.all_tones) | 69 | self.uniques = self._unique_tones(self.all_tones) | ||
70 | if len(self.uniques) < 2: | 70 | if len(self.uniques) < 2: | ||
71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | 72 | #calculates how far is every unique tone from the root and sorts(increase) by this parameter | ||
73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | 73 | self.sorted_unique_tones = sorted(self.uniques, key=lambda tone: | ||
74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | 74 | (CHROMATIC_SCALE.index(tone.name) - CHROMATIC_SCALE.index(root.name)) % len(CHROMATIC_SCALE)) | ||
75 | 75 | ||||
76 | @staticmethod | 76 | @staticmethod | ||
77 | def _unique_tones(tones): | 77 | def _unique_tones(tones): | ||
78 | unique_list = [] | 78 | unique_list = [] | ||
79 | for tone in tones: | 79 | for tone in tones: | ||
80 | if tone not in unique_list: | 80 | if tone not in unique_list: | ||
81 | unique_list.append(tone) | 81 | unique_list.append(tone) | ||
82 | return unique_list | 82 | return unique_list | ||
83 | 83 | ||||
84 | def __str__(self): | 84 | def __str__(self): | ||
85 | self.tones_to_print = '-'.join(str(unique) for unique in self.sorted_unique_tones) | 85 | self.tones_to_print = '-'.join(str(unique) for unique in self.sorted_unique_tones) | ||
86 | return self.tones_to_print | 86 | return self.tones_to_print | ||
87 | 87 | ||||
88 | def is_minor(self): | 88 | def is_minor(self): | ||
89 | return self._minor_major(3) | 89 | return self._minor_major(3) | ||
90 | 90 | ||||
91 | def is_major(self): | 91 | def is_major(self): | ||
92 | return self._minor_major(4) | 92 | return self._minor_major(4) | ||
93 | 93 | ||||
94 | def is_power_chord(self): | 94 | def is_power_chord(self): | ||
95 | return not (self.is_minor() or self.is_major()) | 95 | return not (self.is_minor() or self.is_major()) | ||
96 | 96 | ||||
97 | def _minor_major(self, interval): | 97 | def _minor_major(self, interval): | ||
98 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | 98 | index_of_root = CHROMATIC_SCALE.index(self.root.name) | ||
99 | for tone in self.sorted_unique_tones: | 99 | for tone in self.sorted_unique_tones: | ||
100 | curr_tone = CHROMATIC_SCALE.index(tone.name) | 100 | curr_tone = CHROMATIC_SCALE.index(tone.name) | ||
101 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | 101 | if abs(curr_tone - index_of_root) % len(CHROMATIC_SCALE) == interval: | ||
102 | return True | 102 | return True | ||
103 | elif (12 - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | 103 | elif (12 - abs(curr_tone - index_of_root)) % len(CHROMATIC_SCALE) == interval: | ||
104 | return True | 104 | return True | ||
105 | return False | 105 | return False | ||
106 | 106 | ||||
107 | def __add__(self, other): | 107 | def __add__(self, other): | ||
108 | if isinstance(other, Tone): | 108 | if isinstance(other, Tone): | ||
109 | tones = [] | 109 | tones = [] | ||
110 | for tone in self.all_tones: | 110 | for tone in self.all_tones: | ||
111 | tones.append(tone) | 111 | tones.append(tone) | ||
112 | tones.append(other) | 112 | tones.append(other) | ||
113 | end_tones = tuple(tones) | 113 | end_tones = tuple(tones) | ||
114 | return Chord(self.root, *end_tones) | 114 | return Chord(self.root, *end_tones) | ||
115 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | 115 | all_tones = tuple(self.all_tones) + tuple(other.all_tones) | ||
116 | return Chord(self.root, *all_tones) | 116 | return Chord(self.root, *all_tones) | ||
117 | 117 | ||||
118 | def __sub__(self, other): | 118 | def __sub__(self, other): | ||
119 | if isinstance(other, Tone): | 119 | if isinstance(other, Tone): | ||
120 | if other not in self.uniques: | 120 | if other not in self.uniques: | ||
121 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | 121 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | ||
122 | elif len(self.uniques) >= 3: | 122 | elif len(self.uniques) >= 3: | ||
123 | tones = [] | 123 | tones = [] | ||
124 | for tone in self.all_tones: | 124 | for tone in self.all_tones: | ||
125 | if not tone == other: | 125 | if not tone == other: | ||
126 | tones.append(tone) | 126 | tones.append(tone) | ||
n | n | 127 | new_root = tones[0] | ||
127 | return Chord(self.root, *(tuple(tones))) | 128 | return Chord(new_root, *(tuple(tones))) | ||
128 | else: | 129 | else: | ||
129 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 130 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
130 | 131 | ||||
131 | def transposed(self, interval): | 132 | def transposed(self, interval): | ||
132 | interval_num = interval.number_of_semitones | 133 | interval_num = interval.number_of_semitones | ||
133 | tones = [] | 134 | tones = [] | ||
134 | if interval_num > 0: | 135 | if interval_num > 0: | ||
135 | for tone in self.all_tones: | 136 | for tone in self.all_tones: | ||
136 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | 137 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | ||
137 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 138 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
138 | else: | 139 | else: | ||
139 | for tone in self.all_tones: | 140 | for tone in self.all_tones: | ||
140 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | 141 | curr_idx = (CHROMATIC_SCALE.index(tone.name) + interval_num) % 12 | ||
141 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | 142 | tones.append(Tone(CHROMATIC_SCALE[curr_idx])) | ||
142 | new_root = tones[0] | 143 | new_root = tones[0] | ||
143 | return Chord(new_root, *(tuple(tones[1:]))) | 144 | return Chord(new_root, *(tuple(tones[1:]))) | ||
144 | 145 | ||||
145 | 146 | ||||
t | t | 147 | c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G")) | ||
148 | result_chord = c_major_chord - Tone("C") | ||||
149 | print(result_chord) | ||||
150 | # C-G |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|