1class Tone:
2 """This is the class for Tones."""
3 TONES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
4
5 def __init__(self, name):
6 if name not in Tone.TONES:
7 raise ValueError(f"Invalid tone {name}.")
8 self.name = name
9
10 def __str__(self):
11 return self.name
12
13 def __add__(self, interval):
14 if isinstance(interval, Interval):
15 index = Tone.TONES.index(self.name)
16 new_index = ((index + interval.name) % len(Tone.TONES))
17 return Tone(Tone.TONES[new_index])
18 else:
19 raise TypeError("Invalid operation.")
20
21 def __sub__(self, interval):
22 if isinstance(interval, Interval):
23 index = Tone. TONES.index(self.name)
24 index_other = (index - interval.name) % len(Tone.TONE_ORDER)
25 return Tone(Tone.TONES[index_other])
26 else:
27 raise TypeError("Invalid operation.")
28
29 def __eq__(self, other):
30 if isinstance(other, Tone):
31 return self.name == other.name
32 return False
33
34 def __hash__(self):
35 return hash(self.note)
36
37 def __lt__(self, other):
38 if isinstance(other, Tone):
39 return Tone.TONE_ORDER.index(self.note) < Tone.TONE_ORDER.index(other.note)
40 return NotImplemented
41
42
43class Interval:
44 """This is the class for intervals."""
45 INTERVALS = {0:"unison", 1:"minor 2nd", 2:"major 2nd", 3:"minor 3rd", 4:"major 3rd", 5:"perfect 4th", 6:"diminished 5th",
46 7:"perfect 5th", 8:"minor 6th", 9:"major 6th", 10:"minor 7th", 11:"major 7th"}
47
48 def __init__(self, name):
49 if not isinstance(name, int):
50 raise ValueError("Interval must me an integer.")
51 self.name = name % 12
52
53 def __str__(self):
54 return Interval.INTERVALS[self.name]
55
56 def __eq__(self, other):
57 if isinstance(other, Interval):
58 return self.name == other.name
59 return False
60
61 def __hash__(self):
62 return hash(self.name)
63
64
65class Chord:
66 """This is the class for chords."""
67 def __init__(self, root, *tones):
68 if not isinstance(root, Tone):
69 raise ValueError("Root must be a Tone instance.")
70 self.tones = sorted(set([root] + list(tones)))
71
72 def __str__(self):
73 return "-".join(tone.name for tone in self.tones)
74
75 def is_major(self):
76 if len(self.tones) != 3:
77 return False
78 root, third, fifth = self.tones[0], self.tones[1], self.tones[2]
79 return (
80 Tone.TONE_ORDER.index(third.note) - Tone.TONE_ORDER.index(root.note) == 4 and
81 Tone.TONE_ORDER.index(fifth.note) - Tone.TONE_ORDER.index(third.note) == 3
82 )
83
84 def is_minor(self):
85 if len(self.tones) != 3:
86 return False
87 root, third, fifth = self.tones[0], self.tones[1], self.tones[2]
88 return (
89 Tone.TONE_ORDER.index(third.note) - Tone.TONE_ORDER.index(root.note) == 3 and
90 Tone.TONE_ORDER.index(fifth.note) - Tone.TONE_ORDER.index(third.note) == 4
91 )
92
93 def is_power_chord(self):
94 return not self.is_minor() and not self.is_major()
95
96 def __add__(self, tone):
97 if isinstance(tone, Tone):
98 self.tones.append(tone)
99 self.tones = sorted(set(self.tones))
100 return self
101 else:
102 raise TypeError("Invalid operation.")
103
104 def __sub__(self, tone):
105 if isinstance(tone, Tone):
106 if tone in self.tones:
107 self.tones.remove(tone)
108 else:
109 raise ValueError(f"Cannot remove tone {tone.note} from chord {self}")
110 return self
111 else:
112 raise TypeError("Invalid operation.")
113
114 def transposed(self, interval):
115 transposed_chord = [self.root + interval] + [tone + interval for tone in self.tones[1:]]
116 return Chord(transposed_chord[0], *transposed_chord[1:])
117
EEEEEEEEEEEE...EE.F..EEEEEEFEEEEEFEEE
======================================================================
ERROR: test_chord_not_enough_tones (test.TestBasicChordFunctionality.test_chord_not_enough_tones)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 79, in test_chord_not_enough_tones
Chord(a_sharp)
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_chord_ordering (test.TestBasicChordFunctionality.test_chord_ordering)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 64, in test_chord_ordering
f_sixth_ninth_chord = Chord(*f_major_seventh_tones)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_chord_str (test.TestBasicChordFunctionality.test_chord_str)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 59, in test_chord_str
a_major_chord = Chord(*a_major_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_chord_tone_repetition (test.TestBasicChordFunctionality.test_chord_tone_repetition)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 73, in test_chord_tone_repetition
a_minor_chord = Chord(*a_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 102, in test_is_major
a_minor_chord = Chord(*a_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 89, in test_is_minor
a_minor_chord = Chord(*a_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 115, in test_is_power_chord
a_minor_chord = Chord(*a_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_transposed (test.TestBasicChordFunctionality.test_transposed)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 128, in test_transposed
d_major_chord = Chord(*d_major_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_transposed_negative (test.TestBasicChordFunctionality.test_transposed_negative)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 135, in test_transposed_negative
e_minor_chord = Chord(*e_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_transposed_negative_overflow (test.TestBasicChordFunctionality.test_transposed_negative_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 149, in test_transposed_negative_overflow
e_minor_chord = Chord(*e_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_transposed_overflow (test.TestBasicChordFunctionality.test_transposed_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 142, in test_transposed_overflow
d_major_chord = Chord(*d_major_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_interval_negative (test.TestBasicIntervalFunctionality.test_interval_negative)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 50, in test_interval_negative
minor_2nd = -major_7th
^^^^^^^^^^
TypeError: bad operand type for unary -: 'Interval'
======================================================================
ERROR: test_add_chords (test.TestOperations.test_add_chords)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 296, in test_add_chords
c5_chord = Chord(Tone("C"), Tone("G"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_add_chords_repeating_notes (test.TestOperations.test_add_chords_repeating_notes)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 302, in test_add_chords_repeating_notes
c5_chord = Chord(Tone("C"), Tone("G"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_add_tone_to_chord (test.TestOperations.test_add_tone_to_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 251, in test_add_tone_to_chord
f_major_chord = Chord(*f_major_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_add_tone_to_chord_existing_tone (test.TestOperations.test_add_tone_to_chord_existing_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 265, in test_add_tone_to_chord_existing_tone
f_minor_chord = Chord(*f_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_add_tone_to_chord_order (test.TestOperations.test_add_tone_to_chord_order)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 258, in test_add_tone_to_chord_order
f_major_chord = Chord(*f_major_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_interval_addition (test.TestOperations.test_interval_addition)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 239, in test_interval_addition
major_3rd = minor_2nd + minor_3rd
~~~~~~~~~~^~~~~~~~~~~
TypeError: unsupported operand type(s) for +: 'Interval' and 'Interval'
======================================================================
ERROR: test_interval_addition_overflow (test.TestOperations.test_interval_addition_overflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 245, in test_interval_addition_overflow
unison = perfect_4th + perfect_5th
~~~~~~~~~~~~^~~~~~~~~~~~~
TypeError: unsupported operand type(s) for +: 'Interval' and 'Interval'
======================================================================
ERROR: test_subtract_interval_from_tone (test.TestOperations.test_subtract_interval_from_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 215, in test_subtract_interval_from_tone
f_sharp = g - Interval(1)
~~^~~~~~~~~~~~~
File "/tmp/solution.py", line 24, in __sub__
index_other = (index - interval.name) % len(Tone.TONE_ORDER)
^^^^^^^^^^^^^^^
AttributeError: type object 'Tone' has no attribute 'TONE_ORDER'
======================================================================
ERROR: test_subtract_interval_from_tone_oveflow (test.TestOperations.test_subtract_interval_from_tone_oveflow)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 227, in test_subtract_interval_from_tone_oveflow
g = c - Interval(5)
~~^~~~~~~~~~~~~
File "/tmp/solution.py", line 24, in __sub__
index_other = (index - interval.name) % len(Tone.TONE_ORDER)
^^^^^^^^^^^^^^^
AttributeError: type object 'Tone' has no attribute 'TONE_ORDER'
======================================================================
ERROR: test_subtract_interval_from_tone_same_tone (test.TestOperations.test_subtract_interval_from_tone_same_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 221, in test_subtract_interval_from_tone_same_tone
still_g = g - Interval(0)
~~^~~~~~~~~~~~~
File "/tmp/solution.py", line 24, in __sub__
index_other = (index - interval.name) % len(Tone.TONE_ORDER)
^^^^^^^^^^^^^^^
AttributeError: type object 'Tone' has no attribute 'TONE_ORDER'
======================================================================
ERROR: test_subtract_tone_from_chord (test.TestOperations.test_subtract_tone_from_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 274, in test_subtract_tone_from_chord
f_minor_chord = Chord(*f_minor_tones)
^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_subtract_tone_from_chord_error (test.TestOperations.test_subtract_tone_from_chord_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 288, in test_subtract_tone_from_chord_error
c5_chord = Chord(*c5_chord_tones)
^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 70, in __init__
self.tones = sorted(set([root] + list(tones)))
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/tmp/solution.py", line 35, in __hash__
return hash(self.note)
^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'note'
======================================================================
ERROR: test_tone_addition_different_tones (test.TestOperations.test_tone_addition_different_tones)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 160, in test_tone_addition_different_tones
result_chord = g + f
~~^~~
File "/tmp/solution.py", line 19, in __add__
raise TypeError("Invalid operation.")
TypeError: Invalid operation.
======================================================================
ERROR: test_tone_subtraction (test.TestOperations.test_tone_subtraction)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 172, in test_tone_subtraction
perfect_5th = g - c
~~^~~
File "/tmp/solution.py", line 27, in __sub__
raise TypeError("Invalid operation.")
TypeError: Invalid operation.
======================================================================
ERROR: test_tone_subtraction_inverse (test.TestOperations.test_tone_subtraction_inverse)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 177, in test_tone_subtraction_inverse
perfect_4th = c - g
~~^~~
File "/tmp/solution.py", line 27, in __sub__
raise TypeError("Invalid operation.")
TypeError: Invalid operation.
======================================================================
ERROR: test_tone_subtraction_same_tone (test.TestOperations.test_tone_subtraction_same_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 182, in test_tone_subtraction_same_tone
unison = c - another_c
~~^~~~~~~~~~~
File "/tmp/solution.py", line 27, in __sub__
raise TypeError("Invalid operation.")
TypeError: Invalid operation.
======================================================================
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: "unsupported operand type(s) for +: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for +: 'Interval' and 'Tone'
+ 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_tone_addition_same_tone (test.TestOperations.test_tone_addition_same_tone)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 168, in test_tone_addition_same_tone
self.assertEqual(str(err.exception), CANNOT_CHORD_THIS_CHORD)
AssertionError: 'Invalid operation.' != 'Cannot have a chord made of only 1 unique tone'
- Invalid operation.
+ Cannot have a chord made of only 1 unique tone
----------------------------------------------------------------------
Ran 37 tests in 0.007s
FAILED (failures=3, errors=28)
Мина Евтимова
07.11.2024 16:48Добре, мерси.
|
Виктор Бечев
07.11.2024 15:34За мен не дава никаква допълнителна информация. Знаеш, как работи индексирането при списъците - елементът на 1-ва позиция е с индекс 0, на 2ра - с индекс 1 и т.н.
Да имаш речник, който описва същото не дава кой знае колко повече информация и изобщо не променя начинът, по който би се използвала въпросната структура.
Дори в случая, в който имаш числа от 1 до 12 (например за месеци) - пак не бих използвал речник, тъй като операцията е елементарна - изваждаш едно за да индексираш.
|
Мина Евтимова
07.11.2024 13:47Имам въпрос относно ред 45. Така както съм ги написала не е ли по-четимо или по-скоро обърква? Смисъл трябва да ги пиша само, ако ги сменям (например да почва от 1:, не от 0: и да е до 12:, а не до 11:), така ли?
|
Мина Евтимова
06.11.2024 18:10Ами аз чак сега получих отговора, така че късно. Все пак ще го погледна и ще се опитам да си ги оправя. Благодаря.
|
Виктор Бечев
06.11.2024 17:20Заради 2 дребни глупости ще ти фейлнат 80% от тестовете. Казвам ти го, защото си написала немалко код, който ми изглежда твой и ще е загуба.
Имаш 40 минути да ги оправиш.
Обратна връзка ще ти върнем по-натам, сега няма за кога.
|
f | 1 | class Tone: | f | 1 | class Tone: |
n | n | 2 | """This is the class for Tones.""" | ||
2 | TONES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 3 | TONES = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
3 | 4 | ||||
4 | def __init__(self, name): | 5 | def __init__(self, name): | ||
5 | if name not in Tone.TONES: | 6 | if name not in Tone.TONES: | ||
6 | raise ValueError(f"Invalid tone {name}.") | 7 | raise ValueError(f"Invalid tone {name}.") | ||
7 | self.name = name | 8 | self.name = name | ||
8 | 9 | ||||
9 | def __str__(self): | 10 | def __str__(self): | ||
10 | return self.name | 11 | return self.name | ||
11 | 12 | ||||
12 | def __add__(self, interval): | 13 | def __add__(self, interval): | ||
13 | if isinstance(interval, Interval): | 14 | if isinstance(interval, Interval): | ||
14 | index = Tone.TONES.index(self.name) | 15 | index = Tone.TONES.index(self.name) | ||
15 | new_index = ((index + interval.name) % len(Tone.TONES)) | 16 | new_index = ((index + interval.name) % len(Tone.TONES)) | ||
16 | return Tone(Tone.TONES[new_index]) | 17 | return Tone(Tone.TONES[new_index]) | ||
17 | else: | 18 | else: | ||
18 | raise TypeError("Invalid operation.") | 19 | raise TypeError("Invalid operation.") | ||
19 | 20 | ||||
20 | def __sub__(self, interval): | 21 | def __sub__(self, interval): | ||
21 | if isinstance(interval, Interval): | 22 | if isinstance(interval, Interval): | ||
22 | index = Tone. TONES.index(self.name) | 23 | index = Tone. TONES.index(self.name) | ||
23 | index_other = (index - interval.name) % len(Tone.TONE_ORDER) | 24 | index_other = (index - interval.name) % len(Tone.TONE_ORDER) | ||
n | 24 | return Tone(Tone.TONES[index]) | n | 25 | return Tone(Tone.TONES[index_other]) |
25 | else: | 26 | else: | ||
26 | raise TypeError("Invalid operation.") | 27 | raise TypeError("Invalid operation.") | ||
27 | 28 | ||||
28 | def __eq__(self, other): | 29 | def __eq__(self, other): | ||
29 | if isinstance(other, Tone): | 30 | if isinstance(other, Tone): | ||
30 | return self.name == other.name | 31 | return self.name == other.name | ||
31 | return False | 32 | return False | ||
32 | 33 | ||||
33 | def __hash__(self): | 34 | def __hash__(self): | ||
34 | return hash(self.note) | 35 | return hash(self.note) | ||
35 | 36 | ||||
n | n | 37 | def __lt__(self, other): | ||
38 | if isinstance(other, Tone): | ||||
39 | return Tone.TONE_ORDER.index(self.note) < Tone.TONE_ORDER.index(other.note) | ||||
40 | return NotImplemented | ||||
41 | |||||
36 | 42 | ||||
37 | class Interval: | 43 | class Interval: | ||
n | n | 44 | """This is the class for intervals.""" | ||
38 | INTERVALS = {0:"unison", 1:"minor 2nd", 2:"major 2nd", 3:"minor 3rd", 4:"major 3rd", 5:"perfect 4th", 6:"diminished 5th", | 45 | INTERVALS = {0:"unison", 1:"minor 2nd", 2:"major 2nd", 3:"minor 3rd", 4:"major 3rd", 5:"perfect 4th", 6:"diminished 5th", | ||
39 | 7:"perfect 5th", 8:"minor 6th", 9:"major 6th", 10:"minor 7th", 11:"major 7th"} | 46 | 7:"perfect 5th", 8:"minor 6th", 9:"major 6th", 10:"minor 7th", 11:"major 7th"} | ||
40 | 47 | ||||
41 | def __init__(self, name): | 48 | def __init__(self, name): | ||
n | 42 | self.name = name % 12 if name % 12 != 0 else 12 | n | 49 | if not isinstance(name, int): |
50 | raise ValueError("Interval must me an integer.") | ||||
51 | self.name = name % 12 | ||||
43 | 52 | ||||
44 | def __str__(self): | 53 | def __str__(self): | ||
45 | return Interval.INTERVALS[self.name] | 54 | return Interval.INTERVALS[self.name] | ||
46 | 55 | ||||
47 | def __eq__(self, other): | 56 | def __eq__(self, other): | ||
48 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
49 | return self.name == other.name | 58 | return self.name == other.name | ||
50 | return False | 59 | return False | ||
51 | 60 | ||||
52 | def __hash__(self): | 61 | def __hash__(self): | ||
53 | return hash(self.name) | 62 | return hash(self.name) | ||
54 | 63 | ||||
55 | 64 | ||||
56 | class Chord: | 65 | class Chord: | ||
n | n | 66 | """This is the class for chords.""" | ||
57 | def __init__(self, root, *tones): | 67 | def __init__(self, root, *tones): | ||
n | 58 | if not tones: | n | 68 | if not isinstance(root, Tone): |
59 | raise TypeError("Cannot have chord from only one tone.") | 69 | raise ValueError("Root must be a Tone instance.") | ||
60 | self.root = root | 70 | self.tones = sorted(set([root] + list(tones))) | ||
61 | self.tones = sorted(set([root] + list(tones)), key=lambda tone: Tone.TONE_ORDER.index(tone.note)) | ||||
62 | |||||
63 | if len(self.tones) < 2: | ||||
64 | raise TypeError("Cannot have chord from only one tone.") | ||||
65 | 71 | ||||
66 | def __str__(self): | 72 | def __str__(self): | ||
n | 67 | return "-".join(str(tone) for tone in self.tones) | n | 73 | return "-".join(tone.name for tone in self.tones) |
68 | |||||
69 | def is_minor(self): | ||||
70 | minor_third = Interval(3) | ||||
71 | return any((tone - self.root).name == minor_third.name for tone in self.tones) | ||||
72 | 74 | ||||
73 | def is_major(self): | 75 | def is_major(self): | ||
n | 74 | major_third = Interval(4) | n | 76 | if len(self.tones) != 3: |
75 | return any((tone - self.root).name == major_third.name for tone in self.tones) | 77 | return False | ||
78 | root, third, fifth = self.tones[0], self.tones[1], self.tones[2] | ||||
79 | return ( | ||||
80 | Tone.TONE_ORDER.index(third.note) - Tone.TONE_ORDER.index(root.note) == 4 and | ||||
81 | Tone.TONE_ORDER.index(fifth.note) - Tone.TONE_ORDER.index(third.note) == 3 | ||||
82 | ) | ||||
83 | |||||
84 | def is_minor(self): | ||||
85 | if len(self.tones) != 3: | ||||
86 | return False | ||||
87 | root, third, fifth = self.tones[0], self.tones[1], self.tones[2] | ||||
88 | return ( | ||||
89 | Tone.TONE_ORDER.index(third.note) - Tone.TONE_ORDER.index(root.note) == 3 and | ||||
90 | Tone.TONE_ORDER.index(fifth.note) - Tone.TONE_ORDER.index(third.note) == 4 | ||||
91 | ) | ||||
76 | 92 | ||||
77 | def is_power_chord(self): | 93 | def is_power_chord(self): | ||
78 | return not self.is_minor() and not self.is_major() | 94 | return not self.is_minor() and not self.is_major() | ||
79 | 95 | ||||
80 | def __add__(self, tone): | 96 | def __add__(self, tone): | ||
81 | if isinstance(tone, Tone): | 97 | if isinstance(tone, Tone): | ||
82 | self.tones.append(tone) | 98 | self.tones.append(tone) | ||
t | 83 | self.tones = sorted(self.tones, key=lambda tone: Tone.TONE_ORDER.index(tone.note)) | t | 99 | self.tones = sorted(set(self.tones)) |
84 | return self | 100 | return self | ||
85 | else: | 101 | else: | ||
86 | raise TypeError("Invalid operation.") | 102 | raise TypeError("Invalid operation.") | ||
87 | 103 | ||||
88 | def __sub__(self, tone): | 104 | def __sub__(self, tone): | ||
89 | if isinstance(tone, Tone): | 105 | if isinstance(tone, Tone): | ||
90 | if tone in self.tones: | 106 | if tone in self.tones: | ||
91 | self.tones.remove(tone) | 107 | self.tones.remove(tone) | ||
92 | else: | 108 | else: | ||
93 | raise ValueError(f"Cannot remove tone {tone.note} from chord {self}") | 109 | raise ValueError(f"Cannot remove tone {tone.note} from chord {self}") | ||
94 | return self | 110 | return self | ||
95 | else: | 111 | else: | ||
96 | raise TypeError("Invalid operation.") | 112 | raise TypeError("Invalid operation.") | ||
97 | 113 | ||||
98 | def transposed(self, interval): | 114 | def transposed(self, interval): | ||
99 | transposed_chord = [self.root + interval] + [tone + interval for tone in self.tones[1:]] | 115 | transposed_chord = [self.root + interval] + [tone + interval for tone in self.tones[1:]] | ||
100 | return Chord(transposed_chord[0], *transposed_chord[1:]) | 116 | return Chord(transposed_chord[0], *transposed_chord[1:]) | ||
101 | 117 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
07.11.2024 11:49
07.11.2024 11:49
07.11.2024 11:53
07.11.2024 11:54
07.11.2024 15:48
07.11.2024 15:41
07.11.2024 15:43
07.11.2024 15:50
07.11.2024 15:50