-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdisplay.py
56 lines (47 loc) · 1.76 KB
/
display.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
import copy
from music21.note import Note
from music21.stream import Part, Measure, Score
from music21.instrument import Soprano, Alto, Tenor, Bass
from music21.clef import TrebleClef, BassClef
from music21.tempo import MetronomeMark
from music21.roman import romanNumeralFromChord
satb_voicing = {'s': Soprano(), 'a': Alto(), 't': Tenor(), 'b': Bass()}
def show_sovler_solution(solution, csp, bpm=60, instruments=satb_voicing, method='text'):
"""Displays the solution using music21
Args:
solution: A dictionary mapping variables to notes
csp: The CSP used to solve the problem.
method: A string of either 'text', 'midi', or 'music' to dictate
whether to display test or music for the solution
"""
s = Score(id='Solution Score')
clefs = {
's': TrebleClef(),
'a': TrebleClef(),
't': BassClef(),
'b': BassClef()
}
tempo = MetronomeMark(number=bpm)
for (i, p) in enumerate(csp.parts):
m = Measure()
if i == 0:
m.append(tempo)
for i in range(1, 1 + len(csp.parts[p])):
n = copy.deepcopy(solution[f'{p}{i}'])
m.append(n)
new_part = Part(id=p)
new_part.append([instruments[p], clefs[p], csp.key, m])
s.append(new_part)
# Add roman numerals
sChords = s.chordify().recurse().getElementsByClass('Chord')
for c in sChords:
rn = romanNumeralFromChord(c, csp.key)
c.addLyric(str(rn.figure))
for (i, n) in enumerate(s.parts[-1].recurse().getElementsByClass('Note')):
n.addLyric(str(list(sChords)[i].lyric))
if method == 'text' or method == 'midi':
s.show(method)
elif method == 'music':
s.show()
else:
raise Exception('Invalid method to display')