1ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
  2INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
  3                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
  4
  5
  6class Tone:
  7
  8    def __init__(self, tone):
  9        if tone not in ACCEPTED_TONES:
 10            raise TypeError("Tone is not accepted")
 11        self.tone = tone
 12
 13    def __str__(self):
 14        return self.tone
 15
 16    def __eq__(self, other):
 17        return self.tone == other.tone
 18
 19    def __hash__(self):
 20        return hash(self.tone)
 21
 22    def __add__(self, other):
 23        if isinstance(other, Tone):
 24            return Chord(self, other)
 25        
 26        if isinstance(other, Interval):
 27            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])
 28
 29        raise TypeError("Invalid operation")
 30
 31    def __sub__(self, other):
 32        if isinstance(other, Tone):
 33            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
 34
 35        if isinstance(other, Interval):
 36            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
 37
 38        raise TypeError("Invalid operation")
 39
 40
 41class Interval:
 42
 43    def __init__(self, interval_length):
 44        self.length = interval_length % len(INTERVAL_VALUES)
 45
 46    def __str__(self):
 47        return INTERVAL_VALUES[self.length]
 48
 49    def __add__(self, other):
 50
 51        if isinstance(other, Interval):
 52            return Interval(self.length + other.length)
 53
 54        raise TypeError("Invalid operation")
 55
 56    def __neg__(self):
 57        return Interval(-self.length)
 58
 59
 60class Chord:
 61
 62    def __init__(self, root_tone, *other_tone_args):
 63
 64        self.root_tone = root_tone
 65        self.tone_set = set()
 66        self.tone_set.add(root_tone)
 67
 68        for arg in other_tone_args:
 69            if isinstance(arg, set):
 70                for tone in arg:
 71                    self.tone_set.add(tone)
 72            else:
 73                self.tone_set.add(arg)
 74
 75        if len(self.tone_set) < 2:
 76            raise TypeError("Cannot have a chord made of only 1 unique tone")
 77
 78    def __str__(self):
 79        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
 80        list_of_tone_indexes.sort()
 81
 82        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
 83
 84        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
 85
 86        for tone in tone_indexes_before_root:
 87            list_of_tone_indexes.remove(tone)
 88
 89        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
 90                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
 91
 92        return result
 93
 94    def __add__(self, other):
 95
 96        if isinstance(other, Tone):
 97            return Chord(self.root_tone, self.tone_set, other)
 98
 99        if isinstance(other, Chord):
100            return Chord(self.root_tone, self.tone_set, other.tone_set)
101
102        raise TypeError("Invalid operation")
103
104    def find_next_tone(self, reference_tone, tone_set):
105
106        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)
107
108        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):
109            potential_tone = Tone(ACCEPTED_TONES[i])
110            if potential_tone in tone_set:
111                return potential_tone
112
113        for i in range(0, reference_tone_index):
114            potential_tone = Tone(ACCEPTED_TONES[i])
115            if potential_tone in tone_set:
116                return potential_tone
117
118    def __sub__(self, other):
119        if isinstance(other, Tone):
120            if other not in self.tone_set:
121                raise TypeError(f"Cannot remove tone {other} from chord {self}")
122
123            new_tone_set = self.tone_set.copy()
124            new_tone_set.remove(other)
125
126            if other == self.root_tone:
127                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)
128                return Chord(new_root_tone, new_tone_set)
129
130            return Chord(self.root_tone, new_tone_set)
131
132        raise TypeError("Invalid operation")
133
134    def is_minor(self):
135        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
136        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
137
138        for tone in self.tone_set:
139            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
140                return True
141
142        return False
143
144    def is_major(self):
145        minor_third_index = INTERVAL_VALUES.index("major 3rd")
146        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
147
148        for tone in self.tone_set:
149            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
150                return True
151
152        return False
153
154    def is_power_chord(self):
155        return not self.is_minor() and not self.is_major()
156
157    def transposed(self, interval):
158        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))
....FFF....................F.......F.
======================================================================
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.003s
FAILED (failures=5)
                            
    
 
    
        Георги Балтиев
         
    
06.11.2024 12:11добавих вече функциите които ми липсваха 
                            
                            
                         | 
