Skip to content

Commit

Permalink
fun with linter and docstrings; bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernd committed Jan 24, 2024
1 parent 44d2b8d commit 038703f
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 143 deletions.
4 changes: 0 additions & 4 deletions docs/reference.md

This file was deleted.

4 changes: 2 additions & 2 deletions tests/chromaticscale.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def test_standard_scale(self):

def test_conversion(self):
sc = ChromaticScale(anchor=('a4', 440))
dist = sc.SPN_to_distance('a4')
dist = sc.spn_to_distance('a4')
self.assertTrue( sc.frequencyof(dist) == 440)
self.assertTrue( sc.frequencyof(2*12+7) == 98.0)
self.assertTrue( sc.SPN_from_distance(dist) == 'a4')
self.assertTrue( sc.spn_from_distance(dist) == 'a4')


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions tests/corechromaticscale.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def test_standard_scale(self):

def test_conversion(self):
sc = _CoreChromaticScale(note=('a4', 440))
dist = sc.SPN_to_distance('a4')
dist = sc.spn_to_distance('a4')
self.assertTrue( sc.frequencyof(dist) == 440)
self.assertTrue( sc.frequencyof(2*12+7) == 98.0)
self.assertTrue( sc.SPN_from_distance(dist) == 'a4')
self.assertTrue( sc.spn_from_distance(dist) == 'a4')


if __name__ == '__main__':
Expand Down
1 change: 1 addition & 0 deletions tests/intervals.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def test_interval_note(self):
i = Interval(7)
n1 = Note('c4')
n2 = Note('g4')
self.assertTrue(7 == i)
self.assertTrue([n2,n1] == i)
self.assertTrue((n2,n1) == i)

Expand Down
100 changes: 62 additions & 38 deletions trallala/core/chords.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/usr/bin/env python3

""" Classes and methods used for Chords
"""

from functools import singledispatchmethod

from .scales import ChromaticScale, Scale
Expand All @@ -11,15 +14,40 @@


class Chord:
""" Chord class
Class usable to construct objects representing chords. Object supports the
generation of notes.Note objects as well as the conversion of the voicing.
"""


