Домашни > Pitches love the D > Решения > Решението на Георги Балтиев

Резултати
9 точки от тестове
0 точки от учител

9 точки общо

32 успешни теста
5 неуспешни теста
Код

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

....FFF....................F.......F.
======================================================================
FAIL: test_is_major (test.TestBasicChordFunctionality.test_is_major)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 107, in test_is_major
self.assertTrue(a_major_chord.is_major())
AssertionError: False is not true

======================================================================
FAIL: test_is_minor (test.TestBasicChordFunctionality.test_is_minor)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 90, in test_is_minor
self.assertTrue(a_minor_chord.is_minor())
AssertionError: False is not true

======================================================================
FAIL: test_is_power_chord (test.TestBasicChordFunctionality.test_is_power_chord)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 116, in test_is_power_chord
self.assertFalse(a_minor_chord.is_power_chord())
AssertionError: True is not false

======================================================================
FAIL: test_subtract_interval_from_tone_left_side_error (test.TestOperations.test_subtract_interval_from_tone_left_side_error)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 235, in test_subtract_interval_from_tone_left_side_error
self.assertEqual(str(err.exception), INVALID_OPERATION)
AssertionError: "unsupported operand type(s) for -: 'Interval' and 'Tone'" != 'Invalid operation'
- unsupported operand type(s) for -: 'Interval' and 'Tone'
+ Invalid operation

======================================================================
FAIL: test_tone_subtraction_inverse (test.TestOperations.test_tone_subtraction_inverse)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/tmp/test.py", line 178, in test_tone_subtraction_inverse
self.assertEqual(str(perfect_4th), "perfect 4th")
AssertionError: 'perfect 5th' != 'perfect 4th'
- perfect 5th
? ^
+ perfect 4th
? ^

----------------------------------------------------------------------
Ran 37 tests in 0.003s

FAILED (failures=5)

Дискусия
Георги Балтиев
06.11.2024 12:11

добавих вече функциите които ми липсваха
История

