1class Tone:
2 def __init__(self, tone_name):
3 self.tone_name = tone_name
4
5 def __str__(self):
6 return str(self.tone_name)
7
8 @property
9 def name(self):
10 return self.tone_name
11
12 def __add__(self, other):
13 if isinstance(other, Tone):
14 return Chord(self, other)
15 elif isinstance(other, Interval):
16 tone_index = Chord.index_of_tone(self)
17 dist = other.number_of_semitones
18 result_index = (tone_index + dist) % 12
19 return Tone(Chord.get_tone_by_index(result_index))
20
21 def __sub__(self, other):
22 if isinstance(other, Tone):
23 return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other))
24 elif isinstance(other, Interval):
25 tone_index = Chord.index_of_tone(self)
26 dist = other.number_of_semitones
27 res_index = (tone_index - dist) % 12
28 return Tone(Chord.get_tone_by_index(res_index))
29
30
31class Interval:
32 NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd",
33 "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th",
34 "major 6th", "minor 7th", "major 7th")
35
36 def __init__(self, number_of_semitones):
37 self.number_of_semitones = number_of_semitones % 12
38
39 def __str__(self):
40 return self.NAMED_INTERVALS[self.number_of_semitones]
41
42 def get_index(self):
43 return self.NAMED_INTERVALS.index(str(self))
44
45 def __add__(self, other):
46 return Interval(self.get_index() + other.get_index())
47
48 def __neg__(self):
49 return Interval(-self.number_of_semitones)
50
51
52class Chord:
53 ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
54
55 def __init__(self, root, *tones):
56 self.root = root
57 tones_names = [curr_tone_name.name for curr_tone_name in tones]
58 tones_names = set(tones_names)
59 self.unique_tones_names = {root.name} | tones_names
60 self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names]
61 if len(self.tones_unique) == 1:
62 raise TypeError("Cannot have a chord made of only 1 unique tone")
63 self.create_right_orderd_list()
64
65 def create_right_orderd_list(self):
66 root_index = self.ALL_TONES.index(self.root.name)
67 right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index]
68 self.tones_unique = [Tone(tone) for tone in right_order_seq if tone in self.unique_tones_names]
69 self.unique_tones_names = [str(tone) for tone in self.tones_unique]
70
71 def __str__(self):
72 return "-".join(str(tone) for tone in self.tones_unique)
73
74 @classmethod
75 def index_of_tone(cls, tone):
76 return cls.ALL_TONES.index(tone.name)
77
78 @classmethod
79 def get_tone_by_index(cls, index):
80 return cls.ALL_TONES[index]
81
82 def is_major(self):
83 MAJOR_STRING = "major 3rd"
84
85 for curr_tone in self.tones_unique:
86 curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root)
87 if str(Interval(curr_dist)) == MAJOR_STRING:
88 return True
89 return False
90
91 def is_minor(self):
92 MINOR_STRING = "minor 3rd"
93
94 for curr_tone in self.tones_unique:
95 curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root)
96 if str(Interval(curr_dist)) == MINOR_STRING:
97 return True
98 return False
99
100 def is_power_chord(self):
101 return not self.is_minor() and not self.is_major()
102
103 def __add__(self, other):
104 if isinstance(other, Tone):
105 return Chord(self.root, *(self.tones_unique + [other]))
106 elif isinstance(other, Chord):
107 return Chord(self.root, *(self.tones_unique + other.tones_unique))
108
109 def __sub__(self, other):
110 if isinstance(other, Tone):
111 if other.name not in self.unique_tones_names:
112 raise TypeError(f"Cannot remove tone {other} from chord {self}")
113 chord_tones = tuple(tone for tone in self.tones_unique if tone.name != other.name)
114 return Chord(self.root, *chord_tones)
115
116 def transposed(self, interval):
117 transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique]
118 return Chord(*transposed_chord)
119
..................E........F.........
======================================================================
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 46, in __add__
return Interval(self.get_index() + other.get_index())
^^^^^^^^^^^^^^^
AttributeError: 'Tone' object has no attribute 'get_index'
======================================================================
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
----------------------------------------------------------------------
Ran 37 tests in 0.003s
FAILED (failures=1, errors=1)
f | 1 | class Tone: | f | 1 | class Tone: |
2 | def __init__(self, tone_name): | 2 | def __init__(self, tone_name): | ||
3 | self.tone_name = tone_name | 3 | self.tone_name = tone_name | ||
4 | 4 | ||||
5 | def __str__(self): | 5 | def __str__(self): | ||
6 | return str(self.tone_name) | 6 | return str(self.tone_name) | ||
7 | 7 | ||||
8 | @property | 8 | @property | ||
9 | def name(self): | 9 | def name(self): | ||
10 | return self.tone_name | 10 | return self.tone_name | ||
11 | 11 | ||||
12 | def __add__(self, other): | 12 | def __add__(self, other): | ||
13 | if isinstance(other, Tone): | 13 | if isinstance(other, Tone): | ||
14 | return Chord(self, other) | 14 | return Chord(self, other) | ||
15 | elif isinstance(other, Interval): | 15 | elif isinstance(other, Interval): | ||
16 | tone_index = Chord.index_of_tone(self) | 16 | tone_index = Chord.index_of_tone(self) | ||
17 | dist = other.number_of_semitones | 17 | dist = other.number_of_semitones | ||
18 | result_index = (tone_index + dist) % 12 | 18 | result_index = (tone_index + dist) % 12 | ||
19 | return Tone(Chord.get_tone_by_index(result_index)) | 19 | return Tone(Chord.get_tone_by_index(result_index)) | ||
20 | 20 | ||||
21 | def __sub__(self, other): | 21 | def __sub__(self, other): | ||
22 | if isinstance(other, Tone): | 22 | if isinstance(other, Tone): | ||
23 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | 23 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | ||
24 | elif isinstance(other, Interval): | 24 | elif isinstance(other, Interval): | ||
25 | tone_index = Chord.index_of_tone(self) | 25 | tone_index = Chord.index_of_tone(self) | ||
26 | dist = other.number_of_semitones | 26 | dist = other.number_of_semitones | ||
27 | res_index = (tone_index - dist) % 12 | 27 | res_index = (tone_index - dist) % 12 | ||
28 | return Tone(Chord.get_tone_by_index(res_index)) | 28 | return Tone(Chord.get_tone_by_index(res_index)) | ||
29 | 29 | ||||
30 | 30 | ||||
31 | class Interval: | 31 | class Interval: | ||
32 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | 32 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | ||
33 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | 33 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | ||
34 | "major 6th", "minor 7th", "major 7th") | 34 | "major 6th", "minor 7th", "major 7th") | ||
35 | 35 | ||||
36 | def __init__(self, number_of_semitones): | 36 | def __init__(self, number_of_semitones): | ||
37 | self.number_of_semitones = number_of_semitones % 12 | 37 | self.number_of_semitones = number_of_semitones % 12 | ||
38 | 38 | ||||
39 | def __str__(self): | 39 | def __str__(self): | ||
40 | return self.NAMED_INTERVALS[self.number_of_semitones] | 40 | return self.NAMED_INTERVALS[self.number_of_semitones] | ||
41 | 41 | ||||
42 | def get_index(self): | 42 | def get_index(self): | ||
43 | return self.NAMED_INTERVALS.index(str(self)) | 43 | return self.NAMED_INTERVALS.index(str(self)) | ||
44 | 44 | ||||
45 | def __add__(self, other): | 45 | def __add__(self, other): | ||
46 | return Interval(self.get_index() + other.get_index()) | 46 | return Interval(self.get_index() + other.get_index()) | ||
n | n | 47 | |||
48 | def __neg__(self): | ||||
49 | return Interval(-self.number_of_semitones) | ||||
47 | 50 | ||||
48 | 51 | ||||
49 | class Chord: | 52 | class Chord: | ||
50 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 53 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
51 | 54 | ||||
52 | def __init__(self, root, *tones): | 55 | def __init__(self, root, *tones): | ||
53 | self.root = root | 56 | self.root = root | ||
54 | tones_names = [curr_tone_name.name for curr_tone_name in tones] | 57 | tones_names = [curr_tone_name.name for curr_tone_name in tones] | ||
55 | tones_names = set(tones_names) | 58 | tones_names = set(tones_names) | ||
56 | self.unique_tones_names = {root.name} | tones_names | 59 | self.unique_tones_names = {root.name} | tones_names | ||
57 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | 60 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | ||
58 | if len(self.tones_unique) == 1: | 61 | if len(self.tones_unique) == 1: | ||
59 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 62 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
60 | self.create_right_orderd_list() | 63 | self.create_right_orderd_list() | ||
61 | 64 | ||||
62 | def create_right_orderd_list(self): | 65 | def create_right_orderd_list(self): | ||
63 | root_index = self.ALL_TONES.index(self.root.name) | 66 | root_index = self.ALL_TONES.index(self.root.name) | ||
64 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | 67 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | ||
65 | self.tones_unique = [Tone(tone) for tone in right_order_seq if tone in self.unique_tones_names] | 68 | self.tones_unique = [Tone(tone) for tone in right_order_seq if tone in self.unique_tones_names] | ||
66 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | 69 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | ||
67 | 70 | ||||
68 | def __str__(self): | 71 | def __str__(self): | ||
69 | return "-".join(str(tone) for tone in self.tones_unique) | 72 | return "-".join(str(tone) for tone in self.tones_unique) | ||
70 | 73 | ||||
71 | @classmethod | 74 | @classmethod | ||
72 | def index_of_tone(cls, tone): | 75 | def index_of_tone(cls, tone): | ||
73 | return cls.ALL_TONES.index(tone.name) | 76 | return cls.ALL_TONES.index(tone.name) | ||
74 | 77 | ||||
75 | @classmethod | 78 | @classmethod | ||
76 | def get_tone_by_index(cls, index): | 79 | def get_tone_by_index(cls, index): | ||
77 | return cls.ALL_TONES[index] | 80 | return cls.ALL_TONES[index] | ||
78 | 81 | ||||
79 | def is_major(self): | 82 | def is_major(self): | ||
80 | MAJOR_STRING = "major 3rd" | 83 | MAJOR_STRING = "major 3rd" | ||
81 | 84 | ||||
82 | for curr_tone in self.tones_unique: | 85 | for curr_tone in self.tones_unique: | ||
83 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 86 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
84 | if str(Interval(curr_dist)) == MAJOR_STRING: | 87 | if str(Interval(curr_dist)) == MAJOR_STRING: | ||
85 | return True | 88 | return True | ||
86 | return False | 89 | return False | ||
87 | 90 | ||||
88 | def is_minor(self): | 91 | def is_minor(self): | ||
89 | MINOR_STRING = "minor 3rd" | 92 | MINOR_STRING = "minor 3rd" | ||
90 | 93 | ||||
91 | for curr_tone in self.tones_unique: | 94 | for curr_tone in self.tones_unique: | ||
92 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 95 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
93 | if str(Interval(curr_dist)) == MINOR_STRING: | 96 | if str(Interval(curr_dist)) == MINOR_STRING: | ||
94 | return True | 97 | return True | ||
95 | return False | 98 | return False | ||
96 | 99 | ||||
97 | def is_power_chord(self): | 100 | def is_power_chord(self): | ||
98 | return not self.is_minor() and not self.is_major() | 101 | return not self.is_minor() and not self.is_major() | ||
99 | 102 | ||||
100 | def __add__(self, other): | 103 | def __add__(self, other): | ||
101 | if isinstance(other, Tone): | 104 | if isinstance(other, Tone): | ||
102 | return Chord(self.root, *(self.tones_unique + [other])) | 105 | return Chord(self.root, *(self.tones_unique + [other])) | ||
103 | elif isinstance(other, Chord): | 106 | elif isinstance(other, Chord): | ||
104 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | 107 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | ||
105 | 108 | ||||
106 | def __sub__(self, other): | 109 | def __sub__(self, other): | ||
107 | if isinstance(other, Tone): | 110 | if isinstance(other, Tone): | ||
108 | if other.name not in self.unique_tones_names: | 111 | if other.name not in self.unique_tones_names: | ||
109 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 112 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
110 | chord_tones = tuple(tone for tone in self.tones_unique if tone.name != other.name) | 113 | chord_tones = tuple(tone for tone in self.tones_unique if tone.name != other.name) | ||
111 | return Chord(self.root, *chord_tones) | 114 | return Chord(self.root, *chord_tones) | ||
112 | 115 | ||||
113 | def transposed(self, interval): | 116 | def transposed(self, interval): | ||
114 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | 117 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | ||
115 | return Chord(*transposed_chord) | 118 | return Chord(*transposed_chord) | ||
t | 116 | t | 119 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | def __init__(self, tone_name): | 2 | def __init__(self, tone_name): | ||
3 | self.tone_name = tone_name | 3 | self.tone_name = tone_name | ||
4 | 4 | ||||
5 | def __str__(self): | 5 | def __str__(self): | ||
6 | return str(self.tone_name) | 6 | return str(self.tone_name) | ||
7 | 7 | ||||
n | n | 8 | @property | ||
8 | def get_name(self): | 9 | def name(self): | ||
9 | return self.tone_name | 10 | return self.tone_name | ||
10 | 11 | ||||
11 | def __add__(self, other): | 12 | def __add__(self, other): | ||
12 | if isinstance(other, Tone): | 13 | if isinstance(other, Tone): | ||
13 | return Chord(self, other) | 14 | return Chord(self, other) | ||
14 | elif isinstance(other, Interval): | 15 | elif isinstance(other, Interval): | ||
15 | tone_index = Chord.index_of_tone(self) | 16 | tone_index = Chord.index_of_tone(self) | ||
16 | dist = other.number_of_semitones | 17 | dist = other.number_of_semitones | ||
17 | result_index = (tone_index + dist) % 12 | 18 | result_index = (tone_index + dist) % 12 | ||
18 | return Tone(Chord.get_tone_by_index(result_index)) | 19 | return Tone(Chord.get_tone_by_index(result_index)) | ||
19 | 20 | ||||
20 | def __sub__(self, other): | 21 | def __sub__(self, other): | ||
21 | if isinstance(other, Tone): | 22 | if isinstance(other, Tone): | ||
22 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | 23 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | ||
23 | elif isinstance(other, Interval): | 24 | elif isinstance(other, Interval): | ||
24 | tone_index = Chord.index_of_tone(self) | 25 | tone_index = Chord.index_of_tone(self) | ||
25 | dist = other.number_of_semitones | 26 | dist = other.number_of_semitones | ||
26 | res_index = (tone_index - dist) % 12 | 27 | res_index = (tone_index - dist) % 12 | ||
27 | return Tone(Chord.get_tone_by_index(res_index)) | 28 | return Tone(Chord.get_tone_by_index(res_index)) | ||
28 | 29 | ||||
29 | 30 | ||||
30 | class Interval: | 31 | class Interval: | ||
31 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | 32 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | ||
32 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | 33 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | ||
33 | "major 6th", "minor 7th", "major 7th") | 34 | "major 6th", "minor 7th", "major 7th") | ||
34 | 35 | ||||
35 | def __init__(self, number_of_semitones): | 36 | def __init__(self, number_of_semitones): | ||
36 | self.number_of_semitones = number_of_semitones % 12 | 37 | self.number_of_semitones = number_of_semitones % 12 | ||
37 | 38 | ||||
38 | def __str__(self): | 39 | def __str__(self): | ||
39 | return self.NAMED_INTERVALS[self.number_of_semitones] | 40 | return self.NAMED_INTERVALS[self.number_of_semitones] | ||
40 | 41 | ||||
41 | def get_index(self): | 42 | def get_index(self): | ||
42 | return self.NAMED_INTERVALS.index(str(self)) | 43 | return self.NAMED_INTERVALS.index(str(self)) | ||
43 | 44 | ||||
44 | def __add__(self, other): | 45 | def __add__(self, other): | ||
45 | return Interval(self.get_index() + other.get_index()) | 46 | return Interval(self.get_index() + other.get_index()) | ||
46 | 47 | ||||
n | n | 48 | |||
47 | class Chord: | 49 | class Chord: | ||
48 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 50 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
49 | 51 | ||||
50 | def __init__(self, root, *tones): | 52 | def __init__(self, root, *tones): | ||
51 | self.root = root | 53 | self.root = root | ||
n | 52 | tones_names = [curr_tone_name.get_name() for curr_tone_name in tones] | n | 54 | tones_names = [curr_tone_name.name for curr_tone_name in tones] |
53 | tones_names = set(tones_names) | 55 | tones_names = set(tones_names) | ||
n | 54 | self.unique_tones_names = {root.get_name()} | tones_names | n | 56 | self.unique_tones_names = {root.name} | tones_names |
55 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | 57 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | ||
56 | if len(self.tones_unique) == 1: | 58 | if len(self.tones_unique) == 1: | ||
57 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 59 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
58 | self.create_right_orderd_list() | 60 | self.create_right_orderd_list() | ||
59 | 61 | ||||
60 | def create_right_orderd_list(self): | 62 | def create_right_orderd_list(self): | ||
n | 61 | root_index = self.ALL_TONES.index(self.root.get_name()) | n | 63 | root_index = self.ALL_TONES.index(self.root.name) |
62 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | 64 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | ||
n | 63 | final_order_of_tones = [] | n | 65 | self.tones_unique = [Tone(tone) for tone in right_order_seq if tone in self.unique_tones_names] |
64 | for curr_tone in right_order_seq: | ||||
65 | if curr_tone in self.unique_tones_names: | ||||
66 | final_order_of_tones.append(Tone(curr_tone)) | ||||
67 | self.tones_unique = final_order_of_tones | ||||
68 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | 66 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | ||
69 | 67 | ||||
70 | def __str__(self): | 68 | def __str__(self): | ||
n | 71 | to_print = "-".join(str(tone) for tone in self.tones_unique) | n | 69 | return "-".join(str(tone) for tone in self.tones_unique) |
72 | return f"{to_print}" | ||||
73 | 70 | ||||
74 | @classmethod | 71 | @classmethod | ||
n | 75 | def index_of_tone(self, tone): | n | 72 | def index_of_tone(cls, tone): |
76 | return self.ALL_TONES.index(tone.get_name()) | 73 | return cls.ALL_TONES.index(tone.name) | ||
77 | 74 | ||||
78 | @classmethod | 75 | @classmethod | ||
n | 79 | def get_tone_by_index(self, index): | n | 76 | def get_tone_by_index(cls, index): |
80 | return self.ALL_TONES[index] | 77 | return cls.ALL_TONES[index] | ||
81 | 78 | ||||
82 | def is_major(self): | 79 | def is_major(self): | ||
83 | MAJOR_STRING = "major 3rd" | 80 | MAJOR_STRING = "major 3rd" | ||
84 | 81 | ||||
85 | for curr_tone in self.tones_unique: | 82 | for curr_tone in self.tones_unique: | ||
86 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 83 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
87 | if str(Interval(curr_dist)) == MAJOR_STRING: | 84 | if str(Interval(curr_dist)) == MAJOR_STRING: | ||
88 | return True | 85 | return True | ||
89 | return False | 86 | return False | ||
90 | 87 | ||||
91 | def is_minor(self): | 88 | def is_minor(self): | ||
92 | MINOR_STRING = "minor 3rd" | 89 | MINOR_STRING = "minor 3rd" | ||
n | 93 | n | 90 | ||
94 | for curr_tone in self.tones_unique: | 91 | for curr_tone in self.tones_unique: | ||
95 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 92 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
96 | if str(Interval(curr_dist)) == MINOR_STRING: | 93 | if str(Interval(curr_dist)) == MINOR_STRING: | ||
97 | return True | 94 | return True | ||
98 | return False | 95 | return False | ||
99 | 96 | ||||
100 | def is_power_chord(self): | 97 | def is_power_chord(self): | ||
101 | return not self.is_minor() and not self.is_major() | 98 | return not self.is_minor() and not self.is_major() | ||
102 | 99 | ||||
103 | def __add__(self, other): | 100 | def __add__(self, other): | ||
104 | if isinstance(other, Tone): | 101 | if isinstance(other, Tone): | ||
105 | return Chord(self.root, *(self.tones_unique + [other])) | 102 | return Chord(self.root, *(self.tones_unique + [other])) | ||
106 | elif isinstance(other, Chord): | 103 | elif isinstance(other, Chord): | ||
107 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | 104 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | ||
108 | 105 | ||||
109 | def __sub__(self, other): | 106 | def __sub__(self, other): | ||
110 | if isinstance(other, Tone): | 107 | if isinstance(other, Tone): | ||
n | 111 | if other.get_name() not in self.unique_tones_names: | n | 108 | if other.name not in self.unique_tones_names: |
112 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 109 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
n | 113 | chord_tones = tuple(tone for tone in self.tones_unique if tone.get_name() != other.get_name()) | n | 110 | chord_tones = tuple(tone for tone in self.tones_unique if tone.name != other.name) |
114 | return Chord(self.root, *chord_tones) | 111 | return Chord(self.root, *chord_tones) | ||
115 | 112 | ||||
116 | def transposed(self, interval): | 113 | def transposed(self, interval): | ||
117 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | 114 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | ||
t | 118 | return Chord(transposed_chord[0], *transposed_chord[1:]) | t | 115 | return Chord(*transposed_chord) |
116 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | def __init__(self, tone_name): | 2 | def __init__(self, tone_name): | ||
3 | self.tone_name = tone_name | 3 | self.tone_name = tone_name | ||
4 | 4 | ||||
5 | def __str__(self): | 5 | def __str__(self): | ||
6 | return str(self.tone_name) | 6 | return str(self.tone_name) | ||
7 | 7 | ||||
8 | def get_name(self): | 8 | def get_name(self): | ||
9 | return self.tone_name | 9 | return self.tone_name | ||
10 | 10 | ||||
11 | def __add__(self, other): | 11 | def __add__(self, other): | ||
12 | if isinstance(other, Tone): | 12 | if isinstance(other, Tone): | ||
13 | return Chord(self, other) | 13 | return Chord(self, other) | ||
14 | elif isinstance(other, Interval): | 14 | elif isinstance(other, Interval): | ||
15 | tone_index = Chord.index_of_tone(self) | 15 | tone_index = Chord.index_of_tone(self) | ||
16 | dist = other.number_of_semitones | 16 | dist = other.number_of_semitones | ||
17 | result_index = (tone_index + dist) % 12 | 17 | result_index = (tone_index + dist) % 12 | ||
18 | return Tone(Chord.get_tone_by_index(result_index)) | 18 | return Tone(Chord.get_tone_by_index(result_index)) | ||
19 | 19 | ||||
20 | def __sub__(self, other): | 20 | def __sub__(self, other): | ||
21 | if isinstance(other, Tone): | 21 | if isinstance(other, Tone): | ||
22 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | 22 | return Interval(Chord.index_of_tone(self) - Chord.index_of_tone(other)) | ||
23 | elif isinstance(other, Interval): | 23 | elif isinstance(other, Interval): | ||
24 | tone_index = Chord.index_of_tone(self) | 24 | tone_index = Chord.index_of_tone(self) | ||
25 | dist = other.number_of_semitones | 25 | dist = other.number_of_semitones | ||
26 | res_index = (tone_index - dist) % 12 | 26 | res_index = (tone_index - dist) % 12 | ||
27 | return Tone(Chord.get_tone_by_index(res_index)) | 27 | return Tone(Chord.get_tone_by_index(res_index)) | ||
28 | 28 | ||||
29 | 29 | ||||
30 | class Interval: | 30 | class Interval: | ||
31 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | 31 | NAMED_INTERVALS = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", | ||
32 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | 32 | "perfect 4th", "diminished 5th", "perfect 5th", "minor 6th", | ||
33 | "major 6th", "minor 7th", "major 7th") | 33 | "major 6th", "minor 7th", "major 7th") | ||
34 | 34 | ||||
35 | def __init__(self, number_of_semitones): | 35 | def __init__(self, number_of_semitones): | ||
36 | self.number_of_semitones = number_of_semitones % 12 | 36 | self.number_of_semitones = number_of_semitones % 12 | ||
37 | 37 | ||||
38 | def __str__(self): | 38 | def __str__(self): | ||
39 | return self.NAMED_INTERVALS[self.number_of_semitones] | 39 | return self.NAMED_INTERVALS[self.number_of_semitones] | ||
40 | 40 | ||||
41 | def get_index(self): | 41 | def get_index(self): | ||
42 | return self.NAMED_INTERVALS.index(str(self)) | 42 | return self.NAMED_INTERVALS.index(str(self)) | ||
43 | 43 | ||||
44 | def __add__(self, other): | 44 | def __add__(self, other): | ||
45 | return Interval(self.get_index() + other.get_index()) | 45 | return Interval(self.get_index() + other.get_index()) | ||
46 | 46 | ||||
47 | class Chord: | 47 | class Chord: | ||
48 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 48 | ALL_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
49 | 49 | ||||
50 | def __init__(self, root, *tones): | 50 | def __init__(self, root, *tones): | ||
51 | self.root = root | 51 | self.root = root | ||
52 | tones_names = [curr_tone_name.get_name() for curr_tone_name in tones] | 52 | tones_names = [curr_tone_name.get_name() for curr_tone_name in tones] | ||
53 | tones_names = set(tones_names) | 53 | tones_names = set(tones_names) | ||
54 | self.unique_tones_names = {root.get_name()} | tones_names | 54 | self.unique_tones_names = {root.get_name()} | tones_names | ||
55 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | 55 | self.tones_unique = [Tone(tone_name) for tone_name in self.unique_tones_names] | ||
56 | if len(self.tones_unique) == 1: | 56 | if len(self.tones_unique) == 1: | ||
57 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 57 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
58 | self.create_right_orderd_list() | 58 | self.create_right_orderd_list() | ||
59 | 59 | ||||
60 | def create_right_orderd_list(self): | 60 | def create_right_orderd_list(self): | ||
61 | root_index = self.ALL_TONES.index(self.root.get_name()) | 61 | root_index = self.ALL_TONES.index(self.root.get_name()) | ||
62 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | 62 | right_order_seq = self.ALL_TONES[root_index:] + self.ALL_TONES[:root_index] | ||
63 | final_order_of_tones = [] | 63 | final_order_of_tones = [] | ||
64 | for curr_tone in right_order_seq: | 64 | for curr_tone in right_order_seq: | ||
65 | if curr_tone in self.unique_tones_names: | 65 | if curr_tone in self.unique_tones_names: | ||
66 | final_order_of_tones.append(Tone(curr_tone)) | 66 | final_order_of_tones.append(Tone(curr_tone)) | ||
67 | self.tones_unique = final_order_of_tones | 67 | self.tones_unique = final_order_of_tones | ||
68 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | 68 | self.unique_tones_names = [str(tone) for tone in self.tones_unique] | ||
69 | 69 | ||||
70 | def __str__(self): | 70 | def __str__(self): | ||
71 | to_print = "-".join(str(tone) for tone in self.tones_unique) | 71 | to_print = "-".join(str(tone) for tone in self.tones_unique) | ||
72 | return f"{to_print}" | 72 | return f"{to_print}" | ||
73 | 73 | ||||
74 | @classmethod | 74 | @classmethod | ||
75 | def index_of_tone(self, tone): | 75 | def index_of_tone(self, tone): | ||
76 | return self.ALL_TONES.index(tone.get_name()) | 76 | return self.ALL_TONES.index(tone.get_name()) | ||
77 | 77 | ||||
78 | @classmethod | 78 | @classmethod | ||
79 | def get_tone_by_index(self, index): | 79 | def get_tone_by_index(self, index): | ||
80 | return self.ALL_TONES[index] | 80 | return self.ALL_TONES[index] | ||
81 | 81 | ||||
82 | def is_major(self): | 82 | def is_major(self): | ||
n | 83 | major_string = "major 3rd" | n | 83 | MAJOR_STRING = "major 3rd" |
84 | |||||
84 | for curr_tone in self.tones_unique: | 85 | for curr_tone in self.tones_unique: | ||
85 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 86 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
n | 86 | if str(Interval(curr_dist)) == major_string: | n | 87 | if str(Interval(curr_dist)) == MAJOR_STRING: |
87 | return True | 88 | return True | ||
88 | return False | 89 | return False | ||
89 | 90 | ||||
90 | def is_minor(self): | 91 | def is_minor(self): | ||
n | 91 | minor_string = "minor 3rd" | n | 92 | MINOR_STRING = "minor 3rd" |
93 | |||||
92 | for curr_tone in self.tones_unique: | 94 | for curr_tone in self.tones_unique: | ||
93 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | 95 | curr_dist = self.index_of_tone(curr_tone) - self.index_of_tone(self.root) | ||
t | 94 | if str(Interval(curr_dist)) == minor_string: | t | 96 | if str(Interval(curr_dist)) == MINOR_STRING: |
95 | return True | 97 | return True | ||
96 | return False | 98 | return False | ||
97 | 99 | ||||
98 | def is_power_chord(self): | 100 | def is_power_chord(self): | ||
99 | return not self.is_minor() and not self.is_major() | 101 | return not self.is_minor() and not self.is_major() | ||
100 | 102 | ||||
101 | def __add__(self, other): | 103 | def __add__(self, other): | ||
102 | if isinstance(other, Tone): | 104 | if isinstance(other, Tone): | ||
103 | return Chord(self.root, *(self.tones_unique + [other])) | 105 | return Chord(self.root, *(self.tones_unique + [other])) | ||
104 | elif isinstance(other, Chord): | 106 | elif isinstance(other, Chord): | ||
105 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | 107 | return Chord(self.root, *(self.tones_unique + other.tones_unique)) | ||
106 | 108 | ||||
107 | def __sub__(self, other): | 109 | def __sub__(self, other): | ||
108 | if isinstance(other, Tone): | 110 | if isinstance(other, Tone): | ||
109 | if other.get_name() not in self.unique_tones_names: | 111 | if other.get_name() not in self.unique_tones_names: | ||
110 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 112 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
111 | chord_tones = tuple(tone for tone in self.tones_unique if tone.get_name() != other.get_name()) | 113 | chord_tones = tuple(tone for tone in self.tones_unique if tone.get_name() != other.get_name()) | ||
112 | return Chord(self.root, *chord_tones) | 114 | return Chord(self.root, *chord_tones) | ||
113 | 115 | ||||
114 | def transposed(self, interval): | 116 | def transposed(self, interval): | ||
115 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | 117 | transposed_chord = [curr_tone + interval for curr_tone in self.tones_unique] | ||
116 | return Chord(transposed_chord[0], *transposed_chord[1:]) | 118 | return Chord(transposed_chord[0], *transposed_chord[1:]) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|