1INVALID_OPERATION_MESSAGE = "Invalid operation"
2
3
4class Tone(str):
5 """Class that represents a musical tone."""
6 TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B')
7
8 def __add__(self, other):
9 """Add two tones or an interval to a tone."""
10 if isinstance(other, Tone):
11 return Chord(self, other)
12 if isinstance(other, Interval):
13 current = self.TONES.index(self)
14 new = self.TONES[(current + int(other)) % len(self.TONES)]
15 return Tone(new)
16
17 def __sub__(self, other):
18 """Subtract two tones or an interval from a tone."""
19 if isinstance(other, Tone):
20 return Interval(self.TONES.index(self) - self.TONES.index(other))
21 if isinstance(other, Interval):
22 return self.__add__(-other)
23
24 def __radd__(self, other):
25 """Raise an exception attempting right-hand addition."""
26 raise TypeError(INVALID_OPERATION_MESSAGE)
27
28 __rsub__ = __radd__
29
30
31class Interval:
32 """Class that represents a musical interval."""
33 INTERVAL_NAMES = (
34 "unison",
35 "minor 2nd",
36 "major 2nd",
37 "minor 3rd",
38 "major 3rd",
39 "perfect 4th",
40 "diminished 5th",
41 "perfect 5th",
42 "minor 6th",
43 "major 6th",
44 "minor 7th",
45 "major 7th"
46 )
47
48 def __init__(self, value):
49 """Save the normalized initialization value for the interval."""
50 self._value = value % len(self.INTERVAL_NAMES)
51
52 def __str__(self):
53 return self.INTERVAL_NAMES[self._value]
54
55 def __add__(self, other):
56 """Add two intervals or raise an error if adding anything else to an interval."""
57 if isinstance(other, Interval):
58 return Interval(self._value + other._value)
59 raise TypeError(INVALID_OPERATION_MESSAGE)
60
61 def __neg__(self):
62 return Interval(-self._value)
63
64 def __int__(self):
65 return self._value
66
67
68class Chord:
69 """Class that represents a musical chord."""
70 CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone"
71 CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}"
72
73 def __init__(self, *notes):
74 """Leave only unique notes and sort relative to the root."""
75 root = notes[0]
76 unique_and_sorted = sorted(list(set(notes)))
77 root_index = unique_and_sorted.index(root)
78 unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index]
79 if len(unique_and_sorted) <= 1:
80 raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE)
81 self.notes = unique_and_sorted
82
83 def __str__(self):
84 return "-".join(self.notes)
85
86 def __add__(self, other):
87 """Add a tone to a chord."""
88 if isinstance(other, Tone):
89 return Chord(*self.notes, other)
90 if isinstance(other, Chord):
91 return Chord(*self.notes, *other.notes)
92
93 def __sub__(self, other):
94 """Remove a tone from a chord if possible."""
95 if isinstance(other, Tone):
96 if other not in self.notes:
97 raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self))
98 return Chord(*[note for note in self.notes if note != other])
99
100 def is_minor(self):
101 """Determine the age of the chord."""
102 return (Tone(self.notes[0]) + Interval(3)) in self.notes
103
104 def is_major(self):
105 """Determine the military rank of the chord."""
106 return (Tone(self.notes[0]) + Interval(4)) in self.notes
107
108 def is_power_chord(self):
109 """Determine the strenght of the chord."""
110 return not (self.is_minor() or self.is_major())
111
112 def transposed(self, interval):
113 """Return a new chord with different tones, I'm tired of docstrings."""
114 new_notes = [note + interval for note in self.notes]
115 return Chord(*new_notes)
.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.001s
OK
f | 1 | INVALID_OPERATION_MESSAGE = "Invalid operation" | f | 1 | INVALID_OPERATION_MESSAGE = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
18 | """Subtract two tones or an interval from a tone.""" | 18 | """Subtract two tones or an interval from a tone.""" | ||
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
20 | return Interval(self.TONES.index(self) - self.TONES.index(other)) | 20 | return Interval(self.TONES.index(self) - self.TONES.index(other)) | ||
21 | if isinstance(other, Interval): | 21 | if isinstance(other, Interval): | ||
22 | return self.__add__(-other) | 22 | return self.__add__(-other) | ||
23 | 23 | ||||
24 | def __radd__(self, other): | 24 | def __radd__(self, other): | ||
25 | """Raise an exception attempting right-hand addition.""" | 25 | """Raise an exception attempting right-hand addition.""" | ||
26 | raise TypeError(INVALID_OPERATION_MESSAGE) | 26 | raise TypeError(INVALID_OPERATION_MESSAGE) | ||
27 | 27 | ||||
28 | __rsub__ = __radd__ | 28 | __rsub__ = __radd__ | ||
29 | 29 | ||||
30 | 30 | ||||
31 | class Interval: | 31 | class Interval: | ||
32 | """Class that represents a musical interval.""" | 32 | """Class that represents a musical interval.""" | ||
33 | INTERVAL_NAMES = ( | 33 | INTERVAL_NAMES = ( | ||
34 | "unison", | 34 | "unison", | ||
35 | "minor 2nd", | 35 | "minor 2nd", | ||
36 | "major 2nd", | 36 | "major 2nd", | ||
37 | "minor 3rd", | 37 | "minor 3rd", | ||
38 | "major 3rd", | 38 | "major 3rd", | ||
39 | "perfect 4th", | 39 | "perfect 4th", | ||
40 | "diminished 5th", | 40 | "diminished 5th", | ||
41 | "perfect 5th", | 41 | "perfect 5th", | ||
42 | "minor 6th", | 42 | "minor 6th", | ||
43 | "major 6th", | 43 | "major 6th", | ||
44 | "minor 7th", | 44 | "minor 7th", | ||
45 | "major 7th" | 45 | "major 7th" | ||
46 | ) | 46 | ) | ||
47 | 47 | ||||
48 | def __init__(self, value): | 48 | def __init__(self, value): | ||
49 | """Save the normalized initialization value for the interval.""" | 49 | """Save the normalized initialization value for the interval.""" | ||
50 | self._value = value % len(self.INTERVAL_NAMES) | 50 | self._value = value % len(self.INTERVAL_NAMES) | ||
51 | 51 | ||||
52 | def __str__(self): | 52 | def __str__(self): | ||
53 | return self.INTERVAL_NAMES[self._value] | 53 | return self.INTERVAL_NAMES[self._value] | ||
54 | 54 | ||||
55 | def __add__(self, other): | 55 | def __add__(self, other): | ||
56 | """Add two intervals or raise an error if adding anything else to an interval.""" | 56 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
57 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
58 | return Interval(self._value + other._value) | 58 | return Interval(self._value + other._value) | ||
59 | raise TypeError(INVALID_OPERATION_MESSAGE) | 59 | raise TypeError(INVALID_OPERATION_MESSAGE) | ||
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self._value) | 62 | return Interval(-self._value) | ||
63 | 63 | ||||
64 | def __int__(self): | 64 | def __int__(self): | ||
65 | return self._value | 65 | return self._value | ||
66 | 66 | ||||
67 | 67 | ||||
68 | class Chord: | 68 | class Chord: | ||
69 | """Class that represents a musical chord.""" | 69 | """Class that represents a musical chord.""" | ||
70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
72 | 72 | ||||
73 | def __init__(self, *notes): | 73 | def __init__(self, *notes): | ||
74 | """Leave only unique notes and sort relative to the root.""" | 74 | """Leave only unique notes and sort relative to the root.""" | ||
75 | root = notes[0] | 75 | root = notes[0] | ||
76 | unique_and_sorted = sorted(list(set(notes))) | 76 | unique_and_sorted = sorted(list(set(notes))) | ||
77 | root_index = unique_and_sorted.index(root) | 77 | root_index = unique_and_sorted.index(root) | ||
78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
79 | if len(unique_and_sorted) <= 1: | 79 | if len(unique_and_sorted) <= 1: | ||
80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
81 | self.notes = unique_and_sorted | 81 | self.notes = unique_and_sorted | ||
82 | 82 | ||||
83 | def __str__(self): | 83 | def __str__(self): | ||
84 | return "-".join(self.notes) | 84 | return "-".join(self.notes) | ||
85 | 85 | ||||
86 | def __add__(self, other): | 86 | def __add__(self, other): | ||
87 | """Add a tone to a chord.""" | 87 | """Add a tone to a chord.""" | ||
88 | if isinstance(other, Tone): | 88 | if isinstance(other, Tone): | ||
89 | return Chord(*self.notes, other) | 89 | return Chord(*self.notes, other) | ||
90 | if isinstance(other, Chord): | 90 | if isinstance(other, Chord): | ||
t | 91 | return Chord(*self.notes, *other.notes) | t | 91 | return Chord(*self.notes, *other.notes) |
92 | 92 | ||||
93 | def __sub__(self, other): | 93 | def __sub__(self, other): | ||
94 | """Remove a tone from a chord if possible.""" | 94 | """Remove a tone from a chord if possible.""" | ||
95 | if isinstance(other, Tone): | 95 | if isinstance(other, Tone): | ||
96 | if other not in self.notes: | 96 | if other not in self.notes: | ||
97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
98 | return Chord(*[note for note in self.notes if note != other]) | 98 | return Chord(*[note for note in self.notes if note != other]) | ||
99 | 99 | ||||
100 | def is_minor(self): | 100 | def is_minor(self): | ||
101 | """Determine the age of the chord.""" | 101 | """Determine the age of the chord.""" | ||
102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
103 | 103 | ||||
104 | def is_major(self): | 104 | def is_major(self): | ||
105 | """Determine the military rank of the chord.""" | 105 | """Determine the military rank of the chord.""" | ||
106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
107 | 107 | ||||
108 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
109 | """Determine the strenght of the chord.""" | 109 | """Determine the strenght of the chord.""" | ||
110 | return not (self.is_minor() or self.is_major()) | 110 | return not (self.is_minor() or self.is_major()) | ||
111 | 111 | ||||
112 | def transposed(self, interval): | 112 | def transposed(self, interval): | ||
113 | """Return a new chord with different tones, I'm tired of docstrings.""" | 113 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
114 | new_notes = [note + interval for note in self.notes] | 114 | new_notes = [note + interval for note in self.notes] | ||
115 | return Chord(*new_notes) | 115 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | INVALID_OPERATION_MESSAGE = "Invalid operation" | f | 1 | INVALID_OPERATION_MESSAGE = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
18 | """Subtract two tones or an interval from a tone.""" | 18 | """Subtract two tones or an interval from a tone.""" | ||
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
t | 20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) | t | 20 | return Interval(self.TONES.index(self) - self.TONES.index(other)) |
21 | if isinstance(other, Interval): | 21 | if isinstance(other, Interval): | ||
22 | return self.__add__(-other) | 22 | return self.__add__(-other) | ||
23 | 23 | ||||
24 | def __radd__(self, other): | 24 | def __radd__(self, other): | ||
25 | """Raise an exception attempting right-hand addition.""" | 25 | """Raise an exception attempting right-hand addition.""" | ||
26 | raise TypeError(INVALID_OPERATION_MESSAGE) | 26 | raise TypeError(INVALID_OPERATION_MESSAGE) | ||
27 | 27 | ||||
28 | __rsub__ = __radd__ | 28 | __rsub__ = __radd__ | ||
29 | 29 | ||||
30 | 30 | ||||
31 | class Interval: | 31 | class Interval: | ||
32 | """Class that represents a musical interval.""" | 32 | """Class that represents a musical interval.""" | ||
33 | INTERVAL_NAMES = ( | 33 | INTERVAL_NAMES = ( | ||
34 | "unison", | 34 | "unison", | ||
35 | "minor 2nd", | 35 | "minor 2nd", | ||
36 | "major 2nd", | 36 | "major 2nd", | ||
37 | "minor 3rd", | 37 | "minor 3rd", | ||
38 | "major 3rd", | 38 | "major 3rd", | ||
39 | "perfect 4th", | 39 | "perfect 4th", | ||
40 | "diminished 5th", | 40 | "diminished 5th", | ||
41 | "perfect 5th", | 41 | "perfect 5th", | ||
42 | "minor 6th", | 42 | "minor 6th", | ||
43 | "major 6th", | 43 | "major 6th", | ||
44 | "minor 7th", | 44 | "minor 7th", | ||
45 | "major 7th" | 45 | "major 7th" | ||
46 | ) | 46 | ) | ||
47 | 47 | ||||
48 | def __init__(self, value): | 48 | def __init__(self, value): | ||
49 | """Save the normalized initialization value for the interval.""" | 49 | """Save the normalized initialization value for the interval.""" | ||
50 | self._value = value % len(self.INTERVAL_NAMES) | 50 | self._value = value % len(self.INTERVAL_NAMES) | ||
51 | 51 | ||||
52 | def __str__(self): | 52 | def __str__(self): | ||
53 | return self.INTERVAL_NAMES[self._value] | 53 | return self.INTERVAL_NAMES[self._value] | ||
54 | 54 | ||||
55 | def __add__(self, other): | 55 | def __add__(self, other): | ||
56 | """Add two intervals or raise an error if adding anything else to an interval.""" | 56 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
57 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
58 | return Interval(self._value + other._value) | 58 | return Interval(self._value + other._value) | ||
59 | raise TypeError(INVALID_OPERATION_MESSAGE) | 59 | raise TypeError(INVALID_OPERATION_MESSAGE) | ||
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self._value) | 62 | return Interval(-self._value) | ||
63 | 63 | ||||
64 | def __int__(self): | 64 | def __int__(self): | ||
65 | return self._value | 65 | return self._value | ||
66 | 66 | ||||
67 | 67 | ||||
68 | class Chord: | 68 | class Chord: | ||
69 | """Class that represents a musical chord.""" | 69 | """Class that represents a musical chord.""" | ||
70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
72 | 72 | ||||
73 | def __init__(self, *notes): | 73 | def __init__(self, *notes): | ||
74 | """Leave only unique notes and sort relative to the root.""" | 74 | """Leave only unique notes and sort relative to the root.""" | ||
75 | root = notes[0] | 75 | root = notes[0] | ||
76 | unique_and_sorted = sorted(list(set(notes))) | 76 | unique_and_sorted = sorted(list(set(notes))) | ||
77 | root_index = unique_and_sorted.index(root) | 77 | root_index = unique_and_sorted.index(root) | ||
78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
79 | if len(unique_and_sorted) <= 1: | 79 | if len(unique_and_sorted) <= 1: | ||
80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
81 | self.notes = unique_and_sorted | 81 | self.notes = unique_and_sorted | ||
82 | 82 | ||||
83 | def __str__(self): | 83 | def __str__(self): | ||
84 | return "-".join(self.notes) | 84 | return "-".join(self.notes) | ||
85 | 85 | ||||
86 | def __add__(self, other): | 86 | def __add__(self, other): | ||
87 | """Add a tone to a chord.""" | 87 | """Add a tone to a chord.""" | ||
88 | if isinstance(other, Tone): | 88 | if isinstance(other, Tone): | ||
89 | return Chord(*self.notes, other) | 89 | return Chord(*self.notes, other) | ||
90 | if isinstance(other, Chord): | 90 | if isinstance(other, Chord): | ||
91 | return Chord(*self.notes, *other.notes) | 91 | return Chord(*self.notes, *other.notes) | ||
92 | 92 | ||||
93 | def __sub__(self, other): | 93 | def __sub__(self, other): | ||
94 | """Remove a tone from a chord if possible.""" | 94 | """Remove a tone from a chord if possible.""" | ||
95 | if isinstance(other, Tone): | 95 | if isinstance(other, Tone): | ||
96 | if other not in self.notes: | 96 | if other not in self.notes: | ||
97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
98 | return Chord(*[note for note in self.notes if note != other]) | 98 | return Chord(*[note for note in self.notes if note != other]) | ||
99 | 99 | ||||
100 | def is_minor(self): | 100 | def is_minor(self): | ||
101 | """Determine the age of the chord.""" | 101 | """Determine the age of the chord.""" | ||
102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
103 | 103 | ||||
104 | def is_major(self): | 104 | def is_major(self): | ||
105 | """Determine the military rank of the chord.""" | 105 | """Determine the military rank of the chord.""" | ||
106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
107 | 107 | ||||
108 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
109 | """Determine the strenght of the chord.""" | 109 | """Determine the strenght of the chord.""" | ||
110 | return not (self.is_minor() or self.is_major()) | 110 | return not (self.is_minor() or self.is_major()) | ||
111 | 111 | ||||
112 | def transposed(self, interval): | 112 | def transposed(self, interval): | ||
113 | """Return a new chord with different tones, I'm tired of docstrings.""" | 113 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
114 | new_notes = [note + interval for note in self.notes] | 114 | new_notes = [note + interval for note in self.notes] | ||
115 | return Chord(*new_notes) | 115 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
n | 1 | INVALID_OPERATION = "Invalid operation" | n | 1 | INVALID_OPERATION_MESSAGE = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
18 | """Subtract two tones or an interval from a tone.""" | 18 | """Subtract two tones or an interval from a tone.""" | ||
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) | 20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) | ||
21 | if isinstance(other, Interval): | 21 | if isinstance(other, Interval): | ||
22 | return self.__add__(-other) | 22 | return self.__add__(-other) | ||
23 | 23 | ||||
24 | def __radd__(self, other): | 24 | def __radd__(self, other): | ||
25 | """Raise an exception attempting right-hand addition.""" | 25 | """Raise an exception attempting right-hand addition.""" | ||
n | 26 | raise TypeError(INVALID_OPERATION) | n | 26 | raise TypeError(INVALID_OPERATION_MESSAGE) |
27 | 27 | ||||
28 | __rsub__ = __radd__ | 28 | __rsub__ = __radd__ | ||
29 | 29 | ||||
30 | 30 | ||||
31 | class Interval: | 31 | class Interval: | ||
32 | """Class that represents a musical interval.""" | 32 | """Class that represents a musical interval.""" | ||
33 | INTERVAL_NAMES = ( | 33 | INTERVAL_NAMES = ( | ||
34 | "unison", | 34 | "unison", | ||
35 | "minor 2nd", | 35 | "minor 2nd", | ||
36 | "major 2nd", | 36 | "major 2nd", | ||
37 | "minor 3rd", | 37 | "minor 3rd", | ||
38 | "major 3rd", | 38 | "major 3rd", | ||
39 | "perfect 4th", | 39 | "perfect 4th", | ||
40 | "diminished 5th", | 40 | "diminished 5th", | ||
41 | "perfect 5th", | 41 | "perfect 5th", | ||
42 | "minor 6th", | 42 | "minor 6th", | ||
43 | "major 6th", | 43 | "major 6th", | ||
44 | "minor 7th", | 44 | "minor 7th", | ||
45 | "major 7th" | 45 | "major 7th" | ||
46 | ) | 46 | ) | ||
47 | 47 | ||||
48 | def __init__(self, value): | 48 | def __init__(self, value): | ||
49 | """Save the normalized initialization value for the interval.""" | 49 | """Save the normalized initialization value for the interval.""" | ||
50 | self._value = value % len(self.INTERVAL_NAMES) | 50 | self._value = value % len(self.INTERVAL_NAMES) | ||
51 | 51 | ||||
52 | def __str__(self): | 52 | def __str__(self): | ||
53 | return self.INTERVAL_NAMES[self._value] | 53 | return self.INTERVAL_NAMES[self._value] | ||
54 | 54 | ||||
55 | def __add__(self, other): | 55 | def __add__(self, other): | ||
56 | """Add two intervals or raise an error if adding anything else to an interval.""" | 56 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
57 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
58 | return Interval(self._value + other._value) | 58 | return Interval(self._value + other._value) | ||
t | 59 | raise TypeError(INVALID_OPERATION) | t | 59 | raise TypeError(INVALID_OPERATION_MESSAGE) |
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self._value) | 62 | return Interval(-self._value) | ||
63 | 63 | ||||
64 | def __int__(self): | 64 | def __int__(self): | ||
65 | return self._value | 65 | return self._value | ||
66 | 66 | ||||
67 | 67 | ||||
68 | class Chord: | 68 | class Chord: | ||
69 | """Class that represents a musical chord.""" | 69 | """Class that represents a musical chord.""" | ||
70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
72 | 72 | ||||
73 | def __init__(self, *notes): | 73 | def __init__(self, *notes): | ||
74 | """Leave only unique notes and sort relative to the root.""" | 74 | """Leave only unique notes and sort relative to the root.""" | ||
75 | root = notes[0] | 75 | root = notes[0] | ||
76 | unique_and_sorted = sorted(list(set(notes))) | 76 | unique_and_sorted = sorted(list(set(notes))) | ||
77 | root_index = unique_and_sorted.index(root) | 77 | root_index = unique_and_sorted.index(root) | ||
78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
79 | if len(unique_and_sorted) <= 1: | 79 | if len(unique_and_sorted) <= 1: | ||
80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
81 | self.notes = unique_and_sorted | 81 | self.notes = unique_and_sorted | ||
82 | 82 | ||||
83 | def __str__(self): | 83 | def __str__(self): | ||
84 | return "-".join(self.notes) | 84 | return "-".join(self.notes) | ||
85 | 85 | ||||
86 | def __add__(self, other): | 86 | def __add__(self, other): | ||
87 | """Add a tone to a chord.""" | 87 | """Add a tone to a chord.""" | ||
88 | if isinstance(other, Tone): | 88 | if isinstance(other, Tone): | ||
89 | return Chord(*self.notes, other) | 89 | return Chord(*self.notes, other) | ||
90 | if isinstance(other, Chord): | 90 | if isinstance(other, Chord): | ||
91 | return Chord(*self.notes, *other.notes) | 91 | return Chord(*self.notes, *other.notes) | ||
92 | 92 | ||||
93 | def __sub__(self, other): | 93 | def __sub__(self, other): | ||
94 | """Remove a tone from a chord if possible.""" | 94 | """Remove a tone from a chord if possible.""" | ||
95 | if isinstance(other, Tone): | 95 | if isinstance(other, Tone): | ||
96 | if other not in self.notes: | 96 | if other not in self.notes: | ||
97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
98 | return Chord(*[note for note in self.notes if note != other]) | 98 | return Chord(*[note for note in self.notes if note != other]) | ||
99 | 99 | ||||
100 | def is_minor(self): | 100 | def is_minor(self): | ||
101 | """Determine the age of the chord.""" | 101 | """Determine the age of the chord.""" | ||
102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
103 | 103 | ||||
104 | def is_major(self): | 104 | def is_major(self): | ||
105 | """Determine the military rank of the chord.""" | 105 | """Determine the military rank of the chord.""" | ||
106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
107 | 107 | ||||
108 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
109 | """Determine the strenght of the chord.""" | 109 | """Determine the strenght of the chord.""" | ||
110 | return not (self.is_minor() or self.is_major()) | 110 | return not (self.is_minor() or self.is_major()) | ||
111 | 111 | ||||
112 | def transposed(self, interval): | 112 | def transposed(self, interval): | ||
113 | """Return a new chord with different tones, I'm tired of docstrings.""" | 113 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
114 | new_notes = [note + interval for note in self.notes] | 114 | new_notes = [note + interval for note in self.notes] | ||
115 | return Chord(*new_notes) | 115 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | INVALID_OPERATION = "Invalid operation" | f | 1 | INVALID_OPERATION = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
n | 18 | """Subtract an interval from a tone.""" | n | 18 | """Subtract two tones or an interval from a tone.""" |
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) | 20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) | ||
21 | if isinstance(other, Interval): | 21 | if isinstance(other, Interval): | ||
22 | return self.__add__(-other) | 22 | return self.__add__(-other) | ||
23 | 23 | ||||
24 | def __radd__(self, other): | 24 | def __radd__(self, other): | ||
25 | """Raise an exception attempting right-hand addition.""" | 25 | """Raise an exception attempting right-hand addition.""" | ||
26 | raise TypeError(INVALID_OPERATION) | 26 | raise TypeError(INVALID_OPERATION) | ||
27 | 27 | ||||
28 | __rsub__ = __radd__ | 28 | __rsub__ = __radd__ | ||
29 | 29 | ||||
30 | 30 | ||||
31 | class Interval: | 31 | class Interval: | ||
32 | """Class that represents a musical interval.""" | 32 | """Class that represents a musical interval.""" | ||
33 | INTERVAL_NAMES = ( | 33 | INTERVAL_NAMES = ( | ||
34 | "unison", | 34 | "unison", | ||
35 | "minor 2nd", | 35 | "minor 2nd", | ||
36 | "major 2nd", | 36 | "major 2nd", | ||
37 | "minor 3rd", | 37 | "minor 3rd", | ||
38 | "major 3rd", | 38 | "major 3rd", | ||
39 | "perfect 4th", | 39 | "perfect 4th", | ||
40 | "diminished 5th", | 40 | "diminished 5th", | ||
41 | "perfect 5th", | 41 | "perfect 5th", | ||
42 | "minor 6th", | 42 | "minor 6th", | ||
43 | "major 6th", | 43 | "major 6th", | ||
44 | "minor 7th", | 44 | "minor 7th", | ||
45 | "major 7th" | 45 | "major 7th" | ||
46 | ) | 46 | ) | ||
47 | 47 | ||||
48 | def __init__(self, value): | 48 | def __init__(self, value): | ||
49 | """Save the normalized initialization value for the interval.""" | 49 | """Save the normalized initialization value for the interval.""" | ||
50 | self._value = value % len(self.INTERVAL_NAMES) | 50 | self._value = value % len(self.INTERVAL_NAMES) | ||
51 | 51 | ||||
52 | def __str__(self): | 52 | def __str__(self): | ||
53 | return self.INTERVAL_NAMES[self._value] | 53 | return self.INTERVAL_NAMES[self._value] | ||
54 | 54 | ||||
55 | def __add__(self, other): | 55 | def __add__(self, other): | ||
56 | """Add two intervals or raise an error if adding anything else to an interval.""" | 56 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
57 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
58 | return Interval(self._value + other._value) | 58 | return Interval(self._value + other._value) | ||
59 | raise TypeError(INVALID_OPERATION) | 59 | raise TypeError(INVALID_OPERATION) | ||
60 | 60 | ||||
61 | def __neg__(self): | 61 | def __neg__(self): | ||
62 | return Interval(-self._value) | 62 | return Interval(-self._value) | ||
63 | 63 | ||||
64 | def __int__(self): | 64 | def __int__(self): | ||
65 | return self._value | 65 | return self._value | ||
66 | 66 | ||||
67 | 67 | ||||
68 | class Chord: | 68 | class Chord: | ||
69 | """Class that represents a musical chord.""" | 69 | """Class that represents a musical chord.""" | ||
70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
72 | 72 | ||||
73 | def __init__(self, *notes): | 73 | def __init__(self, *notes): | ||
74 | """Leave only unique notes and sort relative to the root.""" | 74 | """Leave only unique notes and sort relative to the root.""" | ||
75 | root = notes[0] | 75 | root = notes[0] | ||
76 | unique_and_sorted = sorted(list(set(notes))) | 76 | unique_and_sorted = sorted(list(set(notes))) | ||
77 | root_index = unique_and_sorted.index(root) | 77 | root_index = unique_and_sorted.index(root) | ||
78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
79 | if len(unique_and_sorted) <= 1: | 79 | if len(unique_and_sorted) <= 1: | ||
80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
81 | self.notes = unique_and_sorted | 81 | self.notes = unique_and_sorted | ||
82 | 82 | ||||
83 | def __str__(self): | 83 | def __str__(self): | ||
84 | return "-".join(self.notes) | 84 | return "-".join(self.notes) | ||
85 | 85 | ||||
86 | def __add__(self, other): | 86 | def __add__(self, other): | ||
87 | """Add a tone to a chord.""" | 87 | """Add a tone to a chord.""" | ||
88 | if isinstance(other, Tone): | 88 | if isinstance(other, Tone): | ||
89 | return Chord(*self.notes, other) | 89 | return Chord(*self.notes, other) | ||
90 | if isinstance(other, Chord): | 90 | if isinstance(other, Chord): | ||
91 | return Chord(*self.notes, *other.notes) | 91 | return Chord(*self.notes, *other.notes) | ||
92 | 92 | ||||
93 | def __sub__(self, other): | 93 | def __sub__(self, other): | ||
94 | """Remove a tone from a chord if possible.""" | 94 | """Remove a tone from a chord if possible.""" | ||
95 | if isinstance(other, Tone): | 95 | if isinstance(other, Tone): | ||
96 | if other not in self.notes: | 96 | if other not in self.notes: | ||
97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
98 | return Chord(*[note for note in self.notes if note != other]) | 98 | return Chord(*[note for note in self.notes if note != other]) | ||
99 | 99 | ||||
100 | def is_minor(self): | 100 | def is_minor(self): | ||
101 | """Determine the age of the chord.""" | 101 | """Determine the age of the chord.""" | ||
102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
103 | 103 | ||||
104 | def is_major(self): | 104 | def is_major(self): | ||
105 | """Determine the military rank of the chord.""" | 105 | """Determine the military rank of the chord.""" | ||
106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
107 | 107 | ||||
108 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
109 | """Determine the strenght of the chord.""" | 109 | """Determine the strenght of the chord.""" | ||
110 | return not (self.is_minor() or self.is_major()) | 110 | return not (self.is_minor() or self.is_major()) | ||
111 | 111 | ||||
112 | def transposed(self, interval): | 112 | def transposed(self, interval): | ||
113 | """Return a new chord with different tones, I'm tired of docstrings.""" | 113 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
t | 114 | new_notes = [note+interval for note in self.notes] | t | 114 | new_notes = [note + interval for note in self.notes] |
115 | return Chord(*new_notes) | 115 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | INVALID_OPERATION = "Invalid operation" | f | 1 | INVALID_OPERATION = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
18 | """Subtract an interval from a tone.""" | 18 | """Subtract an interval from a tone.""" | ||
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
n | 20 | raise TypeError(INVALID_OPERATION) | n | 20 | return Interval(self.TONES.index(self)-self.TONES.index(other)) |
21 | if isinstance(other, Interval): | ||||
21 | return self.__add__(-other) | 22 | return self.__add__(-other) | ||
22 | 23 | ||||
23 | def __radd__(self, other): | 24 | def __radd__(self, other): | ||
24 | """Raise an exception attempting right-hand addition.""" | 25 | """Raise an exception attempting right-hand addition.""" | ||
25 | raise TypeError(INVALID_OPERATION) | 26 | raise TypeError(INVALID_OPERATION) | ||
26 | 27 | ||||
27 | __rsub__ = __radd__ | 28 | __rsub__ = __radd__ | ||
28 | 29 | ||||
29 | 30 | ||||
30 | class Interval: | 31 | class Interval: | ||
31 | """Class that represents a musical interval.""" | 32 | """Class that represents a musical interval.""" | ||
32 | INTERVAL_NAMES = ( | 33 | INTERVAL_NAMES = ( | ||
33 | "unison", | 34 | "unison", | ||
34 | "minor 2nd", | 35 | "minor 2nd", | ||
35 | "major 2nd", | 36 | "major 2nd", | ||
36 | "minor 3rd", | 37 | "minor 3rd", | ||
37 | "major 3rd", | 38 | "major 3rd", | ||
38 | "perfect 4th", | 39 | "perfect 4th", | ||
39 | "diminished 5th", | 40 | "diminished 5th", | ||
40 | "perfect 5th", | 41 | "perfect 5th", | ||
41 | "minor 6th", | 42 | "minor 6th", | ||
42 | "major 6th", | 43 | "major 6th", | ||
43 | "minor 7th", | 44 | "minor 7th", | ||
44 | "major 7th" | 45 | "major 7th" | ||
45 | ) | 46 | ) | ||
46 | 47 | ||||
47 | def __init__(self, value): | 48 | def __init__(self, value): | ||
48 | """Save the normalized initialization value for the interval.""" | 49 | """Save the normalized initialization value for the interval.""" | ||
49 | self._value = value % len(self.INTERVAL_NAMES) | 50 | self._value = value % len(self.INTERVAL_NAMES) | ||
50 | 51 | ||||
51 | def __str__(self): | 52 | def __str__(self): | ||
52 | return self.INTERVAL_NAMES[self._value] | 53 | return self.INTERVAL_NAMES[self._value] | ||
53 | 54 | ||||
54 | def __add__(self, other): | 55 | def __add__(self, other): | ||
55 | """Add two intervals or raise an error if adding anything else to an interval.""" | 56 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
56 | if isinstance(other, Interval): | 57 | if isinstance(other, Interval): | ||
57 | return Interval(self._value + other._value) | 58 | return Interval(self._value + other._value) | ||
58 | raise TypeError(INVALID_OPERATION) | 59 | raise TypeError(INVALID_OPERATION) | ||
59 | 60 | ||||
60 | def __neg__(self): | 61 | def __neg__(self): | ||
61 | return Interval(-self._value) | 62 | return Interval(-self._value) | ||
62 | 63 | ||||
63 | def __int__(self): | 64 | def __int__(self): | ||
64 | return self._value | 65 | return self._value | ||
65 | 66 | ||||
66 | 67 | ||||
67 | class Chord: | 68 | class Chord: | ||
68 | """Class that represents a musical chord.""" | 69 | """Class that represents a musical chord.""" | ||
69 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
70 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
71 | 72 | ||||
72 | def __init__(self, *notes): | 73 | def __init__(self, *notes): | ||
73 | """Leave only unique notes and sort relative to the root.""" | 74 | """Leave only unique notes and sort relative to the root.""" | ||
74 | root = notes[0] | 75 | root = notes[0] | ||
75 | unique_and_sorted = sorted(list(set(notes))) | 76 | unique_and_sorted = sorted(list(set(notes))) | ||
76 | root_index = unique_and_sorted.index(root) | 77 | root_index = unique_and_sorted.index(root) | ||
77 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
78 | if len(unique_and_sorted) <= 1: | 79 | if len(unique_and_sorted) <= 1: | ||
79 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
80 | self.notes = unique_and_sorted | 81 | self.notes = unique_and_sorted | ||
81 | 82 | ||||
82 | def __str__(self): | 83 | def __str__(self): | ||
83 | return "-".join(self.notes) | 84 | return "-".join(self.notes) | ||
84 | 85 | ||||
85 | def __add__(self, other): | 86 | def __add__(self, other): | ||
86 | """Add a tone to a chord.""" | 87 | """Add a tone to a chord.""" | ||
87 | if isinstance(other, Tone): | 88 | if isinstance(other, Tone): | ||
88 | return Chord(*self.notes, other) | 89 | return Chord(*self.notes, other) | ||
t | t | 90 | if isinstance(other, Chord): | ||
91 | return Chord(*self.notes, *other.notes) | ||||
89 | 92 | ||||
90 | def __sub__(self, other): | 93 | def __sub__(self, other): | ||
91 | """Remove a tone from a chord if possible.""" | 94 | """Remove a tone from a chord if possible.""" | ||
92 | if isinstance(other, Tone): | 95 | if isinstance(other, Tone): | ||
93 | if other not in self.notes: | 96 | if other not in self.notes: | ||
94 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 97 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
95 | return Chord(*[note for note in self.notes if note != other]) | 98 | return Chord(*[note for note in self.notes if note != other]) | ||
96 | 99 | ||||
97 | def is_minor(self): | 100 | def is_minor(self): | ||
98 | """Determine the age of the chord.""" | 101 | """Determine the age of the chord.""" | ||
99 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 102 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
100 | 103 | ||||
101 | def is_major(self): | 104 | def is_major(self): | ||
102 | """Determine the military rank of the chord.""" | 105 | """Determine the military rank of the chord.""" | ||
103 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 106 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
104 | 107 | ||||
105 | def is_power_chord(self): | 108 | def is_power_chord(self): | ||
106 | """Determine the strenght of the chord.""" | 109 | """Determine the strenght of the chord.""" | ||
107 | return not (self.is_minor() or self.is_major()) | 110 | return not (self.is_minor() or self.is_major()) | ||
108 | 111 | ||||
109 | def transposed(self, interval): | 112 | def transposed(self, interval): | ||
110 | """Return a new chord with different tones, I'm tired of docstrings.""" | 113 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
111 | new_notes = [note+interval for note in self.notes] | 114 | new_notes = [note+interval for note in self.notes] | ||
112 | return Chord(*new_notes) | 115 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|
f | 1 | INVALID_OPERATION = "Invalid operation" | f | 1 | INVALID_OPERATION = "Invalid operation" |
2 | 2 | ||||
3 | 3 | ||||
4 | class Tone(str): | 4 | class Tone(str): | ||
5 | """Class that represents a musical tone.""" | 5 | """Class that represents a musical tone.""" | ||
6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 6 | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | ||
7 | 7 | ||||
8 | def __add__(self, other): | 8 | def __add__(self, other): | ||
9 | """Add two tones or an interval to a tone.""" | 9 | """Add two tones or an interval to a tone.""" | ||
10 | if isinstance(other, Tone): | 10 | if isinstance(other, Tone): | ||
11 | return Chord(self, other) | 11 | return Chord(self, other) | ||
12 | if isinstance(other, Interval): | 12 | if isinstance(other, Interval): | ||
13 | current = self.TONES.index(self) | 13 | current = self.TONES.index(self) | ||
14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | 14 | new = self.TONES[(current + int(other)) % len(self.TONES)] | ||
15 | return Tone(new) | 15 | return Tone(new) | ||
16 | 16 | ||||
17 | def __sub__(self, other): | 17 | def __sub__(self, other): | ||
18 | """Subtract an interval from a tone.""" | 18 | """Subtract an interval from a tone.""" | ||
19 | if isinstance(other, Tone): | 19 | if isinstance(other, Tone): | ||
20 | raise TypeError(INVALID_OPERATION) | 20 | raise TypeError(INVALID_OPERATION) | ||
21 | return self.__add__(-other) | 21 | return self.__add__(-other) | ||
22 | 22 | ||||
23 | def __radd__(self, other): | 23 | def __radd__(self, other): | ||
24 | """Raise an exception attempting right-hand addition.""" | 24 | """Raise an exception attempting right-hand addition.""" | ||
25 | raise TypeError(INVALID_OPERATION) | 25 | raise TypeError(INVALID_OPERATION) | ||
26 | 26 | ||||
27 | __rsub__ = __radd__ | 27 | __rsub__ = __radd__ | ||
28 | 28 | ||||
29 | 29 | ||||
30 | class Interval: | 30 | class Interval: | ||
31 | """Class that represents a musical interval.""" | 31 | """Class that represents a musical interval.""" | ||
n | 32 | INTERVAL_NAMES = [ | n | 32 | INTERVAL_NAMES = ( |
33 | "unison", | 33 | "unison", | ||
34 | "minor 2nd", | 34 | "minor 2nd", | ||
35 | "major 2nd", | 35 | "major 2nd", | ||
36 | "minor 3rd", | 36 | "minor 3rd", | ||
37 | "major 3rd", | 37 | "major 3rd", | ||
38 | "perfect 4th", | 38 | "perfect 4th", | ||
39 | "diminished 5th", | 39 | "diminished 5th", | ||
40 | "perfect 5th", | 40 | "perfect 5th", | ||
41 | "minor 6th", | 41 | "minor 6th", | ||
42 | "major 6th", | 42 | "major 6th", | ||
43 | "minor 7th", | 43 | "minor 7th", | ||
44 | "major 7th" | 44 | "major 7th" | ||
n | 45 | ] | n | 45 | ) |
46 | TOTAL_INTERVALS = len(INTERVAL_NAMES) | ||||
47 | 46 | ||||
48 | def __init__(self, value): | 47 | def __init__(self, value): | ||
49 | """Save the normalized initialization value for the interval.""" | 48 | """Save the normalized initialization value for the interval.""" | ||
t | 50 | self._value = value % self.TOTAL_INTERVALS | t | 49 | self._value = value % len(self.INTERVAL_NAMES) |
51 | 50 | ||||
52 | def __str__(self): | 51 | def __str__(self): | ||
53 | return self.INTERVAL_NAMES[self._value] | 52 | return self.INTERVAL_NAMES[self._value] | ||
54 | 53 | ||||
55 | def __add__(self, other): | 54 | def __add__(self, other): | ||
56 | """Add two intervals or raise an error if adding anything else to an interval.""" | 55 | """Add two intervals or raise an error if adding anything else to an interval.""" | ||
57 | if isinstance(other, Interval): | 56 | if isinstance(other, Interval): | ||
58 | return Interval(self._value + other._value) | 57 | return Interval(self._value + other._value) | ||
59 | raise TypeError(INVALID_OPERATION) | 58 | raise TypeError(INVALID_OPERATION) | ||
60 | 59 | ||||
61 | def __neg__(self): | 60 | def __neg__(self): | ||
62 | return Interval(-self._value) | 61 | return Interval(-self._value) | ||
63 | 62 | ||||
64 | def __int__(self): | 63 | def __int__(self): | ||
65 | return self._value | 64 | return self._value | ||
66 | 65 | ||||
67 | 66 | ||||
68 | class Chord: | 67 | class Chord: | ||
69 | """Class that represents a musical chord.""" | 68 | """Class that represents a musical chord.""" | ||
70 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | 69 | CANNOT_CHORD_CHORD_MESSAGE = "Cannot have a chord made of only 1 unique tone" | ||
71 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | 70 | CANNOT_REMOVE_TONE_MESSAGE = "Cannot remove tone {0} from chord {1}" | ||
72 | 71 | ||||
73 | def __init__(self, *notes): | 72 | def __init__(self, *notes): | ||
74 | """Leave only unique notes and sort relative to the root.""" | 73 | """Leave only unique notes and sort relative to the root.""" | ||
75 | root = notes[0] | 74 | root = notes[0] | ||
76 | unique_and_sorted = sorted(list(set(notes))) | 75 | unique_and_sorted = sorted(list(set(notes))) | ||
77 | root_index = unique_and_sorted.index(root) | 76 | root_index = unique_and_sorted.index(root) | ||
78 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | 77 | unique_and_sorted = unique_and_sorted[root_index:] + unique_and_sorted[:root_index] | ||
79 | if len(unique_and_sorted) <= 1: | 78 | if len(unique_and_sorted) <= 1: | ||
80 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | 79 | raise TypeError(self.CANNOT_CHORD_CHORD_MESSAGE) | ||
81 | self.notes = unique_and_sorted | 80 | self.notes = unique_and_sorted | ||
82 | 81 | ||||
83 | def __str__(self): | 82 | def __str__(self): | ||
84 | return "-".join(self.notes) | 83 | return "-".join(self.notes) | ||
85 | 84 | ||||
86 | def __add__(self, other): | 85 | def __add__(self, other): | ||
87 | """Add a tone to a chord.""" | 86 | """Add a tone to a chord.""" | ||
88 | if isinstance(other, Tone): | 87 | if isinstance(other, Tone): | ||
89 | return Chord(*self.notes, other) | 88 | return Chord(*self.notes, other) | ||
90 | 89 | ||||
91 | def __sub__(self, other): | 90 | def __sub__(self, other): | ||
92 | """Remove a tone from a chord if possible.""" | 91 | """Remove a tone from a chord if possible.""" | ||
93 | if isinstance(other, Tone): | 92 | if isinstance(other, Tone): | ||
94 | if other not in self.notes: | 93 | if other not in self.notes: | ||
95 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | 94 | raise TypeError(self.CANNOT_REMOVE_TONE_MESSAGE.format(other, self)) | ||
96 | return Chord(*[note for note in self.notes if note != other]) | 95 | return Chord(*[note for note in self.notes if note != other]) | ||
97 | 96 | ||||
98 | def is_minor(self): | 97 | def is_minor(self): | ||
99 | """Determine the age of the chord.""" | 98 | """Determine the age of the chord.""" | ||
100 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | 99 | return (Tone(self.notes[0]) + Interval(3)) in self.notes | ||
101 | 100 | ||||
102 | def is_major(self): | 101 | def is_major(self): | ||
103 | """Determine the military rank of the chord.""" | 102 | """Determine the military rank of the chord.""" | ||
104 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | 103 | return (Tone(self.notes[0]) + Interval(4)) in self.notes | ||
105 | 104 | ||||
106 | def is_power_chord(self): | 105 | def is_power_chord(self): | ||
107 | """Determine the strenght of the chord.""" | 106 | """Determine the strenght of the chord.""" | ||
108 | return not (self.is_minor() or self.is_major()) | 107 | return not (self.is_minor() or self.is_major()) | ||
109 | 108 | ||||
110 | def transposed(self, interval): | 109 | def transposed(self, interval): | ||
111 | """Return a new chord with different tones, I'm tired of docstrings.""" | 110 | """Return a new chord with different tones, I'm tired of docstrings.""" | ||
112 | new_notes = [note+interval for note in self.notes] | 111 | new_notes = [note+interval for note in self.notes] | ||
113 | return Chord(*new_notes) | 112 | return Chord(*new_notes) |
Legends | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
|