t1 t
2import unittest
31
4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")2ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",3INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")4                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
75
86
9class Tone:7class Tone:
108
11    def __init__(self, tone):9    def __init__(self, tone):
12        if tone not in ACCEPTED_TONES:10        if tone not in ACCEPTED_TONES:
13            raise TypeError("Tone is not accepted")11            raise TypeError("Tone is not accepted")
14        self.tone = tone12        self.tone = tone
1513
16    def __str__(self):14    def __str__(self):
17        return self.tone15        return self.tone
1816
19    def __eq__(self, other):17    def __eq__(self, other):
20        return self.tone == other.tone18        return self.tone == other.tone
2119
22    def __hash__(self):20    def __hash__(self):
23        return hash(self.tone)21        return hash(self.tone)
2422
25    def __add__(self, other):23    def __add__(self, other):
26        if isinstance(other, Tone):24        if isinstance(other, Tone):
27            return Chord(self, other)25            return Chord(self, other)
28        26        
29        if isinstance(other, Interval):27        if isinstance(other, Interval):
30            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])28            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])
3129
32        raise TypeError("Invalid operation")30        raise TypeError("Invalid operation")
3331
34    def __sub__(self, other):32    def __sub__(self, other):
35        if isinstance(other, Tone):33        if isinstance(other, Tone):
36            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))34            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
3735
38        if isinstance(other, Interval):36        if isinstance(other, Interval):
39            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])37            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
4038
41        raise TypeError("Invalid operation")39        raise TypeError("Invalid operation")
4240
4341
44class Interval:42class Interval:
4543
46    def __init__(self, interval_length):44    def __init__(self, interval_length):
47        self.length = interval_length % len(INTERVAL_VALUES)45        self.length = interval_length % len(INTERVAL_VALUES)
4846
49    def __str__(self):47    def __str__(self):
50        return INTERVAL_VALUES[self.length]48        return INTERVAL_VALUES[self.length]
5149
52    def __add__(self, other):50    def __add__(self, other):
5351
54        if isinstance(other, Interval):52        if isinstance(other, Interval):
55            return Interval(self.length + other.length)53            return Interval(self.length + other.length)
5654
57        raise TypeError("Invalid operation")55        raise TypeError("Invalid operation")
5856
59    def __neg__(self):57    def __neg__(self):
60        return Interval(-self.length)58        return Interval(-self.length)
6159
6260
63class Chord:61class Chord:
6462
65    def __init__(self, root_tone, *other_tone_args):63    def __init__(self, root_tone, *other_tone_args):
6664
67        self.root_tone = root_tone65        self.root_tone = root_tone
68        self.tone_set = set()66        self.tone_set = set()
69        self.tone_set.add(root_tone)67        self.tone_set.add(root_tone)
7068
71        for arg in other_tone_args:69        for arg in other_tone_args:
72            if isinstance(arg, set):70            if isinstance(arg, set):
73                for tone in arg:71                for tone in arg:
74                    self.tone_set.add(tone)72                    self.tone_set.add(tone)
75            else:73            else:
76                self.tone_set.add(arg)74                self.tone_set.add(arg)
7775
78        if len(self.tone_set) < 2:76        if len(self.tone_set) < 2:
79            raise TypeError("Cannot have a chord made of only 1 unique tone")77            raise TypeError("Cannot have a chord made of only 1 unique tone")
8078
81    def __str__(self):79    def __str__(self):
82        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))80        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
83        list_of_tone_indexes.sort()81        list_of_tone_indexes.sort()
8482
85        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)83        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
8684
87        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))85        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
8886
89        for tone in tone_indexes_before_root:87        for tone in tone_indexes_before_root:
90            list_of_tone_indexes.remove(tone)88            list_of_tone_indexes.remove(tone)
9189
92        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +90        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
93                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))91                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
9492
95        return result93        return result
9694
97    def __add__(self, other):95    def __add__(self, other):
9896
99        if isinstance(other, Tone):97        if isinstance(other, Tone):
100            return Chord(self.root_tone, self.tone_set, other)98            return Chord(self.root_tone, self.tone_set, other)
10199
102        if isinstance(other, Chord):100        if isinstance(other, Chord):
103            return Chord(self.root_tone, self.tone_set, other.tone_set)101            return Chord(self.root_tone, self.tone_set, other.tone_set)
104102
105        raise TypeError("Invalid operation")103        raise TypeError("Invalid operation")
106104
107    def find_next_tone(self, reference_tone, tone_set):105    def find_next_tone(self, reference_tone, tone_set):
108106
109        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)107        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)
110108
111        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):109        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):
112            potential_tone = Tone(ACCEPTED_TONES[i])110            potential_tone = Tone(ACCEPTED_TONES[i])
113            if potential_tone in tone_set:111            if potential_tone in tone_set:
114                return potential_tone112                return potential_tone
115113
116        for i in range(0, reference_tone_index):114        for i in range(0, reference_tone_index):
117            potential_tone = Tone(ACCEPTED_TONES[i])115            potential_tone = Tone(ACCEPTED_TONES[i])
118            if potential_tone in tone_set:116            if potential_tone in tone_set:
119                return potential_tone117                return potential_tone
120118
121    def __sub__(self, other):119    def __sub__(self, other):
122        if isinstance(other, Tone):120        if isinstance(other, Tone):
123            if other not in self.tone_set:121            if other not in self.tone_set:
124                raise TypeError(f"Cannot remove tone {other} from chord {self}")122                raise TypeError(f"Cannot remove tone {other} from chord {self}")
125123
126            new_tone_set = self.tone_set.copy()124            new_tone_set = self.tone_set.copy()
127            new_tone_set.remove(other)125            new_tone_set.remove(other)
128126
129            if other == self.root_tone:127            if other == self.root_tone:
130                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)128                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)
131                return Chord(new_root_tone, new_tone_set)129                return Chord(new_root_tone, new_tone_set)
132130
133            return Chord(self.root_tone, new_tone_set)131            return Chord(self.root_tone, new_tone_set)
134132
135        raise TypeError("Invalid operation")133        raise TypeError("Invalid operation")
136134
137    def is_minor(self):135    def is_minor(self):
138        minor_third_index = INTERVAL_VALUES.index("minor 3rd")136        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
139        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)137        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
140138
141        for tone in self.tone_set:139        for tone in self.tone_set:
142            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:140            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
143                return True141                return True
144142
145        return False143        return False
146144
147    def is_major(self):145    def is_major(self):
148        minor_third_index = INTERVAL_VALUES.index("major 3rd")146        minor_third_index = INTERVAL_VALUES.index("major 3rd")
149        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)147        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
150148
151        for tone in self.tone_set:149        for tone in self.tone_set:
152            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:150            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
153                return True151                return True
154152
155        return False153        return False
156154
157    def is_power_chord(self):155    def is_power_chord(self):
158        return not self.is_minor() and not self.is_major()156        return not self.is_minor() and not self.is_major()
159157
160    def transposed(self, interval):158    def transposed(self, interval):
161        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))159        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

