Домашни > Pitches love the D > Решения > Решението на Ивайло Кънчев

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

10 точки общо

37 успешни теста
0 неуспешни теста
Код

  1CHROMATIC_SCALE = (
  2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
  3)
  4
  5TONES_COUNT = 12
  6MINOR_THIRD_LEN = 3
  7MAJOR_THIRD_LEN = 4
  8
  9
 10class Tone:
 11
 12    def __init__(self, tone_name):
 13        self.tone_name = tone_name
 14
 15    def __str__(self):
 16        return self.tone_name
 17
 18    def __eq__(self, other):
 19        return self.tone_name == other.tone_name
 20
 21    def __ne__(self, other):
 22        return self.tone_name != other.tone_name
 23
 24    def __add__(self, other):
 25        if isinstance(other, Tone):
 26            return Chord(self,other)
 27        elif isinstance(other, Interval):
 28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT
 29            return Tone(CHROMATIC_SCALE[new_tone_idx])
 30
 31    def __sub__(self, other):
 32        if isinstance(other, Tone):
 33            self_idx = CHROMATIC_SCALE.index(self.tone_name)
 34            other_idx = CHROMATIC_SCALE.index(other.tone_name)
 35            interval_length = (self_idx - other_idx) % TONES_COUNT
 36            return Interval(interval_length)
 37        elif isinstance(other, Interval):
 38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count) % TONES_COUNT
 39            return Tone(CHROMATIC_SCALE[new_tone_idx])
 40
 41
 42class Interval:
 43
 44    _INTERVAL_NAMES = (
 45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',
 46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',
 47        'major 6th', 'minor 7th', 'major 7th'
 48    )
 49
 50    def __init__(self, semitones_count):
 51        self.semitones_count = semitones_count
 52
 53    def __str__(self):
 54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]
 55
 56    def __add__(self, other):
 57        if isinstance(other, Tone):
 58            raise TypeError('Invalid operation')
 59        elif isinstance(other, Interval):
 60            new_count = (self.semitones_count + other.semitones_count)
 61            return Interval(new_count)
 62
 63    def __sub__(self, other):
 64        if isinstance(other, Tone):
 65            raise TypeError('Invalid operation')
 66
 67    def __neg__(self):
 68        return Interval(-self.semitones_count)
 69
 70
 71class Chord:
 72
 73    def __init__(self, *tones):
 74        if not self._are_valid_tones(*tones):
 75            raise TypeError('Cannot have a chord made of only 1 unique tone')
 76        self.sorted_tones = self._sort_tones(*tones)
 77
 78    @staticmethod
 79    def _are_valid_tones(*tones):
 80        tones_set = {str(tone) for tone in tones}
 81        return len(tones_set) > 1
 82
 83    @staticmethod
 84    def _sort_tones(*tones):
 85        root = tones[0]
 86        sorted_tones = [root]
 87        end_idx = CHROMATIC_SCALE.index(str(root))
 88        iter_idx = (end_idx + 1) % TONES_COUNT
 89
 90        while iter_idx != end_idx:
 91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])
 92            if current_tone in tones and current_tone not in sorted_tones:
 93                sorted_tones.append(current_tone)
 94            iter_idx += 1
 95            iter_idx %= TONES_COUNT
 96        return sorted_tones
 97
 98    def __str__(self):
 99        return '-'.join(str(tone) for tone in self.sorted_tones)
100
101    def is_minor(self):
102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT
104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
105
106    def is_major(self):
107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT
109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
110
111    def is_power_chord(self):
112        return not self.is_minor() and not self.is_major()
113
114    def __add__(self, other):
115        if isinstance(other, Tone):
116            new_tones = self.sorted_tones + [other]
117            return Chord(*new_tones)
118        elif isinstance(other, Chord):
119            new_tones = self.sorted_tones[:]
120            new_tones.extend(other.sorted_tones)
121            return Chord(*new_tones)
122
123    def __sub__(self, other):
124        if isinstance(other, Tone):
125            if other not in self.sorted_tones:
126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')
127            new_tones = self.sorted_tones[:]
128            new_tones.remove(other)
129            return Chord(*new_tones)
130
131    def transposed(self, interval):
132        transpose = lambda tone : tone + interval
133        transposed_tones  = list(map(transpose, self.sorted_tones))
134        return Chord(*transposed_tones)

