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