| n | tones_list = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | n | TONES = ('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B') | 
             |  |  | INTERVAL_NAMES = ('unison', 'minor 2nd', 'major 2nd', 'minor 3rd', 'major 3rd', 'perfect 4th', 'diminished 5th', 'perfect 5th', 'minor 6th', 'major 6th', 'minor 7th', 'major 7th') | 
             | tone_to_index = {tone: index for index, tone in enumerate(tones_list)} |  | tone_to_index = {tone: index for index, tone in enumerate(TONES)} | 
             | index_to_tone = {index: tone for index, tone in enumerate(tones_list)} |  | index_to_tone = {index: tone for index, tone in enumerate(TONES)} | 
             |   |  |  | 
             | interval_names = { |  |  | 
             |     0: 'unison', |  |  | 
             |     1: 'minor 2nd', |  |  | 
             |     2: 'major 2nd', |  |  | 
             |     3: 'minor 3rd', |  |  | 
             |     4: 'major 3rd', |  |  | 
             |     5: 'perfect 4th', |  |  | 
             |     6: 'diminished 5th', |  |  | 
             |     7: 'perfect 5th', |  |  | 
             |     8: 'minor 6th', |  |  | 
             |     9: 'major 6th', |  |  | 
             |     10: 'minor 7th', |  |  | 
             |     11: 'major 7th' |  |  | 
             | } |  |  | 
             |  |  |  | 
             |  |  |  | 
             | class Tone: |  | class Tone: | 
             |     def __init__(self, name): |  |     def __init__(self, name): | 
             |         if name not in tone_to_index: |  |         if name not in tone_to_index: | 
             |             raise ValueError(f"Invalid tone name: {name}") |  |             raise ValueError(f"Invalid tone name: {name}") | 
             |         self.name = name |  |         self.name = name | 
             |         self.index = tone_to_index[name] |  |         self.index = tone_to_index[name] | 
             |  |  |  | 
             |     def __str__(self): |  |     def __str__(self): | 
             |         return self.name |  |         return self.name | 
             |  |  |  | 
             |     def __eq__(self, other): |  |     def __eq__(self, other): | 
             |         return isinstance(other, Tone) and self.index == other.index |  |         return isinstance(other, Tone) and self.index == other.index | 
             |  |  |  | 
             |     def __hash__(self): # We need it for sorting |  |     def __hash__(self): # We need it for sorting | 
             |         return hash(self.index) |  |         return hash(self.index) | 
             |  |  |  | 
             |     def __add__(self, other): |  |     def __add__(self, other): | 
             |         if isinstance(other, Tone): |  |         if isinstance(other, Tone): | 
             |             return Chord(self, other) |  |             return Chord(self, other) | 
             |         elif isinstance(other, Interval): |  |         elif isinstance(other, Interval): | 
             |             new_index = (self.index + other.semitones) % 12 |  |             new_index = (self.index + other.semitones) % 12 | 
             |             new_name = index_to_tone[new_index] |  |             new_name = index_to_tone[new_index] | 
             |             return Tone(new_name) |  |             return Tone(new_name) | 
             |         else: |  |         else: | 
             |             raise TypeError("Invalid operation") |  |             raise TypeError("Invalid operation") | 
             |  |  |  | 
             |     def __sub__(self, other): |  |     def __sub__(self, other): | 
             |         if isinstance(other, Tone): |  |         if isinstance(other, Tone): | 
             |             semitones = (self.index - other.index) % 12 |  |             semitones = (self.index - other.index) % 12 | 
             |             return Interval(semitones) |  |             return Interval(semitones) | 
             |         elif isinstance(other, Interval): |  |         elif isinstance(other, Interval): | 
             |             new_index = (self.index - other.semitones) % 12 |  |             new_index = (self.index - other.semitones) % 12 | 
             |             new_name = index_to_tone[new_index] |  |             new_name = index_to_tone[new_index] | 
             |             return Tone(new_name) |  |             return Tone(new_name) | 
             |         else: |  |         else: | 
             |             raise TypeError("Invalid operation") |  |             raise TypeError("Invalid operation") | 
             |  |  |  | 
             |  |  |  | 
             | class Interval: |  | class Interval: | 
             |     def __init__(self, semitones): |  |     def __init__(self, semitones): | 
             |         self.semitones = semitones |  |         self.semitones = semitones | 
             |  |  |  | 
             |     def __str__(self): |  |     def __str__(self): | 
             |         semitones_mod = self.semitones % 12 |  |         semitones_mod = self.semitones % 12 | 
            | n |         name = interval_names[semitones_mod] | n |         name = INTERVAL_NAMES[semitones_mod] | 
             |         return name |  |         return name | 
             |  |  |  | 
             |     def __add__(self, other): |  |     def __add__(self, other): | 
             |         if isinstance(other, Interval): |  |         if isinstance(other, Interval): | 
             |             total_semitones = self.semitones + other.semitones |  |             total_semitones = self.semitones + other.semitones | 
             |             return Interval(total_semitones) |  |             return Interval(total_semitones) | 
             |         else: |  |         else: | 
             |             raise TypeError("Invalid operation") |  |             raise TypeError("Invalid operation") | 
             |  |  |  | 
             |     def __neg__(self): |  |     def __neg__(self): | 
             |         # need it for a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4)) case |  |         # need it for a_sharp_minor_chord = d_minor_chord.transposed(-Interval(4)) case | 
             |         return Interval(-self.semitones) |  |         return Interval(-self.semitones) | 
             |  |  |  | 
             |  |  |  | 
             | class Chord: |  | class Chord: | 
             |     def __init__(self, root, *tones): |  |     def __init__(self, root, *tones): | 
             |         if not isinstance(root, Tone): |  |         if not isinstance(root, Tone): | 
             |             raise TypeError("Root must be a Tone") |  |             raise TypeError("Root must be a Tone") | 
             |         self.root = root |  |         self.root = root | 
             |         all_tones = {root} |  |         all_tones = {root} | 
             |         for tone in tones: |  |         for tone in tones: | 
             |             if not isinstance(tone, Tone): |  |             if not isinstance(tone, Tone): | 
             |                 raise TypeError("All tones must be Tone instances") |  |                 raise TypeError("All tones must be Tone instances") | 
             |             all_tones.add(tone) |  |             all_tones.add(tone) | 
             |         if len(all_tones) < 2: |  |         if len(all_tones) < 2: | 
             |             raise TypeError("Cannot have a chord made of only 1 unique tone") |  |             raise TypeError("Cannot have a chord made of only 1 unique tone") | 
             |         self.tones = all_tones |  |         self.tones = all_tones | 
             |  |  |  | 
             |     def sort_tone(self, tone): |  |     def sort_tone(self, tone): | 
            | n |  | n |         if tone.index >= self.root.index: | 
             |         return (tone.index - self.root.index) % 12 |  |             return tone.index - self.root.index | 
             |  |  |   | 
             |  |  |         return tone.index + (12 - self.root.index) | 
             |  |  |  | 
             |     def __str__(self): |  |     def __str__(self): | 
            | n |  | n |         tones_copy = self.tones.copy() | 
             |  |  |         tones_copy.remove(self.root) | 
             |         sorted_tones = sorted(self.tones, key=self.sort_tone) |  |         sorted_tones = sorted(tones_copy, key=self.sort_tone) | 
             |  |  |   | 
             |  |  |         sorted_tones = [self.root] + list(sorted_tones) | 
             |         return '-'.join(str(tone) for tone in sorted_tones) |  |         return '-'.join(str(tone) for tone in sorted_tones) | 
             |  |  |  | 
             |     def is_minor(self): |  |     def is_minor(self): | 
             |         for tone in self.tones: |  |         for tone in self.tones: | 
             |             if tone == self.root: |  |             if tone == self.root: | 
             |                 continue |  |                 continue | 
             |             interval = (tone.index - self.root.index) % 12 |  |             interval = (tone.index - self.root.index) % 12 | 
             |             if interval == 3: |  |             if interval == 3: | 
             |                 return True |  |                 return True | 
             |         return False |  |         return False | 
             |  |  |  | 
             |     def is_major(self): |  |     def is_major(self): | 
             |         for tone in self.tones: |  |         for tone in self.tones: | 
             |             if tone == self.root: |  |             if tone == self.root: | 
             |                 continue |  |                 continue | 
             |             interval = (tone.index - self.root.index) % 12 |  |             interval = (tone.index - self.root.index) % 12 | 
             |             if interval == 4: |  |             if interval == 4: | 
             |                 return True |  |                 return True | 
             |         return False |  |         return False | 
             |  |  |  | 
             |     def is_power_chord(self): |  |     def is_power_chord(self): | 
             |         return not self.is_minor() and not self.is_major() |  |         return not self.is_minor() and not self.is_major() | 
             |  |  |  | 
             |     def __add__(self, other): |  |     def __add__(self, other): | 
             |         if isinstance(other, Tone): |  |         if isinstance(other, Tone): | 
             |             new_tones = self.tones.copy() |  |             new_tones = self.tones.copy() | 
             |             new_tones.add(other) |  |             new_tones.add(other) | 
             |             return Chord(self.root, *(new_tones - {self.root})) |  |             return Chord(self.root, *(new_tones - {self.root})) | 
             |         elif isinstance(other, Chord): |  |         elif isinstance(other, Chord): | 
             |             new_tones = self.tones.union(other.tones) |  |             new_tones = self.tones.union(other.tones) | 
             |             return Chord(self.root, *(new_tones - {self.root})) |  |             return Chord(self.root, *(new_tones - {self.root})) | 
             |         else: |  |         else: | 
             |             raise TypeError("Invalid operation") |  |             raise TypeError("Invalid operation") | 
             |  |  |  | 
             |     def __sub__(self, other): |  |     def __sub__(self, other): | 
             |         if not isinstance(other, Tone): |  |         if not isinstance(other, Tone): | 
             |             raise TypeError("Invalid operation") |  |             raise TypeError("Invalid operation") | 
             |         if other not in self.tones: |  |         if other not in self.tones: | 
             |             raise TypeError(f"Cannot remove tone {other} from chord {self}") |  |             raise TypeError(f"Cannot remove tone {other} from chord {self}") | 
             |         new_tones = self.tones.copy() |  |         new_tones = self.tones.copy() | 
             |         new_tones.remove(other) |  |         new_tones.remove(other) | 
             |         if len(new_tones) < 2: |  |         if len(new_tones) < 2: | 
             |             raise TypeError("Cannot have a chord made of only 1 unique tone") |  |             raise TypeError("Cannot have a chord made of only 1 unique tone") | 
            | t |  | t |         new_root = self.root | 
             |  |  |         if other == self.root: | 
             |  |  |             new_root = next(iter(new_tones)) | 
             |  |  |             new_tones.remove(new_root) | 
             |  |  |   | 
             |         return Chord(self.root, *(new_tones - {self.root})) |  |         return Chord(new_root, *(new_tones - {self.root})) | 
             |  |  |  | 
             |     def transposed(self, interval): |  |     def transposed(self, interval): | 
             |         if not isinstance(interval, Interval): |  |         if not isinstance(interval, Interval): | 
             |             raise TypeError("Interval must be an Interval instance") |  |             raise TypeError("Interval must be an Interval instance") | 
             |         semitones = interval.semitones |  |         semitones = interval.semitones | 
             |         new_tones = set() |  |         new_tones = set() | 
             |         for tone in self.tones: |  |         for tone in self.tones: | 
             |             new_index = (tone.index + semitones) % 12 |  |             new_index = (tone.index + semitones) % 12 | 
             |             new_tone = Tone(index_to_tone[new_index]) |  |             new_tone = Tone(index_to_tone[new_index]) | 
             |             new_tones.add(new_tone) |  |             new_tones.add(new_tone) | 
             |         new_root_index = (self.root.index + semitones) % 12 |  |         new_root_index = (self.root.index + semitones) % 12 | 
             |         new_root = Tone(index_to_tone[new_root_index]) |  |         new_root = Tone(index_to_tone[new_root_index]) | 
             |         return Chord(new_root, *(new_tones - {new_root})) |  |         return Chord(new_root, *(new_tones - {new_root})) | 
        
     
06.11.2024 14:23
06.11.2024 14:23