.....................................
----------------------------------------------------------------------
Ran 37 tests in 0.001s

OK

Дискусия
История

t1CHROMATIC_SCALE = (t1CHROMATIC_SCALE = (
2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
3)3)
44
5TONES_COUNT = 125TONES_COUNT = 12
6MINOR_THIRD_LEN = 36MINOR_THIRD_LEN = 3
7MAJOR_THIRD_LEN = 47MAJOR_THIRD_LEN = 4
88
99
10class Tone:10class Tone:
1111
12    def __init__(self, tone_name):12    def __init__(self, tone_name):
13        self.tone_name = tone_name13        self.tone_name = tone_name
1414
15    def __str__(self):15    def __str__(self):
16        return self.tone_name16        return self.tone_name
1717
18    def __eq__(self, other):18    def __eq__(self, other):
19        return self.tone_name == other.tone_name19        return self.tone_name == other.tone_name
2020
21    def __ne__(self, other):21    def __ne__(self, other):
22        return self.tone_name != other.tone_name22        return self.tone_name != other.tone_name
2323
24    def __add__(self, other):24    def __add__(self, other):
25        if isinstance(other, Tone):25        if isinstance(other, Tone):
26            return Chord(self,other)26            return Chord(self,other)
27        elif isinstance(other, Interval):27        elif isinstance(other, Interval):
28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT
29            return Tone(CHROMATIC_SCALE[new_tone_idx])29            return Tone(CHROMATIC_SCALE[new_tone_idx])
3030
31    def __sub__(self, other):31    def __sub__(self, other):
32        if isinstance(other, Tone):32        if isinstance(other, Tone):
33            self_idx = CHROMATIC_SCALE.index(self.tone_name)33            self_idx = CHROMATIC_SCALE.index(self.tone_name)
34            other_idx = CHROMATIC_SCALE.index(other.tone_name)34            other_idx = CHROMATIC_SCALE.index(other.tone_name)
35            interval_length = (self_idx - other_idx) % TONES_COUNT35            interval_length = (self_idx - other_idx) % TONES_COUNT
36            return Interval(interval_length)36            return Interval(interval_length)
37        elif isinstance(other, Interval):37        elif isinstance(other, Interval):
38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count) % TONES_COUNT38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count) % TONES_COUNT
39            return Tone(CHROMATIC_SCALE[new_tone_idx])39            return Tone(CHROMATIC_SCALE[new_tone_idx])
4040
4141
42class Interval:42class Interval:
4343
44    _INTERVAL_NAMES = (44    _INTERVAL_NAMES = (
45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',
46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',
47        'major 6th', 'minor 7th', 'major 7th'47        'major 6th', 'minor 7th', 'major 7th'
48    )48    )
4949
50    def __init__(self, semitones_count):50    def __init__(self, semitones_count):
51        self.semitones_count = semitones_count51        self.semitones_count = semitones_count
5252
53    def __str__(self):53    def __str__(self):
54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]
5555
56    def __add__(self, other):56    def __add__(self, other):
57        if isinstance(other, Tone):57        if isinstance(other, Tone):
58            raise TypeError('Invalid operation')58            raise TypeError('Invalid operation')
59        elif isinstance(other, Interval):59        elif isinstance(other, Interval):
60            new_count = (self.semitones_count + other.semitones_count)60            new_count = (self.semitones_count + other.semitones_count)
61            return Interval(new_count)61            return Interval(new_count)
6262
63    def __sub__(self, other):63    def __sub__(self, other):
64        if isinstance(other, Tone):64        if isinstance(other, Tone):
65            raise TypeError('Invalid operation')65            raise TypeError('Invalid operation')
6666
67    def __neg__(self):67    def __neg__(self):
68        return Interval(-self.semitones_count)68        return Interval(-self.semitones_count)
6969
7070
71class Chord:71class Chord:
7272
73    def __init__(self, *tones):73    def __init__(self, *tones):
74        if not self._are_valid_tones(*tones):74        if not self._are_valid_tones(*tones):
75            raise TypeError('Cannot have a chord made of only 1 unique tone')75            raise TypeError('Cannot have a chord made of only 1 unique tone')
76        self.sorted_tones = self._sort_tones(*tones)76        self.sorted_tones = self._sort_tones(*tones)
7777
78    @staticmethod78    @staticmethod
79    def _are_valid_tones(*tones):79    def _are_valid_tones(*tones):
80        tones_set = {str(tone) for tone in tones}80        tones_set = {str(tone) for tone in tones}
81        return len(tones_set) > 181        return len(tones_set) > 1
8282
83    @staticmethod83    @staticmethod
84    def _sort_tones(*tones):84    def _sort_tones(*tones):
85        root = tones[0]85        root = tones[0]
86        sorted_tones = [root]86        sorted_tones = [root]
87        end_idx = CHROMATIC_SCALE.index(str(root))87        end_idx = CHROMATIC_SCALE.index(str(root))
88        iter_idx = (end_idx + 1) % TONES_COUNT88        iter_idx = (end_idx + 1) % TONES_COUNT
8989
90        while iter_idx != end_idx:90        while iter_idx != end_idx:
91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])
92            if current_tone in tones and current_tone not in sorted_tones:92            if current_tone in tones and current_tone not in sorted_tones:
93                sorted_tones.append(current_tone)93                sorted_tones.append(current_tone)
94            iter_idx += 194            iter_idx += 1
95            iter_idx %= TONES_COUNT95            iter_idx %= TONES_COUNT
96        return sorted_tones96        return sorted_tones
9797
98    def __str__(self):98    def __str__(self):
99        return '-'.join(str(tone) for tone in self.sorted_tones)99        return '-'.join(str(tone) for tone in self.sorted_tones)
100100
101    def is_minor(self):101    def is_minor(self):
102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT
104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
105105
106    def is_major(self):106    def is_major(self):
107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT
109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
110110
111    def is_power_chord(self):111    def is_power_chord(self):
112        return not self.is_minor() and not self.is_major()112        return not self.is_minor() and not self.is_major()
113113
114    def __add__(self, other):114    def __add__(self, other):
115        if isinstance(other, Tone):115        if isinstance(other, Tone):
116            new_tones = self.sorted_tones + [other]116            new_tones = self.sorted_tones + [other]
117            return Chord(*new_tones)117            return Chord(*new_tones)
118        elif isinstance(other, Chord):118        elif isinstance(other, Chord):
119            new_tones = self.sorted_tones[:]119            new_tones = self.sorted_tones[:]
120            new_tones.extend(other.sorted_tones)120            new_tones.extend(other.sorted_tones)
121            return Chord(*new_tones)121            return Chord(*new_tones)
122122
123    def __sub__(self, other):123    def __sub__(self, other):
124        if isinstance(other, Tone):124        if isinstance(other, Tone):
125            if other not in self.sorted_tones:125            if other not in self.sorted_tones:
126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')
127            new_tones = self.sorted_tones[:]127            new_tones = self.sorted_tones[:]
128            new_tones.remove(other)128            new_tones.remove(other)
129            return Chord(*new_tones)129            return Chord(*new_tones)
130130
131    def transposed(self, interval):131    def transposed(self, interval):
132        transpose = lambda tone : tone + interval132        transpose = lambda tone : tone + interval
133        transposed_tones  = list(map(transpose, self.sorted_tones))133        transposed_tones  = list(map(transpose, self.sorted_tones))
134        return Chord(*transposed_tones)134        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1CHROMATIC_SCALE = (f1CHROMATIC_SCALE = (
2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
3)3)
44
5TONES_COUNT = 125TONES_COUNT = 12
6MINOR_THIRD_LEN = 36MINOR_THIRD_LEN = 3
7MAJOR_THIRD_LEN = 47MAJOR_THIRD_LEN = 4
88
99
10class Tone:10class Tone:
1111
12    def __init__(self, tone_name):12    def __init__(self, tone_name):
13        self.tone_name = tone_name13        self.tone_name = tone_name
1414
15    def __str__(self):15    def __str__(self):
16        return self.tone_name16        return self.tone_name
1717
18    def __eq__(self, other):18    def __eq__(self, other):
19        return self.tone_name == other.tone_name19        return self.tone_name == other.tone_name
2020
21    def __ne__(self, other):21    def __ne__(self, other):
22        return self.tone_name != other.tone_name22        return self.tone_name != other.tone_name
2323
24    def __add__(self, other):24    def __add__(self, other):
25        if isinstance(other, Tone):25        if isinstance(other, Tone):
26            return Chord(self,other)26            return Chord(self,other)
27        elif isinstance(other, Interval):27        elif isinstance(other, Interval):
28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT
29            return Tone(CHROMATIC_SCALE[new_tone_idx])29            return Tone(CHROMATIC_SCALE[new_tone_idx])
3030
31    def __sub__(self, other):31    def __sub__(self, other):
32        if isinstance(other, Tone):32        if isinstance(other, Tone):
33            self_idx = CHROMATIC_SCALE.index(self.tone_name)33            self_idx = CHROMATIC_SCALE.index(self.tone_name)
34            other_idx = CHROMATIC_SCALE.index(other.tone_name)34            other_idx = CHROMATIC_SCALE.index(other.tone_name)
n35            interval_length = (self_idx - other_idx + TONES_COUNT) % TONES_COUNTn35            interval_length = (self_idx - other_idx) % TONES_COUNT
36            return Interval(interval_length)36            return Interval(interval_length)
37        elif isinstance(other, Interval):37        elif isinstance(other, Interval):
n38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count % TONES_COUNT + TONES_COUNT) % TONES_COUNTn38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count) % TONES_COUNT
39            return Tone(CHROMATIC_SCALE[new_tone_idx])39            return Tone(CHROMATIC_SCALE[new_tone_idx])
4040
4141
42class Interval:42class Interval:
4343
44    _INTERVAL_NAMES = (44    _INTERVAL_NAMES = (
45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',
46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',
47        'major 6th', 'minor 7th', 'major 7th'47        'major 6th', 'minor 7th', 'major 7th'
48    )48    )
4949
50    def __init__(self, semitones_count):50    def __init__(self, semitones_count):
51        self.semitones_count = semitones_count51        self.semitones_count = semitones_count
5252
53    def __str__(self):53    def __str__(self):
54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]
5555
56    def __add__(self, other):56    def __add__(self, other):
57        if isinstance(other, Tone):57        if isinstance(other, Tone):
58            raise TypeError('Invalid operation')58            raise TypeError('Invalid operation')
59        elif isinstance(other, Interval):59        elif isinstance(other, Interval):
60            new_count = (self.semitones_count + other.semitones_count)60            new_count = (self.semitones_count + other.semitones_count)
61            return Interval(new_count)61            return Interval(new_count)
6262
63    def __sub__(self, other):63    def __sub__(self, other):
64        if isinstance(other, Tone):64        if isinstance(other, Tone):
65            raise TypeError('Invalid operation')65            raise TypeError('Invalid operation')
6666
67    def __neg__(self):67    def __neg__(self):
68        return Interval(-self.semitones_count)68        return Interval(-self.semitones_count)
6969
7070
71class Chord:71class Chord:
7272
73    def __init__(self, *tones):73    def __init__(self, *tones):
74        if not self._are_valid_tones(*tones):74        if not self._are_valid_tones(*tones):
75            raise TypeError('Cannot have a chord made of only 1 unique tone')75            raise TypeError('Cannot have a chord made of only 1 unique tone')
76        self.sorted_tones = self._sort_tones(*tones)76        self.sorted_tones = self._sort_tones(*tones)
7777
78    @staticmethod78    @staticmethod
79    def _are_valid_tones(*tones):79    def _are_valid_tones(*tones):
80        tones_set = {str(tone) for tone in tones}80        tones_set = {str(tone) for tone in tones}
81        return len(tones_set) > 181        return len(tones_set) > 1
8282
83    @staticmethod83    @staticmethod
84    def _sort_tones(*tones):84    def _sort_tones(*tones):
85        root = tones[0]85        root = tones[0]
86        sorted_tones = [root]86        sorted_tones = [root]
87        end_idx = CHROMATIC_SCALE.index(str(root))87        end_idx = CHROMATIC_SCALE.index(str(root))
88        iter_idx = (end_idx + 1) % TONES_COUNT88        iter_idx = (end_idx + 1) % TONES_COUNT
8989
90        while iter_idx != end_idx:90        while iter_idx != end_idx:
91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])
92            if current_tone in tones and current_tone not in sorted_tones:92            if current_tone in tones and current_tone not in sorted_tones:
93                sorted_tones.append(current_tone)93                sorted_tones.append(current_tone)
94            iter_idx += 194            iter_idx += 1
95            iter_idx %= TONES_COUNT95            iter_idx %= TONES_COUNT
96        return sorted_tones96        return sorted_tones
9797
98    def __str__(self):98    def __str__(self):
99        return '-'.join(str(tone) for tone in self.sorted_tones)99        return '-'.join(str(tone) for tone in self.sorted_tones)
100100
101    def is_minor(self):101    def is_minor(self):
102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT
104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
105105
106    def is_major(self):106    def is_major(self):
107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT
109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
110110
111    def is_power_chord(self):111    def is_power_chord(self):
112        return not self.is_minor() and not self.is_major()112        return not self.is_minor() and not self.is_major()
113113
114    def __add__(self, other):114    def __add__(self, other):
115        if isinstance(other, Tone):115        if isinstance(other, Tone):
116            new_tones = self.sorted_tones + [other]116            new_tones = self.sorted_tones + [other]
117            return Chord(*new_tones)117            return Chord(*new_tones)
118        elif isinstance(other, Chord):118        elif isinstance(other, Chord):
119            new_tones = self.sorted_tones[:]119            new_tones = self.sorted_tones[:]
120            new_tones.extend(other.sorted_tones)120            new_tones.extend(other.sorted_tones)
121            return Chord(*new_tones)121            return Chord(*new_tones)
122122
123    def __sub__(self, other):123    def __sub__(self, other):
124        if isinstance(other, Tone):124        if isinstance(other, Tone):
125            if other not in self.sorted_tones:125            if other not in self.sorted_tones:
126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')
127            new_tones = self.sorted_tones[:]127            new_tones = self.sorted_tones[:]
128            new_tones.remove(other)128            new_tones.remove(other)
129            return Chord(*new_tones)129            return Chord(*new_tones)
130130
131    def transposed(self, interval):131    def transposed(self, interval):
t132        transpose_with = interval.semitones_countt132        transpose = lambda tone : tone + interval
133        transpose = lambda tone : Tone(CHROMATIC_SCALE[(CHROMATIC_SCALE.index(str(tone)) + transpose_with + TONES_COUNT) % TONES_COUNT])
134        transposed_tones  = list(map(transpose, self.sorted_tones))133        transposed_tones  = list(map(transpose, self.sorted_tones))
135        return Chord(*transposed_tones)134        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op

