1class Tone:
  2    TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]
  3
  4    def __init__(self, input_name):
  5        if input_name not in self.TONES:
  6            raise ValueError("Invalid tone name")
  7        self.name_of_tone = input_name
  8
  9    def __str__(self):
 10        return self.name_of_tone
 11
 12    def __hash__(self):  # this time i didn't use it
 13        return hash(self.name_of_tone)
 14
 15    def __eq__(self, other):
 16        return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone
 17
 18    def __add__(self, other):
 19        if isinstance(other, Tone):
 20            return self._calculate_tone(other, "+")
 21        elif isinstance(other, Interval):
 22            return self._calculate_interval(other, "+")
 23        raise TypeError("Invalid operation")
 24
 25    def __sub__(self, other):
 26        if isinstance(other, Tone):
 27            return self._calculate_tone(other, "-")
 28        elif isinstance(other, Interval):
 29            return self._calculate_interval(other, "-")
 30        raise TypeError("Invalid operation")
 31
 32    def get_index(self):
 33        return Tone.TONES.index(self.name_of_tone)
 34
 35    def _calculate_tone(self, other, symbol):
 36        if symbol == "+":
 37            return Chord(self, other)
 38        elif symbol == "-":
 39            distance = (self.TONES.index(self.name_of_tone) -
 40                        self.TONES.index(other.name_of_tone)) % 12
 41            return Interval(distance)
 42        else:
 43            raise ValueError("Invalid symbol")
 44
 45    def _calculate_interval(self, other, symbol):
 46        if symbol == "+":
 47            index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12
 48            return Tone(self.TONES[index])
 49        elif symbol == "-":
 50            index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12
 51            return Tone(self.TONES[index])
 52        else:
 53            raise ValueError("Invalid symbol")
 54
 55
 56class Interval:
 57    INTERVAL_NAMES = [
 58        "unison", "minor 2nd", "major 2nd", "minor 3rd",
 59        "major 3rd", "perfect 4th", "diminished 5th",
 60        "perfect 5th", "minor 6th", "major 6th",
 61        "minor 7th", "major 7th"
 62    ]
 63
 64    def __init__(self, number):
 65        if isinstance(number, int):
 66            self.semitones = number % 12
 67        else:
 68            raise TypeError("Invalid input")
 69
 70    def __str__(self):
 71        return self.INTERVAL_NAMES[self.semitones]
 72
 73    def __add__(self, other):
 74        if isinstance(other, Interval):
 75            return Interval(self.semitones + other.semitones)
 76        raise TypeError("Invalid operation")
 77
 78    def __neg__(self):
 79        return Interval(-self.semitones)
 80
 81
 82class Chord:
 83    def __init__(self, root, *tones):
 84        if not isinstance(root, Tone):
 85            raise ValueError("Root must be a Tone instance")
 86
 87        for tone in tones:
 88            if not isinstance(tone, Tone):
 89                raise ValueError("All tones must be Tone instances")
 90
 91        unique_tones = [root]
 92        for tone in tones:
 93            if tone not in unique_tones: #using __eq__
 94                unique_tones.append(tone)
 95
 96        if len(unique_tones) < 2:
 97            raise TypeError("Cannot have a chord made of only 1 unique tone")
 98
 99        self.root = root
100        self.tones = unique_tones
101
102    def __str__(self):
103        root_index = Tone.TONES.index(self.root.name_of_tone)
104        other_tones = [tone for tone in self.tones if tone != self.root]
105        sorted_tones = sorted(
106            other_tones,
107            key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12
108        )
109        sorted_tones.insert(0, self.root)
110        return "-".join(str(tone) for tone in sorted_tones)
111
112    def __add__(self, other):
113        if isinstance(other, Tone):
114            return self._add_tone(other)
115        elif isinstance(other, Chord):
116            return self._add_chord(other)
117        raise TypeError("Invalid operation")
118
119    def __sub__(self, other):
120        if isinstance(other, Tone):
121            if other not in self.tones:  # using __eq__
122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
123            new_tones = [tone for tone in self.tones if tone != other]
124            if len(set(new_tones)) < 2:
125                raise TypeError("Cannot have a chord made of only 1 unique tone")
126            return Chord(*new_tones)
127        raise TypeError("Invalid operation")
128
129    def _validate_distance_to_root(self, target):
130        root_index = self.root.get_index()
131        for tone in self.tones:
132            distance = (tone.get_index() - root_index) % 12
133            if distance == target:
134                return True
135        return False
136
137    def is_minor(self):
138        return self._validate_distance_to_root(3)
139
140    def is_major(self):
141        return self._validate_distance_to_root(4)
142
143    def is_power_chord(self):
144        return not self.is_minor() and not self.is_major()
145
146    def _add_tone(self, other):
147        if other not in self.tones:  # using __eq__
148            new_tones = list(self.tones)
149            new_tones.append(other)
150            return Chord(*new_tones)
151        else:
152            return self
153
154    def _add_chord(self, other):
155        new_tones = list(self.tones)
156        for tone in other.tones:
157            if tone not in self.tones:  # using __eq__
158                new_tones.append(tone)
159        return Chord(*new_tones)
160
161    def transposed(self, input_interval):
162        if not isinstance(input_interval, Interval):
163            raise ValueError("Interval must be an instance of Interval")
164
165        transposed_tones = [tone + input_interval for tone in self.tones]
166        return Chord(*transposed_tones)
...........................F.........
======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
    self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation
----------------------------------------------------------------------
Ran 37 tests in 0.001s
FAILED (failures=1)
                            
    
 
    
        Виктор Бечев
         
    
02.11.2024 22:03Отвъд забележките по-горе - good job. 
                            
                            
                         | 
