1class Tone:
2 VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes
3
4 def __init__(self, tone):
5 self.tone = tone
6
7 def __str__(self):
8 return str(self.tone)
9
10 def __eq__(self, other):
11 return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone)
12
13 def __hash__(self):
14 return hash(self.tone)
15
16 def __and__(self, other): # using it for the major and minor functions for convenience
17 return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone))
18
19 def __add__(self, other):
20 if isinstance(other, Tone):
21 return Chord (self, other)
22 elif isinstance(other, Interval):
23 new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12
24 new_tone = self.VALID_NOTES[new_tone_index]
25 return Tone(new_tone)
26 else:
27 raise TypeError("Invalid operation")
28
29 def __sub__(self, other):
30 if isinstance(other, Tone):
31 return Interval(self&other)
32 elif isinstance(other, Interval):
33 new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12
34 new_tone = self.VALID_NOTES[new_tone_index]
35 return Tone(new_tone)
36 else:
37 raise TypeError("Invalid operation")
38
39
40class Interval:
41 SEMITONES = {
42 0: "unison",
43 1: "minor 2nd",
44 2: "major 2nd",
45 3: "minor 3rd",
46 4: "major 3rd",
47 5: "perfect 4th",
48 6: "diminished 5th",
49 7: "perfect 5th",
50 8: "minor 6th",
51 9: "major 6th",
52 10: "minor 7th",
53 11: "major 7th"
54 }
55
56 def __init__(self, number_of_semitones):
57 self.number_of_semitones = number_of_semitones % 12
58
59 def __str__(self):
60 return self.SEMITONES[self.number_of_semitones]
61
62 def __add__(self, other):
63 if isinstance(other, Tone):
64 raise TypeError ("Invalid operation")
65 elif isinstance(other, Interval):
66 return Interval(self.number_of_semitones + other.number_of_semitones)
67
68 def __neg__(self):
69 return Interval(-self.number_of_semitones)
70
71
72class Chord:
73 CHROMATIC_SCALE = Tone.VALID_NOTES
74
75 def __init__(self, main_tone, *tones):
76 self.main_tone = main_tone
77 #remove duplicates by first using a set and then back to a list
78 unique_tones = list(set(tones) | {main_tone})
79 if len(unique_tones) < 2:
80 raise TypeError("Cannot have a chord made of only 1 unique tone")
81 self.tones = unique_tones
82
83 def __str__(self):
84 to_return = str(self.main_tone)
85 main_tone_idx = self.CHROMATIC_SCALE.index(to_return)
86 tone_str = {str(tone) for tone in self.tones}
87 # sorting based on the main tone, NOT based on the indexation in Valid_Tones
88 for tone in Tone.VALID_NOTES[main_tone_idx + 1:]:
89 if tone in tone_str:
90 to_return += "-" + tone
91 for tone in Tone.VALID_NOTES[:main_tone_idx]:
92 if tone in tone_str:
93 to_return += "-" + tone
94 return to_return
95
96 def is_minor(self):
97 for t in self.tones:
98 if self.main_tone != t and (self.main_tone & t) == 3: # 3 is equal to the index of minor 3rd
99 return True
100 return False
101
102 def is_major(self):
103 for t in self.tones:
104 if self.main_tone != t and (self.main_tone & t) == 4: # 4 is equal to the index of major 3rd
105 return True
106 return False
107
108 def is_power_chord(self):
109 is_it_minor = self.is_minor()
110 is_it_major = self.is_major()
111 return not (is_it_major or is_it_minor)
112
113 def __add__(self, other):
114 if isinstance(other, Tone):
115 return Chord(self.main_tone, *(self.tones + [other]))
116 elif isinstance(other, Chord):
117 return Chord(self.main_tone, *(self.tones+other.tones))
118
119 def __sub__(self, other):
120 if isinstance(other, Tone) and other in self.tones:
121 if len(self.tones) >= 3:
122 other_index = self.tones.index(other)
123 return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index]))
124 else:
125 raise TypeError("Cannot have a chord made of only 1 unique tone")
126 else:
127 raise TypeError(f"Cannot remove tone {other} from chord {self}")
128
129 def transposed(self, interval):
130 to_transpose = list()
131 to_transpose.append(self.main_tone+interval)
132 for t in self.tones:
133 to_transpose.append(t + interval)
134 return Chord(*to_transpose)
....FFF....................F.......F.
======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_is_major
self.assertTrue(a_major_chord.is_major())
AssertionError: False is not true
======================================================================
FAIL: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_is_minor
self.assertTrue(a_minor_chord.is_minor())
AssertionError: False is not true
======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_is_power_chord
self.assertFalse(a_minor_chord.is_power_chord())
AssertionError: True is not false
======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation
======================================================================
FAIL: test_tone_subtraction_inverse (test.TestOperations.test_tone_subtraction_inverse)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 178, in test_tone_subtraction_inverse
self.assertEqual(str(perfect_4th), "perfect 4th")
AssertionError: 'perfect 5th' != 'perfect 4th'
- perfect 5th
? ^
+ perfect 4th
? ^
----------------------------------------------------------------------
Ran 37 tests in 0.002s
FAILED (failures=5)
Виктор Бечев
01.11.2024 19:03Както споменаваме често - ползвайте каквото ви е кеф в стандартната библиотека.
`__hash__` е стандартен дъндър от обектният протокол, така че ползвай на воля. :)
|
Екатерина Стоянова
31.10.2024 17:30Благодаря Ви! Аз иначе открих проблема - Tone не е "hashable" по подразбиране и затова не е тръгвало. Проблем ли е, че ще ползвам функцията _ _hash_ _(), понеже я видях в GeeksForGeeks, но поне в презентациите не я намерих?
|
Виктор Бечев
31.10.2024 16:45Ако правилно съм те разбрал, то проблемът ти се свежда до ниво на абстракция, което не е проблем да обсъдим.
Как да сортирам тоновете, без да дефинирам `__eq__` _(перифразирам твоят въпрос)_?
Това, което имаш по-горе е сравнително on-point - сортирането би следвало да стане чрез атрибутът `tone` на обектите от тип `Tone`. Тоновете ти са в списък и те не са сортирани. Сортирането става чак при репрезентацията като стринг, където е и въпросната `lambda` функция, която често се ползва по този начин за да "зададе" алгоритъм за сортиране на `sorted` метода. В случая просто взима въпросния `tone` атрибут и прави изместването, всичко от което на теория изглежда правилно.
Ако решиш да дефинираш `__eq__`, ще можеш да си позволиш сортиране без тази ламбда, съответно "стандартната" употреба на `sorted` ще работи прекрасно, ако самото сравнение имплементираш в `__eq__`.
Не мога на прима виста да ти кажа защо не принтира правилните стойности, а и това вече би било равнозначно на това да дебъгваме твоя код, което вече няма да е справедливо.
|
Екатерина Стоянова
31.10.2024 13:44Здравейте! Извинявам се, че Ви занимавам, но реших, че това ще бъде по-добра стратегия, отколкото да използвам ChatBot-а. Качих нарочно невалидно и (очевидно) незавършено домашно, за да Ви попитам по него. Проблемът ми идва в това как кода "уж" слага отделните тонове в лист, така че да няма повторения и с (малко помощ от чат-бота), после сортирам спрямо индекс на валидните стойности, които съм дала. Въпросът ми е - тук нужно ли е да се предефинира __eq__ оператора, защото дори и когато го направих, пак при генерираните от бота тестове, принтеше грешно. Знам, че това може да се приеме като все едно ми помагате с домашното, така че разбирам, ако не ми отговорите, просто сама се обърках в собствения си код, а реално искам да разбера проблема 😭
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes | 2 | VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes | ||
3 | 3 | ||||
4 | def __init__(self, tone): | 4 | def __init__(self, tone): | ||
5 | self.tone = tone | 5 | self.tone = tone | ||
6 | 6 | ||||
7 | def __str__(self): | 7 | def __str__(self): | ||
8 | return str(self.tone) | 8 | return str(self.tone) | ||
9 | 9 | ||||
10 | def __eq__(self, other): | 10 | def __eq__(self, other): | ||
11 | return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone) | 11 | return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone) | ||
12 | 12 | ||||
13 | def __hash__(self): | 13 | def __hash__(self): | ||
14 | return hash(self.tone) | 14 | return hash(self.tone) | ||
15 | 15 | ||||
16 | def __and__(self, other): # using it for the major and minor functions for convenience | 16 | def __and__(self, other): # using it for the major and minor functions for convenience | ||
17 | return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone)) | 17 | return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone)) | ||
18 | 18 | ||||
19 | def __add__(self, other): | 19 | def __add__(self, other): | ||
20 | if isinstance(other, Tone): | 20 | if isinstance(other, Tone): | ||
21 | return Chord (self, other) | 21 | return Chord (self, other) | ||
22 | elif isinstance(other, Interval): | 22 | elif isinstance(other, Interval): | ||
23 | new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12 | 23 | new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12 | ||
24 | new_tone = self.VALID_NOTES[new_tone_index] | 24 | new_tone = self.VALID_NOTES[new_tone_index] | ||
25 | return Tone(new_tone) | 25 | return Tone(new_tone) | ||
26 | else: | 26 | else: | ||
27 | raise TypeError("Invalid operation") | 27 | raise TypeError("Invalid operation") | ||
28 | 28 | ||||
29 | def __sub__(self, other): | 29 | def __sub__(self, other): | ||
30 | if isinstance(other, Tone): | 30 | if isinstance(other, Tone): | ||
31 | return Interval(self&other) | 31 | return Interval(self&other) | ||
32 | elif isinstance(other, Interval): | 32 | elif isinstance(other, Interval): | ||
33 | new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12 | 33 | new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12 | ||
34 | new_tone = self.VALID_NOTES[new_tone_index] | 34 | new_tone = self.VALID_NOTES[new_tone_index] | ||
35 | return Tone(new_tone) | 35 | return Tone(new_tone) | ||
36 | else: | 36 | else: | ||
37 | raise TypeError("Invalid operation") | 37 | raise TypeError("Invalid operation") | ||
38 | 38 | ||||
39 | 39 | ||||
40 | class Interval: | 40 | class Interval: | ||
41 | SEMITONES = { | 41 | SEMITONES = { | ||
42 | 0: "unison", | 42 | 0: "unison", | ||
43 | 1: "minor 2nd", | 43 | 1: "minor 2nd", | ||
44 | 2: "major 2nd", | 44 | 2: "major 2nd", | ||
45 | 3: "minor 3rd", | 45 | 3: "minor 3rd", | ||
46 | 4: "major 3rd", | 46 | 4: "major 3rd", | ||
47 | 5: "perfect 4th", | 47 | 5: "perfect 4th", | ||
48 | 6: "diminished 5th", | 48 | 6: "diminished 5th", | ||
49 | 7: "perfect 5th", | 49 | 7: "perfect 5th", | ||
50 | 8: "minor 6th", | 50 | 8: "minor 6th", | ||
51 | 9: "major 6th", | 51 | 9: "major 6th", | ||
52 | 10: "minor 7th", | 52 | 10: "minor 7th", | ||
53 | 11: "major 7th" | 53 | 11: "major 7th" | ||
54 | } | 54 | } | ||
55 | 55 | ||||
56 | def __init__(self, number_of_semitones): | 56 | def __init__(self, number_of_semitones): | ||
57 | self.number_of_semitones = number_of_semitones % 12 | 57 | self.number_of_semitones = number_of_semitones % 12 | ||
58 | 58 | ||||
59 | def __str__(self): | 59 | def __str__(self): | ||
60 | return self.SEMITONES[self.number_of_semitones] | 60 | return self.SEMITONES[self.number_of_semitones] | ||
61 | 61 | ||||
62 | def __add__(self, other): | 62 | def __add__(self, other): | ||
63 | if isinstance(other, Tone): | 63 | if isinstance(other, Tone): | ||
64 | raise TypeError ("Invalid operation") | 64 | raise TypeError ("Invalid operation") | ||
65 | elif isinstance(other, Interval): | 65 | elif isinstance(other, Interval): | ||
66 | return Interval(self.number_of_semitones + other.number_of_semitones) | 66 | return Interval(self.number_of_semitones + other.number_of_semitones) | ||
67 | 67 | ||||
68 | def __neg__(self): | 68 | def __neg__(self): | ||
69 | return Interval(-self.number_of_semitones) | 69 | return Interval(-self.number_of_semitones) | ||
70 | 70 | ||||
t | t | 71 | |||
71 | class Chord: | 72 | class Chord: | ||
72 | CHROMATIC_SCALE = Tone.VALID_NOTES | 73 | CHROMATIC_SCALE = Tone.VALID_NOTES | ||
73 | 74 | ||||
74 | def __init__(self, main_tone, *tones): | 75 | def __init__(self, main_tone, *tones): | ||
75 | self.main_tone = main_tone | 76 | self.main_tone = main_tone | ||
76 | #remove duplicates by first using a set and then back to a list | 77 | #remove duplicates by first using a set and then back to a list | ||
77 | unique_tones = list(set(tones) | {main_tone}) | 78 | unique_tones = list(set(tones) | {main_tone}) | ||
78 | if len(unique_tones) < 2: | 79 | if len(unique_tones) < 2: | ||
79 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
80 | self.tones = unique_tones | 81 | self.tones = unique_tones | ||
81 | 82 | ||||
82 | def __str__(self): | 83 | def __str__(self): | ||
83 | to_return = str(self.main_tone) | 84 | to_return = str(self.main_tone) | ||
84 | main_tone_idx = self.CHROMATIC_SCALE.index(to_return) | 85 | main_tone_idx = self.CHROMATIC_SCALE.index(to_return) | ||
85 | tone_str = {str(tone) for tone in self.tones} | 86 | tone_str = {str(tone) for tone in self.tones} | ||
86 | # sorting based on the main tone, NOT based on the indexation in Valid_Tones | 87 | # sorting based on the main tone, NOT based on the indexation in Valid_Tones | ||
87 | for tone in Tone.VALID_NOTES[main_tone_idx + 1:]: | 88 | for tone in Tone.VALID_NOTES[main_tone_idx + 1:]: | ||
88 | if tone in tone_str: | 89 | if tone in tone_str: | ||
89 | to_return += "-" + tone | 90 | to_return += "-" + tone | ||
90 | for tone in Tone.VALID_NOTES[:main_tone_idx]: | 91 | for tone in Tone.VALID_NOTES[:main_tone_idx]: | ||
91 | if tone in tone_str: | 92 | if tone in tone_str: | ||
92 | to_return += "-" + tone | 93 | to_return += "-" + tone | ||
93 | return to_return | 94 | return to_return | ||
94 | 95 | ||||
95 | def is_minor(self): | 96 | def is_minor(self): | ||
96 | for t in self.tones: | 97 | for t in self.tones: | ||
97 | if self.main_tone != t and (self.main_tone & t) == 3: # 3 is equal to the index of minor 3rd | 98 | if self.main_tone != t and (self.main_tone & t) == 3: # 3 is equal to the index of minor 3rd | ||
98 | return True | 99 | return True | ||
99 | return False | 100 | return False | ||
100 | 101 | ||||
101 | def is_major(self): | 102 | def is_major(self): | ||
102 | for t in self.tones: | 103 | for t in self.tones: | ||
103 | if self.main_tone != t and (self.main_tone & t) == 4: # 4 is equal to the index of major 3rd | 104 | if self.main_tone != t and (self.main_tone & t) == 4: # 4 is equal to the index of major 3rd | ||
104 | return True | 105 | return True | ||
105 | return False | 106 | return False | ||
106 | 107 | ||||
107 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
108 | is_it_minor = self.is_minor() | 109 | is_it_minor = self.is_minor() | ||
109 | is_it_major = self.is_major() | 110 | is_it_major = self.is_major() | ||
110 | return not (is_it_major or is_it_minor) | 111 | return not (is_it_major or is_it_minor) | ||
111 | 112 | ||||
112 | def __add__(self, other): | 113 | def __add__(self, other): | ||
113 | if isinstance(other, Tone): | 114 | if isinstance(other, Tone): | ||
114 | return Chord(self.main_tone, *(self.tones + [other])) | 115 | return Chord(self.main_tone, *(self.tones + [other])) | ||
115 | elif isinstance(other, Chord): | 116 | elif isinstance(other, Chord): | ||
116 | return Chord(self.main_tone, *(self.tones+other.tones)) | 117 | return Chord(self.main_tone, *(self.tones+other.tones)) | ||
117 | 118 | ||||
118 | def __sub__(self, other): | 119 | def __sub__(self, other): | ||
119 | if isinstance(other, Tone) and other in self.tones: | 120 | if isinstance(other, Tone) and other in self.tones: | ||
120 | if len(self.tones) >= 3: | 121 | if len(self.tones) >= 3: | ||
121 | other_index = self.tones.index(other) | 122 | other_index = self.tones.index(other) | ||
122 | return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index])) | 123 | return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index])) | ||
123 | else: | 124 | else: | ||
124 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
125 | else: | 126 | else: | ||
126 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 127 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
127 | 128 | ||||
128 | def transposed(self, interval): | 129 | def transposed(self, interval): | ||
129 | to_transpose = list() | 130 | to_transpose = list() | ||
130 | to_transpose.append(self.main_tone+interval) | 131 | to_transpose.append(self.main_tone+interval) | ||
131 | for t in self.tones: | 132 | for t in self.tones: | ||
132 | to_transpose.append(t + interval) | 133 | to_transpose.append(t + interval) | ||
133 | return Chord(*to_transpose) | 134 | return Chord(*to_transpose) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
n | 2 | VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] #arr to use the indexes | n | 2 | VALID_NOTES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] # list to use the indexes |
3 | 3 | ||||
4 | def __init__(self, tone): | 4 | def __init__(self, tone): | ||
n | 5 | if tone not in self.VALID_NOTES: | n | ||
6 | raise TypeError("nqkakuv text lol") | ||||
7 | self.tone = tone | 5 | self.tone = tone | ||
8 | 6 | ||||
9 | def __str__(self): | 7 | def __str__(self): | ||
n | 10 | return self.tone | n | 8 | return str(self.tone) |
9 | |||||
10 | def __eq__(self, other): | ||||
11 | return self.VALID_NOTES.index(self.tone) == self.VALID_NOTES.index(other.tone) | ||||
12 | |||||
13 | def __hash__(self): | ||||
14 | return hash(self.tone) | ||||
15 | |||||
16 | def __and__(self, other): # using it for the major and minor functions for convenience | ||||
17 | return abs(self.VALID_NOTES.index(self.tone) - self.VALID_NOTES.index(other.tone)) | ||||
18 | |||||
19 | def __add__(self, other): | ||||
20 | if isinstance(other, Tone): | ||||
21 | return Chord (self, other) | ||||
22 | elif isinstance(other, Interval): | ||||
23 | new_tone_index = (self.VALID_NOTES.index(self.tone) + other.number_of_semitones) % 12 | ||||
24 | new_tone = self.VALID_NOTES[new_tone_index] | ||||
25 | return Tone(new_tone) | ||||
26 | else: | ||||
27 | raise TypeError("Invalid operation") | ||||
28 | |||||
29 | def __sub__(self, other): | ||||
30 | if isinstance(other, Tone): | ||||
31 | return Interval(self&other) | ||||
32 | elif isinstance(other, Interval): | ||||
33 | new_tone_index = (self.VALID_NOTES.index(self.tone) - other.number_of_semitones) % 12 | ||||
34 | new_tone = self.VALID_NOTES[new_tone_index] | ||||
35 | return Tone(new_tone) | ||||
36 | else: | ||||
37 | raise TypeError("Invalid operation") | ||||
11 | 38 | ||||
12 | 39 | ||||
13 | class Interval: | 40 | class Interval: | ||
14 | SEMITONES = { | 41 | SEMITONES = { | ||
15 | 0: "unison", | 42 | 0: "unison", | ||
16 | 1: "minor 2nd", | 43 | 1: "minor 2nd", | ||
17 | 2: "major 2nd", | 44 | 2: "major 2nd", | ||
18 | 3: "minor 3rd", | 45 | 3: "minor 3rd", | ||
19 | 4: "major 3rd", | 46 | 4: "major 3rd", | ||
20 | 5: "perfect 4th", | 47 | 5: "perfect 4th", | ||
21 | 6: "diminished 5th", | 48 | 6: "diminished 5th", | ||
22 | 7: "perfect 5th", | 49 | 7: "perfect 5th", | ||
23 | 8: "minor 6th", | 50 | 8: "minor 6th", | ||
24 | 9: "major 6th", | 51 | 9: "major 6th", | ||
25 | 10: "minor 7th", | 52 | 10: "minor 7th", | ||
26 | 11: "major 7th" | 53 | 11: "major 7th" | ||
27 | } | 54 | } | ||
n | n | 55 | |||
28 | def __init__(self, number_of_semitones): | 56 | def __init__(self, number_of_semitones): | ||
29 | self.number_of_semitones = number_of_semitones % 12 | 57 | self.number_of_semitones = number_of_semitones % 12 | ||
30 | 58 | ||||
31 | def __str__(self): | 59 | def __str__(self): | ||
32 | return self.SEMITONES[self.number_of_semitones] | 60 | return self.SEMITONES[self.number_of_semitones] | ||
33 | 61 | ||||
n | n | 62 | def __add__(self, other): | ||
63 | if isinstance(other, Tone): | ||||
64 | raise TypeError ("Invalid operation") | ||||
65 | elif isinstance(other, Interval): | ||||
66 | return Interval(self.number_of_semitones + other.number_of_semitones) | ||||
67 | |||||
68 | def __neg__(self): | ||||
69 | return Interval(-self.number_of_semitones) | ||||
34 | 70 | ||||
35 | class Chord: | 71 | class Chord: | ||
36 | CHROMATIC_SCALE = Tone.VALID_NOTES | 72 | CHROMATIC_SCALE = Tone.VALID_NOTES | ||
37 | 73 | ||||
38 | def __init__(self, main_tone, *tones): | 74 | def __init__(self, main_tone, *tones): | ||
39 | self.main_tone = main_tone | 75 | self.main_tone = main_tone | ||
n | 40 | #remove duplicates by fisr using a set and then back to a list | n | 76 | #remove duplicates by first using a set and then back to a list |
41 | unique_tones = {main_tone} | set(tones) | 77 | unique_tones = list(set(tones) | {main_tone}) | ||
42 | if len(unique_tones) < 2: | 78 | if len(unique_tones) < 2: | ||
43 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 79 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
n | 44 | self.tones = list(unique_tones) #this way we dont have duplicates and still can sort | n | 80 | self.tones = unique_tones |
45 | 81 | ||||
46 | def __str__(self): | 82 | def __str__(self): | ||
n | 47 | #get the index of the 'root' | n | 83 | to_return = str(self.main_tone) |
48 | main_tone_idx = self.CHROMATIC_SCALE.index(self.main_tone.tone) | 84 | main_tone_idx = self.CHROMATIC_SCALE.index(to_return) | ||
49 | sorted_tones = sorted( | 85 | tone_str = {str(tone) for tone in self.tones} | ||
50 | self.tones, | 86 | # sorting based on the main tone, NOT based on the indexation in Valid_Tones | ||
51 | key = lambda t: (self.CHROMATIC_SCALE.index(t.tone) - main_tone_idx) % 12 | 87 | for tone in Tone.VALID_NOTES[main_tone_idx + 1:]: | ||
52 | ) #asked for help from a friend for this (aka chat-bota) :~( | 88 | if tone in tone_str: | ||
53 | return "-".join(str(tone) for tone in sorted_tones) | 89 | to_return += "-" + tone | ||
90 | for tone in Tone.VALID_NOTES[:main_tone_idx]: | ||||
91 | if tone in tone_str: | ||||
92 | to_return += "-" + tone | ||||
93 | return to_return | ||||
54 | 94 | ||||
n | n | 95 | def is_minor(self): | ||
96 | for t in self.tones: | ||||
97 | if self.main_tone != t and (self.main_tone & t) == 3: # 3 is equal to the index of minor 3rd | ||||
98 | return True | ||||
99 | return False | ||||
55 | 100 | ||||
n | 56 | c = Tone("C") | n | 101 | def is_major(self): |
57 | d_sharp = Tone("D#") | 102 | for t in self.tones: | ||
58 | g = Tone("G") | 103 | if self.main_tone != t and (self.main_tone & t) == 4: # 4 is equal to the index of major 3rd | ||
104 | return True | ||||
105 | return False | ||||
59 | 106 | ||||
n | 60 | c_minor_chord = Chord(c, d_sharp, g) | n | 107 | def is_power_chord(self): |
61 | print(str(c_minor_chord)) # Output should be "C-D#-G" | 108 | is_it_minor = self.is_minor() | ||
109 | is_it_major = self.is_major() | ||||
110 | return not (is_it_major or is_it_minor) | ||||
62 | 111 | ||||
n | 63 | c, another_c, f = Tone("C"), Tone("C"), Tone("F") | n | 112 | def __add__(self, other): |
64 | csus4_chord = Chord(c, f, another_c) | 113 | if isinstance(other, Tone): | ||
65 | print(str(csus4_chord)) # Output should be "C-F" -> printi C-C-F | 114 | return Chord(self.main_tone, *(self.tones + [other])) | ||
115 | elif isinstance(other, Chord): | ||||
116 | return Chord(self.main_tone, *(self.tones+other.tones)) | ||||
66 | 117 | ||||
n | 67 | f, c, d, a, g = Tone("F"), Tone("C"), Tone("D"), Tone("A"), Tone("G") | n | 118 | def __sub__(self, other): |
68 | f_sixth_ninth_chord = Chord(f, c, d, a, g) | 119 | if isinstance(other, Tone) and other in self.tones: | ||
69 | print(str(f_sixth_ninth_chord)) # Output should be "F-G-A-C-D" | 120 | if len(self.tones) >= 3: | ||
121 | other_index = self.tones.index(other) | ||||
122 | return Chord(self.main_tone, *(self.tones[other_index + 1:] + self.tones[:other_index])) | ||||
123 | else: | ||||
124 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||||
125 | else: | ||||
126 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||||
70 | 127 | ||||
t | t | 128 | def transposed(self, interval): | ||
129 | to_transpose = list() | ||||
130 | to_transpose.append(self.main_tone+interval) | ||||
131 | for t in self.tones: | ||||
132 | to_transpose.append(t + interval) | ||||
133 | return Chord(*to_transpose) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
04.11.2024 14:20
04.11.2024 14:20
04.11.2024 14:21
04.11.2024 14:22
04.11.2024 14:23
04.11.2024 14:24
04.11.2024 14:25