f1CHROMATIC_SCALE = (f1CHROMATIC_SCALE = (
2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'2    'C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'
3)3)
44
5TONES_COUNT = 125TONES_COUNT = 12
6MINOR_THIRD_LEN = 36MINOR_THIRD_LEN = 3
7MAJOR_THIRD_LEN = 47MAJOR_THIRD_LEN = 4
88
99
10class Tone:10class Tone:
1111
12    def __init__(self, tone_name):12    def __init__(self, tone_name):
13        self.tone_name = tone_name13        self.tone_name = tone_name
1414
15    def __str__(self):15    def __str__(self):
16        return self.tone_name16        return self.tone_name
1717
18    def __eq__(self, other):18    def __eq__(self, other):
19        return self.tone_name == other.tone_name19        return self.tone_name == other.tone_name
2020
21    def __ne__(self, other):21    def __ne__(self, other):
22        return self.tone_name != other.tone_name22        return self.tone_name != other.tone_name
2323
24    def __add__(self, other):24    def __add__(self, other):
25        if isinstance(other, Tone):25        if isinstance(other, Tone):
26            return Chord(self,other)26            return Chord(self,other)
27        elif isinstance(other, Interval):27        elif isinstance(other, Interval):
28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT28            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) + other.semitones_count) % TONES_COUNT
29            return Tone(CHROMATIC_SCALE[new_tone_idx])29            return Tone(CHROMATIC_SCALE[new_tone_idx])
3030
31    def __sub__(self, other):31    def __sub__(self, other):
32        if isinstance(other, Tone):32        if isinstance(other, Tone):
33            self_idx = CHROMATIC_SCALE.index(self.tone_name)33            self_idx = CHROMATIC_SCALE.index(self.tone_name)
34            other_idx = CHROMATIC_SCALE.index(other.tone_name)34            other_idx = CHROMATIC_SCALE.index(other.tone_name)
35            interval_length = (self_idx - other_idx + TONES_COUNT) % TONES_COUNT35            interval_length = (self_idx - other_idx + TONES_COUNT) % TONES_COUNT
36            return Interval(interval_length)36            return Interval(interval_length)
37        elif isinstance(other, Interval):37        elif isinstance(other, Interval):
38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count % TONES_COUNT + TONES_COUNT) % TONES_COUNT38            new_tone_idx = (CHROMATIC_SCALE.index(self.tone_name) - other.semitones_count % TONES_COUNT + TONES_COUNT) % TONES_COUNT
39            return Tone(CHROMATIC_SCALE[new_tone_idx])39            return Tone(CHROMATIC_SCALE[new_tone_idx])
4040
4141
42class Interval:42class Interval:
4343
44    _INTERVAL_NAMES = (44    _INTERVAL_NAMES = (
45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',45        'unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd',
46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',46        'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th',
47        'major 6th', 'minor 7th', 'major 7th'47        'major 6th', 'minor 7th', 'major 7th'
48    )48    )
4949
50    def __init__(self, semitones_count):50    def __init__(self, semitones_count):
51        self.semitones_count = semitones_count51        self.semitones_count = semitones_count
5252
53    def __str__(self):53    def __str__(self):
54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]54        return self._INTERVAL_NAMES[self.semitones_count % TONES_COUNT]
5555
56    def __add__(self, other):56    def __add__(self, other):
57        if isinstance(other, Tone):57        if isinstance(other, Tone):
58            raise TypeError('Invalid operation')58            raise TypeError('Invalid operation')
59        elif isinstance(other, Interval):59        elif isinstance(other, Interval):
60            new_count = (self.semitones_count + other.semitones_count)60            new_count = (self.semitones_count + other.semitones_count)
61            return Interval(new_count)61            return Interval(new_count)
6262
63    def __sub__(self, other):63    def __sub__(self, other):
64        if isinstance(other, Tone):64        if isinstance(other, Tone):
65            raise TypeError('Invalid operation')65            raise TypeError('Invalid operation')
6666
67    def __neg__(self):67    def __neg__(self):
68        return Interval(-self.semitones_count)68        return Interval(-self.semitones_count)
6969
7070
71class Chord:71class Chord:
7272
73    def __init__(self, *tones):73    def __init__(self, *tones):
74        if not self._are_valid_tones(*tones):74        if not self._are_valid_tones(*tones):
75            raise TypeError('Cannot have a chord made of only 1 unique tone')75            raise TypeError('Cannot have a chord made of only 1 unique tone')
76        self.sorted_tones = self._sort_tones(*tones)76        self.sorted_tones = self._sort_tones(*tones)
7777
78    @staticmethod78    @staticmethod
79    def _are_valid_tones(*tones):79    def _are_valid_tones(*tones):
n80        tones_set = set()n80        tones_set = {str(tone) for tone in tones}
81 
82        for tone in tones:
83            tones_set.add(str(tone))
84 
85        return len(tones_set) > 181        return len(tones_set) > 1
8682
87    @staticmethod83    @staticmethod
88    def _sort_tones(*tones):84    def _sort_tones(*tones):
89        root = tones[0]85        root = tones[0]
90        sorted_tones = [root]86        sorted_tones = [root]
91        end_idx = CHROMATIC_SCALE.index(str(root))87        end_idx = CHROMATIC_SCALE.index(str(root))
92        iter_idx = (end_idx + 1) % TONES_COUNT88        iter_idx = (end_idx + 1) % TONES_COUNT
9389
94        while iter_idx != end_idx:90        while iter_idx != end_idx:
n95            current_tone  = Tone(CHROMATIC_SCALE[iter_idx])n91            current_tone = Tone(CHROMATIC_SCALE[iter_idx])
96            if current_tone in tones and current_tone not in sorted_tones:92            if current_tone in tones and current_tone not in sorted_tones:
97                sorted_tones.append(current_tone)93                sorted_tones.append(current_tone)
n98            iter_idx+=1n94            iter_idx += 1
99            iter_idx %= TONES_COUNT95            iter_idx %= TONES_COUNT
100        return sorted_tones96        return sorted_tones
10197
102    def __str__(self):98    def __str__(self):
103        return '-'.join(str(tone) for tone in self.sorted_tones)99        return '-'.join(str(tone) for tone in self.sorted_tones)
104100
105    def is_minor(self):101    def is_minor(self):
106        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))102        root_idx = CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
107        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT103        end_tone_idx = (root_idx + MINOR_THIRD_LEN) % TONES_COUNT
108        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones104        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
109105
110    def is_major(self):106    def is_major(self):
111        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))107        root_idx =  CHROMATIC_SCALE.index(str(self.sorted_tones[0]))
112        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT108        end_tone_idx = (root_idx + MAJOR_THIRD_LEN) % TONES_COUNT
113        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones109        return Tone(CHROMATIC_SCALE[end_tone_idx]) in self.sorted_tones
114110
115    def is_power_chord(self):111    def is_power_chord(self):
116        return not self.is_minor() and not self.is_major()112        return not self.is_minor() and not self.is_major()
117113
118    def __add__(self, other):114    def __add__(self, other):
119        if isinstance(other, Tone):115        if isinstance(other, Tone):
120            new_tones = self.sorted_tones + [other]116            new_tones = self.sorted_tones + [other]
121            return Chord(*new_tones)117            return Chord(*new_tones)
122        elif isinstance(other, Chord):118        elif isinstance(other, Chord):
n123            new_tones = self.sorted_tones.copy()n119            new_tones = self.sorted_tones[:]
124            new_tones.extend(other.sorted_tones)120            new_tones.extend(other.sorted_tones)
125            return Chord(*new_tones)121            return Chord(*new_tones)
126122
127    def __sub__(self, other):123    def __sub__(self, other):
128        if isinstance(other, Tone):124        if isinstance(other, Tone):
129            if other not in self.sorted_tones:125            if other not in self.sorted_tones:
130                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')126                raise TypeError(f'Cannot remove tone {str(other)} from chord {str(self)}')
t131            new_tones = self.sorted_tones.copy()t127            new_tones = self.sorted_tones[:]
132            new_tones.remove(other)128            new_tones.remove(other)
133            return Chord(*new_tones)129            return Chord(*new_tones)
134130
135    def transposed(self, interval):131    def transposed(self, interval):
136        transpose_with = interval.semitones_count132        transpose_with = interval.semitones_count
137        transpose = lambda tone : Tone(CHROMATIC_SCALE[(CHROMATIC_SCALE.index(str(tone)) + transpose_with + TONES_COUNT) % TONES_COUNT])133        transpose = lambda tone : Tone(CHROMATIC_SCALE[(CHROMATIC_SCALE.index(str(tone)) + transpose_with + TONES_COUNT) % TONES_COUNT])
138        transposed_tones  = list(map(transpose, self.sorted_tones))134        transposed_tones  = list(map(transpose, self.sorted_tones))
139        return Chord(*transposed_tones)135        return Chord(*transposed_tones)
Legends
Colors
 Added 
Changed
Deleted
Links
(f)irst change
(n)ext change
(t)op