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