1# Updated solution: 4.11.2024, прочетох коментарите по предишното решение, някои неща са оправени, някои не
2
3# и с главни букви, ама вече ги бех написал така
4interval_tuple = ("unison", "minor 2nd", "major 2nd", "minor 3rd",
5 "major 3rd", "perfect 4th", "diminished 5th", "perfect 5th",
6 "minor 6th", "major 6th", "minor 7th", "major 7th")
7tones_tuple = ("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#")
8# не е само за tuple, а за всички iterable колекции с __getitem__ или квото беше там operator[]
9def get_position_in_tuple(element, collection):
10 for i in range(0,len(collection)):
11 if collection[i] == element:
12 return i
13 return -101 # да не ми счупи is_minor и is_major ако ми въведете невалиден тон
14
15
16class Tone:
17
18 def __init__(self, data):
19 # не правя проверка дали е валиден тон, но ако не е не би трябвало да счупи програмата
20 # или поне функциите, за които го тествах
21 self.data = data
22
23 def __str__(self):
24 return self.data
25
26 def __eq__(self, other): # трябва да се дефинира, иначе проверката е с is, което не е желаното поведение
27 return self.data == other.data
28
29 def __add__(self, other):
30 if type(other) is Tone:
31 return Chord(self, other)
32 if type(other) is Interval:
33 result_tone = get_position_in_tuple(self.data, tones_tuple) + other.data
34 size_tones = len(tones_tuple)
35 while result_tone >= size_tones:
36 result_tone -= size_tones
37 return Tone(tones_tuple[result_tone])
38
39 def __sub__(self, other):
40 if type(other) is Tone:
41 left_position = get_position_in_tuple(self.data, tones_tuple)
42 right_position = get_position_in_tuple(other.data, tones_tuple)
43 difference = left_position - right_position
44 # G - C = 7 (perfect 5th), но C - G = 5 (perfect 4th), явно въртим окръжността наобратно?
45 if difference >= 0:
46 return Interval(difference)
47 else:
48 return Interval(len(interval_tuple) - abs(difference))
49 if type(other) is Interval:
50 result_tone = get_position_in_tuple(self.data, tones_tuple) - other.data
51 size_tones = len(tones_tuple)
52 while result_tone < 0:
53 result_tone += size_tones
54 return Tone(tones_tuple[result_tone])
55
56class Interval:
57
58 def __init__(self, data):
59 size_intervals = len(interval_tuple)
60 while data >= size_intervals: # може би трябваше с %, чак сега виждам колко зле съм го направил
61 data -= size_intervals
62 self.data = data
63
64 def __neg__(self):
65 return -self.data
66
67 def __str__(self):
68 return interval_tuple[self.data]
69
70 def __add__(self, other): # ако имаме Interval + Tone
71 if type(other) is Tone:
72 raise TypeError("Invalid operation")
73 if type(other) is Interval:
74 result_data = self.data + other.data
75 return Interval(result_data) # __init__ ще се оправи дори с неща >= 12
76
77 def __sub__(self, other): # ако имаме Interval - Tone
78 if type(other) is Tone:
79 raise TypeError("Invalid operation")
80
81
82class Chord:
83
84 def __init__(self, *args):
85 self.tones = set()
86 self.main_tone = str(args[0])
87 for item in args: # добавяме всички тонове, включително и main-a
88 if type(item) is Tone:
89 self.tones.add(str(item))
90 if len(self.tones) == 1:
91 raise TypeError("Cannot have a chord made of only 1 unique tone")
92
93 def _fill_list_from_source(self, source, destination): # може би не е най-доброто име за функцията
94 for item in source:
95 if item in self.tones:
96 destination.append(item)
97 return destination
98
99 def __str__(self):
100 self.tones = list(self.tones)
101 position_of_main_tone = 0
102 # тъй като въртенето започва от main тона, правя два tuple-a, за тези след него в окръжността
103 # е за тези преди него
104 # целта е да се създаде сортиран лист, който в последствие да join-нем с "-"
105 for i in range(0, len(tones_tuple)):
106 if self.main_tone == tones_tuple[i]:
107 position_of_main_tone = i
108 after_tuple = tones_tuple[position_of_main_tone:]
109 before_tuple = tones_tuple[:position_of_main_tone]
110 to_return = list()
111 to_return = self._fill_list_from_source(after_tuple, to_return)
112 to_return = self._fill_list_from_source(before_tuple, to_return)
113 return "-".join(to_return)
114
115 def check_for_specific_chord(self, chord_string):
116 main_position = get_position_in_tuple(str(self.main_tone), tones_tuple)
117 wanted_position = get_position_in_tuple(chord_string, interval_tuple)
118 for item in self.tones:
119 curr_position = get_position_in_tuple(str(item), tones_tuple)
120 # ако са на позиции 3 и 6 примерно, ще има minor 3rd
121 # но трябва да проверим и случая ако са на позиции 11 и 2 примерно, разликата м/у тях пак е 3
122 # тогава пак ще има minor 3rd, но за целта трябва 12 - (11-2)
123 current_diff = abs(curr_position - main_position)
124 if current_diff == wanted_position or len(tones_tuple) - current_diff == wanted_position:
125 return True
126 return False
127
128 def is_minor(self):
129 return self.check_for_specific_chord("minor 3rd")
130
131 def is_major(self):
132 return self.check_for_specific_chord("major 3rd")
133
134 def is_power_chord(self):
135 return not self.is_minor() and not self.is_major()
136
137 def __add__(self, other):
138 if type(other) is Tone:
139 result_chord = Chord(self.main_tone)
140 result_chord.tones.update(self.tones)
141 result_chord.tones.add(other.data)
142 return result_chord
143 if type(other) is Chord:
144 result_chord = Chord(self.main_tone)
145 result_chord.tones.update(self.tones)
146 result_chord.tones.add(other.main_tone)
147 result_chord.tones.update(other.tones)
148 return result_chord
149
150 def __sub__(self, other):
151 if (not other.data in self.tones) and other.data != self.main_tone:
152 raise TypeError(f"Cannot remove tone {other.data} from chord {self}")
153 if len(self.tones) < 3:
154 raise TypeError("Cannot have a chord made of only 1 unique tone")
155 if type(other) is Tone:
156 result_chord = Chord(self.main_tone)
157 if other.data == self.main_tone: # fix, не работеше правилно
158 main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple)
159 for item in tones_tuple[main_tone_pos+1:]: # гледаме първо тия след него до края на tuple-a
160 if item in tuple(self.tones):
161 result_chord.main_tone = item
162 break
163 if result_chord.main_tone == other.data: # ако не сме променили main_tone с тези след него
164 for item in tones_tuple[:main_tone_pos]:
165 if item in tuple(self.tones):
166 result_chord.main_tone = item
167 break
168 result_chord.tones.update(self.tones)
169 result_chord.tones.remove(self.main_tone)
170 else: # и тук пак fix
171 result_chord.tones.update(self.tones)
172 result_chord.tones.remove(other.data)
173 return result_chord
174
175 def transposed(self, interval):
176 # можеше да използвам и събирането, което дефинирах за тоновете...
177 position = 0
178 if type(interval) is int:
179 position = interval
180 else:
181 position = interval.data
182 result_chord = Chord(self.main_tone)
183 main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) + position
184 # имаше някакви бъгове, които всеки път даваха различен main_tone, затова main тона е отделно, нищо че може да се сложи и в долния цикъл
185 if main_tone_pos < 0:
186 main_tone_pos += len(tones_tuple)
187 elif main_tone_pos >= len(tones_tuple):
188 main_tone_pos -= len(tones_tuple)
189 result_chord.main_tone = tones_tuple[main_tone_pos]
190 # а иначе цикъла, в който пак си го има main тона попринцип, ама програмата си прави каквото си иска и всеки път дава различен резултат
191 for item in self.tones:
192 curr_position = get_position_in_tuple(item, tones_tuple) + position
193 if curr_position < 0:
194 curr_position += len(tones_tuple)
195 elif curr_position >= len(tones_tuple):
196 curr_position -= len(tones_tuple)
197 result_chord.tones.add(tones_tuple[curr_position])
198 return result_chord
...........F.........................
======================================================================
FAIL: test_interval_negative (test.TestBasicIntervalFunctionality.test_interval_negative)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 51, in test_interval_negative
self.assertEqual(str(minor_2nd), "minor 2nd")
AssertionError: '-11' != 'minor 2nd'
- -11
+ minor 2nd
----------------------------------------------------------------------
Ran 37 tests in 0.001s
FAILED (failures=1)
n | 1 | # коментарите ги пиша след като съм готов с целия код, има доста тъпотии из кода, нема ги оправям | n | 1 | # Updated solution: 4.11.2024, прочетох коментарите по предишното решение, някои неща са оправени, някои не |
2 | 2 | ||||
n | 3 | # може би трябваше да са сетове | n | ||
4 | # и с главни букви, ама вече ги бех написал така | 3 | # и с главни букви, ама вече ги бех написал така | ||
5 | interval_tuple = ("unison", "minor 2nd", "major 2nd", "minor 3rd", | 4 | interval_tuple = ("unison", "minor 2nd", "major 2nd", "minor 3rd", | ||
6 | "major 3rd", "perfect 4th", "diminished 5th", "perfect 5th", | 5 | "major 3rd", "perfect 4th", "diminished 5th", "perfect 5th", | ||
7 | "minor 6th", "major 6th", "minor 7th", "major 7th") | 6 | "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
8 | tones_tuple = ("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#") | 7 | tones_tuple = ("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#") | ||
9 | # не е само за tuple, а за всички iterable колекции с __getitem__ или квото беше там operator[] | 8 | # не е само за tuple, а за всички iterable колекции с __getitem__ или квото беше там operator[] | ||
n | 10 | def get_position_in_tuple(element, tuple): | n | 9 | def get_position_in_tuple(element, collection): |
11 | for i in range(0,len(tuple)): | 10 | for i in range(0,len(collection)): | ||
12 | if tuple[i] == element: | 11 | if collection[i] == element: | ||
13 | return i | 12 | return i | ||
14 | return -101 # да не ми счупи is_minor и is_major ако ми въведете невалиден тон | 13 | return -101 # да не ми счупи is_minor и is_major ако ми въведете невалиден тон | ||
15 | 14 | ||||
16 | 15 | ||||
17 | class Tone: | 16 | class Tone: | ||
18 | 17 | ||||
19 | def __init__(self, data): | 18 | def __init__(self, data): | ||
20 | # не правя проверка дали е валиден тон, но ако не е не би трябвало да счупи програмата | 19 | # не правя проверка дали е валиден тон, но ако не е не би трябвало да счупи програмата | ||
21 | # или поне функциите, за които го тествах | 20 | # или поне функциите, за които го тествах | ||
22 | self.data = data | 21 | self.data = data | ||
23 | 22 | ||||
24 | def __str__(self): | 23 | def __str__(self): | ||
25 | return self.data | 24 | return self.data | ||
26 | 25 | ||||
27 | def __eq__(self, other): # трябва да се дефинира, иначе проверката е с is, което не е желаното поведение | 26 | def __eq__(self, other): # трябва да се дефинира, иначе проверката е с is, което не е желаното поведение | ||
28 | return self.data == other.data | 27 | return self.data == other.data | ||
29 | 28 | ||||
30 | def __add__(self, other): | 29 | def __add__(self, other): | ||
31 | if type(other) is Tone: | 30 | if type(other) is Tone: | ||
32 | return Chord(self, other) | 31 | return Chord(self, other) | ||
33 | if type(other) is Interval: | 32 | if type(other) is Interval: | ||
34 | result_tone = get_position_in_tuple(self.data, tones_tuple) + other.data | 33 | result_tone = get_position_in_tuple(self.data, tones_tuple) + other.data | ||
35 | size_tones = len(tones_tuple) | 34 | size_tones = len(tones_tuple) | ||
36 | while result_tone >= size_tones: | 35 | while result_tone >= size_tones: | ||
37 | result_tone -= size_tones | 36 | result_tone -= size_tones | ||
38 | return Tone(tones_tuple[result_tone]) | 37 | return Tone(tones_tuple[result_tone]) | ||
39 | 38 | ||||
40 | def __sub__(self, other): | 39 | def __sub__(self, other): | ||
41 | if type(other) is Tone: | 40 | if type(other) is Tone: | ||
42 | left_position = get_position_in_tuple(self.data, tones_tuple) | 41 | left_position = get_position_in_tuple(self.data, tones_tuple) | ||
43 | right_position = get_position_in_tuple(other.data, tones_tuple) | 42 | right_position = get_position_in_tuple(other.data, tones_tuple) | ||
44 | difference = left_position - right_position | 43 | difference = left_position - right_position | ||
45 | # G - C = 7 (perfect 5th), но C - G = 5 (perfect 4th), явно въртим окръжността наобратно? | 44 | # G - C = 7 (perfect 5th), но C - G = 5 (perfect 4th), явно въртим окръжността наобратно? | ||
46 | if difference >= 0: | 45 | if difference >= 0: | ||
47 | return Interval(difference) | 46 | return Interval(difference) | ||
48 | else: | 47 | else: | ||
49 | return Interval(len(interval_tuple) - abs(difference)) | 48 | return Interval(len(interval_tuple) - abs(difference)) | ||
50 | if type(other) is Interval: | 49 | if type(other) is Interval: | ||
51 | result_tone = get_position_in_tuple(self.data, tones_tuple) - other.data | 50 | result_tone = get_position_in_tuple(self.data, tones_tuple) - other.data | ||
52 | size_tones = len(tones_tuple) | 51 | size_tones = len(tones_tuple) | ||
53 | while result_tone < 0: | 52 | while result_tone < 0: | ||
54 | result_tone += size_tones | 53 | result_tone += size_tones | ||
55 | return Tone(tones_tuple[result_tone]) | 54 | return Tone(tones_tuple[result_tone]) | ||
56 | 55 | ||||
57 | class Interval: | 56 | class Interval: | ||
58 | 57 | ||||
59 | def __init__(self, data): | 58 | def __init__(self, data): | ||
60 | size_intervals = len(interval_tuple) | 59 | size_intervals = len(interval_tuple) | ||
61 | while data >= size_intervals: # може би трябваше с %, чак сега виждам колко зле съм го направил | 60 | while data >= size_intervals: # може би трябваше с %, чак сега виждам колко зле съм го направил | ||
62 | data -= size_intervals | 61 | data -= size_intervals | ||
63 | self.data = data | 62 | self.data = data | ||
64 | 63 | ||||
65 | def __neg__(self): | 64 | def __neg__(self): | ||
66 | return -self.data | 65 | return -self.data | ||
67 | 66 | ||||
68 | def __str__(self): | 67 | def __str__(self): | ||
69 | return interval_tuple[self.data] | 68 | return interval_tuple[self.data] | ||
70 | 69 | ||||
71 | def __add__(self, other): # ако имаме Interval + Tone | 70 | def __add__(self, other): # ако имаме Interval + Tone | ||
72 | if type(other) is Tone: | 71 | if type(other) is Tone: | ||
73 | raise TypeError("Invalid operation") | 72 | raise TypeError("Invalid operation") | ||
74 | if type(other) is Interval: | 73 | if type(other) is Interval: | ||
75 | result_data = self.data + other.data | 74 | result_data = self.data + other.data | ||
76 | return Interval(result_data) # __init__ ще се оправи дори с неща >= 12 | 75 | return Interval(result_data) # __init__ ще се оправи дори с неща >= 12 | ||
77 | 76 | ||||
78 | def __sub__(self, other): # ако имаме Interval - Tone | 77 | def __sub__(self, other): # ако имаме Interval - Tone | ||
79 | if type(other) is Tone: | 78 | if type(other) is Tone: | ||
80 | raise TypeError("Invalid operation") | 79 | raise TypeError("Invalid operation") | ||
n | 81 | pass | n | ||
82 | 80 | ||||
83 | 81 | ||||
84 | class Chord: | 82 | class Chord: | ||
85 | 83 | ||||
86 | def __init__(self, *args): | 84 | def __init__(self, *args): | ||
87 | self.tones = set() | 85 | self.tones = set() | ||
88 | self.main_tone = str(args[0]) | 86 | self.main_tone = str(args[0]) | ||
89 | for item in args: # добавяме всички тонове, включително и main-a | 87 | for item in args: # добавяме всички тонове, включително и main-a | ||
90 | if type(item) is Tone: | 88 | if type(item) is Tone: | ||
91 | self.tones.add(str(item)) | 89 | self.tones.add(str(item)) | ||
92 | if len(self.tones) == 1: | 90 | if len(self.tones) == 1: | ||
93 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 91 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
94 | 92 | ||||
95 | def _fill_list_from_source(self, source, destination): # може би не е най-доброто име за функцията | 93 | def _fill_list_from_source(self, source, destination): # може би не е най-доброто име за функцията | ||
96 | for item in source: | 94 | for item in source: | ||
97 | if item in self.tones: | 95 | if item in self.tones: | ||
98 | destination.append(item) | 96 | destination.append(item) | ||
99 | return destination | 97 | return destination | ||
100 | 98 | ||||
101 | def __str__(self): | 99 | def __str__(self): | ||
102 | self.tones = list(self.tones) | 100 | self.tones = list(self.tones) | ||
103 | position_of_main_tone = 0 | 101 | position_of_main_tone = 0 | ||
104 | # тъй като въртенето започва от main тона, правя два tuple-a, за тези след него в окръжността | 102 | # тъй като въртенето започва от main тона, правя два tuple-a, за тези след него в окръжността | ||
105 | # е за тези преди него | 103 | # е за тези преди него | ||
106 | # целта е да се създаде сортиран лист, който в последствие да join-нем с "-" | 104 | # целта е да се създаде сортиран лист, който в последствие да join-нем с "-" | ||
n | 107 | for i in range(0,len(tones_tuple)): | n | 105 | for i in range(0, len(tones_tuple)): |
108 | if self.main_tone == tones_tuple[i]: | 106 | if self.main_tone == tones_tuple[i]: | ||
109 | position_of_main_tone = i | 107 | position_of_main_tone = i | ||
110 | after_tuple = tones_tuple[position_of_main_tone:] | 108 | after_tuple = tones_tuple[position_of_main_tone:] | ||
111 | before_tuple = tones_tuple[:position_of_main_tone] | 109 | before_tuple = tones_tuple[:position_of_main_tone] | ||
112 | to_return = list() | 110 | to_return = list() | ||
113 | to_return = self._fill_list_from_source(after_tuple, to_return) | 111 | to_return = self._fill_list_from_source(after_tuple, to_return) | ||
114 | to_return = self._fill_list_from_source(before_tuple, to_return) | 112 | to_return = self._fill_list_from_source(before_tuple, to_return) | ||
115 | return "-".join(to_return) | 113 | return "-".join(to_return) | ||
116 | 114 | ||||
117 | def check_for_specific_chord(self, chord_string): | 115 | def check_for_specific_chord(self, chord_string): | ||
118 | main_position = get_position_in_tuple(str(self.main_tone), tones_tuple) | 116 | main_position = get_position_in_tuple(str(self.main_tone), tones_tuple) | ||
119 | wanted_position = get_position_in_tuple(chord_string, interval_tuple) | 117 | wanted_position = get_position_in_tuple(chord_string, interval_tuple) | ||
120 | for item in self.tones: | 118 | for item in self.tones: | ||
121 | curr_position = get_position_in_tuple(str(item), tones_tuple) | 119 | curr_position = get_position_in_tuple(str(item), tones_tuple) | ||
122 | # ако са на позиции 3 и 6 примерно, ще има minor 3rd | 120 | # ако са на позиции 3 и 6 примерно, ще има minor 3rd | ||
123 | # но трябва да проверим и случая ако са на позиции 11 и 2 примерно, разликата м/у тях пак е 3 | 121 | # но трябва да проверим и случая ако са на позиции 11 и 2 примерно, разликата м/у тях пак е 3 | ||
124 | # тогава пак ще има minor 3rd, но за целта трябва 12 - (11-2) | 122 | # тогава пак ще има minor 3rd, но за целта трябва 12 - (11-2) | ||
125 | current_diff = abs(curr_position - main_position) | 123 | current_diff = abs(curr_position - main_position) | ||
126 | if current_diff == wanted_position or len(tones_tuple) - current_diff == wanted_position: | 124 | if current_diff == wanted_position or len(tones_tuple) - current_diff == wanted_position: | ||
127 | return True | 125 | return True | ||
128 | return False | 126 | return False | ||
129 | 127 | ||||
130 | def is_minor(self): | 128 | def is_minor(self): | ||
131 | return self.check_for_specific_chord("minor 3rd") | 129 | return self.check_for_specific_chord("minor 3rd") | ||
132 | 130 | ||||
133 | def is_major(self): | 131 | def is_major(self): | ||
134 | return self.check_for_specific_chord("major 3rd") | 132 | return self.check_for_specific_chord("major 3rd") | ||
135 | 133 | ||||
136 | def is_power_chord(self): | 134 | def is_power_chord(self): | ||
137 | return not self.is_minor() and not self.is_major() | 135 | return not self.is_minor() and not self.is_major() | ||
138 | 136 | ||||
139 | def __add__(self, other): | 137 | def __add__(self, other): | ||
140 | if type(other) is Tone: | 138 | if type(other) is Tone: | ||
141 | result_chord = Chord(self.main_tone) | 139 | result_chord = Chord(self.main_tone) | ||
142 | result_chord.tones.update(self.tones) | 140 | result_chord.tones.update(self.tones) | ||
143 | result_chord.tones.add(other.data) | 141 | result_chord.tones.add(other.data) | ||
144 | return result_chord | 142 | return result_chord | ||
145 | if type(other) is Chord: | 143 | if type(other) is Chord: | ||
146 | result_chord = Chord(self.main_tone) | 144 | result_chord = Chord(self.main_tone) | ||
147 | result_chord.tones.update(self.tones) | 145 | result_chord.tones.update(self.tones) | ||
148 | result_chord.tones.add(other.main_tone) | 146 | result_chord.tones.add(other.main_tone) | ||
149 | result_chord.tones.update(other.tones) | 147 | result_chord.tones.update(other.tones) | ||
150 | return result_chord | 148 | return result_chord | ||
151 | 149 | ||||
152 | def __sub__(self, other): | 150 | def __sub__(self, other): | ||
153 | if (not other.data in self.tones) and other.data != self.main_tone: | 151 | if (not other.data in self.tones) and other.data != self.main_tone: | ||
154 | raise TypeError(f"Cannot remove tone {other.data} from chord {self}") | 152 | raise TypeError(f"Cannot remove tone {other.data} from chord {self}") | ||
155 | if len(self.tones) < 3: | 153 | if len(self.tones) < 3: | ||
156 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 154 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
157 | if type(other) is Tone: | 155 | if type(other) is Tone: | ||
158 | result_chord = Chord(self.main_tone) | 156 | result_chord = Chord(self.main_tone) | ||
t | 159 | if other.data == self.main_tone: | t | 157 | if other.data == self.main_tone: # fix, не работеше правилно |
158 | main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) | ||||
159 | for item in tones_tuple[main_tone_pos+1:]: # гледаме първо тия след него до края на tuple-a | ||||
160 | if item in tuple(self.tones): | ||||
160 | result_chord.main_tone = self.tones[0] | 161 | result_chord.main_tone = item | ||
162 | break | ||||
163 | if result_chord.main_tone == other.data: # ако не сме променили main_tone с тези след него | ||||
164 | for item in tones_tuple[:main_tone_pos]: | ||||
165 | if item in tuple(self.tones): | ||||
166 | result_chord.main_tone = item | ||||
167 | break | ||||
161 | result_chord.tones.update(list(self.tones)[1:]) | 168 | result_chord.tones.update(self.tones) | ||
162 | else: | 169 | result_chord.tones.remove(self.main_tone) | ||
163 | position = get_position_in_tuple(other.data, tuple(self.tones)) | 170 | else: # и тук пак fix | ||
164 | result_chord.tones.update(list(self.tones)[:position]) | 171 | result_chord.tones.update(self.tones) | ||
165 | result_chord.tones.update(list(self.tones)[position+1:]) | 172 | result_chord.tones.remove(other.data) | ||
166 | return result_chord | 173 | return result_chord | ||
167 | 174 | ||||
168 | def transposed(self, interval): | 175 | def transposed(self, interval): | ||
169 | # можеше да използвам и събирането, което дефинирах за тоновете... | 176 | # можеше да използвам и събирането, което дефинирах за тоновете... | ||
170 | position = 0 | 177 | position = 0 | ||
171 | if type(interval) is int: | 178 | if type(interval) is int: | ||
172 | position = interval | 179 | position = interval | ||
173 | else: | 180 | else: | ||
174 | position = interval.data | 181 | position = interval.data | ||
175 | result_chord = Chord(self.main_tone) | 182 | result_chord = Chord(self.main_tone) | ||
176 | main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) + position | 183 | main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) + position | ||
177 | # имаше някакви бъгове, които всеки път даваха различен main_tone, затова main тона е отделно, нищо че може да се сложи и в долния цикъл | 184 | # имаше някакви бъгове, които всеки път даваха различен main_tone, затова main тона е отделно, нищо че може да се сложи и в долния цикъл | ||
178 | if main_tone_pos < 0: | 185 | if main_tone_pos < 0: | ||
179 | main_tone_pos += len(tones_tuple) | 186 | main_tone_pos += len(tones_tuple) | ||
180 | elif main_tone_pos >= len(tones_tuple): | 187 | elif main_tone_pos >= len(tones_tuple): | ||
181 | main_tone_pos -= len(tones_tuple) | 188 | main_tone_pos -= len(tones_tuple) | ||
182 | result_chord.main_tone = tones_tuple[main_tone_pos] | 189 | result_chord.main_tone = tones_tuple[main_tone_pos] | ||
183 | # а иначе цикъла, в който пак си го има main тона попринцип, ама програмата си прави каквото си иска и всеки път дава различен резултат | 190 | # а иначе цикъла, в който пак си го има main тона попринцип, ама програмата си прави каквото си иска и всеки път дава различен резултат | ||
184 | for item in self.tones: | 191 | for item in self.tones: | ||
185 | curr_position = get_position_in_tuple(item, tones_tuple) + position | 192 | curr_position = get_position_in_tuple(item, tones_tuple) + position | ||
186 | if curr_position < 0: | 193 | if curr_position < 0: | ||
187 | curr_position += len(tones_tuple) | 194 | curr_position += len(tones_tuple) | ||
188 | elif curr_position >= len(tones_tuple): | 195 | elif curr_position >= len(tones_tuple): | ||
189 | curr_position -= len(tones_tuple) | 196 | curr_position -= len(tones_tuple) | ||
190 | result_chord.tones.add(tones_tuple[curr_position]) | 197 | result_chord.tones.add(tones_tuple[curr_position]) | ||
191 | return result_chord | 198 | return result_chord |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | # коментарите ги пиша след като съм готов с целия код, има доста тъпотии из кода, нема ги оправям | f | 1 | # коментарите ги пиша след като съм готов с целия код, има доста тъпотии из кода, нема ги оправям |
2 | 2 | ||||
3 | # може би трябваше да са сетове | 3 | # може би трябваше да са сетове | ||
4 | # и с главни букви, ама вече ги бех написал така | 4 | # и с главни букви, ама вече ги бех написал така | ||
5 | interval_tuple = ("unison", "minor 2nd", "major 2nd", "minor 3rd", | 5 | interval_tuple = ("unison", "minor 2nd", "major 2nd", "minor 3rd", | ||
6 | "major 3rd", "perfect 4th", "diminished 5th", "perfect 5th", | 6 | "major 3rd", "perfect 4th", "diminished 5th", "perfect 5th", | ||
7 | "minor 6th", "major 6th", "minor 7th", "major 7th") | 7 | "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
8 | tones_tuple = ("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#") | 8 | tones_tuple = ("A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#") | ||
9 | # не е само за tuple, а за всички iterable колекции с __getitem__ или квото беше там operator[] | 9 | # не е само за tuple, а за всички iterable колекции с __getitem__ или квото беше там operator[] | ||
10 | def get_position_in_tuple(element, tuple): | 10 | def get_position_in_tuple(element, tuple): | ||
11 | for i in range(0,len(tuple)): | 11 | for i in range(0,len(tuple)): | ||
12 | if tuple[i] == element: | 12 | if tuple[i] == element: | ||
13 | return i | 13 | return i | ||
14 | return -101 # да не ми счупи is_minor и is_major ако ми въведете невалиден тон | 14 | return -101 # да не ми счупи is_minor и is_major ако ми въведете невалиден тон | ||
15 | 15 | ||||
16 | 16 | ||||
17 | class Tone: | 17 | class Tone: | ||
18 | 18 | ||||
19 | def __init__(self, data): | 19 | def __init__(self, data): | ||
20 | # не правя проверка дали е валиден тон, но ако не е не би трябвало да счупи програмата | 20 | # не правя проверка дали е валиден тон, но ако не е не би трябвало да счупи програмата | ||
21 | # или поне функциите, за които го тествах | 21 | # или поне функциите, за които го тествах | ||
22 | self.data = data | 22 | self.data = data | ||
23 | 23 | ||||
24 | def __str__(self): | 24 | def __str__(self): | ||
25 | return self.data | 25 | return self.data | ||
26 | 26 | ||||
27 | def __eq__(self, other): # трябва да се дефинира, иначе проверката е с is, което не е желаното поведение | 27 | def __eq__(self, other): # трябва да се дефинира, иначе проверката е с is, което не е желаното поведение | ||
28 | return self.data == other.data | 28 | return self.data == other.data | ||
29 | 29 | ||||
30 | def __add__(self, other): | 30 | def __add__(self, other): | ||
31 | if type(other) is Tone: | 31 | if type(other) is Tone: | ||
32 | return Chord(self, other) | 32 | return Chord(self, other) | ||
33 | if type(other) is Interval: | 33 | if type(other) is Interval: | ||
34 | result_tone = get_position_in_tuple(self.data, tones_tuple) + other.data | 34 | result_tone = get_position_in_tuple(self.data, tones_tuple) + other.data | ||
35 | size_tones = len(tones_tuple) | 35 | size_tones = len(tones_tuple) | ||
36 | while result_tone >= size_tones: | 36 | while result_tone >= size_tones: | ||
37 | result_tone -= size_tones | 37 | result_tone -= size_tones | ||
38 | return Tone(tones_tuple[result_tone]) | 38 | return Tone(tones_tuple[result_tone]) | ||
39 | 39 | ||||
40 | def __sub__(self, other): | 40 | def __sub__(self, other): | ||
41 | if type(other) is Tone: | 41 | if type(other) is Tone: | ||
42 | left_position = get_position_in_tuple(self.data, tones_tuple) | 42 | left_position = get_position_in_tuple(self.data, tones_tuple) | ||
43 | right_position = get_position_in_tuple(other.data, tones_tuple) | 43 | right_position = get_position_in_tuple(other.data, tones_tuple) | ||
44 | difference = left_position - right_position | 44 | difference = left_position - right_position | ||
45 | # G - C = 7 (perfect 5th), но C - G = 5 (perfect 4th), явно въртим окръжността наобратно? | 45 | # G - C = 7 (perfect 5th), но C - G = 5 (perfect 4th), явно въртим окръжността наобратно? | ||
46 | if difference >= 0: | 46 | if difference >= 0: | ||
47 | return Interval(difference) | 47 | return Interval(difference) | ||
48 | else: | 48 | else: | ||
49 | return Interval(len(interval_tuple) - abs(difference)) | 49 | return Interval(len(interval_tuple) - abs(difference)) | ||
50 | if type(other) is Interval: | 50 | if type(other) is Interval: | ||
51 | result_tone = get_position_in_tuple(self.data, tones_tuple) - other.data | 51 | result_tone = get_position_in_tuple(self.data, tones_tuple) - other.data | ||
52 | size_tones = len(tones_tuple) | 52 | size_tones = len(tones_tuple) | ||
53 | while result_tone < 0: | 53 | while result_tone < 0: | ||
54 | result_tone += size_tones | 54 | result_tone += size_tones | ||
55 | return Tone(tones_tuple[result_tone]) | 55 | return Tone(tones_tuple[result_tone]) | ||
56 | 56 | ||||
57 | class Interval: | 57 | class Interval: | ||
58 | 58 | ||||
59 | def __init__(self, data): | 59 | def __init__(self, data): | ||
60 | size_intervals = len(interval_tuple) | 60 | size_intervals = len(interval_tuple) | ||
61 | while data >= size_intervals: # може би трябваше с %, чак сега виждам колко зле съм го направил | 61 | while data >= size_intervals: # може би трябваше с %, чак сега виждам колко зле съм го направил | ||
62 | data -= size_intervals | 62 | data -= size_intervals | ||
63 | self.data = data | 63 | self.data = data | ||
64 | 64 | ||||
65 | def __neg__(self): | 65 | def __neg__(self): | ||
66 | return -self.data | 66 | return -self.data | ||
67 | 67 | ||||
68 | def __str__(self): | 68 | def __str__(self): | ||
69 | return interval_tuple[self.data] | 69 | return interval_tuple[self.data] | ||
70 | 70 | ||||
71 | def __add__(self, other): # ако имаме Interval + Tone | 71 | def __add__(self, other): # ако имаме Interval + Tone | ||
72 | if type(other) is Tone: | 72 | if type(other) is Tone: | ||
73 | raise TypeError("Invalid operation") | 73 | raise TypeError("Invalid operation") | ||
74 | if type(other) is Interval: | 74 | if type(other) is Interval: | ||
75 | result_data = self.data + other.data | 75 | result_data = self.data + other.data | ||
76 | return Interval(result_data) # __init__ ще се оправи дори с неща >= 12 | 76 | return Interval(result_data) # __init__ ще се оправи дори с неща >= 12 | ||
77 | 77 | ||||
78 | def __sub__(self, other): # ако имаме Interval - Tone | 78 | def __sub__(self, other): # ако имаме Interval - Tone | ||
79 | if type(other) is Tone: | 79 | if type(other) is Tone: | ||
80 | raise TypeError("Invalid operation") | 80 | raise TypeError("Invalid operation") | ||
81 | pass | 81 | pass | ||
82 | 82 | ||||
83 | 83 | ||||
84 | class Chord: | 84 | class Chord: | ||
85 | 85 | ||||
86 | def __init__(self, *args): | 86 | def __init__(self, *args): | ||
87 | self.tones = set() | 87 | self.tones = set() | ||
88 | self.main_tone = str(args[0]) | 88 | self.main_tone = str(args[0]) | ||
89 | for item in args: # добавяме всички тонове, включително и main-a | 89 | for item in args: # добавяме всички тонове, включително и main-a | ||
90 | if type(item) is Tone: | 90 | if type(item) is Tone: | ||
91 | self.tones.add(str(item)) | 91 | self.tones.add(str(item)) | ||
92 | if len(self.tones) == 1: | 92 | if len(self.tones) == 1: | ||
t | 93 | raise AttributeError("Cannot have a chord made of only 1 unique tone") | t | 93 | raise TypeError("Cannot have a chord made of only 1 unique tone") |
94 | 94 | ||||
95 | def _fill_list_from_source(self, source, destination): # може би не е най-доброто име за функцията | 95 | def _fill_list_from_source(self, source, destination): # може би не е най-доброто име за функцията | ||
96 | for item in source: | 96 | for item in source: | ||
97 | if item in self.tones: | 97 | if item in self.tones: | ||
98 | destination.append(item) | 98 | destination.append(item) | ||
99 | return destination | 99 | return destination | ||
100 | 100 | ||||
101 | def __str__(self): | 101 | def __str__(self): | ||
102 | self.tones = list(self.tones) | 102 | self.tones = list(self.tones) | ||
103 | position_of_main_tone = 0 | 103 | position_of_main_tone = 0 | ||
104 | # тъй като въртенето започва от main тона, правя два tuple-a, за тези след него в окръжността | 104 | # тъй като въртенето започва от main тона, правя два tuple-a, за тези след него в окръжността | ||
105 | # е за тези преди него | 105 | # е за тези преди него | ||
106 | # целта е да се създаде сортиран лист, който в последствие да join-нем с "-" | 106 | # целта е да се създаде сортиран лист, който в последствие да join-нем с "-" | ||
107 | for i in range(0,len(tones_tuple)): | 107 | for i in range(0,len(tones_tuple)): | ||
108 | if self.main_tone == tones_tuple[i]: | 108 | if self.main_tone == tones_tuple[i]: | ||
109 | position_of_main_tone = i | 109 | position_of_main_tone = i | ||
110 | after_tuple = tones_tuple[position_of_main_tone:] | 110 | after_tuple = tones_tuple[position_of_main_tone:] | ||
111 | before_tuple = tones_tuple[:position_of_main_tone] | 111 | before_tuple = tones_tuple[:position_of_main_tone] | ||
112 | to_return = list() | 112 | to_return = list() | ||
113 | to_return = self._fill_list_from_source(after_tuple, to_return) | 113 | to_return = self._fill_list_from_source(after_tuple, to_return) | ||
114 | to_return = self._fill_list_from_source(before_tuple, to_return) | 114 | to_return = self._fill_list_from_source(before_tuple, to_return) | ||
115 | return "-".join(to_return) | 115 | return "-".join(to_return) | ||
116 | 116 | ||||
117 | def check_for_specific_chord(self, chord_string): | 117 | def check_for_specific_chord(self, chord_string): | ||
118 | main_position = get_position_in_tuple(str(self.main_tone), tones_tuple) | 118 | main_position = get_position_in_tuple(str(self.main_tone), tones_tuple) | ||
119 | wanted_position = get_position_in_tuple(chord_string, interval_tuple) | 119 | wanted_position = get_position_in_tuple(chord_string, interval_tuple) | ||
120 | for item in self.tones: | 120 | for item in self.tones: | ||
121 | curr_position = get_position_in_tuple(str(item), tones_tuple) | 121 | curr_position = get_position_in_tuple(str(item), tones_tuple) | ||
122 | # ако са на позиции 3 и 6 примерно, ще има minor 3rd | 122 | # ако са на позиции 3 и 6 примерно, ще има minor 3rd | ||
123 | # но трябва да проверим и случая ако са на позиции 11 и 2 примерно, разликата м/у тях пак е 3 | 123 | # но трябва да проверим и случая ако са на позиции 11 и 2 примерно, разликата м/у тях пак е 3 | ||
124 | # тогава пак ще има minor 3rd, но за целта трябва 12 - (11-2) | 124 | # тогава пак ще има minor 3rd, но за целта трябва 12 - (11-2) | ||
125 | current_diff = abs(curr_position - main_position) | 125 | current_diff = abs(curr_position - main_position) | ||
126 | if current_diff == wanted_position or len(tones_tuple) - current_diff == wanted_position: | 126 | if current_diff == wanted_position or len(tones_tuple) - current_diff == wanted_position: | ||
127 | return True | 127 | return True | ||
128 | return False | 128 | return False | ||
129 | 129 | ||||
130 | def is_minor(self): | 130 | def is_minor(self): | ||
131 | return self.check_for_specific_chord("minor 3rd") | 131 | return self.check_for_specific_chord("minor 3rd") | ||
132 | 132 | ||||
133 | def is_major(self): | 133 | def is_major(self): | ||
134 | return self.check_for_specific_chord("major 3rd") | 134 | return self.check_for_specific_chord("major 3rd") | ||
135 | 135 | ||||
136 | def is_power_chord(self): | 136 | def is_power_chord(self): | ||
137 | return not self.is_minor() and not self.is_major() | 137 | return not self.is_minor() and not self.is_major() | ||
138 | 138 | ||||
139 | def __add__(self, other): | 139 | def __add__(self, other): | ||
140 | if type(other) is Tone: | 140 | if type(other) is Tone: | ||
141 | result_chord = Chord(self.main_tone) | 141 | result_chord = Chord(self.main_tone) | ||
142 | result_chord.tones.update(self.tones) | 142 | result_chord.tones.update(self.tones) | ||
143 | result_chord.tones.add(other.data) | 143 | result_chord.tones.add(other.data) | ||
144 | return result_chord | 144 | return result_chord | ||
145 | if type(other) is Chord: | 145 | if type(other) is Chord: | ||
146 | result_chord = Chord(self.main_tone) | 146 | result_chord = Chord(self.main_tone) | ||
147 | result_chord.tones.update(self.tones) | 147 | result_chord.tones.update(self.tones) | ||
148 | result_chord.tones.add(other.main_tone) | 148 | result_chord.tones.add(other.main_tone) | ||
149 | result_chord.tones.update(other.tones) | 149 | result_chord.tones.update(other.tones) | ||
150 | return result_chord | 150 | return result_chord | ||
151 | 151 | ||||
152 | def __sub__(self, other): | 152 | def __sub__(self, other): | ||
153 | if (not other.data in self.tones) and other.data != self.main_tone: | 153 | if (not other.data in self.tones) and other.data != self.main_tone: | ||
154 | raise TypeError(f"Cannot remove tone {other.data} from chord {self}") | 154 | raise TypeError(f"Cannot remove tone {other.data} from chord {self}") | ||
155 | if len(self.tones) < 3: | 155 | if len(self.tones) < 3: | ||
156 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 156 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
157 | if type(other) is Tone: | 157 | if type(other) is Tone: | ||
158 | result_chord = Chord(self.main_tone) | 158 | result_chord = Chord(self.main_tone) | ||
159 | if other.data == self.main_tone: | 159 | if other.data == self.main_tone: | ||
160 | result_chord.main_tone = self.tones[0] | 160 | result_chord.main_tone = self.tones[0] | ||
161 | result_chord.tones.update(list(self.tones)[1:]) | 161 | result_chord.tones.update(list(self.tones)[1:]) | ||
162 | else: | 162 | else: | ||
163 | position = get_position_in_tuple(other.data, tuple(self.tones)) | 163 | position = get_position_in_tuple(other.data, tuple(self.tones)) | ||
164 | result_chord.tones.update(list(self.tones)[:position]) | 164 | result_chord.tones.update(list(self.tones)[:position]) | ||
165 | result_chord.tones.update(list(self.tones)[position+1:]) | 165 | result_chord.tones.update(list(self.tones)[position+1:]) | ||
166 | return result_chord | 166 | return result_chord | ||
167 | 167 | ||||
168 | def transposed(self, interval): | 168 | def transposed(self, interval): | ||
169 | # можеше да използвам и събирането, което дефинирах за тоновете... | 169 | # можеше да използвам и събирането, което дефинирах за тоновете... | ||
170 | position = 0 | 170 | position = 0 | ||
171 | if type(interval) is int: | 171 | if type(interval) is int: | ||
172 | position = interval | 172 | position = interval | ||
173 | else: | 173 | else: | ||
174 | position = interval.data | 174 | position = interval.data | ||
175 | result_chord = Chord(self.main_tone) | 175 | result_chord = Chord(self.main_tone) | ||
176 | main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) + position | 176 | main_tone_pos = get_position_in_tuple(self.main_tone, tones_tuple) + position | ||
177 | # имаше някакви бъгове, които всеки път даваха различен main_tone, затова main тона е отделно, нищо че може да се сложи и в долния цикъл | 177 | # имаше някакви бъгове, които всеки път даваха различен main_tone, затова main тона е отделно, нищо че може да се сложи и в долния цикъл | ||
178 | if main_tone_pos < 0: | 178 | if main_tone_pos < 0: | ||
179 | main_tone_pos += len(tones_tuple) | 179 | main_tone_pos += len(tones_tuple) | ||
180 | elif main_tone_pos >= len(tones_tuple): | 180 | elif main_tone_pos >= len(tones_tuple): | ||
181 | main_tone_pos -= len(tones_tuple) | 181 | main_tone_pos -= len(tones_tuple) | ||
182 | result_chord.main_tone = tones_tuple[main_tone_pos] | 182 | result_chord.main_tone = tones_tuple[main_tone_pos] | ||
183 | # а иначе цикъла, в който пак си го има main тона попринцип, ама програмата си прави каквото си иска и всеки път дава различен резултат | 183 | # а иначе цикъла, в който пак си го има main тона попринцип, ама програмата си прави каквото си иска и всеки път дава различен резултат | ||
184 | for item in self.tones: | 184 | for item in self.tones: | ||
185 | curr_position = get_position_in_tuple(item, tones_tuple) + position | 185 | curr_position = get_position_in_tuple(item, tones_tuple) + position | ||
186 | if curr_position < 0: | 186 | if curr_position < 0: | ||
187 | curr_position += len(tones_tuple) | 187 | curr_position += len(tones_tuple) | ||
188 | elif curr_position >= len(tones_tuple): | 188 | elif curr_position >= len(tones_tuple): | ||
189 | curr_position -= len(tones_tuple) | 189 | curr_position -= len(tones_tuple) | ||
190 | result_chord.tones.add(tones_tuple[curr_position]) | 190 | result_chord.tones.add(tones_tuple[curr_position]) | ||
191 | return result_chord | 191 | return result_chord |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|