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