nn1 
2import unittest
13
2ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
3INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
4                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
57
68
7class Tone:9class Tone:
810
9    def __init__(self, tone):11    def __init__(self, tone):
10        if tone not in ACCEPTED_TONES:12        if tone not in ACCEPTED_TONES:
11            raise TypeError("Tone is not accepted")13            raise TypeError("Tone is not accepted")
12        self.tone = tone14        self.tone = tone
1315
14    def __str__(self):16    def __str__(self):
15        return self.tone17        return self.tone
1618
17    def __eq__(self, other):19    def __eq__(self, other):
n18        if type(other) is not Tone:n
19            return False
20        return self.tone == other.tone20        return self.tone == other.tone
2121
22    def __hash__(self):22    def __hash__(self):
23        return hash(self.tone)23        return hash(self.tone)
2424
25    def __add__(self, other):25    def __add__(self, other):
n26        if type(other) is Tone:n26        if isinstance(other, Tone):
27            return Chord(self, other)27            return Chord(self, other)
28        28        
n29        if type(other) is Interval:n29        if isinstance(other, Interval):
30            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])30            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])
3131
32        raise TypeError("Invalid operation")32        raise TypeError("Invalid operation")
3333
34    def __sub__(self, other):34    def __sub__(self, other):
n35        if type(other) is Tone:n35        if isinstance(other, Tone):
36            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))36            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
3737
n38        if type(other) is Interval:n38        if isinstance(other, Interval):
39            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])39            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
4040
41        raise TypeError("Invalid operation")41        raise TypeError("Invalid operation")
4242
4343
44class Interval:44class Interval:
4545
46    def __init__(self, interval_length):46    def __init__(self, interval_length):
n47        if type(interval_length) is not int:n
48            raise TypeError("Interval length is not int")
49 
50        self.length = interval_length % len(INTERVAL_VALUES)47        self.length = interval_length % len(INTERVAL_VALUES)
5148
52    def __str__(self):49    def __str__(self):
n53        return INTERVAL_VALUES[abs(self.length)]n50        return INTERVAL_VALUES[self.length]
5451
55    def __add__(self, other):52    def __add__(self, other):
n56        return INTERVAL_VALUES[(self.length + other.length) % len(INTERVAL_VALUES)]n53 
54        if isinstance(other, Interval):
55            return Interval(self.length + other.length)
56 
57        raise TypeError("Invalid operation")
5758
58    def __neg__(self):59    def __neg__(self):
n59        reference = Interval(self.length)n60        return Interval(-self.length)
60        reference.length *= -1
61        return reference
6261
6362
64class Chord:63class Chord:
6564
66    def __init__(self, root_tone, *other_tone_args):65    def __init__(self, root_tone, *other_tone_args):
6766
68        self.root_tone = root_tone67        self.root_tone = root_tone
69        self.tone_set = set()68        self.tone_set = set()
70        self.tone_set.add(root_tone)69        self.tone_set.add(root_tone)
7170
72        for arg in other_tone_args:71        for arg in other_tone_args:
n73            if type(arg) is set:n72            if isinstance(arg, set):
74                for tone in arg:73                for tone in arg:
75                    self.tone_set.add(tone)74                    self.tone_set.add(tone)
76            else:75            else:
77                self.tone_set.add(arg)76                self.tone_set.add(arg)
7877
79        if len(self.tone_set) < 2:78        if len(self.tone_set) < 2:
80            raise TypeError("Cannot have a chord made of only 1 unique tone")79            raise TypeError("Cannot have a chord made of only 1 unique tone")
8180
82    def __str__(self):81    def __str__(self):
83        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))82        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
84        list_of_tone_indexes.sort()83        list_of_tone_indexes.sort()
8584
86        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)85        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
8786
88        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))87        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
8988
90        for tone in tone_indexes_before_root:89        for tone in tone_indexes_before_root:
91            list_of_tone_indexes.remove(tone)90            list_of_tone_indexes.remove(tone)
9291
93        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +92        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
94                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))93                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
9594
96        return result95        return result
9796
98    def __add__(self, other):97    def __add__(self, other):
9998
n100        if type(other) is Tone:n99        if isinstance(other, Tone):
101            return Chord(self.root_tone, self.tone_set, other)100            return Chord(self.root_tone, self.tone_set, other)
102101
n103        if type(other) is Chord:n102        if isinstance(other, Chord):
104            return Chord(self.root_tone, self.tone_set, other.tone_set)103            return Chord(self.root_tone, self.tone_set, other.tone_set)
105104
106        raise TypeError("Invalid operation")105        raise TypeError("Invalid operation")
107106
108    def find_next_tone(self, reference_tone, tone_set):107    def find_next_tone(self, reference_tone, tone_set):
109108
110        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)109        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)
111110
112        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):111        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):
113            potential_tone = Tone(ACCEPTED_TONES[i])112            potential_tone = Tone(ACCEPTED_TONES[i])
114            if potential_tone in tone_set:113            if potential_tone in tone_set:
115                return potential_tone114                return potential_tone
116115
117        for i in range(0, reference_tone_index):116        for i in range(0, reference_tone_index):
118            potential_tone = Tone(ACCEPTED_TONES[i])117            potential_tone = Tone(ACCEPTED_TONES[i])
119            if potential_tone in tone_set:118            if potential_tone in tone_set:
120                return potential_tone119                return potential_tone
121120
122    def __sub__(self, other):121    def __sub__(self, other):
n123        if type(other) is Tone:n122        if isinstance(other, Tone):
124            if other not in self.tone_set:123            if other not in self.tone_set:
t125                raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")t124                raise TypeError(f"Cannot remove tone {other} from chord {self}")
126125
127            new_tone_set = self.tone_set.copy()126            new_tone_set = self.tone_set.copy()
128            new_tone_set.remove(other)127            new_tone_set.remove(other)
129128
130            if other == self.root_tone:129            if other == self.root_tone:
131                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)130                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)
132                return Chord(new_root_tone, new_tone_set)131                return Chord(new_root_tone, new_tone_set)
133132
134            return Chord(self.root_tone, new_tone_set)133            return Chord(self.root_tone, new_tone_set)
135134
136        raise TypeError("Invalid operation")135        raise TypeError("Invalid operation")
137136
138    def is_minor(self):137    def is_minor(self):
139        minor_third_index = INTERVAL_VALUES.index("minor 3rd")138        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
140        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)139        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
141140
142        for tone in self.tone_set:141        for tone in self.tone_set:
143            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:142            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
144                return True143                return True
145144
146        return False145        return False
147146
148    def is_major(self):147    def is_major(self):
149        minor_third_index = INTERVAL_VALUES.index("major 3rd")148        minor_third_index = INTERVAL_VALUES.index("major 3rd")
150        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)149        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
151150
152        for tone in self.tone_set:151        for tone in self.tone_set:
153            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:152            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
154                return True153                return True
155154
156        return False155        return False
157156
158    def is_power_chord(self):157    def is_power_chord(self):
159        return not self.is_minor() and not self.is_major()158        return not self.is_minor() and not self.is_major()
160159
161    def transposed(self, interval):160    def transposed(self, interval):
162        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))161        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

