-
Notifications
You must be signed in to change notification settings - Fork 0
/
datastructures.py
119 lines (106 loc) · 4.33 KB
/
datastructures.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from constants import ENCR_NOTES, DECR_FLAT_NOTES, DECR_SHARP_NOTES
class Note:
def __init__(self, annotation):
self.annotation = annotation
self.encr = ENCR_NOTES[annotation]
self.sign = None
if len(annotation) == 2:
self.sign = annotation[1]
def __str__(self):
return self.annotation
def __repr__(self):
return self.annotation
def __eq__(self, other):
return self.encr == other.encr
def stack_semitones(self, num_semitones, sign = None):
if sign == "#":
decr = DECR_SHARP_NOTES
elif sign == "b":
decr = DECR_FLAT_NOTES
else:
if self.sign:
decr = DECR_SHARP_NOTES if self.sign == "#" else DECR_FLAT_NOTES
else:
decr = DECR_SHARP_NOTES if num_semitones >= 0 else DECR_FLAT_NOTES # FIXME
return Note(decr[self.encr + num_semitones])
def distance_to_note(self, note):
interval = (note.encr - self.encr) % 12
return interval
class ScaleInfo:
def __init__(self, thirds, beginning):
self.sign = None
for third in thirds:
if third.sign:
self.sign = third.sign
self.coherent_maj_thirds = thirds
self.incoherent_maj_thirds = set()
self.beginning = beginning
self.end = None
self.min_root = None
if len(thirds) == 3:
self.maj_root = thirds[1]
else:
self.maj_root = None
self.mode = None
def __repr__(self):
return (f"(maj_root: {self.maj_root}, min_root: {self.min_root}, mode: {self.mode}, "
f"coherent major thirds: {self.coherent_maj_thirds}, "
f"incoherent major thirds: {self.incoherent_maj_thirds}, "
f"beginning: {self.beginning}, end: {self.end})")
def _is_match(self, new_third):
if self.incoherent_maj_thirds:
lower_notes_thirds = self.incoherent_maj_thirds
for first, second in itertools.combinations(self.incoherent_maj_thirds, 2):
fifth_stacking = stack_as_fifth(first, second, new_third)
if fifth_stacking:
return fifth_stacking
return None
elif self.coherent_maj_thirds:
lower_notes_thirds = self.coherent_maj_thirds
num_thirds = len(lower_notes_thirds)
if num_thirds == 1:
old = lower_notes_thirds[0]
interval = old.distance_to_note(new_third)
if interval == 2:
scale_root = old.stack_semitones(7)
stacked_lower_notes = [old, scale_root, new_third]
elif interval == 10:
scale_root = new_third.stack_semitones(7)
stacked_lower_notes = [new_third, scale_root, old]
elif interval == 7:
stacked_lower_notes = [old, new_third]
elif interval == 5:
stacked_lower_notes = [new_third, old]
else:
stacked_lower_notes = None
return stacked_lower_notes
elif num_thirds == 2:
lower = lower_notes_thirds[0]
higher = lower_notes_thirds[1]
if new_third.distance_to_note(lower) == 7:
stacked_lower_notes = [new_third] + lower_notes_thirds
elif higher.distance_to_note(new_third) == 7:
stacked_lower_notes = lower_notes_thirds + [new_third]
else:
stacked_lower_notes = None
return stacked_lower_notes
else:
if new_third in lower_notes_thirds:
return lower_notes_thirds
else:
return None
else:
return [new_third]
def update_key(self, new_third):
thirds = self._is_match(new_third)
if not thirds:
if self.maj_root or len(self.incoherent_maj_thirds) > 2:
return False
else:
self.coherent_maj_thirds = []
self.incoherent_maj_thirds = set((self.coherent_maj_thirds + [new_third]))
else:
self.coherent_maj_thirds = thirds
if len(thirds) == 3:
self.maj_root = thirds[1]
return True