1VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'}
2VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
3INTERVALS = {
4 0: 'unison',
5 1: 'minor 2nd',
6 2: 'major 2nd',
7 3: 'minor 3rd',
8 4: 'major 3rd',
9 5: 'perfect 4th',
10 6: 'diminished 5th',
11 7: 'perfect 5th',
12 8: 'minor 6th',
13 9: 'major 6th',
14 10: 'minor 7th',
15 11: 'major 7th'}
16
17
18class Tone:
19 """Represents a musical tone."""
20
21 def __init__(self, note):
22 self.note = note
23
24 @property
25 def note(self):
26 return self._note
27
28 @note.setter
29 def note(self, value):
30 if value not in VALID_TONES_SET:
31 raise ValueError(f'{value} is not a valid musical tone')
32 self._note = value
33
34 def __str__(self):
35 return self._note
36
37 def __eq__(self, other):
38 if isinstance(other, Tone):
39 return self._note == other._note
40 return False
41
42 def __hash__(self):
43 return hash(self._note)
44
45 def __add__(self, other):
46 if type(other) == Tone:
47 return Chord(self, other)
48 elif type(other) == Interval:
49 tone_index = VALID_TONES_LIST.index(self._note)
50 new_index = (tone_index + other.steps) % len(VALID_TONES_LIST)
51 return Tone(VALID_TONES_LIST[new_index])
52 else:
53 raise TypeError('Invalid operation')
54
55 def __sub__(self, other):
56 if type(other) == Tone:
57 first_tone_index = VALID_TONES_LIST.index(self._note)
58 second_tone_index = VALID_TONES_LIST.index(str(other))
59 steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST)
60 return Interval(steps)
61 elif type(other) == Interval:
62 tone_index = VALID_TONES_LIST.index(self._note)
63 new_index = (tone_index - other.steps) % len(VALID_TONES_LIST)
64 return Tone(VALID_TONES_LIST[new_index])
65 else:
66 raise TypeError('Invalid operation')
67
68
69class Interval:
70 """Represents a musical interval."""
71
72 def __init__(self, steps):
73 self._steps = steps
74
75 @property
76 def steps(self):
77 return self._steps
78
79 @steps.setter
80 def steps(self, value):
81 if value < 0:
82 raise ValueError(f'{value} is not a positive number')
83
84 self._steps = value % 12
85
86 def __str__(self):
87 return INTERVALS[self._steps]
88
89 def __add__(self, other):
90 if type(other) == Interval:
91 new_steps = (self.steps + other.steps) % 12
92 return Interval(new_steps)
93
94 raise TypeError(f'Cannot add {type(other)} to Interval')
95
96 def __neg__(self):
97 return Interval(-self._steps)
98
99
100class Chord:
101 """Represents a musical chord."""
102
103 def __init__(self, main_tone, *tones):
104 self._tones = {main_tone, *tones}
105 self._main_tone = main_tone # Keep root so we don't cast to list
106 if len(self._tones) < 2:
107 raise TypeError('Cannot have a chord made of only 1 unique tone')
108
109 def __str__(self):
110 root_index = VALID_TONES_LIST.index(str(self._main_tone))
111 sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST))
112
113 return "-".join(str(tone) for tone in sorted_tones)
114
115 def __add__(self, other):
116 if type(other) == Tone:
117 new_tones = self._tones | {other} # Union of sets
118 return Chord(self._main_tone, *new_tones)
119 elif type(other) == Chord:
120 new_tones = self._tones | other._tones # Union of sets
121 new_main_tone = self._main_tone
122 return Chord(new_main_tone, *new_tones)
123 else:
124 raise TypeError('Invalid operation')
125
126 def __sub__(self, other):
127 if type(other) == Tone:
128 if other not in self._tones:
129 raise TypeError(f"Cannot remove tone {other} from chord {self}")
130
131 new_tones = self._tones - {other}
132 if len(new_tones) < 2:
133 raise TypeError("Cannot have a chord made of only 1 unique tone")
134
135 new_main_tone = next(iter(new_tones))
136 return Chord(new_main_tone, *(new_tones - {new_main_tone}))
137
138 raise TypeError(f"Cannot subtract {type(other)} from Chord")
139
140 def is_minor(self):
141 main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
142 for tone in self._tones:
143 if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3:
144 return True
145
146 return False
147
148 def is_major(self):
149 steps = 0
150 main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
151 for tone in self._tones:
152 if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4:
153 return True
154
155 return False
156
157 def is_power_chord(self):
158 if not self.is_minor() and not self.is_major():
159 return True
160
161 return False
162
163 def transposed(self, interval):
164 if type(interval) != Interval:
165 raise TypeError("Transposition requires an Interval object")
166
167 transposed_tones = set()
168 for tone in self._tones:
169 original_index = VALID_TONES_LIST.index(str(tone))
170 transposed_index = (original_index + interval.steps) % 12
171 transposed_tone = Tone(VALID_TONES_LIST[transposed_index])
172 transposed_tones.add(transposed_tone)
173
174 main_tone_index = VALID_TONES_LIST.index(str(self._main_tone))
175 transposed_main_tone_index = (main_tone_index + interval.steps) % 12
176 transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index])
177
178 return Chord(transposed_main_tone, *transposed_tones)
....FFF....EE.....F........F..F......
======================================================================
ERROR: 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")
^^^^^^^^^^^^^^
File "/tmp/solution.py", line 87, in __str__
return INTERVALS[self._steps]
~~~~~~~~~^^^^^^^^^^^^^
KeyError: -11
======================================================================
ERROR: test_interval_overflow (test.TestBasicIntervalFunctionality.test_interval_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 46, in test_interval_overflow
self.assertEqual(str(major_7th), "major 7th")
^^^^^^^^^^^^^^
File "/tmp/solution.py", line 87, in __str__
return INTERVALS[self._steps]
~~~~~~~~~^^^^^^^^^^^^^
KeyError: 155
======================================================================
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_add_interval_to_tone_left_side_error (test.TestOperations.test_add_interval_to_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 211, in test_add_interval_to_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "Cannot add <class 'solution.Tone'> to Interval" != 'Invalid operation'
- Cannot add <class 'solution.Tone'> to Interval
+ Invalid operation
======================================================================
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_subtract_tone_from_chord (test.TestOperations.test_subtract_tone_from_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 277, in test_subtract_tone_from_chord
self.assertEqual(str(result_chord), "F-G#")
AssertionError: 'G#-F' != 'F-G#'
- G#-F
+ F-G#
----------------------------------------------------------------------
Ran 37 tests in 0.002s
FAILED (failures=6, errors=2)
Владимир Коцев
05.11.2024 13:16Окей, благодаря!
|
Георги Кунчев
05.11.2024 13:12Ние няма да тестваме с невалидни данни, така че ако грешките ти се хвърлят само в такива неочаквани случаи, не би трябвало никога да стигнем до тях.
|
Владимир Коцев
05.11.2024 12:43Хвърлям допълнителни грешки спрямо условието и имайки в предвид ООП-то смятам, че трябва да бъдат покрити edge кейсовете. Ако биха били проблем за тестовете при оценяване ще ги премахна
|
| f | 1 | VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'} | f | 1 | VALID_TONES_SET = {'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'} |
| 2 | VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 2 | VALID_TONES_LIST = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
| 3 | INTERVALS = { | 3 | INTERVALS = { | ||
| 4 | 0: 'unison', | 4 | 0: 'unison', | ||
| 5 | 1: 'minor 2nd', | 5 | 1: 'minor 2nd', | ||
| 6 | 2: 'major 2nd', | 6 | 2: 'major 2nd', | ||
| 7 | 3: 'minor 3rd', | 7 | 3: 'minor 3rd', | ||
| 8 | 4: 'major 3rd', | 8 | 4: 'major 3rd', | ||
| 9 | 5: 'perfect 4th', | 9 | 5: 'perfect 4th', | ||
| 10 | 6: 'diminished 5th', | 10 | 6: 'diminished 5th', | ||
| 11 | 7: 'perfect 5th', | 11 | 7: 'perfect 5th', | ||
| 12 | 8: 'minor 6th', | 12 | 8: 'minor 6th', | ||
| 13 | 9: 'major 6th', | 13 | 9: 'major 6th', | ||
| 14 | 10: 'minor 7th', | 14 | 10: 'minor 7th', | ||
| 15 | 11: 'major 7th'} | 15 | 11: 'major 7th'} | ||
| 16 | 16 | ||||
| 17 | 17 | ||||
| 18 | class Tone: | 18 | class Tone: | ||
| 19 | """Represents a musical tone.""" | 19 | """Represents a musical tone.""" | ||
| 20 | 20 | ||||
| 21 | def __init__(self, note): | 21 | def __init__(self, note): | ||
| 22 | self.note = note | 22 | self.note = note | ||
| 23 | 23 | ||||
| 24 | @property | 24 | @property | ||
| 25 | def note(self): | 25 | def note(self): | ||
| 26 | return self._note | 26 | return self._note | ||
| 27 | 27 | ||||
| 28 | @note.setter | 28 | @note.setter | ||
| 29 | def note(self, value): | 29 | def note(self, value): | ||
| 30 | if value not in VALID_TONES_SET: | 30 | if value not in VALID_TONES_SET: | ||
| 31 | raise ValueError(f'{value} is not a valid musical tone') | 31 | raise ValueError(f'{value} is not a valid musical tone') | ||
| 32 | self._note = value | 32 | self._note = value | ||
| 33 | 33 | ||||
| 34 | def __str__(self): | 34 | def __str__(self): | ||
| 35 | return self._note | 35 | return self._note | ||
| 36 | 36 | ||||
| 37 | def __eq__(self, other): | 37 | def __eq__(self, other): | ||
| 38 | if isinstance(other, Tone): | 38 | if isinstance(other, Tone): | ||
| 39 | return self._note == other._note | 39 | return self._note == other._note | ||
| 40 | return False | 40 | return False | ||
| 41 | 41 | ||||
| 42 | def __hash__(self): | 42 | def __hash__(self): | ||
| 43 | return hash(self._note) | 43 | return hash(self._note) | ||
| 44 | 44 | ||||
| 45 | def __add__(self, other): | 45 | def __add__(self, other): | ||
| 46 | if type(other) == Tone: | 46 | if type(other) == Tone: | ||
| 47 | return Chord(self, other) | 47 | return Chord(self, other) | ||
| 48 | elif type(other) == Interval: | 48 | elif type(other) == Interval: | ||
| 49 | tone_index = VALID_TONES_LIST.index(self._note) | 49 | tone_index = VALID_TONES_LIST.index(self._note) | ||
| 50 | new_index = (tone_index + other.steps) % len(VALID_TONES_LIST) | 50 | new_index = (tone_index + other.steps) % len(VALID_TONES_LIST) | ||
| 51 | return Tone(VALID_TONES_LIST[new_index]) | 51 | return Tone(VALID_TONES_LIST[new_index]) | ||
| 52 | else: | 52 | else: | ||
| 53 | raise TypeError('Invalid operation') | 53 | raise TypeError('Invalid operation') | ||
| 54 | 54 | ||||
| 55 | def __sub__(self, other): | 55 | def __sub__(self, other): | ||
| 56 | if type(other) == Tone: | 56 | if type(other) == Tone: | ||
| 57 | first_tone_index = VALID_TONES_LIST.index(self._note) | 57 | first_tone_index = VALID_TONES_LIST.index(self._note) | ||
| 58 | second_tone_index = VALID_TONES_LIST.index(str(other)) | 58 | second_tone_index = VALID_TONES_LIST.index(str(other)) | ||
| 59 | steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST) | 59 | steps = (first_tone_index - second_tone_index) % len(VALID_TONES_LIST) | ||
| 60 | return Interval(steps) | 60 | return Interval(steps) | ||
| 61 | elif type(other) == Interval: | 61 | elif type(other) == Interval: | ||
| 62 | tone_index = VALID_TONES_LIST.index(self._note) | 62 | tone_index = VALID_TONES_LIST.index(self._note) | ||
| 63 | new_index = (tone_index - other.steps) % len(VALID_TONES_LIST) | 63 | new_index = (tone_index - other.steps) % len(VALID_TONES_LIST) | ||
| 64 | return Tone(VALID_TONES_LIST[new_index]) | 64 | return Tone(VALID_TONES_LIST[new_index]) | ||
| 65 | else: | 65 | else: | ||
| 66 | raise TypeError('Invalid operation') | 66 | raise TypeError('Invalid operation') | ||
| 67 | 67 | ||||
| 68 | 68 | ||||
| 69 | class Interval: | 69 | class Interval: | ||
| 70 | """Represents a musical interval.""" | 70 | """Represents a musical interval.""" | ||
| 71 | 71 | ||||
| 72 | def __init__(self, steps): | 72 | def __init__(self, steps): | ||
| 73 | self._steps = steps | 73 | self._steps = steps | ||
| 74 | 74 | ||||
| 75 | @property | 75 | @property | ||
| 76 | def steps(self): | 76 | def steps(self): | ||
| 77 | return self._steps | 77 | return self._steps | ||
| 78 | 78 | ||||
| 79 | @steps.setter | 79 | @steps.setter | ||
| 80 | def steps(self, value): | 80 | def steps(self, value): | ||
| 81 | if value < 0: | 81 | if value < 0: | ||
| 82 | raise ValueError(f'{value} is not a positive number') | 82 | raise ValueError(f'{value} is not a positive number') | ||
| 83 | 83 | ||||
| 84 | self._steps = value % 12 | 84 | self._steps = value % 12 | ||
| 85 | 85 | ||||
| 86 | def __str__(self): | 86 | def __str__(self): | ||
| 87 | return INTERVALS[self._steps] | 87 | return INTERVALS[self._steps] | ||
| 88 | 88 | ||||
| 89 | def __add__(self, other): | 89 | def __add__(self, other): | ||
| 90 | if type(other) == Interval: | 90 | if type(other) == Interval: | ||
| 91 | new_steps = (self.steps + other.steps) % 12 | 91 | new_steps = (self.steps + other.steps) % 12 | ||
| 92 | return Interval(new_steps) | 92 | return Interval(new_steps) | ||
| 93 | 93 | ||||
| 94 | raise TypeError(f'Cannot add {type(other)} to Interval') | 94 | raise TypeError(f'Cannot add {type(other)} to Interval') | ||
| 95 | 95 | ||||
| 96 | def __neg__(self): | 96 | def __neg__(self): | ||
| 97 | return Interval(-self._steps) | 97 | return Interval(-self._steps) | ||
| 98 | 98 | ||||
| 99 | 99 | ||||
| 100 | class Chord: | 100 | class Chord: | ||
| 101 | """Represents a musical chord.""" | 101 | """Represents a musical chord.""" | ||
| 102 | 102 | ||||
| 103 | def __init__(self, main_tone, *tones): | 103 | def __init__(self, main_tone, *tones): | ||
| 104 | self._tones = {main_tone, *tones} | 104 | self._tones = {main_tone, *tones} | ||
| 105 | self._main_tone = main_tone # Keep root so we don't cast to list | 105 | self._main_tone = main_tone # Keep root so we don't cast to list | ||
| 106 | if len(self._tones) < 2: | 106 | if len(self._tones) < 2: | ||
| 107 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 107 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
| 108 | 108 | ||||
| 109 | def __str__(self): | 109 | def __str__(self): | ||
| 110 | root_index = VALID_TONES_LIST.index(str(self._main_tone)) | 110 | root_index = VALID_TONES_LIST.index(str(self._main_tone)) | ||
| 111 | sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST)) | 111 | sorted_tones = sorted(self._tones, key=lambda tone: (VALID_TONES_LIST.index(str(tone)) - root_index) % len(VALID_TONES_LIST)) | ||
| 112 | 112 | ||||
| 113 | return "-".join(str(tone) for tone in sorted_tones) | 113 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 114 | 114 | ||||
| 115 | def __add__(self, other): | 115 | def __add__(self, other): | ||
| 116 | if type(other) == Tone: | 116 | if type(other) == Tone: | ||
| 117 | new_tones = self._tones | {other} # Union of sets | 117 | new_tones = self._tones | {other} # Union of sets | ||
| 118 | return Chord(self._main_tone, *new_tones) | 118 | return Chord(self._main_tone, *new_tones) | ||
| 119 | elif type(other) == Chord: | 119 | elif type(other) == Chord: | ||
| 120 | new_tones = self._tones | other._tones # Union of sets | 120 | new_tones = self._tones | other._tones # Union of sets | ||
| 121 | new_main_tone = self._main_tone | 121 | new_main_tone = self._main_tone | ||
| 122 | return Chord(new_main_tone, *new_tones) | 122 | return Chord(new_main_tone, *new_tones) | ||
| 123 | else: | 123 | else: | ||
| 124 | raise TypeError('Invalid operation') | 124 | raise TypeError('Invalid operation') | ||
| 125 | 125 | ||||
| 126 | def __sub__(self, other): | 126 | def __sub__(self, other): | ||
| 127 | if type(other) == Tone: | 127 | if type(other) == Tone: | ||
| 128 | if other not in self._tones: | 128 | if other not in self._tones: | ||
| 129 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 129 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 130 | 130 | ||||
| 131 | new_tones = self._tones - {other} | 131 | new_tones = self._tones - {other} | ||
| 132 | if len(new_tones) < 2: | 132 | if len(new_tones) < 2: | ||
| 133 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 133 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 134 | 134 | ||||
| 135 | new_main_tone = next(iter(new_tones)) | 135 | new_main_tone = next(iter(new_tones)) | ||
| 136 | return Chord(new_main_tone, *(new_tones - {new_main_tone})) | 136 | return Chord(new_main_tone, *(new_tones - {new_main_tone})) | ||
| 137 | 137 | ||||
| 138 | raise TypeError(f"Cannot subtract {type(other)} from Chord") | 138 | raise TypeError(f"Cannot subtract {type(other)} from Chord") | ||
| 139 | 139 | ||||
| 140 | def is_minor(self): | 140 | def is_minor(self): | ||
| 141 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | 141 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | ||
| 142 | for tone in self._tones: | 142 | for tone in self._tones: | ||
| 143 | if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3: | 143 | if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 3: | ||
| 144 | return True | 144 | return True | ||
| 145 | 145 | ||||
| 146 | return False | 146 | return False | ||
| 147 | 147 | ||||
| 148 | def is_major(self): | 148 | def is_major(self): | ||
| 149 | steps = 0 | 149 | steps = 0 | ||
| 150 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | 150 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | ||
| 151 | for tone in self._tones: | 151 | for tone in self._tones: | ||
| 152 | if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4: | 152 | if VALID_TONES_LIST.index(str(tone)) - main_tone_index == 4: | ||
| 153 | return True | 153 | return True | ||
| 154 | 154 | ||||
| 155 | return False | 155 | return False | ||
| 156 | 156 | ||||
| 157 | def is_power_chord(self): | 157 | def is_power_chord(self): | ||
| 158 | if not self.is_minor() and not self.is_major(): | 158 | if not self.is_minor() and not self.is_major(): | ||
| 159 | return True | 159 | return True | ||
| 160 | 160 | ||||
| 161 | return False | 161 | return False | ||
| 162 | 162 | ||||
| 163 | def transposed(self, interval): | 163 | def transposed(self, interval): | ||
| 164 | if type(interval) != Interval: | 164 | if type(interval) != Interval: | ||
| 165 | raise TypeError("Transposition requires an Interval object") | 165 | raise TypeError("Transposition requires an Interval object") | ||
| 166 | 166 | ||||
| 167 | transposed_tones = set() | 167 | transposed_tones = set() | ||
| 168 | for tone in self._tones: | 168 | for tone in self._tones: | ||
| 169 | original_index = VALID_TONES_LIST.index(str(tone)) | 169 | original_index = VALID_TONES_LIST.index(str(tone)) | ||
| 170 | transposed_index = (original_index + interval.steps) % 12 | 170 | transposed_index = (original_index + interval.steps) % 12 | ||
| 171 | transposed_tone = Tone(VALID_TONES_LIST[transposed_index]) | 171 | transposed_tone = Tone(VALID_TONES_LIST[transposed_index]) | ||
| 172 | transposed_tones.add(transposed_tone) | 172 | transposed_tones.add(transposed_tone) | ||
| 173 | 173 | ||||
| 174 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | 174 | main_tone_index = VALID_TONES_LIST.index(str(self._main_tone)) | ||
| 175 | transposed_main_tone_index = (main_tone_index + interval.steps) % 12 | 175 | transposed_main_tone_index = (main_tone_index + interval.steps) % 12 | ||
| 176 | transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index]) | 176 | transposed_main_tone = Tone(VALID_TONES_LIST[transposed_main_tone_index]) | ||
| 177 | 177 | ||||
| 178 | return Chord(transposed_main_tone, *transposed_tones) | 178 | return Chord(transposed_main_tone, *transposed_tones) | ||
| t | 179 | t | |||
| 180 | c_major_chord = Chord(Tone("C"), Tone("E"), Tone("G")) | ||||
| 181 | result_chord = c_major_chord - Tone("E") | ||||
| 182 | print(str(result_chord)) | ||||
| 183 | #self.assertEqual(str(result_chord), "C-G") |
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
| |||||||||
06.11.2024 13:57
06.11.2024 13:56
06.11.2024 13:59
06.11.2024 14:00
06.11.2024 14:03
06.11.2024 14:08
06.11.2024 14:11
06.11.2024 14:12