def __init__(self, chord, root, voicing=None,
chromaticscale=ChromaticScale()):
""" Creates a Chord object for 'root' and 'chord' (integer list).
'root' must be name of PitchClass (str) """
""" Instaciates a Chord object
Args:
chord:
Chord name from trallala.config_chords or list/ tuple of notes
or pitchclasses
root:
pitchclass name of the root note of the chord.
voicing:
tuple of octave numbers. len(voicing) must be equal to the amount
of notes in the resulting chord object.
chromaticscale:
Chromaticscale used for the calculation of the notes in this
chord
Raises:
ValueError:
Chord cannot be constructed from provided parameters
"""
self._scale = chromaticscale
self._root_index = self._scale.temperament.name_to_distance(root)
self._root_name = root
self._chord = self._dispatch_init(chord, root, voicing, chromaticscale)
self._chord = self._dispatch_init(chord)
if voicing:
self._voicing = tuple(voicing)
else:
Expand All @@ -30,23 +58,23 @@ def __init__(self, chord, root, voicing=None,

# prevent bug in singledispatchmethod https://bugs.python.org/issue41122
@singledispatchmethod
def _dispatch_init(self, chord, root, voicing, chromaticscale):
def _dispatch_init(self, chord):
raise ValueError(f"Chord() not implemented for type(chord)={type(chord)}")

@_dispatch_init.register
def _1(self, chord: str, root, voicing, chromaticscale):
return chord_integer[chromaticscale.temperament.length][chord]
def _1(self, chord: str):
return chord_integer[self._scale.temperament.length][chord]

@_dispatch_init.register(tuple)
@_dispatch_init.register(list)
def _2(self, chord , root, voicing, chromaticscale):
if type(chord[0]) is intervals.Interval:
def _2(self, chord):
if isinstance(chord[0], intervals.Interval):
c = self._chord_int_from_intervals(chord)
else:
c = chord
for x in c:
if type(x) is not int:
raise ValueError("Chord definition must be from predefined"
if not isinstance(x, int):
raise ValueError("Chord definition must be from predefined"
+ "chords or a list of integers "
+" (e.g., a major chord would be [0, 4, 7]")
return tuple(sorted(c))
Expand Down Expand Up @@ -77,14 +105,19 @@ def get_chord(self) -> list:
n = notes.Note(root + e, self._scale)
chord.append(n)
else:
for e in range(len(self._chord)):
n = self._chord[e] \
for e, note in enumerate(self._chord):
n = note \
+ self._scale.temperament.length * self._voicing[e]
chord.append( notes.Note(n, self._scale) )
return chord

def get_pitchclasses(self) -> list:
return tuple([notes.PitchClass(n) for n in self.get_chord()])
""" Pitchlasses in this chord
Returns:
tuple(pitchlass1..n)
"""
return tuple(notes.PitchClass(n) for n in self.get_chord())

def get_frequencies(self) -> list:
""" Returns the list of the frequencies of the chord.
Expand All @@ -105,7 +138,8 @@ def __getitem__(self,key):
return self.get_chord()[key]

def __str__(self):
return ", ".join(("/".join(self._scale.temperament.distance_to_name(x)) for x in self._chord))
return ", ".join(("/".join(self._scale.temperament.distance_to_name(x)) \
for x in self._chord))

@singledispatchmethod
def __add__(self, a):
Expand All @@ -117,10 +151,10 @@ def _1(self, a: notes.Note):
raise ValueError("Cannot add Note to Chord without set voicing")
d = a.distance % self._scale.temperament.length
o = int(a.distance / self._scale.temperament.length)
c = [x for x in self._chord]
c = list(self._chord)
if d not in c:
c.append(d)
v = [x for x in self._voicing]
v = list(self._voicing)
v.append(o)
return Chord(c, root=self._root_name, voicing=v,
chromaticscale=self._scale)
Expand All @@ -132,7 +166,7 @@ def _2(self, a: notes.PitchClass):
if a.numeric not in new_chord_int:
new_chord_int.append(a.numeric)
if self._voicing:
v = [x for x in self._voicing]
v = list(self._voicing)
v.append(v[0])
else:
v = None
Expand All @@ -141,7 +175,7 @@ def _2(self, a: notes.PitchClass):

@__add__.register
def _3(self, a: intervals.Interval):
new_chord_int = [x for x in self._chord]
new_chord_int = list(self._chord)
v = list(self._voicing[:])
if a.distance not in new_chord_int:
new_chord_int.append(a.distance)
Expand All @@ -155,32 +189,20 @@ def _3(self, a: intervals.Interval):

@singledispatchmethod
def __eq__(self,a):
if type(a) is Chord:
if isinstance(a, Chord):
return self.get_chord() == a.get_chord()
else:
raise ValueError(f'__eq__ not defined for type {type(a)}')
raise ValueError(f'__eq__ not defined for type {type(a)}')

@__eq__.register(tuple)
@__eq__.register(list)
def _1(self, a ):
isint = True
isinterval = True
isstr = True
for i in a:
if type(i) is not int:
isint = False
if type(i) is not intervals.Interval:
isinterval = False
if type(i) is not str:
isstr = False
if isint:
if min(isinstance(x,int) for x in a):
return self._chord == a
elif isinterval:
if min(isinstance(x, intervals.Interval) for x in a):
return self._chord == self._chord_int_from_intervals(a)
elif isstr:
if min(isinstance(x, str) for x in a):
return self.get_chord() == a
else:
raise ValueError("Chord can only compared to lists/tuple containing"
raise ValueError("Chord can only compared to lists/tuple containing"
+ " int or Interval. Mixed content is not supported")

@singledispatchmethod
Expand All @@ -207,7 +229,8 @@ def _2(self,a: notes.PitchClass):
@__contains__.register
def _3(self,a: intervals.Interval):
for i in range(len(self._chord)):
if self._chord[i] - self._chord[0]:
print( self._chord[i] - self._chord[0])
if a == self._chord[i] - self._chord[0]:
return True
return False

Expand All @@ -219,7 +242,8 @@ def _4(self,a: float):

@__contains__.register
def _5(self,a: int):
return self.__contains__(float(a))
# return self.__contains__(float(a))
return float(a) in self

def __iter__(self):
return self.get_chord().__iter__()
Expand Down
4 changes: 4 additions & 0 deletions trallala/core/intervals.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ def _1(self,a: list):
def _2(self, a: tuple):
return self == list(a)

@__eq__.register
def _3(self, a: int):
return self._distance == a

@singledispatchmethod
def __lt__(self,a):
if type(a) is Interval:
Expand Down
Loading

0 comments on commit 038703f

Please sign in to comment.