1from collections import OrderedDict
2
3class Tone:
4 TONES_ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
5 TONES_TOTAL = len(TONES_ORDER)
6
7 def __init__(self, name):
8 if name not in Tone.TONES_ORDER:
9 raise Exception(f'{name} is not a valid tone!')
10 self.name = name
11
12 def __str__(self):
13 return self.name
14
15 def __eq__(self, other):
16 if not isinstance(other, Tone):
17
18 return False
19 return self.name == other.name
20
21 def __add__(self, other):
22 if isinstance(other, Tone):
23 return Chord(self, other)
24 elif isinstance(other, Interval):
25 current_tone_idx = Tone.TONES_ORDER.index(self.name)
26 new_tone_idx = (current_tone_idx + other.num_semitones) % Tone.TONES_TOTAL
27
28 new_tone = Tone.TONES_ORDER[new_tone_idx]
29 return Tone(new_tone)
30
31 def __sub__(self, other):
32 if isinstance(other, Tone):
33 self_tone_idx = Tone.TONES_ORDER.index(self.name)
34 other_tone_idx = Tone.TONES_ORDER.index(other.name)
35
36 num_semitones = (self_tone_idx - other_tone_idx) % Tone.TONES_TOTAL
37 return Interval(num_semitones)
38 elif isinstance(other, Interval):
39 return self + (-other)
40
41 def __hash__(self):
42 return hash(self.name)
43
44
45class Interval:
46 INTERVAL_NAMES = (
47 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th',
48 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th'
49 )
50 def __init__(self, num_semitones):
51 if not isinstance(num_semitones, int):
52 raise TypeError('Number of semitones must be an integer! :)')
53 self.num_semitones = num_semitones % Tone.TONES_TOTAL
54
55 def __str__(self):
56 return Interval.INTERVAL_NAMES[self.num_semitones]
57
58 def __add__(self, other):
59 if isinstance(other, Interval):
60 new_interval = self.num_semitones + other.num_semitones
61 return Interval(new_interval)
62 raise TypeError ('Invalid operation')
63
64 def __sub__(self, other):
65 if isinstance(other, Tone):
66 raise TypeError ('Invalid operation')
67
68 def __neg__(self):
69 return Interval(-self.num_semitones)
70
71
72class Chord:
73 def __init__(self, *args):
74 if any(not isinstance(t, Tone) for t in args):
75 raise TypeError('One or more arguments are not of type Tone!')
76
77 ordered_tones_set = OrderedDict.fromkeys(args)
78
79 self.distinct_tones = list(ordered_tones_set.keys())
80
81 if len(self.distinct_tones) == 1:
82 raise TypeError("Cannot have a chord made of only 1 unique tone")
83
84 def __str__(self):
85 root_tone = self.distinct_tones[0]
86 root_tone_idx = Tone.TONES_ORDER.index(root_tone.name)
87
88 final_order_tones = []
89
90 for i in range(Tone.TONES_TOTAL):
91 current_tone_idx = (i + root_tone_idx) % Tone.TONES_TOTAL
92 current_tone = Tone.TONES_ORDER[current_tone_idx]
93 if Tone(current_tone) in self.distinct_tones:
94 final_order_tones.append(Tone.TONES_ORDER[current_tone_idx])
95 return '-'.join(final_order_tones)
96
97 def is_minor(self):
98 root_tone = self.distinct_tones[0]
99 root_tone_idx = Tone.TONES_ORDER.index(root_tone.name)
100
101 minor_3rd_idx = (root_tone_idx + 3) % Tone.TONES_TOTAL
102 minor_3rd_tone = Tone(Tone.TONES_ORDER[minor_3rd_idx])
103
104 return minor_3rd_tone in self.distinct_tones
105
106 def is_major(self):
107 root_tone = self.distinct_tones[0]
108 root_tone_idx = Tone.TONES_ORDER.index(root_tone.name)
109
110 major_3rd_idx = (root_tone_idx + 4) % Tone.TONES_TOTAL
111 major_3rd_tone = Tone(Tone.TONES_ORDER[major_3rd_idx])
112
113 return major_3rd_tone in self.distinct_tones
114
115 def is_power_chord(self):
116 return not self.is_minor() and not self.is_major()
117
118 def __add__(self, other):
119 if isinstance(other, Tone):
120 if other in self.distinct_tones:
121 return self
122
123 new_distinct_tones = self.distinct_tones[:]
124 new_distinct_tones.append(other)
125 return Chord(*new_distinct_tones)
126
127 elif isinstance(other, Chord):
128 combined_tones = self.distinct_tones + other.distinct_tones
129 return Chord(*combined_tones)
130
131 def __sub__(self, other):
132 if isinstance(other, Tone):
133 if other not in self.distinct_tones:
134 raise TypeError(f'Cannot remove tone {other.name} from chord {self}')
135
136 new_distinct_tones = self.distinct_tones[:]
137 new_distinct_tones.remove(other)
138 return Chord(*new_distinct_tones)
139
140 def transposed(self, interval):
141 if isinstance(interval, Interval):
142 transposed_tones = [tone + interval for tone in self.distinct_tones]
143
144 return Chord(*transposed_tones)
145
.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.001s
OK
Мая Панова
06.11.2024 16:10Разбрах, благодаря :)
|
Виктор Бечев
06.11.2024 15:42Останалите грешки не са проблем, имах предвид съобщенията на тези, които ни вълнуват.
И за двете няма да ти дам повече, защото ще е еквивалентно на това да ти кажа къде имаш проблеми в кода.
Споменах колкото споменах, защото не ми се ще в стремежа си да имаш по-хубаво решение да получиш някоя друга точка по-малко.
|
Мая Панова
06.11.2024 15:35Изпуснала съм да променя transposed метода..видях
|
Мая Панова
06.11.2024 14:46Аз онази грешка я написах просто, защото ми е странно да проверявам няколко различни isinstance, но да не raise-вам грешки, ако стане нещо друго.. въпреки че сте казали, че ще тествате само за грешките, които изрично сте опоменали. Та въпросът ми е останалите грешки, които raise-вам да ги махна ли? Също...да това променя решението вече можем да работим с tone, вместо със str(tone), но тези words of caution са защото все пак съм объркала нещо ли? :D
|
Виктор Бечев
06.11.2024 13:49Word of caution - помисли дали тази промяна не е афектирала определени парчета функционалност.
Second word of caution - провери си внимателно стринговете на грешките дали спазват условието (включително този, на който се бях засмял).
|
Мая Панова
06.11.2024 00:11предефинирах __eq__ и вече сравняваме обекти а не str репрезентации
|
Виктор Бечев
05.11.2024 15:59Единствената ми генерална забележка е по отношение на решението да пазиш `str()` версиите на тоновете, вместо самите обекти.
В случая работи, защото тоновете не пазят кой знае каква допълнителна информация. Но го имай предвид за по-комплексни обекти и взаимовръзки между тях - понякога може да ти трябва да пазиш самите обекти и техния стейт в момента на подаването им на конструктора.
Просто за тази задача са достатъчно прости, че можеш да си позволиш да пазиш стринговата им репрезентация и на базата на нея да ги пре-създаваш когато ти трябват.
|
f | 1 | from collections import OrderedDict | f | 1 | from collections import OrderedDict |
2 | 2 | ||||
3 | class Tone: | 3 | class Tone: | ||
4 | TONES_ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 4 | TONES_ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
5 | TONES_TOTAL = len(TONES_ORDER) | 5 | TONES_TOTAL = len(TONES_ORDER) | ||
6 | 6 | ||||
7 | def __init__(self, name): | 7 | def __init__(self, name): | ||
8 | if name not in Tone.TONES_ORDER: | 8 | if name not in Tone.TONES_ORDER: | ||
9 | raise Exception(f'{name} is not a valid tone!') | 9 | raise Exception(f'{name} is not a valid tone!') | ||
10 | self.name = name | 10 | self.name = name | ||
11 | 11 | ||||
12 | def __str__(self): | 12 | def __str__(self): | ||
13 | return self.name | 13 | return self.name | ||
14 | 14 | ||||
15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
16 | if not isinstance(other, Tone): | 16 | if not isinstance(other, Tone): | ||
n | n | 17 | |||
17 | return False | 18 | return False | ||
18 | return self.name == other.name | 19 | return self.name == other.name | ||
19 | 20 | ||||
20 | def __add__(self, other): | 21 | def __add__(self, other): | ||
21 | if isinstance(other, Tone): | 22 | if isinstance(other, Tone): | ||
22 | return Chord(self, other) | 23 | return Chord(self, other) | ||
23 | elif isinstance(other, Interval): | 24 | elif isinstance(other, Interval): | ||
24 | current_tone_idx = Tone.TONES_ORDER.index(self.name) | 25 | current_tone_idx = Tone.TONES_ORDER.index(self.name) | ||
25 | new_tone_idx = (current_tone_idx + other.num_semitones) % Tone.TONES_TOTAL | 26 | new_tone_idx = (current_tone_idx + other.num_semitones) % Tone.TONES_TOTAL | ||
26 | 27 | ||||
27 | new_tone = Tone.TONES_ORDER[new_tone_idx] | 28 | new_tone = Tone.TONES_ORDER[new_tone_idx] | ||
28 | return Tone(new_tone) | 29 | return Tone(new_tone) | ||
29 | 30 | ||||
30 | def __sub__(self, other): | 31 | def __sub__(self, other): | ||
31 | if isinstance(other, Tone): | 32 | if isinstance(other, Tone): | ||
32 | self_tone_idx = Tone.TONES_ORDER.index(self.name) | 33 | self_tone_idx = Tone.TONES_ORDER.index(self.name) | ||
33 | other_tone_idx = Tone.TONES_ORDER.index(other.name) | 34 | other_tone_idx = Tone.TONES_ORDER.index(other.name) | ||
34 | 35 | ||||
35 | num_semitones = (self_tone_idx - other_tone_idx) % Tone.TONES_TOTAL | 36 | num_semitones = (self_tone_idx - other_tone_idx) % Tone.TONES_TOTAL | ||
36 | return Interval(num_semitones) | 37 | return Interval(num_semitones) | ||
37 | elif isinstance(other, Interval): | 38 | elif isinstance(other, Interval): | ||
38 | return self + (-other) | 39 | return self + (-other) | ||
39 | 40 | ||||
40 | def __hash__(self): | 41 | def __hash__(self): | ||
41 | return hash(self.name) | 42 | return hash(self.name) | ||
42 | 43 | ||||
43 | 44 | ||||
44 | class Interval: | 45 | class Interval: | ||
45 | INTERVAL_NAMES = ( | 46 | INTERVAL_NAMES = ( | ||
46 | 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', | 47 | 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', | ||
47 | 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th' | 48 | 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th' | ||
48 | ) | 49 | ) | ||
49 | def __init__(self, num_semitones): | 50 | def __init__(self, num_semitones): | ||
50 | if not isinstance(num_semitones, int): | 51 | if not isinstance(num_semitones, int): | ||
51 | raise TypeError('Number of semitones must be an integer! :)') | 52 | raise TypeError('Number of semitones must be an integer! :)') | ||
52 | self.num_semitones = num_semitones % Tone.TONES_TOTAL | 53 | self.num_semitones = num_semitones % Tone.TONES_TOTAL | ||
53 | 54 | ||||
54 | def __str__(self): | 55 | def __str__(self): | ||
55 | return Interval.INTERVAL_NAMES[self.num_semitones] | 56 | return Interval.INTERVAL_NAMES[self.num_semitones] | ||
56 | 57 | ||||
57 | def __add__(self, other): | 58 | def __add__(self, other): | ||
58 | if isinstance(other, Interval): | 59 | if isinstance(other, Interval): | ||
59 | new_interval = self.num_semitones + other.num_semitones | 60 | new_interval = self.num_semitones + other.num_semitones | ||
60 | return Interval(new_interval) | 61 | return Interval(new_interval) | ||
n | 61 | raise TypeError ('nqkva greshka, ne znam veche...') | n | 62 | raise TypeError ('Invalid operation') |
62 | 63 | ||||
63 | def __sub__(self, other): | 64 | def __sub__(self, other): | ||
64 | if isinstance(other, Tone): | 65 | if isinstance(other, Tone): | ||
n | 65 | raise TypeError ('Invalid operation.') | n | 66 | raise TypeError ('Invalid operation') |
66 | 67 | ||||
67 | def __neg__(self): | 68 | def __neg__(self): | ||
68 | return Interval(-self.num_semitones) | 69 | return Interval(-self.num_semitones) | ||
69 | 70 | ||||
70 | 71 | ||||
71 | class Chord: | 72 | class Chord: | ||
72 | def __init__(self, *args): | 73 | def __init__(self, *args): | ||
73 | if any(not isinstance(t, Tone) for t in args): | 74 | if any(not isinstance(t, Tone) for t in args): | ||
74 | raise TypeError('One or more arguments are not of type Tone!') | 75 | raise TypeError('One or more arguments are not of type Tone!') | ||
75 | 76 | ||||
76 | ordered_tones_set = OrderedDict.fromkeys(args) | 77 | ordered_tones_set = OrderedDict.fromkeys(args) | ||
77 | 78 | ||||
78 | self.distinct_tones = list(ordered_tones_set.keys()) | 79 | self.distinct_tones = list(ordered_tones_set.keys()) | ||
79 | 80 | ||||
80 | if len(self.distinct_tones) == 1: | 81 | if len(self.distinct_tones) == 1: | ||
81 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 82 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
82 | 83 | ||||
83 | def __str__(self): | 84 | def __str__(self): | ||
84 | root_tone = self.distinct_tones[0] | 85 | root_tone = self.distinct_tones[0] | ||
85 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | 86 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | ||
86 | 87 | ||||
87 | final_order_tones = [] | 88 | final_order_tones = [] | ||
88 | 89 | ||||
89 | for i in range(Tone.TONES_TOTAL): | 90 | for i in range(Tone.TONES_TOTAL): | ||
90 | current_tone_idx = (i + root_tone_idx) % Tone.TONES_TOTAL | 91 | current_tone_idx = (i + root_tone_idx) % Tone.TONES_TOTAL | ||
91 | current_tone = Tone.TONES_ORDER[current_tone_idx] | 92 | current_tone = Tone.TONES_ORDER[current_tone_idx] | ||
92 | if Tone(current_tone) in self.distinct_tones: | 93 | if Tone(current_tone) in self.distinct_tones: | ||
93 | final_order_tones.append(Tone.TONES_ORDER[current_tone_idx]) | 94 | final_order_tones.append(Tone.TONES_ORDER[current_tone_idx]) | ||
94 | return '-'.join(final_order_tones) | 95 | return '-'.join(final_order_tones) | ||
95 | 96 | ||||
96 | def is_minor(self): | 97 | def is_minor(self): | ||
97 | root_tone = self.distinct_tones[0] | 98 | root_tone = self.distinct_tones[0] | ||
98 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | 99 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | ||
99 | 100 | ||||
100 | minor_3rd_idx = (root_tone_idx + 3) % Tone.TONES_TOTAL | 101 | minor_3rd_idx = (root_tone_idx + 3) % Tone.TONES_TOTAL | ||
101 | minor_3rd_tone = Tone(Tone.TONES_ORDER[minor_3rd_idx]) | 102 | minor_3rd_tone = Tone(Tone.TONES_ORDER[minor_3rd_idx]) | ||
102 | 103 | ||||
103 | return minor_3rd_tone in self.distinct_tones | 104 | return minor_3rd_tone in self.distinct_tones | ||
104 | 105 | ||||
105 | def is_major(self): | 106 | def is_major(self): | ||
106 | root_tone = self.distinct_tones[0] | 107 | root_tone = self.distinct_tones[0] | ||
107 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | 108 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) | ||
108 | 109 | ||||
109 | major_3rd_idx = (root_tone_idx + 4) % Tone.TONES_TOTAL | 110 | major_3rd_idx = (root_tone_idx + 4) % Tone.TONES_TOTAL | ||
110 | major_3rd_tone = Tone(Tone.TONES_ORDER[major_3rd_idx]) | 111 | major_3rd_tone = Tone(Tone.TONES_ORDER[major_3rd_idx]) | ||
111 | 112 | ||||
112 | return major_3rd_tone in self.distinct_tones | 113 | return major_3rd_tone in self.distinct_tones | ||
113 | 114 | ||||
114 | def is_power_chord(self): | 115 | def is_power_chord(self): | ||
115 | return not self.is_minor() and not self.is_major() | 116 | return not self.is_minor() and not self.is_major() | ||
116 | 117 | ||||
117 | def __add__(self, other): | 118 | def __add__(self, other): | ||
118 | if isinstance(other, Tone): | 119 | if isinstance(other, Tone): | ||
119 | if other in self.distinct_tones: | 120 | if other in self.distinct_tones: | ||
120 | return self | 121 | return self | ||
121 | 122 | ||||
122 | new_distinct_tones = self.distinct_tones[:] | 123 | new_distinct_tones = self.distinct_tones[:] | ||
123 | new_distinct_tones.append(other) | 124 | new_distinct_tones.append(other) | ||
124 | return Chord(*new_distinct_tones) | 125 | return Chord(*new_distinct_tones) | ||
125 | 126 | ||||
126 | elif isinstance(other, Chord): | 127 | elif isinstance(other, Chord): | ||
127 | combined_tones = self.distinct_tones + other.distinct_tones | 128 | combined_tones = self.distinct_tones + other.distinct_tones | ||
128 | return Chord(*combined_tones) | 129 | return Chord(*combined_tones) | ||
129 | 130 | ||||
130 | def __sub__(self, other): | 131 | def __sub__(self, other): | ||
131 | if isinstance(other, Tone): | 132 | if isinstance(other, Tone): | ||
132 | if other not in self.distinct_tones: | 133 | if other not in self.distinct_tones: | ||
n | 133 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | n | 134 | raise TypeError(f'Cannot remove tone {other.name} from chord {self}') |
134 | 135 | ||||
135 | new_distinct_tones = self.distinct_tones[:] | 136 | new_distinct_tones = self.distinct_tones[:] | ||
136 | new_distinct_tones.remove(other) | 137 | new_distinct_tones.remove(other) | ||
137 | return Chord(*new_distinct_tones) | 138 | return Chord(*new_distinct_tones) | ||
138 | 139 | ||||
n | 139 | def transposed(self, inverval): | n | 140 | def transposed(self, interval): |
140 | if isinstance(inverval, Interval): | 141 | if isinstance(interval, Interval): | ||
141 | transposed_tones = [Tone(tone) + inverval for tone in self.distinct_tones] | 142 | transposed_tones = [tone + interval for tone in self.distinct_tones] | ||
142 | 143 | ||||
143 | return Chord(*transposed_tones) | 144 | return Chord(*transposed_tones) | ||
t | t | 145 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
n | n | 1 | from collections import OrderedDict | ||
2 | |||||
1 | class Tone(): | 3 | class Tone: | ||
2 | tones_order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 4 | TONES_ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
3 | tones_total = len(tones_order) | 5 | TONES_TOTAL = len(TONES_ORDER) | ||
4 | 6 | ||||
5 | def __init__(self, name): | 7 | def __init__(self, name): | ||
n | 6 | if name not in Tone.tones_order: | n | 8 | if name not in Tone.TONES_ORDER: |
7 | raise Exception(f'{name} is not a valid tone!') | 9 | raise Exception(f'{name} is not a valid tone!') | ||
n | 8 | n | |||
9 | self.name = name | 10 | self.name = name | ||
10 | 11 | ||||
11 | def __str__(self): | 12 | def __str__(self): | ||
12 | return self.name | 13 | return self.name | ||
n | n | 14 | |||
15 | def __eq__(self, other): | ||||
16 | if not isinstance(other, Tone): | ||||
17 | return False | ||||
18 | return self.name == other.name | ||||
13 | 19 | ||||
14 | def __add__(self, other): | 20 | def __add__(self, other): | ||
15 | if isinstance(other, Tone): | 21 | if isinstance(other, Tone): | ||
n | 16 | return Chord(other, self) | n | 22 | return Chord(self, other) |
17 | elif isinstance(other, Interval): | 23 | elif isinstance(other, Interval): | ||
n | 18 | current_tone_idx = Tone.tones_order.index(self.name) | n | 24 | current_tone_idx = Tone.TONES_ORDER.index(self.name) |
19 | new_tone_idx = (current_tone_idx + other.num_semitones) % Tone.tones_total | 25 | new_tone_idx = (current_tone_idx + other.num_semitones) % Tone.TONES_TOTAL | ||
20 | 26 | ||||
n | 21 | new_tone = Tone.tones_order[new_tone_idx] | n | 27 | new_tone = Tone.TONES_ORDER[new_tone_idx] |
22 | return Tone(new_tone) | 28 | return Tone(new_tone) | ||
23 | 29 | ||||
24 | def __sub__(self, other): | 30 | def __sub__(self, other): | ||
25 | if isinstance(other, Tone): | 31 | if isinstance(other, Tone): | ||
n | 26 | self_tone_idx = Tone.tones_order.index(self.name) | n | 32 | self_tone_idx = Tone.TONES_ORDER.index(self.name) |
27 | other_tone_idx = Tone.tones_order.index(other.name) | 33 | other_tone_idx = Tone.TONES_ORDER.index(other.name) | ||
28 | 34 | ||||
n | 29 | num_semitones = (self_tone_idx - other_tone_idx) % Tone.tones_total | n | 35 | num_semitones = (self_tone_idx - other_tone_idx) % Tone.TONES_TOTAL |
30 | return Interval(num_semitones) | 36 | return Interval(num_semitones) | ||
31 | elif isinstance(other, Interval): | 37 | elif isinstance(other, Interval): | ||
32 | return self + (-other) | 38 | return self + (-other) | ||
n | n | 39 | |||
40 | def __hash__(self): | ||||
41 | return hash(self.name) | ||||
33 | 42 | ||||
34 | 43 | ||||
n | 35 | class Interval(): | n | 44 | class Interval: |
45 | INTERVAL_NAMES = ( | ||||
46 | 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', | ||||
47 | 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th' | ||||
48 | ) | ||||
36 | def __init__(self, num_semitones): | 49 | def __init__(self, num_semitones): | ||
37 | if not isinstance(num_semitones, int): | 50 | if not isinstance(num_semitones, int): | ||
38 | raise TypeError('Number of semitones must be an integer! :)') | 51 | raise TypeError('Number of semitones must be an integer! :)') | ||
n | 39 | n | |||
40 | self.num_semitones = num_semitones % Tone.tones_total | 52 | self.num_semitones = num_semitones % Tone.TONES_TOTAL | ||
41 | 53 | ||||
42 | def __str__(self): | 54 | def __str__(self): | ||
n | 43 | return ( | n | 55 | return Interval.INTERVAL_NAMES[self.num_semitones] |
44 | 'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', | ||||
45 | 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th' | ||||
46 | )[self.num_semitones] | ||||
47 | 56 | ||||
48 | def __add__(self, other): | 57 | def __add__(self, other): | ||
49 | if isinstance(other, Interval): | 58 | if isinstance(other, Interval): | ||
50 | new_interval = self.num_semitones + other.num_semitones | 59 | new_interval = self.num_semitones + other.num_semitones | ||
51 | return Interval(new_interval) | 60 | return Interval(new_interval) | ||
52 | raise TypeError ('nqkva greshka, ne znam veche...') | 61 | raise TypeError ('nqkva greshka, ne znam veche...') | ||
53 | 62 | ||||
54 | def __sub__(self, other): | 63 | def __sub__(self, other): | ||
55 | if isinstance(other, Tone): | 64 | if isinstance(other, Tone): | ||
56 | raise TypeError ('Invalid operation.') | 65 | raise TypeError ('Invalid operation.') | ||
57 | 66 | ||||
58 | def __neg__(self): | 67 | def __neg__(self): | ||
59 | return Interval(-self.num_semitones) | 68 | return Interval(-self.num_semitones) | ||
60 | 69 | ||||
61 | 70 | ||||
n | 62 | class Chord(): | n | 71 | class Chord: |
63 | def __init__(self, *args): | 72 | def __init__(self, *args): | ||
64 | if any(not isinstance(t, Tone) for t in args): | 73 | if any(not isinstance(t, Tone) for t in args): | ||
65 | raise TypeError('One or more arguments are not of type Tone!') | 74 | raise TypeError('One or more arguments are not of type Tone!') | ||
66 | 75 | ||||
n | 67 | ordered_tones_set = dict.fromkeys([str(tone) for tone in args]) | n | 76 | ordered_tones_set = OrderedDict.fromkeys(args) |
77 | |||||
68 | self.distinct_tones = list(ordered_tones_set.keys()) | 78 | self.distinct_tones = list(ordered_tones_set.keys()) | ||
69 | 79 | ||||
70 | if len(self.distinct_tones) == 1: | 80 | if len(self.distinct_tones) == 1: | ||
71 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 81 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
72 | 82 | ||||
73 | def __str__(self): | 83 | def __str__(self): | ||
74 | root_tone = self.distinct_tones[0] | 84 | root_tone = self.distinct_tones[0] | ||
n | 75 | root_tone_idx = Tone.tones_order.index(root_tone) | n | 85 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) |
76 | 86 | ||||
77 | final_order_tones = [] | 87 | final_order_tones = [] | ||
78 | 88 | ||||
n | 79 | for i in range(0, Tone.tones_total): | n | 89 | for i in range(Tone.TONES_TOTAL): |
80 | current_tone_idx = (i + root_tone_idx) % Tone.tones_total | 90 | current_tone_idx = (i + root_tone_idx) % Tone.TONES_TOTAL | ||
81 | current_tone = Tone.tones_order[current_tone_idx] | 91 | current_tone = Tone.TONES_ORDER[current_tone_idx] | ||
82 | if current_tone in self.distinct_tones: | 92 | if Tone(current_tone) in self.distinct_tones: | ||
83 | final_order_tones.append(Tone.tones_order[current_tone_idx]) | 93 | final_order_tones.append(Tone.TONES_ORDER[current_tone_idx]) | ||
84 | |||||
85 | return '-'.join(final_order_tones) | 94 | return '-'.join(final_order_tones) | ||
86 | 95 | ||||
87 | def is_minor(self): | 96 | def is_minor(self): | ||
88 | root_tone = self.distinct_tones[0] | 97 | root_tone = self.distinct_tones[0] | ||
n | 89 | root_tone_idx = Tone.tones_order.index(root_tone) | n | 98 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) |
90 | 99 | ||||
n | 91 | minor_3rd_idx = (root_tone_idx + 3) % Tone.tones_total | n | 100 | minor_3rd_idx = (root_tone_idx + 3) % Tone.TONES_TOTAL |
92 | minor_3rd_tone = Tone.tones_order[minor_3rd_idx] | 101 | minor_3rd_tone = Tone(Tone.TONES_ORDER[minor_3rd_idx]) | ||
93 | 102 | ||||
94 | return minor_3rd_tone in self.distinct_tones | 103 | return minor_3rd_tone in self.distinct_tones | ||
95 | 104 | ||||
96 | def is_major(self): | 105 | def is_major(self): | ||
97 | root_tone = self.distinct_tones[0] | 106 | root_tone = self.distinct_tones[0] | ||
n | 98 | root_tone_idx = Tone.tones_order.index(root_tone) | n | 107 | root_tone_idx = Tone.TONES_ORDER.index(root_tone.name) |
99 | 108 | ||||
n | 100 | major_3rd_idx = (root_tone_idx + 4) % Tone.tones_total | n | 109 | major_3rd_idx = (root_tone_idx + 4) % Tone.TONES_TOTAL |
101 | major_3rd_tone = Tone.tones_order[major_3rd_idx] | 110 | major_3rd_tone = Tone(Tone.TONES_ORDER[major_3rd_idx]) | ||
102 | 111 | ||||
103 | return major_3rd_tone in self.distinct_tones | 112 | return major_3rd_tone in self.distinct_tones | ||
104 | 113 | ||||
105 | def is_power_chord(self): | 114 | def is_power_chord(self): | ||
106 | return not self.is_minor() and not self.is_major() | 115 | return not self.is_minor() and not self.is_major() | ||
107 | 116 | ||||
108 | def __add__(self, other): | 117 | def __add__(self, other): | ||
109 | if isinstance(other, Tone): | 118 | if isinstance(other, Tone): | ||
n | 110 | if str(other) in self.distinct_tones: | n | 119 | if other in self.distinct_tones: |
111 | return self | 120 | return self | ||
112 | 121 | ||||
113 | new_distinct_tones = self.distinct_tones.copy() | 122 | new_distinct_tones = self.distinct_tones[:] | ||
114 | new_distinct_tones.append(str(other)) | 123 | new_distinct_tones.append(other) | ||
115 | tones = [Tone(tone) for tone in new_distinct_tones] | ||||
116 | |||||
117 | return Chord(*tones) | 124 | return Chord(*new_distinct_tones) | ||
118 | 125 | ||||
119 | elif isinstance(other, Chord): | 126 | elif isinstance(other, Chord): | ||
n | 120 | combined_tones = set(self.distinct_tones + other.distinct_tones) | n | 127 | combined_tones = self.distinct_tones + other.distinct_tones |
121 | tones = [Tone(tone) for tone in combined_tones] | ||||
122 | return Chord(*tones) | 128 | return Chord(*combined_tones) | ||
123 | 129 | ||||
124 | def __sub__(self, other): | 130 | def __sub__(self, other): | ||
125 | if isinstance(other, Tone): | 131 | if isinstance(other, Tone): | ||
n | 126 | if str(other) not in self.distinct_tones: | n | 132 | if other not in self.distinct_tones: |
127 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | 133 | raise TypeError(f'Cannot remove tone {other.name} from chord {str(self)}') | ||
128 | 134 | ||||
t | 129 | new_distinct_tones = self.distinct_tones.copy() | t | 135 | new_distinct_tones = self.distinct_tones[:] |
130 | new_distinct_tones.remove(other.name) | 136 | new_distinct_tones.remove(other) | ||
131 | tones = [Tone(tone) for tone in new_distinct_tones] | ||||
132 | |||||
133 | return Chord(*tones) | 137 | return Chord(*new_distinct_tones) | ||
134 | 138 | ||||
135 | def transposed(self, inverval): | 139 | def transposed(self, inverval): | ||
136 | if isinstance(inverval, Interval): | 140 | if isinstance(inverval, Interval): | ||
137 | transposed_tones = [Tone(tone) + inverval for tone in self.distinct_tones] | 141 | transposed_tones = [Tone(tone) + inverval for tone in self.distinct_tones] | ||
138 | 142 | ||||
139 | return Chord(*transposed_tones) | 143 | return Chord(*transposed_tones) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|