1class Tone:
2 NAME_DOMAIN = ("A", "A#", "B", "C", "C#", "D",
3 "D#", "E", "F", "F#", "G", "G#")
4
5 def __init__(self, tone_name):
6 self.name_index = Tone.NAME_DOMAIN.index(tone_name)
7
8 def __str__(self):
9 return Tone.NAME_DOMAIN[self.name_index]
10
11 def __eq__(self, other):
12 if isinstance(other, Tone):
13 return self.name_index == other.name_index
14 raise TypeError("Invalid operation")
15
16 def __hash__(self):
17 return hash(self.name_index)
18
19 def __add__(self, other):
20 if isinstance(other, Tone):
21 return Chord(self, other)
22 elif isinstance(other, Interval):
23 return Tone(Tone.NAME_DOMAIN[(self.name_index +
24 other.interval_name_index)
25 % len(Tone.NAME_DOMAIN)])
26 raise TypeError("Invalid operation")
27
28 def __sub__(self, other):
29 if isinstance(other, Tone):
30 return Interval(abs(self.name_index - other.name_index))
31 elif isinstance(other, Interval):
32 return Tone(Tone.NAME_DOMAIN[(self.name_index -
33 other.interval_name_index)
34 % len(Tone.NAME_DOMAIN)])
35 raise TypeError("Invalid operation")
36
37
38class Interval:
39 INTERVAL_NAME_DOMAIN = ("unison", "minor 2nd", "major 2nd", "minor 3rd",
40 "major 3rd", "perfect 4th", "diminished 5th",
41 "perfect 5th", "minor 6th", "major 6th",
42 "minor 7th", "major 7th")
43
44 def __init__(self, number_of_semitones):
45 self.interval_name_index = (number_of_semitones %
46 len(Interval.INTERVAL_NAME_DOMAIN))
47
48 def __str__(self):
49 return Interval.INTERVAL_NAME_DOMAIN[self.interval_name_index]
50
51 def __eq__(self, other):
52 if isinstance(other, Interval):
53 return self.interval_name_index == other.interval_name_index
54 raise TypeError("Invalid operation")
55
56 def __neg__(self):
57 return Interval(-self.interval_name_index)
58
59 def __add__(self, other):
60 if isinstance(other, Interval):
61 return Interval(self.interval_name_index +
62 other.interval_name_index)
63 raise TypeError("Invalid operation")
64
65
66class Chord:
67 major_chord_interval = Interval(4)
68 minor_chord_interval = Interval(3)
69
70 def __init__(self, root_tone: Tone, *additional_tones: Tone):
71 self.tones = []
72 self.tones.append(root_tone)
73
74 additional_tones = {tone for tone in additional_tones
75 if tone != root_tone}
76
77 if len(additional_tones) == 0:
78 raise TypeError("Cannot have a chord made of only 1 unique tone")
79
80 temp_tones = []
81 current_tone = self.tones[0] + Interval(1)
82
83 while current_tone != self.tones[0]:
84 if current_tone in additional_tones:
85 temp_tones.append(current_tone)
86 current_tone = current_tone + Interval(1)
87
88 self.tones.extend(temp_tones)
89
90 def __str__(self):
91 return "-".join([str(tone) for tone in self.tones])
92
93 def __add__(self, other):
94 if isinstance(other, Tone):
95 return Chord(*self.tones, other)
96 elif isinstance(other, Chord):
97 return Chord(*self.tones, *other.tones)
98 raise TypeError("Invalid operation")
99
100 def __sub__(self, other):
101 if isinstance(other, Tone):
102 if other not in self.tones:
103 raise TypeError(f"Cannot remove tone {other} from chord "
104 f"{self}")
105 elif len(self.tones) == 2:
106 raise TypeError("Cannot have a chord made of only 1 unique "
107 "tone")
108
109 return Chord(*[tone for tone in self.tones if tone != other])
110 raise TypeError("Invalid operation")
111
112 def is_major(self) -> bool:
113 root_tone_index = self.tones[0].name_index
114
115 for secondary_tone in self.tones[1:]:
116 temp_interval = Interval(abs(root_tone_index -
117 secondary_tone.name_index))
118
119 if temp_interval == Chord.major_chord_interval:
120 return True
121
122 return False
123
124 def is_minor(self) -> bool:
125 root_tone_index = self.tones[0].name_index
126
127 for secondary_tone in self.tones[1:]:
128 temp_interval = Interval(abs(root_tone_index -
129 secondary_tone.name_index))
130
131 if temp_interval == Chord.minor_chord_interval:
132 return True
133
134 return False
135
136 def is_power_chord(self) -> bool:
137 root_tone_index = self.tones[0].name_index
138
139 for secondary_tone in self.tones[1:]:
140 temp_interval = Interval(abs(root_tone_index -
141 secondary_tone.name_index))
142
143 if (temp_interval == Chord.major_chord_interval
144 or temp_interval == Chord.minor_chord_interval):
145 return False
146
147 return True
148
149 def transposed(self, interval: Interval):
150 return Chord(*[tone + interval for tone in self.tones])
...........................F.......F.
======================================================================
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=2)
Виктор Бечев
04.11.2024 11:46PyCharm нещо не е наред, ако списъците не дефинират `__getitem__` - не знам какво.
Нищо от това, което си споменал не променя този факт. Just PyCharm being drunk, I guess.
|
Димитър Танков
04.11.2024 00:37На 81 ред PyCharm изписва "Class 'list' does not define '__getitem__', so the '[]' operator cannot be used on its instances", което ме обърка. Възможно ли е да е обвързано с факта, че използвам елемент от self.tones list-а в условието на цикъла и след това променям temp_tones, което може да е нещо като shallow copy на self.tones, поради оптимизация.
|
Димитър Танков
04.11.2024 00:32Да кажем, че имам бегло понятие за теория на музиката, понеже съм свирил на китара😁
|
f | 1 | class Tone: | f | 1 | class Tone: |
n | 2 | name_domain = ("A", "A#", "B", "C", "C#", "D", | n | 2 | NAME_DOMAIN = ("A", "A#", "B", "C", "C#", "D", |
3 | "D#", "E", "F", "F#", "G", "G#") | 3 | "D#", "E", "F", "F#", "G", "G#") | ||
4 | 4 | ||||
5 | def __init__(self, tone_name): | 5 | def __init__(self, tone_name): | ||
n | 6 | self.name_index = Tone.name_domain.index(tone_name) | n | 6 | self.name_index = Tone.NAME_DOMAIN.index(tone_name) |
7 | 7 | ||||
8 | def __str__(self): | 8 | def __str__(self): | ||
n | 9 | return Tone.name_domain[self.name_index] | n | 9 | return Tone.NAME_DOMAIN[self.name_index] |
10 | 10 | ||||
11 | def __eq__(self, other): | 11 | def __eq__(self, other): | ||
12 | if isinstance(other, Tone): | 12 | if isinstance(other, Tone): | ||
13 | return self.name_index == other.name_index | 13 | return self.name_index == other.name_index | ||
n | 14 | return False | n | 14 | raise TypeError("Invalid operation") |
15 | 15 | ||||
16 | def __hash__(self): | 16 | def __hash__(self): | ||
17 | return hash(self.name_index) | 17 | return hash(self.name_index) | ||
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): | ||
n | 23 | return Tone(Tone.name_domain[(self.name_index + | n | 23 | return Tone(Tone.NAME_DOMAIN[(self.name_index + |
24 | other.interval_name_index) | 24 | other.interval_name_index) | ||
n | 25 | % len(Tone.name_domain)]) | n | 25 | % len(Tone.NAME_DOMAIN)]) |
26 | raise TypeError("Invalid operation") | 26 | raise TypeError("Invalid operation") | ||
27 | 27 | ||||
28 | def __sub__(self, other): | 28 | def __sub__(self, other): | ||
29 | if isinstance(other, Tone): | 29 | if isinstance(other, Tone): | ||
30 | return Interval(abs(self.name_index - other.name_index)) | 30 | return Interval(abs(self.name_index - other.name_index)) | ||
31 | elif isinstance(other, Interval): | 31 | elif isinstance(other, Interval): | ||
n | 32 | return Tone(Tone.name_domain[(self.name_index - | n | 32 | return Tone(Tone.NAME_DOMAIN[(self.name_index - |
33 | other.interval_name_index) | 33 | other.interval_name_index) | ||
n | 34 | % len(Tone.name_domain)]) | n | 34 | % len(Tone.NAME_DOMAIN)]) |
35 | raise TypeError("Invalid operation") | 35 | raise TypeError("Invalid operation") | ||
36 | 36 | ||||
37 | 37 | ||||
38 | class Interval: | 38 | class Interval: | ||
n | 39 | interval_name_domain = ("unison", "minor 2nd", "major 2nd", "minor 3rd", | n | 39 | INTERVAL_NAME_DOMAIN = ("unison", "minor 2nd", "major 2nd", "minor 3rd", |
40 | "major 3rd", "perfect 4th", "diminished 5th", | 40 | "major 3rd", "perfect 4th", "diminished 5th", | ||
41 | "perfect 5th", "minor 6th", "major 6th", | 41 | "perfect 5th", "minor 6th", "major 6th", | ||
42 | "minor 7th", "major 7th") | 42 | "minor 7th", "major 7th") | ||
43 | 43 | ||||
44 | def __init__(self, number_of_semitones): | 44 | def __init__(self, number_of_semitones): | ||
45 | self.interval_name_index = (number_of_semitones % | 45 | self.interval_name_index = (number_of_semitones % | ||
n | 46 | len(Interval.interval_name_domain)) | n | 46 | len(Interval.INTERVAL_NAME_DOMAIN)) |
47 | 47 | ||||
48 | def __str__(self): | 48 | def __str__(self): | ||
n | 49 | return Interval.interval_name_domain[self.interval_name_index] | n | 49 | return Interval.INTERVAL_NAME_DOMAIN[self.interval_name_index] |
50 | 50 | ||||
51 | def __eq__(self, other): | 51 | def __eq__(self, other): | ||
52 | if isinstance(other, Interval): | 52 | if isinstance(other, Interval): | ||
53 | return self.interval_name_index == other.interval_name_index | 53 | return self.interval_name_index == other.interval_name_index | ||
t | 54 | return False | t | 54 | raise TypeError("Invalid operation") |
55 | 55 | ||||
56 | def __neg__(self): | 56 | def __neg__(self): | ||
57 | return Interval(-self.interval_name_index) | 57 | return Interval(-self.interval_name_index) | ||
58 | 58 | ||||
59 | def __add__(self, other): | 59 | def __add__(self, other): | ||
60 | if isinstance(other, Interval): | 60 | if isinstance(other, Interval): | ||
61 | return Interval(self.interval_name_index + | 61 | return Interval(self.interval_name_index + | ||
62 | other.interval_name_index) | 62 | other.interval_name_index) | ||
63 | raise TypeError("Invalid operation") | 63 | raise TypeError("Invalid operation") | ||
64 | 64 | ||||
65 | 65 | ||||
66 | class Chord: | 66 | class Chord: | ||
67 | major_chord_interval = Interval(4) | 67 | major_chord_interval = Interval(4) | ||
68 | minor_chord_interval = Interval(3) | 68 | minor_chord_interval = Interval(3) | ||
69 | 69 | ||||
70 | def __init__(self, root_tone: Tone, *additional_tones: Tone): | 70 | def __init__(self, root_tone: Tone, *additional_tones: Tone): | ||
71 | self.tones = [] | 71 | self.tones = [] | ||
72 | self.tones.append(root_tone) | 72 | self.tones.append(root_tone) | ||
73 | 73 | ||||
74 | additional_tones = {tone for tone in additional_tones | 74 | additional_tones = {tone for tone in additional_tones | ||
75 | if tone != root_tone} | 75 | if tone != root_tone} | ||
76 | 76 | ||||
77 | if len(additional_tones) == 0: | 77 | if len(additional_tones) == 0: | ||
78 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 78 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
79 | 79 | ||||
80 | temp_tones = [] | 80 | temp_tones = [] | ||
81 | current_tone = self.tones[0] + Interval(1) | 81 | current_tone = self.tones[0] + Interval(1) | ||
82 | 82 | ||||
83 | while current_tone != self.tones[0]: | 83 | while current_tone != self.tones[0]: | ||
84 | if current_tone in additional_tones: | 84 | if current_tone in additional_tones: | ||
85 | temp_tones.append(current_tone) | 85 | temp_tones.append(current_tone) | ||
86 | current_tone = current_tone + Interval(1) | 86 | current_tone = current_tone + Interval(1) | ||
87 | 87 | ||||
88 | self.tones.extend(temp_tones) | 88 | self.tones.extend(temp_tones) | ||
89 | 89 | ||||
90 | def __str__(self): | 90 | def __str__(self): | ||
91 | return "-".join([str(tone) for tone in self.tones]) | 91 | return "-".join([str(tone) for tone in self.tones]) | ||
92 | 92 | ||||
93 | def __add__(self, other): | 93 | def __add__(self, other): | ||
94 | if isinstance(other, Tone): | 94 | if isinstance(other, Tone): | ||
95 | return Chord(*self.tones, other) | 95 | return Chord(*self.tones, other) | ||
96 | elif isinstance(other, Chord): | 96 | elif isinstance(other, Chord): | ||
97 | return Chord(*self.tones, *other.tones) | 97 | return Chord(*self.tones, *other.tones) | ||
98 | raise TypeError("Invalid operation") | 98 | raise TypeError("Invalid operation") | ||
99 | 99 | ||||
100 | def __sub__(self, other): | 100 | def __sub__(self, other): | ||
101 | if isinstance(other, Tone): | 101 | if isinstance(other, Tone): | ||
102 | if other not in self.tones: | 102 | if other not in self.tones: | ||
103 | raise TypeError(f"Cannot remove tone {other} from chord " | 103 | raise TypeError(f"Cannot remove tone {other} from chord " | ||
104 | f"{self}") | 104 | f"{self}") | ||
105 | elif len(self.tones) == 2: | 105 | elif len(self.tones) == 2: | ||
106 | raise TypeError("Cannot have a chord made of only 1 unique " | 106 | raise TypeError("Cannot have a chord made of only 1 unique " | ||
107 | "tone") | 107 | "tone") | ||
108 | 108 | ||||
109 | return Chord(*[tone for tone in self.tones if tone != other]) | 109 | return Chord(*[tone for tone in self.tones if tone != other]) | ||
110 | raise TypeError("Invalid operation") | 110 | raise TypeError("Invalid operation") | ||
111 | 111 | ||||
112 | def is_major(self) -> bool: | 112 | def is_major(self) -> bool: | ||
113 | root_tone_index = self.tones[0].name_index | 113 | root_tone_index = self.tones[0].name_index | ||
114 | 114 | ||||
115 | for secondary_tone in self.tones[1:]: | 115 | for secondary_tone in self.tones[1:]: | ||
116 | temp_interval = Interval(abs(root_tone_index - | 116 | temp_interval = Interval(abs(root_tone_index - | ||
117 | secondary_tone.name_index)) | 117 | secondary_tone.name_index)) | ||
118 | 118 | ||||
119 | if temp_interval == Chord.major_chord_interval: | 119 | if temp_interval == Chord.major_chord_interval: | ||
120 | return True | 120 | return True | ||
121 | 121 | ||||
122 | return False | 122 | return False | ||
123 | 123 | ||||
124 | def is_minor(self) -> bool: | 124 | def is_minor(self) -> bool: | ||
125 | root_tone_index = self.tones[0].name_index | 125 | root_tone_index = self.tones[0].name_index | ||
126 | 126 | ||||
127 | for secondary_tone in self.tones[1:]: | 127 | for secondary_tone in self.tones[1:]: | ||
128 | temp_interval = Interval(abs(root_tone_index - | 128 | temp_interval = Interval(abs(root_tone_index - | ||
129 | secondary_tone.name_index)) | 129 | secondary_tone.name_index)) | ||
130 | 130 | ||||
131 | if temp_interval == Chord.minor_chord_interval: | 131 | if temp_interval == Chord.minor_chord_interval: | ||
132 | return True | 132 | return True | ||
133 | 133 | ||||
134 | return False | 134 | return False | ||
135 | 135 | ||||
136 | def is_power_chord(self) -> bool: | 136 | def is_power_chord(self) -> bool: | ||
137 | root_tone_index = self.tones[0].name_index | 137 | root_tone_index = self.tones[0].name_index | ||
138 | 138 | ||||
139 | for secondary_tone in self.tones[1:]: | 139 | for secondary_tone in self.tones[1:]: | ||
140 | temp_interval = Interval(abs(root_tone_index - | 140 | temp_interval = Interval(abs(root_tone_index - | ||
141 | secondary_tone.name_index)) | 141 | secondary_tone.name_index)) | ||
142 | 142 | ||||
143 | if (temp_interval == Chord.major_chord_interval | 143 | if (temp_interval == Chord.major_chord_interval | ||
144 | or temp_interval == Chord.minor_chord_interval): | 144 | or temp_interval == Chord.minor_chord_interval): | ||
145 | return False | 145 | return False | ||
146 | 146 | ||||
147 | return True | 147 | return True | ||
148 | 148 | ||||
149 | def transposed(self, interval: Interval): | 149 | def transposed(self, interval: Interval): | ||
150 | return Chord(*[tone + interval for tone in self.tones]) | 150 | return Chord(*[tone + interval for tone in self.tones]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|