1INTERVALS = ['unison','minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th']
2ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
3MINOR_3RD = 3
4MAJOR_3RD = 4
5
6
7class Tone:
8 def __init__(self, name):
9 self.name = name
10
11 def __str__(self):
12 return self.name
13
14 def __add__(self, other):
15 if type(other) is Tone:
16 return Chord(self, other)
17 elif type(other) is Interval:
18 tone_index = other.number_of_semitones
19 tone_index += ORDER.index(self.name)
20 tone_index %= 12
21 return Tone(ORDER[tone_index])
22
23 def __sub__(self, other):
24 if type(other) is Interval:
25 return Tone(ORDER[(ORDER.index(self.name) - other.number_of_semitones) % 12])
26 elif type(other) is Tone:
27 return Interval(abs(ORDER.index(self.name) - ORDER.index(other.name)) % 12)
28
29 def __eq__(self, other):
30 return self.name == other.name
31
32
33class Interval:
34 def __init__(self, number_of_semitones):
35 self.number_of_semitones = number_of_semitones
36
37 def __str__(self):
38 interval = self.number_of_semitones % 12
39 return INTERVALS[interval]
40
41 def __add__(self, other):
42 if type(other) is Tone:
43 raise TypeError('Invalid operation')
44 elif type(other) is Interval:
45 res = (self.number_of_semitones + other.number_of_semitones) % 12
46 return Interval(res)
47
48 def __neg__(self):
49 return Interval(-self.number_of_semitones)
50
51
52
53def sort_tones(root, tones):
54 # rotating the ORDER list based on the root index
55 root_index = ORDER.index(root)
56 rotated_ORDER = ORDER[root_index:] + ORDER[:root_index]
57 ORDER_index = {char: idx for idx, char in enumerate(rotated_ORDER)}
58 return sorted(tones, key = lambda x: ORDER_index[x]) # custom sorting func
59
60
61def remove_duplicates(input_list):
62 seen = set()
63 return [x for x in input_list if not (x in seen or seen.add(x))]
64
65
66class Chord:
67 def __init__(self, main_tone, *args):
68 tones_names_list = []
69 for el in args:
70 tones_names_list.append(el.name)
71 if main_tone.name in tones_names_list:
72 tones_names_list.remove(main_tone.name)
73 tones_names = set(tones_names_list)
74 if len(tones_names) == 0:
75 raise TypeError('Cannot have a chord made of only 1 unique tone')
76 self.root = main_tone
77 self.tones = args
78
79 def __str__(self):
80 res = self.root.name
81 tones = []
82 for el in self.tones: tones.append(el.name)
83 sorted = remove_duplicates(sort_tones(self.root.name, tones))
84 for el in sorted:
85 if el != self.root.name:
86 res = f'{res}-{el}'
87 return res
88
89 def is_minor(self):
90 for el in self.tones:
91 if abs(ORDER.index(self.root.name) - ORDER.index(el.name)) == MINOR_3RD:
92 return True
93 return False
94
95 def is_major(self):
96 for el in self.tones:
97 if abs(ORDER.index(self.root.name) - ORDER.index(el.name)) == MAJOR_3RD:
98 return True
99 return False
100
101 def is_power_chord(self):
102 return not self.is_major() and not self.is_minor()
103
104 def __add__(self, other):
105 if type(other) is Tone:
106 return Chord(self.root, *self.tones, other)
107 elif type(other) is Chord:
108 return Chord(self.root, *self.tones, other.root, *other.tones)
109
110 def __sub__(self, other):
111 if type(other) is Tone:
112 tones = []
113 for el in self.tones: tones.append(el.name)
114 sorted = remove_duplicates(sort_tones(self.root.name, tones))
115 if other not in self.tones and other != self.root:
116 raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')
117 if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3:
118 raise TypeError('Cannot have a chord made of only 1 unique tone')
119 if self.root == other:
120 if sorted[0] != self.root:
121 self.root = Chord(sorted[0])
122 else:
123 self.root = Chord(sorted[1])
124 res = []
125 for el in sorted:
126 if el != other.name:
127 res.append(Tone(el))
128 return Chord(self.root, *res)
129
130 def transposed(self, interval):
131 tones = []
132 root = self.root
133 for el in self.tones:
134 tones.append(Tone(ORDER[(ORDER.index(el.name) + interval.number_of_semitones) % 12]))
135 root = Tone(ORDER[(ORDER.index(root.name) + interval.number_of_semitones) % 12])
136 return Chord(root, *tones)
137
138
F...FFF....................F.......F.
======================================================================
FAIL: test_chord_not_enough_tones (test.TestBasicChordFunctionality.test_chord_not_enough_tones)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 83, in test_chord_not_enough_tones
with self.assertRaises(TypeError) as err:
AssertionError: TypeError not raised
======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_is_major
self.assertTrue(a_major_chord.is_major())
AssertionError: False is not true
======================================================================
FAIL: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_is_minor
self.assertTrue(a_minor_chord.is_minor())
AssertionError: False is not true
======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_is_power_chord
self.assertFalse(a_minor_chord.is_power_chord())
AssertionError: True is not false
======================================================================
FAIL: test_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=6)
n | 1 | n | |||
2 | intervals = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 1 | INTERVALS = ['unison','minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th'] | ||
3 | order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 2 | ORDER = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
4 | minor_3rd = 3 | 3 | MINOR_3RD = 3 | ||
5 | major_3rd = 4 | 4 | MAJOR_3RD = 4 | ||
6 | 5 | ||||
7 | 6 | ||||
8 | class Tone: | 7 | class Tone: | ||
9 | def __init__(self, name): | 8 | def __init__(self, name): | ||
10 | self.name = name | 9 | self.name = name | ||
n | n | 10 | |||
11 | def __str__(self): | 11 | def __str__(self): | ||
12 | return self.name | 12 | return self.name | ||
n | n | 13 | |||
13 | def __add__(self, other): | 14 | def __add__(self, other): | ||
14 | if type(other) is Tone: | 15 | if type(other) is Tone: | ||
15 | return Chord(self, other) | 16 | return Chord(self, other) | ||
16 | elif type(other) is Interval: | 17 | elif type(other) is Interval: | ||
n | 17 | res = other.number_of_semitones | n | 18 | tone_index = other.number_of_semitones |
18 | res += order.index(self.name) | 19 | tone_index += ORDER.index(self.name) | ||
19 | res %= 12 | 20 | tone_index %= 12 | ||
20 | return Tone(order[res]) | 21 | return Tone(ORDER[tone_index]) | ||
22 | |||||
21 | def __sub__(self, other): | 23 | def __sub__(self, other): | ||
22 | if type(other) is Interval: | 24 | if type(other) is Interval: | ||
n | 23 | return Tone(order[(order.index(self.name) - other.number_of_semitones) % 12]) | n | 25 | return Tone(ORDER[(ORDER.index(self.name) - other.number_of_semitones) % 12]) |
24 | elif type(other) is Tone: | 26 | elif type(other) is Tone: | ||
n | 25 | return Interval(abs(order.index(self.name) - order.index(other.name)) % 12) | n | 27 | return Interval(abs(ORDER.index(self.name) - ORDER.index(other.name)) % 12) |
28 | |||||
26 | def __eq__(self, other): | 29 | def __eq__(self, other): | ||
27 | return self.name == other.name | 30 | return self.name == other.name | ||
28 | 31 | ||||
29 | 32 | ||||
30 | class Interval: | 33 | class Interval: | ||
31 | def __init__(self, number_of_semitones): | 34 | def __init__(self, number_of_semitones): | ||
32 | self.number_of_semitones = number_of_semitones | 35 | self.number_of_semitones = number_of_semitones | ||
n | n | 36 | |||
33 | def __str__(self): | 37 | def __str__(self): | ||
34 | interval = self.number_of_semitones % 12 | 38 | interval = self.number_of_semitones % 12 | ||
n | 35 | return intervals[interval] | n | 39 | return INTERVALS[interval] |
40 | |||||
36 | def __add__(self, other): | 41 | def __add__(self, other): | ||
37 | if type(other) is Tone: | 42 | if type(other) is Tone: | ||
38 | raise TypeError('Invalid operation') | 43 | raise TypeError('Invalid operation') | ||
39 | elif type(other) is Interval: | 44 | elif type(other) is Interval: | ||
40 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | 45 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | ||
41 | return Interval(res) | 46 | return Interval(res) | ||
n | n | 47 | |||
42 | def __neg__(self): | 48 | def __neg__(self): | ||
43 | return Interval(-self.number_of_semitones) | 49 | return Interval(-self.number_of_semitones) | ||
44 | 50 | ||||
45 | 51 | ||||
46 | 52 | ||||
n | 47 | def sortTones(root, tones): | n | 53 | def sort_tones(root, tones): |
48 | # rotating the order list based on the root index | 54 | # rotating the ORDER list based on the root index | ||
49 | root_index = order.index(root) | 55 | root_index = ORDER.index(root) | ||
50 | rotated_order = order[root_index:] + order[:root_index] | 56 | rotated_ORDER = ORDER[root_index:] + ORDER[:root_index] | ||
51 | order_index = {char: idx for idx, char in enumerate(rotated_order)} | 57 | ORDER_index = {char: idx for idx, char in enumerate(rotated_ORDER)} | ||
52 | return sorted(tones, key = lambda x: order_index[x]) # custom sorting func | 58 | return sorted(tones, key = lambda x: ORDER_index[x]) # custom sorting func | ||
53 | 59 | ||||
54 | 60 | ||||
55 | def remove_duplicates(input_list): | 61 | def remove_duplicates(input_list): | ||
56 | seen = set() | 62 | seen = set() | ||
57 | return [x for x in input_list if not (x in seen or seen.add(x))] | 63 | return [x for x in input_list if not (x in seen or seen.add(x))] | ||
n | n | 64 | |||
65 | |||||
58 | class Chord: | 66 | class Chord: | ||
59 | def __init__(self, main_tone, *args): | 67 | def __init__(self, main_tone, *args): | ||
60 | tones_names_list = [] | 68 | tones_names_list = [] | ||
n | n | 69 | for el in args: | ||
61 | for el in args: tones_names_list.append(el.name) | 70 | tones_names_list.append(el.name) | ||
62 | if main_tone.name in tones_names_list: | 71 | if main_tone.name in tones_names_list: | ||
63 | tones_names_list.remove(main_tone.name) | 72 | tones_names_list.remove(main_tone.name) | ||
64 | tones_names = set(tones_names_list) | 73 | tones_names = set(tones_names_list) | ||
65 | if len(tones_names) == 0: | 74 | if len(tones_names) == 0: | ||
66 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 75 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
67 | self.root = main_tone | 76 | self.root = main_tone | ||
68 | self.tones = args | 77 | self.tones = args | ||
n | n | 78 | |||
69 | def __str__(self): | 79 | def __str__(self): | ||
n | 70 | res = f'{self.root.name}' | n | 80 | res = self.root.name |
71 | tones = [] | 81 | tones = [] | ||
72 | for el in self.tones: tones.append(el.name) | 82 | for el in self.tones: tones.append(el.name) | ||
n | 73 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | n | 83 | sorted = remove_duplicates(sort_tones(self.root.name, tones)) |
74 | for el in sorted: | 84 | for el in sorted: | ||
75 | if el != self.root.name: | 85 | if el != self.root.name: | ||
n | 76 | res = res + f'-{el}' | n | 86 | res = f'{res}-{el}' |
77 | return res | 87 | return res | ||
n | n | 88 | |||
78 | def is_minor(self): | 89 | def is_minor(self): | ||
79 | for el in self.tones: | 90 | for el in self.tones: | ||
n | 80 | if abs(order.index(self.root.name) - order.index(el.name)) == minor_3rd: | n | 91 | if abs(ORDER.index(self.root.name) - ORDER.index(el.name)) == MINOR_3RD: |
81 | return True | 92 | return True | ||
82 | return False | 93 | return False | ||
n | n | 94 | |||
83 | def is_major(self): | 95 | def is_major(self): | ||
84 | for el in self.tones: | 96 | for el in self.tones: | ||
n | 85 | if abs(order.index(self.root.name) - order.index(el.name)) == major_3rd: | n | 97 | if abs(ORDER.index(self.root.name) - ORDER.index(el.name)) == MAJOR_3RD: |
86 | return True | 98 | return True | ||
87 | return False | 99 | return False | ||
n | n | 100 | |||
88 | def is_power_chord(self): | 101 | def is_power_chord(self): | ||
n | 89 | if self.is_major() or self.is_minor(): | n | 102 | return not self.is_major() and not self.is_minor() |
90 | return False | 103 | |||
91 | return True | ||||
92 | def __add__(self, other): | 104 | def __add__(self, other): | ||
93 | if type(other) is Tone: | 105 | if type(other) is Tone: | ||
94 | return Chord(self.root, *self.tones, other) | 106 | return Chord(self.root, *self.tones, other) | ||
95 | elif type(other) is Chord: | 107 | elif type(other) is Chord: | ||
96 | return Chord(self.root, *self.tones, other.root, *other.tones) | 108 | return Chord(self.root, *self.tones, other.root, *other.tones) | ||
n | n | 109 | |||
97 | def __sub__(self, other): | 110 | def __sub__(self, other): | ||
98 | if type(other) is Tone: | 111 | if type(other) is Tone: | ||
99 | tones = [] | 112 | tones = [] | ||
100 | for el in self.tones: tones.append(el.name) | 113 | for el in self.tones: tones.append(el.name) | ||
n | 101 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | n | 114 | sorted = remove_duplicates(sort_tones(self.root.name, tones)) |
102 | if other not in self.tones and other != self.root: | 115 | if other not in self.tones and other != self.root: | ||
103 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | 116 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | ||
104 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | 117 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | ||
105 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 118 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
106 | if self.root == other: | 119 | if self.root == other: | ||
107 | if sorted[0] != self.root: | 120 | if sorted[0] != self.root: | ||
108 | self.root = Chord(sorted[0]) | 121 | self.root = Chord(sorted[0]) | ||
109 | else: | 122 | else: | ||
110 | self.root = Chord(sorted[1]) | 123 | self.root = Chord(sorted[1]) | ||
111 | res = [] | 124 | res = [] | ||
112 | for el in sorted: | 125 | for el in sorted: | ||
113 | if el != other.name: | 126 | if el != other.name: | ||
114 | res.append(Tone(el)) | 127 | res.append(Tone(el)) | ||
115 | return Chord(self.root, *res) | 128 | return Chord(self.root, *res) | ||
n | n | 129 | |||
116 | def transposed(self, interval): | 130 | def transposed(self, interval): | ||
117 | tones = [] | 131 | tones = [] | ||
118 | root = self.root | 132 | root = self.root | ||
119 | for el in self.tones: | 133 | for el in self.tones: | ||
t | 120 | tones.append(Tone(order[(order.index(el.name) + interval.number_of_semitones) % 12])) | t | 134 | tones.append(Tone(ORDER[(ORDER.index(el.name) + interval.number_of_semitones) % 12])) |
121 | root = Tone(order[(order.index(root.name) + interval.number_of_semitones) % 12]) | 135 | root = Tone(ORDER[(ORDER.index(root.name) + interval.number_of_semitones) % 12]) | ||
122 | return Chord(root, *tones) | 136 | return Chord(root, *tones) | ||
123 | 137 | ||||
124 | 138 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
n | n | 1 | |||
2 | intervals = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||||
3 | order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||||
4 | minor_3rd = 3 | ||||
5 | major_3rd = 4 | ||||
6 | |||||
7 | |||||
1 | class Tone: | 8 | class Tone: | ||
2 | def __init__(self, name): | 9 | def __init__(self, name): | ||
3 | self.name = name | 10 | self.name = name | ||
4 | def __str__(self): | 11 | def __str__(self): | ||
5 | return self.name | 12 | return self.name | ||
6 | def __add__(self, other): | 13 | def __add__(self, other): | ||
7 | if type(other) is Tone: | 14 | if type(other) is Tone: | ||
8 | return Chord(self, other) | 15 | return Chord(self, other) | ||
9 | elif type(other) is Interval: | 16 | elif type(other) is Interval: | ||
10 | res = other.number_of_semitones | 17 | res = other.number_of_semitones | ||
11 | res += order.index(self.name) | 18 | res += order.index(self.name) | ||
12 | res %= 12 | 19 | res %= 12 | ||
13 | return Tone(order[res]) | 20 | return Tone(order[res]) | ||
14 | def __sub__(self, other): | 21 | def __sub__(self, other): | ||
15 | if type(other) is Interval: | 22 | if type(other) is Interval: | ||
16 | return Tone(order[(order.index(self.name) - other.number_of_semitones) % 12]) | 23 | return Tone(order[(order.index(self.name) - other.number_of_semitones) % 12]) | ||
17 | elif type(other) is Tone: | 24 | elif type(other) is Tone: | ||
18 | return Interval(abs(order.index(self.name) - order.index(other.name)) % 12) | 25 | return Interval(abs(order.index(self.name) - order.index(other.name)) % 12) | ||
19 | def __eq__(self, other): | 26 | def __eq__(self, other): | ||
20 | return self.name == other.name | 27 | return self.name == other.name | ||
21 | 28 | ||||
22 | 29 | ||||
n | 23 | intervals = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | n | ||
24 | class Interval: | 30 | class Interval: | ||
25 | def __init__(self, number_of_semitones): | 31 | def __init__(self, number_of_semitones): | ||
26 | self.number_of_semitones = number_of_semitones | 32 | self.number_of_semitones = number_of_semitones | ||
27 | def __str__(self): | 33 | def __str__(self): | ||
28 | interval = self.number_of_semitones % 12 | 34 | interval = self.number_of_semitones % 12 | ||
29 | return intervals[interval] | 35 | return intervals[interval] | ||
30 | def __add__(self, other): | 36 | def __add__(self, other): | ||
31 | if type(other) is Tone: | 37 | if type(other) is Tone: | ||
32 | raise TypeError('Invalid operation') | 38 | raise TypeError('Invalid operation') | ||
33 | elif type(other) is Interval: | 39 | elif type(other) is Interval: | ||
34 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | 40 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | ||
35 | return Interval(res) | 41 | return Interval(res) | ||
36 | def __neg__(self): | 42 | def __neg__(self): | ||
37 | return Interval(-self.number_of_semitones) | 43 | return Interval(-self.number_of_semitones) | ||
38 | 44 | ||||
39 | 45 | ||||
n | 40 | order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | n | 46 | |
41 | def sortTones(root, tones): | 47 | def sortTones(root, tones): | ||
42 | # rotating the order list based on the root index | 48 | # rotating the order list based on the root index | ||
43 | root_index = order.index(root) | 49 | root_index = order.index(root) | ||
44 | rotated_order = order[root_index:] + order[:root_index] | 50 | rotated_order = order[root_index:] + order[:root_index] | ||
45 | order_index = {char: idx for idx, char in enumerate(rotated_order)} | 51 | order_index = {char: idx for idx, char in enumerate(rotated_order)} | ||
46 | return sorted(tones, key = lambda x: order_index[x]) # custom sorting func | 52 | return sorted(tones, key = lambda x: order_index[x]) # custom sorting func | ||
47 | 53 | ||||
t | 48 | minor_3rd = 3 | t | 54 | |
49 | major_3rd = 4 | ||||
50 | def remove_duplicates(input_list): | 55 | def remove_duplicates(input_list): | ||
51 | seen = set() | 56 | seen = set() | ||
52 | return [x for x in input_list if not (x in seen or seen.add(x))] | 57 | return [x for x in input_list if not (x in seen or seen.add(x))] | ||
53 | class Chord: | 58 | class Chord: | ||
54 | def __init__(self, main_tone, *args): | 59 | def __init__(self, main_tone, *args): | ||
55 | tones_names_list = [] | 60 | tones_names_list = [] | ||
56 | for el in args: tones_names_list.append(el.name) | 61 | for el in args: tones_names_list.append(el.name) | ||
57 | if main_tone.name in tones_names_list: | 62 | if main_tone.name in tones_names_list: | ||
58 | tones_names_list.remove(main_tone.name) | 63 | tones_names_list.remove(main_tone.name) | ||
59 | tones_names = set(tones_names_list) | 64 | tones_names = set(tones_names_list) | ||
60 | if len(tones_names) == 0: | 65 | if len(tones_names) == 0: | ||
61 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 66 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
62 | self.root = main_tone | 67 | self.root = main_tone | ||
63 | self.tones = args | 68 | self.tones = args | ||
64 | def __str__(self): | 69 | def __str__(self): | ||
65 | res = f'{self.root.name}' | 70 | res = f'{self.root.name}' | ||
66 | tones = [] | 71 | tones = [] | ||
67 | for el in self.tones: tones.append(el.name) | 72 | for el in self.tones: tones.append(el.name) | ||
68 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | 73 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | ||
69 | for el in sorted: | 74 | for el in sorted: | ||
70 | if el != self.root.name: | 75 | if el != self.root.name: | ||
71 | res = res + f'-{el}' | 76 | res = res + f'-{el}' | ||
72 | return res | 77 | return res | ||
73 | def is_minor(self): | 78 | def is_minor(self): | ||
74 | for el in self.tones: | 79 | for el in self.tones: | ||
75 | if abs(order.index(self.root.name) - order.index(el.name)) == minor_3rd: | 80 | if abs(order.index(self.root.name) - order.index(el.name)) == minor_3rd: | ||
76 | return True | 81 | return True | ||
77 | return False | 82 | return False | ||
78 | def is_major(self): | 83 | def is_major(self): | ||
79 | for el in self.tones: | 84 | for el in self.tones: | ||
80 | if abs(order.index(self.root.name) - order.index(el.name)) == major_3rd: | 85 | if abs(order.index(self.root.name) - order.index(el.name)) == major_3rd: | ||
81 | return True | 86 | return True | ||
82 | return False | 87 | return False | ||
83 | def is_power_chord(self): | 88 | def is_power_chord(self): | ||
84 | if self.is_major() or self.is_minor(): | 89 | if self.is_major() or self.is_minor(): | ||
85 | return False | 90 | return False | ||
86 | return True | 91 | return True | ||
87 | def __add__(self, other): | 92 | def __add__(self, other): | ||
88 | if type(other) is Tone: | 93 | if type(other) is Tone: | ||
89 | return Chord(self.root, *self.tones, other) | 94 | return Chord(self.root, *self.tones, other) | ||
90 | elif type(other) is Chord: | 95 | elif type(other) is Chord: | ||
91 | return Chord(self.root, *self.tones, other.root, *other.tones) | 96 | return Chord(self.root, *self.tones, other.root, *other.tones) | ||
92 | def __sub__(self, other): | 97 | def __sub__(self, other): | ||
93 | if type(other) is Tone: | 98 | if type(other) is Tone: | ||
94 | tones = [] | 99 | tones = [] | ||
95 | for el in self.tones: tones.append(el.name) | 100 | for el in self.tones: tones.append(el.name) | ||
96 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | 101 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | ||
97 | if other not in self.tones and other != self.root: | 102 | if other not in self.tones and other != self.root: | ||
98 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | 103 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | ||
99 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | 104 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | ||
100 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 105 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
101 | if self.root == other: | 106 | if self.root == other: | ||
102 | if sorted[0] != self.root: | 107 | if sorted[0] != self.root: | ||
103 | self.root = Chord(sorted[0]) | 108 | self.root = Chord(sorted[0]) | ||
104 | else: | 109 | else: | ||
105 | self.root = Chord(sorted[1]) | 110 | self.root = Chord(sorted[1]) | ||
106 | res = [] | 111 | res = [] | ||
107 | for el in sorted: | 112 | for el in sorted: | ||
108 | if el != other.name: | 113 | if el != other.name: | ||
109 | res.append(Tone(el)) | 114 | res.append(Tone(el)) | ||
110 | return Chord(self.root, *res) | 115 | return Chord(self.root, *res) | ||
111 | def transposed(self, interval): | 116 | def transposed(self, interval): | ||
112 | tones = [] | 117 | tones = [] | ||
113 | root = self.root | 118 | root = self.root | ||
114 | for el in self.tones: | 119 | for el in self.tones: | ||
115 | tones.append(Tone(order[(order.index(el.name) + interval.number_of_semitones) % 12])) | 120 | tones.append(Tone(order[(order.index(el.name) + interval.number_of_semitones) % 12])) | ||
116 | root = Tone(order[(order.index(root.name) + interval.number_of_semitones) % 12]) | 121 | root = Tone(order[(order.index(root.name) + interval.number_of_semitones) % 12]) | ||
117 | return Chord(root, *tones) | 122 | return Chord(root, *tones) | ||
118 | 123 | ||||
119 | 124 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | class Tone: | f | 1 | class Tone: |
2 | def __init__(self, name): | 2 | def __init__(self, name): | ||
3 | self.name = name | 3 | self.name = name | ||
4 | def __str__(self): | 4 | def __str__(self): | ||
5 | return self.name | 5 | return self.name | ||
6 | def __add__(self, other): | 6 | def __add__(self, other): | ||
7 | if type(other) is Tone: | 7 | if type(other) is Tone: | ||
8 | return Chord(self, other) | 8 | return Chord(self, other) | ||
9 | elif type(other) is Interval: | 9 | elif type(other) is Interval: | ||
10 | res = other.number_of_semitones | 10 | res = other.number_of_semitones | ||
11 | res += order.index(self.name) | 11 | res += order.index(self.name) | ||
12 | res %= 12 | 12 | res %= 12 | ||
13 | return Tone(order[res]) | 13 | return Tone(order[res]) | ||
14 | def __sub__(self, other): | 14 | def __sub__(self, other): | ||
15 | if type(other) is Interval: | 15 | if type(other) is Interval: | ||
16 | return Tone(order[(order.index(self.name) - other.number_of_semitones) % 12]) | 16 | return Tone(order[(order.index(self.name) - other.number_of_semitones) % 12]) | ||
17 | elif type(other) is Tone: | 17 | elif type(other) is Tone: | ||
18 | return Interval(abs(order.index(self.name) - order.index(other.name)) % 12) | 18 | return Interval(abs(order.index(self.name) - order.index(other.name)) % 12) | ||
19 | def __eq__(self, other): | 19 | def __eq__(self, other): | ||
20 | return self.name == other.name | 20 | return self.name == other.name | ||
21 | 21 | ||||
22 | 22 | ||||
23 | intervals = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | 23 | intervals = {0 : 'unison', 1 : 'minor 2nd', 2 : 'major 2nd', 3 : 'minor 3rd', 4 : 'major 3rd', 5 : 'perfect 4th', 6 : 'diminished 5th', 7 : 'perfect 5th', 8 : 'minor 6th', 9 : 'major 6th', 10 : 'minor 7th', 11 : 'major 7th'} | ||
24 | class Interval: | 24 | class Interval: | ||
25 | def __init__(self, number_of_semitones): | 25 | def __init__(self, number_of_semitones): | ||
26 | self.number_of_semitones = number_of_semitones | 26 | self.number_of_semitones = number_of_semitones | ||
27 | def __str__(self): | 27 | def __str__(self): | ||
28 | interval = self.number_of_semitones % 12 | 28 | interval = self.number_of_semitones % 12 | ||
29 | return intervals[interval] | 29 | return intervals[interval] | ||
30 | def __add__(self, other): | 30 | def __add__(self, other): | ||
31 | if type(other) is Tone: | 31 | if type(other) is Tone: | ||
32 | raise TypeError('Invalid operation') | 32 | raise TypeError('Invalid operation') | ||
33 | elif type(other) is Interval: | 33 | elif type(other) is Interval: | ||
34 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | 34 | res = (self.number_of_semitones + other.number_of_semitones) % 12 | ||
35 | return Interval(res) | 35 | return Interval(res) | ||
36 | def __neg__(self): | 36 | def __neg__(self): | ||
37 | return Interval(-self.number_of_semitones) | 37 | return Interval(-self.number_of_semitones) | ||
38 | 38 | ||||
39 | 39 | ||||
40 | order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | 40 | order = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'] | ||
41 | def sortTones(root, tones): | 41 | def sortTones(root, tones): | ||
42 | # rotating the order list based on the root index | 42 | # rotating the order list based on the root index | ||
43 | root_index = order.index(root) | 43 | root_index = order.index(root) | ||
44 | rotated_order = order[root_index:] + order[:root_index] | 44 | rotated_order = order[root_index:] + order[:root_index] | ||
45 | order_index = {char: idx for idx, char in enumerate(rotated_order)} | 45 | order_index = {char: idx for idx, char in enumerate(rotated_order)} | ||
46 | return sorted(tones, key = lambda x: order_index[x]) # custom sorting func | 46 | return sorted(tones, key = lambda x: order_index[x]) # custom sorting func | ||
47 | 47 | ||||
48 | minor_3rd = 3 | 48 | minor_3rd = 3 | ||
49 | major_3rd = 4 | 49 | major_3rd = 4 | ||
50 | def remove_duplicates(input_list): | 50 | def remove_duplicates(input_list): | ||
51 | seen = set() | 51 | seen = set() | ||
52 | return [x for x in input_list if not (x in seen or seen.add(x))] | 52 | return [x for x in input_list if not (x in seen or seen.add(x))] | ||
53 | class Chord: | 53 | class Chord: | ||
54 | def __init__(self, main_tone, *args): | 54 | def __init__(self, main_tone, *args): | ||
55 | tones_names_list = [] | 55 | tones_names_list = [] | ||
56 | for el in args: tones_names_list.append(el.name) | 56 | for el in args: tones_names_list.append(el.name) | ||
57 | if main_tone.name in tones_names_list: | 57 | if main_tone.name in tones_names_list: | ||
58 | tones_names_list.remove(main_tone.name) | 58 | tones_names_list.remove(main_tone.name) | ||
59 | tones_names = set(tones_names_list) | 59 | tones_names = set(tones_names_list) | ||
60 | if len(tones_names) == 0: | 60 | if len(tones_names) == 0: | ||
61 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 61 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
62 | self.root = main_tone | 62 | self.root = main_tone | ||
63 | self.tones = args | 63 | self.tones = args | ||
64 | def __str__(self): | 64 | def __str__(self): | ||
65 | res = f'{self.root.name}' | 65 | res = f'{self.root.name}' | ||
66 | tones = [] | 66 | tones = [] | ||
67 | for el in self.tones: tones.append(el.name) | 67 | for el in self.tones: tones.append(el.name) | ||
68 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | 68 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | ||
69 | for el in sorted: | 69 | for el in sorted: | ||
70 | if el != self.root.name: | 70 | if el != self.root.name: | ||
71 | res = res + f'-{el}' | 71 | res = res + f'-{el}' | ||
72 | return res | 72 | return res | ||
73 | def is_minor(self): | 73 | def is_minor(self): | ||
74 | for el in self.tones: | 74 | for el in self.tones: | ||
75 | if abs(order.index(self.root.name) - order.index(el.name)) == minor_3rd: | 75 | if abs(order.index(self.root.name) - order.index(el.name)) == minor_3rd: | ||
76 | return True | 76 | return True | ||
77 | return False | 77 | return False | ||
78 | def is_major(self): | 78 | def is_major(self): | ||
79 | for el in self.tones: | 79 | for el in self.tones: | ||
80 | if abs(order.index(self.root.name) - order.index(el.name)) == major_3rd: | 80 | if abs(order.index(self.root.name) - order.index(el.name)) == major_3rd: | ||
81 | return True | 81 | return True | ||
82 | return False | 82 | return False | ||
83 | def is_power_chord(self): | 83 | def is_power_chord(self): | ||
84 | if self.is_major() or self.is_minor(): | 84 | if self.is_major() or self.is_minor(): | ||
85 | return False | 85 | return False | ||
86 | return True | 86 | return True | ||
87 | def __add__(self, other): | 87 | def __add__(self, other): | ||
88 | if type(other) is Tone: | 88 | if type(other) is Tone: | ||
89 | return Chord(self.root, *self.tones, other) | 89 | return Chord(self.root, *self.tones, other) | ||
90 | elif type(other) is Chord: | 90 | elif type(other) is Chord: | ||
91 | return Chord(self.root, *self.tones, other.root, *other.tones) | 91 | return Chord(self.root, *self.tones, other.root, *other.tones) | ||
92 | def __sub__(self, other): | 92 | def __sub__(self, other): | ||
93 | if type(other) is Tone: | 93 | if type(other) is Tone: | ||
94 | tones = [] | 94 | tones = [] | ||
95 | for el in self.tones: tones.append(el.name) | 95 | for el in self.tones: tones.append(el.name) | ||
96 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | 96 | sorted = remove_duplicates(sortTones(self.root.name, tones)) | ||
97 | if other not in self.tones and other != self.root: | 97 | if other not in self.tones and other != self.root: | ||
98 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | 98 | raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}') | ||
99 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | 99 | if self.root.name in sorted and len(sorted) < 3 or len(sorted) + 1 < 3: | ||
100 | raise TypeError('Cannot have a chord made of only 1 unique tone') | 100 | raise TypeError('Cannot have a chord made of only 1 unique tone') | ||
101 | if self.root == other: | 101 | if self.root == other: | ||
102 | if sorted[0] != self.root: | 102 | if sorted[0] != self.root: | ||
103 | self.root = Chord(sorted[0]) | 103 | self.root = Chord(sorted[0]) | ||
104 | else: | 104 | else: | ||
105 | self.root = Chord(sorted[1]) | 105 | self.root = Chord(sorted[1]) | ||
106 | res = [] | 106 | res = [] | ||
107 | for el in sorted: | 107 | for el in sorted: | ||
108 | if el != other.name: | 108 | if el != other.name: | ||
109 | res.append(Tone(el)) | 109 | res.append(Tone(el)) | ||
110 | return Chord(self.root, *res) | 110 | return Chord(self.root, *res) | ||
111 | def transposed(self, interval): | 111 | def transposed(self, interval): | ||
112 | tones = [] | 112 | tones = [] | ||
113 | root = self.root | 113 | root = self.root | ||
114 | for el in self.tones: | 114 | for el in self.tones: | ||
115 | tones.append(Tone(order[(order.index(el.name) + interval.number_of_semitones) % 12])) | 115 | tones.append(Tone(order[(order.index(el.name) + interval.number_of_semitones) % 12])) | ||
116 | root = Tone(order[(order.index(root.name) + interval.number_of_semitones) % 12]) | 116 | root = Tone(order[(order.index(root.name) + interval.number_of_semitones) % 12]) | ||
117 | return Chord(root, *tones) | 117 | return Chord(root, *tones) | ||
118 | 118 | ||||
t | 119 | c = Tone("C") | t | ||
120 | perfect_fifth = Interval(7) | ||||
121 | result_tone = c - perfect_fifth | ||||
122 | print(str(result_tone))#, "F") | ||||
123 | 119 |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|