f | TONE_ORDER = ["C", "C#", "D", "D#", "E", | f | TONE_ORDER = ["C", "C#", "D", "D#", "E", |
| "F", "F#", "G", "G#", "A", "A#", "B"] | | "F", "F#", "G", "G#", "A", "A#", "B"] |
| TONE_ORDER_MAP = {tone: index for index, tone in enumerate(TONE_ORDER)} | | TONE_ORDER_MAP = {tone: index for index, tone in enumerate(TONE_ORDER)} |
| | | |
| | | |
| class Tone: | | class Tone: |
| | | |
| def __init__(self, tone): | | def __init__(self, tone): |
| self.name = tone | | self.name = tone |
| | | |
| 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.name == other.name | | return isinstance(other, Tone) and self.name == other.name |
| | | |
| def __hash__(self): | | def __hash__(self): |
| return hash(self.name) | | return hash(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): |
n | index = (TONE_ORDER_MAP[self.name]+other.number_of_semitones) % 12 | n | index = (TONE_ORDER_MAP[self.name] + other.number_of_semitones) % 12 |
| return Tone(TONE_ORDER[index]) | | return Tone(TONE_ORDER[index]) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
n | return Interval((12 + TONE_ORDER_MAP[self.name]-TONE_ORDER_MAP[other.name]) % 12) | n | return Interval((12 + TONE_ORDER_MAP[self.name] - TONE_ORDER_MAP[other.name]) % 12) |
| elif isinstance(other, Interval): | | elif isinstance(other, Interval): |
n | index = (12 + TONE_ORDER_MAP[self.name] - | n | index = (12 + TONE_ORDER_MAP[self.name] - other.number_of_semitones) % 12 |
| other.number_of_semitones) % 12 | | |
| return Tone(TONE_ORDER[index]) | | return Tone(TONE_ORDER[index]) |
| | | |
| | | |
| class Interval: | | class Interval: |
| intervals = {0: 'unison', | | intervals = {0: 'unison', |
| 1: 'minor 2nd', | | 1: 'minor 2nd', |
| 2: 'major 2nd', | | 2: 'major 2nd', |
| 3: 'minor 3rd', | | 3: 'minor 3rd', |
| 4: 'major 3rd', | | 4: 'major 3rd', |
| 5: 'perfect 4th', | | 5: 'perfect 4th', |
| 6: 'diminished 5th', | | 6: 'diminished 5th', |
| 7: 'perfect 5th', | | 7: 'perfect 5th', |
| 8: 'minor 6th', | | 8: 'minor 6th', |
| 9: 'major 6th', | | 9: 'major 6th', |
| 10: 'minor 7th', | | 10: 'minor 7th', |
| 11: 'major 7th'} | | 11: 'major 7th'} |
| | | |
| def __init__(self, number): | | def __init__(self, number): |
n | number = number % 12 | n | |
| self.number_of_semitones = number | | self.number_of_semitones = number % 12 |
| | | |
| def __str__(self): | | def __str__(self): |
| return self.intervals[self.number_of_semitones] | | return self.intervals[self.number_of_semitones] |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| if isinstance(other, Interval): | | if isinstance(other, Interval): |
| return Interval(self.number_of_semitones + other.number_of_semitones) | | return Interval(self.number_of_semitones + other.number_of_semitones) |
| elif isinstance(other, Tone): | | elif isinstance(other, Tone): |
| raise TypeError("Invalid operation") | | raise TypeError("Invalid operation") |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| raise TypeError("Invalid operation") | | raise TypeError("Invalid operation") |
| | | |
| def __neg__(self): | | def __neg__(self): |
| return Interval(12 - self.number_of_semitones) | | return Interval(12 - self.number_of_semitones) |
| | | |
| | | |
| class Chord: | | class Chord: |
| | | |
| def __init__(self, root, *args): | | def __init__(self, root, *args): |
| unique_tones = {root, *args} | | unique_tones = {root, *args} |
| | | |
| if len(unique_tones) < 2: | | if len(unique_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.main_tone = root | | self.main_tone = root |
| self.root_index = TONE_ORDER_MAP[root.name] | | self.root_index = TONE_ORDER_MAP[root.name] |
| | | |
| self.tones = sorted( | | self.tones = sorted( |
| unique_tones, | | unique_tones, |
| key=lambda tone: (TONE_ORDER_MAP[tone.name] - self.root_index) % len(TONE_ORDER)) | | key=lambda tone: (TONE_ORDER_MAP[tone.name] - self.root_index) % len(TONE_ORDER)) |
| | | |
| def __str__(self): | | def __str__(self): |
| return "-".join(tone.name for tone in self.tones) | | return "-".join(tone.name for tone in self.tones) |
| | | |
| def is_minor(self): | | def is_minor(self): |
| for tone in self.tones: | | for tone in self.tones: |
n | if (TONE_ORDER_MAP[tone.name]-self.root_index+12) % 12 == 3: | n | if (TONE_ORDER_MAP[tone.name] - self.root_index + 12) % 12 == 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: |
n | if (TONE_ORDER_MAP[tone.name]-self.root_index+12) % 12 == 4: | n | if (TONE_ORDER_MAP[tone.name] - self.root_index + 12) % 12 == 4: |
| return True | | return True |
| return False | | return False |
| | | |
| def is_power_chord(self): | | def is_power_chord(self): |
| return not self.is_major() and not self.is_minor() | | return not self.is_major() and not self.is_minor() |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| return Chord(self.main_tone, *self.tones, other) | | return Chord(self.main_tone, *self.tones, other) |
| elif isinstance(other, Chord): | | elif isinstance(other, Chord): |
| return Chord(self.main_tone, *self.tones, *other.tones) | | return Chord(self.main_tone, *self.tones, *other.tones) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| if other in self.tones: | | if other in self.tones: |
| new_tones = [tone for tone in self.tones if tone != other] | | new_tones = [tone for tone in self.tones if tone != other] |
n | if other == self.main_tone: | n | new_main_tone = self.main_tone if self.main_tone != other else new_tones[0] |
| self.main_tone = new_tones[0] | | |
| return Chord(self.main_tone, *new_tones) | | return Chord(new_main_tone, *new_tones) |
| else: | | else: |
| raise TypeError( | | raise TypeError( |
| f'Cannot remove tone {other} from chord {self}') | | f'Cannot remove tone {other} from chord {self}') |
| | | |
| def transposed(self, interval): | | def transposed(self, interval): |
t | newRoot = self.main_tone + interval | t | new_root = self.main_tone + interval |
| newTones = tuple(tone + interval for tone in self.tones) | | new_tones = tuple(tone + interval for tone in self.tones) |
| return Chord(newRoot, *newTones) | | return Chord(new_root, *new_tones) |
| | | |
| | | |
| | | |