n1 n
2import unittest
31
4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")2ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",3INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")4                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
75
86
9class Tone:7class Tone:
108
11    def __init__(self, tone):9    def __init__(self, tone):
12        if tone not in ACCEPTED_TONES:10        if tone not in ACCEPTED_TONES:
13            raise TypeError("Tone is not accepted")11            raise TypeError("Tone is not accepted")
14        self.tone = tone12        self.tone = tone
1513
16    def __str__(self):14    def __str__(self):
17        return self.tone15        return self.tone
1816
19    def __eq__(self, other):17    def __eq__(self, other):
20        if type(other) is not Tone:18        if type(other) is not Tone:
21            return False19            return False
22        return self.tone == other.tone20        return self.tone == other.tone
2321
24    def __hash__(self):22    def __hash__(self):
25        return hash(self.tone)23        return hash(self.tone)
2624
27    def __add__(self, other):25    def __add__(self, other):
28        if type(other) is Tone:26        if type(other) is Tone:
29            return Chord(self, other)27            return Chord(self, other)
30        28        
31        if type(other) is Interval:29        if type(other) is Interval:
n32            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)])n30            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length) % len(ACCEPTED_TONES)])
3331
34        raise TypeError("Invalid operation")32        raise TypeError("Invalid operation")
3533
36    def __sub__(self, other):34    def __sub__(self, other):
37        if type(other) is Tone:35        if type(other) is Tone:
38            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))36            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
3937
40        if type(other) is Interval:38        if type(other) is Interval:
41            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])39            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
4240
43        raise TypeError("Invalid operation")41        raise TypeError("Invalid operation")
4442
4543
46class Interval:44class Interval:
4745
48    def __init__(self, interval_length):46    def __init__(self, interval_length):
49        if type(interval_length) is not int:47        if type(interval_length) is not int:
50            raise TypeError("Interval length is not int")48            raise TypeError("Interval length is not int")
5149
52        self.length = interval_length % len(INTERVAL_VALUES)50        self.length = interval_length % len(INTERVAL_VALUES)
5351
54    def __str__(self):52    def __str__(self):
n55        return INTERVAL_VALUES[self.length]n53        return INTERVAL_VALUES[abs(self.length)]
54 
55    def __add__(self, other):
56        return INTERVAL_VALUES[(self.length + other.length) % len(INTERVAL_VALUES)]
57 
58    def __neg__(self):
59        reference = Interval(self.length)
60        reference.length *= -1
61        return reference
5662
5763
58class Chord:64class Chord:
5965
60    def __init__(self, root_tone, *other_tone_args):66    def __init__(self, root_tone, *other_tone_args):
6167
62        self.root_tone = root_tone68        self.root_tone = root_tone
63        self.tone_set = set()69        self.tone_set = set()
64        self.tone_set.add(root_tone)70        self.tone_set.add(root_tone)
6571
66        for arg in other_tone_args:72        for arg in other_tone_args:
67            if type(arg) is set:73            if type(arg) is set:
68                for tone in arg:74                for tone in arg:
69                    self.tone_set.add(tone)75                    self.tone_set.add(tone)
70            else:76            else:
71                self.tone_set.add(arg)77                self.tone_set.add(arg)
7278
73        if len(self.tone_set) < 2:79        if len(self.tone_set) < 2:
74            raise TypeError("Cannot have a chord made of only 1 unique tone")80            raise TypeError("Cannot have a chord made of only 1 unique tone")
7581
76    def __str__(self):82    def __str__(self):
77        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))83        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
78        list_of_tone_indexes.sort()84        list_of_tone_indexes.sort()
7985
80        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)86        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
8187
82        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))88        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
8389
84        for tone in tone_indexes_before_root:90        for tone in tone_indexes_before_root:
85            list_of_tone_indexes.remove(tone)91            list_of_tone_indexes.remove(tone)
8692
87        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +93        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
88                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))94                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
8995
90        return result96        return result
9197
92    def __add__(self, other):98    def __add__(self, other):
9399
94        if type(other) is Tone:100        if type(other) is Tone:
n95            return Chord(self.root_tone, self.tone_set, other.tone)n101            return Chord(self.root_tone, self.tone_set, other)
96102
97        if type(other) is Chord:103        if type(other) is Chord:
n98            return Chord(self.root_tone, self.tone_set.union(other.tone))n104            return Chord(self.root_tone, self.tone_setother.tone_set)
99105
100        raise TypeError("Invalid operation")106        raise TypeError("Invalid operation")
101107
102    def find_next_tone(self, reference_tone, tone_set):108    def find_next_tone(self, reference_tone, tone_set):
103109
104        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)110        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)
105111
106        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):112        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):
107            potential_tone = Tone(ACCEPTED_TONES[i])113            potential_tone = Tone(ACCEPTED_TONES[i])
108            if potential_tone in tone_set:114            if potential_tone in tone_set:
109                return potential_tone115                return potential_tone
110116
111        for i in range(0, reference_tone_index):117        for i in range(0, reference_tone_index):
112            potential_tone = Tone(ACCEPTED_TONES[i])118            potential_tone = Tone(ACCEPTED_TONES[i])
113            if potential_tone in tone_set:119            if potential_tone in tone_set:
114                return potential_tone120                return potential_tone
115121
116    def __sub__(self, other):122    def __sub__(self, other):
117        if type(other) is Tone:123        if type(other) is Tone:
118            if other not in self.tone_set:124            if other not in self.tone_set:
119                raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")125                raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")
120126
121            new_tone_set = self.tone_set.copy()127            new_tone_set = self.tone_set.copy()
122            new_tone_set.remove(other)128            new_tone_set.remove(other)
123129
124            if other == self.root_tone:130            if other == self.root_tone:
125                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)131                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)
126                return Chord(new_root_tone, new_tone_set)132                return Chord(new_root_tone, new_tone_set)
127133
128            return Chord(self.root_tone, new_tone_set)134            return Chord(self.root_tone, new_tone_set)
129135
130        raise TypeError("Invalid operation")136        raise TypeError("Invalid operation")
131137
132    def is_minor(self):138    def is_minor(self):
133        minor_third_index = INTERVAL_VALUES.index("minor 3rd")139        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
134        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)140        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
135141
136        for tone in self.tone_set:142        for tone in self.tone_set:
137            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:143            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
138                return True144                return True
139145
140        return False146        return False
141147
142    def is_major(self):148    def is_major(self):
143        minor_third_index = INTERVAL_VALUES.index("major 3rd")149        minor_third_index = INTERVAL_VALUES.index("major 3rd")
144        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)150        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
145151
146        for tone in self.tone_set:152        for tone in self.tone_set:
147            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:153            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
148                return True154                return True
149155
150        return False156        return False
151157
152    def is_power_chord(self):158    def is_power_chord(self):
153        return not self.is_minor() and not self.is_major()159        return not self.is_minor() and not self.is_major()
154160
155    def transposed(self, interval):161    def transposed(self, interval):
156        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))162        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))
t157 t
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1f1
2import unittest2import unittest
33
4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
77
88
9class Tone:9class Tone:
1010
11    def __init__(self, tone):11    def __init__(self, tone):
12        if tone not in ACCEPTED_TONES:12        if tone not in ACCEPTED_TONES:
13            raise TypeError("Tone is not accepted")13            raise TypeError("Tone is not accepted")
14        self.tone = tone14        self.tone = tone
1515
16    def __str__(self):16    def __str__(self):
17        return self.tone17        return self.tone
1818
19    def __eq__(self, other):19    def __eq__(self, other):
20        if type(other) is not Tone:20        if type(other) is not Tone:
21            return False21            return False
22        return self.tone == other.tone22        return self.tone == other.tone
2323
24    def __hash__(self):24    def __hash__(self):
25        return hash(self.tone)25        return hash(self.tone)
2626
27    def __add__(self, other):27    def __add__(self, other):
28        if type(other) is Tone:28        if type(other) is Tone:
29            return Chord(self, other)29            return Chord(self, other)
30        30        
31        if type(other) is Interval:31        if type(other) is Interval:
32            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)])32            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)])
3333
34        raise TypeError("Invalid operation")34        raise TypeError("Invalid operation")
3535
36    def __sub__(self, other):36    def __sub__(self, other):
37        if type(other) is Tone:37        if type(other) is Tone:
38            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))38            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
3939
40        if type(other) is Interval:40        if type(other) is Interval:
41            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])41            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
4242
43        raise TypeError("Invalid operation")43        raise TypeError("Invalid operation")
4444
4545
46class Interval:46class Interval:
4747
48    def __init__(self, interval_length):48    def __init__(self, interval_length):
49        if type(interval_length) is not int:49        if type(interval_length) is not int:
50            raise TypeError("Interval length is not int")50            raise TypeError("Interval length is not int")
5151
52        self.length = interval_length % len(INTERVAL_VALUES)52        self.length = interval_length % len(INTERVAL_VALUES)
5353
54    def __str__(self):54    def __str__(self):
55        return INTERVAL_VALUES[self.length]55        return INTERVAL_VALUES[self.length]
5656
5757
58class Chord:58class Chord:
5959
60    def __init__(self, root_tone, *other_tone_args):60    def __init__(self, root_tone, *other_tone_args):
6161
62        self.root_tone = root_tone62        self.root_tone = root_tone
63        self.tone_set = set()63        self.tone_set = set()
64        self.tone_set.add(root_tone)64        self.tone_set.add(root_tone)
6565
66        for arg in other_tone_args:66        for arg in other_tone_args:
67            if type(arg) is set:67            if type(arg) is set:
68                for tone in arg:68                for tone in arg:
69                    self.tone_set.add(tone)69                    self.tone_set.add(tone)
70            else:70            else:
71                self.tone_set.add(arg)71                self.tone_set.add(arg)
7272
73        if len(self.tone_set) < 2:73        if len(self.tone_set) < 2:
74            raise TypeError("Cannot have a chord made of only 1 unique tone")74            raise TypeError("Cannot have a chord made of only 1 unique tone")
7575
76    def __str__(self):76    def __str__(self):
77        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))77        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
78        list_of_tone_indexes.sort()78        list_of_tone_indexes.sort()
7979
80        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)80        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
8181
82        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))82        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
8383
84        for tone in tone_indexes_before_root:84        for tone in tone_indexes_before_root:
85            list_of_tone_indexes.remove(tone)85            list_of_tone_indexes.remove(tone)
8686
87        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +87        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
88                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))88                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
8989
90        return result90        return result
9191
92    def __add__(self, other):92    def __add__(self, other):
9393
94        if type(other) is Tone:94        if type(other) is Tone:
95            return Chord(self.root_tone, self.tone_set, other.tone)95            return Chord(self.root_tone, self.tone_set, other.tone)
9696
97        if type(other) is Chord:97        if type(other) is Chord:
n98            return Chord(self.root_tone, self.tone_set.union(other.tone_set))n98            return Chord(self.root_tone, self.tone_set.union(other.tone))
9999
100        raise TypeError("Invalid operation")100        raise TypeError("Invalid operation")
101101
nn102    def find_next_tone(self, reference_tone, tone_set):
103 
104        reference_tone_index = ACCEPTED_TONES.index(reference_tone.tone)
105 
106        for i in range(reference_tone_index + 1, len(ACCEPTED_TONES)):
107            potential_tone = Tone(ACCEPTED_TONES[i])
108            if potential_tone in tone_set:
109                return potential_tone
110 
111        for i in range(0, reference_tone_index):
112            potential_tone = Tone(ACCEPTED_TONES[i])
113            if potential_tone in tone_set:
114                return potential_tone
115 
102    def __sub__(self, other):116    def __sub__(self, other):
nn117        if type(other) is Tone:
118            if other not in self.tone_set:
119                raise TypeError(f"Cannot remove tone {str(other)} from chord {str(self)}")
103120
n104        if type(other) is Tone:n121            new_tone_set = self.tone_set.copy()
105            pass122            new_tone_set.remove(other)
123 
124            if other == self.root_tone:
125                new_root_tone = self.find_next_tone(self.root_tone, self.tone_set)
126                return Chord(new_root_tone, new_tone_set)
127 
128            return Chord(self.root_tone, new_tone_set)
106129
107        raise TypeError("Invalid operation")130        raise TypeError("Invalid operation")
108131
109    def is_minor(self):132    def is_minor(self):
110        minor_third_index = INTERVAL_VALUES.index("minor 3rd")133        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
111        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)134        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
112135
113        for tone in self.tone_set:136        for tone in self.tone_set:
114            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:137            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
115                return True138                return True
116139
117        return False140        return False
118141
119    def is_major(self):142    def is_major(self):
120        minor_third_index = INTERVAL_VALUES.index("major 3rd")143        minor_third_index = INTERVAL_VALUES.index("major 3rd")
121        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)144        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
122145
123        for tone in self.tone_set:146        for tone in self.tone_set:
124            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:147            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
125                return True148                return True
126149
127        return False150        return False
128151
129    def is_power_chord(self):152    def is_power_chord(self):
130        return not self.is_minor() and not self.is_major()153        return not self.is_minor() and not self.is_major()
tt154 
155    def transposed(self, interval):
156        return Chord(self.root_tone + interval, set(map(lambda x: x + interval, list(self.tone_set))))
157 
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1f1
2import unittest2import unittest
33
4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")4ACCEPTED_TONES = ("C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B")
5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",5INTERVAL_VALUES = ("unison", "minor 2nd", "major 2nd", "minor 3rd", "major 3rd", "perfect 4th",
6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")6                   "diminished 5th", "perfect 5th", "minor 6th", "major 6th", "minor 7th", "major 7th")
77
88
9class Tone:9class Tone:
1010
11    def __init__(self, tone):11    def __init__(self, tone):
12        if tone not in ACCEPTED_TONES:12        if tone not in ACCEPTED_TONES:
13            raise TypeError("Tone is not accepted")13            raise TypeError("Tone is not accepted")
14        self.tone = tone14        self.tone = tone
1515
16    def __str__(self):16    def __str__(self):
17        return self.tone17        return self.tone
1818
19    def __eq__(self, other):19    def __eq__(self, other):
20        if type(other) is not Tone:20        if type(other) is not Tone:
21            return False21            return False
22        return self.tone == other.tone22        return self.tone == other.tone
2323
24    def __hash__(self):24    def __hash__(self):
25        return hash(self.tone)25        return hash(self.tone)
2626
27    def __add__(self, other):27    def __add__(self, other):
28        if type(other) is Tone:28        if type(other) is Tone:
29            return Chord(self, other)29            return Chord(self, other)
30        30        
31        if type(other) is Interval:31        if type(other) is Interval:
32            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)])32            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) + other.length)])
3333
34        raise TypeError("Invalid operation")34        raise TypeError("Invalid operation")
3535
36    def __sub__(self, other):36    def __sub__(self, other):
37        if type(other) is Tone:37        if type(other) is Tone:
38            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))38            return Interval(abs(ACCEPTED_TONES.index(self.tone) - ACCEPTED_TONES.index(other.tone)))
3939
40        if type(other) is Interval:40        if type(other) is Interval:
41            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])41            return Tone(ACCEPTED_TONES[(ACCEPTED_TONES.index(self.tone) - other.length) % len(ACCEPTED_TONES)])
4242
43        raise TypeError("Invalid operation")43        raise TypeError("Invalid operation")
4444
4545
46class Interval:46class Interval:
4747
48    def __init__(self, interval_length):48    def __init__(self, interval_length):
49        if type(interval_length) is not int:49        if type(interval_length) is not int:
50            raise TypeError("Interval length is not int")50            raise TypeError("Interval length is not int")
5151
52        self.length = interval_length % len(INTERVAL_VALUES)52        self.length = interval_length % len(INTERVAL_VALUES)
5353
54    def __str__(self):54    def __str__(self):
55        return INTERVAL_VALUES[self.length]55        return INTERVAL_VALUES[self.length]
5656
5757
58class Chord:58class Chord:
5959
60    def __init__(self, root_tone, *other_tone_args):60    def __init__(self, root_tone, *other_tone_args):
6161
62        self.root_tone = root_tone62        self.root_tone = root_tone
63        self.tone_set = set()63        self.tone_set = set()
64        self.tone_set.add(root_tone)64        self.tone_set.add(root_tone)
6565
66        for arg in other_tone_args:66        for arg in other_tone_args:
67            if type(arg) is set:67            if type(arg) is set:
68                for tone in arg:68                for tone in arg:
69                    self.tone_set.add(tone)69                    self.tone_set.add(tone)
70            else:70            else:
71                self.tone_set.add(arg)71                self.tone_set.add(arg)
7272
73        if len(self.tone_set) < 2:73        if len(self.tone_set) < 2:
74            raise TypeError("Cannot have a chord made of only 1 unique tone")74            raise TypeError("Cannot have a chord made of only 1 unique tone")
7575
76    def __str__(self):76    def __str__(self):
77        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))77        list_of_tone_indexes = list(map(lambda x: ACCEPTED_TONES.index(x.tone), list(self.tone_set)))
78        list_of_tone_indexes.sort()78        list_of_tone_indexes.sort()
7979
80        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)80        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
8181
82        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))82        tone_indexes_before_root = list(filter(lambda x: x < root_tone_index, list_of_tone_indexes))
8383
84        for tone in tone_indexes_before_root:84        for tone in tone_indexes_before_root:
85            list_of_tone_indexes.remove(tone)85            list_of_tone_indexes.remove(tone)
8686
87        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +87        result = "-".join(list(map(lambda x: ACCEPTED_TONES[x], list_of_tone_indexes)) +
88                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))88                          list(map(lambda x: ACCEPTED_TONES[x], tone_indexes_before_root)))
8989
90        return result90        return result
9191
92    def __add__(self, other):92    def __add__(self, other):
9393
94        if type(other) is Tone:94        if type(other) is Tone:
95            return Chord(self.root_tone, self.tone_set, other.tone)95            return Chord(self.root_tone, self.tone_set, other.tone)
9696
97        if type(other) is Chord:97        if type(other) is Chord:
98            return Chord(self.root_tone, self.tone_set.union(other.tone_set))98            return Chord(self.root_tone, self.tone_set.union(other.tone_set))
9999
100        raise TypeError("Invalid operation")100        raise TypeError("Invalid operation")
101101
102    def __sub__(self, other):102    def __sub__(self, other):
103103
104        if type(other) is Tone:104        if type(other) is Tone:
105            pass105            pass
106106
107        raise TypeError("Invalid operation")107        raise TypeError("Invalid operation")
108108
109    def is_minor(self):109    def is_minor(self):
110        minor_third_index = INTERVAL_VALUES.index("minor 3rd")110        minor_third_index = INTERVAL_VALUES.index("minor 3rd")
111        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)111        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
112112
113        for tone in self.tone_set:113        for tone in self.tone_set:
114            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:114            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
115                return True115                return True
116116
117        return False117        return False
118118
119    def is_major(self):119    def is_major(self):
120        minor_third_index = INTERVAL_VALUES.index("major 3rd")120        minor_third_index = INTERVAL_VALUES.index("major 3rd")
121        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)121        root_tone_index = ACCEPTED_TONES.index(self.root_tone.tone)
122122
123        for tone in self.tone_set:123        for tone in self.tone_set:
124            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:124            if abs(root_tone_index - ACCEPTED_TONES.index(tone.tone)) == minor_third_index:
125                return True125                return True
126126
127        return False127        return False
128128
129    def is_power_chord(self):129    def is_power_chord(self):
130        return not self.is_minor() and not self.is_major()130        return not self.is_minor() and not self.is_major()
t131 t
132 
133c5_chord = Chord(Tone("C"), Tone("G"))
134this_other_chord = Chord(Tone("A"), Tone("B"))
135result_chord = c5_chord + this_other_chord
136print(result_chord)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op