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