1"""
2 This module provides classes and functions for representing musical
3 tones, intervals, and chords. It allows for operations such as
4 adding tones to chords, removing tones from chords, and
5 transposing chords using intervals.
6"""
7
8
9MINIMUM_UNIQUE_TONES = 1
10TOTAL_TONES = 12
11MINOR_THIRD_INTERVAL = 3
12MAJOR_THIRD_INTERVAL = 4
13
14
15class Tone:
16 """Represent a musical tone and methods to perform operations with intervals and other tones."""
17 def __init__(self, name):
18 self.name = name
19
20 def __str__(self):
21 return self.name
22
23 @property
24 def tone_index(self):
25 return Chord.LIST_OF_TONES.index(self.name)
26
27 def __add__(self, other):
28 if isinstance(other, Interval):
29 new_tone_index = (self.tone_index + other.number_of_semitones) % TOTAL_TONES
30 return Tone(Chord.LIST_OF_TONES[new_tone_index])
31 return Chord(self, other)
32
33 def __radd__(self, other):
34 raise TypeError("Invalid operation")
35
36 def __sub__(self, other):
37 if isinstance(other, Interval):
38 new_tone_index = (self.tone_index - other.number_of_semitones) % TOTAL_TONES
39 return Tone(Chord.LIST_OF_TONES[new_tone_index])
40 distance = (self.tone_index - other.tone_index) % TOTAL_TONES
41 return Interval(distance)
42
43 def __rsub__(self, other):
44 raise TypeError("Invalid operation")
45
46
47class Interval:
48 """Represent a musical interval in semitones."""
49 INTERVALS_NAME = [
50 "unison",
51 "minor 2nd",
52 "major 2nd",
53 "minor 3rd",
54 "major 3rd",
55 "perfect 4th",
56 "diminished 5th",
57 "perfect 5th",
58 "minor 6th",
59 "major 6th",
60 "minor 7th",
61 "major 7th"
62 ]
63
64 def __init__(self, number_of_semitones):
65 self.number_of_semitones = number_of_semitones % TOTAL_TONES
66
67 def __neg__(self):
68 return Interval(-self.number_of_semitones)
69
70 def __str__(self):
71 return f"{self.INTERVALS_NAME[self.number_of_semitones]}"
72
73 def __add__(self, other):
74 new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES
75 return Interval(new_semitones)
76
77
78class Chord:
79 """Represent a musical chord of multiple tones."""
80 LIST_OF_TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
81
82 def __init__(self, root, *arg):
83 self.tones = [root]
84 for tone in arg:
85 if tone.name not in [t.name for t in self.tones]:
86 self.tones.append(tone)
87
88 if len(self.tones) == MINIMUM_UNIQUE_TONES:
89 raise TypeError("Cannot have a chord made of only 1 unique tone")
90 self.root_index = self.LIST_OF_TONES.index(root.name)
91 self.sort_tones = sorted(self.tones, key=self._get_distance_from_root)
92
93 def _get_distance_from_root(self, tone):
94 return (self.LIST_OF_TONES.index(tone.name) - self.root_index) % TOTAL_TONES
95
96 def __str__(self):
97 return "-".join(str(tone) for tone in self.sort_tones)
98
99 def is_minor(self):
100 """Check if the formed chord is 'minor 3rd'."""
101 for tone in self.tones:
102 if self._get_distance_from_root(tone) == MINOR_THIRD_INTERVAL:
103 return True
104 return False
105
106 def is_major(self):
107 """Check if the formed chord is 'major 3rd'."""
108 for tone in self.tones:
109 if self._get_distance_from_root(tone) == MAJOR_THIRD_INTERVAL:
110 return True
111 return False
112
113 def is_power_chord(self):
114 """Check if the chord is a power chord (not major or minor)."""
115 return not (self.is_minor() or self.is_major())
116
117 def __add__(self, other):
118 if isinstance(other, Tone):
119 return Chord(*self.tones, other)
120 return Chord(*self.tones, *other.tones)
121
122 def __sub__(self, other):
123 if isinstance(other, Tone):
124 if other.name not in [tone.name for tone in self.tones]:
125 raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")
126 new_tones = [tone for tone in self.tones if tone.name != other.name]
127 return Chord(*new_tones)
128
129 def transposed(self, interval):
130 """Transpose the chord by a given interval."""
131 transposed_tones = []
132 for tone in self.tones:
133 new_index = (self.LIST_OF_TONES.index(tone.name) + interval.number_of_semitones) % TOTAL_TONES
134 transposed_tones.append(Tone(self.LIST_OF_TONES[new_index]))
135 return Chord(transposed_tones[0], *transposed_tones[1:])
..................E..................
======================================================================
ERROR: 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 210, in test_add_interval_to_tone_left_side_error
Interval(2) + g
~~~~~~~~~~~~^~~
File "/tmp/solution.py", line 74, in __add__
new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES
^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'number_of_semitones'
----------------------------------------------------------------------
Ran 37 tests in 0.002s
FAILED (errors=1)
f | 1 | """ | f | 1 | """ |
2 | This module provides classes and functions for representing musical | 2 | This module provides classes and functions for representing musical | ||
3 | tones, intervals, and chords. It allows for operations such as | 3 | tones, intervals, and chords. It allows for operations such as | ||
4 | adding tones to chords, removing tones from chords, and | 4 | adding tones to chords, removing tones from chords, and | ||
5 | transposing chords using intervals. | 5 | transposing chords using intervals. | ||
6 | """ | 6 | """ | ||
7 | 7 | ||||
8 | 8 | ||||
9 | MINIMUM_UNIQUE_TONES = 1 | 9 | MINIMUM_UNIQUE_TONES = 1 | ||
10 | TOTAL_TONES = 12 | 10 | TOTAL_TONES = 12 | ||
11 | MINOR_THIRD_INTERVAL = 3 | 11 | MINOR_THIRD_INTERVAL = 3 | ||
12 | MAJOR_THIRD_INTERVAL = 4 | 12 | MAJOR_THIRD_INTERVAL = 4 | ||
13 | 13 | ||||
14 | 14 | ||||
15 | class Tone: | 15 | class Tone: | ||
16 | """Represent a musical tone and methods to perform operations with intervals and other tones.""" | 16 | """Represent a musical tone and methods to perform operations with intervals and other tones.""" | ||
17 | def __init__(self, name): | 17 | def __init__(self, name): | ||
18 | self.name = name | 18 | self.name = name | ||
19 | 19 | ||||
20 | def __str__(self): | 20 | def __str__(self): | ||
21 | return self.name | 21 | return self.name | ||
22 | 22 | ||||
n | n | 23 | @property | ||
23 | def get_tone_index(self): | 24 | def tone_index(self): | ||
24 | return Chord.list_of_tones.index(self.name) | 25 | return Chord.LIST_OF_TONES.index(self.name) | ||
25 | 26 | ||||
26 | def __add__(self, other): | 27 | def __add__(self, other): | ||
27 | if isinstance(other, Interval): | 28 | if isinstance(other, Interval): | ||
n | 28 | tone_index = (self.get_tone_index() + other.number_of_semitones) % TOTAL_TONES | n | 29 | new_tone_index = (self.tone_index + other.number_of_semitones) % TOTAL_TONES |
29 | return Tone(Chord.list_of_tones[tone_index]) | 30 | return Tone(Chord.LIST_OF_TONES[new_tone_index]) | ||
30 | return Chord(self, other) | 31 | return Chord(self, other) | ||
31 | 32 | ||||
32 | def __radd__(self, other): | 33 | def __radd__(self, other): | ||
33 | raise TypeError("Invalid operation") | 34 | raise TypeError("Invalid operation") | ||
34 | 35 | ||||
35 | def __sub__(self, other): | 36 | def __sub__(self, other): | ||
36 | if isinstance(other, Interval): | 37 | if isinstance(other, Interval): | ||
n | 37 | tone_index = (self.get_tone_index() - other.number_of_semitones) % TOTAL_TONES | n | 38 | new_tone_index = (self.tone_index - other.number_of_semitones) % TOTAL_TONES |
38 | return Tone(Chord.list_of_tones[tone_index]) | 39 | return Tone(Chord.LIST_OF_TONES[new_tone_index]) | ||
39 | distance = (self.get_tone_index() - other.get_tone_index()) % TOTAL_TONES | 40 | distance = (self.tone_index - other.tone_index) % TOTAL_TONES | ||
40 | return Interval(distance) | 41 | return Interval(distance) | ||
41 | 42 | ||||
42 | def __rsub__(self, other): | 43 | def __rsub__(self, other): | ||
43 | raise TypeError("Invalid operation") | 44 | raise TypeError("Invalid operation") | ||
44 | 45 | ||||
45 | 46 | ||||
46 | class Interval: | 47 | class Interval: | ||
47 | """Represent a musical interval in semitones.""" | 48 | """Represent a musical interval in semitones.""" | ||
n | 48 | INTERVALS_NAME = ( | n | 49 | INTERVALS_NAME = [ |
49 | "unison", | 50 | "unison", | ||
50 | "minor 2nd", | 51 | "minor 2nd", | ||
51 | "major 2nd", | 52 | "major 2nd", | ||
52 | "minor 3rd", | 53 | "minor 3rd", | ||
53 | "major 3rd", | 54 | "major 3rd", | ||
54 | "perfect 4th", | 55 | "perfect 4th", | ||
55 | "diminished 5th", | 56 | "diminished 5th", | ||
56 | "perfect 5th", | 57 | "perfect 5th", | ||
57 | "minor 6th", | 58 | "minor 6th", | ||
58 | "major 6th", | 59 | "major 6th", | ||
59 | "minor 7th", | 60 | "minor 7th", | ||
60 | "major 7th" | 61 | "major 7th" | ||
n | 61 | ) | n | 62 | ] |
62 | 63 | ||||
63 | def __init__(self, number_of_semitones): | 64 | def __init__(self, number_of_semitones): | ||
64 | self.number_of_semitones = number_of_semitones % TOTAL_TONES | 65 | self.number_of_semitones = number_of_semitones % TOTAL_TONES | ||
65 | 66 | ||||
66 | def __neg__(self): | 67 | def __neg__(self): | ||
67 | return Interval(-self.number_of_semitones) | 68 | return Interval(-self.number_of_semitones) | ||
68 | 69 | ||||
69 | def __str__(self): | 70 | def __str__(self): | ||
70 | return f"{self.INTERVALS_NAME[self.number_of_semitones]}" | 71 | return f"{self.INTERVALS_NAME[self.number_of_semitones]}" | ||
71 | 72 | ||||
72 | def __add__(self, other): | 73 | def __add__(self, other): | ||
73 | new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES | 74 | new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES | ||
74 | return Interval(new_semitones) | 75 | return Interval(new_semitones) | ||
75 | 76 | ||||
76 | 77 | ||||
77 | class Chord: | 78 | class Chord: | ||
78 | """Represent a musical chord of multiple tones.""" | 79 | """Represent a musical chord of multiple tones.""" | ||
n | 79 | list_of_tones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | n | 80 | LIST_OF_TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] |
80 | 81 | ||||
81 | def __init__(self, root, *arg): | 82 | def __init__(self, root, *arg): | ||
82 | self.tones = [root] | 83 | self.tones = [root] | ||
83 | for tone in arg: | 84 | for tone in arg: | ||
84 | if tone.name not in [t.name for t in self.tones]: | 85 | if tone.name not in [t.name for t in self.tones]: | ||
85 | self.tones.append(tone) | 86 | self.tones.append(tone) | ||
86 | 87 | ||||
87 | if len(self.tones) == MINIMUM_UNIQUE_TONES: | 88 | if len(self.tones) == MINIMUM_UNIQUE_TONES: | ||
88 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 89 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
n | 89 | self.root_index = self.list_of_tones.index(root.name) | n | 90 | self.root_index = self.LIST_OF_TONES.index(root.name) |
90 | self.sort_tones = sorted(self.tones, key=self._get_distance_from_root) | 91 | self.sort_tones = sorted(self.tones, key=self._get_distance_from_root) | ||
91 | 92 | ||||
92 | def _get_distance_from_root(self, tone): | 93 | def _get_distance_from_root(self, tone): | ||
n | 93 | return (self.list_of_tones.index(tone.name) - self.root_index) % TOTAL_TONES | n | 94 | return (self.LIST_OF_TONES.index(tone.name) - self.root_index) % TOTAL_TONES |
94 | 95 | ||||
95 | def __str__(self): | 96 | def __str__(self): | ||
96 | return "-".join(str(tone) for tone in self.sort_tones) | 97 | return "-".join(str(tone) for tone in self.sort_tones) | ||
97 | 98 | ||||
98 | def is_minor(self): | 99 | def is_minor(self): | ||
99 | """Check if the formed chord is 'minor 3rd'.""" | 100 | """Check if the formed chord is 'minor 3rd'.""" | ||
100 | for tone in self.tones: | 101 | for tone in self.tones: | ||
101 | if self._get_distance_from_root(tone) == MINOR_THIRD_INTERVAL: | 102 | if self._get_distance_from_root(tone) == MINOR_THIRD_INTERVAL: | ||
102 | return True | 103 | return True | ||
103 | return False | 104 | return False | ||
104 | 105 | ||||
105 | def is_major(self): | 106 | def is_major(self): | ||
106 | """Check if the formed chord is 'major 3rd'.""" | 107 | """Check if the formed chord is 'major 3rd'.""" | ||
107 | for tone in self.tones: | 108 | for tone in self.tones: | ||
108 | if self._get_distance_from_root(tone) == MAJOR_THIRD_INTERVAL: | 109 | if self._get_distance_from_root(tone) == MAJOR_THIRD_INTERVAL: | ||
109 | return True | 110 | return True | ||
110 | return False | 111 | return False | ||
111 | 112 | ||||
112 | def is_power_chord(self): | 113 | def is_power_chord(self): | ||
113 | """Check if the chord is a power chord (not major or minor).""" | 114 | """Check if the chord is a power chord (not major or minor).""" | ||
114 | return not (self.is_minor() or self.is_major()) | 115 | return not (self.is_minor() or self.is_major()) | ||
115 | 116 | ||||
116 | def __add__(self, other): | 117 | def __add__(self, other): | ||
117 | if isinstance(other, Tone): | 118 | if isinstance(other, Tone): | ||
118 | return Chord(*self.tones, other) | 119 | return Chord(*self.tones, other) | ||
119 | return Chord(*self.tones, *other.tones) | 120 | return Chord(*self.tones, *other.tones) | ||
120 | 121 | ||||
121 | def __sub__(self, other): | 122 | def __sub__(self, other): | ||
122 | if isinstance(other, Tone): | 123 | if isinstance(other, Tone): | ||
123 | if other.name not in [tone.name for tone in self.tones]: | 124 | if other.name not in [tone.name for tone in self.tones]: | ||
124 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | 125 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | ||
125 | new_tones = [tone for tone in self.tones if tone.name != other.name] | 126 | new_tones = [tone for tone in self.tones if tone.name != other.name] | ||
126 | return Chord(*new_tones) | 127 | return Chord(*new_tones) | ||
127 | 128 | ||||
128 | def transposed(self, interval): | 129 | def transposed(self, interval): | ||
129 | """Transpose the chord by a given interval.""" | 130 | """Transpose the chord by a given interval.""" | ||
130 | transposed_tones = [] | 131 | transposed_tones = [] | ||
131 | for tone in self.tones: | 132 | for tone in self.tones: | ||
t | 132 | new_index = (self.list_of_tones.index(tone.name) + interval.number_of_semitones) % TOTAL_TONES | t | 133 | new_index = (self.LIST_OF_TONES.index(tone.name) + interval.number_of_semitones) % TOTAL_TONES |
133 | transposed_tones.append(Tone(self.list_of_tones[new_index])) | 134 | transposed_tones.append(Tone(self.LIST_OF_TONES[new_index])) | ||
134 | return Chord(transposed_tones[0], *transposed_tones[1:]) | 135 | return Chord(transposed_tones[0], *transposed_tones[1:]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | """ | f | 1 | """ |
2 | This module provides classes and functions for representing musical | 2 | This module provides classes and functions for representing musical | ||
3 | tones, intervals, and chords. It allows for operations such as | 3 | tones, intervals, and chords. It allows for operations such as | ||
4 | adding tones to chords, removing tones from chords, and | 4 | adding tones to chords, removing tones from chords, and | ||
5 | transposing chords using intervals. | 5 | transposing chords using intervals. | ||
6 | """ | 6 | """ | ||
7 | 7 | ||||
n | n | 8 | |||
8 | MINIMUM_UNIQUE_TONES = 1 | 9 | MINIMUM_UNIQUE_TONES = 1 | ||
9 | TOTAL_TONES = 12 | 10 | TOTAL_TONES = 12 | ||
10 | MINOR_THIRD_INTERVAL = 3 | 11 | MINOR_THIRD_INTERVAL = 3 | ||
11 | MAJOR_THIRD_INTERVAL = 4 | 12 | MAJOR_THIRD_INTERVAL = 4 | ||
n | n | 13 | |||
12 | 14 | ||||
13 | class Tone: | 15 | class Tone: | ||
14 | """Represent a musical tone and methods to perform operations with intervals and other tones.""" | 16 | """Represent a musical tone and methods to perform operations with intervals and other tones.""" | ||
15 | def __init__(self, name): | 17 | def __init__(self, name): | ||
16 | self.name = name | 18 | self.name = name | ||
17 | 19 | ||||
18 | def __str__(self): | 20 | def __str__(self): | ||
19 | return self.name | 21 | return self.name | ||
20 | 22 | ||||
n | 21 | def _get_tone_index(self, tone_name): | n | 23 | def get_tone_index(self): |
22 | return Chord.list_of_tones.index(tone_name) | 24 | return Chord.list_of_tones.index(self.name) | ||
23 | 25 | ||||
24 | def __add__(self, other): | 26 | def __add__(self, other): | ||
25 | if isinstance(other, Interval): | 27 | if isinstance(other, Interval): | ||
n | 26 | tone_index = (self._get_tone_index(self.name) + other.number_of_semitones) % TOTAL_TONES | n | 28 | tone_index = (self.get_tone_index() + other.number_of_semitones) % TOTAL_TONES |
27 | return Tone(Chord.list_of_tones[tone_index]) | 29 | return Tone(Chord.list_of_tones[tone_index]) | ||
28 | return Chord(self, other) | 30 | return Chord(self, other) | ||
29 | 31 | ||||
30 | def __radd__(self, other): | 32 | def __radd__(self, other): | ||
31 | raise TypeError("Invalid operation") | 33 | raise TypeError("Invalid operation") | ||
32 | 34 | ||||
33 | def __sub__(self, other): | 35 | def __sub__(self, other): | ||
34 | if isinstance(other, Interval): | 36 | if isinstance(other, Interval): | ||
n | 35 | tone_index = (self._get_tone_index(self.name) - other.number_of_semitones) % TOTAL_TONES | n | 37 | tone_index = (self.get_tone_index() - other.number_of_semitones) % TOTAL_TONES |
36 | return Tone(Chord.list_of_tones[tone_index]) | 38 | return Tone(Chord.list_of_tones[tone_index]) | ||
n | 37 | distance = (self._get_tone_index(self.name) | n | 39 | distance = (self.get_tone_index() - other.get_tone_index()) % TOTAL_TONES |
38 | - self._get_tone_index(other.name)) % TOTAL_TONES | ||||
39 | return Interval(distance) | 40 | return Interval(distance) | ||
40 | 41 | ||||
41 | def __rsub__(self, other): | 42 | def __rsub__(self, other): | ||
42 | raise TypeError("Invalid operation") | 43 | raise TypeError("Invalid operation") | ||
43 | 44 | ||||
n | n | 45 | |||
44 | class Interval: | 46 | class Interval: | ||
45 | """Represent a musical interval in semitones.""" | 47 | """Represent a musical interval in semitones.""" | ||
n | 46 | intervals_name = { | n | 48 | INTERVALS_NAME = ( |
47 | 0: "unison", | 49 | "unison", | ||
48 | 1: "minor 2nd", | 50 | "minor 2nd", | ||
49 | 2: "major 2nd", | 51 | "major 2nd", | ||
50 | 3: "minor 3rd", | 52 | "minor 3rd", | ||
51 | 4: "major 3rd", | 53 | "major 3rd", | ||
52 | 5: "perfect 4th", | 54 | "perfect 4th", | ||
53 | 6: "diminished 5th", | 55 | "diminished 5th", | ||
54 | 7: "perfect 5th", | 56 | "perfect 5th", | ||
55 | 8: "minor 6th", | 57 | "minor 6th", | ||
56 | 9: "major 6th", | 58 | "major 6th", | ||
57 | 10: "minor 7th", | 59 | "minor 7th", | ||
58 | 11: "major 7th" | 60 | "major 7th" | ||
59 | } | 61 | ) | ||
60 | 62 | ||||
61 | def __init__(self, number_of_semitones): | 63 | def __init__(self, number_of_semitones): | ||
62 | self.number_of_semitones = number_of_semitones % TOTAL_TONES | 64 | self.number_of_semitones = number_of_semitones % TOTAL_TONES | ||
63 | 65 | ||||
64 | def __neg__(self): | 66 | def __neg__(self): | ||
65 | return Interval(-self.number_of_semitones) | 67 | return Interval(-self.number_of_semitones) | ||
66 | 68 | ||||
67 | def __str__(self): | 69 | def __str__(self): | ||
n | 68 | return f"{self.intervals_name[self.number_of_semitones]}" | n | 70 | return f"{self.INTERVALS_NAME[self.number_of_semitones]}" |
69 | 71 | ||||
70 | def __add__(self, other): | 72 | def __add__(self, other): | ||
71 | new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES | 73 | new_semitones = (self.number_of_semitones + other.number_of_semitones) % TOTAL_TONES | ||
72 | return Interval(new_semitones) | 74 | return Interval(new_semitones) | ||
73 | 75 | ||||
74 | 76 | ||||
75 | class Chord: | 77 | class Chord: | ||
76 | """Represent a musical chord of multiple tones.""" | 78 | """Represent a musical chord of multiple tones.""" | ||
77 | list_of_tones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 79 | list_of_tones = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
78 | 80 | ||||
79 | def __init__(self, root, *arg): | 81 | def __init__(self, root, *arg): | ||
80 | self.tones = [root] | 82 | self.tones = [root] | ||
81 | for tone in arg: | 83 | for tone in arg: | ||
82 | if tone.name not in [t.name for t in self.tones]: | 84 | if tone.name not in [t.name for t in self.tones]: | ||
83 | self.tones.append(tone) | 85 | self.tones.append(tone) | ||
84 | 86 | ||||
85 | if len(self.tones) == MINIMUM_UNIQUE_TONES: | 87 | if len(self.tones) == MINIMUM_UNIQUE_TONES: | ||
86 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 88 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
87 | self.root_index = self.list_of_tones.index(root.name) | 89 | self.root_index = self.list_of_tones.index(root.name) | ||
88 | self.sort_tones = sorted(self.tones, key=self._get_distance_from_root) | 90 | self.sort_tones = sorted(self.tones, key=self._get_distance_from_root) | ||
89 | 91 | ||||
90 | def _get_distance_from_root(self, tone): | 92 | def _get_distance_from_root(self, tone): | ||
91 | return (self.list_of_tones.index(tone.name) - self.root_index) % TOTAL_TONES | 93 | return (self.list_of_tones.index(tone.name) - self.root_index) % TOTAL_TONES | ||
92 | 94 | ||||
93 | def __str__(self): | 95 | def __str__(self): | ||
94 | return "-".join(str(tone) for tone in self.sort_tones) | 96 | return "-".join(str(tone) for tone in self.sort_tones) | ||
95 | 97 | ||||
96 | def is_minor(self): | 98 | def is_minor(self): | ||
97 | """Check if the formed chord is 'minor 3rd'.""" | 99 | """Check if the formed chord is 'minor 3rd'.""" | ||
98 | for tone in self.tones: | 100 | for tone in self.tones: | ||
99 | if self._get_distance_from_root(tone) == MINOR_THIRD_INTERVAL: | 101 | if self._get_distance_from_root(tone) == MINOR_THIRD_INTERVAL: | ||
100 | return True | 102 | return True | ||
101 | return False | 103 | return False | ||
102 | 104 | ||||
103 | def is_major(self): | 105 | def is_major(self): | ||
104 | """Check if the formed chord is 'major 3rd'.""" | 106 | """Check if the formed chord is 'major 3rd'.""" | ||
105 | for tone in self.tones: | 107 | for tone in self.tones: | ||
106 | if self._get_distance_from_root(tone) == MAJOR_THIRD_INTERVAL: | 108 | if self._get_distance_from_root(tone) == MAJOR_THIRD_INTERVAL: | ||
107 | return True | 109 | return True | ||
108 | return False | 110 | return False | ||
109 | 111 | ||||
110 | def is_power_chord(self): | 112 | def is_power_chord(self): | ||
111 | """Check if the chord is a power chord (not major or minor).""" | 113 | """Check if the chord is a power chord (not major or minor).""" | ||
n | 112 | return not(self.is_minor() or self.is_major()) | n | 114 | return not (self.is_minor() or self.is_major()) |
113 | 115 | ||||
114 | def __add__(self, other): | 116 | def __add__(self, other): | ||
115 | if isinstance(other, Tone): | 117 | if isinstance(other, Tone): | ||
116 | return Chord(*self.tones, other) | 118 | return Chord(*self.tones, other) | ||
117 | return Chord(*self.tones, *other.tones) | 119 | return Chord(*self.tones, *other.tones) | ||
118 | 120 | ||||
119 | def __sub__(self, other): | 121 | def __sub__(self, other): | ||
120 | if isinstance(other, Tone): | 122 | if isinstance(other, Tone): | ||
121 | if other.name not in [tone.name for tone in self.tones]: | 123 | if other.name not in [tone.name for tone in self.tones]: | ||
122 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | 124 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | ||
123 | new_tones = [tone for tone in self.tones if tone.name != other.name] | 125 | new_tones = [tone for tone in self.tones if tone.name != other.name] | ||
124 | return Chord(*new_tones) | 126 | return Chord(*new_tones) | ||
125 | 127 | ||||
126 | def transposed(self, interval): | 128 | def transposed(self, interval): | ||
127 | """Transpose the chord by a given interval.""" | 129 | """Transpose the chord by a given interval.""" | ||
128 | transposed_tones = [] | 130 | transposed_tones = [] | ||
129 | for tone in self.tones: | 131 | for tone in self.tones: | ||
t | 130 | new_index = (self.list_of_tones.index(tone.name) | t | 132 | new_index = (self.list_of_tones.index(tone.name) + interval.number_of_semitones) % TOTAL_TONES |
131 | + interval.number_of_semitones) % TOTAL_TONES | ||||
132 | transposed_tones.append(Tone(self.list_of_tones[new_index])) | 133 | transposed_tones.append(Tone(self.list_of_tones[new_index])) | ||
133 | return Chord(transposed_tones[0], *transposed_tones[1:]) | 134 | return Chord(transposed_tones[0], *transposed_tones[1:]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|