| t | 1 | t | |||
| 2 | import unittest | ||||
| 3 | 1 | ||||
| 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 2 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
| 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | 3 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | ||
| 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | 4 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
| 7 | 5 | ||||
| 8 | 6 | ||||
| 9 | class Tone: | 7 | class Tone: | ||
| 10 | 8 | ||||
| 11 | def __init__(self, tone): | 9 | def __init__(self, tone): | ||
| 12 | if tone not in ACCEPTED_TONES: | 10 | if tone not in ACCEPTED_TONES: | ||
| 13 | raise TypeError("Tone is not accepted") | 11 | raise TypeError("Tone is not accepted") | ||
| 14 | self.tone = tone | 12 | self.tone = tone | ||
| 15 | 13 | ||||
| 16 | def __str__(self): | 14 | def __str__(self): | ||
| 17 | return self.tone | 15 | return self.tone | ||
| 18 | 16 | ||||
| 19 | def __eq__(self, other): | 17 | def __eq__(self, other): | ||
| 20 | return self.tone == other.tone | 18 | return self.tone == other.tone | ||
| 21 | 19 | ||||
| 22 | def __hash__(self): | 20 | def __hash__(self): | ||
| 23 | return hash(self.tone) | 21 | return hash(self.tone) | ||
| 24 | 22 | ||||
| 25 | def __add__(self, other): | 23 | def __add__(self, other): | ||
| 26 | if isinstance(other, Tone): | 24 | if isinstance(other, Tone): | ||
| 27 | return Chord(self, other) | 25 | return Chord(self, other) | ||
| 28 | 26 | ||||
| 29 | if isinstance(other, Interval): | 27 | if isinstance(other, Interval): | ||
| 30 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)]) | 28 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)]) | ||
| 31 | 29 | ||||
| 32 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 33 | 31 | ||||
| 34 | def __sub__(self, other): | 32 | def __sub__(self, other): | ||
| 35 | if isinstance(other, Tone): | 33 | if isinstance(other, Tone): | ||
| 36 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | 34 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | ||
| 37 | 35 | ||||
| 38 | if isinstance(other, Interval): | 36 | if isinstance(other, Interval): | ||
| 39 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | 37 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | ||
| 40 | 38 | ||||
| 41 | raise TypeError("Invalid operation") | 39 | raise TypeError("Invalid operation") | ||
| 42 | 40 | ||||
| 43 | 41 | ||||
| 44 | class Interval: | 42 | class Interval: | ||
| 45 | 43 | ||||
| 46 | def __init__(self, interval_length): | 44 | def __init__(self, interval_length): | ||
| 47 | self.length = interval_length % len(INTERVAL_VALUES) | 45 | self.length = interval_length % len(INTERVAL_VALUES) | ||
| 48 | 46 | ||||
| 49 | def __str__(self): | 47 | def __str__(self): | ||
| 50 | return INTERVAL_VALUES[self.length] | 48 | return INTERVAL_VALUES[self.length] | ||
| 51 | 49 | ||||
| 52 | def __add__(self, other): | 50 | def __add__(self, other): | ||
| 53 | 51 | ||||
| 54 | if isinstance(other, Interval): | 52 | if isinstance(other, Interval): | ||
| 55 | return Interval(self.length + other.length) | 53 | return Interval(self.length + other.length) | ||
| 56 | 54 | ||||
| 57 | raise TypeError("Invalid operation") | 55 | raise TypeError("Invalid operation") | ||
| 58 | 56 | ||||
| 59 | def __neg__(self): | 57 | def __neg__(self): | ||
| 60 | return Interval(-self.length) | 58 | return Interval(-self.length) | ||
| 61 | 59 | ||||
| 62 | 60 | ||||
| 63 | class Chord: | 61 | class Chord: | ||
| 64 | 62 | ||||
| 65 | def __init__(self, root_tone, *other_tone_args): | 63 | def __init__(self, root_tone, *other_tone_args): | ||
| 66 | 64 | ||||
| 67 | self.root_tone = root_tone | 65 | self.root_tone = root_tone | ||
| 68 | self.tone_set = set() | 66 | self.tone_set = set() | ||
| 69 | self.tone_set.add(root_tone) | 67 | self.tone_set.add(root_tone) | ||
| 70 | 68 | ||||
| 71 | for arg in other_tone_args: | 69 | for arg in other_tone_args: | ||
| 72 | if isinstance(arg, set): | 70 | if isinstance(arg, set): | ||
| 73 | for tone in arg: | 71 | for tone in arg: | ||
| 74 | self.tone_set.add(tone) | 72 | self.tone_set.add(tone) | ||
| 75 | else: | 73 | else: | ||
| 76 | self.tone_set.add(arg) | 74 | self.tone_set.add(arg) | ||
| 77 | 75 | ||||
| 78 | if len(self.tone_set) < 2: | 76 | if len(self.tone_set) < 2: | ||
| 79 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 77 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 80 | 78 | ||||
| 81 | def __str__(self): | 79 | def __str__(self): | ||
| 82 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | 80 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | ||
| 83 | list_of_tone_indexes.sort() | 81 | list_of_tone_indexes.sort() | ||
| 84 | 82 | ||||
| 85 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 83 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 86 | 84 | ||||
| 87 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | 85 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | ||
| 88 | 86 | ||||
| 89 | for tone in tone_indexes_before_root: | 87 | for tone in tone_indexes_before_root: | ||
| 90 | list_of_tone_indexes.remove(tone) | 88 | list_of_tone_indexes.remove(tone) | ||
| 91 | 89 | ||||
| 92 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | 90 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | ||
| 93 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | 91 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | ||
| 94 | 92 | ||||
| 95 | return result | 93 | return result | ||
| 96 | 94 | ||||
| 97 | def __add__(self, other): | 95 | def __add__(self, other): | ||
| 98 | 96 | ||||
| 99 | if isinstance(other, Tone): | 97 | if isinstance(other, Tone): | ||
| 100 | return Chord(self.root_tone, self.tone_set, other) | 98 | return Chord(self.root_tone, self.tone_set, other) | ||
| 101 | 99 | ||||
| 102 | if isinstance(other, Chord): | 100 | if isinstance(other, Chord): | ||
| 103 | return Chord(self.root_tone, self.tone_set, other.tone_set) | 101 | return Chord(self.root_tone, self.tone_set, other.tone_set) | ||
| 104 | 102 | ||||
| 105 | raise TypeError("Invalid operation") | 103 | raise TypeError("Invalid operation") | ||
| 106 | 104 | ||||
| 107 | def find_next_tone(self, reference_tone, tone_set): | 105 | def find_next_tone(self, reference_tone, tone_set): | ||
| 108 | 106 | ||||
| 109 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | 107 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | ||
| 110 | 108 | ||||
| 111 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | 109 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | ||
| 112 | potential_tone = Tone(ACCEPTED_TONES[i]) | 110 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 113 | if potential_tone in tone_set: | 111 | if potential_tone in tone_set: | ||
| 114 | return potential_tone | 112 | return potential_tone | ||
| 115 | 113 | ||||
| 116 | for i in range(0, reference_tone_index): | 114 | for i in range(0, reference_tone_index): | ||
| 117 | potential_tone = Tone(ACCEPTED_TONES[i]) | 115 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 118 | if potential_tone in tone_set: | 116 | if potential_tone in tone_set: | ||
| 119 | return potential_tone | 117 | return potential_tone | ||
| 120 | 118 | ||||
| 121 | def __sub__(self, other): | 119 | def __sub__(self, other): | ||
| 122 | if isinstance(other, Tone): | 120 | if isinstance(other, Tone): | ||
| 123 | if other not in self.tone_set: | 121 | if other not in self.tone_set: | ||
| 124 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 125 | 123 | ||||
| 126 | new_tone_set = self.tone_set.copy() | 124 | new_tone_set = self.tone_set.copy() | ||
| 127 | new_tone_set.remove(other) | 125 | new_tone_set.remove(other) | ||
| 128 | 126 | ||||
| 129 | if other == self.root_tone: | 127 | if other == self.root_tone: | ||
| 130 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | 128 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | ||
| 131 | return Chord(new_root_tone, new_tone_set) | 129 | return Chord(new_root_tone, new_tone_set) | ||
| 132 | 130 | ||||
| 133 | return Chord(self.root_tone, new_tone_set) | 131 | return Chord(self.root_tone, new_tone_set) | ||
| 134 | 132 | ||||
| 135 | raise TypeError("Invalid operation") | 133 | raise TypeError("Invalid operation") | ||
| 136 | 134 | ||||
| 137 | def is_minor(self): | 135 | def is_minor(self): | ||
| 138 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | 136 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | ||
| 139 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 137 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 140 | 138 | ||||
| 141 | for tone in self.tone_set: | 139 | for tone in self.tone_set: | ||
| 142 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 140 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 143 | return True | 141 | return True | ||
| 144 | 142 | ||||
| 145 | return False | 143 | return False | ||
| 146 | 144 | ||||
| 147 | def is_major(self): | 145 | def is_major(self): | ||
| 148 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | 146 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | ||
| 149 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 147 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 150 | 148 | ||||
| 151 | for tone in self.tone_set: | 149 | for tone in self.tone_set: | ||
| 152 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 150 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 153 | return True | 151 | return True | ||
| 154 | 152 | ||||
| 155 | return False | 153 | return False | ||
| 156 | 154 | ||||
| 157 | def is_power_chord(self): | 155 | def is_power_chord(self): | ||
| 158 | return not self.is_minor() and not self.is_major() | 156 | return not self.is_minor() and not self.is_major() | ||
| 159 | 157 | ||||
| 160 | def transposed(self, interval): | 158 | def transposed(self, interval): | ||
| 161 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | 159 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| n | n | 1 | |||
| 2 | import unittest | ||||
| 1 | 3 | ||||
| 2 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
| 3 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | ||
| 4 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
| 5 | 7 | ||||
| 6 | 8 | ||||
| 7 | class Tone: | 9 | class Tone: | ||
| 8 | 10 | ||||
| 9 | def __init__(self, tone): | 11 | def __init__(self, tone): | ||
| 10 | if tone not in ACCEPTED_TONES: | 12 | if tone not in ACCEPTED_TONES: | ||
| 11 | raise TypeError("Tone is not accepted") | 13 | raise TypeError("Tone is not accepted") | ||
| 12 | self.tone = tone | 14 | self.tone = tone | ||
| 13 | 15 | ||||
| 14 | def __str__(self): | 16 | def __str__(self): | ||
| 15 | return self.tone | 17 | return self.tone | ||
| 16 | 18 | ||||
| 17 | def __eq__(self, other): | 19 | def __eq__(self, other): | ||
| n | 18 | if type(other) is not Tone: | n | ||
| 19 | return False | ||||
| 20 | return self.tone == other.tone | 20 | return self.tone == other.tone | ||
| 21 | 21 | ||||
| 22 | def __hash__(self): | 22 | def __hash__(self): | ||
| 23 | return hash(self.tone) | 23 | return hash(self.tone) | ||
| 24 | 24 | ||||
| 25 | def __add__(self, other): | 25 | def __add__(self, other): | ||
| n | 26 | if type(other) is Tone: | n | 26 | if isinstance(other, Tone): | 
| 27 | return Chord(self, other) | 27 | return Chord(self, other) | ||
| 28 | 28 | ||||
| n | 29 | if type(other) is Interval: | n | 29 | if isinstance(other, Interval): | 
| 30 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)]) | 30 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)]) | ||
| 31 | 31 | ||||
| 32 | raise TypeError("Invalid operation") | 32 | raise TypeError("Invalid operation") | ||
| 33 | 33 | ||||
| 34 | def __sub__(self, other): | 34 | def __sub__(self, other): | ||
| n | 35 | if type(other) is Tone: | n | 35 | if isinstance(other, Tone): | 
| 36 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | 36 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | ||
| 37 | 37 | ||||
| n | 38 | if type(other) is Interval: | n | 38 | if isinstance(other, Interval): | 
| 39 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | 39 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | ||
| 40 | 40 | ||||
| 41 | raise TypeError("Invalid operation") | 41 | raise TypeError("Invalid operation") | ||
| 42 | 42 | ||||
| 43 | 43 | ||||
| 44 | class Interval: | 44 | class Interval: | ||
| 45 | 45 | ||||
| 46 | def __init__(self, interval_length): | 46 | def __init__(self, interval_length): | ||
| n | 47 | if type(interval_length) is not int: | n | ||
| 48 | raise TypeError("Interval length is not int") | ||||
| 49 | |||||
| 50 | self.length = interval_length % len(INTERVAL_VALUES) | 47 | self.length = interval_length % len(INTERVAL_VALUES) | ||
| 51 | 48 | ||||
| 52 | def __str__(self): | 49 | def __str__(self): | ||
| n | 53 | return INTERVAL_VALUES[abs(self.length)] | n | 50 | return INTERVAL_VALUES[self.length] | 
| 54 | 51 | ||||
| 55 | def __add__(self, other): | 52 | def __add__(self, other): | ||
| n | 56 | return INTERVAL_VALUES[(self.length + other.length) % len(INTERVAL_VALUES)] | n | 53 | |
| 54 | if isinstance(other, Interval): | ||||
| 55 | return Interval(self.length + other.length) | ||||
| 56 | |||||
| 57 | raise TypeError("Invalid operation") | ||||
| 57 | 58 | ||||
| 58 | def __neg__(self): | 59 | def __neg__(self): | ||
| n | 59 | reference = Interval(self.length) | n | 60 | return Interval(-self.length) | 
| 60 | reference.length *= -1 | ||||
| 61 | return reference | ||||
| 62 | 61 | ||||
| 63 | 62 | ||||
| 64 | class Chord: | 63 | class Chord: | ||
| 65 | 64 | ||||
| 66 | def __init__(self, root_tone, *other_tone_args): | 65 | def __init__(self, root_tone, *other_tone_args): | ||
| 67 | 66 | ||||
| 68 | self.root_tone = root_tone | 67 | self.root_tone = root_tone | ||
| 69 | self.tone_set = set() | 68 | self.tone_set = set() | ||
| 70 | self.tone_set.add(root_tone) | 69 | self.tone_set.add(root_tone) | ||
| 71 | 70 | ||||
| 72 | for arg in other_tone_args: | 71 | for arg in other_tone_args: | ||
| n | 73 | if type(arg) is set: | n | 72 | if isinstance(arg, set): | 
| 74 | for tone in arg: | 73 | for tone in arg: | ||
| 75 | self.tone_set.add(tone) | 74 | self.tone_set.add(tone) | ||
| 76 | else: | 75 | else: | ||
| 77 | self.tone_set.add(arg) | 76 | self.tone_set.add(arg) | ||
| 78 | 77 | ||||
| 79 | if len(self.tone_set) < 2: | 78 | if len(self.tone_set) < 2: | ||
| 80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 79 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 81 | 80 | ||||
| 82 | def __str__(self): | 81 | def __str__(self): | ||
| 83 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | 82 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | ||
| 84 | list_of_tone_indexes.sort() | 83 | list_of_tone_indexes.sort() | ||
| 85 | 84 | ||||
| 86 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 85 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 87 | 86 | ||||
| 88 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | 87 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | ||
| 89 | 88 | ||||
| 90 | for tone in tone_indexes_before_root: | 89 | for tone in tone_indexes_before_root: | ||
| 91 | list_of_tone_indexes.remove(tone) | 90 | list_of_tone_indexes.remove(tone) | ||
| 92 | 91 | ||||
| 93 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | 92 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | ||
| 94 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | 93 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | ||
| 95 | 94 | ||||
| 96 | return result | 95 | return result | ||
| 97 | 96 | ||||
| 98 | def __add__(self, other): | 97 | def __add__(self, other): | ||
| 99 | 98 | ||||
| n | 100 | if type(other) is Tone: | n | 99 | if isinstance(other, Tone): | 
| 101 | return Chord(self.root_tone, self.tone_set, other) | 100 | return Chord(self.root_tone, self.tone_set, other) | ||
| 102 | 101 | ||||
| n | 103 | if type(other) is Chord: | n | 102 | if isinstance(other, Chord): | 
| 104 | return Chord(self.root_tone, self.tone_set, other.tone_set) | 103 | return Chord(self.root_tone, self.tone_set, other.tone_set) | ||
| 105 | 104 | ||||
| 106 | raise TypeError("Invalid operation") | 105 | raise TypeError("Invalid operation") | ||
| 107 | 106 | ||||
| 108 | def find_next_tone(self, reference_tone, tone_set): | 107 | def find_next_tone(self, reference_tone, tone_set): | ||
| 109 | 108 | ||||
| 110 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | 109 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | ||
| 111 | 110 | ||||
| 112 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | 111 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | ||
| 113 | potential_tone = Tone(ACCEPTED_TONES[i]) | 112 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 114 | if potential_tone in tone_set: | 113 | if potential_tone in tone_set: | ||
| 115 | return potential_tone | 114 | return potential_tone | ||
| 116 | 115 | ||||
| 117 | for i in range(0, reference_tone_index): | 116 | for i in range(0, reference_tone_index): | ||
| 118 | potential_tone = Tone(ACCEPTED_TONES[i]) | 117 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 119 | if potential_tone in tone_set: | 118 | if potential_tone in tone_set: | ||
| 120 | return potential_tone | 119 | return potential_tone | ||
| 121 | 120 | ||||
| 122 | def __sub__(self, other): | 121 | def __sub__(self, other): | ||
| n | 123 | if type(other) is Tone: | n | 122 | if isinstance(other, Tone): | 
| 124 | if other not in self.tone_set: | 123 | if other not in self.tone_set: | ||
| t | 125 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | t | 124 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 
| 126 | 125 | ||||
| 127 | new_tone_set = self.tone_set.copy() | 126 | new_tone_set = self.tone_set.copy() | ||
| 128 | new_tone_set.remove(other) | 127 | new_tone_set.remove(other) | ||
| 129 | 128 | ||||
| 130 | if other == self.root_tone: | 129 | if other == self.root_tone: | ||
| 131 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | 130 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | ||
| 132 | return Chord(new_root_tone, new_tone_set) | 131 | return Chord(new_root_tone, new_tone_set) | ||
| 133 | 132 | ||||
| 134 | return Chord(self.root_tone, new_tone_set) | 133 | return Chord(self.root_tone, new_tone_set) | ||
| 135 | 134 | ||||
| 136 | raise TypeError("Invalid operation") | 135 | raise TypeError("Invalid operation") | ||
| 137 | 136 | ||||
| 138 | def is_minor(self): | 137 | def is_minor(self): | ||
| 139 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | 138 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | ||
| 140 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 139 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 141 | 140 | ||||
| 142 | for tone in self.tone_set: | 141 | for tone in self.tone_set: | ||
| 143 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 142 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 144 | return True | 143 | return True | ||
| 145 | 144 | ||||
| 146 | return False | 145 | return False | ||
| 147 | 146 | ||||
| 148 | def is_major(self): | 147 | def is_major(self): | ||
| 149 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | 148 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | ||
| 150 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 149 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 151 | 150 | ||||
| 152 | for tone in self.tone_set: | 151 | for tone in self.tone_set: | ||
| 153 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 152 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 154 | return True | 153 | return True | ||
| 155 | 154 | ||||
| 156 | return False | 155 | return False | ||
| 157 | 156 | ||||
| 158 | def is_power_chord(self): | 157 | def is_power_chord(self): | ||
| 159 | return not self.is_minor() and not self.is_major() | 158 | return not self.is_minor() and not self.is_major() | ||
| 160 | 159 | ||||
| 161 | def transposed(self, interval): | 160 | def transposed(self, interval): | ||
| 162 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | 161 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| n | 1 | n | |||
| 2 | import unittest | ||||
| 3 | 1 | ||||
| 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 2 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
| 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | 3 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | ||
| 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | 4 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
| 7 | 5 | ||||
| 8 | 6 | ||||
| 9 | class Tone: | 7 | class Tone: | ||
| 10 | 8 | ||||
| 11 | def __init__(self, tone): | 9 | def __init__(self, tone): | ||
| 12 | if tone not in ACCEPTED_TONES: | 10 | if tone not in ACCEPTED_TONES: | ||
| 13 | raise TypeError("Tone is not accepted") | 11 | raise TypeError("Tone is not accepted") | ||
| 14 | self.tone = tone | 12 | self.tone = tone | ||
| 15 | 13 | ||||
| 16 | def __str__(self): | 14 | def __str__(self): | ||
| 17 | return self.tone | 15 | return self.tone | ||
| 18 | 16 | ||||
| 19 | def __eq__(self, other): | 17 | def __eq__(self, other): | ||
| 20 | if type(other) is not Tone: | 18 | if type(other) is not Tone: | ||
| 21 | return False | 19 | return False | ||
| 22 | return self.tone == other.tone | 20 | return self.tone == other.tone | ||
| 23 | 21 | ||||
| 24 | def __hash__(self): | 22 | def __hash__(self): | ||
| 25 | return hash(self.tone) | 23 | return hash(self.tone) | ||
| 26 | 24 | ||||
| 27 | def __add__(self, other): | 25 | def __add__(self, other): | ||
| 28 | if type(other) is Tone: | 26 | if type(other) is Tone: | ||
| 29 | return Chord(self, other) | 27 | return Chord(self, other) | ||
| 30 | 28 | ||||
| 31 | if type(other) is Interval: | 29 | if type(other) is Interval: | ||
| n | 32 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)]) | n | 30 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)]) | 
| 33 | 31 | ||||
| 34 | raise TypeError("Invalid operation") | 32 | raise TypeError("Invalid operation") | ||
| 35 | 33 | ||||
| 36 | def __sub__(self, other): | 34 | def __sub__(self, other): | ||
| 37 | if type(other) is Tone: | 35 | if type(other) is Tone: | ||
| 38 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | 36 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | ||
| 39 | 37 | ||||
| 40 | if type(other) is Interval: | 38 | if type(other) is Interval: | ||
| 41 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | 39 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | ||
| 42 | 40 | ||||
| 43 | raise TypeError("Invalid operation") | 41 | raise TypeError("Invalid operation") | ||
| 44 | 42 | ||||
| 45 | 43 | ||||
| 46 | class Interval: | 44 | class Interval: | ||
| 47 | 45 | ||||
| 48 | def __init__(self, interval_length): | 46 | def __init__(self, interval_length): | ||
| 49 | if type(interval_length) is not int: | 47 | if type(interval_length) is not int: | ||
| 50 | raise TypeError("Interval length is not int") | 48 | raise TypeError("Interval length is not int") | ||
| 51 | 49 | ||||
| 52 | self.length = interval_length % len(INTERVAL_VALUES) | 50 | self.length = interval_length % len(INTERVAL_VALUES) | ||
| 53 | 51 | ||||
| 54 | def __str__(self): | 52 | def __str__(self): | ||
| n | 55 | return INTERVAL_VALUES[self.length] | n | 53 | return INTERVAL_VALUES[abs(self.length)] | 
| 54 | |||||
| 55 | def __add__(self, other): | ||||
| 56 | return INTERVAL_VALUES[(self.length + other.length) % len(INTERVAL_VALUES)] | ||||
| 57 | |||||
| 58 | def __neg__(self): | ||||
| 59 | reference = Interval(self.length) | ||||
| 60 | reference.length *= -1 | ||||
| 61 | return reference | ||||
| 56 | 62 | ||||
| 57 | 63 | ||||
| 58 | class Chord: | 64 | class Chord: | ||
| 59 | 65 | ||||
| 60 | def __init__(self, root_tone, *other_tone_args): | 66 | def __init__(self, root_tone, *other_tone_args): | ||
| 61 | 67 | ||||
| 62 | self.root_tone = root_tone | 68 | self.root_tone = root_tone | ||
| 63 | self.tone_set = set() | 69 | self.tone_set = set() | ||
| 64 | self.tone_set.add(root_tone) | 70 | self.tone_set.add(root_tone) | ||
| 65 | 71 | ||||
| 66 | for arg in other_tone_args: | 72 | for arg in other_tone_args: | ||
| 67 | if type(arg) is set: | 73 | if type(arg) is set: | ||
| 68 | for tone in arg: | 74 | for tone in arg: | ||
| 69 | self.tone_set.add(tone) | 75 | self.tone_set.add(tone) | ||
| 70 | else: | 76 | else: | ||
| 71 | self.tone_set.add(arg) | 77 | self.tone_set.add(arg) | ||
| 72 | 78 | ||||
| 73 | if len(self.tone_set) < 2: | 79 | if len(self.tone_set) < 2: | ||
| 74 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 80 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 75 | 81 | ||||
| 76 | def __str__(self): | 82 | def __str__(self): | ||
| 77 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | 83 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | ||
| 78 | list_of_tone_indexes.sort() | 84 | list_of_tone_indexes.sort() | ||
| 79 | 85 | ||||
| 80 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 86 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 81 | 87 | ||||
| 82 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | 88 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | ||
| 83 | 89 | ||||
| 84 | for tone in tone_indexes_before_root: | 90 | for tone in tone_indexes_before_root: | ||
| 85 | list_of_tone_indexes.remove(tone) | 91 | list_of_tone_indexes.remove(tone) | ||
| 86 | 92 | ||||
| 87 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | 93 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | ||
| 88 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | 94 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | ||
| 89 | 95 | ||||
| 90 | return result | 96 | return result | ||
| 91 | 97 | ||||
| 92 | def __add__(self, other): | 98 | def __add__(self, other): | ||
| 93 | 99 | ||||
| 94 | if type(other) is Tone: | 100 | if type(other) is Tone: | ||
| n | 95 | return Chord(self.root_tone, self.tone_set, other.tone) | n | 101 | return Chord(self.root_tone, self.tone_set, other) | 
| 96 | 102 | ||||
| 97 | if type(other) is Chord: | 103 | if type(other) is Chord: | ||
| n | 98 | return Chord(self.root_tone, self.tone_set.union(other.tone)) | n | 104 | return Chord(self.root_tone, self.tone_set, other.tone_set) | 
| 99 | 105 | ||||
| 100 | raise TypeError("Invalid operation") | 106 | raise TypeError("Invalid operation") | ||
| 101 | 107 | ||||
| 102 | def find_next_tone(self, reference_tone, tone_set): | 108 | def find_next_tone(self, reference_tone, tone_set): | ||
| 103 | 109 | ||||
| 104 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | 110 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | ||
| 105 | 111 | ||||
| 106 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | 112 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | ||
| 107 | potential_tone = Tone(ACCEPTED_TONES[i]) | 113 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 108 | if potential_tone in tone_set: | 114 | if potential_tone in tone_set: | ||
| 109 | return potential_tone | 115 | return potential_tone | ||
| 110 | 116 | ||||
| 111 | for i in range(0, reference_tone_index): | 117 | for i in range(0, reference_tone_index): | ||
| 112 | potential_tone = Tone(ACCEPTED_TONES[i]) | 118 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||
| 113 | if potential_tone in tone_set: | 119 | if potential_tone in tone_set: | ||
| 114 | return potential_tone | 120 | return potential_tone | ||
| 115 | 121 | ||||
| 116 | def __sub__(self, other): | 122 | def __sub__(self, other): | ||
| 117 | if type(other) is Tone: | 123 | if type(other) is Tone: | ||
| 118 | if other not in self.tone_set: | 124 | if other not in self.tone_set: | ||
| 119 | 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)}") | ||
| 120 | 126 | ||||
| 121 | new_tone_set = self.tone_set.copy() | 127 | new_tone_set = self.tone_set.copy() | ||
| 122 | new_tone_set.remove(other) | 128 | new_tone_set.remove(other) | ||
| 123 | 129 | ||||
| 124 | if other == self.root_tone: | 130 | if other == self.root_tone: | ||
| 125 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | 131 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | ||
| 126 | return Chord(new_root_tone, new_tone_set) | 132 | return Chord(new_root_tone, new_tone_set) | ||
| 127 | 133 | ||||
| 128 | return Chord(self.root_tone, new_tone_set) | 134 | return Chord(self.root_tone, new_tone_set) | ||
| 129 | 135 | ||||
| 130 | raise TypeError("Invalid operation") | 136 | raise TypeError("Invalid operation") | ||
| 131 | 137 | ||||
| 132 | def is_minor(self): | 138 | def is_minor(self): | ||
| 133 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | 139 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | ||
| 134 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 140 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 135 | 141 | ||||
| 136 | for tone in self.tone_set: | 142 | for tone in self.tone_set: | ||
| 137 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 143 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 138 | return True | 144 | return True | ||
| 139 | 145 | ||||
| 140 | return False | 146 | return False | ||
| 141 | 147 | ||||
| 142 | def is_major(self): | 148 | def is_major(self): | ||
| 143 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | 149 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | ||
| 144 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 150 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 145 | 151 | ||||
| 146 | for tone in self.tone_set: | 152 | for tone in self.tone_set: | ||
| 147 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 153 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 148 | return True | 154 | return True | ||
| 149 | 155 | ||||
| 150 | return False | 156 | return False | ||
| 151 | 157 | ||||
| 152 | def is_power_chord(self): | 158 | def is_power_chord(self): | ||
| 153 | return not self.is_minor() and not self.is_major() | 159 | return not self.is_minor() and not self.is_major() | ||
| 154 | 160 | ||||
| 155 | def transposed(self, interval): | 161 | def transposed(self, interval): | ||
| 156 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | 162 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | ||
| t | 157 | t | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | f | 1 | ||
| 2 | import unittest | 2 | import unittest | ||
| 3 | 3 | ||||
| 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
| 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | ||
| 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class Tone: | 9 | class Tone: | ||
| 10 | 10 | ||||
| 11 | def __init__(self, tone): | 11 | def __init__(self, tone): | ||
| 12 | if tone not in ACCEPTED_TONES: | 12 | if tone not in ACCEPTED_TONES: | ||
| 13 | raise TypeError("Tone is not accepted") | 13 | raise TypeError("Tone is not accepted") | ||
| 14 | self.tone = tone | 14 | self.tone = tone | ||
| 15 | 15 | ||||
| 16 | def __str__(self): | 16 | def __str__(self): | ||
| 17 | return self.tone | 17 | return self.tone | ||
| 18 | 18 | ||||
| 19 | def __eq__(self, other): | 19 | def __eq__(self, other): | ||
| 20 | if type(other) is not Tone: | 20 | if type(other) is not Tone: | ||
| 21 | return False | 21 | return False | ||
| 22 | return self.tone == other.tone | 22 | return self.tone == other.tone | ||
| 23 | 23 | ||||
| 24 | def __hash__(self): | 24 | def __hash__(self): | ||
| 25 | return hash(self.tone) | 25 | return hash(self.tone) | ||
| 26 | 26 | ||||
| 27 | def __add__(self, other): | 27 | def __add__(self, other): | ||
| 28 | if type(other) is Tone: | 28 | if type(other) is Tone: | ||
| 29 | return Chord(self, other) | 29 | return Chord(self, other) | ||
| 30 | 30 | ||||
| 31 | if type(other) is Interval: | 31 | if type(other) is Interval: | ||
| 32 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)]) | 32 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)]) | ||
| 33 | 33 | ||||
| 34 | raise TypeError("Invalid operation") | 34 | raise TypeError("Invalid operation") | ||
| 35 | 35 | ||||
| 36 | def __sub__(self, other): | 36 | def __sub__(self, other): | ||
| 37 | if type(other) is Tone: | 37 | if type(other) is Tone: | ||
| 38 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | 38 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | ||
| 39 | 39 | ||||
| 40 | if type(other) is Interval: | 40 | if type(other) is Interval: | ||
| 41 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | 41 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | ||
| 42 | 42 | ||||
| 43 | raise TypeError("Invalid operation") | 43 | raise TypeError("Invalid operation") | ||
| 44 | 44 | ||||
| 45 | 45 | ||||
| 46 | class Interval: | 46 | class Interval: | ||
| 47 | 47 | ||||
| 48 | def __init__(self, interval_length): | 48 | def __init__(self, interval_length): | ||
| 49 | if type(interval_length) is not int: | 49 | if type(interval_length) is not int: | ||
| 50 | raise TypeError("Interval length is not int") | 50 | raise TypeError("Interval length is not int") | ||
| 51 | 51 | ||||
| 52 | self.length = interval_length % len(INTERVAL_VALUES) | 52 | self.length = interval_length % len(INTERVAL_VALUES) | ||
| 53 | 53 | ||||
| 54 | def __str__(self): | 54 | def __str__(self): | ||
| 55 | return INTERVAL_VALUES[self.length] | 55 | return INTERVAL_VALUES[self.length] | ||
| 56 | 56 | ||||
| 57 | 57 | ||||
| 58 | class Chord: | 58 | class Chord: | ||
| 59 | 59 | ||||
| 60 | def __init__(self, root_tone, *other_tone_args): | 60 | def __init__(self, root_tone, *other_tone_args): | ||
| 61 | 61 | ||||
| 62 | self.root_tone = root_tone | 62 | self.root_tone = root_tone | ||
| 63 | self.tone_set = set() | 63 | self.tone_set = set() | ||
| 64 | self.tone_set.add(root_tone) | 64 | self.tone_set.add(root_tone) | ||
| 65 | 65 | ||||
| 66 | for arg in other_tone_args: | 66 | for arg in other_tone_args: | ||
| 67 | if type(arg) is set: | 67 | if type(arg) is set: | ||
| 68 | for tone in arg: | 68 | for tone in arg: | ||
| 69 | self.tone_set.add(tone) | 69 | self.tone_set.add(tone) | ||
| 70 | else: | 70 | else: | ||
| 71 | self.tone_set.add(arg) | 71 | self.tone_set.add(arg) | ||
| 72 | 72 | ||||
| 73 | if len(self.tone_set) < 2: | 73 | if len(self.tone_set) < 2: | ||
| 74 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 74 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 75 | 75 | ||||
| 76 | def __str__(self): | 76 | def __str__(self): | ||
| 77 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | 77 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | ||
| 78 | list_of_tone_indexes.sort() | 78 | list_of_tone_indexes.sort() | ||
| 79 | 79 | ||||
| 80 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 80 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 81 | 81 | ||||
| 82 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | 82 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | ||
| 83 | 83 | ||||
| 84 | for tone in tone_indexes_before_root: | 84 | for tone in tone_indexes_before_root: | ||
| 85 | list_of_tone_indexes.remove(tone) | 85 | list_of_tone_indexes.remove(tone) | ||
| 86 | 86 | ||||
| 87 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | 87 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | ||
| 88 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | 88 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | ||
| 89 | 89 | ||||
| 90 | return result | 90 | return result | ||
| 91 | 91 | ||||
| 92 | def __add__(self, other): | 92 | def __add__(self, other): | ||
| 93 | 93 | ||||
| 94 | if type(other) is Tone: | 94 | if type(other) is Tone: | ||
| 95 | return Chord(self.root_tone, self.tone_set, other.tone) | 95 | return Chord(self.root_tone, self.tone_set, other.tone) | ||
| 96 | 96 | ||||
| 97 | if type(other) is Chord: | 97 | if type(other) is Chord: | ||
| n | 98 | return Chord(self.root_tone, self.tone_set.union(other.tone_set)) | n | 98 | return Chord(self.root_tone, self.tone_set.union(other.tone)) | 
| 99 | 99 | ||||
| 100 | raise TypeError("Invalid operation") | 100 | raise TypeError("Invalid operation") | ||
| 101 | 101 | ||||
| n | n | 102 | def find_next_tone(self, reference_tone, tone_set): | ||
| 103 | |||||
| 104 | reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone) | ||||
| 105 | |||||
| 106 | for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)): | ||||
| 107 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||||
| 108 | if potential_tone in tone_set: | ||||
| 109 | return potential_tone | ||||
| 110 | |||||
| 111 | for i in range(0, reference_tone_index): | ||||
| 112 | potential_tone = Tone(ACCEPTED_TONES[i]) | ||||
| 113 | if potential_tone in tone_set: | ||||
| 114 | return potential_tone | ||||
| 115 | |||||
| 102 | def __sub__(self, other): | 116 | def __sub__(self, other): | ||
| n | n | 117 | if type(other) is Tone: | ||
| 118 | if other not in self.tone_set: | ||||
| 119 | raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}") | ||||
| 103 | 120 | ||||
| n | 104 | if type(other) is Tone: | n | 121 | new_tone_set = self.tone_set.copy() | 
| 105 | pass | 122 | new_tone_set.remove(other) | ||
| 123 | |||||
| 124 | if other == self.root_tone: | ||||
| 125 | new_root_tone = self.find_next_tone(self.root_tone, self.tone_set) | ||||
| 126 | return Chord(new_root_tone, new_tone_set) | ||||
| 127 | |||||
| 128 | return Chord(self.root_tone, new_tone_set) | ||||
| 106 | 129 | ||||
| 107 | raise TypeError("Invalid operation") | 130 | raise TypeError("Invalid operation") | ||
| 108 | 131 | ||||
| 109 | def is_minor(self): | 132 | def is_minor(self): | ||
| 110 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | 133 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | ||
| 111 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 134 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 112 | 135 | ||||
| 113 | for tone in self.tone_set: | 136 | for tone in self.tone_set: | ||
| 114 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 137 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 115 | return True | 138 | return True | ||
| 116 | 139 | ||||
| 117 | return False | 140 | return False | ||
| 118 | 141 | ||||
| 119 | def is_major(self): | 142 | def is_major(self): | ||
| 120 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | 143 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | ||
| 121 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 144 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 122 | 145 | ||||
| 123 | for tone in self.tone_set: | 146 | for tone in self.tone_set: | ||
| 124 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 147 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 125 | return True | 148 | return True | ||
| 126 | 149 | ||||
| 127 | return False | 150 | return False | ||
| 128 | 151 | ||||
| 129 | def is_power_chord(self): | 152 | def is_power_chord(self): | ||
| 130 | return not self.is_minor() and not self.is_major() | 153 | return not self.is_minor() and not self.is_major() | ||
| t | t | 154 | |||
| 155 | def transposed(self, interval): | ||||
| 156 | return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set)))) | ||||
| 157 | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | f | 1 | ||
| 2 | import unittest | 2 | import unittest | ||
| 3 | 3 | ||||
| 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | 4 | ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B") | ||
| 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | 5 | INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th", | ||
| 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | 6 | "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th") | ||
| 7 | 7 | ||||
| 8 | 8 | ||||
| 9 | class Tone: | 9 | class Tone: | ||
| 10 | 10 | ||||
| 11 | def __init__(self, tone): | 11 | def __init__(self, tone): | ||
| 12 | if tone not in ACCEPTED_TONES: | 12 | if tone not in ACCEPTED_TONES: | ||
| 13 | raise TypeError("Tone is not accepted") | 13 | raise TypeError("Tone is not accepted") | ||
| 14 | self.tone = tone | 14 | self.tone = tone | ||
| 15 | 15 | ||||
| 16 | def __str__(self): | 16 | def __str__(self): | ||
| 17 | return self.tone | 17 | return self.tone | ||
| 18 | 18 | ||||
| 19 | def __eq__(self, other): | 19 | def __eq__(self, other): | ||
| 20 | if type(other) is not Tone: | 20 | if type(other) is not Tone: | ||
| 21 | return False | 21 | return False | ||
| 22 | return self.tone == other.tone | 22 | return self.tone == other.tone | ||
| 23 | 23 | ||||
| 24 | def __hash__(self): | 24 | def __hash__(self): | ||
| 25 | return hash(self.tone) | 25 | return hash(self.tone) | ||
| 26 | 26 | ||||
| 27 | def __add__(self, other): | 27 | def __add__(self, other): | ||
| 28 | if type(other) is Tone: | 28 | if type(other) is Tone: | ||
| 29 | return Chord(self, other) | 29 | return Chord(self, other) | ||
| 30 | 30 | ||||
| 31 | if type(other) is Interval: | 31 | if type(other) is Interval: | ||
| 32 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)]) | 32 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)]) | ||
| 33 | 33 | ||||
| 34 | raise TypeError("Invalid operation") | 34 | raise TypeError("Invalid operation") | ||
| 35 | 35 | ||||
| 36 | def __sub__(self, other): | 36 | def __sub__(self, other): | ||
| 37 | if type(other) is Tone: | 37 | if type(other) is Tone: | ||
| 38 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | 38 | return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone))) | ||
| 39 | 39 | ||||
| 40 | if type(other) is Interval: | 40 | if type(other) is Interval: | ||
| 41 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | 41 | return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)]) | ||
| 42 | 42 | ||||
| 43 | raise TypeError("Invalid operation") | 43 | raise TypeError("Invalid operation") | ||
| 44 | 44 | ||||
| 45 | 45 | ||||
| 46 | class Interval: | 46 | class Interval: | ||
| 47 | 47 | ||||
| 48 | def __init__(self, interval_length): | 48 | def __init__(self, interval_length): | ||
| 49 | if type(interval_length) is not int: | 49 | if type(interval_length) is not int: | ||
| 50 | raise TypeError("Interval length is not int") | 50 | raise TypeError("Interval length is not int") | ||
| 51 | 51 | ||||
| 52 | self.length = interval_length % len(INTERVAL_VALUES) | 52 | self.length = interval_length % len(INTERVAL_VALUES) | ||
| 53 | 53 | ||||
| 54 | def __str__(self): | 54 | def __str__(self): | ||
| 55 | return INTERVAL_VALUES[self.length] | 55 | return INTERVAL_VALUES[self.length] | ||
| 56 | 56 | ||||
| 57 | 57 | ||||
| 58 | class Chord: | 58 | class Chord: | ||
| 59 | 59 | ||||
| 60 | def __init__(self, root_tone, *other_tone_args): | 60 | def __init__(self, root_tone, *other_tone_args): | ||
| 61 | 61 | ||||
| 62 | self.root_tone = root_tone | 62 | self.root_tone = root_tone | ||
| 63 | self.tone_set = set() | 63 | self.tone_set = set() | ||
| 64 | self.tone_set.add(root_tone) | 64 | self.tone_set.add(root_tone) | ||
| 65 | 65 | ||||
| 66 | for arg in other_tone_args: | 66 | for arg in other_tone_args: | ||
| 67 | if type(arg) is set: | 67 | if type(arg) is set: | ||
| 68 | for tone in arg: | 68 | for tone in arg: | ||
| 69 | self.tone_set.add(tone) | 69 | self.tone_set.add(tone) | ||
| 70 | else: | 70 | else: | ||
| 71 | self.tone_set.add(arg) | 71 | self.tone_set.add(arg) | ||
| 72 | 72 | ||||
| 73 | if len(self.tone_set) < 2: | 73 | if len(self.tone_set) < 2: | ||
| 74 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 74 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 75 | 75 | ||||
| 76 | def __str__(self): | 76 | def __str__(self): | ||
| 77 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | 77 | list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set))) | ||
| 78 | list_of_tone_indexes.sort() | 78 | list_of_tone_indexes.sort() | ||
| 79 | 79 | ||||
| 80 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 80 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 81 | 81 | ||||
| 82 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | 82 | tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes)) | ||
| 83 | 83 | ||||
| 84 | for tone in tone_indexes_before_root: | 84 | for tone in tone_indexes_before_root: | ||
| 85 | list_of_tone_indexes.remove(tone) | 85 | list_of_tone_indexes.remove(tone) | ||
| 86 | 86 | ||||
| 87 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | 87 | result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) + | ||
| 88 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | 88 | list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root))) | ||
| 89 | 89 | ||||
| 90 | return result | 90 | return result | ||
| 91 | 91 | ||||
| 92 | def __add__(self, other): | 92 | def __add__(self, other): | ||
| 93 | 93 | ||||
| 94 | if type(other) is Tone: | 94 | if type(other) is Tone: | ||
| 95 | return Chord(self.root_tone, self.tone_set, other.tone) | 95 | return Chord(self.root_tone, self.tone_set, other.tone) | ||
| 96 | 96 | ||||
| 97 | if type(other) is Chord: | 97 | if type(other) is Chord: | ||
| 98 | return Chord(self.root_tone, self.tone_set.union(other.tone_set)) | 98 | return Chord(self.root_tone, self.tone_set.union(other.tone_set)) | ||
| 99 | 99 | ||||
| 100 | raise TypeError("Invalid operation") | 100 | raise TypeError("Invalid operation") | ||
| 101 | 101 | ||||
| 102 | def __sub__(self, other): | 102 | def __sub__(self, other): | ||
| 103 | 103 | ||||
| 104 | if type(other) is Tone: | 104 | if type(other) is Tone: | ||
| 105 | pass | 105 | pass | ||
| 106 | 106 | ||||
| 107 | raise TypeError("Invalid operation") | 107 | raise TypeError("Invalid operation") | ||
| 108 | 108 | ||||
| 109 | def is_minor(self): | 109 | def is_minor(self): | ||
| 110 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | 110 | minor_third_index = INTERVAL_VALUES.index("minor 3rd") | ||
| 111 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 111 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 112 | 112 | ||||
| 113 | for tone in self.tone_set: | 113 | for tone in self.tone_set: | ||
| 114 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 114 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 115 | return True | 115 | return True | ||
| 116 | 116 | ||||
| 117 | return False | 117 | return False | ||
| 118 | 118 | ||||
| 119 | def is_major(self): | 119 | def is_major(self): | ||
| 120 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | 120 | minor_third_index = INTERVAL_VALUES.index("major 3rd") | ||
| 121 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | 121 | root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone) | ||
| 122 | 122 | ||||
| 123 | for tone in self.tone_set: | 123 | for tone in self.tone_set: | ||
| 124 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | 124 | if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index: | ||
| 125 | return True | 125 | return True | ||
| 126 | 126 | ||||
| 127 | return False | 127 | return False | ||
| 128 | 128 | ||||
| 129 | def is_power_chord(self): | 129 | def is_power_chord(self): | ||
| 130 | return not self.is_minor() and not self.is_major() | 130 | return not self.is_minor() and not self.is_major() | ||
| t | 131 | t | |||
| 132 | |||||
| 133 | c5_chord = Chord(Tone("C"), Tone("G")) | ||||
| 134 | this_other_chord = Chord(Tone("A"), Tone("B")) | ||||
| 135 | result_chord = c5_chord + this_other_chord | ||||
| 136 | print(result_chord) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||