t | class Tone: | t | class Tone: |
| TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] | | TONES = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"] |
| | | |
| def __init__(self, name): | | def __init__(self, name): |
| if name not in self.TONES: | | if name not in self.TONES: |
| raise ValueError(f"Invalid tone name: {name}") | | raise ValueError(f"Invalid tone name: {name}") |
| self.name = name | | self.name = name |
| | | |
| def __str__(self): | | def __str__(self): |
| return self.name | | return self.name |
| | | |
| 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): |
| | | |
| curr_index = self.TONES.index(self.name) | | curr_index = self.TONES.index(self.name) |
| new_index = (curr_index + other.semitones) % 12 | | new_index = (curr_index + other.semitones) % 12 |
| new_name = self.TONES[new_index] | | new_name = self.TONES[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): |
| curr_index = self.TONES.index(self.name) | | curr_index = self.TONES.index(self.name) |
| other_index = other.TONES.index(other.name) | | other_index = other.TONES.index(other.name) |
| semitones = (curr_index - other_index) % 12 | | semitones = (curr_index - other_index) % 12 |
| return Interval(semitones) | | return Interval(semitones) |
| | | |
| elif isinstance(other, Interval): | | elif isinstance(other, Interval): |
| curr_index = self.TONES.index(self.name) | | curr_index = self.TONES.index(self.name) |
| new_index = (curr_index - other.semitones) % 12 | | new_index = (curr_index - other.semitones) % 12 |
| new_name = self.TONES[new_index] | | new_name = self.TONES[new_index] |
| return Tone(new_name) | | return Tone(new_name) |
| | | |
| else: | | else: |
| raise TypeError("Invalid operation") | | raise TypeError("Invalid operation") |
| | | |
| def __eq__(self, other): | | def __eq__(self, other): |
| | | |
| return isinstance(other, Tone) and self.name == other.name | | return isinstance(other, Tone) and self.name == other.name |
| | | |
| def __ne__(self, other): | | def __ne__(self, other): |
| return not self.__eq__(other) | | return not self.__eq__(other) |
| | | |
| | | |
| class Interval: | | class Interval: |
| INTERVALS = [ | | INTERVALS = [ |
| "unison", "minor 2nd", "major 2nd", "minor 3rd", | | "unison", "minor 2nd", "major 2nd", "minor 3rd", |
| "major 3rd", "perfect 4th", "diminished 5th", | | "major 3rd", "perfect 4th", "diminished 5th", |
| "perfect 5th", "minor 6th", "major 6th", | | "perfect 5th", "minor 6th", "major 6th", |
| "minor 7th", "major 7th" | | "minor 7th", "major 7th" |
| ] | | ] |
| | | |
| def __init__(self, number_of_semitones): | | def __init__(self, number_of_semitones): |
| if isinstance(number_of_semitones, int): | | if isinstance(number_of_semitones, int): |
| self.semitones = number_of_semitones % 12 | | self.semitones = number_of_semitones % 12 |
| else: | | else: |
| raise ValueError(f"Invalid number of semitones") | | raise ValueError(f"Invalid number of semitones") |
| | | |
| def __str__(self): | | def __str__(self): |
| return self.INTERVALS[self.semitones] | | return self.INTERVALS[self.semitones] |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| if isinstance(other, Interval): | | if isinstance(other, Interval): |
| return Interval(self.semitones + other.semitones) | | return Interval(self.semitones + other.semitones) |
| raise TypeError("Invalid operation") | | raise TypeError("Invalid operation") |
| | | |
| def __neg__(self): | | def __neg__(self): |
| return Interval(-self.semitones) | | return Interval(-self.semitones) |
| | | |
| | | |
| class Chord: | | class Chord: |
| def __init__(self, root, *args): | | def __init__(self, root, *args): |
| if isinstance(root, Tone): | | if isinstance(root, Tone): |
| for tone in args: | | for tone in args: |
| if not isinstance(tone, Tone): | | if not isinstance(tone, Tone): |
| raise ValueError("All arguments must of type Tone") | | raise ValueError("All arguments must of type Tone") |
| self.root = root | | self.root = root |
| self.tones = [] | | self.tones = [] |
| self.tones.append(root) | | self.tones.append(root) |
| for tone in args: | | for tone in args: |
| if tone not in self.tones: | | if tone not in self.tones: |
| self.tones.append(tone) | | self.tones.append(tone) |
| else: | | else: |
| raise ValueError("Root must be type Tone") | | raise ValueError("Root must be type Tone") |
| | | |
| if len(self.tones) < 2: | | if len(self.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") |
| | | |
| def __str__(self): | | def __str__(self): |
| | | |
| tones_no_root = [tone for tone in self.tones if tone != self.root] | | tones_no_root = [tone for tone in self.tones if tone != self.root] |
| root_index = Tone.TONES.index(self.root.name) | | root_index = Tone.TONES.index(self.root.name) |
| | | |
| def tone_sort_key(tone): | | def tone_sort_key(tone): |
| cur_index = Tone.TONES.index(tone.name) | | cur_index = Tone.TONES.index(tone.name) |
| return (cur_index - root_index) % 12 | | return (cur_index - root_index) % 12 |
| | | |
| sorted_tones = [self.root] + sorted(tones_no_root, key=tone_sort_key) | | sorted_tones = [self.root] + sorted(tones_no_root, key=tone_sort_key) |
| 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): |
| root_index = Tone.TONES.index(self.root.name) | | root_index = Tone.TONES.index(self.root.name) |
| for tone in self.tones: | | for tone in self.tones: |
| if tone == self.root: | | if tone == self.root: |
| continue | | continue |
| cur_index = Tone.TONES.index(tone.name) | | cur_index = Tone.TONES.index(tone.name) |
| interval = (cur_index - root_index) % 12 | | interval = (cur_index - 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): |
| root_index = Tone.TONES.index(self.root.name) | | root_index = Tone.TONES.index(self.root.name) |
| for tone in self.tones: | | for tone in self.tones: |
| if tone == self.root: | | if tone == self.root: |
| continue | | continue |
| cur_index = Tone.TONES.index(tone.name) | | cur_index = Tone.TONES.index(tone.name) |
| interval = (cur_index - root_index) % 12 | | interval = (cur_index - 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() |
| if other not in new_tones: | | if other not in new_tones: |
| new_tones.append(other) | | new_tones.append(other) |
| return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) | | return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) |
| elif isinstance(other, Chord): | | elif isinstance(other, Chord): |
| new_tones = list(self.tones) | | new_tones = list(self.tones) |
| for tone in other.tones: | | for tone in other.tones: |
| if tone not in self.tones: | | if tone not in self.tones: |
| new_tones.append(tone) | | new_tones.append(tone) |
| return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) | | return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) |
| 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): |
| 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}") |
| if other == self.root: | | if other == self.root: |
| raise TypeError(f"Cannot remove tone {other} from chord {self}") | | raise TypeError(f"Cannot remove tone {other} from chord {self}") |
| new_tones = [tone for tone in self.tones if tone != other] | | new_tones = [tone for tone in self.tones if tone != other] |
| if len(new_tones) <= 1: | | if len(new_tones) <= 1: |
| raise TypeError("Cannot have a chord made of only 1 unique tone") | | raise TypeError("Cannot have a chord made of only 1 unique tone") |
| return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) | | return Chord(self.root, *[tone for tone in new_tones if tone != self.root]) |
| else: | | else: |
| raise TypeError("Invalid operation") | | raise TypeError("Invalid operation") |
| | | |
| def transposed(self, interval): | | def transposed(self, interval): |
| if not isinstance(interval, Interval): | | if not isinstance(interval, Interval): |
| raise TypeError("Interval expected") | | raise TypeError("Interval expected") |
| new_tones = [] | | new_tones = [] |
| root_index = Tone.TONES.index(self.root.name) | | root_index = Tone.TONES.index(self.root.name) |
| for tone in self.tones: | | for tone in self.tones: |
| cur_index = Tone.TONES.index(tone.name) | | cur_index = Tone.TONES.index(tone.name) |
| new_index = (cur_index + interval.semitones) % 12 | | new_index = (cur_index + interval.semitones) % 12 |
| new_name = Tone.TONES[new_index] | | new_name = Tone.TONES[new_index] |
| new_tone = Tone(new_name) | | new_tone = Tone(new_name) |
| if new_tone not in new_tones: | | if new_tone not in new_tones: |
| new_tones.append(new_tone) | | new_tones.append(new_tone) |
| new_root_index = (root_index + interval.semitones) % 12 | | new_root_index = (root_index + interval.semitones) % 12 |
| new_root_name = Tone.TONES[new_root_index] | | new_root_name = Tone.TONES[new_root_index] |
| new_root = Tone(new_root_name) | | new_root = Tone(new_root_name) |
| return Chord(new_root, *[tone for tone in new_tones if tone != new_root]) | | return Chord(new_root, *[tone for tone in new_tones if tone != new_root]) |
| | | |
07.11.2024 11:57
07.11.2024 12:01
07.11.2024 11:59
07.11.2024 11:59
07.11.2024 12:01
07.11.2024 12:02
07.11.2024 12:02
07.11.2024 12:04