f | TONES_COUNT = 12 | f | TONES_COUNT = 12 |
| 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'] |
| | | |
| class Tone: | | class Tone: |
| """Musical tone""" | | """Musical tone""" |
| def __init__(self, tone): | | def __init__(self, tone): |
| self.tone = tone | | self.tone = tone |
| if tone == 'C': | | if tone == 'C': |
| self.position = 0 | | self.position = 0 |
| elif tone == 'C#': | | elif tone == 'C#': |
| self.position = 1 | | self.position = 1 |
| elif tone == 'D': | | elif tone == 'D': |
| self.position = 2 | | self.position = 2 |
| elif tone == 'D#': | | elif tone == 'D#': |
| self.position = 3 | | self.position = 3 |
| elif tone == 'E': | | elif tone == 'E': |
| self.position = 4 | | self.position = 4 |
| elif tone == 'F': | | elif tone == 'F': |
| self.position = 5 | | self.position = 5 |
| elif tone == 'F#': | | elif tone == 'F#': |
| self.position = 6 | | self.position = 6 |
| elif tone == 'G': | | elif tone == 'G': |
| self.position = 7 | | self.position = 7 |
| elif tone == 'G#': | | elif tone == 'G#': |
| self.position = 8 | | self.position = 8 |
| elif tone == 'A': | | elif tone == 'A': |
| self.position = 9 | | self.position = 9 |
| elif tone == 'A#': | | elif tone == 'A#': |
| self.position = 10 | | self.position = 10 |
| elif tone == 'B': | | elif tone == 'B': |
| self.position = 11 | | self.position = 11 |
| | | |
| def __str__(self): | | def __str__(self): |
| return str(self.tone) | | return str(self.tone) |
| | | |
| def __eq__(self, other): | | def __eq__(self, other): |
| return self.tone == other.tone | | return self.tone == other.tone |
| | | |
| def __int__(self): | | def __int__(self): |
| return self.position | | return self.position |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| return Chord(self, other) | | return Chord(self, other) |
| if isinstance(other, Interval): | | if isinstance(other, Interval): |
| return Tone(TONES[(self.position + other.semitones) % TONES_COUNT]) | | return Tone(TONES[(self.position + other.semitones) % TONES_COUNT]) |
| return Tone(self.tone) | | return Tone(self.tone) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| if self.position < other.position: | | if self.position < other.position: |
| return Interval(self.position + TONES_COUNT - other.position) | | return Interval(self.position + TONES_COUNT - other.position) |
| return Interval(self.position - other.position) | | return Interval(self.position - other.position) |
| if isinstance(other, Interval): | | if isinstance(other, Interval): |
| if self.position < other.semitones: | | if self.position < other.semitones: |
| return Tone(TONES[TONES_COUNT + self.position - other.semitones]) | | return Tone(TONES[TONES_COUNT + self.position - other.semitones]) |
| return Tone(TONES[self.position - other.semitones]) | | return Tone(TONES[self.position - other.semitones]) |
| return Tone(self.tone) | | return Tone(self.tone) |
| | | |
| | | |
| class Interval: | | class Interval: |
| """Musical interval, measured in semitones.""" | | """Musical interval, measured in semitones.""" |
| def __init__(self, semitones): | | def __init__(self, semitones): |
| self.semitones = semitones % 12 | | self.semitones = semitones % 12 |
| self.is_negative = False # Chord.transposed function uses it | | self.is_negative = False # Chord.transposed function uses it |
| | | |
| if self.semitones == 0: | | if self.semitones == 0: |
| self.name = 'unison' | | self.name = 'unison' |
| elif self.semitones == 1: | | elif self.semitones == 1: |
| self.name = 'minor 2nd' | | self.name = 'minor 2nd' |
| elif self.semitones == 2: | | elif self.semitones == 2: |
| self.name = 'major 2nd' | | self.name = 'major 2nd' |
| elif self.semitones == 3: | | elif self.semitones == 3: |
| self.name = 'minor 3rd' | | self.name = 'minor 3rd' |
| elif self.semitones == 4: | | elif self.semitones == 4: |
| self.name = 'major 3rd' | | self.name = 'major 3rd' |
| elif self.semitones == 5: | | elif self.semitones == 5: |
| self.name = 'perfect 4th' | | self.name = 'perfect 4th' |
| elif self.semitones == 6: | | elif self.semitones == 6: |
| self.name = 'diminished 5th' | | self.name = 'diminished 5th' |
| elif self.semitones == 7: | | elif self.semitones == 7: |
| self.name = 'perfect 5th' | | self.name = 'perfect 5th' |
| elif self.semitones == 8: | | elif self.semitones == 8: |
| self.name = 'minor 6th' | | self.name = 'minor 6th' |
| elif self.semitones == 9: | | elif self.semitones == 9: |
| self.name = 'major 6th' | | self.name = 'major 6th' |
| elif self.semitones == 10: | | elif self.semitones == 10: |
| self.name = 'minor 7th' | | self.name = 'minor 7th' |
| elif self.semitones == 11: | | elif self.semitones == 11: |
| self.name = 'major 7th' | | self.name = 'major 7th' |
| | | |
| def __str__(self): | | def __str__(self): |
| return self.name | | return self.name |
| | | |
| def __int__(self): | | def __int__(self): |
| return self.semitones | | return self.semitones |
| | | |
| def __add__(self, other): | | def __add__(self, other): |
| if isinstance(other, Tone): | | if isinstance(other, Tone): |
| raise TypeError('Invalid operation') | | raise TypeError('Invalid operation') |
| return Interval(self.semitones + other.semitones) | | return Interval(self.semitones + other.semitones) |
| | | |
| 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): |
| self.is_negative = True | | self.is_negative = True |
| return self | | return self |
| | | |
| | | |
| class Chord: | | class Chord: |
| """Musical chord made up of 2 or more unique tones.""" | | """Musical chord made up of 2 or more unique tones.""" |
| def __init__(self, root_tone, *other_tones): | | def __init__(self, root_tone, *other_tones): |
| self.root_tone = root_tone | | self.root_tone = root_tone |
| tones = {self.root_tone.tone : self.root_tone.position} | | tones = {self.root_tone.tone : self.root_tone.position} |
| self.tones = [self.root_tone] | | self.tones = [self.root_tone] |
| | | |
| # remove duplicates | | # remove duplicates |
| for tone in other_tones: | | for tone in other_tones: |
| if tone.tone not in tones: | | if tone.tone not in tones: |
| tones[tone.tone] = tone.position | | tones[tone.tone] = tone.position |
| self.tones.append(tone) | | self.tones.append(tone) |
| | | |
| if self.tones.__len__() == 1: | | if self.tones.__len__() == 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') |
| | | |
| # order relative to root | | # order relative to root |
| tones = [self.root_tone] | | tones = [self.root_tone] |
| position_list = list(map(int, self.tones)) | | position_list = list(map(int, self.tones)) |
| for i in range(self.root_tone.position + 1, self.root_tone.position + TONES_COUNT): | | for i in range(self.root_tone.position + 1, self.root_tone.position + TONES_COUNT): |
| if i % TONES_COUNT in position_list: | | if i % TONES_COUNT in position_list: |
| tones.append(self.tones[position_list.index(i % TONES_COUNT)]) | | tones.append(self.tones[position_list.index(i % TONES_COUNT)]) |
| self.tones = tones | | self.tones = tones |
| | | |
| def __str__(self): | | def __str__(self): |
| return '-'.join(map(str, self.tones)) | | return '-'.join(map(str, self.tones)) |
| | | |
| def is_minor(self): | | def is_minor(self): |
| """Return whether there are 3 semitones between the root | | """Return whether there are 3 semitones between the root |
| and any other tone in the chord.""" | | and any other tone in the chord.""" |
| for tone in self.tones: | | for tone in self.tones: |
| if (tone is not self.root_tone | | if (tone is not self.root_tone |
n | and abs(self.root_tone.position - tone.position) == 3): | n | and self.root_tone + Interval(3) == tone): |
| return True | | return True |
| return False | | return False |
| | | |
| def is_major(self): | | def is_major(self): |
| """Return whether there are 4 semitones between the root | | """Return whether there are 4 semitones between the root |
| and any other tone in the chord.""" | | and any other tone in the chord.""" |
| for tone in self.tones: | | for tone in self.tones: |
| if (tone is not self.root_tone | | if (tone is not self.root_tone |
t | and abs(self.root_tone.position - tone.position) == 4): | t | and self.root_tone + Interval(4) == tone): |
| return True | | return True |
| return False | | return False |
| | | |
| def is_power_chord(self): | | def is_power_chord(self): |
| """Return whether chord is neither minor nor major.""" | | """Return whether chord is neither minor nor major.""" |
| 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): |
| tones = self.tones[:] | | tones = self.tones[:] |
| tones.append(other) | | tones.append(other) |
| return Chord(*tones) | | return Chord(*tones) |
| if isinstance(other, Chord): | | if isinstance(other, Chord): |
| tones = self.tones[:] | | tones = self.tones[:] |
| tones.extend(other.tones) | | tones.extend(other.tones) |
| return Chord(*tones) | | return Chord(*tones) |
| return Chord(*self.tones) | | return Chord(*self.tones) |
| | | |
| def __sub__(self, other): | | def __sub__(self, other): |
| if other in self.tones: | | if other in self.tones: |
| tones = self.tones[:] | | tones = self.tones[:] |
| tones.remove(other) | | tones.remove(other) |
| return Chord(*tones) | | return Chord(*tones) |
| raise TypeError(f'Cannot remove tone {other} from chord {self}') | | raise TypeError(f'Cannot remove tone {other} from chord {self}') |
| | | |
| def transposed(self, interval): | | def transposed(self, interval): |
| """Subtract or add an interval to all of chord's tones.""" | | """Subtract or add an interval to all of chord's tones.""" |
| tones = [tone - interval if interval.is_negative | | tones = [tone - interval if interval.is_negative |
| else tone + interval for tone in self.tones] | | else tone + interval for tone in self.tones] |
| return Chord(*tones) | | return Chord(*tones) |
07.11.2024 10:44
07.11.2024 10:45
07.11.2024 10:46
07.11.2024 10:47
07.11.2024 10:48
07.11.2024 10:50
07.11.2024 10:51
07.11.2024 10:52