| f | 1 | class Tone: | f | 1 | class Tone: | 
| 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
| 3 | 3 | ||||
| 4 | def __init__(self, input_name): | 4 | def __init__(self, input_name): | ||
| 5 | if input_name not in self.TONES: | 5 | if input_name not in self.TONES: | ||
| 6 | raise ValueError("Invalid tone name") | 6 | raise ValueError("Invalid tone name") | ||
| 7 | self.name_of_tone = input_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 18 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| 20 | return self._calculate_tone(other, "+") | 20 | return self._calculate_tone(other, "+") | ||
| 21 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| 22 | return self._calculate_interval(other, "+") | 22 | return self._calculate_interval(other, "+") | ||
| 23 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 24 | 24 | ||||
| 25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 26 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| 27 | return self._calculate_tone(other, "-") | 27 | return self._calculate_tone(other, "-") | ||
| 28 | elif isinstance(other, Interval): | 28 | elif isinstance(other, Interval): | ||
| 29 | return self._calculate_interval(other, "-") | 29 | return self._calculate_interval(other, "-") | ||
| 30 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 31 | 31 | ||||
| 32 | def get_index(self): | 32 | def get_index(self): | ||
| 33 | return Tone.TONES.index(self.name_of_tone) | 33 | return Tone.TONES.index(self.name_of_tone) | ||
| 34 | 34 | ||||
| 35 | def _calculate_tone(self, other, symbol): | 35 | def _calculate_tone(self, other, symbol): | ||
| 36 | if symbol == "+": | 36 | if symbol == "+": | ||
| 37 | return Chord(self, other) | 37 | return Chord(self, other) | ||
| 38 | elif symbol == "-": | 38 | elif symbol == "-": | ||
| 39 | distance = (self.TONES.index(self.name_of_tone) - | 39 | distance = (self.TONES.index(self.name_of_tone) - | ||
| 40 | self.TONES.index(other.name_of_tone)) % 12 | 40 | self.TONES.index(other.name_of_tone)) % 12 | ||
| 41 | return Interval(distance) | 41 | return Interval(distance) | ||
| 42 | else: | 42 | else: | ||
| 43 | raise ValueError("Invalid symbol") | 43 | raise ValueError("Invalid symbol") | ||
| 44 | 44 | ||||
| 45 | def _calculate_interval(self, other, symbol): | 45 | def _calculate_interval(self, other, symbol): | ||
| 46 | if symbol == "+": | 46 | if symbol == "+": | ||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | ||
| 48 | return Tone(self.TONES[index]) | 48 | return Tone(self.TONES[index]) | ||
| 49 | elif symbol == "-": | 49 | elif symbol == "-": | ||
| 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 51 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| 52 | else: | 52 | else: | ||
| 53 | raise ValueError("Invalid symbol") | 53 | raise ValueError("Invalid symbol") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| 57 | INTERVAL_NAMES = [ | 57 | INTERVAL_NAMES = [ | ||
| t | 58 | "octave", "minor 2nd", "major 2nd", "minor 3rd", | t | 58 | "unison", "minor 2nd", "major 2nd", "minor 3rd", | 
| 59 | "major 3rd", "perfect 4th", "diminished 5th", | 59 | "major 3rd", "perfect 4th", "diminished 5th", | ||
| 60 | "perfect 5th", "minor 6th", "major 6th", | 60 | "perfect 5th", "minor 6th", "major 6th", | ||
| 61 | "minor 7th", "major 7th" | 61 | "minor 7th", "major 7th" | ||
| 62 | ] | 62 | ] | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 73 | def __add__(self, other): | 73 | def __add__(self, other): | ||
| 74 | if isinstance(other, Interval): | 74 | if isinstance(other, Interval): | ||
| 75 | return Interval(self.semitones + other.semitones) | 75 | return Interval(self.semitones + other.semitones) | ||
| 76 | raise TypeError("Invalid operation") | 76 | raise TypeError("Invalid operation") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class Chord: | ||
| 83 | def __init__(self, root, *tones): | 83 | def __init__(self, root, *tones): | ||
| 84 | if not isinstance(root, Tone): | 84 | if not isinstance(root, Tone): | ||
| 85 | raise ValueError("Root must be a Tone instance") | 85 | raise ValueError("Root must be a Tone instance") | ||
| 86 | 86 | ||||
| 87 | for tone in tones: | 87 | for tone in tones: | ||
| 88 | if not isinstance(tone, Tone): | 88 | if not isinstance(tone, Tone): | ||
| 89 | raise ValueError("All tones must be Tone instances") | 89 | raise ValueError("All tones must be Tone instances") | ||
| 90 | 90 | ||||
| 91 | unique_tones = [root] | 91 | unique_tones = [root] | ||
| 92 | for tone in tones: | 92 | for tone in tones: | ||
| 93 | if tone not in unique_tones: #using __eq__ | 93 | if tone not in unique_tones: #using __eq__ | ||
| 94 | unique_tones.append(tone) | 94 | unique_tones.append(tone) | ||
| 95 | 95 | ||||
| 96 | if len(unique_tones) < 2: | 96 | if len(unique_tones) < 2: | ||
| 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 102 | def __str__(self): | 102 | def __str__(self): | ||
| 103 | root_index = Tone.TONES.index(self.root.name_of_tone) | 103 | root_index = Tone.TONES.index(self.root.name_of_tone) | ||
| 104 | other_tones = [tone for tone in self.tones if tone != self.root] | 104 | other_tones = [tone for tone in self.tones if tone != self.root] | ||
| 105 | sorted_tones = sorted( | 105 | sorted_tones = sorted( | ||
| 106 | other_tones, | 106 | other_tones, | ||
| 107 | key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12 | 107 | key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12 | ||
| 108 | ) | 108 | ) | ||
| 109 | sorted_tones.insert(0, self.root) | 109 | sorted_tones.insert(0, self.root) | ||
| 110 | return "-".join(str(tone) for tone in sorted_tones) | 110 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 111 | 111 | ||||
| 112 | def __add__(self, other): | 112 | def __add__(self, other): | ||
| 113 | if isinstance(other, Tone): | 113 | if isinstance(other, Tone): | ||
| 114 | return self._add_tone(other) | 114 | return self._add_tone(other) | ||
| 115 | elif isinstance(other, Chord): | 115 | elif isinstance(other, Chord): | ||
| 116 | return self._add_chord(other) | 116 | return self._add_chord(other) | ||
| 117 | raise TypeError("Invalid operation") | 117 | raise TypeError("Invalid operation") | ||
| 118 | 118 | ||||
| 119 | def __sub__(self, other): | 119 | def __sub__(self, other): | ||
| 120 | if isinstance(other, Tone): | 120 | if isinstance(other, Tone): | ||
| 121 | if other not in self.tones: # using __eq__ | 121 | if other not in self.tones: # using __eq__ | ||
| 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 123 | new_tones = [tone for tone in self.tones if tone != other] | 123 | new_tones = [tone for tone in self.tones if tone != other] | ||
| 124 | if len(set(new_tones)) < 2: | 124 | if len(set(new_tones)) < 2: | ||
| 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 126 | return Chord(*new_tones) | 126 | return Chord(*new_tones) | ||
| 127 | raise TypeError("Invalid operation") | 127 | raise TypeError("Invalid operation") | ||
| 128 | 128 | ||||
| 129 | def _validate_distance_to_root(self, target): | 129 | def _validate_distance_to_root(self, target): | ||
| 130 | root_index = self.root.get_index() | 130 | root_index = self.root.get_index() | ||
| 131 | for tone in self.tones: | 131 | for tone in self.tones: | ||
| 132 | distance = (tone.get_index() - root_index) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 133 | if distance == target: | 133 | if distance == target: | ||
| 134 | return True | 134 | return True | ||
| 135 | return False | 135 | return False | ||
| 136 | 136 | ||||
| 137 | def is_minor(self): | 137 | def is_minor(self): | ||
| 138 | return self._validate_distance_to_root(3) | 138 | return self._validate_distance_to_root(3) | ||
| 139 | 139 | ||||
| 140 | def is_major(self): | 140 | def is_major(self): | ||
| 141 | return self._validate_distance_to_root(4) | 141 | return self._validate_distance_to_root(4) | ||
| 142 | 142 | ||||
| 143 | def is_power_chord(self): | 143 | def is_power_chord(self): | ||
| 144 | return not self.is_minor() and not self.is_major() | 144 | return not self.is_minor() and not self.is_major() | ||
| 145 | 145 | ||||
| 146 | def _add_tone(self, other): | 146 | def _add_tone(self, other): | ||
| 147 | if other not in self.tones: # using __eq__ | 147 | if other not in self.tones: # using __eq__ | ||
| 148 | new_tones = list(self.tones) | 148 | new_tones = list(self.tones) | ||
| 149 | new_tones.append(other) | 149 | new_tones.append(other) | ||
| 150 | return Chord(*new_tones) | 150 | return Chord(*new_tones) | ||
| 151 | else: | 151 | else: | ||
| 152 | return self | 152 | return self | ||
| 153 | 153 | ||||
| 154 | def _add_chord(self, other): | 154 | def _add_chord(self, other): | ||
| 155 | new_tones = list(self.tones) | 155 | new_tones = list(self.tones) | ||
| 156 | for tone in other.tones: | 156 | for tone in other.tones: | ||
| 157 | if tone not in self.tones: # using __eq__ | 157 | if tone not in self.tones: # using __eq__ | ||
| 158 | new_tones.append(tone) | 158 | new_tones.append(tone) | ||
| 159 | return Chord(*new_tones) | 159 | return Chord(*new_tones) | ||
| 160 | 160 | ||||
| 161 | def transposed(self, input_interval): | 161 | def transposed(self, input_interval): | ||
| 162 | if not isinstance(input_interval, Interval): | 162 | if not isinstance(input_interval, Interval): | ||
| 163 | raise ValueError("Interval must be an instance of Interval") | 163 | raise ValueError("Interval must be an instance of Interval") | ||
| 164 | 164 | ||||
| 165 | transposed_tones = [tone + input_interval for tone in self.tones] | 165 | transposed_tones = [tone + input_interval for tone in self.tones] | ||
| 166 | return Chord(*transposed_tones) | 166 | return Chord(*transposed_tones) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | class Tone: | f | 1 | class Tone: | 
| 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
| 3 | 3 | ||||
| 4 | def __init__(self, input_name): | 4 | def __init__(self, input_name): | ||
| 5 | if input_name not in self.TONES: | 5 | if input_name not in self.TONES: | ||
| 6 | raise ValueError("Invalid tone name") | 6 | raise ValueError("Invalid tone name") | ||
| 7 | self.name_of_tone = input_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 18 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| 20 | return self._calculate_tone(other, "+") | 20 | return self._calculate_tone(other, "+") | ||
| 21 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| 22 | return self._calculate_interval(other, "+") | 22 | return self._calculate_interval(other, "+") | ||
| 23 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 24 | 24 | ||||
| 25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 26 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| 27 | return self._calculate_tone(other, "-") | 27 | return self._calculate_tone(other, "-") | ||
| 28 | elif isinstance(other, Interval): | 28 | elif isinstance(other, Interval): | ||
| 29 | return self._calculate_interval(other, "-") | 29 | return self._calculate_interval(other, "-") | ||
| 30 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 31 | 31 | ||||
| 32 | def get_index(self): | 32 | def get_index(self): | ||
| 33 | return Tone.TONES.index(self.name_of_tone) | 33 | return Tone.TONES.index(self.name_of_tone) | ||
| 34 | 34 | ||||
| 35 | def _calculate_tone(self, other, symbol): | 35 | def _calculate_tone(self, other, symbol): | ||
| 36 | if symbol == "+": | 36 | if symbol == "+": | ||
| 37 | return Chord(self, other) | 37 | return Chord(self, other) | ||
| 38 | elif symbol == "-": | 38 | elif symbol == "-": | ||
| 39 | distance = (self.TONES.index(self.name_of_tone) - | 39 | distance = (self.TONES.index(self.name_of_tone) - | ||
| 40 | self.TONES.index(other.name_of_tone)) % 12 | 40 | self.TONES.index(other.name_of_tone)) % 12 | ||
| 41 | return Interval(distance) | 41 | return Interval(distance) | ||
| 42 | else: | 42 | else: | ||
| 43 | raise ValueError("Invalid symbol") | 43 | raise ValueError("Invalid symbol") | ||
| 44 | 44 | ||||
| 45 | def _calculate_interval(self, other, symbol): | 45 | def _calculate_interval(self, other, symbol): | ||
| 46 | if symbol == "+": | 46 | if symbol == "+": | ||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | ||
| 48 | return Tone(self.TONES[index]) | 48 | return Tone(self.TONES[index]) | ||
| 49 | elif symbol == "-": | 49 | elif symbol == "-": | ||
| 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 51 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| 52 | else: | 52 | else: | ||
| 53 | raise ValueError("Invalid symbol") | 53 | raise ValueError("Invalid symbol") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| t | 57 | INTERVAL_NAMES = { | t | 57 | INTERVAL_NAMES = [ | 
| 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 58 | "octave", "minor 2nd", "major 2nd", "minor 3rd", | ||
| 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 59 | "major 3rd", "perfect 4th", "diminished 5th", | ||
| 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 60 | "perfect 5th", "minor 6th", "major 6th", | ||
| 61 | 10: "minor 7th", 11: "major 7th" | 61 | "minor 7th", "major 7th" | ||
| 62 | } | 62 | ] | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 73 | def __add__(self, other): | 73 | def __add__(self, other): | ||
| 74 | if isinstance(other, Interval): | 74 | if isinstance(other, Interval): | ||
| 75 | return Interval(self.semitones + other.semitones) | 75 | return Interval(self.semitones + other.semitones) | ||
| 76 | raise TypeError("Invalid operation") | 76 | raise TypeError("Invalid operation") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class Chord: | ||
| 83 | def __init__(self, root, *tones): | 83 | def __init__(self, root, *tones): | ||
| 84 | if not isinstance(root, Tone): | 84 | if not isinstance(root, Tone): | ||
| 85 | raise ValueError("Root must be a Tone instance") | 85 | raise ValueError("Root must be a Tone instance") | ||
| 86 | 86 | ||||
| 87 | for tone in tones: | 87 | for tone in tones: | ||
| 88 | if not isinstance(tone, Tone): | 88 | if not isinstance(tone, Tone): | ||
| 89 | raise ValueError("All tones must be Tone instances") | 89 | raise ValueError("All tones must be Tone instances") | ||
| 90 | 90 | ||||
| 91 | unique_tones = [root] | 91 | unique_tones = [root] | ||
| 92 | for tone in tones: | 92 | for tone in tones: | ||
| 93 | if tone not in unique_tones: #using __eq__ | 93 | if tone not in unique_tones: #using __eq__ | ||
| 94 | unique_tones.append(tone) | 94 | unique_tones.append(tone) | ||
| 95 | 95 | ||||
| 96 | if len(unique_tones) < 2: | 96 | if len(unique_tones) < 2: | ||
| 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 102 | def __str__(self): | 102 | def __str__(self): | ||
| 103 | root_index = Tone.TONES.index(self.root.name_of_tone) | 103 | root_index = Tone.TONES.index(self.root.name_of_tone) | ||
| 104 | other_tones = [tone for tone in self.tones if tone != self.root] | 104 | other_tones = [tone for tone in self.tones if tone != self.root] | ||
| 105 | sorted_tones = sorted( | 105 | sorted_tones = sorted( | ||
| 106 | other_tones, | 106 | other_tones, | ||
| 107 | key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12 | 107 | key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12 | ||
| 108 | ) | 108 | ) | ||
| 109 | sorted_tones.insert(0, self.root) | 109 | sorted_tones.insert(0, self.root) | ||
| 110 | return "-".join(str(tone) for tone in sorted_tones) | 110 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 111 | 111 | ||||
| 112 | def __add__(self, other): | 112 | def __add__(self, other): | ||
| 113 | if isinstance(other, Tone): | 113 | if isinstance(other, Tone): | ||
| 114 | return self._add_tone(other) | 114 | return self._add_tone(other) | ||
| 115 | elif isinstance(other, Chord): | 115 | elif isinstance(other, Chord): | ||
| 116 | return self._add_chord(other) | 116 | return self._add_chord(other) | ||
| 117 | raise TypeError("Invalid operation") | 117 | raise TypeError("Invalid operation") | ||
| 118 | 118 | ||||
| 119 | def __sub__(self, other): | 119 | def __sub__(self, other): | ||
| 120 | if isinstance(other, Tone): | 120 | if isinstance(other, Tone): | ||
| 121 | if other not in self.tones: # using __eq__ | 121 | if other not in self.tones: # using __eq__ | ||
| 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 123 | new_tones = [tone for tone in self.tones if tone != other] | 123 | new_tones = [tone for tone in self.tones if tone != other] | ||
| 124 | if len(set(new_tones)) < 2: | 124 | if len(set(new_tones)) < 2: | ||
| 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 126 | return Chord(*new_tones) | 126 | return Chord(*new_tones) | ||
| 127 | raise TypeError("Invalid operation") | 127 | raise TypeError("Invalid operation") | ||
| 128 | 128 | ||||
| 129 | def _validate_distance_to_root(self, target): | 129 | def _validate_distance_to_root(self, target): | ||
| 130 | root_index = self.root.get_index() | 130 | root_index = self.root.get_index() | ||
| 131 | for tone in self.tones: | 131 | for tone in self.tones: | ||
| 132 | distance = (tone.get_index() - root_index) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 133 | if distance == target: | 133 | if distance == target: | ||
| 134 | return True | 134 | return True | ||
| 135 | return False | 135 | return False | ||
| 136 | 136 | ||||
| 137 | def is_minor(self): | 137 | def is_minor(self): | ||
| 138 | return self._validate_distance_to_root(3) | 138 | return self._validate_distance_to_root(3) | ||
| 139 | 139 | ||||
| 140 | def is_major(self): | 140 | def is_major(self): | ||
| 141 | return self._validate_distance_to_root(4) | 141 | return self._validate_distance_to_root(4) | ||
| 142 | 142 | ||||
| 143 | def is_power_chord(self): | 143 | def is_power_chord(self): | ||
| 144 | return not self.is_minor() and not self.is_major() | 144 | return not self.is_minor() and not self.is_major() | ||
| 145 | 145 | ||||
| 146 | def _add_tone(self, other): | 146 | def _add_tone(self, other): | ||
| 147 | if other not in self.tones: # using __eq__ | 147 | if other not in self.tones: # using __eq__ | ||
| 148 | new_tones = list(self.tones) | 148 | new_tones = list(self.tones) | ||
| 149 | new_tones.append(other) | 149 | new_tones.append(other) | ||
| 150 | return Chord(*new_tones) | 150 | return Chord(*new_tones) | ||
| 151 | else: | 151 | else: | ||
| 152 | return self | 152 | return self | ||
| 153 | 153 | ||||
| 154 | def _add_chord(self, other): | 154 | def _add_chord(self, other): | ||
| 155 | new_tones = list(self.tones) | 155 | new_tones = list(self.tones) | ||
| 156 | for tone in other.tones: | 156 | for tone in other.tones: | ||
| 157 | if tone not in self.tones: # using __eq__ | 157 | if tone not in self.tones: # using __eq__ | ||
| 158 | new_tones.append(tone) | 158 | new_tones.append(tone) | ||
| 159 | return Chord(*new_tones) | 159 | return Chord(*new_tones) | ||
| 160 | 160 | ||||
| 161 | def transposed(self, input_interval): | 161 | def transposed(self, input_interval): | ||
| 162 | if not isinstance(input_interval, Interval): | 162 | if not isinstance(input_interval, Interval): | ||
| 163 | raise ValueError("Interval must be an instance of Interval") | 163 | raise ValueError("Interval must be an instance of Interval") | ||
| 164 | 164 | ||||
| 165 | transposed_tones = [tone + input_interval for tone in self.tones] | 165 | transposed_tones = [tone + input_interval for tone in self.tones] | ||
| 166 | return Chord(*transposed_tones) | 166 | return Chord(*transposed_tones) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | class Tone: | f | 1 | class Tone: | 
| 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
| 3 | 3 | ||||
| 4 | def __init__(self, input_name): | 4 | def __init__(self, input_name): | ||
| 5 | if input_name not in self.TONES: | 5 | if input_name not in self.TONES: | ||
| 6 | raise ValueError("Invalid tone name") | 6 | raise ValueError("Invalid tone name") | ||
| 7 | self.name_of_tone = input_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| 12 | def __hash__(self): # this time i didn't use it | 12 | def __hash__(self): # this time i didn't use it | ||
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 18 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| 20 | return self._calculate_tone(other, "+") | 20 | return self._calculate_tone(other, "+") | ||
| 21 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| 22 | return self._calculate_interval(other, "+") | 22 | return self._calculate_interval(other, "+") | ||
| 23 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 24 | 24 | ||||
| 25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 26 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| 27 | return self._calculate_tone(other, "-") | 27 | return self._calculate_tone(other, "-") | ||
| 28 | elif isinstance(other, Interval): | 28 | elif isinstance(other, Interval): | ||
| 29 | return self._calculate_interval(other, "-") | 29 | return self._calculate_interval(other, "-") | ||
| 30 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 31 | 31 | ||||
| 32 | def get_index(self): | 32 | def get_index(self): | ||
| 33 | return Tone.TONES.index(self.name_of_tone) | 33 | return Tone.TONES.index(self.name_of_tone) | ||
| 34 | 34 | ||||
| 35 | def _calculate_tone(self, other, symbol): | 35 | def _calculate_tone(self, other, symbol): | ||
| 36 | if symbol == "+": | 36 | if symbol == "+": | ||
| 37 | return Chord(self, other) | 37 | return Chord(self, other) | ||
| 38 | elif symbol == "-": | 38 | elif symbol == "-": | ||
| 39 | distance = (self.TONES.index(self.name_of_tone) - | 39 | distance = (self.TONES.index(self.name_of_tone) - | ||
| 40 | self.TONES.index(other.name_of_tone)) % 12 | 40 | self.TONES.index(other.name_of_tone)) % 12 | ||
| 41 | return Interval(distance) | 41 | return Interval(distance) | ||
| 42 | else: | 42 | else: | ||
| 43 | raise ValueError("Invalid symbol") | 43 | raise ValueError("Invalid symbol") | ||
| 44 | 44 | ||||
| 45 | def _calculate_interval(self, other, symbol): | 45 | def _calculate_interval(self, other, symbol): | ||
| 46 | if symbol == "+": | 46 | if symbol == "+": | ||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | ||
| 48 | return Tone(self.TONES[index]) | 48 | return Tone(self.TONES[index]) | ||
| 49 | elif symbol == "-": | 49 | elif symbol == "-": | ||
| 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 51 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| 52 | else: | 52 | else: | ||
| 53 | raise ValueError("Invalid symbol") | 53 | raise ValueError("Invalid symbol") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| 57 | INTERVAL_NAMES = { | 57 | INTERVAL_NAMES = { | ||
| 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | ||
| 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | ||
| 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | ||
| 61 | 10: "minor 7th", 11: "major 7th" | 61 | 10: "minor 7th", 11: "major 7th" | ||
| 62 | } | 62 | } | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 73 | def __add__(self, other): | 73 | def __add__(self, other): | ||
| 74 | if isinstance(other, Interval): | 74 | if isinstance(other, Interval): | ||
| 75 | return Interval(self.semitones + other.semitones) | 75 | return Interval(self.semitones + other.semitones) | ||
| 76 | raise TypeError("Invalid operation") | 76 | raise TypeError("Invalid operation") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class Chord: | ||
| 83 | def __init__(self, root, *tones): | 83 | def __init__(self, root, *tones): | ||
| 84 | if not isinstance(root, Tone): | 84 | if not isinstance(root, Tone): | ||
| 85 | raise ValueError("Root must be a Tone instance") | 85 | raise ValueError("Root must be a Tone instance") | ||
| 86 | 86 | ||||
| 87 | for tone in tones: | 87 | for tone in tones: | ||
| 88 | if not isinstance(tone, Tone): | 88 | if not isinstance(tone, Tone): | ||
| 89 | raise ValueError("All tones must be Tone instances") | 89 | raise ValueError("All tones must be Tone instances") | ||
| 90 | 90 | ||||
| 91 | unique_tones = [root] | 91 | unique_tones = [root] | ||
| 92 | for tone in tones: | 92 | for tone in tones: | ||
| 93 | if tone not in unique_tones: #using __eq__ | 93 | if tone not in unique_tones: #using __eq__ | ||
| 94 | unique_tones.append(tone) | 94 | unique_tones.append(tone) | ||
| 95 | 95 | ||||
| 96 | if len(unique_tones) < 2: | 96 | if len(unique_tones) < 2: | ||
| 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 102 | def __str__(self): | 102 | def __str__(self): | ||
| n | n | 103 | root_index = Tone.TONES.index(self.root.name_of_tone) | ||
| 103 | other_tones = [tone for tone in self.tones if tone != self.root] | 104 | other_tones = [tone for tone in self.tones if tone != self.root] | ||
| 104 | sorted_tones = sorted( | 105 | sorted_tones = sorted( | ||
| t | t | 106 | other_tones, | ||
| 105 | other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone) | 107 | key=lambda tone: (Tone.TONES.index(tone.name_of_tone) - root_index) % 12 | ||
| 106 | ) | 108 | ) | ||
| 107 | sorted_tones.insert(0, self.root) | 109 | sorted_tones.insert(0, self.root) | ||
| 108 | return "-".join(str(tone) for tone in sorted_tones) | 110 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 109 | 111 | ||||
| 110 | def __add__(self, other): | 112 | def __add__(self, other): | ||
| 111 | if isinstance(other, Tone): | 113 | if isinstance(other, Tone): | ||
| 112 | return self._add_tone(other) | 114 | return self._add_tone(other) | ||
| 113 | elif isinstance(other, Chord): | 115 | elif isinstance(other, Chord): | ||
| 114 | return self._add_chord(other) | 116 | return self._add_chord(other) | ||
| 115 | raise TypeError("Invalid operation") | 117 | raise TypeError("Invalid operation") | ||
| 116 | 118 | ||||
| 117 | def __sub__(self, other): | 119 | def __sub__(self, other): | ||
| 118 | if isinstance(other, Tone): | 120 | if isinstance(other, Tone): | ||
| 119 | if other not in self.tones: # using __eq__ | 121 | if other not in self.tones: # using __eq__ | ||
| 120 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 122 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 121 | new_tones = [tone for tone in self.tones if tone != other] | 123 | new_tones = [tone for tone in self.tones if tone != other] | ||
| 122 | if len(set(new_tones)) < 2: | 124 | if len(set(new_tones)) < 2: | ||
| 123 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 125 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 124 | return Chord(*new_tones) | 126 | return Chord(*new_tones) | ||
| 125 | raise TypeError("Invalid operation") | 127 | raise TypeError("Invalid operation") | ||
| 126 | 128 | ||||
| 127 | def _validate_distance_to_root(self, target): | 129 | def _validate_distance_to_root(self, target): | ||
| 128 | root_index = self.root.get_index() | 130 | root_index = self.root.get_index() | ||
| 129 | for tone in self.tones: | 131 | for tone in self.tones: | ||
| 130 | distance = (tone.get_index() - root_index) % 12 | 132 | distance = (tone.get_index() - root_index) % 12 | ||
| 131 | if distance == target: | 133 | if distance == target: | ||
| 132 | return True | 134 | return True | ||
| 133 | return False | 135 | return False | ||
| 134 | 136 | ||||
| 135 | def is_minor(self): | 137 | def is_minor(self): | ||
| 136 | return self._validate_distance_to_root(3) | 138 | return self._validate_distance_to_root(3) | ||
| 137 | 139 | ||||
| 138 | def is_major(self): | 140 | def is_major(self): | ||
| 139 | return self._validate_distance_to_root(4) | 141 | return self._validate_distance_to_root(4) | ||
| 140 | 142 | ||||
| 141 | def is_power_chord(self): | 143 | def is_power_chord(self): | ||
| 142 | return not self.is_minor() and not self.is_major() | 144 | return not self.is_minor() and not self.is_major() | ||
| 143 | 145 | ||||
| 144 | def _add_tone(self, other): | 146 | def _add_tone(self, other): | ||
| 145 | if other not in self.tones: # using __eq__ | 147 | if other not in self.tones: # using __eq__ | ||
| 146 | new_tones = list(self.tones) | 148 | new_tones = list(self.tones) | ||
| 147 | new_tones.append(other) | 149 | new_tones.append(other) | ||
| 148 | return Chord(*new_tones) | 150 | return Chord(*new_tones) | ||
| 149 | else: | 151 | else: | ||
| 150 | return self | 152 | return self | ||
| 151 | 153 | ||||
| 152 | def _add_chord(self, other): | 154 | def _add_chord(self, other): | ||
| 153 | new_tones = list(self.tones) | 155 | new_tones = list(self.tones) | ||
| 154 | for tone in other.tones: | 156 | for tone in other.tones: | ||
| 155 | if tone not in self.tones: # using __eq__ | 157 | if tone not in self.tones: # using __eq__ | ||
| 156 | new_tones.append(tone) | 158 | new_tones.append(tone) | ||
| 157 | return Chord(*new_tones) | 159 | return Chord(*new_tones) | ||
| 158 | 160 | ||||
| 159 | def transposed(self, input_interval): | 161 | def transposed(self, input_interval): | ||
| 160 | if not isinstance(input_interval, Interval): | 162 | if not isinstance(input_interval, Interval): | ||
| 161 | raise ValueError("Interval must be an instance of Interval") | 163 | raise ValueError("Interval must be an instance of Interval") | ||
| 162 | 164 | ||||
| 163 | transposed_tones = [tone + input_interval for tone in self.tones] | 165 | transposed_tones = [tone + input_interval for tone in self.tones] | ||
| 164 | return Chord(*transposed_tones) | 166 | return Chord(*transposed_tones) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | class Tone: | f | 1 | class Tone: | 
| 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
| 3 | 3 | ||||
| 4 | def __init__(self, input_name): | 4 | def __init__(self, input_name): | ||
| 5 | if input_name not in self.TONES: | 5 | if input_name not in self.TONES: | ||
| 6 | raise ValueError("Invalid tone name") | 6 | raise ValueError("Invalid tone name") | ||
| 7 | self.name_of_tone = input_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| n | 12 | def __hash__(self): #this time i didn't use it | n | 12 | def __hash__(self): # this time i didn't use it | 
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| 18 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| n | 20 | return self.calculate_tone(other, "+") | n | 20 | return self._calculate_tone(other, "+") | 
| 21 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| n | 22 | return self.calculate_interval(other, "+") | n | 22 | return self._calculate_interval(other, "+") | 
| 23 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 24 | 24 | ||||
| 25 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 26 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| n | 27 | return self.calculate_tone(other, "-") | n | 27 | return self._calculate_tone(other, "-") | 
| 28 | elif isinstance(other, Interval): | 28 | elif isinstance(other, Interval): | ||
| n | 29 | return self.calculate_interval(other, "-") | n | 29 | return self._calculate_interval(other, "-") | 
| 30 | raise TypeError("Invalid operation") | 30 | raise TypeError("Invalid operation") | ||
| 31 | 31 | ||||
| 32 | def get_index(self): | 32 | def get_index(self): | ||
| 33 | return Tone.TONES.index(self.name_of_tone) | 33 | return Tone.TONES.index(self.name_of_tone) | ||
| 34 | 34 | ||||
| n | 35 | def calculate_tone(self, other, symbol): | n | 35 | def _calculate_tone(self, other, symbol): | 
| 36 | if symbol == "+": | 36 | if symbol == "+": | ||
| 37 | return Chord(self, other) | 37 | return Chord(self, other) | ||
| 38 | elif symbol == "-": | 38 | elif symbol == "-": | ||
| 39 | distance = (self.TONES.index(self.name_of_tone) - | 39 | distance = (self.TONES.index(self.name_of_tone) - | ||
| 40 | self.TONES.index(other.name_of_tone)) % 12 | 40 | self.TONES.index(other.name_of_tone)) % 12 | ||
| 41 | return Interval(distance) | 41 | return Interval(distance) | ||
| 42 | else: | 42 | else: | ||
| 43 | raise ValueError("Invalid symbol") | 43 | raise ValueError("Invalid symbol") | ||
| 44 | 44 | ||||
| n | 45 | def calculate_interval(self, other, symbol): | n | 45 | def _calculate_interval(self, other, symbol): | 
| 46 | if symbol == "+": | 46 | if symbol == "+": | ||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | ||
| 48 | return Tone(self.TONES[index]) | 48 | return Tone(self.TONES[index]) | ||
| 49 | elif symbol == "-": | 49 | elif symbol == "-": | ||
| 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 51 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| 52 | else: | 52 | else: | ||
| 53 | raise ValueError("Invalid symbol") | 53 | raise ValueError("Invalid symbol") | ||
| 54 | 54 | ||||
| 55 | 55 | ||||
| 56 | class Interval: | 56 | class Interval: | ||
| 57 | INTERVAL_NAMES = { | 57 | INTERVAL_NAMES = { | ||
| 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | ||
| 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | ||
| 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | ||
| 61 | 10: "minor 7th", 11: "major 7th" | 61 | 10: "minor 7th", 11: "major 7th" | ||
| 62 | } | 62 | } | ||
| 63 | 63 | ||||
| 64 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| 65 | if isinstance(number, int): | 65 | if isinstance(number, int): | ||
| 66 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 67 | else: | 67 | else: | ||
| 68 | raise TypeError("Invalid input") | 68 | raise TypeError("Invalid input") | ||
| 69 | 69 | ||||
| 70 | def __str__(self): | 70 | def __str__(self): | ||
| 71 | return self.INTERVAL_NAMES[self.semitones] | 71 | return self.INTERVAL_NAMES[self.semitones] | ||
| 72 | 72 | ||||
| 73 | def __add__(self, other): | 73 | def __add__(self, other): | ||
| 74 | if isinstance(other, Interval): | 74 | if isinstance(other, Interval): | ||
| 75 | return Interval(self.semitones + other.semitones) | 75 | return Interval(self.semitones + other.semitones) | ||
| 76 | raise TypeError("Invalid operation") | 76 | raise TypeError("Invalid operation") | ||
| 77 | 77 | ||||
| 78 | def __neg__(self): | 78 | def __neg__(self): | ||
| 79 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 80 | 80 | ||||
| 81 | 81 | ||||
| 82 | class Chord: | 82 | class Chord: | ||
| 83 | def __init__(self, root, *tones): | 83 | def __init__(self, root, *tones): | ||
| 84 | if not isinstance(root, Tone): | 84 | if not isinstance(root, Tone): | ||
| 85 | raise ValueError("Root must be a Tone instance") | 85 | raise ValueError("Root must be a Tone instance") | ||
| 86 | 86 | ||||
| 87 | for tone in tones: | 87 | for tone in tones: | ||
| 88 | if not isinstance(tone, Tone): | 88 | if not isinstance(tone, Tone): | ||
| 89 | raise ValueError("All tones must be Tone instances") | 89 | raise ValueError("All tones must be Tone instances") | ||
| 90 | 90 | ||||
| 91 | unique_tones = [root] | 91 | unique_tones = [root] | ||
| 92 | for tone in tones: | 92 | for tone in tones: | ||
| 93 | if tone not in unique_tones: #using __eq__ | 93 | if tone not in unique_tones: #using __eq__ | ||
| 94 | unique_tones.append(tone) | 94 | unique_tones.append(tone) | ||
| 95 | 95 | ||||
| 96 | if len(unique_tones) < 2: | 96 | if len(unique_tones) < 2: | ||
| 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 98 | 98 | ||||
| 99 | self.root = root | 99 | self.root = root | ||
| 100 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 101 | 101 | ||||
| 102 | def __str__(self): | 102 | def __str__(self): | ||
| 103 | other_tones = [tone for tone in self.tones if tone != self.root] | 103 | other_tones = [tone for tone in self.tones if tone != self.root] | ||
| 104 | sorted_tones = sorted( | 104 | sorted_tones = sorted( | ||
| 105 | other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone) | 105 | other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone) | ||
| 106 | ) | 106 | ) | ||
| 107 | sorted_tones.insert(0, self.root) | 107 | sorted_tones.insert(0, self.root) | ||
| 108 | return "-".join(str(tone) for tone in sorted_tones) | 108 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 109 | 109 | ||||
| 110 | def __add__(self, other): | 110 | def __add__(self, other): | ||
| 111 | if isinstance(other, Tone): | 111 | if isinstance(other, Tone): | ||
| n | 112 | return self.add_tone(other) | n | 112 | return self._add_tone(other) | 
| 113 | elif isinstance(other, Chord): | 113 | elif isinstance(other, Chord): | ||
| n | 114 | return self.add_chord(other) | n | 114 | return self._add_chord(other) | 
| 115 | raise TypeError("Invalid operation") | 115 | raise TypeError("Invalid operation") | ||
| 116 | 116 | ||||
| 117 | def __sub__(self, other): | 117 | def __sub__(self, other): | ||
| 118 | if isinstance(other, Tone): | 118 | if isinstance(other, Tone): | ||
| n | 119 | if other not in self.tones: #using __eq__ | n | 119 | if other not in self.tones: # using __eq__ | 
| 120 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 120 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||
| 121 | new_tones = [tone for tone in self.tones if tone != other] | 121 | new_tones = [tone for tone in self.tones if tone != other] | ||
| 122 | if len(set(new_tones)) < 2: | 122 | if len(set(new_tones)) < 2: | ||
| 123 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 123 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 124 | return Chord(*new_tones) | 124 | return Chord(*new_tones) | ||
| 125 | raise TypeError("Invalid operation") | 125 | raise TypeError("Invalid operation") | ||
| 126 | 126 | ||||
| n | 127 | def validate_distance_to_root(self, target): | n | 127 | def _validate_distance_to_root(self, target): | 
| 128 | root_index = self.root.get_index() | 128 | root_index = self.root.get_index() | ||
| 129 | for tone in self.tones: | 129 | for tone in self.tones: | ||
| 130 | distance = (tone.get_index() - root_index) % 12 | 130 | distance = (tone.get_index() - root_index) % 12 | ||
| 131 | if distance == target: | 131 | if distance == target: | ||
| 132 | return True | 132 | return True | ||
| 133 | return False | 133 | return False | ||
| 134 | 134 | ||||
| 135 | def is_minor(self): | 135 | def is_minor(self): | ||
| n | 136 | return self.validate_distance_to_root(3) | n | 136 | return self._validate_distance_to_root(3) | 
| 137 | 137 | ||||
| 138 | def is_major(self): | 138 | def is_major(self): | ||
| n | 139 | return self.validate_distance_to_root(4) | n | 139 | return self._validate_distance_to_root(4) | 
| 140 | 140 | ||||
| 141 | def is_power_chord(self): | 141 | def is_power_chord(self): | ||
| 142 | return not self.is_minor() and not self.is_major() | 142 | return not self.is_minor() and not self.is_major() | ||
| 143 | 143 | ||||
| n | 144 | def add_tone(self, other): | n | 144 | def _add_tone(self, other): | 
| 145 | if other not in self.tones: #using __eq__ | 145 | if other not in self.tones: # using __eq__ | ||
| 146 | new_tones = list(self.tones) | 146 | new_tones = list(self.tones) | ||
| 147 | new_tones.append(other) | 147 | new_tones.append(other) | ||
| 148 | return Chord(*new_tones) | 148 | return Chord(*new_tones) | ||
| 149 | else: | 149 | else: | ||
| 150 | return self | 150 | return self | ||
| 151 | 151 | ||||
| n | 152 | def add_chord(self, other): | n | 152 | def _add_chord(self, other): | 
| 153 | new_tones = list(self.tones) | 153 | new_tones = list(self.tones) | ||
| 154 | for tone in other.tones: | 154 | for tone in other.tones: | ||
| t | 155 | if tone not in self.tones: #using __eq__ | t | 155 | if tone not in self.tones: # using __eq__ | 
| 156 | new_tones.append(tone) | 156 | new_tones.append(tone) | ||
| 157 | return Chord(*new_tones) | 157 | return Chord(*new_tones) | ||
| 158 | 158 | ||||
| 159 | def transposed(self, input_interval): | 159 | def transposed(self, input_interval): | ||
| 160 | if not isinstance(input_interval, Interval): | 160 | if not isinstance(input_interval, Interval): | ||
| 161 | raise ValueError("Interval must be an instance of Interval") | 161 | raise ValueError("Interval must be an instance of Interval") | ||
| 162 | 162 | ||||
| 163 | transposed_tones = [tone + input_interval for tone in self.tones] | 163 | transposed_tones = [tone + input_interval for tone in self.tones] | ||
| 164 | return Chord(*transposed_tones) | 164 | return Chord(*transposed_tones) | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||
| f | 1 | class Tone: | f | 1 | class Tone: | 
| 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | 2 | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | ||
| 3 | 3 | ||||
| 4 | def __init__(self, input_name): | 4 | def __init__(self, input_name): | ||
| 5 | if input_name not in self.TONES: | 5 | if input_name not in self.TONES: | ||
| 6 | raise ValueError("Invalid tone name") | 6 | raise ValueError("Invalid tone name") | ||
| 7 | self.name_of_tone = input_name | 7 | self.name_of_tone = input_name | ||
| 8 | 8 | ||||
| 9 | def __str__(self): | 9 | def __str__(self): | ||
| 10 | return self.name_of_tone | 10 | return self.name_of_tone | ||
| 11 | 11 | ||||
| n | 12 | def __hash__(self): | n | 12 | def __hash__(self): #this time i didn't use it | 
| 13 | return hash(self.name_of_tone) | 13 | return hash(self.name_of_tone) | ||
| 14 | 14 | ||||
| 15 | def __eq__(self, other): | 15 | def __eq__(self, other): | ||
| 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | 16 | return isinstance(other, Tone) and self.name_of_tone == other.name_of_tone | ||
| 17 | 17 | ||||
| n | 18 | def get_index(self): | n | ||
| 19 | return Tone.TONES.index(self.name_of_tone) | ||||
| 20 | |||||
| 21 | def __add__(self, other): | 18 | def __add__(self, other): | ||
| 22 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
| n | 23 | return Chord(self, other) | n | 20 | return self.calculate_tone(other, "+") | 
| 24 | elif isinstance(other, Interval): | 21 | elif isinstance(other, Interval): | ||
| n | 25 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | n | 22 | return self.calculate_interval(other, "+") | 
| 26 | return Tone(self.TONES[index]) | ||||
| 27 | raise TypeError("Invalid operation") | 23 | raise TypeError("Invalid operation") | ||
| 28 | 24 | ||||
| 29 | def __sub__(self, other): | 25 | def __sub__(self, other): | ||
| 30 | if isinstance(other, Tone): | 26 | if isinstance(other, Tone): | ||
| n | 31 | distance = (self.TONES.index(self.name_of_tone) - self.TONES.index(other.name_of_tone)) % 12 | n | 27 | return self.calculate_tone(other, "-") | 
| 28 | elif isinstance(other, Interval): | ||||
| 29 | return self.calculate_interval(other, "-") | ||||
| 30 | raise TypeError("Invalid operation") | ||||
| 31 | |||||
| 32 | def get_index(self): | ||||
| 33 | return Tone.TONES.index(self.name_of_tone) | ||||
| 34 | |||||
| 35 | def calculate_tone(self, other, symbol): | ||||
| 36 | if symbol == "+": | ||||
| 37 | return Chord(self, other) | ||||
| 38 | elif symbol == "-": | ||||
| 39 | distance = (self.TONES.index(self.name_of_tone) - | ||||
| 40 | self.TONES.index(other.name_of_tone)) % 12 | ||||
| 32 | return Interval(distance) | 41 | return Interval(distance) | ||
| n | 33 | elif isinstance(other, Interval): | n | 42 | else: | 
| 43 | raise ValueError("Invalid symbol") | ||||
| 44 | |||||
| 45 | def calculate_interval(self, other, symbol): | ||||
| 46 | if symbol == "+": | ||||
| 47 | index = (self.TONES.index(self.name_of_tone) + other.semitones) % 12 | ||||
| 48 | return Tone(self.TONES[index]) | ||||
| 49 | elif symbol == "-": | ||||
| 34 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | 50 | index = (self.TONES.index(self.name_of_tone) - other.semitones) % 12 | ||
| 35 | return Tone(self.TONES[index]) | 51 | return Tone(self.TONES[index]) | ||
| n | 36 | raise TypeError("Invalid operation") | n | 52 | else: | 
| 53 | raise ValueError("Invalid symbol") | ||||
| 37 | 54 | ||||
| 38 | 55 | ||||
| 39 | class Interval: | 56 | class Interval: | ||
| n | 40 | interval_names = { | n | 57 | INTERVAL_NAMES = { | 
| 41 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | 58 | 0: "octave", 1: "minor 2nd", 2: "major 2nd", 3: "minor 3rd", | ||
| 42 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | 59 | 4: "major 3rd", 5: "perfect 4th", 6: "diminished 5th", | ||
| n | 43 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", 10: "minor 7th", 11: "major 7th" | n | 60 | 7: "perfect 5th", 8: "minor 6th", 9: "major 6th", | 
| 61 | 10: "minor 7th", 11: "major 7th" | ||||
| 44 | } | 62 | } | ||
| 45 | 63 | ||||
| 46 | def __init__(self, number): | 64 | def __init__(self, number): | ||
| n | 47 | if type(number) is int: | n | 65 | if isinstance(number, int): | 
| 48 | self.semitones = number % 12 | 66 | self.semitones = number % 12 | ||
| 49 | else: | 67 | else: | ||
| n | 50 | raise TypeError("Negative interval number") | n | 68 | raise TypeError("Invalid input") | 
| 51 | 69 | ||||
| 52 | def __str__(self): | 70 | def __str__(self): | ||
| n | 53 | return self.interval_names[self.semitones] | n | 71 | return self.INTERVAL_NAMES[self.semitones] | 
| 54 | 72 | ||||
| 55 | def __add__(self, other): | 73 | def __add__(self, other): | ||
| 56 | if isinstance(other, Interval): | 74 | if isinstance(other, Interval): | ||
| 57 | return Interval(self.semitones + other.semitones) | 75 | return Interval(self.semitones + other.semitones) | ||
| 58 | raise TypeError("Invalid operation") | 76 | raise TypeError("Invalid operation") | ||
| 59 | 77 | ||||
| 60 | def __neg__(self): | 78 | def __neg__(self): | ||
| 61 | return Interval(-self.semitones) | 79 | return Interval(-self.semitones) | ||
| 62 | 80 | ||||
| 63 | 81 | ||||
| 64 | class Chord: | 82 | class Chord: | ||
| 65 | def __init__(self, root, *tones): | 83 | def __init__(self, root, *tones): | ||
| 66 | if not isinstance(root, Tone): | 84 | if not isinstance(root, Tone): | ||
| 67 | raise ValueError("Root must be a Tone instance") | 85 | raise ValueError("Root must be a Tone instance") | ||
| 68 | 86 | ||||
| 69 | for tone in tones: | 87 | for tone in tones: | ||
| 70 | if not isinstance(tone, Tone): | 88 | if not isinstance(tone, Tone): | ||
| 71 | raise ValueError("All tones must be Tone instances") | 89 | raise ValueError("All tones must be Tone instances") | ||
| 72 | 90 | ||||
| n | 73 | unique_tones = [root] + list(tones) | n | 91 | unique_tones = [root] | 
| 92 | for tone in tones: | ||||
| 93 | if tone not in unique_tones: #using __eq__ | ||||
| 94 | unique_tones.append(tone) | ||||
| 74 | 95 | ||||
| n | 75 | if len(set(unique_tones)) < 2: #using __eq__ from Tone | n | 96 | if len(unique_tones) < 2: | 
| 76 | raise TypeError("Cannot have a chord made of only 1 unique tone") | 97 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||
| 77 | 98 | ||||
| 78 | self.root = root | 99 | self.root = root | ||
| 79 | self.tones = unique_tones | 100 | self.tones = unique_tones | ||
| 80 | 101 | ||||
| 81 | def __str__(self): | 102 | def __str__(self): | ||
| 82 | other_tones = [tone for tone in self.tones if tone != self.root] | 103 | other_tones = [tone for tone in self.tones if tone != self.root] | ||
| n | n | 104 | sorted_tones = sorted( | ||
| 83 | sorted_tones = sorted(other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone)) | 105 | other_tones, key=lambda tone: Tone.TONES.index(tone.name_of_tone) | ||
| 106 | ) | ||||
| 84 | sorted_tones.insert(0, self.root) | 107 | sorted_tones.insert(0, self.root) | ||
| n | 85 | n | |||
| 86 | return "-".join(str(tone) for tone in sorted_tones) | 108 | return "-".join(str(tone) for tone in sorted_tones) | ||
| 87 | 109 | ||||
| n | 88 | def is_minor(self): | n | 110 | def __add__(self, other): | 
| 111 | if isinstance(other, Tone): | ||||
| 112 | return self.add_tone(other) | ||||
| 113 | elif isinstance(other, Chord): | ||||
| 114 | return self.add_chord(other) | ||||
| 115 | raise TypeError("Invalid operation") | ||||
| 116 | |||||
| 117 | def __sub__(self, other): | ||||
| 118 | if isinstance(other, Tone): | ||||
| 119 | if other not in self.tones: #using __eq__ | ||||
| 120 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | ||||
| 121 | new_tones = [tone for tone in self.tones if tone != other] | ||||
| 122 | if len(set(new_tones)) < 2: | ||||
| 123 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||||
| 124 | return Chord(*new_tones) | ||||
| 125 | raise TypeError("Invalid operation") | ||||
| 126 | |||||
| 127 | def validate_distance_to_root(self, target): | ||||
| 89 | root_index = self.root.get_index() | 128 | root_index = self.root.get_index() | ||
| n | 90 | n | |||
| 91 | for tone in self.tones: | 129 | for tone in self.tones: | ||
| 92 | distance = (tone.get_index() - root_index) % 12 | 130 | distance = (tone.get_index() - root_index) % 12 | ||
| n | 93 | if distance == 3: | n | 131 | if distance == target: | 
| 94 | return True | 132 | return True | ||
| 95 | return False | 133 | return False | ||
| 96 | 134 | ||||
| n | n | 135 | def is_minor(self): | ||
| 136 | return self.validate_distance_to_root(3) | ||||
| 137 | |||||
| 97 | def is_major(self): | 138 | def is_major(self): | ||
| n | 98 | root_index = self.root.get_index() | n | 139 | return self.validate_distance_to_root(4) | 
| 99 | |||||
| 100 | for tone in self.tones: | ||||
| 101 | distance = (tone.get_index() - root_index) % 12 | ||||
| 102 | if distance == 4: | ||||
| 103 | return True | ||||
| 104 | return False | ||||
| 105 | 140 | ||||
| 106 | def is_power_chord(self): | 141 | def is_power_chord(self): | ||
| 107 | return not self.is_minor() and not self.is_major() | 142 | return not self.is_minor() and not self.is_major() | ||
| 108 | 143 | ||||
| n | 109 | def __add__(self, other): | n | 144 | def add_tone(self, other): | 
| 110 | if isinstance(other, Tone): | 145 | if other not in self.tones: #using __eq__ | ||
| 111 | new_tones = list(self.tones) | 146 | new_tones = list(self.tones) | ||
| n | 112 | if other not in new_tones: #using __eq__ from Tone | n | ||
| 113 | new_tones.append(other) | 147 | new_tones.append(other) | ||
| 114 | return Chord(*new_tones) | 148 | return Chord(*new_tones) | ||
| n | 115 | elif isinstance(other, Chord): | n | 149 | else: | 
| 116 | new_tones = list(self.tones) | 150 | return self | ||
| 117 | for tone in other.tones: | ||||
| 118 | if tone not in new_tones: #using __eq__ from Tone | ||||
| 119 | new_tones.append(tone) | ||||
| 120 | return Chord(*new_tones) | ||||
| 121 | raise TypeError("Invalid operation") | ||||
| 122 | 151 | ||||
| n | 123 | def __sub__(self, other): | n | 152 | def add_chord(self, other): | 
| 124 | if isinstance(other, Tone): | 153 | new_tones = list(self.tones) | ||
| 154 | for tone in other.tones: | ||||
| 125 | if other not in self.tones: | 155 | if tone not in self.tones: #using __eq__ | ||
| 126 | raise TypeError(f"Cannot remove tone {other} from chord {self}") | 156 | new_tones.append(tone) | ||
| 127 | |||||
| 128 | new_tones = [tone for tone in self.tones if tone != other] | ||||
| 129 | if len(set(new_tones)) < 2: | ||||
| 130 | raise TypeError("Cannot have a chord made of only 1 unique tone") | ||||
| 131 | |||||
| 132 | return Chord(self.root, *new_tones) | 157 | return Chord(*new_tones) | ||
| 133 | raise TypeError("Invalid operation") | ||||
| 134 | 158 | ||||
| 135 | def transposed(self, input_interval): | 159 | def transposed(self, input_interval): | ||
| 136 | if not isinstance(input_interval, Interval): | 160 | if not isinstance(input_interval, Interval): | ||
| 137 | raise ValueError("Interval must be an instance of Interval") | 161 | raise ValueError("Interval must be an instance of Interval") | ||
| 138 | 162 | ||||
| n | 139 | transposed_tones = {tone + input_interval for tone in self.tones} #using __hash__ from Tone | n | 163 | transposed_tones = [tone + input_interval for tone in self.tones] | 
| 140 | return Chord(*transposed_tones) | 164 | return Chord(*transposed_tones) | ||
| t | 141 | t | 
| Legends | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|
 
  | 
              
  |  |||||||||