Professional Documents
Culture Documents
Contents
Introduction
0.1 Overview . . . . . . . . . . . .
0.2 Purpose . . . . . . . . . . . . .
0.3 Guide to this document . . . .
0.4 Comparisons . . . . . . . . . .
0.4.1 Musical languages . . .
0.4.2 Automatic composition
0.5 Future Directions . . . . . . . .
0.5.1 Pelog . . . . . . . . . .
0.5.2 Visual Pelog . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Pelog
1 The
1.1
1.2
1.3
1.4
ii
ii
iii
iii
iv
iv
iv
v
v
v
Pelog language
What is a rule set? . . . . . . . . . . . . . . . . . . . . . .
Anatomy of a rule . . . . . . . . . . . . . . . . . . . . . .
How rules are applied to the score . . . . . . . . . . . . .
Rule ordering for efficiency . . . . . . . . . . . . . . . . .
1.4.1 Put the rule class with the fewest branches first . .
1.4.2 When the given class and weighting system doesnt
it yourself . . . . . . . . . . . . . . . . . . . . . . .
Pelog library reference . . . . . . . . . . . . . . . . . . . .
1.5.1 Comments . . . . . . . . . . . . . . . . . . . . . . .
1.5.2 Event . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.3 Global . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.4 Interval . . . . . . . . . . . . . . . . . . . . . . . .
1.5.5 Parts . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.6 Pitch . . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.7 Range . . . . . . . . . . . . . . . . . . . . . . . . .
1.5.8 Scale . . . . . . . . . . . . . . . . . . . . . . . . . .
2
2
2
4
5
5
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
apply, do
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
6
8
8
9
10
10
12
13
14
15
2 Modal Counterpoint
2.1 Sources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 The rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
20
20
22
1.5
CONTENTS
2.3
2.4
ii
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
22
23
24
26
26
28
28
28
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
36
36
36
36
36
37
37
37
37
38
39
39
39
39
4 Tools
4.1 Selecting the tools . . . . . . . .
4.1.1 Prolog interpreter . . . .
4.1.2 GUI development system
4.1.3 Connecting the two . . .
4.2 Reflections after-the-fact . . . .
4.2.1 SWI-Prolog . . . . . . . .
4.2.2 Tcl/Tk . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
41
41
42
43
44
44
44
5 Data Structure
5.1 Design notes . . . . . . .
5.2 A worked example . . . .
5.2.1 Index . . . . . . .
5.2.2 Part . . . . . . . .
5.2.3 Pitch . . . . . . . .
5.2.4 Duration . . . . .
5.2.5 Time . . . . . . . .
5.2.6 Previous and Next
5.2.7 Vertical . . . . . .
5.2.8 Comments . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
45
45
46
48
48
48
49
51
51
51
53
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
iii
5.2.9 Penalty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.10 Other . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6 Code and Testing
6.1 The Pelog-GUI . . . . . . . .
6.1.1 pelog-gui.tcl . . . . . .
6.2 The Core Interpreter . . . . .
6.2.1 pelog.pl . . . . . . . .
6.2.2 main.pl . . . . . . . .
6.2.3 bi-math.pl . . . . . . .
6.2.4 preprocessor.pl . . . .
6.2.5 rule-def.pl . . . . . . .
6.2.6 score.pl . . . . . . . .
6.2.7 trees.pl . . . . . . . .
6.3 The GUIDO parser/grammar
6.3.1 parser.pl . . . . . . . .
6.3.2 lexer.pl . . . . . . . .
6.3.3 grammar.pl . . . . . .
6.3.4 Testing . . . . . . . .
6.4 The Pelog library . . . . . . .
6.4.1 main.pl . . . . . . . .
6.4.2 comments.pl . . . . .
6.4.3 event.pl . . . . . . . .
6.4.4 global.pl . . . . . . . .
6.4.5 interval.pl . . . . . . .
6.4.6 parts.pl . . . . . . . .
6.4.7 pitch.pl . . . . . . . .
6.4.8 range.pl . . . . . . . .
6.4.9 scale.pl . . . . . . . .
II
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Visual Pelog
54
54
55
55
55
67
67
70
70
74
80
82
84
84
84
89
91
93
101
101
102
105
108
110
118
120
131
135
150
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
151
151
151
152
153
.
.
.
.
.
.
154
154
156
157
157
157
159
CONTENTS
8.4
9 The
9.1
9.2
9.3
9.4
iv
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
160
160
160
162
162
162
162
162
162
162
162
163
164
165
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
166
166
167
167
168
168
168
169
169
169
169
169
170
170
170
170
171
171
178
187
.
.
.
.
.
.
192
193
193
194
194
203
203
9.5
9.6
11 Custom Megawidgets
11.1 Tree Widget . . . . . . .
11.1.1 End user usage .
11.1.2 Developer usage
11.1.3 Tree.py . . . . .
11.2 Help Browser Widget . .
11.2.1 End user usage .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CONTENTS
11.2.2 Developer usage . .
11.2.3 Help.py . . . . . . .
11.3 On-screen musical keyboard
11.3.1 End user usage . . .
11.3.2 Developer usage . .
11.3.3 Future plans . . . .
11.3.4 MusicWidgets.py . .
v
. . . . .
. . . . .
Widget
. . . . .
. . . . .
. . . . .
. . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
203
204
209
209
210
211
211
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
220
220
220
223
229
230
230
231
III
Appendices
233
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
functions
. . . . . .
. . . . . .
. . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
234
234
234
235
236
236
236
236
236
239
241
.
.
.
.
.
.
.
.
.
.
.
.
.
244
244
245
245
248
248
248
248
249
249
249
249
249
250
CONTENTS
vi
Introduction
There once was a brainy baboon
Who always breathed down a bassoon,
For he said, It appears
That in billions of years
I shall certainly hit on a tune.
Ezra Pound (1885-1972)
How can we make our modern day brainy baboon (i.e. a computer) hit on a
tune any faster? We certainly dont have billions of years to wait. The answer
lies in constraining the number of possibilities. Certainly, this is what Western Art
Music has done all along. Would Renaissance music have been the same if the
church had not imposed strong restrictions1 on musical language? Many believe
that Mozarts genius lies not in his emotive ability, but in how he worked within the
rigid musical confines of his day. Even the so-called Romantic composers expressed
themselves within a rigid tonality. Despite recent years of liberalism, musical constraints remain alive and well. Just consider the serialist and minimalist movements.
The purpose of this project is to help determine exactly what makes up these various musical frameworks. In other words, what restrictions do we need to place on
the brainy baboon in order to arrive at the elusive tune?
0.1
Overview
The Pelog system provides an intuitive language in which to specify musical constraints. These constraints are then applied by the Pelog interpreter to generate
or evaluate musical scores.
The musical constraints are written in a superset of Prolog known as the Pelog
language (see 1). Since the constraints themselves are separate from the core system, it is possible for the end user to modify or create constraints with minimal
effort. The advantage of using Prolog as the base language is that Pelog applications
are highly declarative and accessible to the novice programmer with a musical background. Just like Prolog, the complexities of solution searching and backtracking
are hidden from the programmer.
Sets of constraints can be written to represent any rule-based musical system.
For example, the first set of constraints developed in Pelog is for Modal Counterpoint
[20]. Future candidates include Tonal Harmonic Counterpoint and Schonberg-style
1
vii
INTRODUCTION
viii
0.2
Purpose
0.3
INTRODUCTION
ix
0.4
Comparisons
This section is intended to shed some light on where Pelog fits within the field of
Computer Music. It is by no means as an exhaustive survey of Computer Music
tools.
0.4.1
Musical languages
Some of the more common music-specific languages are Common Music [36], KeyKit
[38], CSound [40] and the commercial Max language [2]. Common Music is essentially Common Lisp Object System (CLOS) with a number of musical extensions.
KeyKit receives its lineage from awk. CSound is the latest in a series of musical
generation languages based on Music V. Max is both a language and visual programming environment. The programmer can set up musical generators as units
connected by wires. What sets Pelog apart from all of these languages is its
logical semantics. It builds on the strength of Prolog by allowing the programmer to specify what the output should look like rather than how to get there.
While the application domains of these other languages is perhaps larger than that
of Pelog, Pelog takes a more natural approach to musical generation that is likely
more familiar to non-programmers.
0.4.2
Automatic composition
Automatic Counterpoint [33] appears to do one thing very well, and has in fact
proved to be a very valuable starting point for Pelogs Modal Counterpoint application. Pelogs advantage however2 , is in the declarative nature and maintainability of
the musical constraints themselves. While Schottsteadts work seems to be firmly
grounded in modal counterpoint, Pelog can be extended to any musical system
imaginable.
Perhaps the most popular work in automatic composition is David Copes Experiments in Musical Intelligence (EMI) [6]. His work began in the same domain
as Pelog: musical constraints. This approach was quickly abandoned for one involving recombination of musical phrases from composers bodies of work. While
Copes work was remarkably effective at generating enjoyable and original music
in the styles of various composers, it lacks the pure musicological focus of Pelog.
The research domain of Pelog is less interested in generating interesting music than
testing the effectiveness of given sets of musical constraints.
Copes work does, however, illustrate a fundamental weakness of the Pelog system. No composer exists in a vacuum, and therefore it is impossible to believe
2
INTRODUCTION
0.5
0.5.1
Future Directions
Pelog
Pelog is still very much a ninety-pound weakling, and theres lots more to do.
Following are some future projects I am currently considering, in approximate order
of priority.
1. Re-write the core algorithm
The rule application mechanism as it currently stands is extremely
inefficient, almost to the point of uselessness. A complete re-write
of the main rule application mechanism to take advantage of the
constraint proving theorems presented in Constraint Satisfaction in
Logic Programming [39] is in order. These theorems could decrease
the time complexity by a factor of up to 10 times.
2. Improve and expand library functions
There is almost an infinite variety of library functions that would
be useful to rule programmers in various domains. These might
include texture, rhythmic accent patterns, contextualism or parallelism. A deeper examination of other musical languages may reveal
the kinds of high-level information often deemed necessary by Pelog
programmers.
3. Implement more rule sets
As already mentioned, there are many more musical constraint systems than just modal counterpoint. Future applications could include tonal counterpoint or serial composition.
0.5.2
Visual Pelog
The Visual Pelog system currently exists only as a user-interface prototype. Most
importantly, the connection between Visual Pelog and the core Pelog system needs
to be completed. Beyond the obvious to-do list, there are some other important
directions the system could take:
1. Integrate it with musical input/output system
INTRODUCTION
xi
This would most likely take the form of adding MIDI capability
to Python. The on-screen musical keyboard widget could be expanded to accept input from a physical musical keyboard connected
via MIDI. The system could output the results of the musical constraints through a MIDI synthesiser.
2. Re-write parts of the code to take advantage of an object-oriented GUI
paradigm.
It was rather late in the project when I discovered the book ObjectOriented GUI Application Development by Geoff Lee [22]. Adopting a more disciplined approach to GUI development, such as the
Document-View architecture, would improve the scalability and
overall flexibility of the system.
I
Pelog
1.1
The Pelog language is nothing more than a superset of the Prolog language. Therefore, a rule set is essentially a Prolog source file containing a number of specially
defined rule predicates. The Pelog programmer has access to all of the built-in and
library functions that make up SWI-Prolog, as well as the extensions provided by
Pelog.
The difference between a Pelog rule set and a plain-vanilla Prolog source file
is that Pelog rule sets are run through a special preprocessor before they can be
interpreted. This process is explained below.
1.2
Anatomy of a rule
Each rule in the rule set looks like a regular Prolog predicate of the form:
rule(name, class, weight) :goal1,
goal2, . . .
name is a simply a name for the rule to use for reference.
2
class specifies the rule class that the rule belongs to.
weight is an integer value specifying the weight of the rule.
Rule classes and weights are described in the next section.
The following is a rule that ensures that every event stays within the scale.
% P i t c h must be a member o f t h e c u r r e n t s c a l e
r u l e ( In scale , scale , 0 ) :
$event
scale .
The name of this rule is In scale, and its class is scale, and the strength value
is 0. Following this is one goal that ensures the current event is in the scale. For a
list of the Pelog library functions that are useful for writing musical goals, see the
Pelog library reference (page 8).
In many ways the predicate above is just like a normal Prolog predicate. Notice,
however, that there are no arguments that unify with variables, and thus seemingly
no way to interact with external data. This problem is solved, however, by a special
Pelog rule preprocessor. Rather than listing all of the arguments in the head of
each and every rule predicate, which could get quite tedious and cumbersome, the
preprocessor does all that work for you. From a predicate such as the one above, it
creates a new predicate with a set of all the arguments you could possibly need to
interact with the score. These arguments are matched up with Pelogs concept of
metavariables, which all begin with a dollar sign $. Table 1.1 is a listing of the
available metavariables. The above rule predicate, therefore, is converted internally
metavariable
$event
$prevx
abbrev.
$e
$px
$vert
$v
$events
$x
description
The current event
The x th event before the current event
An event vertically related to
the current event. (i.e. an
event occurring at the same
time as the current event)
The set of all events. Used by
some library predicates that
need access to the enter score.
Table 1.1: Metavariables
to:
r u l e ( In scale , s c a l e ,
a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
A
scale .
(And it actually creates an extra fact predicate, rule def/3, to keep around for
internal housekeeping.)
r u l e d e f ( In scale , s c a l e , 0 ) .
1.3
It is wise for the rule programmer to know exactly how the rules will be applied to
the score in order to maximize time efficiency.
The entire set of rules is applied to the score event-by-event. The order in which
the events are visited is determined when the input file is read in by the GUIDO
parser. A time path is generated by sorting all the events by their starting times.
Therefore its essentially a first-events-first scheme. Figure 1.1 shows an example
time-path. For more information on the internal data representation, see A data
structure for encoding incomplete musical fragments on page 45.
are preferred to rules with higher weights. For example, in modal counterpoint
[11], steps are considered preferable to skips, so a rule for steps would have a lower
weighting than a rule for skips. Rules with the same weight are equally preferred.
The interpreter will arbitrarily choose one rule as preferable to another rule with
the same weight.
Lets examine how all of these classifications are used by the Pelog interpreter.
Each event needs to pass only one rule from each of the rule classes. The rule
classes are visited in the order they appear in the rule set file. When a class is
visited, the rule with the lowest weighting is tried first. If this rule fails, a rule with
the same weighting is tried. If all the rules with the same weighting fail, control
backtracks through the previously visited rule classes to find an alternate solution.
If all the rules at a particular weight level fail with all of the alternate solutions of
the previously visited rule classes, then, and only then, will rules of higher weighting
be tried. If all the rules in an entire rule class still fail, control backtracks to the
previous event. Alternate solutions of the previous event may lead to successful
completion of the current event.
1.4
Given this, what is the best way to order the rules to maximize efficiency?
1.4.1
The first rule class visited will serve as the generating rule class. If an event
is specified as a variable in the input score, the first-visited rule class will serve
to instantiate that variable with a value. All rule classes visited thereafter will
only serve to verify that the instantiation is correct, not to generate new values.
By limiting the alternative solutions in the generating rule class you can limit the
number of values that need to be tested to arrive at a solution.
Take, for example, the following two rules:
% Good i n t e r v a l s
r u l e ( Good intervals , melodic intervals , 1 ) :
$prev1 = s t a r t
;
( member ( I , [ 2 , 3 , 4 , 5 ] ) ,
member ( IC , [ min , maj , p e r ] ) ,
$prev1 : $event a b s o l u t e i n t e r v a l
IC I ) .
% P i t c h must be a member o f t h e c u r r e n t s c a l e
r u l e ( In scale , scale , 0 ) :
$event
scale .
The Good intervals rule checks for permissible melodic intervals. There are 12
possible solutions for this rule. The In scale rule ensures that the event is in
the current scale. A major scale, for example, consists of seven notes. When
you consider that Pelog handles those seven notes in sixteen octaves, this rule will
generate 7 16 = 112 possible solutions. It would therefore be much wiser to select
Good intervals as the generating rule instead of In scale.
1.4.2
The rule set programmer is by no means forced to follow the default weighting and
class scheme for every rule class. It is merely provided as convenience. In cases
where it may be simpler or more declarative to override it, the rule programmer is
encouraged to do so.
Lets take another example from modal counterpoint. Events that occur at
certain times during the melody, namely the first, last and penultimate events,
must be at certain degrees of the scale. What exactly these are depends on whether
the event is in the top voice or in the bottom voice. This relationship is given in
Table 1.2. Since the mutual exclusivity is a little bit hard to follow here, the entire
top voice
bottom voice
first event
final, tenor
final
last event
final, third
final
penultimate event
leading tone
2, 4, 5
;
E p a r t bottom >
m c f i r s t p i t c h b o t t o m (E ) ) .
% F i r s t p i t c h i n t o p v o i c e must be f i n a l ( t o n i c ) o r t e n o r ( dominant )
m c f i r s t p i t c h t o p (E) :
E final
;
E scale degree tenor .
% F i r s t p i t c h i n bottom v o i c e must be f i n a l ( t o n i c )
m c f i r s t p i t c h b o t t o m (E) :
E final .
% L a s t p i t c h d e p e n d s on w hi c h v o i c e i t i s i n
m c l a s t p i t c h (E) :
E comment Last pitch ,
( E p a r t t o p >
m c l a s t p i t c h t o p (E)
;
E p a r t bottom >
m c l a s t p i t c h b o t t o m (E ) ) .
% L a s t p i t c h i n t o p v o i c e must be f i n a l ( t o n i c ) o r
% t h e t h i r d ( m e d i a n t ) i f t h e r e a r e more t h a n t h r e e v o i c e s
m c l a s t p i t c h t o p (E) :
E final
;
( num parts X , X > 2,
E scale degree 3).
% L a s t p i t c h i n bottom v o i c e must be f i n a l
m c l a s t p i t c h b o t t o m (E) :
E final .
% P e n u l t i m a t e p i t c h d e p e n d s on w hi c h v o i c e i t i s i n
m c p e n u l t i m a t e p i t c h (E) :
E comment Penultimate pitch ,
( E p a r t t o p >
m c p e n u l t i m a t e p i t c h t o p (E)
;
E p a r t bottom >
mc penultimate pitch bottom (E ) ) .
% P e n u l t i m a t e p i t c h i n t o p v o i c e must be l e a d i n g t o n e o r
% f o u r t h d e g r e e ( s u b d o m i n a n t ) i f t h e r e a r e more t h a n two v o i c e s
m c p e n u l t i m a t e p i t c h t o p (E) :
E leading tone
;
( num parts X , X > 2,
E scale degree 4).
% P e n u l t i m a t e p i t c h i n bottom v o i c e must be 2 nd , 4 t h o r 5 t h s c a l e d e g r e e
mc penultimate pitch bottom (E) :
E scale degree 2
;
E scale degree 4
;
E scale degree 5.
1.5
The Pelog system provides a rich set of library predicates for specifying musical relationships. Most of the predicates are implemented as infix operators. For example,
to ensure that the interval between two notes is a minor third, you would write:
$ p r e v 1 : $ e v e n t a b s o l u t e i n t e r v a l min 3
Of course, this is equivalent to Prologs more standard notation with the functor in
front:
a b s o l u t e i n t e r v a l ( $ p r e v 1 : $ e v e n t , min 3 )
While the syntax may be a little different from what most Prolog programmers
may be used to, it makes the code seem more declarative. After all, one of the goals
of this project was to make the rule specifications as readable as possible.
For consistency, all library predicates follow the convention of having the events
on the left hand side of the functor and all other arguments on the right hand side.
The Pelog library predicates are listed by library below. Each section contains
an introduction to the conventions used throughout the library followed by an alphabetical listing of the user predicates.
Syntax follows the form used in a lot of Prolog documentation, including the
SWI-Prolog documentation and OKeefe.
An argument preceded by a + is input only (i.e. it must be instantiated)
An argument preceded by a - is output only (i.e. it may not be instantiated)
An argument preceded by a ? is input or output.
1.5.1
Comments
This library provides a mechanism to store comment strings with each event. These
comments can be used to provide feedback about events within the score. These
comments are added to the output file in the style specified by the comment style
tag in the input file. See the section on GUIDO Music Notation (page 35) for more
information.
comment
+event comment +comment
comment(+event, +comment)
If comment is instantiated, comment is added to events comment list.
If comment is a variable, comment unifies with any comment currently in events
comment list.
r u l e ( Step - step , contour , 1 ) :
$prev2 = s t a r t
;
( $prev2 : $prev1 a i n t e r v a l step ,
$prev1 : $event a i n t e r v a l step ,
$ p r e v 2 comment Step - step ) .
1.5.2
Event
first event
+event first event
first event(+event)
Succeeds if event is the first event in its part.
r u l e ( First -Last- Penultimate , chronological , 0 ) :
$ e v e n t f i r s t e v e n t >
m c f i r s t p i t c h ( $event )
;
$ e v e n t l a s t e v e n t >
m c l a s t p i t c h ( $event )
;
$ e v e n t p e n u l t i m a t e e v e n t >
mc penultimate pitch ( $event )
;
true .
last event
+event last event
last event(+event)
Succeeds if event is the last event in its part.
penultimate event
+events penultimate event
penultimate event(+events)
Succeeds if the current event defined in events is the penultimate event.
Note that this predicate requires $events and not $event as input.
1.5.3
10
Global
The global library can be used to extend the GUIDO parser to recognize new tags.
Simply add a global default predicate to your Pelog rule set file. global default
predicates are of the form:
global default(tag(tagname), default)
tagname is the name of the tag
default is the default value for the tag if the tag is not present in the input file
The value of the tag can be used in your rules at runtime using the tag predicate.
tag
+tagname tag -value
tag(+tagname, -value)
Succeeds if tagname is assigned to value.
Tags that are assigned to specific parts, (i.e. not global to the score,) are retrieved using the form tag ~part, where tag is the tag name as it occurs in the input
file and part is the part name.
1.5.4
Interval
meaning
major
minor
perfect
diminished
augmented
chromatic
semitones (size field is # of semitones)
tritone
Table 1.3: Interval types
For example, min~3 is a minor third and maj~7 is a major seventh. The sem
type returns the size field as the number of semitones, regardless of note names.
Therefore, maj~3 is equivalent to sem~4.
Note that either the type or size field may be left variable using Prologs anonymous variable, the underscore . Therefore, ~3 will specify dim~3, min~3, maj~3
and aug~3.
11
meaning
Unison (the two given notes have the same pitch)
A second
Any interval larger than a second
Any thirds, fifths, sixths or octaves that are not diminished
or augmented
Any seconds, fourths or sevenths
Table 1.4: Interval keywords
Intervals can also be specified using any of the keywords in Table 1.4.
absolute interval
?eventa : ?eventb absolute interval ?interval
?eventa : ?eventb absolute interval ?interval contour ?contour
absolute interval(?eventa : ?eventb, ?interval)
absolute interval(?eventa : ?eventb, ?interval contour ?contour )
?eventa : ?eventb a interval ?interval
?eventa : ?eventb a interval ?interval contour ?contour
a interval(?eventa : ?eventb, ?interval)
a interval(?eventa : ?eventb, ?interval contour ?contour )
Succeeds if the absolute interval between two events, eventa and empheventb, is
equal to interval. interval may be in any form specified in the introduction to the
interval library.
The optional contour unifies with up if empheventb is higher in pitch than eventa,
down if empheventb is lower in pitch than eventa, and static if empheventb is the
same pitch as eventa.
For specifying the cyclic interval between two events (i.e. where maj~10 is equivalent to maj~3, see the cyclic interval predicate.)
% Good i n t e r v a l s
r u l e ( Good intervals , melodic intervals , 1 ) :
$prev1 = s t a r t
;
( member ( I , [ 2 , 3 , 4 , 5 ] ) ,
member ( IC , [ min , maj , p e r ] ) ,
$prev1 : $event a i n t e r v a l
IC I ) . $
contour
+eventa : +eventb contour ?contour
contour(+eventa : +eventb, ?contour )
contour unifies with up if empheventb is higher in pitch than eventa, down if
empheventb is lower in pitch than eventa, and static if empheventb is the same pitch
as eventa.
12
cyclic interval
?eventa : ?eventb cyclic interval ?interval
?eventa : ?eventb cyclic interval ?interval contour ?contour
cyclic interval(?eventa : ?eventb, ?interval)
cyclic interval(?eventa : ?eventb, ?interval contour ?contour )
?eventa : ?eventb c interval ?interval
?eventa : ?eventb c interval ?interval contour ?contour
c interval(?eventa : ?eventb, ?interval)
c interval(?eventa : ?eventb, ?interval contour ?contour )
Succeeds if the cyclic interval between two events, eventa and eventb, is equal
to interval. interval may be in any form specified in the introduction to the interval
library.
The optional contour unifies with up if empheventb is higher in pitch than eventa,
down if empheventb is lower in pitch than eventa, and static if empheventb is the
same pitch as eventa.
For specifying the absolute interval between two events (i.e. where maj~10 is
not equivalent to maj~3, see the absolute interval predicate.)
1.5.5
Parts
13
def ( only ,
, 1) : !.
d e f ( top , 1 , X ) .
d e f ( bottom , X , X ) : X > 1 .
d e f ( c a n t u s f i r m u s , X , X) : X > 1.
d e f ( i n n e r , X , Y ) : X > 1 , X \= Y .
d e f ( soprano , 1 , 4 ) .
def ( alto , 2 , 4 ) .
def ( tenor , 3 , 4 ) .
def ( bass , 4 , 4 ) .
part
+event part -part
part(+event, -part)
Succeeds if event is in part.
part may be an integer or a part name defined in part def.
1.5.6
Pitch
In Pelog, pitch names follow the same convention as GUIDO (see GUIDO Music
Notation on page 35 for more information.) A pitch name is defined as a letter {c,
d, e, f, g, a, b} optionally followed by an accidental {#, &, ##, &&}.
An octave is an integer where octave 1 is the octave containing A440Hz.
equivalent pitch
+eventa : +eventb equivalent pitch
equivalent pitch(+eventa : +eventb)
Succeeds if eventa sounds at the same pitch as eventb.
For example, c# : d& equivalent pitch succeeds.
octave
+event octave -octave
octave(+event, -octave)
Succeeds if event is in octave.
pitch name
14
1.5.7
Range
in range
+event in range +range
in range(+event, +range)
Succeeds if event is in range.
event must be instantiated. in range does not generate pitches.
15
range must be between two fixed points. This may be a named range or two
instantiated events. It can not be defined as an roaming interval.
range is
+events range is ?range
range is(+events, ?range)
Succeeds if the range of all events up to and including the current event is exactly
equal to range. The events must be instantiated.
range can be defined in any of the ways defined in the introduction to the range
library.
range within
+events range within +range
range within(+events, +range)
Succeeds if all events up to and including the current event are within range
range. The events and the range must be instantiated.
range can be defined in any of the ways defined in the introduction to the range
library.
1.5.8
Scale
A collection of pitches. In this context, the words scale and mode are used
interchangeably.
In Pelog, scales are specified in the form tonic~pitchset, where tonic is the starting pitch of the scale, and pitchset the name of a set of pitches.
tonic can be any pitch name. See the pitch library (page 13) for more information.
pitchset can be any pitch set name defined in a scale def clause.
The built-in pitch set definitions are given in Table 1.7.
The Pelog rule set programmer can add more scale definitions by adding scale def
fact predicates to the rule set file. scale def predicates have the form:
scale def(name, pitch list, key interval).
name is the name of the scale.
pitch list is a list of pitch names that make up the scale. This list should be provided
with c as tonic. The scale library will do the work of transposing the scale to
tonics other than c.
key interval is the interval between the scales tonic and the tonic of the major key signature that this scale should be written in. For example, the tonic of the dorian
mode is on the second degree of the major scale, so the key interval value is
maj~2.
major
harmonic-minor
natural-minor
melodic-minor
dorian
hypo-dorian
phrygian
hypo-phyrgian
lydian
hypo-lydian
mixolydian
hypo-mixolydian
aeolian
hypo-aeolian
locrian
ionian
hypo-ionian
chromatic (-sharps, -flats)
whole-tone (-sharps, -flats)
gypsy
pentatonic (-23, -32)
octatonic (-sharps, -flats)
half-whole (-sharps, -flats)
whole-half (-sharps, -flats)
Table 1.7: Built-in pitch sets
16
17
d e f ( major , [ c , d , e , f , g , a , b ] , per 1 ) : ! .
d e f ( h a r m o n i cm i n o r , [ c , d , e & , f , g , a & , b ] , maj 6 ) : ! .
d e f ( n a t u r a lm i n o r , [ c , d , e & , f , g , a & , b & ] , maj 6 ) : ! .
d e f ( m e l o d i cm i n o r , [ c , d , e & , f , g , a & , a , b & , b ] , maj 6 ) : ! .
scale
scale
scale
scale
scale
scale
scale
scale
scale
scale
scale
scale
scale
s c a l e d e f ( c h r o m a t i c , [ c , c #, d , d #, e , f , f #, g , g #, a , a #, b ] , none ) : ! .
s c a l e d e f ( c h r o m a t i cs h a r p s , X , Y ) : s c a l e ( c h r o m a t i c , X , Y ) , ! .
s c a l e d e f ( c h r o m a t i c f l a t s , [ c , d & , d , e & , e , f , g & , g , a & , a , b & , b ] ,
none ) : ! .
s c a l e d e f ( w h o l et o n e , [ c , d , e , f #, g #, a # ] , none ) : ! .
s c a l e d e f ( w h o l et o n es h a r p s , X , Y ) : s c a l e ( w h o l et o n e , X , Y ) , ! .
s c a l e d e f ( w h o l et o n e f l a t s , [ c , d , e , g & , a & , b & ] , none ) : ! .
s c a l e d e f ( g y p s y , [ c , d , e & , f #, g , a , a & , b ] , u n i s ) : ! .
scale def ( pentatonic , [ c , d , f , g , a ] , unis ) : !.
s c a l e d e f ( p e n t a t o n i ca , X , Y ) : s c a l e d e f ( p e n t a t o n i c , X , Y ) , ! .
s c a l e d e f ( p e n t a t o n i cb , [ c , d , e , g , a ] , u n i s ) : ! .
s c a l e d e f ( o c t a t o n i c , [ c , c #, d #, e , f #, g , a , a # ] , none ) : ! .
s c a l e d e f ( o c t a t o n i cs h a r p s , X , Y ) : s c a l e d e f ( o c t a t o n i c , X , Y ) .
s c a l e d e f ( o c t a t o n i c f l a t s , [ c , d & , e & , e , g & , g , a , b & ] , none ) : ! .
scale
scale
scale
scale
scale
d e f ( h a l fw h o l e , X , Y ) : s c a l e d e f ( o c t a t o n i c , X , Y ) , ! .
d e f ( h a l fw h o l eX , Y , Z ) : s c a l e d e f ( o c t a t o n i cX , Y , Z ) , ! .
d e f ( w h o l eh a l f , [ c , d , d #, f , f #, g #, a , b ] , none ) : ! .
d e f ( w h o l eh a l fs h a r p s , X , Y ) : s c a l e d e f ( w h o l eh a l f , X , Y ) .
d e f ( w h o l eh a l f f l a t s , [ c , d , e & , f , g & , a & , a , b ] , none ) : ! .
The scale library also handles scale degrees. Scale degrees can be specified as an
integer where the tonic of the scale is equal to 1. Scale degrees can also be specified
by name. The list of built-in scale degree names, or special tones is given in Table
1.8. Note that not all of these special tones are available for use with all scales.
18
final
ambitus
tenor
tonic
leading
supertonic
mediant
subdominant
dominant
submediant
subtonic
Table 1.8: Built-in special tones
The Pelog rule programmer can add more special tone definitions by adding
special tone def fact predicates to the Pelog rule set file. special tone def predicates
have the form:
special tone def(name, pitchset, pitch).
name is the name of the special tone.
pitchset specifies the scales that the special tone applies to.
pitch is the special tone in that scale with c as tonic.
The built-in special tone defs are listed below:
special
special
special
special
special
special
special
special
special
special
special
% Only
special
special
special
special
special
special
special
special
special
tone
tone
tone
tone
tone
tone
tone
tone
tone
tone
tone
def ( final ,
, c ) : !.
d e f ( ambitus , X , Y) : s p e c i a l t o n e d e f ( t o n i c , X , Y) .
d e f ( t e n o r , hypo , e ) : ! .
def ( tenor ,
, g) : !.
d e f ( t o n i c , hypo , g ) : ! .
def ( tonic ,
, c ) : !.
d e f ( leading , phrygian , b &) : !.
d e f ( l e a d i n g , hypol y d i a n , f # ) : ! .
d e f ( l e a d i n g , hypo , f ) : ! .
d e f ( l e a d i n g , n a t u r a lm i n o r , b & ) : ! .
def ( leading ,
, b) : !.
u s e d i n TONAL h a r m o n i c p r a c t i c e
t o n e d e f ( s u p e r t o n i c , major , d ) : ! .
t o n e d e f ( s u p e r t o n i c , m i n o r , d ) : ! .
t o n e d e f ( mediant , major , e ) : ! .
t o n e d e f ( m e d i a n t , m i n o r , e & ) : ! .
t o n e d e f ( subdominant , major , f ) : ! .
t o n e d e f ( s u b d o m i n a n t , m i n o r , f ) : ! .
t o n e d e f ( dominant , m a j o r , g ) : ! .
t o n e d e f ( dominant , m i n o r , g ) : ! .
t o n e d e f ( submediant , major , a ) : ! .
tone
tone
tone
tone
tone
19
d e f ( s u b m e d i a n t , m e l o d i cm i n o r , a ) : ! .
d e f ( s u b m e d i a n t , m i n o r , a & ) : ! .
d e f ( s u b t o n i c , major , b ) : ! .
d e f ( s u b t o n i c , n a t u r a lm i n o r , b & ) : ! .
d e f ( s u b t o n i c , m i n o r , b ) : ! .
current scale
current scale ?scale
current scale(?scale)
Succeeds if emphscale is the current scale as defined by the scale tag in the input
file.
in scale
?event in scale
?event in scale +scale
in scale(?event)
in scale(?event, +scale)
Succeeds if event is in scale.
If scale is omitted, the scale specified by the scale tag in the input file is used.
scale degree
?event scale degree +degree
scale degree(?event, +degree)
Succeeds if event is the scale degree degree of the scale specified by the current
scale. (i.e. specified by the scale tag in the input file.)
degree can be
an integer, indicating the degreeth scale degree.
a special tone defined by special tone def
of the form raised~n, indicating the nth scale degree, raised by a semitone
of the form lowered~n, indicating the nth scale degree, lowered by a semitone.
2.1
Sources
21
[20] Krenek, Ernst. (1953) Modal Counterpoint in the Style of the Sixteenth Century.
New York: Boosey & Hawkes, 1959.
Krenek provides a very concise overview of counterpoint that is widely
regarded as the definitive modern standard. Modal Counterpoint is the
first volume in a series which also contains Tonal Counterpoint in the
Style of the Eighteenth Century and Studies in Counterpoint based on
Twelve-Tone Technique. Each volume is under thirty pages in length,
making it very easy to convert the text into individual rules.
[11] Fux, Johann Joseph. (1725) The Study of Counterpoint from Johann Joseph
Fuxs Gradus ad Parnassum, translated and edited by Alfred Mann. New York: W.
W. Norton & Co., 1943.
The central role of counterpoint in classical musical education is due in
large part to this influential book. It is used here to correct any loose
ends in the rules developed in Schottstaedt [33]. It is an odd, rambling,
book that is for the most part presented as a dialogue.
The serious difficulty of using Fux for the purposes of this project is
that the rules are not often fully distilled. For example, consider the
following excerpt (page 22), where the three possible motions are direct,
contrary and oblique:
First rule: From one perfect consonance to another perfect consonance one
must proceed in contrary or oblique motion.
Second rule: From a perfect consonance to an imperfect consonance one may
proceed in any of the three motions.
Third rule: From an imperfect consonance to a perfect consonance, one must
proceed in contrary or oblique motion.
Fourth rule: From one imperfect consonance to another imperfect consonance
one may proceed in any of the three motions.
By constructing these rules in a diagram, it becomes obvious that these
four rules can be combined into one.
perfect consonance imperfect consonance
perfect consonance
parallel
imperfect consonance parallel
The only situation not allowed is direct motion to a perfect consonance.
This, and numerous examples like it, occur frequently throughout the
text.
[37] Thakar, Markand. (1990) Counterpoint: Fundamentals of Music Making. New
Haven: Yale University Press, 1990.
Thakar takes a performers approach to counterpoint, which does not
always readily lend itself to this project. It does present, however, some
practical issues, such as the overall length of exercises, that are overlooked by the other texts.
2.2
2.2.1
22
The rules
Species rules
Though it is not readily apparent from the overly-complex approach of Fux [11],
the species of counterpoint are not very different.
Note that Krenek [20] does away with the concept of species, presenting all
examples in Fuxs fifth species.
Here, cantus firmus refers to the lowest voice.
The only rules that are dependent on species are:
Every duration in the cantus firmus is a whole note
First species is note-against-note.
In first species, every duration in the upper voice, except the last, is equal to
the durations in the cantus firmus
Second species is note-against-two-notes.
In second species, every duration in the upper voice, except the last, is half
the length of the durations in the cantus firmus
Third species is note-against-four-notes.
In third species, every duration in the upper voice, except the last, is onequarter the length of the durations in the cantus firmus
Fourth species is note-against-note with ligatures.
In fourth species, every duration in the upper voice, except that of the last
note, is equal to the length of the durations in the cantus firmus. The start
times of the notes in the upper voice are shifted forward by half of the length
of the durations in the cantus firmus.
Fifth species is free counterpoint. If in fifth species, the durations of the
upper voice may be any of the following: { whole note, half note, quarter note,
eigth note } Selection of these durations must follow the rules listed below
Fifth-Species rhythmic rules
Fifth-species rhythmic rules are quite complex and involve a lot of inter-relations.
Periodical symmetry of rhythmic values.
More than two thirds of potentially accented beats are articulated
More than eight quarter notes in a row
Less than 75% of melody in half notes
2.2.2
23
Static rules
Static rules are rules that involve only individual notes, not the relationships between notes.
Note class
These rules involve selecting notes from a particular subset of notes. These subsets
are typically modes or scales.
Fux [11] sticks to the convention of using only one mode across all parts. Krenek
[20] claims that the parts may each be in their own different modes as long as those
modes are related.
The standard modes and scales, and their inter-relationships will be included in
the system.
Note outside of mode or scale
In Dorian, Mixolydian and Aeolian modes, the leading tone is not raised by a
semi-tone
In Aeolian, if leading tone is approached stepwise from below, this sixth scale
degree is not raised
Range
Note outside the range of a per12
Note outside the range of a per8
Note on outer edge of range
Chronological position
Notes at certain points in time must meet certain criteria.
First pitch is not tonic/final or fifth-scale-degree pitch
If there are two voices, last pitch in upper voice is not the tonic/final
If more than two voices, last pitch in upper voice is not the tonic/final or
third-scale-degree
Last pitch in lower voice is not the tonic/final
Last note is not on downbeat
Last duration is less than a whole note
Penultimate pitch in the upper voice is not the leading-tone
Penultimate pitch in lowest voice is not the fourth or fifth-scale-degree
Penultimate pitch in lowest voice is not the second scale degree.
24
Overall length
Fux [11] and Krenek [20] make no references to overall length, though Thakar [37]
recommends a length of 8 to 14 whole notes. Presumably some finite limit will need
to be defined or the system will continue to generate counterpoint infinitely (or at
least until running out of memory.)
2.2.3
Horizontal rules
25
Repetition
First pitch is repeated
Repeated pattern of four notes
Repeated pattern of three notes
Repeated pattern of two notes
Imitation
Better melodies include sequences of three or more notes that are repeated in pitchtransposed form.
Melody doesnt contain an imitative sequence of three notes
Melody doesnt contain an imitative sequence of four notes
Melody doesnt contain an imitative sequence of five notes
Melody doesnt contain an imitative sequence of six notes
Accent Pattern
Certain rules are dependent on the beat on which they occur. In standard fourbeats-per-measure counterpoint, beat 1 is the strongest, and beat 3 is slightly less
strong. Beats 2 and 4 are weak beats. Another definition of accent pattern is that
an accented beat exists wherever two or more note onsets occur at the same time.
If two quarter notes are on an accented beat, not preceded by at least one
quarter note, followed by two quarter notes or followed by a duration greater
than a half note
Upward skip from accented beat in quarter notes
In fourth species, upper voice note starts on down beat of bar
If on downbeat, repeated note
If on last beat in bar, repeated note
If on downbeat of bar, preceded by a skip
Voice leading
Leading tone not followed by tonic
Leading tone not approached by step
26
High point
Highest note in melody occurs more than once over entire melody
2.3
Vertical rules
Vertical rules define the relationship between notes in different melodic lines at a
given moment in time. In this section, interval refers to harmonic interval.
Harmonic intervals
Interval is aug5
Interval is unis
If three-part or more, pitches do not form triad
If Lydian mode, interval is dim5
Interval is per4 or per5
Interval is dim5
Interval is greater than per8 (Krenek [20] says maj10 )
Chronological position / Accent pattern
Interval of last notes is not unis or per8
Interval of notes on accented beat is dissonant and duration is less than a half
note
Penultimate interval is not maj6 or min3
If note is on downbeat of bar, interval is unis
per8 or per5 appear less than four quarter notes apart
Voice crossing
Upper voice is below cantus firmus
2.3.1
Parallel motion
Parallel motion occurs when the same harmonic interval occurs consecutively.
Parallel per5 or per8 : Harmonic interval is per5 or per8 and previous harmonic interval is the same.
27
28
Cross-part imitation
Melodies that imitate the other voices in the counterpoint are preferable. At this
stage, the system will not be embedded with the logic of complex imitative forms
such as the fugue. However, it will reward counterpoints that contain embedded
imitative motifs. According to Krenek [20], the motifs can be transposed (pitchshifted), inverted (pitches-mirror imaged), retrograde (pitches in reverse order),
augmented (durations doubled) or diminished (durations halved) in order to be
considered imitative. This is a lot of different ways to modify motifs, and may
require considerable processing time to search for all possible variations.
Melody doesnt contain a cross-part imitative sequence of three notes
Melody doesnt contain a cross-part imitative sequence of four notes
Melody doesnt contain a cross-part imitative sequence of five notes
Melody doesnt contain a cross-part imitative sequence of six notes
Miscellaneous
All voices have skips at once
2.3.2
Strict canon
In strictly imitative canon, the melodic lines are identical, with different start times.
The parts must obey all other counterpoint rules.
2.4
2.4.1
Implementation
modal counterpoint.pl
Code
: d i s c o n t i g u o u s
r u l e d e f /4,
r u l e /3.
r u l e o r d e r ( [ chronological ,
special cases for modes ,
melodic intervals ,
scale ,
range ,
tendency ,
harmonic intervals ,
avoid harmonic intervals ,
two leading tones ,
contour ,
29
repeat first ,
repeat patterns ,
outline a tritone ] ) .
/
CHRONOLOGICAL POSITION
N ot e s a t s p e c i a l p o s i t i o n s , namely t h e f i r s t , p e n u l t i m a t e and
l a s t n o t e s must be c e r t a i n s c a l e d e g r e e s
/
r u l e ( First -Last- Penultimate , chronological , 0 ) :
$ e v e n t f i r s t e v e n t >
m c f i r s t p i t c h ( $event )
;
$ e v e n t l a s t e v e n t >
m c l a s t p i t c h ( $event )
;
$ e v e n t s p e n u l t i m a t e e v e n t >
mc penultimate pitch ( $event )
;
true .
% The f i r s t p i t c h d e p e n d s on w hi c h v o i c e i t i s i n
m c f i r s t p i t c h (E) :
E comment First pitch ,
( E p a r t t o p >
m c f i r s t p i t c h t o p (E)
;
E p a r t bottom >
m c f i r s t p i t c h b o t t o m (E ) ) .
% F i r s t p i t c h i n t o p v o i c e must be f i n a l ( t o n i c ) o r t e n o r ( dominant )
m c f i r s t p i t c h t o p (E) :
E final
;
E scale degree tenor .
% F i r s t p i t c h i n bottom v o i c e must be f i n a l ( t o n i c )
m c f i r s t p i t c h b o t t o m (E) :
E final .
% L a s t p i t c h d e p e n d s on w hi c h v o i c e i t i s i n
m c l a s t p i t c h (E) :
E comment Last pitch ,
( E p a r t t o p >
m c l a s t p i t c h t o p (E)
;
E p a r t bottom >
m c l a s t p i t c h b o t t o m (E ) ) .
30
% L a s t p i t c h i n t o p v o i c e must be f i n a l ( t o n i c ) o r
% t h e t h i r d ( m e d i a n t ) i f t h e r e a r e more t h a n t h r e e v o i c e s
m c l a s t p i t c h t o p (E) :
E final
;
( num parts X , X > 2,
E scale degree 3).
% L a s t p i t c h i n bottom v o i c e must be f i n a l
m c l a s t p i t c h b o t t o m (E) :
E final .
% P e n u l t i m a t e p i t c h d e p e n d s on w hi c h v o i c e i t i s i n
m c p e n u l t i m a t e p i t c h (E) :
E comment Penultimate pitch ,
( E p a r t t o p >
m c p e n u l t i m a t e p i t c h t o p (E)
;
E p a r t bottom >
mc penultimate pitch bottom (E ) ) .
% P e n u l t i m a t e p i t c h i n t o p v o i c e must be l e a d i n g t o n e o r
% f o u r t h d e g r e e ( s u b d o m i n a n t ) i f t h e r e a r e more t h a n two v o i c e s
m c p e n u l t i m a t e p i t c h t o p (E) :
E leading tone
;
E scale degree 2
;
( num parts X , X > 2,
E scale degree 4).
% P e n u l t i m a t e p i t c h i n bottom v o i c e must be 2 nd , 4 t h o r 5 t h s c a l e d e g r e e
mc penultimate pitch bottom (E) :
E scale degree 2
;
E scale degree 4
;
E scale degree 5.
/
TENDENCIES
/
r u l e ( leading tone leads to tonic , tendency , 0 ) :
$ e v e n t l e a d i n g t o n e >
$next t o n i c
;
true .
31
/
MELODIC INTERVALS
/
% Good i n t e r v a l s
r u l e ( Good intervals , melodic intervals , 1 ) :
$prev1 = s t a r t ;
( member ( I , [ 2 , 3 , 4 , 5 ] ) ,
member ( IC , [ min , maj , p e r ] ) ,
$ p r e v 1 : $ e v e n t a i n t e r v a l IC I ) .
r u l e ( Repeated note , melodic intervals , 1 ) :
$prev1 = s t a r t ;
$prev1 : $event a i n t e r v a l unis .
% intervals
r u l e ( Less good intervals , melodic intervals , 1 0 ) :
$prev1 = s t a r t ;
( $ p r e v 1 : $ e v e n t a i n t e r v a l min 6 c o n t o u r up
; $prev1 : $event a i n t e r v a l per 8).
/
IN SCALE
/
% P i t c h must be a member o f t h e c u r r e n t s c a l e
r u l e ( In scale , scale , 0 ) :
$event s c a l e .
32
/
HARMONIC INTERVALS
N ot e s a t s p e c i a l p o s i t i o n s , namely t h e f i r s t , p e n u l t i m a t e and
l a s t n o t e s must be c e r t a i n s c a l e d e g r e e s
/
r u l e ( Good intervals , harmonic intervals , 0 ) :
$vert = []
;
( member ( I , [ 3 , 6 , 8 , 1 0 , 1 3 , 1 5 ] ) ,
member ( IC , [ min , maj , p e r ] ) ,
$ v e r t : $ e v e n t a i n t e r v a l IC I ) .
r u l e ( Diminished 5 th , harmonic intervals , 0 ) :
$vert = []
;
not c u r r e n t s c a l e l y d i a n ,
$ v e r t : $ e v e n t c i n t e r v a l dim 5 .
r u l e ( Perfect intervals , harmonic intervals , 1 0 ) :
member ( I , [ 4 , 5 ] ) ,
$vert : $event c i n t e r v a l per I .
r u l e ( Unison , harmonic intervals , 2 0 ) :
$vert : $event a i n t e r v a l unis .
/
AVOID CERTAIN HARMONIC INTERVALS
/
r u l e ( Penultimate maj6 or min3 , avoid harmonic intervals , 0 ) :
$ e v e n t p e n u l t i m a t e e v e n t >
( not ( $ v e r t : $ e v e n t c i n t e r v a l maj 6
; $ v e r t : $ e v e n t c i n t e r v a l min 3 ) )
;
true .
r u l e ( Avoid , two leading tones , 0 ) :
not ( $ v e r t l e a d i n g t o n e ,
$event leading tone ) .
/
DIRECTION / CONTOUR
/
r u l e ( Step - step , contour , 1 ) :
$prev2 = s t a r t ;
33
/
REPEAT FIRST NOTE
/
r u l e ( Avoid the repeat , repeat first , 0 ) :
$prev1 = s t a r t
;
not $ p r e v 1 : $ e v e n t a i n t e r v a l u n i s .
r u l e ( Let it repeat , repeat first , 1 0 ) :
34
/
REPEATING PATTERNS
/
r u l e ( Avoid two , repeat patterns , 0 ) :
not m c r e p e a t p a t t e r n ( $ p r e v 3 , $ p r e v 2 , $ p r e v 1 , $ e v e n t ) .
r u l e ( Avoid three , repeat patterns , 1 0 ) :
$ p r e v 3 comment repeated two-note sequence ,
not m c r e p e a t p a t t e r n ( $ p r e v 5 , $ p r e v 4 , $ p r e v 3 , $ p r e v 2 , $ p r e v 1 , $ e v e n t ) .
/
RANGE
/
% Each n o t e must s t a y w i t h i n i t s g i v e n p a r t s r a n g e a s s p e c i f i e d i n t h e i n p u t f i
r u l e ( In range , range , 0 ) :
$event part Part ,
r a n g e P a r t t a g Range ,
$ e v e n t i n r a n g e Range .
The GUIDO Music Notation Format [15] was developed as a joint project between
Holger H. Hoos at the Technical University of Darmstadt and Keith A. Hamel at
the University of British Columbia.
It was chosen as the input/output language of the Pelog system for the following
reasons:
It is a very concise and user-friendly language
It is simple to write a parser/grammar for it
There are free tools available for viewing and listening to GUIDO files
It is easily extendible to support features unique to the Pelog system
The Pelog system only supports a subset of the complete specification provided by
Hoos & Hamel. Therefore, while any input and output used by Pelog is in GUIDO
Format, not every file in GUIDO Format can be used by the Pelog system. In
particular, because Pelog is only concerned with (at least at this stage) counterpoint
and the interaction between monophonic voices, it does not support elements such
as:
multiple voices on one staff
accents and other expressive markings
strictly visual features such as beaming, staves and octava markings
This section describes all of the GUIDO language features supported by the Pelog
system1 . Using features beyond what is defined below is not recommended and may
have unpredictable results.
1
This is an adaptation of the complete specification available at http://www.informatik.tudarmstadt.de/AFS/GUIDO
35
3.1
36
Scores
3.2
Parts
3.3
Events
For variable notes (pitches that are left to be filled in by the interpreter):
duration
octave and duration can be omitted. If so, they are inferred from preceding
events. The default octave is 1, and the default duration is the quarter note.
3.4
Notename
3.5
Accidentals
3.6
37
Octave
octave is an integer indicating the octave of the note, where a1 is 440Hz, and
middle c is c1.
Pelog supports octaves in the range [8, +8], though the need for such a large
range is questionable.
If octave is omitted from the note description, it is assumed to be identical to
the last octave specified.
3.7
Duration
3.8
Tags
Tags provide additional information about the score. They are of the form:
\ i d <param l i s t >
The tags must occur within parts. (i.e. inside square brackets [ ].)
The tags supported by Pelog are listed below. Of these, only meter and key are
part of the standard GUIDO specification. The rest are Pelog-specific extensions
to the language.
Individual rule sets may also support proprietary tags to handle unique attributes of a given musical genre.
3.8.1
Key
\key<i>
sets the key signature to i sharps, if i is positive, or to |i| flats, if i is negative. For
i = 0 , the key signature contains no sharps or flats.
\key<s> indicates a key of s, where s is of the form n, n#, or n& for any
note name n. Uppercase and lowercase letters are used to denote major and minor
scales, respectively.
3.8.2
38
Scale
scale <tonicscale>
indicates a scale starting on tonic. tonic is of the form notename described above.
Table 3.1 contains a list of the built-in scale definitions. It is also possible to define
additional scales for a given rule set (see Scale library on page 15).
major
harmonic-minor
natural-minor
melodic-minor
dorian
hypo-dorian
phrygian
hypo-phrygian
lydian
hypo-lydian
mixolydian
hypo-mixolydian
aeolian
hypo-aeolian
locrian
ionian
hypo-ionian
chromatic (-sharps, -flats)
whole-tone (-sharps, -flats)
gypsy
pentatonic (-23, -32)
octatonic (-sharps, -flats)
half-whole (-sharps, -flats)
whole-half (-sharps, -flats)
Table 3.1: built-in scales
A note about key vs. scale: While scale explicitly specifies the set of
pitches allowable in the score, key only specifies the number of sharps or
flats in the key signature. The key and scale of a score are determined
automatically based on the following rules:
If an input score only specifies the key, then the scale defaults to
major with key as the tonic.
If an input score only specifies the scale, the key is chosen based on
rules defined by the given scale. (e.g. d dorian is in the key of c.)
If a score specifies both scale and key, both are transferred as-is to
the output score.
3.8.3
39
Range
\range<range>
selects the allowable range for the current part. This may be one of the built-in
ranges (soprano, alto, tenor, bass) or one of the custom ranges defined in the current
rule set.
3.8.4
Meter
\meter<s>
3.8.5
Accent pattern
\accented beats<s>
sets the accent pattern to s where s is of the form a1 , a2 . . . an , where an are the
accented beats.
The duration of each beat is equal to the denominator of the time signature
specified by the meter tag. If accented beats is not specified, it defaults to the first
beat of the measure.
accented beats is useful for defining things such as French 3/4 where the accented beat is not the first beat of each measure.
3.8.6
Comment style
\comment style<s>
defines the style of comments in the output file where s is one of referenced, lyrics
or none.
There are three ways in which comments about the score can be presented in
the output file:
referenced: Each event is assigned a number using the text tag which will be
displayed under the note in GUIDO NoteViewer. The comments for each of
these notes is listed by event number at the end of the output file. Figure 3.1
shows how referenced comments are displayed in the Pelog-GUI.
lyrics: Each comment is displayed as a lyric underneath each event.
Note: this option is not currently very useful due to a bug in GUIDO NoteViewer which causes the lyrics to write on top of each other.
none: No comments are added to the output file.
40
Tools
4.1
4.1.1
Prolog interpreter
Chosen tool
SWI-Prolog [http://www.swi.psy.uva.nl/projects/SWI-Prolog/]
41
CHAPTER 4. TOOLS
SWI-Prolog was developed at the University of Amsterdam by Jan
Wielemaker. It is free for non-commercial use and runs on Windows
and UNIX platforms. It interprets standard Edinburgh-Prolog, with
large subsets of the ISO-standard, Quintus and SICStus libraries. All
the included modules are upward compatible to Quintus and SICStus,
and most other Quintus modules will run directly in SWI without modifications. It will create stand-alone Prolog applications and has an
extensive C language interface.
Other candidates
B-Prolog [http://www.cad.mse.kyutech.ac.jp/people/zhou/bprolog.html]
There is another freeware Prolog interpreter for Windows called BProlog developed by Neng-Fa Zhou at the Kyushu Institute in Japan.
It is a strong second-choice to SWI-Prolog in most respects. However,
it does not support libraries or garbage collection, nor does it claim to
support as many standards as SWI-Prolog.
BIN-Prolog [www.binnet.com]
Bin-Prolog was originally developed as a free, portable Prolog by Paul
Tarau at the University of Moncton. Since 1994, it is a commercial
product distributed by BinNet corporation in Texas. While the free
product is still available by FTP, it lacks some of the more advanced
features of SWI-Prolog, such as the creation of stand-alones. It is also
only available as a 16-bit Windows application, making interaction with
32-bit Windows applications difficult or impossible.
Quintus Prolog and Sicstus Prolog [www.sics.com]
Quintus and SICStus Prolog seem to be the most popular commercial
Prolog interpreters on the market. Quintus was recently acquired by
SICS and the two products appear to be gradually merging into one.
The advantages of these commercial systems over SWI-Prolog are many,
but few of them are significant. The commercial products have graphical
IDEs, and will create C or Java code from Prolog source. The interface
between these Prologs and other languages and systems is also stronger
than their freeware counterpoints.
4.1.2
42
CHAPTER 4. TOOLS
43
language, any changes made to the code are updated instantly. Not only
is it never necessary to recompile, it is often not necessary to restart execution of the Tcl code.
Ports of Tcl/Tk are available for virtually every major platform, including Windows, UNIX and Macintosh. The core functions are free
and open-sourced. Additional functionality, such as the generation of
stand-alone executables and object-oriented data structures, is included
as part of the commercial Tcl/Tk Pro package.
Other candidates
Java [www.java.sun.com]
Javas new GUI system, Swing, released with Java 1.2, makes it very
easy to create flexible and portable graphical user interfaces. While implementing these interfaces directly with code is quite labour-intensive,
there are a number of free tools available for graphically drawing interface designs. Presumably, however, such tools reduce the amount of
flexibility for future changes to the interface. As well, the fact that Java
is a byte-compiled language means that updating and debugging the
code is a slow and tedious process.
XPCE [http://www.swi.psy.uva.nl/projects/xpce/]
XPCE is a graphical user interface system developed by the authors of
SWI-Prolog. Since it is designed from the ground up for use with Prolog,
it is perhaps the most natural choice. It is available on the all of the
same platforms as SWI-Prolog. Unfortunately, XPCE is a commercial
product, currently costing 300 euros for a single-user academic license.
It is also extremely specialized and proprietary, designed only for the
Prolog domain.
4.1.3
Connecting Tcl/Tk and Prolog is rather involved. A great deal of effort was spent
implementing a set of procedures and predicates for this purpose (see the appendix
on the Tcl/Tk-Prolog Connector System on page 234). While this work was enlightening and a good tutorial on using piping on a Windows system, that approach was
ultimately abandoned. The technique that won out was simply to use the command
line interface. The Tcl/Tk portion of the system starts the Prolog portion by calling
it with all its arguments on the MS-DOS command line. Its simple, easy to debug
and directly ports to UNIX. It doesnt provide any interactivity between Tcl/Tk
and Prolog, but as it turns out, this is not a major drawback. You can see this
mechanism implemented in the rules apply function in the Pelog-GUI code.
CHAPTER 4. TOOLS
4.2
44
Reflections after-the-fact
These reflections are based on my own casual observations during the development
of the Pelog system.
4.2.1
SWI-Prolog
4.2.2
Tcl/Tk
Design notes
The data structure, hereafter referred to as incomplete score, was designed with the
following initiatives:
To allow the most common calculations, such as intervals and scale degrees,
to be straightforward and efficient
To access the horizontal and vertical relationships between events efficiently
To make the conversion to and from the GUIDO music notation language [15]
as elegant and trivial as possible
To allow portions of the score to be unspecified. These unspecified parts can
later be filled in by the solver.
Incomplete score uses A Generalized Model for Encoding Musical Data [32]
as its starting point. From their model comes the representation of events as Prolog
terms and the use of Brinkmans binomial pitch representation. Their text is the
only full length tutorial, to my knowledge, on using Prolog as a tool for musical
research and makes a strong case for the straightforward implementation of musical expert systems. However, their data structure design is based too strongly
on Brinkman [4]. In their implementation, every musical event is asserted into the
Prolog database. Any changes in the database must be explicitly retracted and
asserted. In most Prolog implementations, backtracking over assertions and retractions does not restore the database to its previous state. Schaffer and McGees
45
46
fundamental design choice therefore negates one of the principle features of Prolog,
namely backtracking to compute alternate solutions. Their approach is thus useful
only for analysis, and thus inadequate for generation. By contrast, the incomplete
score uses a binary tree structure to store the events, where the previous states of
variable elements can be restored through backtracking. This allows the same code
to function both as a verifier and generator of musical events.
5.2
A worked example
The most intuitive way to present the incomplete score data structure is through a
case study of how a given musical fragment is encoded. While encoding an incomplete score by hand has been useful for preliminary testing, the next phase of the
project will include a grammar/parser to convert between the internal representation and the GUIDO music notation format [15]. GUIDO has been chosen as the
input/output language for its brevity and ease of use: two things lacking in this
internal representation designed for efficient manipulation by the Prolog system.
Figure 5.1 shows an example musical fragment. Table 5.1 shows its corresponding data representation.
The incomplete score data structure consists of a tree of terms of the form:
event(index, part, pitch, duration, time, previous, vertical, next, comment,
penalty, other )
The tree structure itself is the binary tree implementation included with Quintus
Prolog [5].
Borrowing from object-oriented programming practice, users of the data structure are encouraged (though not forced) to unify with the fields of each event using
a series of predicates with names such as e pitch and e duration etc.
We will now examine each of these arguments one-by-one.
47
t r e e ( e v e n t ( 1 , 1 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 4 ) , time ( 0 , 3 0 0 0 ) , s t a r t ,
[], 2,
,
, )
e v e n t ( 2 , 1 , p i t c h ( 1 1 , 6 ) , d u r ( 1 / 4 ) , time ( 3 0 0 0 , 6 0 0 0 ) , 1 ,
[12, 9], 3,
,
, )
e v e n t ( 3 , 1 , p i t c h ( 9 , 5 ) , d u r ( 1 / 4 ) , time ( 6 0 0 0 , 9 0 0 0 ) , 2 ,
[12], 4,
,
, )
e v e n t ( 4 , 1 , p i t c h ( 5 , 3 ) , d u r ( 1 / 4 ) , time ( 9 0 0 0 , 1 2 0 0 0 ) , 3 ,
[10, 12], 5,
,
, )
e v e n t ( 5 , 1 , p i t c h ( 7 , 4 ) , d u r ( 1 / 8 ) , time ( 1 2 0 0 0 , 1 3 5 0 0 ) , 4 ,
[10], 6,
,
, )
e v e n t ( 6 , 1 , p i t c h ( 9 , 5 ) , d u r ( 1 / 8 ) , time ( 1 3 5 0 0 , 1 5 0 0 0 ) , 5 ,
[13, 10], 7,
,
, )
e v e n t ( 7 , 1 , p i t c h ( 1 1 , 6 ) , d u r ( 1 / 4 ) , time ( 1 5 0 0 0 , 1 8 0 0 0 ) , 6 ,
[13, 10], 8,
,
, )
e v e n t ( 8 , 1 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 2 ) , time ( 1 8 0 0 0 , 2 4 0 0 0 ) , 7 ,
[ 1 3 ] , end ,
,
, )
e v e n t ( 9 , 2 , p i t c h ( 4 , 2 ) , d u r ( 1 / 2 ) , time ( 0 , 6 0 0 0 ) , s t a r t ,
[1], 10,
,
, )
e v e n t ( 1 0 , 2 , p i t c h ( 2 , 1 ) , d u r ( 1 / 1 ) , time ( 6 0 0 0 , 1 8 0 0 0 ) , 9 ,
[3, 12], 11,
,
, )
e v e n t ( 1 1 , 2 , p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) , time ( 1 8 0 0 0 , 2 4 0 0 0 ) , 1 0 ,
[ 8 , 1 3 ] , end ,
,
, )
e v e n t ( 1 2 , 3 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 1 ) , time ( 0 , 1 2 0 0 0 ) , s t a r t ,
[9, 1], 13,
,
, )
e v e n t ( 1 3 , 3 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 1 ) , time ( 1 2 0 0 0 , 2 4 0 0 0 ) , 1 2 ,
[ 5 , 1 0 ] , end ,
,
, )
)
5.2.1
48
Index
e index(?Event, ?Index)
Each event has a unique index reference number. The indexing of elements is
completely arbitrary, as long as all indexes are unique. All of the events are stored
in a binary tree [5] so they can be retrieved by their index in O(lgn) time.1
Figure 5.2 shows example indexes that are likely to occur if the score were
interpreted from GUIDO.
5.2.2
Part
e part(?Event, ?Part)
Part is an integer value indicating which melodic line, or voice, the event belongs
to.
In our example, the first staff, or soprano part, is part 1, the second staff is part
2 and the bottom staff, or cantus firmus is part 3.
5.2.3
Pitch
e pitch(?Event, ?Pitch)
Pitches are stored in a format that is loosely adapted from binomial pitch representation (BPR) [4]. BPR defines musical pitch using four key attributes: pitch
class, note class, continuous pitch class and continuous note class.
1
Originally, an array implementation with constant access time [24] was considered. However,
when changes are made to such an array, the old state is lost and can not be restored through
backtracking. OKeefe himself describes the array implementation as a logical hack that is not
suitable for most logic programming applications. His array implementation is therefore inadequate for the present problem, in which many different subgoals must be examined before arriving
at a globally optimal solution. Some Prolog implementations have array libraries implemented
externally in C which may work for the present purpose.
49
Pitch class is an integer value corresponding to the frequency of the note. One
pitch class degree represents one semi-tone. Pitch class is the standard representation in most electronic music applications, such as MIDI.
Note class is an integer value related to the lines and spaces on the staff. Each
note class degree corresponds to one of the white keys on keyboard instruments. The
historical notation format DARMS uses only note class for pitch representation.
By maintaining both representations, a distinction is made between notes with
the same sounding pitch but different note classes (eg. c# and d&). It also makes
diatonic intervals, which are dependent on note class, much easier to calculate. This
is an important factor when developing systems, such as the present project, with
knowledge of traditional modal and tonal practice.
While pitch class is confined to the range [0 ... 11] and repeats at every octave,
continuous pitch class includes the octave as a factor. The same relationship exists
between note class and continuous note class. Octave numbering follows the convention adopted by GUIDO that the octave containing middle C is numbered 0.
The octaves below are negative and the octaves above are positive.
The relationship between continuous and non-continuous classes is therefore
defined by the following equations:
continuous-pitch-class = pitch-class + 12 octave
continous-note-class = note-class + 7 octave
Each event in the data structure stores a pitch value in a term of the form:
pitch(continuous-pitch-class, continuous-note-class)
Note that only the continuous classes are explicitly stored in the data structure.
Continuous classes are used in calculating intervals, which comprises the majority
of the counterpoint rules in this project. The penalty of converting from continuous
to non-continuous classes, for example to determine if a particular note is a member
of a scale, is quite minimal (modulo arithmetic.) Therefore, it is moderately more
efficient to keep track of continuous classes rather than non-continuous classes with
an octave value.
Figure 5.3 shows the continuous pitch and note classes of all the events in the
example score.
5.2.4
Duration
e duration(?Event, ?Duration)
Every event stores duration information in a term of the following form:
dur(numerator / denominator)
The representation used here is fairly intuitive: durations are written as fractions
of the whole note. This is the same convention used in GUIDO.
Figure 5.4 shows the duration values in the given example.
Figure 5.3: Pitch and note class values in the example score
50
5.2.5
51
Time
e time(?Event, ?Time)
Every event stores the absolute time of its onset and offset. Time is stored in a
term of the form:
time(start, end)
Absolute time is given as an integer value where 0 is the start of the score and
each whole note unit thereafter is a factor of some constant. By default, this constant is 12000, because it is divisible by most of the commonly occurring duration
denominators such as 2, 3, 4, 8, 9 and 16.
The time values of events can be derived automatically, when duration, previous
and next are non-variable, by using the set event time/2 predicate.
Figure 5.5 shows the time values in the example using a time precision constant
of 12000.
5.2.6
e prev(?Event, ?Prev)
e next(?Event, ?Next)
The previous and next values define the horizontal relationships between notes.
The previous and next fields contain the index of the events preceding and following
a given event.
The previous field of the first event in each part contains the atom start. The
next field in the last event of each part contains the atom end.
5.2.7
Vertical
e vertical(?Event, ?Vertical)
The vertical field defines the vertical relationships between notes. Perhaps
counter-intuitively, the vertical field does not contain the indexes of all events occurring at the same time in other parts. Instead, the field contains directed vectors
52
53
to other events that will require vertical relationship examination. The set of vectors in all events represents the minimum number of vectors required to specify all
vertical relationships. The predicate build verticals creates these vectors using the
following criteria:
Eliminate redundant vectors to improve efficiency. Vertical relationships are symmetric, therefore if the vector X Y is defined, the vector
Y X does not need to also be defined. This will prevent the same vertical
relationship from being tested twice.
No vector points to a previously unexamined event. This improves
efficiency by ensuring that vertical rules will never generate new results, but
will only verify that the results of horizontal rules meet vertical criteria.
The former criteria is simple enough to understand on its own. The explain
the latter, however, requires taking a diversion into the concept of time-paths. The
concept of time-path presented here is generally based on Schaffer & McGees [32]
time-spine. The time-path defines the order in which the events will be visited by
the rule-applying mechanism. For lack of a better ordering scheme, the time-path
is created by sorting all the events by their start times. (If experimentation reveals
a more efficient order, this could change.) In our example, the events will be visited
in the order shown in Figure 5.8.
As stated above, the vertical vectors should never point to unvisited events.
Therefore, the vertical vectors are generated from a reversed time-path. The vertical
vectors in the example are given in Figure 5.9
Note that all vectors point either vertically or backwards.
5.2.8
Comments
e comment(?Event, ?Comment)
54
The comment field contains a list of the rules that were applied to the event.
This information is output as comments in the GUIDO code. This field allows
the system to function as a primitive musical expert system, providing feedback
revealing how pitches were chosen or penalties were applied.
This list is implemented as an improper list so that an arbitrary number of
comments can be added throughout execution.
5.2.9
Penalty
e penalty(?Event, ?Penalty)
The penalty field contains the sum of all penalties applied to the event. This, in
conjunction with the comment field, creates a simple musical expert system. The
user can discover which events were severely penalized and in the most need of
improvement.
5.2.10
Other
The Pelog-GUI
6.1.1
pelog-gui.tcl
Code
#
# PELOGGUI
#
###############################################
# MAIN WINDOW
###############################################
# s t a r t g u i i n i t i a l i s e s t h e main e d i t i n g window
proc s t a r t g u i {} {
g l o b a l PG m ain fr am e menuCount t c l p l a t f o r m
t o p l e v e l . m ai n fr a m e
wm w i t h d r a w . m ai nf r a m e
s e t P a r e n t . m ai nf r a m e
# Menu f r a m e
s e t menuframe $ P a r e n t . menuframe
f r a m e $menuframe
pack $menuframe \
a n c h o r nw \
s i d e t o p \
expand 0 \
f i l l x
# Toolbar frame
55
56
57
{command v i e w l o o k a c c e l e r a t o r "Ctrl+L" u n d e r l i n e 0 } }
{{command } { Hear } \
{command v i e w h e a r a c c e l e r a t o r "Ctrl+H" u n d e r l i n e 0 } }
}
c o n s t r u c t m e n u $menuview View $ M e n u S t r i n g ( $menuview )
s e t m e n u r u l e s $menuframe . r u l e s
s e t Menu String ( $menurules ) {
{{ t e a r o f f } { no } { } }
{{command } { S e l e c t r u l e s e t . . . } \
{command r u l e s b r o w s e a c c e l e r a t o r "Ctrl+R" u n d e r l i n e 0 } }
{{command } { Go ! } \
{command r u l e s a p p l y a c c e l e r a t o r "Ctrl+G" u n d e r l i n e 0 } }
}
construct menu $menurules Rules $Menu String ( $menurules )
# Toolbar button d e f i n i t i o n s
construct button $toolbarframe
" file_new " "New"
construct button $toolbarframe
" file_open " "Open"
construct button $toolbarframe
" file_save " "Save"
construct spacer $toolbarframe
construct button $toolbarframe
" edit_cut " "Cut"
construct button $toolbarframe
" edit_copy " "Copy"
construct button $toolbarframe
" edit_paste " " Paste "
construct spacer $toolbarframe
construct button $toolbarframe
" view_look " "Look"
construct button $toolbarframe
" view_hear " "Hear"
construct spacer $toolbarframe
construct button $toolbarframe
" rules_apply " "Go"
construct spacer $toolbarframe
button $ t o o l b a r f r a m e . browse \
t e x t { S e l e c t r u l e s e t : } \
command { r u l e s b r o w s e } \
c u r s o r hand2 \
h e i g h t 1
pack $ t o o l b a r f r a m e . b r o w s e \
a n c h o r w \
s i d e l e f t \
expand 0 \
f i l l x
58
59
PG ( menuCount )
f o r e a c h cmd $ c m d L i s t {
s w i t c h [ l i n d e x $cmd 0 ] {
" separator " {
s e t d o i t " $menubutton add separator [ lindex $cmd 2]"
eval $doit
}
" tearoff " {
i f { [ s t r i n g match [ l i n d e x $cmd 1 ] "no" ] } {
$menubutton c o n f i g u r e t e a r o f f no
}
}
" command " {
s e t d o i t " $menubutton add [ lindex $cmd 0] \
l a b e l { [ l i n d e x $cmd 1 ] } \
[ l i n d e x $cmd 2 ] "
eval $doit
}
" cascade " {
i n c r PG ( menuCount ) ;
s e t newmenu $menubutton . m$ PG ( menuCount )
s e t d o i t " $menubutton add cascade \
l a b e l { [ l i n d e x $cmd 1 ] } \
menu $newmenu"
eval $doit
menu $newmenu
a d d i t e m s t o m e n u $newmenu [ l i n d e x $cmd 2 ]
}
}
}
}
###############################################
# TOOLBAR BUTTON CONSTRUCTION
###############################################
# c r e a t e s an image b u t t o n
p r o c c o n s t r u c t b u t t o n { Name f i l e cmd h e l p m s g } {
s e t im [ image c r e a t e p h o t o f i l e $ f i l e ]
b u t t o n $Name \
image $im \
t e x t $ he lp m s g \
j u s t i f y center \
c u r s o r hand2 \
r e l i e f raised \
command "$cmd" \
padx 0 pady 0
pack $Name \
60
61
t e x t $Name . t e x t F r a m e . l e f t . t e x t \
y s c r o l l c o m m a n d " $Name . textFrame . right . vertScrollbar set" \
f o n t { { A n d a l e Mono } 9 { n o r m a l }}
pack $Name . t e x t F r a m e . l e f t . t e x t \
a n c h o r c e n t e r \
expand 1 \
f i l l bo t h \
s i d e l e f t
b i n d $Name . t e x t F r a m e . l e f t . t e x t < K e y P r e s s> m a k e d i r t y
update
}
###############################################
# SPLASH WINDOW
###############################################
# d i s p l a y s t h e s p l a s h window
proc sp lash {} {
wm w i t h d r a w .
toplevel . splash
f r a m e . s p l a s h . m ain frame
pack . s p l a s h . m ainfra m e
wm t i t l e . s p l a s h { }
wm r e s i z a b l e . s p l a s h 0 0
focus force . splash
b u t t o n . s p l a s h . mai nf ra m e . main \
image [ image c r e a t e p h o t o f i l e " images / splash .gif" ] \
b o r d e r w i d t h 0 \
command s p l a s h b u t t o n
pack . s p l a s h . m a inf ra m e . main
update
}
# r e m o v e s t h e s p l a s h window
proc splash button {} {
wm w i t h d r a w . s p l a s h
start gui
}
###############################################
# FILE FUNCTIONS
###############################################
PG ( f i l e ) [ t k g e t O p e n F i l e \
d e f a u l t e x t e n s i o n . gmn \
f i l e t y p e s { { {GUIDO Music N o t a t i o n } { . gmn } } } \
p a r e n t . m a i n fr a m e \
t i t l e { Open . . . } ]
i f { $ PG ( f i l e ) == "" } { r e t u r n }
i f { ! [ f i l e r e a d a b l e $ PG ( f i l e ) ] } {
s h o w e r r o r "File \[ $_PG(file )\] is not readable ."
return
}
i f { [ c a t c h "open $_PG(file ) r" f d ] } {
s h o w e r r o r " Error while opening $_PG(file ): \[ $fd \]"
return
}
. m ain fr am e . t e x t F r a m e . l e f t . t e x t i n s e r t end [ r e a d $ f d ]
c l o s e $fd
update titlebar
update
}
# o p e n s t h e c u r r e n t f i l e from d i s k i n t o t h e e d i t o r
proc f i l e r e v e r t {} {
g l o b a l PG
62
63
i f { $ PG ( d i r t y ) == 1} {
s e t outcome l o s e c h a n g e s
i f { $outcome == "no" } { r e t u r n }
}
s e t PG ( d i r t y ) 0
. m a inf ram e . t e x t F r a m e . l e f t . t e x t d e l e t e 1 . 0 end
i f { [ c a t c h "open $_PG(file ) r" f d ] } {
s h o w e r r o r " Error while opening $_PG(file ): \[ $fd \]"
return
}
. m ain fr am e . t e x t F r a m e . l e f t . t e x t i n s e r t end [ r e a d $ f d ]
c l o s e $fd
}
# saves the curre nt f i l e .
proc f i l e s a v e {} {
g l o b a l PG
I f no f i l e n a m e e x i s t s , prompts f o r one
i f { $ PG ( d i r t y ) == 0} { r e t u r n }
i f { $ PG ( f i l e ) == "" } { f i l e s a v e a s }
i f { [ f i l e e x i s t s $ PG ( f i l e ) ] } {
i f { ! [ f i l e w r i t a b l e $ PG ( f i l e ) ] } {
s h o w e r r o r "File \[ $PG(file )\] is not writable ."
return
}
}
i f { [ c a t c h "open $_PG(file ) w" f d ] } {
s h o w e r r o r " Error writing to $_PG(file ): \[ $fd \]"
return
}
p u t s $ f d "[. mainframe . textFrame .left.text get 1.0 end ]"
c l o s e $fd
set
PG ( d i r t y ) 0
}
# Prompts f o r a new f i l e n a m e and s a v e s t o d i s k
proc f i l e s a v e a s {} {
g l o b a l PG
set
PG ( f i l e ) [ t k g e t S a v e F i l e \
d e f a u l t e x t e n s i o n . gmn \
f i l e t y p e s { { {GUIDO Music N o t a t i o n } { . gmn } } } \
p a r e n t . m ai n fr am e \
t i t l e { Save As . . . } \
i n i t i a l f i l e $ PG ( f i l e ) ]
64
file save
update titlebar
update
}
# c l o s e s t h e P e l o g GUI
proc f i l e e x i t {} {
exit
}
# I f the t e x t e d i t o r i s d i r t y , ensure that the user i s i n t e n d s to l o s e
# changes
proc lose changes {} {
tk messageBox \
d e f a u l t no \
t y p e y e s n o \
message { Any u n s a v e d c h a n g e s w i l l be l o s t . P r o c e e d anyway ? } \
p a r e n t . m a i n fr a m e \
t i t l e { L o s e c h a n g e s ?}
}
###############################################
# EDITING ( CLIPBOARD ) FUNCTIONS
###############################################
proc e d i t c u t {} {
t k t e x t C u t . ma in fr a m e . t e x t F r a m e . l e f t . t e x t
}
proc e d i t c o p y {} {
t k t e x t C o p y . m ai nf r a m e . t e x t F r a m e . l e f t . t e x t
}
proc e d i t p a s t e {} {
t k t e x t P a s t e . m a i nf ra m e . t e x t F r a m e . l e f t . t e x t
}
proc e d i t c l e a r {} {
. m ai nf ram e . t e x t F r a m e . l e f t . t e x t d e l e t e s e l . f i r s t
}
###############################################
# VIEWING FUNCTIONS
###############################################
proc view look {} {
g l o b a l PG
save temp
e x e c command . com / c gmnview . e x e temp . gmn &
sel . last
65
update
}
proc view hear {} {
g l o b a l PG
save temp
c a t c h "exec gmn2midi .exe temp.gmn"
update
c a t c h "exec mplayer .exe - play temp.mid &"
update
}
# s a v e s t h e c o n t e n t s o f t h e e d i t o r i n a t e m p o r a r y f i l e s o i t can
# be v i e w e d by an e x t e r n a l program
proc save temp {} {
s e t f i l e temp . gmn
i f {[ f i l e exists $file )]} {
i f {![ f i l e writable $file )]} {
s h o w e r r o r "File \[ $file \] is not writable ."
return
}
}
i f { [ c a t c h "open $file w" f d ] } {
s h o w e r r o r " Error writing to $file : \[ $fd \]"
return
}
p u t s $ f d "[. mainframe . textFrame .left.text get 1.0 end ]"
c l o s e $fd
}
###############################################
# RULES FUNCTIONS
###############################################
proc r u l e s a p p l y {} {
g l o b a l PG
i f { $ PG ( r u l e s ) == "" } {
s h o w e r r o r "You need to specify a rule set before you can apply rules ."
rules browse
}
s e t outcome [ t k m e s s a g e B o x \
d e f a u l t ok \
t y p e o k c a n c e l \
message " Apply $_PG( rules ) to the currently open buffer ?" \
p a r e n t . m ai n fr am e \
t i t l e { App l y r u l e s ? } ]
66
set
}
###############################################
# HELPER FUNCTIONS
###############################################
# c a l l e d when a cha nge i s made i n t h e e d i t o r
proc make dirty {} {
g l o b a l PG
s e t PG ( d i r t y ) 1
}
# c a l l e d when t h e name o f t h e open f i l e c h a n g e s
proc u p d a t e t i t l e b a r {} {
g l o b a l PG
wm t i t l e . ma inf r am e " Pelog GUI - $_PG(file )"
update
}
# c a l l e d when t h e name o f t h e r u l e s e t c h a n g e s
proc u p d a t e r u l e s e t {} {
g l o b a l PG
PG ( menuCount ) 0 ;
PG ( f i l e ) ""
PG ( r u n s l a v e ) ""
PG ( f i l e ) ""
PG ( t e m p f i l e ) ""
PG ( f i l e k e y n a m e ) ""
PG ( d i r t y ) 0
PG ( r u l e s ) ""
PG ( h a s s l a v e ) ""
6.2
6.2.1
Code
/
PRELIMINARY MAIN MODULE
67
68
M i c h a e l Droettboom
May 1 9 9 9
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( library /main.pl ) .
: e n s u r e l o a d e d ( core/main.pl ) .
: e n s u r e l o a d e d ( guido /main.pl ) .
: d i s c o n t i g u o u s
r u l e d e f /3,
r u l e /2,
g l o b a l d e f a u l t /2.
: m u l t i f i l e
g l o b a l d e f a u l t /2.
PRIVATE PREDICATES
/
a l l s o l u t i o n s (T , TimePath ) :
start penalty , ! ,
o r g a n i z e r u l e s ( Rules ) , nl ,
w r i t e ( Time Path : ) , w r i t e ( TimePath ) , n l , n l ,
69
apply rules1 ( ,
, [ ] , p e n a l t y (P , P ) ) .
a p p l y r u l e s 1 ( E , Param , [ R u l e C l a s s | R u l e T a i l ] , p e n a l t y ( P e n I n , PenOut ) ) :
g l o b a l s e t ( d i r e c t i o n , forward ) ,
a p p l y r u l e s 1 ( E , Param , R u l e T a i l , p e n a l t y ( P e n I n , PenOut0 ) ) ,
( e p i t c h (E , Pitch ) ,
n o n v a r ( P i t c h ) >
onc e ( ( member ( R u l e L e v e l , R u l e C l a s s ) ,
a p p l y r u l e c l a s s ( E , Param , R u l e L e v e l , p e n a l t y ( PenOut0 , Pe
;
( member ( R u l e L e v e l , R u l e C l a s s ) ,
a p p l y r u l e c l a s s ( E , Param , R u l e L e v e l , p e n a l t y ( PenOut0 , PenOut ) ) ) )
;
([[( ,
,
, FailTo ) | ] | ] = RuleClass ,
a p p l y r u l e c l a s s f a i l ( F a i l T o , Param ) ) .
a p p l y r u l e c l a s s ( E , Param , R u l e L e v e l , P e n a l t y ) :
( member ( ( PenValue , R u l e , C l a s s , F a i l T o ) , R u l e L e v e l ) ,
a p p l y r u l e ( E , Param , PenValue , C l a s s , R u l e , F a i l T o , P e n a l t y ) ) .
apply rule class fail (1, ) : !, fail .
apply rule class fail (11, ) : !, f a i l .
a p p l y r u l e c l a s s f a i l ( F a i l T o , Param ) :
global ( d i r e c t i o n , forward ) ,
70
w r i t e ( Special Fail : ) , w r i t e ( F a i l T o ) ,
a r g ( F a i l T o , Param , X ) ,
e i n d e x (X , F a i l T o I n d e x ) ,
w r i t e ( Going back to --> ) , w r i t e ( F a i l T o I n d e x ) , n l ,
g l o b a l s e t ( d i r e c t i o n , backward ) ,
f a i l ( FailToIndex ).
a p p l y r u l e ( E , Param , P e n a l t y , C l a s s , R u l e ,
, p e n a l t y ( P e nI n , PenOut ) ) :
e i n d e x (E , Index ) ,
g e t r e p ( E , PName , Octave ) ,
( r u l e ( R u l e , C l a s s , Param) >
( a d d p e n a l t y ( P e n a l t y , P e n I n , PenOut ) ,
w r i t e ( Event ) , w r i t e ( I n d e x ) , w r i t e ( : ) ,
w r i t e (PName ) , w r i t e ( Octave ) ,
w r i t e ( --> ) ,
w r i t e ( C l a s s ) , w r i t e ( :: ) , w r i t e ( R u l e ) , n l ) ;
( w r i t e ( Event ) , w r i t e ( I n d e x ) , w r i t e ( : ) ,
w r i t e (PName ) , w r i t e ( Octave ) ,
w r i t e ( -/> ) , w r i t e ( C l a s s ) ,
w r i t e ( :: ) , w r i t e ( R u l e ) , n l , f a i l )
).
g e t r e p ( E , ~ , ) :
e p i t c h (E , P ) ,
v a r (P ) , ! .
g e t r e p ( E , PName , Octave ) :
p i t c h n a m e o c t a v e ( E , PName , Octave ) , ! .
6.2.2
main.pl
Code
%%%%%%%%%%%%%%%%%%%%%%%
% CORE MAIN MODULE
%%%%%%%%%%%%%%%%%%%%%%%
% Simply ens ure s that a l l the Pelog core l i b r a r i e s are loaded
:
:
:
:
:
:
ensure
ensure
ensure
ensure
ensure
ensure
6.2.3
l o a d e d ( b imath ) .
l o a d e d ( message ) .
loaded ( preprocessor ).
l o a d e d ( r u l ed e f ) .
loaded ( score ).
loaded ( trees ).
bi-math.pl
Code
/
BIDIRECTIONAL MATH LIBRARY
G e n e r a t i v e math f u n c t i o n s
71
Some o f t h e s e f u n c t i o n s a r e i n t e g e r v e r s i o n s o f t h e Q u i n t u s
f l o a t i n gp o i n t math l i b r a r y , math . p l
M i c h a e l Droettboom
May 1 9 9 9
/
/
OPPOSITE
o p p o s i t e ( ?X , ? Y)
m a i n t a i n s X = Y
/
o p p o s i t e (X , Y) :
n o n v a r (X ) ,
Y is X 1, !.
o p p o s i t e (X , Y) :
n o n v a r (Y ) ,
X is Y 1, !.
/
FACTOR
f a c t o r ( ?X , ? Y , ? Z , ? F )
m a i n t a i n s X = Z F + Y , where Y < Z , 8 < F < 8 .
/
, 0).
factor (0, 0,
f a c t o r (X , X , Z , 0 ) :
n o n v a r (Z ) ,
g e t i n t e g e r (X ) ,
X > 0,
X < Z.
f a c t o r (X , Y , Z , F ) :
nonvar (X ) ,
nonvar (Z ) ,
X \= 0,
Y1 i s X mod Z ,
F1 i s X / / Z ,
( Y1 < 0 >
(Y i s Y1 + Z ,
F i s F1 1 )
;
(Y i s Y1 , F i s F1 ) ) ,
F \= 0.
f a c t o r (X , Y , Z , F ) :
v a r (X ) ,
n o n v a r (Y ) ,
n o n v a r (Z ) ,
Testing
1 ? [ b imath ] .
b imath c o m p i l e d , 0 . 0 0 s e c , 2 , 2 3 6 b y t e s .
Yes
2 ? o p p o s i t e ( 5 , X ) .
X = 5 ;
No
3 ? o p p o s i t e (Y , 3 ) .
Y = 3 ;
No
4 ? o p p o s i t e ( 1 2 , X ) .
X = 12 ;
No
5 ? o p p o s i t e (X , Y ) .
No
6 ? o p p o s i t e ( 5 , 3 ) .
No
7 ? f a c t o r ( 0 , X , Y , Z ) .
X = 0
Y = G297
Z = 0 ;
No
8 ? f a c t o r ( 1 , X , Y , Z ) .
No
9 ? f a c t o r ( 1 , X , Y , 0 ) .
No
10 ? f a c t o r (X , Y , 3 , 0 ) .
72
73
6.2.4
Code
preprocessor.pl
74
75
/
PREPROCESSOR FOR RULE FILES
M i c h a e l Droettboom
May 1 9 9 9
/
/
T h i s p r ep r o c e s s o r module i s d e s i g n e d t o make w r i t i n g r u l e s e t s a s c o n c i s e a s
p o s s i b l e . E v e r y r u l e i s s e n t a l a r g e number o f v a r i a b l e s by t h e r u l e
i n t e r p r e t e r . Rather than l i s t i n g a l l t h e s e parameters with e v e r y
r u l e , t h e p r e p r o c e s s o r p e r f o r m s some magic s o t h a t r u l e s o n l y need make
r e f e r e n c e t o metav a r i a b l e s t h a t a r e l i n k e d a t c o m p i l et i m e t o t h e
a c t u a l v a r i a b l e s . These metav a r i a b l e s by c o n v e n t i o n a l w a y s b e g i n
w i t h $ and a r e d e f i n e d i n p r e a t o m d e f / 2 .
For e x a m p l e , t h e f o l l o w i n g r u l e d e f i n i t i o n :
r u l e ( i n s c a l e , min ) :
$event s c a l e .
becomes :
r u l e d e f ( i n s c a l e , min ) % u s e d o n l y f o r i n f o r m a t i o n
r u l e ( i n s c a l e , a r g (A , B , C , D , E , F , G , H , I , J ) ) :
A scale .
s i n c e $ e v e n t i s d e f i n e d a s t h e f i r s t argument i n t h e term a r g ( ) .
/
TERM EXPANSION
T h i s i s a hook p r e d i c a t e i n t o t h e term r e a d i n g p r o c e s s t h a t
o c c u r s when c o n s u l t i n g a f i l e .
/
t e r m e x p a n s i o n ( ( r u l e (X , A , B ) : G o a l s ) ,
[ ( r u l e d e f (X , A , B , C ) ) , ( r u l e (X , A , A r g s ) : R e p l a c e d G o a l s ) ] ) :
f u n c t o r ( Args , a r g s , 1 2 ) ,
p r e r e p l a c e a t o m s ( Goals , ReplacedGoals , Args , 0 , C ) .
/
REPLACING METAVARIABLES
pre re p lac e at om s /3 searches through a l i s t of goals , r e p l a c i n g
a l l metav a r i a b l e s d e f i n e d i n p r e a t o m d e f / 2 w i t h t h e
a p p r o p r i a t e v a r i a b l e argument .
/
76
p r e r e p l a c e a t o m s ( G o a l s , R e p l a c e d G o a l s , A r g s , L I n , LOut ) :
Goals = . . GoalList ,
p r e r e p l a c e a t o m s 1 ( G o a l L i s t , R e p l a c e d G o a l L i s t , A r g s , L I n , LOut ) ,
ReplacedGoals =. . ReplacedGoalList .
pre replace atoms1 ( [ ] , [ ] ,
, L, L).
p r e r e p l a c e a t o m s 1 ( [ X | I n R e s t ] , [ Y | OutRest ] , A r g s , L I n , LOut ) :
p r e a t o m (X , ArgNo ) ,
a r g ( ArgNo , A r g s , Y ) ,
( ArgNo > L I n > L0 = ArgNo ; L0 = L I n ) ,
p r e r e p l a c e a t o m s 1 ( I n R e s t , OutRest , A r g s , L0 , LOut ) .
p r e r e p l a c e a t o m s 1 ( [ X | I n R e s t ] , [ Y | OutRest ] , A r g s , L I n , LOut ) :
n o t a t o m i c (X ) ,
p r e r e p l a c e a t o m s (X , Y , A r g s , L I n , L0 ) ,
p r e r e p l a c e a t o m s 1 ( I n R e s t , OutRest , A r g s , L0 , LOut ) .
p r e r e p l a c e a t o m s 1 ( [ X | I n R e s t ] , [ X | OutRest ] , A r g s , L I n , LOut ) :
p r e r e p l a c e a t o m s 1 ( I n R e s t , OutRest , A r g s , L I n , LOut ) .
p r e a t o m (X , Y) :
n o n v a r (X ) ,
p r e a t o m d e f (X , Y ) .
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
pre
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
atom
def ( $event , 1 ) .
d e f ( $e , 1 ) .
def ( $prev1 , 2 ) .
d e f ( $p1 , 2 ) .
def ( $prev2 , 3 ) .
d e f ( $p2 , 3 ) .
def ( $prev3 , 4 ) .
d e f ( $p3 , 4 ) .
def ( $prev4 , 5 ) .
d e f ( $p4 , 5 ) .
def ( $prev5 , 6 ) .
d e f ( $p5 , 6 ) .
def ( $prev6 , 7 ) .
d e f ( $p6 , 7 ) .
def ( $prev7 , 8 ) .
d e f ( $p7 , 8 ) .
def ( $prev8 , 9 ) .
d e f ( $p8 , 9 ) .
def ( $vert , 1 0 ) .
d e f ( $v , 1 0 ) .
def ( $events , 1 1 ) .
d e f ( $x , 1 1 ) .
def ( $next , 1 2 ) .
/
PACKAGING PARAMETERS
T h i s i s t h e o t h e r s i d e o f t h e p r e p r o c e s s o r . The r u l e i n t e r p r e t o r
uses t h i s p r e d i c a t e to package a l l of the n e c e s s a r y i n f o r m a t i o n
to send to the r u l e s .
77
/
p a c k a g e p a r a m e t e r (T , E , V , Param ) :
f u n c t o r ( Param , a r g s , 1 2 ) ,
a r g ( 1 , Param , E ) ,
g e t p r e v e v e n t (T , E , EP1 ) ,
g e t p r e v e v e n t (T , EP1 , EP2 ) ,
g e t p r e v e v e n t (T , EP2 , EP3 ) ,
g e t p r e v e v e n t (T , EP3 , EP4 ) ,
g e t p r e v e v e n t (T , EP4 , EP5 ) ,
g e t p r e v e v e n t (T , EP5 , EP6 ) ,
g e t p r e v e v e n t (T , EP6 , EP7 ) ,
g e t p r e v e v e n t (T , EP7 , EP8 ) ,
a r g ( 2 , Param , EP1 ) ,
a r g ( 3 , Param , EP2 ) ,
a r g ( 4 , Param , EP3 ) ,
a r g ( 5 , Param , EP4 ) ,
a r g ( 6 , Param , EP5 ) ,
a r g ( 7 , Param , EP6 ) ,
a r g ( 8 , Param , EP7 ) ,
a r g ( 9 , Param , EP8 ) ,
(V \= [] >
( g e t l a b e l (V , T , V e r t ) ,
a r g ( 1 0 , Param , V e r t ) )
;
a r g ( 1 0 , Param , [ ] ) ) ,
a r g ( 1 1 , Param , ( T , E ) ) ,
g e t n e x t e v e n t (T , E , N ) ,
a r g ( 1 2 , Param , N ) .
Testing
2 ? [ rule-sets/ modal_counterpoint .pl ] .
r u l es e t s / m o d a l c o u n t e r p o i n t . p l c o m p i l e d , 0 . 1 7 s e c , 1 2 , 1 4 4 b y t e s .
Yes 3 ? l i s t i n g ( r u l e ) .
78
; true
).
r u l e ( Aeolian , special cases for modes , a r g s (A , B , C ,
D, E , F , G, H, I , J , K, L)) :
( c u r r e n t s c a l e M a e o l i a n
> m c a e o l i a n s p e c i a l (A , B)
; true
).
r u l e ( Good intervals , melodic intervals , a r g s (A , B , C ,
D, E , F , G, H, I , J , K, L)) :
( B=s t a r t
; member (M, [ 2 , 3 , 4 , 5 ] ) ,
member (N , [ min , maj , p e r ] ) ,
B : A a i n t e r v a l NM
).
r u l e ( Repeated note , melodic intervals , a r g s (A , B , C ,
D, E , F , G, H, I , J , K, L)) :
( B=s t a r t
; B:A a i n t e r v a l unis
).
r u l e ( Less good intervals , melodic intervals , a r g s (A ,
B, C , D, E , F , G, H, I , J , K, L)) :
( B=s t a r t
; B : A a i n t e r v a l min 6 c o n t o u r up
; B : A a i n t e r v a l p e r 8
).
r u l e ( In scale , s c a l e , a r g s (A , B , C , D , E , F , G , H , I , J ,
K, L)) :
A s c a l e . r u l e ( Good intervals , harmonic intervals , a r g s (A ,
B, C , D, E , F , G, H, I , J , K, L)) :
( J =[]
; member (M, [ 3 , 6 , 8 , 1 0 , 1 3 , 1 5 ] ) ,
member (N , [ min , maj , p e r ] ) ,
J : A a i n t e r v a l NM
).
r u l e ( Diminished 5 th , harmonic intervals , a r g s (A , B , C ,
D, E , F , G, H, I , J , K, L)) :
( J =[]
; n o t c u r r e n t s c a l e M l y d i a n ,
J : A c i n t e r v a l dim 5
).
r u l e ( Perfect intervals , harmonic intervals , a r g s (A , B ,
C , D, E , F , G, H, I , J , K, L)) :
member (M, [ 4 , 5 ] ) ,
J : A c i n t e r v a l p e r M. r u l e ( Unison , harmonic intervals ,
a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
J : A a i n t e r v a l u n i s . r u l e ( Penultimate maj6 or min3 , avoid
h a r m o n i c i n t e r v a l s , args(A, B, C, D, E, F, G, H, I, J, K, L )) :( A penultimate event
> n o t ( (
( J : A c i n t e r v a l maj 6
; J : A c i n t e r v a l min 3
)
))
; true
).
r u l e ( Avoid , two leading tones , a r g s (A , B , C , D , E , F ,
G, H, I , J , K, L)) :
not ( (
79
J leading tone ,
A leading tone
)).
r u l e ( Step - step , c o n t o u r , a r g s (A , B , C , D , E , F ,
G, H, I , J , K, L)) :
( C=s t a r t
; C:B a i n t e r v a l step ,
B:A a i n t e r v a l step ,
C commentStep - step
).
r u l e ( X - skip : same direction , c o n t o u r , a r g s (A , B , C , D ,
E , F , G, H, I , J , K, L)) :
( C=s t a r t
; C : B c o n t o u r M,
B : A a i n t e r v a l s k i p c o n t o u r M,
C commentX - skip : same direction
).
r u l e ( Skip - skip : opposite direction , c o n t o u r , a r g s (A ,
B, C , D, E , F , G, H, I , J , K, L)) :
( C=s t a r t
; C : B a i n t e r v a l s k i p c o n t o u r M,
B:A a i n t e r v a l s k i p contour N,
M\=N
).
r u l e ( Ensure not , outline a tritone , a r g s (A , B , C , D ,
E , F , G, H, I , J , K, L)) :
( C=s t a r t
; C : B c o n t o u r M,
B:A contour M
> n o t C : A a i n t e r v a l t r i N
; true
).
r u l e ( Two skips that form a triad , c o n t o u r , a r g s (A , B , C ,
D, E , F , G, H, I , J , K, L)) :
C : B a i n t e r v a l M 3 c o n t o u r N ,
B : A a i n t e r v a l O 3 c o n t o u r N , ! .
r u l e ( Avoid the repeat ,
repeat first , a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
( B=s t a r t
; not B:A a i n t e r v a l u n i s
).
r u l e ( Let it repeat , repeat first , a r g s (A , B , C , D , E ,
F , G, H, I , J , K, L)) :
B : A a i n t e r v a l u n i s . r u l e ( Avoid two , repeat patterns ,
a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
n o t m c r e p e a t p a t t e r n (D , C , B , A ) .
r u l e ( Avoid three ,
repeat patterns , a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
not m c r e p e a t p a t t e r n (F , E , D, C , B , A ) .
r u l e ( Avoid four ,
repeat patterns , a r g s (A , B , C , D , E , F , G , H , I , J , K , L ) ) :
n o t m c r e p e a t p a t t e r n (H , G , F , E , D , C , B , A ) .
r u l e ( In
r a n g e , range , args(A, B, C, D, E, F, G, H, I, J, K, L )) :A p a r t M,
r a n g e M t a g N ,
A i n r a n g e N.
Yes 4 ? l i s t i n g ( r u l e d e f ) .
r u l e d e f ( First -Last- Penultimate , c h r o n o l o g i c a l , 0 ) .
80
This test runs the modal counterpoint.pl rule set through the preprocessor and
then lists the created rule and rule def predicates.
6.2.5
rule-def.pl
Code
/
RULE DEFINITION LIBRARY
P r e d i c a t e s f o r managing t h e r u l e d e f i n i t i o n d a t a b a s e .
M i c h a e l Droettboom
May 1 9 9 9
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( p r e p r o c e s s o r ) .
/
LOAD RULES
/
l o a d r u l e s (X) :
c o n s u l t (X ) .
81
/
MANAGEMENT FUNCTIONS
/
r u l e p e n a l t y (R , P e n a l t y ) : r u l e d e f (R , P e n a l t y ) .
o r g a n i z e r u l e s ( RulePath ) :
r u l e o r d e r ( Order0 ) ,
r e v e r s e ( Order0 , Order ) ,
f i n d a l l ( L1 ,
( member (X , O r d e r ) ,
f i n d a l l ((S , N, X, FailTo ) ,
( r u l e d e f (N , X , P , F a i l T o ) ,
(P = min > S i s 1 ; S i s P ) ) ,
L),
s o r t ( L , L0 ) ,
o r g a n i z e r u l e s 0 ( L0 , L1 ) ) ,
RulePath ) ,
w r i t e ( Rule path : ) , w r i t e ( R u l e P a t h ) , n l .
organize rules0 ( [ ] , [ ] ) .
o r g a n i z e r u l e s 0 ( [ ( P , A , B ) | T ] , [ HO|TO] ) :
o r g a n i z e r u l e s 0 (P , [ ( P , A , B ) | T ] , HO , R e s t ) ,
o r g a n i z e r u l e s 0 ( R e s t , TO ) .
o r g a n i z e r u l e s 0 (P , [ ( P , A , B ) | T ] , [ ( P , A , B ) | TO ] , R e s t ) :
o r g a n i z e r u l e s 0 (P , T , TO , R e s t ) .
o r g a n i z e r u l e s 0 ( P0 , [ ( P1 , A , B ) | T ] , [ ] , [ ( P1 , A , B ) | T ] ) :
P0 \= P1 .
organize rules0 ( , [ ] , [ ] , [ ] ) .
Testing
2 ? go ( example .gmn , exampleout .gmn ,
rule-sets/ modal_counterpoint .pl ) .
r u l es e t s / m o d a l c o u n t e r p o i n t . p l
c o m p i l e d , 0 . 1 1 s e c , 1 2 , 1 4 4 b y t e s . P a r s e OK R u l e p a t h : [ [ [ ( 1 , E n s u r e
n o t , o u t l i n e a t r i t o n e ) ] ] , [ [ ( 0 , A v o i d two , r e p e a t p a t t e r n s ) ] , [ ( 1 0 ,
Avoid t h r e e , r e p e a t p a t t e r n s ) ] , [ ( 2 0 , Avoid f o u r , r e p e a t p a t t e r n s ) ] ] ,
[ [ ( 0 , Avoid the r e p e a t , r e p e a t f i r s t ) ] , [ ( 1 0 , Let i t r e p e a t , r e p e a t
f i r s t ) ] ] , [ [ ( 1 , S t e p s t e p , c o n t o u r ) , ( 1 , X s k i p : same d i r e c t i o n ,
c o n t o u r ) ] , [ ( 3 , S k i p s k i p : o p p o s i t e d i r e c t i o n , c o n t o u r ) ] , [ ( 5 , Two
s k i p s t h a t form a t r i a d , c o n t o u r ) ] ] , [ [ ( 0 , A v o i d , two l e a d i n g
t o n e s ) ] ] , [ [ ( 0 , P e n u l t i m a t e maj6 o r min3 , a v o i d h a r m o n i c
i n t e r v a l s ) ] ] , [ [ ( 0 , D i m i n i s h e d 5 t h , h a r m o n i c i n t e r v a l s ) , ( 0 , Good
i n t e r v a l s , harmonic i n t e r v a l s ) ] , [ ( 1 0 , P e r f e c t i n t e r v a l s , harmonic
i n t e r v a l s ) ] , [ ( 2 0 , Unison , harmonic i n t e r v a l s ) ] ] , [ [ ( 0 , l e a d i n g tone
l e a d s to t o n i c , tendency ) ] ] , [ [ ( 0 , In s c a l e , s c a l e ) ] ] , [ [ ( 0 , In
r a n g e , r a n g e ) ] ] , [ [ ( 1 , Good i n t e r v a l s , m e l o d i c i n t e r v a l s ) , ( 1 ,
R e p e a t e d n o t e , m e l o d i c i n t e r v a l s ) ] , [ ( 1 0 , L e s s good i n t e r v a l s ,
m e l o d i c i n t e r v a l s ) ] ] , [ [ ( 0 , A e o l i a n , s p e c i a l c a s e s f o r modes ) ] ] , [ [
( 0 , F i r s tL a s tP e n u l t i m a t e , c h r o n o l o g i c a l ) ] ] ]
82
This testing shows the rule path created for the modal counterpoint.pl rule set.
6.2.6
score.pl
Code
/
SCORE DATA STRUCTURE
M i c h a e l Droettboom
May 1 9 9 9
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( ../ library / event .pl ) .
: e n s u r e l o a d e d ( t r e e s ) .
% c r e a t e s a TimePath and s e t s up t h e v e r t i c a l r e l a t i o n s h i p s i n a
% score
i n i t s c o r e (T , TimePath ) :
t i m e p a t h (T , TimePath ) ,
b u i l d v e r t i c a l s (T , TimePath ) .
l o a d s c o r e ( F i l e , T , TimePath ) :
parse GUIDO ( F i l e , L ) , ! ,
number score (L ) ,
L0 = [ L , endm ar k e r ] ,
f l a t t e n ( L0 , F ) ,
l i s t t o t r e e (F , T) ,
t i m e p a t h (T , TimePath ) ,
b u i l d v e r t i c a l s (T , TimePath ) .
s a v e s c o r e ( F i l e , T , TimePath ) :
grammar GUIDO ( F i l e , T , TimePath ) .
number score (L) :
number score (L , 1 ,
).
n u m b e r s c o r e ( [ Head | T a i l ] , I , F ) :
n u m b e r p a r t ( Head , s t a r t , I , N ) ,
number score ( Tail , N, F ) .
number score ( [ ] , X , X) .
n u m b e r p a r t ( [ L a s t ] , P , I , N) :
83
e index ( Last , I ) ,
e prev ( Last , P) ,
e n e x t ( L a s t , end ) ,
N i s I + 1.
n u m b e r p a r t ( [ Head | T a i l ] , P , I , F ) :
e i n d e x ( Head , I ) ,
e p r e v ( Head , P ) ,
N i s I + 1,
e n e x t ( Head , N ) ,
number part ( Tai l , I , N, F ) .
/
TIME PATH BUILDER
T h i s b u i l d s a l i s t o f i n d e x e s t o e v e n t s s o r t e d by s t a r t t i m e s .
/
t i m e p a t h (T , TimePath ) :
t r e e s i z e (T , T S i z e ) ,
t i m e p a t h (T , T S i z e , TimePath ) .
t i m e p a t h (T , T S i z e , TimePath ) :
t i m e p a t h 0 (T , 1 ,
, T S i z e , TimePath0 ) ,
s o r t ( TimePath0 , TimePath ) .
time path0 ( , E , E , E , [ ] ) .
t i m e p a t h 0 (T , E l I n , ElOut , T S i z e , [ ( Time , E l I n ) | T a i l ] ) :
get label ( ElIn , T, E),
s e t e v e n t t i m e (T , E ) ,
t i m e s t a r t ( E , Time ) ,
ElIn0 i s ElIn + 1,
t i m e p a t h 0 (T , E l I n 0 , ElOut , T S i z e , T a i l ) .
/
VERTICAL RELATIONSHIPS
T h i s c r e a t e s t h e v e r t i c a l r e l a t i o n s h i p s between e v e n t s .
/
b u i l d v e r t i c a l s (T , TimePath ) :
r e v e r s e ( TimePath , TimePathP ) , ! ,
b u i l d v e r t 1 (T , TimePathP ) .
build vert1 ( , [ ] ) .
b u i l d v e r t 1 (T , [ ( S t a r t T i m e , I n d e x ) | T i m e P a t h T a i l ] ) :
g e t l a b e l ( Index , T, E ) ,
t i m e e n d ( E , EndTime ) ,
b u i l d v e r t 0 (T , S t a r t T i m e , EndTime , T i m e P a t h T a i l , V e r t i c a l s ) ,
e v e r t i c a l (E , V e r t i c a l s ) ,
b u i l d v e r t 1 (T , T i m e P a t h T a i l ) .
84
build vert0 ( ,
,
, [], []).
b u i l d v e r t 0 (T , S t a r t T i m e , EndTime ,
[ ( StartTime2 , Index ) | TimePathTail ] , [ Index | T a i l ] ) :
g e t l a b e l ( Index , T, E ) ,
t i m e e n d ( E , EndTime2 ) ,
FixEndTime i s EndTime 1 ,
FixEndTime2 i s EndTime2 1 ,
( between ( S t a r t T i m e , FixEndTime , S t a r t T i m e 2 )
;
between ( S t a r t T i m e , FixEndTime , FixEndTime2 )
;
between ( S t a r t T i m e 2 , FixEndTime2 , S t a r t T i m e )
;
between ( S t a r t T i m e 2 , FixEndTime2 , FixEndTime ) ) , ! ,
b u i l d v e r t 0 (T , S t a r t T i m e , EndTime , T i m e P a t h T a i l , T a i l ) .
b u i l d v e r t 0 (T , S t a r t T i m e , EndTime , [ | T a i l ] , V e r t i c a l s ) :
b u i l d v e r t 0 (T , S t a r t T i m e , EndTime , T a i l , V e r t i c a l s ) .
Testing
For testing of this module, see the testing for the GUIDO parser/grammar.
6.2.7
trees.pl
6.3
6.3.1
Code
/
GUIDO LANGUAGE PARSER
M i c h a e l Droettboom
June 1 9 9 9
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( l e x e r ) .
: e n s u r e l o a d e d ( ../ library / global .pl ) .
: e n s u r e l o a d e d ( ../ library / pitch .pl ) .
/
85
USER PREDICATE
/
parse GUIDO ( F i l e , L i s t ) :
see ( F i l e ) ,
t o k e n i s e G U I D O ( Words ) , ! ,
w r i t e ( Words ) ,
seen ,
parse GUIDO ( L i s t , Words , [ ] ) ,
w r i t e ( Parse OK ) , n l .
PRIVATE PREDICATES
/
% TOP LEVEL
parse GUIDO (X) >
{ i n i t p a r s e r },
s c o r e (X ) ,
{ set key }.
s c o r e ( L) >
( [ { ] , p a r t s ( L , 1 , L a s t P a r t ) , [ } ] ,
{ g l o b a l s e t ( num parts , LastPart )})
;
( part (L , 1 ) ,
{ g l o b a l s e t ( num parts , 1 ) } ) .
p a r t s ( L , PartNo , L a s t P a r t ) >
p a r t (X , PartNo ) ,
(([(,)],
{ N e x t P a r t i s PartNo + 1 } ,
p a r t s ( Xs , N e x t P a r t , L a s t P a r t ) ,
{L = [X | Xs ] } )
;
([],
{L = [X ] , L a s t P a r t = PartNo } ) ) .
p a r t (X , PartNo) >
[ [ ] , e v e n t s ( L , PartNo ) , [ ] ] ,
{ i n c r p a r t , f l a t t e n (L , X)}.
e v e n t s ( L , PartNo) >
e v e n t (X , PartNo ) ,
( ( e v e n t s ( Xs , PartNo ) ,
{ (X = , L = Xs ) ; ( L = [X | Xs ] ) } )
;
86
([],
{ (X = , L = [ ] ) ; ( L = X ) } ) ) .
e v e n t ( E , PartNo) >
c h o r d ( E ) ; t a g ( E , PartNo ) ; n o t e o r r e s t ( E , PartNo ) .
% NOTE OR REST EVENTS t o k e n s w i t h a d i r e c t c o r r e l a t i o n t o t h e
%
Pelog data s t r u c t u r e .
n o t e o r r e s t ( E , PartNo) >
p i t c h (P ) ,
( d u r a t i o n (D ) ; ( [ ] , { g l o b a l ( d u r a t i o n , D ) } ) ) , ! ,
{ e p a r t ( E , PartNo ) ,
e p i t c h (E , P ) ,
e d u r a t i o n ( E , D) } .
p i t c h (CP) >
r e s t (CP ) ; v a r i a b l e (CP ) ; s o u n d e d (CP ) .
s o un d e d (CP) >
p i t c h c l a s s (P ) ,
( ( o c t a v e (O ) , a c c i d e n t a l s (A ) ) ; ( a c c i d e n t a l s (A ) , o c t a v e (O ) ) ) ,
{ c o n c a t (P , A , N ) ,
t e r m t o a t o m ( N0 , N ) ,
p i t c h n a m e o c t a v e (CP , N0 , O) } .
p i t c h c l a s s (P) > [P ] , { p i t c h n a m e (P ,
)}.
o c t a v e (O) >
( [ O]
;
( [ ] , { g l o b a l ( octave , O) } ) ) ,
{ i n t e g e r (O ) ,
O > 13,
O < 13,
g l o b a l s e t ( o c t a v e , O) } .
a c c i d e n t a l s (C) >
a c c i d e n t a l (A ) ,
a c c i d e n t a l (B ) ,
{A = B , c o n c a t (A , B , C ) } .
a c c i d e n t a l s (C) > a c c i d e n t a l (C ) .
a c c i d e n t a l s ( ) > [ ] .
a c c i d e n t a l (A) > [A ] , { A = & ; A = # } .
r e s t ( r e s t ) > [ _ ] .
% NOTE : v a r i a b l e p i t c h e s a r e an e x t e n s i o n t o t h e n o r m a l GUIDO grammar ,
% and w i l l c a u s e a p a r s i n g e r r o r i f v i e w e d by t h e GUIDO N o t e V i e w e r
v a r i a b l e ( ) > [ ~ ] .
d u r a t i o n ( Dur) >
( ( n u m e r a t o r (N ) , d e n o m i n a t o r (D) )
87
( d e n o m i n a t o r (D) > {N i s 1 }
( n u m e r a t o r (N ) , { D i s 1 } ) ) ) ,
d o t s (NF , DF ) , { D0 i s D DF , N0 i s N NF } ,
{ dur num ( Dur , N0 ) ,
d u r d e n ( Dur , D0 ) ,
g l o b a l s e t ( d u r a t i o n , Dur ) } .
n u m e r a t o r (N) > [ * ] , ! , [ N ] , { i n t e g e r (N ) } .
d e n o m i n a t o r (D) > [ / ] , ! , [ D ] , { i n t e g e r (D) } .
% e a c h d o t l e n g t h e n s d u r a t i o n by 150%
d o t s (NF , DF) > c o u n t d o t s (N ) ,
{N > 0 > (DF i s N 2 , NF i s ( DF 2 1 ) )
; ( DF i s 1 , NF i s 1 ) } .
c o u n t d o t s (N) > [ . ] , ! , c o u n t d o t s (M) , { s u c c (M, N ) } .
count dots (0) > [].
% TAGS and ESCAPE SEQUENCES
t a g ( E , PartNo) >
( [ \\], [ Tag ]),
( ( [ < ] , param ( Param ) , [ > ] ) ; ( [ ] , { Param = } ) ) ,
{ h a n d l e t a g ( Tag , Param ) } ,
( ( [ ( ] , e v e n t s ( E , PartNo ) , [ ) ] )
;
( [ ] , ( e v e n t s ( E , PartNo ) ) ; ( [ ] , { E = } ) ) ) .
param ( I n t e g e r ) > [ I n t e g e r ] , { i n t e g e r ( I n t e g e r ) } .
param ( Param ) > [ S t r i n g ] , { s t r i n g t o a t o m ( S t r i n g , Param ) } .
) . % Handle u n r e c o g n i z e d t a g s g r a c e f u l l y
% CHORD
% a l w a y s f a i l . No p a r t may c o n t a i n c h o r d s .
c h o r d ( ) > [ { ] , { f a i l } .
88
/
HELPER PREDICATES
/
i n i t p a r s e r :
% s e t g l o b a l v a r i a b l e s u s e d t o f o r making o c t a v e s
% and d u r a t i o n s s t i c k u n t i l changed
retractall ( global ( ,
)),
g l o b a l s e t ( octave , 1 ) ,
g l o b a l s e t ( d u r a t i o n , dur ( 1 / 4 ) ) ,
global set ( part , 1 ) ,
set all defaults .
i n c r p a r t :
global ( part , X),
Y i s X + 1,
global set ( part , Y).
s e t k e y :
not g l o b a l ( tag ( key ) ,
),
not g l o b a l ( tag ( s c a l e ) ,
), !,
g l o b a l s e t ( tag ( key ) , 0 ) ,
g l o b a l s e t ( tag ( s c a l e ) , c major ) .
s e t k e y :
not g l o b a l ( tag ( key ) ,
),
g l o b a l ( tag ( s c a l e ) , Scale ) , ! ,
s c a l e k e y ( S c a l e , Key ) ,
k e y s h a r p s f l a t s ( KeyNumber , Key ) ,
g l o b a l s e t ( t a g ( k e y ) , KeyNumber ) .
s e t k e y :
not g l o b a l ( tag ( s c a l e ) ,
),
g l o b a l ( t a g ( k e y ) , Key ) ,
i n t e g e r ( Key ) , ! ,
k e y s h a r p s f l a t s ( Key , KeyName ) ,
g l o b a l s e t ( t a g ( s c a l e ) , KeyName m a j o r ) .
s e t k e y :
not g l o b a l ( tag ( s c a l e ) ,
),
g l o b a l ( t a g ( k e y ) , Key ) ,
g l o b a l s e t ( t a g ( s c a l e ) , Key m a j o r ) .
/
TEST
/
test parser grammar ( F i l e I n , FileOut ) :
w r i t e ( Testing GUIDO Parser / Grammar ----> ) , n l ,
w r i t e ( Parsing ) , w r i t e ( F i l e I n ) , n l ,
l o a d s c o r e ( F i l e I n , T , TimePath ) ,
n l , w r i t e ( Score tree : ) , n l ,
89
p r i n t t r e e (T ) , n l ,
w r i t e ( Tags : ) , n l ,
l i s t i n g ( global ),
n l , w r i t e ( Generating ) , w r i t e ( F i l e O u t ) , n l ,
s a v e s c o r e ( F i l e O u t , T , TimePath ) ,
w r i t e ( Succeeded . ) , n l .
6.3.2
lexer.pl
Code
/
GUIDO LANGUAGE LEXER
M i c h a e l Droettboom
June 1 9 9 9
/
/ ADAPTED FROM THE FOLLOWING QUINTUS LIBRARY > /
%
%
%
%
Module :
Aut hor :
Updated :
Purpose :
read in
R i c h a r d A . O K e e f e
2 1 Apr 1 9 8 7
Read i n a s e n t e n c e a s a l i s t o f words .
%
%
%
There i s a s h a r e d v e r s i o n o f t h i s f i l e w r i t t e n by L a w re nc e Byrd .
T h i s v e r s i o n was w r i t t e n e n t i r e l y from s c r a t c h f o r Q u i n t u s .
C o p y r i g h t ( C ) 1 9 8 7 , Q u i n t u s Computer S y s t e m s , I n c . A l l r i g h t s r e s e r v e d .
%
%
%
%
%
%
%
%
%
r e a d i n ( Words )
r e a d s c h a r a c t e r s u n t i l i t f i n d s a p e r i o d ( . ? o r ! ) a t t h e end o f a
l i n e , t h a t i s , f o l l o w e d by any number o f s p a c e s b u t n o t h i n g e l s e .
Words a r e s e q u e n c e s o f l e t t e r s ( i n e i t h e r c a s e , f o r c e d t o l o w e r
c a s e ) , o r s t r i n g s o f d i g i t s , o r p u n c t u a t i o n marks . O t h er c h a r a c t e r s
a c t a s word s e p a r a t o r s , and a r e o t h e r w i s e i g n o r e d . The s u b r o u t i n e s
i n t h i s f i l e a l l s t a r t with r i , to avoid a c o l l i s i o n with a user s
p r e d i c a t e s . Note t h a t t h i s p r e d i c a t e may r e a d more t h a n one
sentence ; i t i s best thought of as re ading paragraphs .
t o k e n i s e G U I D O ( Words ) :
g e t 0 (C ) ,
t g s e n t (C , Found ) ,
Words = Found .
t g s e n t (C , Words ) :
(
C < 0
;
C =< " "
> Words = [ ]
> = g e t 0 (D ) , t g s e n t (D , Words )
90
C =:= "%"
> g e t 0 (D ) , t g l i n e c o m m e n t (D ) , t g s e n t (D , Words )
Words = [ Word | R e s t ] , D i s C \ / 3 2 ,
(
C =:= "." > Word = . , g e t 0 ( F ) , t g s t o p ( F , R e s t )
;
C =:= "!" > Word = ! , g e t 0 ( F ) , t g s t o p ( F , R e s t )
;
C =:= "?" > Word = ? , g e t 0 ( F ) , t g s t o p ( F , R e s t )
;
D >= "a" , D =< "z" > =
get0 (E ) , tg word (E , Chars , F ) ,
name ( Word , [ D | C h a r s ] ) , t g s e n t ( F , R e s t )
;
C =:= "\"" >
get0 (E ) , t g s t r i n g (E , Chars ) ,
Word = C h a r s , g e t 0 ( F ) , t g s e n t ( F , R e s t )
;
C =:= "-" >
get0 (E ) , tg neg (E , Chars , F ) ,
name ( Word , [ C | C h a r s ] ) , t g s e n t ( F , R e s t )
;
C >= "0" , C =< "9" > =
get0 (E ) , tg nmbr (E , Chars , F ) ,
name ( Word , [ C | C h a r s ] ) , t g s e n t ( F , R e s t )
;
get0 (F ) ,
name ( Word , [ C ] ) ,
t g s e n t (F , Rest )
)
).
t g l i n e c o m m e n t (C ) : % i g n o r e comments
C < " "
;
( g e t 0 (D ) ,
t g l i n e c o m m e n t (D ) ) .
t g s t o p (C , R e s t ) :
(
C =:= " " > g e t 0 (D ) , t g s t o p (D , R e s t )
;
C =:= 9 > g e t 0 (D ) , t g s t o p (D , R e s t )
;
C < " " > R e s t = [ ]
;
t g s e n t (C , R e s t )
).
t g w o r d (C , [ Lower | L o w e rs ] , F ) :
(
C >= "a" , C =< "z" > = Lower = C
;
C =:= "_"
> Lower = C
;
C >= "A" , C =< "Z" > = Lower i s C"A"+"a"
/ ;
C >= 0 , C =< 9
> Lower = C /
), !,
g e t 0 (D ) ,
t g w o r d (D , L o w e r s , F ) .
tg word (F , [ ] , F ) .
tg string (34, []) : !.
t g s t r i n g ( Char , [ Char | C h a r s ] ) :
g e t 0 (D ) ,
t g s t r i n g (D , C h a r s ) .
% space
% tab
% end o f l i n e
91
t g n e g (C , D i g i t s , F ) :
(
D i g i t s = [C| D i g i t s 1 ] ,
g e t 0 (D ) ,
t g n m b r (D , D i g i t s 1 , F )
;
F = C, Digits = [] ).
t g n m b r (C , D i g i t s , F ) :
(
C >= "0" , C =< "9" > =
D i g i t s = [C| D i g i t s 1 ] ,
g e t 0 (D ) ,
t g n m b r (D , D i g i t s 1 , F )
;
C =:= "_" >
g e t 0 (D ) ,
t g n m b r (D , D i g i t s , F )
;
F = C, Digits = []
).
6.3.3
grammar.pl
Code
/
GRAMMAR FOR GUIDO
M i c h a e l Droettboom
May 1 9 9 9
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( ../ library / global .pl ) .
: e n s u r e l o a d e d ( ../ core/ trees .pl ) .
: e n s u r e l o a d e d ( ../ library / pitch.pl ) .
/
OPTIONS
/
% c o m m e n t s t y l e = { l y r i c s | r e f e r e n c e d | none }
%
l y r i c s : comments a r e w r i t t e n i n a s l y r i c s on t h e s c o r e
%
r e f e r e n c e d : comments a r e added t o t h e end o f f i l e and r e f e r e n c e d
%
t o t h e n o t e s t h e m s e l v e s by number
%
none : no comments
g l o b a l d e f a u l t ( tag ( comment style ) , r e f e r e n c e d ) .
92
/
GRAMMAR
/
grammar GUIDO ( F i l e , T , TimePath ) :
g g m ai n (T , TimePath , L , [ ] ) ,
tell ( File ),
write tokens (L ) ,
told .
g g m ai n (T ,
gg
gg
gg
TimePath) >
header ,
s c o r e (T , TimePath ) ,
i n f o (T ) .
g g h e a d e r >
[ % GUIDO Music Notation Language v1 .0\n ] ,
[ %\n ] ,
[ % This file was generated by the PELOG musical rule system \n ] ,
[ % (c ) 1999 Michael Droettboom \n\n ] .
g g s c o r e (T , TimePath) >
[ {\n ] , g g p a r t (T , TimePath ) , [ }\n\n ] .
g g p a r t (T , [ ( 0 , I n d e x )]) >
! , [ [ ] , g g t a g s , g g e v e n t (T , I n d e x ) , [ ]\n ] .
g g p a r t (T , [ ( 0 , I n d e x ) , ( X ,
) | ]) >
{X > 0 } , ! ,
[ [ ] , g g t a g s , g g e v e n t (T , I n d e x ) , [ ] ] .
g g p a r t (T , [ ( 0 , I n d e x ) | T a i l ]) >
!,
[ [ ] , g g t a g s , g g e v e n t (T , I n d e x ) , [ ] ] , [ ( , ) ] , [ \n ] , g g p a r t (T , T a i l )
gg part ( , ) > [].
g g t a g s >
{ f i n d a l l ( ( X , Y ) , g l o b a l ( t a g (X ) , Y ) , L ) } ,
gg tag (L ) .
gg
gg
gg
gg
g g e v e n t ( , end) > [ ] .
g g e v e n t (T , I n d e x ) >
{ g e t l a b e l ( Index , T, E ) ,
e p i t c h (E , Pitch ) ,
p i t c h n a m e ( P i t c h , PN ) ,
93
o c t a v e ( P i t c h , Octave ) ,
e d u r a t i o n (E , Duration ) ,
dur num ( D u r a t i o n , Numerator ) ,
d u r d e n ( D u r a t i o n , Denominator ) ,
e n e x t ( E , Next ) } ,
(({ g l o b a l ( tag ( comment style ) , l y r i c s ) ,
e p r e t t y c o m m e n t ( E , Comment ) } ,
gg comment ( Comment ) )
;
({ g l o b a l ( tag ( comment style ) , r e f e r e n c e d ) } ,
gg comment ( I n d e x ) ) ) ,
g g e v e n t t o k e n (PN , Octave , Numerator , Denominator ) ,
g g e v e n t (T , Next ) .
gg comment ( Comment) >
[ \\ text <\" ] , [ Comment ] , [ \"> ] .
g g e v e n t t o k e n (PN , Octave , Numerator , Denominator) >
[ PN ] , [ Octave ] , [ * ] , [ Numerator ] , [ / ] , [ Denominator ] , [ ] .
g g i n f o (T) >
{ g l o b a l ( tag ( comment style ) , r e f e r e n c e d ) ,
l i s t t o t r e e (L , T) } ,
gg referenced comments (L ) .
g g i n f o ( ) > [].
g g r e f e r e n c e d c o m m e n t s ( [ Head | T a i l ]) >
{ e i n d e x ( Head , I n d e x ) ,
e p r e t t y c o m m e n t ( Head , Comment ) } ,
[ % ] , [ I n d e x ] , [ : ] , [ Comment ] , [ \n ] ,
gg referenced comments ( Tail ) .
g g r e f e r e n c e d c o m m e n t s ( [ endm a r k e r ] ) > [ ] .
/
A REVERSE LEXER
/
write tokens ( [ ] ) .
w r i t e t o k e n s ( [ Head | T a i l ] ) :
w r i t e ( Head ) , w r i t e t o k e n s ( T a i l ) .
6.3.4
Testing
Testing the parser/grammar involved using three GUIDO BNF testing files provided
by the GUIDO authors. The test parser grammar predicate parses an input file,
displays the score tree and global tags, and generates a copy to an output file.
If the output file is functionally identical to the input file, then one can assume
the parser/grammar is working correctly. Each test below contains the following
information:
The text of the input and output files
94
A view of the input and ouput files in common music notation (cmn) as
generated by the GUIDO NoteViewer
The console output of the test parser grammar predicate
simple1
IN
%
%
%
%
s i m p l e GUIDO e x a m p l e
f i r s t enhancement
added some more n o t e s
added m e t e rt a g
[ \ m e t e r<"2/4">m e t e r c1 1 / 8 d e f g / 4 g a / 8 a a a g /2
a / 8 a a a g / 2 f / 8 f f f e / 4 e g /8
g g g c 1 ]
% s e e s i m p l e 2 . gmn v o r f u r t h e r e n h a c e m e n t s .
OUT
% GUIDO Music N o t a t i o n Language v1 . 0
%
% T h i s f i l e was g e n e r a t e d by t h e PELOG m u s i c a l r u l e s y s t e m
% ( c ) 1 9 9 9 M i c h a e l Droettboom
{
[ \ p i t c h s y s t e m<" diatonic ">p i t c h s y s t e m \ c o m m e n t s t y l e<" referenced ">c o m m e n t s t y l e \
\ k e y<"c">k e y \ s c a l e<"c~ major ">s c a l e \ t e x t<"1">t e x t c 1 1 / 8 \ t e x t<"2">t e x t d 1 1/8
\ t e x t<"3">t e x t e 1 1 / 8 \ t e x t<"4">t e x t f 1 1 / 8 \ t e x t<"5">t e x t g 1 1 / 4 \ t e x t<"6">t e x t g 1 1/
\ t e x t<"7">t e x t a 1 1 / 8 \ t e x t<"8">t e x t a 1 1 / 8 \ t e x t<"9">t e x t a 1 1 / 8 \ t e x t<"10">t e x t a 1 1
\ t e x t<"11">t e x t g 1 1 / 2 \ t e x t<"12">t e x t a 1 1 / 8 \ t e x t<"13">t e x t a 1 1/8
\ t e x t<"14">t e x t a 1 1 / 8 \ t e x t<"15">t e x t a 1 1 / 8 \ t e x t<"16">t e x t g 1 1/2
\ t e x t<"17">t e x t f 1 1 / 8 \ t e x t<"18">t e x t f 1 1 / 8 \ t e x t<"19">t e x t f 1 1/8
\ t e x t<"20">t e x t f 1 1 / 8 \ t e x t<"21">t e x t e 1 1 / 4 \ t e x t<"22">t e x t e 1 1/4
\ t e x t<"23">t e x t g 1 1 / 8 \ t e x t<"24">t e x t g 1 1 / 8 \ t e x t<"25">t e x t g 1 1/8
\ t e x t<"26">t e x t g 1 1 / 8 \ t e x t<"27">t e x t c 1 1 / 1 ] }
% 1:
95
TERMINAL
t e s t p a r s e r g r a m m a r ( simple1 .gmn , simple1out .gmn ) .
T e s t i n g GUIDO P a r s e r /Grammar >
P a r s i n g s i m p l e 1 . gmn
P a r s e OK
Score t r e e :
event ( 1 , 1 , p i t c h ( 1 2 , 7 ) , dur ( 1 / 8 ) , time ( 0 , 1 5 0 0 ) , s t a r t , [ ] , 2 ,
G1715 , G1716 , G1717 ) e v e n t ( 2 , 1 , p i t c h ( 1 4 , 8 ) , d u r ( 1 / 8 ) , t i m e ( 1 5 0 0 ,
3 0 0 0 ) , 1 , [ ] , 3 , G1753 , G1754 , G1755 ) e v e n t ( 3 , 1 , p i t c h ( 1 6 , 9 ) ,
d u r ( 1 / 8 ) , t i m e ( 3 0 0 0 , 4 5 0 0 ) , 2 , [ ] , 4 , G1791 , G1792 , G1793 ) e v e n t ( 4 ,
1 , p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) , t i m e ( 4 5 0 0 , 6 0 0 0 ) , 3 , [ ] , 5 , G1829 ,
G1830 , G1831 ) e v e n t ( 5 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) , t i m e ( 6 0 0 0 , 9 0 0 0 ) ,
4 , [ ] , 6 , G1879 , G1880 , G1881 ) e v e n t ( 6 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) ,
t i m e ( 9 0 0 0 , 1 2 0 0 0 ) , 5 , [ ] , 7 , G1917 , G1918 , G1919 ) e v e n t ( 7 , 1 ,
p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 1 2 0 0 0 , 1 3 5 0 0 ) , 6 , [ ] , 8 , G1967 , G1968 ,
G1969 ) e v e n t ( 8 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 1 3 5 0 0 , 1 5 0 0 0 ) , 7 ,
[ ] , 9 , G2005 , G2006 , G2007 ) e v e n t ( 9 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) ,
t i m e ( 1 5 0 0 0 , 1 6 5 0 0 ) , 8 , [ ] , 1 0 , G2043 , G2044 , G2045 ) e v e n t ( 1 0 , 1 ,
p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 1 6 5 0 0 , 1 8 0 0 0 ) , 9 , [ ] , 1 1 , G2081 ,
G2082 , G2083 ) e v e n t ( 1 1 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 2 ) , t i m e ( 1 8 0 0 0 ,
2 4 0 0 0 ) , 1 0 , [ ] , 1 2 , G2131 , G2132 , G2133 ) e v e n t ( 1 2 , 1 , p i t c h ( 2 1 ,
1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 4 0 0 0 , 2 5 5 0 0 ) , 1 1 , [ ] , 1 3 , G2181 , G2182 , G2183 )
event ( 1 3 , 1 , p i t c h ( 2 1 , 1 2 ) , dur ( 1 / 8 ) , time ( 2 5 5 0 0 , 2 7 0 0 0 ) , 1 2 , [ ] , 1 4 ,
G2219 , G2220 , G2221 ) e v e n t ( 1 4 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) ,
t i m e ( 2 7 0 0 0 , 2 8 5 0 0 ) , 1 3 , [ ] , 1 5 , G2257 , G2258 , G2259 ) e v e n t ( 1 5 , 1 ,
p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 8 5 0 0 , 3 0 0 0 0 ) , 1 4 , [ ] , 1 6 , G2295 ,
G2296 , G2297 ) e v e n t ( 1 6 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 2 ) , t i m e ( 3 0 0 0 0 ,
3 6 0 0 0 ) , 1 5 , [ ] , 1 7 , G2345 , G2346 , G2347 ) e v e n t ( 1 7 , 1 , p i t c h ( 1 7 ,
1 0 ) , d u r ( 1 / 8 ) , t i m e ( 3 6 0 0 0 , 3 7 5 0 0 ) , 1 6 , [ ] , 1 8 , G2395 , G2396 , G2397 )
event ( 1 8 , 1 , p i t c h ( 1 7 , 1 0 ) , dur ( 1 / 8 ) , time ( 3 7 5 0 0 , 3 9 0 0 0 ) , 1 7 , [ ] , 1 9 ,
G2433 , G2434 , G2435 ) e v e n t ( 1 9 , 1 , p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) ,
t i m e ( 3 9 0 0 0 , 4 0 5 0 0 ) , 1 8 , [ ] , 2 0 , G2471 , G2472 , G2473 ) e v e n t ( 2 0 , 1 ,
p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) , t i m e ( 4 0 5 0 0 , 4 2 0 0 0 ) , 1 9 , [ ] , 2 1 , G2509 ,
G2510 , G2511 ) e v e n t ( 2 1 , 1 , p i t c h ( 1 6 , 9 ) , d u r ( 1 / 4 ) , t i m e ( 4 2 0 0 0 ,
4 5 0 0 0 ) , 2 0 , [ ] , 2 2 , G2559 , G2560 , G2561 ) e v e n t ( 2 2 , 1 , p i t c h ( 1 6 , 9 ) ,
d u r ( 1 / 4 ) , t i m e ( 4 5 0 0 0 , 4 8 0 0 0 ) , 2 1 , [ ] , 2 3 , G2597 , G2598 , G2599 )
event ( 2 3 , 1 , p i t c h ( 1 9 , 1 1 ) , dur ( 1 / 8 ) , time ( 4 8 0 0 0 , 4 9 5 0 0 ) , 2 2 , [ ] , 2 4 ,
G2647 , G2648 , G2649 ) e v e n t ( 2 4 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) ,
t i m e ( 4 9 5 0 0 , 5 1 0 0 0 ) , 2 3 , [ ] , 2 5 , G2685 , G2686 , G2687 ) e v e n t ( 2 5 , 1 ,
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) , t i m e ( 5 1 0 0 0 , 5 2 5 0 0 ) , 2 4 , [ ] , 2 6 , G2723 ,
G2724 , G2725 ) e v e n t ( 2 6 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) , t i m e ( 5 2 5 0 0 ,
5 4 0 0 0 ) , 2 5 , [ ] , 2 7 , G2761 , G2762 , G2763 ) e v e n t ( 2 7 , 1 , p i t c h ( 1 2 , 7 ) ,
d u r ( 1 / 1 ) , t i m e ( 5 4 0 0 0 , 6 6 0 0 0 ) , 2 6 , [ ] , end , G2811 , G2812 , G2813 )
endm ar k e r
Tags :
g l o b a l ( tag ( pi t c h s y s t e m ) , d i a t o n i c ) .
g l o b a l ( tag ( comment style ) , r e f e r e n c e d ) .
g l o b a l ( tag ( meter ) , 2 / 4 ) .
96
global ( octave , 1 ) .
g l o b a l ( d u r a t i o n , dur ( 1 / 1 ) ) .
global ( part , 2 ) .
g l o b a l ( num parts , 1 ) .
g l o b a l ( tag ( key ) , c ) .
g l o b a l ( tag ( s c a l e ) , c major ) .
G e n e r a t i n g s i m p l e 1 o u t . gmn
Succeeded .
simple2
IN
%
%
%
%
s i m p l e GUIDO e x a m p l e
s e c o n d enhancement
adding a second v o i c e
and t h e s t a f f O f fTag
{ [ \ m e t e r<"2/4">m e t e r c1 1 / 8 d e f g / 4 g a / 8 a a a g /2
a / 8 a a a g / 2 f / 8 f f f e / 4 e g /8
g g g c 1 \ s t a f f O f f ] ,
[ \ m e t e r<"2/4">m e t e r \ c l e f <"bass"> c l e f c0 / 2 c f c f c g c g1 c0 ] }
% s e e s i m p l e 3 . gmn v o r f u r t h e r e n h a c e m e n t s .
OUT
% GUIDO Music N o t a t i o n Language v1 . 0
%
% T h i s f i l e was g e n e r a t e d by t h e PELOG m u s i c a l r u l e s y s t e m
% ( c ) 1 9 9 9 M i c h a e l Droettboom
{
[ \ p i t c h s y s t e m<" diatonic ">p i t c h s y s t e m \ c o m m e n t s t y l e<" referenced ">c o m m e n t s t y l e \
\ k e y<"c">k e y \ s c a l e<"c~ major ">s c a l e \ t e x t<"1">t e x t c 1 1 / 8 \ t e x t<"2">t e x t d 1 1/8
\ t e x t<"3">t e x t e 1 1 / 8 \ t e x t<"4">t e x t f 1 1 / 8 \ t e x t<"5">t e x t g 1 1 / 4 \ t e x t<"6">t e x t g 1 1/
\ t e x t<"7">t e x t a 1 1 / 8 \ t e x t<"8">t e x t a 1 1 / 8 \ t e x t<"9">t e x t a 1 1 / 8 \ t e x t<"10">t e x t a 1 1
\ t e x t<"11">t e x t g 1 1 / 2 \ t e x t<"12">t e x t a 1 1 / 8 \ t e x t<"13">t e x t a 1 1/8
\ t e x t<"14">t e x t a 1 1 / 8 \ t e x t<"15">t e x t a 1 1 / 8 \ t e x t<"16">t e x t g 1 1/2
97
TERMINAL
t e s t p a r s e r g r a m m a r ( simple2 .gmn , simple2out .gmn ) .
T e s t i n g GUIDO P a r s e r /Grammar >
P a r s i n g s i m p l e 2 . gmn
P a r s e OK
Score t r e e :
event ( 1 , 1 , p i t c h ( 1 2 , 7 ) , dur ( 1 / 8 ) , time ( 0 , 1 5 0 0 ) , s t a r t , [ ] , 2 ,
G2297 , G2298 , G2299 ) e v e n t ( 2 , 1 , p i t c h ( 1 4 , 8 ) , d u r ( 1 / 8 ) , t i m e ( 1 5 0 0 ,
3 0 0 0 ) , 1 , [ 2 8 ] , 3 , G2335 , G2336 , G2337 ) e v e n t ( 3 , 1 , p i t c h ( 1 6 , 9 ) ,
d u r ( 1 / 8 ) , t i m e ( 3 0 0 0 , 4 5 0 0 ) , 2 , [ 2 8 ] , 4 , G2373 , G2374 , G2375 )
event ( 4 , 1 , p i t c h ( 1 7 , 1 0 ) , dur ( 1 / 8 ) , time ( 4 5 0 0 , 6 0 0 0 ) , 3 , [ 2 8 ] , 5 ,
G2411 , G2412 , G2413 ) e v e n t ( 5 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) ,
t i m e ( 6 0 0 0 , 9 0 0 0 ) , 4 , [ ] , 6 , G2461 , G2462 , G2463 ) e v e n t ( 6 , 1 ,
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) , t i m e ( 9 0 0 0 , 1 2 0 0 0 ) , 5 , [ 2 9 ] , 7 , G2499 ,
G2500 , G2501 ) e v e n t ( 7 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 1 2 0 0 0 ,
1 3 5 0 0 ) , 6 , [ ] , 8 , G2549 , G2550 , G2551 ) e v e n t ( 8 , 1 , p i t c h ( 2 1 , 1 2 ) ,
d u r ( 1 / 8 ) , t i m e ( 1 3 5 0 0 , 1 5 0 0 0 ) , 7 , [ 3 0 ] , 9 , G2587 , G2588 , G2589 )
event ( 9 , 1 , p i t c h ( 2 1 , 1 2 ) , dur ( 1 / 8 ) , time ( 1 5 0 0 0 , 1 6 5 0 0 ) , 8 , [ 3 0 ] , 1 0 ,
G2625 , G2626 , G2627 ) e v e n t ( 1 0 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) ,
t i m e ( 1 6 5 0 0 , 1 8 0 0 0 ) , 9 , [ 3 0 ] , 1 1 , G2663 , G2664 , G2665 ) e v e n t ( 1 1 , 1 ,
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 2 ) , t i m e ( 1 8 0 0 0 , 2 4 0 0 0 ) , 1 0 , [ ] , 1 2 , G2713 ,
G2714 , G2715 ) e v e n t ( 1 2 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 4 0 0 0 ,
2 5 5 0 0 ) , 1 1 , [ ] , 1 3 , G2763 , G2764 , G2765 ) e v e n t ( 1 3 , 1 , p i t c h ( 2 1 ,
1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 5 5 0 0 , 2 7 0 0 0 ) , 1 2 , [ 3 2 ] , 1 4 , G2801 , G2802 ,
G2803 ) e v e n t ( 1 4 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 7 0 0 0 , 2 8 5 0 0 ) , 1 3 ,
[ 3 2 ] , 1 5 , G2839 , G2840 , G2841 ) e v e n t ( 1 5 , 1 , p i t c h ( 2 1 , 1 2 ) ,
d u r ( 1 / 8 ) , t i m e ( 2 8 5 0 0 , 3 0 0 0 0 ) , 1 4 , [ 3 2 ] , 1 6 , G2877 , G2878 , G2879 )
98
simple3
IN
% s i m p l e GUIDO e x a m p l e
99
% t h i r d enhancement
% adding s l u r s voice
% and n e w L i n eTag
{ [ \ m e t e r<"2/4">m e t e r \ s l u r ( c1 1 / 8 d e f g / 4 g ) \ s l u r ( a / 8 a a a g / 2 )
a / 8 a a a g / 2 f / 8 f f f \ n e w L i n e e / 4 e g /8
g g g c 1 \ s t a f f O f f ] ,
[ \ m e t e r<"2/4">m e t e r \ c l e f <"bass"> c l e f c0 / 2 c f c f c g c g1 c0 ] }
% s e e s i m p l e 4 . gmn v o r f u r t h e r e n h a c e m e n t s .
OUT
% GUIDO Music N o t a t i o n Language v1 . 0
%
% T h i s f i l e was g e n e r a t e d by t h e PELOG m u s i c a l r u l e s y s t e m
% ( c ) 1 9 9 9 M i c h a e l Droettboom
{
[ \ p i t c h s y s t e m<" diatonic ">p i t c h s y s t e m \ c o m m e n t s t y l e<" referenced ">c o m m e n t s t y l e \
\ k e y<"c">k e y \ s c a l e<"c~ major ">s c a l e \ t e x t<"1">t e x t c 1 1 / 8 \ t e x t<"2">t e x t d 1 1/8
\ t e x t<"3">t e x t e 1 1 / 8 \ t e x t<"4">t e x t f 1 1 / 8 \ t e x t<"5">t e x t g 1 1 / 4 \ t e x t<"6">t e x t g 1 1/
\ t e x t<"7">t e x t a 1 1 / 8 \ t e x t<"8">t e x t a 1 1 / 8 \ t e x t<"9">t e x t a 1 1 / 8 \ t e x t<"10">t e x t a 1 1
\ t e x t<"11">t e x t g 1 1 / 2 \ t e x t<"12">t e x t a 1 1 / 8 \ t e x t<"13">t e x t a 1 1/8
\ t e x t<"14">t e x t a 1 1 / 8 \ t e x t<"15">t e x t a 1 1 / 8 \ t e x t<"16">t e x t g 1 1/2
\ t e x t<"17">t e x t f 1 1 / 8 \ t e x t<"18">t e x t f 1 1 / 8 \ t e x t<"19">t e x t f 1 1/8
\ t e x t<"20">t e x t f 1 1 / 8 \ t e x t<"21">t e x t e 1 1 / 4 \ t e x t<"22">t e x t e 1 1/4
\ t e x t<"23">t e x t g 1 1 / 8 \ t e x t<"24">t e x t g 1 1 / 8 \ t e x t<"25">t e x t g 1 1/8
\ t e x t<"26">t e x t g 1 1 / 8 \ t e x t<"27">t e x t c 1 1 / 1 ] , [ \ p i t c h s y s t e m<" diatonic ">p i t c h s y s
\ c o m m e n t s t y l e<" referenced ">c o m m e n t s t y l e \ m e t e r<"2/4">m e t e r \ k e y<"c">k e y \ s c a l e<"
\ t e x t<"28">t e x t c 0 1 / 2 \ t e x t<"29">t e x t c 0 1 / 2 \ t e x t<"30">t e x t f 0 1/2
\ t e x t<"31">t e x t c 0 1 / 2 \ t e x t<"32">t e x t f 0 1 / 2 \ t e x t<"33">t e x t c 0 1/2
\ t e x t<"34">t e x t g 0 1 / 2 \ t e x t<"35">t e x t c 0 1 / 2 \ t e x t<"36">t e x t g 11/2
\ t e x t<"37">t e x t c 0 1 / 2 ] }
100
TERMINAL
7 ? t e s t p a r s e r g r a m m a r ( simple3 .gmn , simple3out .gmn ) .
T e s t i n g GUIDO P a r s e r /Grammar >
P a r s i n g s i m p l e 3 . gmn
P a r s e OK
Score t r e e :
event ( 1 , 1 , p i t c h ( 1 2 , 7 ) , dur ( 1 / 8 ) , time ( 0 , 1 5 0 0 ) , s t a r t , [ ] , 2 ,
G2519 , G2520 , G2521 ) e v e n t ( 2 , 1 , p i t c h ( 1 4 , 8 ) , d u r ( 1 / 8 ) , t i m e ( 1 5 0 0 ,
3 0 0 0 ) , 1 , [ 2 8 ] , 3 , G2557 , G2558 , G2559 ) e v e n t ( 3 , 1 , p i t c h ( 1 6 , 9 ) ,
d u r ( 1 / 8 ) , t i m e ( 3 0 0 0 , 4 5 0 0 ) , 2 , [ 2 8 ] , 4 , G2595 , G2596 , G2597 )
event ( 4 , 1 , p i t c h ( 1 7 , 1 0 ) , dur ( 1 / 8 ) , time ( 4 5 0 0 , 6 0 0 0 ) , 3 , [ 2 8 ] , 5 ,
G2633 , G2634 , G2635 ) e v e n t ( 5 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) ,
t i m e ( 6 0 0 0 , 9 0 0 0 ) , 4 , [ ] , 6 , G2683 , G2684 , G2685 ) e v e n t ( 6 , 1 ,
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 4 ) , t i m e ( 9 0 0 0 , 1 2 0 0 0 ) , 5 , [ 2 9 ] , 7 , G2721 ,
G2722 , G2723 ) e v e n t ( 7 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 1 2 0 0 0 ,
1 3 5 0 0 ) , 6 , [ ] , 8 , G2786 , G2787 , G2788 ) e v e n t ( 8 , 1 , p i t c h ( 2 1 , 1 2 ) ,
d u r ( 1 / 8 ) , t i m e ( 1 3 5 0 0 , 1 5 0 0 0 ) , 7 , [ 3 0 ] , 9 , G2824 , G2825 , G2826 )
event ( 9 , 1 , p i t c h ( 2 1 , 1 2 ) , dur ( 1 / 8 ) , time ( 1 5 0 0 0 , 1 6 5 0 0 ) , 8 , [ 3 0 ] , 1 0 ,
G2862 , G2863 , G2864 ) e v e n t ( 1 0 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) ,
t i m e ( 1 6 5 0 0 , 1 8 0 0 0 ) , 9 , [ 3 0 ] , 1 1 , G2900 , G2901 , G2902 ) e v e n t ( 1 1 , 1 ,
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 2 ) , t i m e ( 1 8 0 0 0 , 2 4 0 0 0 ) , 1 0 , [ ] , 1 2 , G2950 ,
G2951 , G2952 ) e v e n t ( 1 2 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 4 0 0 0 ,
2 5 5 0 0 ) , 1 1 , [ ] , 1 3 , G3012 , G3013 , G3014 ) e v e n t ( 1 3 , 1 , p i t c h ( 2 1 ,
1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 5 5 0 0 , 2 7 0 0 0 ) , 1 2 , [ 3 2 ] , 1 4 , G3050 , G3051 ,
G3052 ) e v e n t ( 1 4 , 1 , p i t c h ( 2 1 , 1 2 ) , d u r ( 1 / 8 ) , t i m e ( 2 7 0 0 0 , 2 8 5 0 0 ) , 1 3 ,
[ 3 2 ] , 1 5 , G3088 , G3089 , G3090 ) e v e n t ( 1 5 , 1 , p i t c h ( 2 1 , 1 2 ) ,
d u r ( 1 / 8 ) , t i m e ( 2 8 5 0 0 , 3 0 0 0 0 ) , 1 4 , [ 3 2 ] , 1 6 , G3126 , G3127 , G3128 )
event ( 1 6 , 1 , p i t c h ( 1 9 , 1 1 ) , dur ( 1 / 2 ) , time ( 3 0 0 0 0 , 3 6 0 0 0 ) , 1 5 , [ ] , 1 7 ,
G3176 , G3177 , G3178 ) e v e n t ( 1 7 , 1 , p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) ,
t i m e ( 3 6 0 0 0 , 3 7 5 0 0 ) , 1 6 , [ ] , 1 8 , G3226 , G3227 , G3228 ) e v e n t ( 1 8 , 1 ,
p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) , t i m e ( 3 7 5 0 0 , 3 9 0 0 0 ) , 1 7 , [ 3 4 ] , 1 9 , G3264 ,
G3265 , G3266 ) e v e n t ( 1 9 , 1 , p i t c h ( 1 7 , 1 0 ) , d u r ( 1 / 8 ) , t i m e ( 3 9 0 0 0 ,
4 0 5 0 0 ) , 1 8 , [ 3 4 ] , 2 0 , G3302 , G3303 , G3304 ) e v e n t ( 2 0 , 1 , p i t c h ( 1 7 ,
1 0 ) , d u r ( 1 / 8 ) , t i m e ( 4 0 5 0 0 , 4 2 0 0 0 ) , 1 9 , [ 3 4 ] , 2 1 , G3340 , G3341 ,
G3342 ) e v e n t ( 2 1 , 1 , p i t c h ( 1 6 , 9 ) , d u r ( 1 / 4 ) , t i m e ( 4 2 0 0 0 , 4 5 0 0 0 ) , 2 0 ,
[ ] , 2 2 , G3390 , G3391 , G3392 ) e v e n t ( 2 2 , 1 , p i t c h ( 1 6 , 9 ) , d u r ( 1 / 4 ) ,
t i m e ( 4 5 0 0 0 , 4 8 0 0 0 ) , 2 1 , [ 3 5 ] , 2 3 , G3428 , G3429 , G3430 ) e v e n t ( 2 3 , 1 ,
101
p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) , t i m e ( 4 8 0 0 0 , 4 9 5 0 0 ) , 2 2 , [ ] , 2 4 , G3478 ,
G3479 , G3480 ) e v e n t ( 2 4 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) , t i m e ( 4 9 5 0 0 ,
5 1 0 0 0 ) , 2 3 , [ 3 6 ] , 2 5 , G3516 , G3517 , G3518 ) e v e n t ( 2 5 , 1 , p i t c h ( 1 9 ,
1 1 ) , d u r ( 1 / 8 ) , t i m e ( 5 1 0 0 0 , 5 2 5 0 0 ) , 2 4 , [ 3 6 ] , 2 6 , G3554 , G3555 ,
G3556 ) e v e n t ( 2 6 , 1 , p i t c h ( 1 9 , 1 1 ) , d u r ( 1 / 8 ) , t i m e ( 5 2 5 0 0 , 5 4 0 0 0 ) , 2 5 ,
[ 3 6 ] , 2 7 , G3592 , G3593 , G3594 ) e v e n t ( 2 7 , 1 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 1 ) ,
t i m e ( 5 4 0 0 0 , 6 6 0 0 0 ) , 2 6 , [ ] , end , G3642 , G3643 , G3644 ) e v e n t ( 2 8 , 2 ,
p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) , t i m e ( 0 , 6 0 0 0 ) , s t a r t , [ 1 ] , 2 9 , G3874 , G3875 ,
G3876 ) e v e n t ( 2 9 , 2 , p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) , t i m e ( 6 0 0 0 , 1 2 0 0 0 ) , 2 8 ,
[ 5 ] , 3 0 , G3912 , G3913 , G3914 ) e v e n t ( 3 0 , 2 , p i t c h ( 5 , 3 ) , d u r ( 1 / 2 ) ,
t i m e ( 1 2 0 0 0 , 1 8 0 0 0 ) , 2 9 , [ 7 ] , 3 1 , G3950 , G3951 , G3952 ) e v e n t ( 3 1 , 2 ,
p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) , t i m e ( 1 8 0 0 0 , 2 4 0 0 0 ) , 3 0 , [ 1 1 ] , 3 2 , G3988 ,
G3989 , G3990 ) e v e n t ( 3 2 , 2 , p i t c h ( 5 , 3 ) , d u r ( 1 / 2 ) , t i m e ( 2 4 0 0 0 ,
3 0 0 0 0 ) , 3 1 , [ 1 2 ] , 3 3 , G4026 , G4027 , G4028 ) e v e n t ( 3 3 , 2 , p i t c h ( 0 ,
0 ) , d u r ( 1 / 2 ) , t i m e ( 3 0 0 0 0 , 3 6 0 0 0 ) , 3 2 , [ 1 6 ] , 3 4 , G4064 , G4065 ,
G4066 ) e v e n t ( 3 4 , 2 , p i t c h ( 7 , 4 ) , d u r ( 1 / 2 ) , t i m e ( 3 6 0 0 0 , 4 2 0 0 0 ) , 3 3 ,
[ 1 7 ] , 3 5 , G4102 , G4103 , G4104 ) e v e n t ( 3 5 , 2 , p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) ,
t i m e ( 4 2 0 0 0 , 4 8 0 0 0 ) , 3 4 , [ 2 1 ] , 3 6 , G4140 , G4141 , G4142 ) e v e n t ( 3 6 , 2 ,
p i t c h ( 5 , 3 ) , d u r ( 1 / 2 ) , t i m e ( 4 8 0 0 0 , 5 4 0 0 0 ) , 3 5 , [ 2 3 ] , 3 7 , G4178 ,
G4179 , G4180 ) e v e n t ( 3 7 , 2 , p i t c h ( 0 , 0 ) , d u r ( 1 / 2 ) , t i m e ( 5 4 0 0 0 ,
6 0 0 0 0 ) , 3 6 , [ 2 7 ] , end , G4216 , G4217 , G4218 )
endm ar k e r
Tags :
g l o b a l ( tag ( pi t c h s y s t e m ) , d i a t o n i c ) .
g l o b a l ( tag ( comment style ) , r e f e r e n c e d ) .
g l o b a l ( tag ( meter ) , 2 / 4 ) .
g l o b a l ( d u r a t i o n , dur ( 1 / 2 ) ) .
global ( octave , 0 ) .
global ( part , 3 ) .
g l o b a l ( num parts , 2 ) .
g l o b a l ( tag ( key ) , c ) .
g l o b a l ( tag ( s c a l e ) , c major ) .
G e n e r a t i n g s i m p l e 3 o u t . gmn
Succeeded .
6.4
6.4.1
Code
%%%%%%%%%%%%%%%%%%%%%%%
% LIBRARIES MAIN MODULE
%%%%%%%%%%%%%%%%%%%%%%%
% Simply e ns u r e s that a l l the Pelog user l i b r a r i e s are loaded
: e n s u r e l o a d e d ( comments ) .
ensure
ensure
ensure
ensure
ensure
ensure
ensure
ensure
ensure
6.4.2
102
loaded ( event ) .
loaded ( global ).
loaded ( interval ).
loaded ( parts ).
loaded ( penalty ).
loaded ( pitch ).
loaded ( range ) .
loaded ( scale ).
l o a d e d ( time ) .
comments.pl
Code
/
COMMENTS MANAGER
M i c h a e l Droettboom
1999
T h i s l i b r a r y p r o v i d e s a mechanism t o s t o r e comment s t r i n g s w i t h e a c h e v e n t .
These comments can be u s e d t o p r o v i d e f e e d b a c k a b o u t e v e n t s w i t h i n t h e
score .
Comments a r e added t o t h e o u t p u t f i l e i n t h e s t y l e s p e c i f i e d by t h e
comment style tag i n the input f i l e .
/
/
USER PREDICATE
comment(+ e v e n t , + comment )
I f comment i s i n s t a n t i a t e d , comment i s added t o e v e n t s
event l i s t .
I f comment i s a v a r i a b l e , u n i f i e s w i t h any comment c u r r e n t l y
i n e v e n t s comment l i s t
/
: op ( 1 5 0 , x f x , comment ) .
comment ( E , Comment ) :
n o n v a r ( Comment ) ,
add comment ( E , Comment ) .
comment ( E , Comment ) :
v a r ( Comment ) ,
in comment ( E , Comment ) .
103
/
CHECKING FOR THE EXISTENCE OF A COMMENT
/
in comment ( E , Comment ) :
e comment ( E , CommentList ) ,
in comment0 ( Comment , CommentList ) .
in comment0 ( , [ Head | ] ) :
v a r ( Head ) , ! ,
fail .
in comment0 ( Comment , [ Head | T a i l ] ) :
Comment = Head
;
in comment0 ( Comment , T a i l ) .
/
OUTPUT A LIST OF COMMENTS IN USERREADABLE FORM
e p r e t t y c o m m e n t (+E , Comment )
Comment i s a P r o l o g s t r i n g c o n t a i n i n g t h e e v e n t s comment
l i s t i s a u s e rr e a d a b l e form
/
e p r e t t y c o m m e n t ( E , Comment ) :
e comment ( E , L ) ,
e p r e t t y c o m m e n t 0 ( L , , Comment ) .
e p r e t t y c o m m e n t 0 ( Var ,
, ) :
% I f t h e l i s t e l e m e n t i s a v a r i a b l e , t h e r e was an e r r o r
% i n t h e l i s t c r e a t i o n and we s h o u l d s t o p r e c u r s i o n
v a r ( Var ) , ! .
e p r e t t y c o m m e n t 0 ( [ Head | T a i l ] , X , Y) :
% The l a s t good e l e m e n t i n t h e l i s t
var ( Tail ) , ! ,
c o n c a t (X , Head , Y ) .
e p r e t t y c o m m e n t 0 ( [ Head | T a i l ] , X , Y) :
% Concatenate each element to the output s t r i n g
104
% and r e c u r s e
c o n c a t (X , Head , Y0 ) ,
c o n c a t ( Y0 , , , Y1 ) ,
e p r e t t y c o m m e n t 0 ( T a i l , Y1 , Y ) .
/
ADDING EXTRA INFORMATION TO EVENTS
T h i s p r e d i c a t e ad d s a n o t h e r b i t o f a r b i t r a r y i n f o r m a t i o n
to a given event .
There i s ( a s o f y e t ) no p r a c t i c a l u s e f o r t h i s f e a t u r e and
i t i s included f o r the sake of e x p a n d i b i l i t y only
/
a d d e x t r a (E , Extra ) : e e x t r a (E , [ Extra | ] ) , ! .
a d d e x t r a ( E , E x t r a ) : e e x t r a ( E , X ) , add comment0 ( E x t r a , X ) , ! .
/
TEST CODE
/
test comment :
add comment ( E , Bach ) ,
add comment ( E , Beethoven ) ,
w r i t e ( Event with comments Bach & Beethoven : ) , n l ,
w r i t e (E ) , n l ,
in comment ( E , Bach ) ,
w r i t e ( in_comment check for Bach succeeded ) , n l ,
in comment ( E , Beethoven ) ,
w r i t e ( in_comment check for Beethoven succeeded ) , n l ,
n o t in comment ( E , Brahms ) ,
w r i t e ( in_comment check for Brahms failed ) , n l ,
e pretty comment (E , P r e t t y ) ,
w r i t e ( Pretty comment : ) , n l ,
write ( Pretty ).
Testing
? t e s t c o m m e n t .
E v e n t w i t h comments Bach & B e e t h o v e n :
e v e n t ( G327 , G328 , G329 , G330 , G331 , G332 ,
[ Bach , B e e t h o v e n | G340 ] , G336 , G337 )
in comment c h e c k f o r Bach s u c c e e d e d
in comment c h e c k f o r B e e t h o v e n s u c c e e d e d
in comment c h e c k f o r Brahms f a i l e d
P r e t t y comment :
Bach , B e e t h o v e n
Yes
G333 ,
G334 ,
6.4.3
105
event.pl
Code
/
EVENT LIBRARY
M i c h a e l Droettboom
May 1 9 9 9
ensure
ensure
ensure
ensure
l o a d e d ( time ) .
loaded ( pitch ).
l o a d e d ( ../ core/ trees .pl ) .
l o a d e d ( comments ) .
/
USER PREDICATES
f i r s t e v e n t (+ e v e n t )
succeeds i f event i s the f i r s t event in i t s part
l a s t e v e n t (+ e v e n t )
succeeds i f event i s the l a s t event in i t s part
p e n u l t i m a t e e v e n t (+ e v e n t s )
succeeds i f event i s the penultimate event in i t s part
e v e n t s i s o f t h e form ( T , E ) where T i s t h e s c o r e t r e e and
E i s the c u r r e n t event . This i s handled behind the scenes
by t h e r u l e a p p l i c a t i o n mechanism .
/
: op ( 1 5 0 , x f , f i r s t e v e n t ) .
: op ( 1 5 0 , x f , l a s t e v e n t ) .
: op ( 1 5 0 , x f , p e n u l t i m a t e e v e n t ) .
f i r s t e v e n t (E) :
e p r e v (E , S ) ,
nonvar (S ) ,
106
S = start .
l a s t e v e n t (E) :
e n e x t (E , S ) ,
nonvar (S ) ,
S = end .
p e n u l t i m a t e e v e n t ( (T , E ) ) :
g e t n e x t e v e n t (T , E , EN ) ,
EN \= end ,
l a s t e v e n t (EN ) .
/
DATA STRUCTURE DEFINITION
These p r e d i c a t e s r e t r i e v e d i f f e r e n t f i e l d s o f t h e d a t a s t r u c t u r e
The d a t a s t r u c t u r e i s d e f i n e d a s f o l l o w s :
e v e n t ( I n d e x , P a r t , p i t c h (CPC , CNC ) , d u r (Num , Den ) ,
t i m e ( S t a r t , End ) , P r e v I n d e x , C o n c u r r e n t I n d e x L i s t , N e x t I n d e x ,
[ Comment ] , P e n a l t y , [ E x t r a ] ) .
Note t h a t t h e s e p r e d i c a t e s c o u l d have been d e f i n e d u s i n g t h e
b u i l ti n p r e d i c a t e a r g / 3 , b u t t h a t would n o t a l l o w t h e s y s t e m
t o g e n e r a t e new e v e n t s ont h e f l y . For i n s t a n c e , t h e q u e r y
e i n d e x (E , 1 )
c r e a t e s a new e v e n t w i t h i n d e x 1 :
E = e v e n t ( 1 , G352 , G353 ,
G359 , G360 , G361 )
G354 ,
G355 ,
G356 ,
G357 ,
G358 ,
The e q u i v a l e n t i m p l e m e n t a t i o n u s i n g a r g / 3 :
e i n d e x (E , Index ) : arg ( 1 , E , Index ) .
would c a u s e an e r r o r i f E were a v a r i a b l e :
[ WARNING : a r g / 3 : Arguments a r e n o t s u f f i c i e n t l y i n s t a n t i a t e d ]
Exception : (
8 ) a r g ( 1 , G265 , 1 ) ?
/
e
e
e
e
e
e
e
) , Index
) , Part ) .
,
,
,
,
,
) , Pitch
,
,
,
,
,
,
,
),
,
,
,
,
) , Time ) .
,
,
,
,
) , Prev ) .
Vertical ,
,
,
,
),
,
).
).
Duration ) .
Vertical ).
107
next ( event ( ,
,
,
,
,
,
, Next ,
,
,
) , Next ) .
comment ( e v e n t ( ,
,
,
,
,
,
,
, Comment ,
,
) , Comment ) .
penalty ( event ( ,
,
,
,
,
,
,
,
, Penalty ,
) , Penalty ) .
extra ( event ( ,
,
,
,
,
,
,
,
,
, Extra ) , Extra ) .
/
EVENT TRAVERSAL
g e t n e x t e v e n t (+ s c o r et r e e , + e v e n t , n e x t )
g e t p r e v e v e n t (+ s c o r et r e e , + e v e n t , p r e v )
Note t h a t i f t h e r e i s no n e x t o r p r e v i o u s e v e n t i n t h e g i v e n
p a r t ( i . e . e v e n t i s t h e f i r s t o r l a s t e v e n t ) r e t u r n s t h e atoms
s t a r t o r end .
/
g e t n e x t e v e n t ( , end , end ) : ! .
g e t n e x t e v e n t (T , E , EN) :
e n e x t (E , INext ) ,
g e t n e x t e v e n t 0 (T , I N e x t , EN ) .
g e t n e x t e v e n t 0 ( , end , end ) : ! .
g e t n e x t e v e n t 0 (T , N , EN) :
i n t e g e r (N ) , ! ,
g e t l a b e l (N , T , EN ) .
/
VERTICAL RELATIONSHIPS
/
g e t v e r t i c a l (T , E , EV ) :
e v e r t i c a l (E , VList ) ,
member (N , V L i s t ) ,
g e t l a b e l (N , T , EV ) .
get vertical ( , E, []) :
e v e r t i c a l (E , [ ] ) .
6.4.4
108
global.pl
Code
/
GLOBAL VARIABLE MANAGER
M i c h a e l Droettboom
1999
L i b r a r y f o r s i m u l a t i n g the concept of g l o b a l v a r i a b l e s .
v a r i a b l e s may h o l d o n l y one v a l u e a t a g i v e n t i m e .
Global
/
USER PREDICATES
t a g (+tagname , v a l u e )
S u c c e e d s i f tagname i s a s s i g n e d t o v a l u e .
Tags t h a t a r e a s s i g n e d t o p a r t s ( i . e . n o t g l o b a l t o t h e s c o r e
a r e r e t r i e v e d u s i n g t h e form t a g p a r t where t a g i s t h e tagname
and p a r t i s a p a r t name .
/
: op ( 1 5 0 , x f x , t a g ) .
: op ( 1 0 0 , x f x , ) .
109
t a g ( Name P a r t , V a l u e ) :
i n t e g e r ( Part ) , ! ,
g l o b a l ( t a g (Name P a r t ) , V a l u e ) .
t a g ( Name P a r t , V a l u e ) :
not i n t e g e r ( Part ) , ! ,
num parts ( Parts ) ,
p a r t d e f ( P a r t , PartNo , P a r t s ) ,
g l o b a l ( t a g (Name PartNo ) , V a l u e ) .
t a g (Name , V a l u e ) :
g l o b a l ( t a g (Name ) , V a l u e ) .
/
SETTING GLOBAL VARIABLES
/
g l o b a l s e t (X , Y) :
r e t r a c t a l l ( g l o b a l (X , ) ) ,
a s s e r t ( g l o b a l (X , Y ) ) .
s e t a l l d e f a u l t s :
f o r a l l ( g l o b a l d e f a u l t (X , Y ) ,
g l o b a l s e t (X , Y ) ) .
Testing
7 ? g l o b a l s e t ( t e s t , Beethoven ) .
Yes
8 ? g l o b a l ( t e s t , X ) .
X = Beethoven ;
No
9 ? g l o b a l s e t ( t e s t , Bach ) .
Yes
10 ? g l o b a l ( t e s t , X ) .
X = Bach ;
No
11 ? g l o b a l s e t ( t a g ( composer ) , Mozart ) .
Yes
12 ? composer t a g X .
X = Mozart ;
No
13 ? X t a g Mozart .
110
X = composer ;
6.4.5
interval.pl
Code
/
INTERVAL LIBRARY
M i c h a e l Droettboom
1999
L i b r a r y f o r d e a l i n g w i t h t h e r e l a t i o n s h i p between two p i t c h e s .
I n P e l o g , i n t e r v a l s can be s p e c i f i e d i n t h e form t y p e s i z e , where
t y p e i s a t h r e ec h a r a c t e r atom s p e c i f y i n g t h e t y p e o f i n t e r v a l .
The a v a i l a b l e t y p e s a r e l i s t e d b e l o w .
type
maj
min
per
dim
aug
chr
sem
tri
meaning
major
minor
perfect
diminished
augmented
chromatic
semitones ( s i z e f i e l d
tritone
i s # semitones )
unison
step
skip
consonant
dissonant
meaning
111
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( p i t c h ) .
: e n s u r e l o a d e d ( ../ core/bi-math.pl ) .
: e n s u r e l o a d e d ( global .pl ) .
/
OPERATORS
/
: op ( 1 2 5 , x f x , ) .
: op ( 1 0 0 , f y , ) .
: op ( 1 2 5 , x f x , : ) .
/
USER FUNCTIONS
a b s o l u t e i n t e r v a l (? eventa : ? eventb , ? i n t e r v a l [ contour ? d i r ] )
S u c c e e d s i f t h e a b s o l u t e i n t e r v a l between two e v e n t s , e v e n t a
and e v e n t b , i s e q u a l t o i n t e r v a l .
i n t e r v a l may be i n any form
s p e c i f i e d i n the i n t r o d u c t i o n to the i n t e r v a l l i b r a r y .
c y c l i c i n t e r v a l (? eventa : ? eventb , ? i n t e r v a l [ contour ? d i r ] )
S u c c e e d s i s t h e c y c l i c i n t e r v a l between two e v e n t s , e v e n t a and
eventb , i s equal to i n t e r v a l .
i n t e r v a l may be i n any form
s p e c i f i e d i n the i n t r o d u c t i o n to the i n t e r v a l l i b r a r y .
The o p t i o n a l c o n t o u r u n i f i e s w i t h up i f e v e n t b i s h i g h e r i n p i t c h
t h a n e v e n t a , down i f e v e n t b i s l o w e r i n p i t c h t h a n e v e n t a , and
s t a t i c i f e v e n t b i s t h e same p i t c h a s e v e n t a .
contour (? eventa : ? eventb , ? d i r )
Determines d i r e c t i o n of i n t e r v a l .
/
: op ( 1 4 9 , x f x , c o n t o u r ) .
: op ( 1 5 0 , x f x , c i n t e r v a l ) .
: op ( 1 5 0 , x f x , c y c l i c i n t e r v a l ) .
c i n t e r v a l ( FromP : ToP , I c o n t o u r D i r ) : ! ,
c y c l i c i n t e r v a l ( FromP , ToP , I , D i r ) .
c i n t e r v a l ( FromP : ToP , I ) :
c y c l i c i n t e r v a l ( FromP , ToP , I ,
).
c y c l i c i n t e r v a l ( FromP : ToP , I c o n t o u r D i r ) : ! ,
112
c y c l i c i n t e r v a l ( FromP , ToP , I , D i r ) .
c y c l i c i n t e r v a l ( FromP : ToP , I ) :
c y c l i c i n t e r v a l ( FromP , ToP , I ,
).
c y c l i c i n t e r v a l ( FromP , ToP , I , D i r ) :
c o n t p i t c h c l a s s ( FromP , FromCPC ) ,
c o n t n o t e c l a s s ( FromP , FromCNC ) ,
c o n t p i t c h c l a s s ( ToP , ToCPC ) ,
c o n t n o t e c l a s s ( ToP , ToCNC ) ,
c y c l i c i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , I , D i r ) .
% Note : c y c l i c i n t e r v a l c p comes i n m u l t i p l e f l a v o u r s d e p e n d i n g on
% wh ich a r g u m e n t s a r e i n s t a n t i a t e d . When t h e p i t c h e s a r e known , t h e i r
% d i f f e r e n c e s a r e c a l c u l a t e d f i r s t . When t h e i n t e r v a l name i s known ,
% i t s value i s calculated f i r s t .
c y c l i c i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , IC I , D i r ) :
nonvar ( I ) ,
I =< 8,
factor ( I0 , I , 7 ,
),
i n t e r v a l ( IC I 0 , Dir , CPC , CNC ) ,
p l u s ( FromCPC , CPC , ToCPC ) ,
p l u s ( FromCNC , CNC , ToCNC ) .
c y c l i c i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , IC I , Dir ) :
v ar ( I ) ,
p l u s ( FromCPC , CPC , ToCPC ) ,
p l u s ( FromCNC , CNC , ToCNC ) ,
i n t e r v a l ( IC I 1 , Dir , CPC , CNC ) ,
I i s ( I 1 mod 7 ) .
c y c l i c i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , I , Dir ) :
nonvar ( I ) ,
atomic ( I ) ,
i n t e r v a l ( I , Dir , CPC , CNC ) ,
p l u s ( FromCPC , CPC , ToCPC ) ,
p l u s ( FromCNC , CNC , ToCNC ) .
: op ( 1 5 0 , x f x , a b s i n t e r v a l ) .
: op ( 1 5 0 , x f x , a i n t e r v a l ) .
a b s o l u t e i n t e r v a l ( FromP : ToP , I c o n t o u r Dir ) : ! ,
a b s o l u t e i n t e r v a l ( FromP , ToP , I , Dir ) .
a b s o l u t e i n t e r v a l ( FromP : ToP , I ) :
a b s o l u t e i n t e r v a l ( FromP , ToP , I ,
).
a i n t e r v a l ( FromP : ToP , I c o n t o u r Dir ) : ! ,
a b s o l u t e i n t e r v a l ( FromP , ToP , I , Dir ) .
a i n t e r v a l ( FromP : ToP , I ) :
a b s o l u t e i n t e r v a l ( FromP , ToP , I ,
).
a b s o l u t e i n t e r v a l ( FromP , ToP , I , Dir ) :
c o n t p i t c h c l a s s ( FromP , FromCPC ) ,
c o n t n o t e c l a s s ( FromP , FromCNC ) ,
c o n t p i t c h c l a s s (ToP , ToCPC ) ,
113
c o n t n o t e c l a s s ( ToP , ToCNC ) ,
a b s o l u t e i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , I , Dir ) .
% Note : a b s o l u t e i n t e r v a l c p comes i n m u l t i p l e f l a v o u r s d e p e n d i n g on
% whi c h a r g u m e n t s a r e i n s t a n t i a t e d . When t h e p i t c h e s a r e known , t h e i r
% d i f f e r e n c e s a r e c a l c u l a t e d f i r s t . When t h e i n t e r v a l name i s known ,
% i t s value i s calculated f i r s t .
a b s o l u t e i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , I , Dir ) :
nonvar ( FromCPC ) , nonvar ( FromCNC ) , nonvar (ToCPC ) , nonvar (ToCNC ) , ! ,
p l u s ( FromCPC , CPC , ToCPC ) ,
p l u s ( FromCNC , CNC , ToCNC ) ,
i n t e r v a l ( I , Dir , CPC , CNC ) .
a b s o l u t e i n t e r v a l c p ( FromCPC , FromCNC , ToCPC , ToCNC , I , Dir ) :
nonvar ( I ) ,
i n t e r v a l ( I , Dir , CPC , CNC ) ,
p l u s ( FromCPC , CPC , ToCPC ) ,
p l u s ( FromCNC , CNC , ToCNC ) .
c o n t o u r ( FromP : ToP , Dir ) :
c o n t o u r ( FromP , ToP , Dir ) .
c o n t o u r ( FromP , ToP , Dir ) :
c o n t p i t c h c l a s s ( FromP , FromCPC ) ,
c o n t p i t c h c l a s s ( ToP , ToCPC ) ,
c o n t o u r c p ( FromCPC , ToCPC , Dir ) .
c o n t o u r c p ( FromCPC , ToCPC , Dir ) :
(ToCPC > = FromCPC >
D i r = up
;
ToCPC = FromCPC >
Dir = s t a t i c
;
D i r = down ) .
PRIVATE PREDICATES
/
INTERVAL DEFINITION
i n t e r v a l ( ? I n t e r v a l C l a s s ? I n t e r v a l , ? PC , ? NC)
I n t e r v a l i s t h e i n t e r v a l from ( 0 , 0 ) t o ( PC , NC)
/
i n t e r v a l ( u n i s , up , 0 , 0 ) .
b a s i c ( dim 2 ,
b a s i c ( min 2 ,
b a s i c ( maj 2 ,
b a s i c ( aug 2 ,
0,
1,
2,
3,
1)
1)
1)
1)
.
.
.
.
interval
interval
interval
interval
b a s i c ( dim 3 ,
b a s i c ( min 3 ,
b a s i c ( maj 3 ,
b a s i c ( aug 3 ,
2,
3,
4,
5,
2)
2)
2)
2)
.
.
.
.
i n t e r v a l b a s i c ( dim 4 , 4 , 3 ) .
i n t e r v a l b a s i c ( per 4 , 5 , 3 ) .
i n t e r v a l b a s i c ( aug 4 , 6 , 3 ) .
i n t e r v a l b a s i c ( t r i 4 , 6 , 3 ) . % t r i t o n e ( aug 4 )
i n t e r v a l b a s i c ( t r i 5 , 6 , 4 ) . % t r i t o n e ( dim 5 )
i n t e r v a l b a s i c ( dim 5 , 6 , 4 ) .
i n t e r v a l b a s i c ( per 5 , 7 , 4 ) .
i n t e r v a l b a s i c ( aug 5 , 8 , 4 ) .
interval
interval
interval
interval
b a s i c ( dim 6 ,
b a s i c ( min 6 ,
b a s i c ( maj 6 ,
b a s i c ( aug 6 ,
7, 5) .
8, 5) .
9, 5) .
10, 5) .
114
interval
interval
interval
interval
b a s i c ( dim 7 ,
b a s i c ( min 7 ,
b a s i c ( maj 7 ,
b a s i c ( aug 7 ,
9, 6) .
10, 6) .
11, 6) .
12, 6) .
i n t e r v a l b a s i c ( dim 8 , 1 1 , 7 ) .
i n t e r v a l b a s i c ( per 8 , 1 2 , 7 ) .
i n t e r v a l b a s i c ( aug 8 , 1 3 , 7 ) .
% TONAL INTERVALS
i n t e r v a l t o n a l ( IC , PC , NC) :
i n t e r v a l b a s i c ( IC , PC , NC ) .
% COMPOUND INTERVALS
i n t e r v a l t o n a l ( IC I , PC , NC) :
n o n v a r (PC ) , n o n v a r (NC ) , v a r ( IC ) , v a r ( I ) ,
PC > 1 2 ,
f a c t o r (PC , PC1 , 1 2 , Oct ) ,
f a c t o r (NC , NC1 , 7 , Oct ) ,
i n t e r v a l b a s i c ( IC I L o c a l , PC1 , NC1 ) ,
I i s I L o c a l + ( Oct 8 ) 1 .
i n t e r v a l t o n a l ( IC I , PC , NC) :
n o n v a r ( IC ) , n o n v a r ( I ) , v a r (PC ) , v a r (NC ) ,
I L o c a l 0 i s ( I mod 7 ) ,
( I L o c a l 0 = 1 > I L o c a l = 8 ; I L o c a l i s I L o c a l 0 ) ,
Oct i s I / / 8 ,
i n t e r v a l b a s i c ( IC I L o c a l , PC1 , NC1 ) ,
f a c t o r (PC , PC1 , 1 2 , Oct ) ,
PC > 1 2 ,
f a c t o r (NC , NC1 , 7 , Oct ) .
i n t e r v a l t o n a l ( IC I , PC , NC) :
i n t e r v a l b a s i c ( IC I L o c a l , PC1 , NC1 ) ,
f a c t o r (PC , PC1 , 1 2 , Oct ) ,
PC > 1 2 ,
f a c t o r (NC , NC1 , 7 , Oct ) ,
I i s I L o c a l + ( Oct 7 ) .
% SKIPS AND STEPS
i n t e r v a l t o n a l ( s t e p , PC , 1 ) :
i n t e r v a l b a s i c ( 2 , PC , 1 ) .
i n t e r v a l t o n a l ( s k i p , PC , NC) :
i n t e r v a l t o n a l ( , PC , NC ) ,
NC > 1 , PC > 2 .
% CONSONANCES AND DISSONANCES
i n t e r v a l t o n a l ( c o n s o n a n t , PC , NC) :
115
Testing
? g l o b a l s e t ( t a g ( p i t c h s y s t e m ) , d i a t o n i c ) .
Yes
? p i t c h ( 0 , 0 ) : p i t c h ( 7 , 5 ) c i n t e r v a l I c o n t o u r C .
I = dim 6
C = up ;
No
? p i t c h ( 0 , 0 ) : X c i n t e r v a l dim 6 c o n t o u r up .
X = pitch ( 7 , 5 ) ;
X = pitch (19, 12) ;
X = pitch (19, 12) ;
X = pitch (31, 19) ;
X = pitch (31, 19) ;
X = pitch (43, 26) ;
X = pitch (43, 26) ;
X = pitch (55, 33) ;
X = pitch (55, 33) ;
X = pitch (67, 40) ;
X = pitch (67, 40) ;
X = pitch (79, 47) ;
116
117
I = c o n s o n a n t c o n t o u r up ;
No
? p i t c h ( 0 , 0 ) : X a i n t e r v a l c o n s o n a n t .
X = pitch ( 3 , 2 ) ;
X = pitch ( 4 , 2 ) ;
X = pitch ( 7 , 4 ) ;
X = pitch ( 8 , 5 ) ;
X = pitch ( 9 , 5 ) ;
X = pitch (15, 9) ;
X = pitch (16, 9) ;
X = pitch (19, 11) ;
X = pitch (20, 12) ;
X = pitch (21, 12) ;
;
X = pitch (27, 16)
Yes
? p i t c h ( 0 , 0 ) : p i t c h ( 4 , 2 ) c o n t o u r C .
C = up
Yes
? p i t c h ( 4 , 2 ) : p i t c h ( 0 , 0 ) c o n t o u r C .
C = down ;
No
6.4.6
parts.pl
Code
/
PARTS MANAGER
M i c h a e l Droettboom
1999
118
119
/
USER FUNCTIONS
/
: op ( 1 5 0 , x f x , p a r t ) .
: op ( 1 5 0 , f x , n u m p a r t s ) .
p a r t ( E , Number ) :
e p a r t ( E , Number ) .
p a r t ( E , Text ) :
n o t i n t e g e r ( Text ) ,
e p a r t (E , P ) ,
p a r t (P , Text ) .
p a r t (P , Text ) :
i n t e g e r (P ) ,
n u m p a r t s (NP ) ,
p a r t d e f ( Text , P , NP ) .
num parts ( Parts ) :
g l o b a l ( num parts , Parts ) .
part
part
part
part
part
def ( only ,
, 1) : !.
d e f ( top , 1 ,
).
d e f ( bottom , X , X ) : X > 1 .
d e f ( c a n t u s f i r m u s , X , X) : X > 1.
d e f ( i n n e r , X , Y) :
part
part
part
part
120
X > 1,
X \= Y .
d e f ( soprano , 1 , 4 ) .
def ( alto , 2 , 4 ) .
def ( tenor , 3 , 4 ) .
def ( bass , 4 , 4 ) .
Testing
? g l o b a l s e t ( n u m p a r t s , 1 ) .
Yes ? e p a r t ( E , 1 ) , p a r t ( E , X ) .
E = e v e n t ( G459 , 1 , G461 ,
G468 , G469 ) X = 1 ;
G462 ,
G463 ,
G464 ,
G465 ,
G466 ,
G467 ,
E = e v e n t ( G459 , 1 , G461 ,
G468 , G469 ) X = o n l y ;
G462 ,
G463 ,
G464 ,
G465 ,
G466 ,
G467 ,
G448 ,
G449 ,
G450 ,
G451 ,
G452 ,
No ? e p a r t ( E , 4 ) , p a r t ( E , 4 ) .
E = e v e n t ( G444 , 4 ,
G453 , G454 ) ;
G446 ,
G447 ,
No ? e p a r t ( E , 4 ) , p a r t ( E , X ) .
E = e v e n t ( G459 , 4 , G461 ,
G468 , G469 ) X = 4 ;
G462 ,
G463 ,
G464 ,
G465 ,
G466 ,
G467 ,
E = e v e n t ( G459 , 4 , G461 ,
G468 , G469 ) X = o n l y ;
G462 ,
G463 ,
G464 ,
G465 ,
G466 ,
G467 ,
No
6.4.7
pitch.pl
Code
/
PITCH LIBRARY
M i c h a e l Droettboom
1999
121
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( ../ core/bi-math.pl ) .
: e n s u r e l o a d e d ( global .pl ) .
/
OPTIONS
These g l o b a l o p t i o n s a r e s e t by t h e GUI s y s t e m .
/
% p i t c h s y s t e m ( { d i a t o n i c | german | a t o n a l | s o l f e g e | a l l } )
g l o b a l d e f a u l t ( tag ( pi t c h s y s t e m ) , d i a t o n i c ) .
/
USER PREDICATES t o be u s e d w i t h i n r u l e d e f i n i t i o n s
e q u i v a l e n t p i t c h ( ? e v e n t a , ? e v e n t b ) ( i . e . c# == d&)
S u c c e e d s i f e v e n t a s o u n d s a t t h e same p i t c h a s e v e n t b .
octave (? event , ? octave )
Succeeds i f event i s in octave .
p i t c h n a m e ( ? e v e n t , ? name ) ( eq . t o p i t c h )
S u c c e e d s i f f r i e n d l y GUIDO name o f e v e n t i s name
/
: op ( 1 5 0 , x f x , o c t a v e ) .
o c t a v e ( E , Oct ) : e p i t c h ( E , P ) , o c t a v e (P , Oct ) .
o c t a v e ( p i t c h ( , CNC ) , Oct ) : ! , f a c t o r (CNC ,
, 7 , Oct ) .
: op ( 1 5 0 , x f x , p i t c h n a m e ) .
p i t c h n a m e (P , T) :
n o n v a r (T ) ,
p i t c h n a m e (T , PC , NC ) ,
p i t c h n o t e c l a s s (P , PC , NC ) .
p i t c h n a m e (P , T) :
v a r (T ) ,
p i t c h n o t e c l a s s (P , PC , NC ) ,
p i t c h n a m e (T , PC , NC ) .
: op ( 1 5 0 , x f , e q u i v a l e n t p i t c h ) .
122
e q u i v a l e n t p i t c h ( P1 : P2 ) :
p i t c h c l a s s ( P1 , PC ) ,
p i t c h c l a s s ( P2 , PC ) .
/
OPERATORS
/
% & f l a t , # s h a r p , && d o u b l e f l a t , ## s h a r p
% i s atonal
:
:
:
:
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
yf ,
yf ,
yf ,
yf ,
&).
#).
&&).
##).
/
DATA STRUCTURE
p i t c h (CPC , CNC)
where CPC i s c o n t i n u o u s p i t c h c l a s s
where CNC i s c o n t i n u o u s n o t e c l a s s
a s d e f i n e d i n [ Brinkmann . ]
/
) , CPC ) : ! .
c o n t p i t c h c l a s s ( p i t c h (CPC ,
c o n t p i t c h c l a s s ( E , CPC ) : e p i t c h ( E , p i t c h (CPC ,
)), !.
c o n t n o t e c l a s s ( p i t c h ( , CNC ) , CNC ) : ! .
c o n t n o t e c l a s s ( E , CNC ) : e p i t c h ( E , p i t c h ( , CNC ) ) , ! .
/
INFORMATION
p i t c h c l a s s ( ? P i t c h , ? P i t c hC l a s s ) % nonc o n t i n u o u s p i t c hc l a s s
n o t e c l a s s ( ? P i t c h , ? NoteC l a s s ) % nonc o n t i n u o u s n o t ec l a s s
p i t c h i n f o ( ? P i t c h , ? PC , ? NC , ? Oct , ? CPC , ? CNC , ? Name)
v a l i d p i t c h (? Pitch )
s u c c e e d s i f P i t c h i s v a l i d ( i . e . a v a l i d ( PC , NC)
relationship )
/
p i t c h n a m e o c t a v e (P , T , O) :
v a r (T ) , v a r (O ) ,
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , O ) ,
p i t c h n a m e (T , PC , NC ) .
p i t c h n a m e o c t a v e (P , T , O) :
n o n v a r (T ) ,
n o n v a r (O ) ,
p i t c h n a m e (T , PC , NC ) ,
123
CPC i s PC + (O 1 2 ) ,
CNC i s NC + (O 7 ) ,
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) .
p i t c h c l a s s (P , PC) :
c o n t p i t c h c l a s s (P , CPC ) ,
f a c t o r (CPC , PC , 1 2 ,
).
n o t e c l a s s (P , NC) :
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CNC , NC , 7 ,
).
p i t c h n o t e c l a s s (P , PC , NC) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) .
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , Oct ) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) .
p i t c h i n f o (P , PC , NC , Oct , CPC , CNC , Name ) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) ,
p i t c h n a m e (Name , PC , NC ) .
v a l i d p i t c h (P ) :
p i t c h c l a s s (P , PC ) ,
n o t e c l a s s (P , NC ) ,
p i t c h ( , PC , NC ) .
PRIVATE PREDICATES
/
USER and GUIDO PITCH NAMES
p i t c h n a m e ( ? Text , ? P i t c hC l a s s , ? NoteC l a s s )
s u c c e e d s when t e x t d e s c r i p t i o n o f n o t e s , c o m p a t i b l e w i t h
124
basic (c , 0, 0).
basic (d , 2 , 1 ) .
basic (e , 4, 2).
basic ( f , 5, 3).
basic (g , 7 , 4 ) .
basic (a , 9, 5).
basic (b , 1 1 , 6 ) .
basic (h , 11, 6) :
g l o b a l ( t a g ( p i t c h s y s t e m ) , german ) .
% ATONAL
p i t c h a t o n a l (T , PC , NC) :
p i t c h b a s i c (T , PC , NC ) .
pitch atonal ( cis , 1, 0).
pitch atonal ( cis , 1, 1).
pitch atonal ( dis , 3, 1).
pitch atonal ( dis , 3, 2).
pitch atonal ( f is , 6, 3).
pitch atonal ( f is , 6, 4).
pitch atonal ( gis , 8, 4).
pitch atonal ( gis , 8, 5).
pitch atonal ( ais , 10, 5).
pitch atonal ( ais , 10, 6).
% SOLFEGE
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
s o l f e g e ( do ,
solfege ( di ,
s o l f e g e ( ra ,
s o l f e g e ( re ,
solfege ( ri ,
s o l f e g e (me ,
s o l f e g e ( mi ,
s o l f e g e ( fa ,
solfege ( fi ,
s o l f e g e ( se ,
solfege ( sol
s o l f e g e ( so ,
solfege ( si ,
solfege ( le ,
solfege ( la ,
solfege ( l i ,
s o l f e g e ( te ,
solfege ( ti ,
0, 0) .
1, 0) .
1, 1) .
2, 1) .
3, 1) .
3, 2) .
4, 2) .
5, 3) .
6, 3) .
6, 4) .
, 7, 4) .
7, 4) .
8, 4) .
8, 5) .
9, 5) .
10, 5) .
10, 6) .
11, 6).
Testing
/
PITCH LIBRARY
M i c h a e l Droettboom
125
126
1999
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( ../ core/bi-math.pl ) .
: e n s u r e l o a d e d ( global .pl ) .
/
OPTIONS
These g l o b a l o p t i o n s a r e s e t by t h e GUI s y s t e m .
/
% p i t c h s y s t e m ( { d i a t o n i c | german | a t o n a l | s o l f e g e | a l l } )
g l o b a l d e f a u l t ( tag ( pi t c h s y s t e m ) , d i a t o n i c ) .
/
USER PREDICATES t o be u s e d w i t h i n r u l e d e f i n i t i o n s
e q u i v a l e n t p i t c h ( ? e v e n t a , ? e v e n t b ) ( i . e . c# == d&)
S u c c e e d s i f e v e n t a s o u n d s a t t h e same p i t c h a s e v e n t b .
octave (? event , ? octave )
Succeeds i f event i s in octave .
p i t c h n a m e ( ? e v e n t , ? name ) ( eq . t o p i t c h )
S u c c e e d s i f f r i e n d l y GUIDO name o f e v e n t i s name
/
: op ( 1 5 0 , x f x , o c t a v e ) .
o c t a v e ( E , Oct ) : e p i t c h ( E , P ) , o c t a v e (P , Oct ) .
o c t a v e ( p i t c h ( , CNC ) , Oct ) : ! , f a c t o r (CNC ,
, 7 , Oct ) .
127
: op ( 1 5 0 , x f x , p i t c h n a m e ) .
p i t c h n a m e (P , T) :
n o n v a r (T ) ,
p i t c h n a m e (T , PC , NC ) ,
p i t c h n o t e c l a s s (P , PC , NC ) .
p i t c h n a m e (P , T) :
v a r (T ) ,
p i t c h n o t e c l a s s (P , PC , NC ) ,
p i t c h n a m e (T , PC , NC ) .
: op ( 1 5 0 , x f , e q u i v a l e n t p i t c h ) .
e q u i v a l e n t p i t c h ( P1 : P2 ) :
p i t c h c l a s s ( P1 , PC ) ,
p i t c h c l a s s ( P2 , PC ) .
/
OPERATORS
/
% & f l a t , # s h a r p , && d o u b l e f l a t , ## s h a r p
% i s atonal
:
:
:
:
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
yf ,
yf ,
yf ,
yf ,
&).
#).
&&).
##).
/
DATA STRUCTURE
p i t c h (CPC , CNC)
where CPC i s c o n t i n u o u s p i t c h c l a s s
where CNC i s c o n t i n u o u s n o t e c l a s s
a s d e f i n e d i n [ Brinkmann . ]
/
) , CPC ) : ! .
c o n t p i t c h c l a s s ( p i t c h (CPC ,
c o n t p i t c h c l a s s ( E , CPC ) : e p i t c h ( E , p i t c h (CPC ,
)), !.
c o n t n o t e c l a s s ( p i t c h ( , CNC ) , CNC ) : ! .
c o n t n o t e c l a s s ( E , CNC ) : e p i t c h ( E , p i t c h ( , CNC ) ) , ! .
/
INFORMATION
p i t c h c l a s s ( ? P i t c h , ? P i t c hC l a s s ) % nonc o n t i n u o u s p i t c hc l a s s
n o t e c l a s s ( ? P i t c h , ? NoteC l a s s ) % nonc o n t i n u o u s n o t ec l a s s
p i t c h i n f o ( ? P i t c h , ? PC , ? NC , ? Oct , ? CPC , ? CNC , ? Name)
v a l i d p i t c h (? Pitch )
s u c c e e d s i f P i t c h i s v a l i d ( i . e . a v a l i d ( PC , NC)
128
relationship )
/
p i t c h n a m e o c t a v e (P , T , O) :
v a r (T ) , v a r (O ) ,
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , O ) ,
p i t c h n a m e (T , PC , NC ) .
p i t c h n a m e o c t a v e (P , T , O) :
n o n v a r (T ) ,
n o n v a r (O ) ,
p i t c h n a m e (T , PC , NC ) ,
CPC i s PC + (O 1 2 ) ,
CNC i s NC + (O 7 ) ,
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) .
p i t c h c l a s s (P , PC) :
c o n t p i t c h c l a s s (P , CPC ) ,
f a c t o r (CPC , PC , 1 2 ,
).
n o t e c l a s s (P , NC) :
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CNC , NC , 7 ,
).
p i t c h n o t e c l a s s (P , PC , NC) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) .
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , Oct ) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) .
p i t c h i n f o (P , PC , NC , Oct , CPC , CNC , Name ) :
c o n t p i t c h c l a s s (P , CPC ) ,
c o n t n o t e c l a s s (P , CNC ) ,
f a c t o r (CPC , PC , 1 2 , Oct ) ,
f a c t o r (CNC , NC , 7 , Oct ) ,
p i t c h n a m e (Name , PC , NC ) .
v a l i d p i t c h (P ) :
p i t c h c l a s s (P , PC ) ,
n o t e c l a s s (P , NC ) ,
p i t c h ( , PC , NC ) .
129
PRIVATE PREDICATES
/
USER and GUIDO PITCH NAMES
p i t c h n a m e ( ? Text , ? P i t c hC l a s s , ? NoteC l a s s )
s u c c e e d s when t e x t d e s c r i p t i o n o f n o t e s , c o m p a t i b l e w i t h
GUIDO N o t a t i o n Language ( Hoos / Hamel ( 1 9 9 7 ) ) , matches t h e
c o r r e s p o n d i n g p i t c h and n o t e c l a s s s y s t e m .
( b a s e d on Brinkman ( 1 9 9 0 ) )
Pitch c l a s s e s are i n t e g e r s in the range { 0 . . . 1 2 }
Note c l a s s e s a r e i n t e g e r s i n t h e r a n g e { 0 . . . 6 }
compatible with the f o l l o w i n g systems :
d i a t o n i c { c , d , e , f , g , a , b } w i t h s h a r p s and f l a t s
german { c , d , e , f , g , a , h } w i t h s h a r p s and f l a t s
a t o n a l { c , c i s , d , d i s , e , f , f i s , g , g i s , a , a i s , b}
s o l f e g e { do , d i , r a , r e , r i , me , mi , f a , f i , s e , s o l , s i ,
l e , l a , l i , t e , t i } ( u s i n g f i x e d do c o n v e n t i o n )
choose the c u r r e n t system with g l o b a l v a r i a b l e p i t c h s y s t e m .
( See t h e g l o b a l l i b r a r y f o r i n f o on g l o b a l v a r i a b l e s . )
/
p i t c h n a m e (T , PC , NC) :
p i t c h t o n a l (T , PC , NC ) .
p i t c h n a m e (T , PC , NC) :
( g l o b a l ( tag ( p it c h s y s t e m ) , atonal )
;
g l o b a l ( tag ( p it c h s y s t e m ) , a l l ) ) ,
p i t c h a t o n a l (T , PC , NC ) .
p i t c h n a m e (T , PC , NC) :
g l o b a l ( tag ( pi t c h s y s t e m ) , s o l f e g e ) ,
p i t c h s o l f e g e (T , PC , NC ) .
basic (c , 0, 0).
basic (d , 2 , 1 ) .
basic (e , 4, 2).
basic ( f , 5, 3).
basic (g , 7 , 4 ) .
basic (a , 9, 5).
basic (b , 1 1 , 6 ) .
basic (h , 11, 6) :
g l o b a l ( t a g ( p i t c h s y s t e m ) , german ) .
% ATONAL
p i t c h a t o n a l (T , PC , NC) :
p i t c h b a s i c (T , PC , NC ) .
pitch atonal ( cis , 1, 0).
pitch atonal ( cis , 1, 1).
pitch atonal ( dis , 3, 1).
pitch atonal ( dis , 3, 2).
pitch atonal ( f is , 6, 3).
pitch atonal ( f is , 6, 4).
pitch atonal ( gis , 8, 4).
pitch atonal ( gis , 8, 5).
pitch atonal ( ais , 10, 5).
pitch atonal ( ais , 10, 6).
% SOLFEGE
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
pitch
s o l f e g e ( do ,
solfege ( di ,
s o l f e g e ( ra ,
s o l f e g e ( re ,
solfege ( ri ,
s o l f e g e (me ,
s o l f e g e ( mi ,
s o l f e g e ( fa ,
solfege ( fi ,
s o l f e g e ( se ,
solfege ( sol
s o l f e g e ( so ,
solfege ( si ,
0, 0) .
1, 0) .
1, 1) .
2, 1) .
3, 1) .
3, 2) .
4, 2) .
5, 3) .
6, 3) .
6, 4) .
, 7, 4) .
7, 4) .
8, 4) .
130
6.4.8
solfege ( le
solfege ( la
solfege ( l i
s o l f e g e ( te
solfege ( ti
,
,
,
,
,
131
8, 5) .
9, 5) .
10, 5) .
10, 6) .
11, 6).
range.pl
Code
/
RANGE LIBRARY
M i c h a e l Droettboom
1999
These two e v e n t s
By name
Please note that a l l e v e n t s used with the p r e d i c a t e s i n t h i s l i b r a r y
must be i n s t a n t i a t e d . The p r e d i c a t e s w i l l n o t g e n e r a t e e v e n t s .
A d d i t i o n a l named r a n g e s can be d e f i n e d by a d d i n g r a n g e d e f p r e d i c a t e s
to the Pelog r u l e s e t f i l e .
r a n g e d e f p r e d i c a t e s have t h e form :
r a n g e d e f ( name , l o w p i t c h , h i g h p i t c h )
name i s t h e name o f t h e r a n g e .
l o w p i t c h i s t h e l o w e s t p i t c h i n t h e r a n g e , g i v e n i n t h e form
p i t c h n a m e o c t a v e . See t h e p i t c h l i b r a r y f o r d e t a i l s .
h i gh p i t c h i s the hi g he s t pitch in the range
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( event ) .
132
/
USER PREDICATES
i n r a n g e (+ e v e n t , + r a n g e )
Succeeds i f event i s in range .
e v e n t must be i n s t a n t i a t e d . i n r a n g e w i l l n o t g e n e r a t e
pitches .
r a n g e must be a named r a n g e o r two i n s t a n t i a t e d p i t c h e s . I t
may n o t be d e f i n e d a s a r o a m i n g i n t e r v a l .
r a n g e i s (+ e v e n t s , ? r a n g e )
S u c c e e d s i f t h e r a n g e o f a l l e v e n t s up t o and i n c l u d i n g t h e
c u r r e n t event i s e x a c t l y equal to range .
r a n g e w i t h i n (+ e v e n t s , + r a n g e )
S u c c e e d s i f t h e r a n g e o f a l l e v e n t s up t o and i n c l u d i n g t h e
c u r r e n t e v e n t i s w i t h i n r a n g e . The e v e n t s must be
instantiated .
/
: op ( 1 5 0 , x f x , r a n g e w i t h i n ) .
: op ( 1 2 5 , x f x , : ) .
% Named r a n g e
r a n g e w i t h i n ( ( T , E ) , Range ) :
n o n v a r ( Range ) ,
r a n g e d e f 0 ( Range , LP , UP ) ,
f i n d r a n g e (T , E , FLP , FUP ) , ! ,
r a n g e i n r a n g e ( FLP : FUP , LP : UP ) .
% E x p l i c i t range
r a n g e w i t h i n ( ( T , E ) , LN : UN) :
n o n v a r (LN ) ,
n o n v a r (UN ) ,
f i n d r a n g e (T , E , FLP , FUP ) , ! ,
r a n g e i n r a n g e ( FLP : FUP , LN : UN ) .
% Roaming i n t e r v a l r a n g e
r a n g e w i t h i n ( (T , E ) , I ) :
f i n d r a n g e (T , E , FLP , FUP ) , ! ,
a b s o l u t e i n t e r v a l ( FLP : IU , I c o n t o u r up ) ,
c o n t o u r ( IU , FUP , down ) .
: op ( 1 5 0 , x f x , r a n g e i s ) .
% Named Range
r a n g e i s ( ( T , E ) , Range ) :
n o n v a r ( Range ) ,
r a n g e d e f 0 ( Range , LP , UP ) ,
f i n d r a n g e (T , E , LP , UP ) .
% E x p l i c i t Range
133
r a n g e i s ( ( T , E ) , FLP : FUP ) :
f i n d r a n g e (T , E , FLP , FUP ) .
% I n t e r v a l Range
r a n g e i s ( (T , E ) , I ) :
f i n d r a n g e (T , E , FLP , FUP ) ,
a b s o l u t e i n t e r v a l ( FLP : FUP , I ) .
: op ( 1 5 0 , x f x , i n r a n g e ) .
% Named Range
i n r a n g e ( E , Range ) :
n o n v a r ( Range ) , n o n v a r ( E ) ,
r a n g e d e f 0 ( Range , LP , UP ) ,
r a n g e i n r a n g e ( E : E , LP : UP ) .
% E x p l i c i t Range
i n r a n g e ( E , LP : UP) :
r a n g e i n r a n g e ( E : E , LP : UP ) .
/
HELPER PREDICATES
/
r a n g e i n r a n g e ( I L : IU , RL
f i x r a n g e ( I L : IU ,
f i x r a n g e ( RL : RU ,
( c o n t o u r ( I L 0 , RL0 ,
( c o n t o u r ( IU0 , RU0 ,
: RU)
IL0 :
RL0 :
down )
up ) ;
:
IU0 ) ,
RU0 ) ,
; c o n t o u r ( I L 0 , RL0 , s t a t i c ) ) ,
c o n t o u r ( IU0 , RU0 , s t a t i c ) ) .
f i x r a n g e ( L : U , L0 : U0 ) :
( c o n t o u r ( L , U , down) >
( L0 = U ,
U0 = L )
;
( L0 = L ,
U0 = U ) ) , ! .
f i n d r a n g e (T , E , FLP , FUP ) :
f i n d r a n g e (T , E , E , FLP , E , FUP ) , ! .
f i n d r a n g e (T , E , X , X , Y , Y) :
g e t p r e v e v e n t (T , E , s t a r t ) .
f i n d r a n g e (T , E , FLPIn , FLPOut , FUPIn , FUPOut ) :
g e t p r e v e v e n t (T , E , P r e v ) ,
( c o n t o u r ( FLPIn , P r e v , down) >
FLP0 = P r e v ;
FLP0 = FLPIn ) ,
( c o n t o u r ( FUPIn , P r e v , up) >
FUP0 = P r e v ;
FUP0 = FUPIn ) ,
f i n d r a n g e (T , P r e v , FLP0 , FLPOut , FUP0 , FUPOut ) .
134
/
NAMED RANGE DEFINITIONS
/
% C o n v e r t s t h e r a n g e d e f s u s i n g GUIDO p i t c h names t o t h e i n t e r n a l
% (PC , NC ) r e p r e s e n t a t i o n u s e d t h r o u g h o u t P e l o g . The r e s u l t i s
% a s s e r t e d s o t h a t i t d o e s n o t have t o be computed t w i c e .
r a n g e d e f 0 ( Range , LP , UP) :
r a n g e d e f ( Range , LPNLO , UPNUO ) ,
p i t c h n a m e o c t a v e ( LP , LPN , LO ) ,
p i t c h n a m e o c t a v e (UP , UPN , UO ) ,
a s s e r t a ( r a n g e d e f 0 ( Range , LP , UP ) ) .
range
range
range
range
/
TEST
/
t e s t r a n g e i s ( F i l e , I n d e x , Range ) :
load score ( File , T,
),
g e t l a b e l ( Index , T, E ) , ! ,
r a n g e i s ( ( T , E ) , Range ) .
t e s t r a n g e w i t h i n ( F i l e , I n d e x , Range ) :
load score ( File , T,
),
g e t l a b e l ( Index , T, E ) , ! ,
r a n g e w i t h i n ( ( T , E ) , Range ) .
Testing
? t e s t r a n g e i s ( ../ example .gmn , 8 , X ) .
P a r s e OK
P a r s e OK
135
X = min 3 c o n t o u r up ;
X = s k i p c o n t o u r up ;
X = c o n s o n a n t c o n t o u r up ;
No ? t e s t r a n g e i s ( ../ example .gmn , 1 1 , X ) .
P a r s e OK
P a r s e OK
P a r s e OK
Yes 1 3 ? i n r a n g e ( p i t c h ( 1 2 , 7 ) , s o p r a n o ) .
Yes 1 4 ? i n r a n g e ( p i t c h ( 1 2 , 7 ) , p i t c h ( 1 3 , 8 ) : p i t c h ( 1 1 , 6 ) ) .
Yes 1 5 ? i n r a n g e ( p i t c h ( 1 2 , 7 ) , p i t c h ( 1 3 , 8 ) : p i t c h ( 1 5 , 9 ) ) .
No
6.4.9
scale.pl
Code
/
SCALE LIBRARY
M i c h a e l Droettboom
1999
The l i b r a r y h a n d l e s c o l l e c t i o n s o f p i t c h e s . I n t h i s c o n t e x t , t h e
words s c a l e and mode a r e u s e d i n t e r c h a n g e a b l y .
I n P e l o g , s c a l e s names a r e s p e c i f i e d i n t h e form t o n i c p i t c h s e t ,
where t o n i c i s t h e s t a r t i n g p i t c h o f t h e s c a l e , and p i t c h s e t t h e
name o f a s e t o f p i t c h e s .
t o n i c can be any v a l i d p i t c h name .
information .
See t h e p i t c h l i b r a r y f o r more
136
/
OPTIONS
These g l o b a l o p t i o n s a r e s e t by t h e GUI s y s t e m .
/
% g l o b a l d e f a u l t ( tag ( s c a l e ) , c major ) .
137
/
OPERATORS
/
:
:
:
:
:
op ( 1 2 5 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
yfx
yf ,
yf ,
yf ,
yf ,
, ).
&).
#).
&&).
##).
/
USER PREDICATES
c u r r e n t s c a l e (? s c a l e )
Succeeds i f s c a l e i s the c u r r e n t s c a l e
i n s c a l e (? event , + Tonic S c a l e )
Succeeds i f event i s i n Tonic S c a l e
s c a l e d e g ( ? e v e n t , + s c a l e , ? deg )
S u c c e e d s i f e v e n t i s t h e degt h d e g r e e o f s c a l e
s c a l e d e g ( ? e v e n t , ? deg )
S u c c e e d s i f e v e n t i s t h e Degt h d e g r e e o f t h e c u r r e n t s c a l e
l e a d i n g t o n e (? Pitch , + Scale )
l e a d i n g t o n e (? Pitch )
t o n i c (? Pitch , + Scale )
t o n i c (? Pitch )
/
: op ( 1 5 0 , x f , s c a l e ) .
: op ( 1 5 0 , x f x , s c a l e ) .
s c a l e (P ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s c a l e (P , S c a l e ) .
s c a l e (P , S c a l e ) :
v a r (P ) , % P i t c h i s v a r i a b l e , s o g e n e r a t e p i t c h e s
get scale ( Scale , Pitches ),
member ( Octave , [ 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 ,
5, 5, 6, 6, 7, 7, 8, 8]),
member ( ( PC , NC ) , P i t c h e s ) ,
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , Octave ) .
s c a l e (P , S c a l e ) :
n o n v a r (P ) , % P i t c h e s i s n o t v a r i a b l e , s o s i m p l e v e r i f y
get scale ( Scale , Pitches ),
p i t c h n o t e c l a s s (P , PC , NC ) ,
member ( ( PC , NC ) , P i t c h e s ) .
: op ( 1 5 0 , f x , c u r r e n t s c a l e ) .
c u r r e n t s c a l e ( Scale ) :
g l o b a l ( tag ( s c a l e ) , Scale ) .
: op ( 1 5 0 , x f x , s c a l e d e g r e e ) .
% r a i s e d s c al e degree
s c a l e d e g r e e ( P i t c h , r a i s e d Deg ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , 1 ) .
% lowered s c a l e degree
s c a l e d e g r e e ( P i t c h , l o w e r e d Deg ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , 1 ) .
% numbered s c a l e d e g r e e
s c a l e d e g r e e ( P i t c h , Deg ) :
i n t e g e r ( Deg ) ,
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s c a l e d e g r e e ( P i t c h , S c a l e , Deg ) .
% named s c a l e d e g r e e ( a s d e f i n e d i n s p e c i a l t o n e d e f )
s c a l e d e g r e e ( P i t c h , Deg ) :
n o t i n t e g e r ( Deg ) ,
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s p e c i a l t o n e ( P i t c h , S c a l e , Deg ) .
% s c a l e d e g r e e i n a nonc u r r e n t s c a l e
s c a l e d e g r e e ( P i t c h , S c a l e , Deg ) :
i n t e g e r ( Deg ) ,
p i t c h n o t e c l a s s ( P i t c h , PC , NC ) ,
get scale ( Scale , Pitches ),
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) .
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , Mod ) :
i n t e g e r ( Deg ) ,
nonvar ( Pitch ) ,
p i t c h n o t e c l a s s ( P i t c h , PC1 , NC ) ,
get scale ( Scale , Pitches ),
PC i s PC1 Mod ,
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) .
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , Mod ) :
i n t e g e r ( Deg ) ,
var ( Pitch ) ,
get scale ( Scale , Pitches ),
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) ,
PC1 i s PC + Mod ,
p i t c h n o t e c l a s s ( P i t c h , PC1 , NC ) .
s p e c i a l t o n e ( Pitch , Special ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s p e c i a l t o n e ( Pitch , Scale , Special ) .
s p e c i a l t o n e ( Pitch , Scale , Special ) :
s p e c i a l t o n e ( S c a l e , S p e c i a l , PC , NC ) ,
p i t c h n o t e c l a s s ( P i t c h , PC , NC ) .
s p e i c a l t o n e ( c S c a l e , S p e c i a l , PC , NC) :
s p e c i a l t o n e d e f ( S p e c i a l , S c a l e , STN ) ,
p i t c h n a m e (STN , PC , NC ) .
138
139
s p e c i a l t o n e ( T o n i c S c a l e , S p e c i a l , PC , NC) :
p i t c h n a m e ( T o n i c , TPC , TNC ) ,
s p e c i a l t o n e d e f ( S p e c i a l , S c a l e , STN ) ,
p i t c h n a m e (STN , SPC , SNC ) ,
PC i s ( SPC + TPC ) mod 1 2 ,
NC i s ( SNC + TNC ) mod 7 ,
a s s e r t a ( ( s p e c i a l t o n e ( T o n i c S c a l e , S p e c i a l , PC , NC ) : ! ) ) .
: op ( 1 5 0 , x f , l e a d i n g t o n e ) .
leading tone ( Pitch ) :
s p e c i a l t o n e ( Pitch , leading ) .
: op ( 1 5 0 , x f , t o n i c ) .
tonic ( Pitch ) :
s p e c i a l t o n e ( Pitch , tonic ) .
: op ( 1 5 0 , x f , f i n a l ) .
f i n a l ( Pitch ) :
s p e c i a l t o n e ( Pitch , f i n a l ) .
/
SCALE DEFINITIONS
/
% Converts the s c a l e d e f i n e d i n s c a l e d e f to the binomial r e p r e s e n t a t i o n
% used throughout Pelog . Also t r a n s p o s e s the s c a l e s to t o n i c s ot he r
% t h e n c . The r e s u l t i s a s s e r t e d t o t h e d a t a b a s e t o a v o i d r ec o m p u t a t i o n .
g e t s c a l e ( Tonic S c a l e , P i t c h e s ) :
s c a l e d e f ( S c a l e , BPNs ,
),
p i t c h n a m e l i s t ( BPNs , BPs ) ,
p i t c h n a m e ( T o n i c , TPC , TNC ) ,
c r e a t e s c a l e ( BPs , TPC , TNC , P i t c h e s ) , ! ,
a s s e r t a ( ( g e t s c a l e ( Tonic S c a l e , P i t c h e s ) : ! ) ) .
pitch name list ( [ ] , [ ] ) .
p i t c h n a m e l i s t ( [ PN | N T a i l ] , [ ( PC , NC ) | P T a i l ] ) :
p i t c h n a m e (PN , PC , NC ) ,
p i t c h n a m e l i s t ( NTail , PTail ) .
c r e a t e s c a l e ( [ ( BPC , BNC ) | BT ] , TPC , TNC , [ ( PC , NC ) | T ] ) :
PC i s ( BPC + TPC ) mod 1 2 ,
NC i s ( BNC + TNC ) mod 7 ,
c r e a t e s c a l e (BT , TPC , TNC , T ) .
create scale ( [ ] ,
,
, []).
: m u l t i f i l e
s c a l e d e f /3.
s c a l e d e f ( major , [ c , d , e , f , g , a , b ] , per 1 ) : ! .
140
d e f ( o c t a t o n i c , [ c , c #, d #, e , f #, g , a , a # ] , none ) : ! .
d e f ( o c t a t o n i c s h a r p s , X , Y) : s c a l e d e f ( o c t a t o n i c , X , Y) .
d e f ( o c t a t o n i c f l a t s , [ c , d & , e & , e , g & , g , a , b & ] , none ) : ! .
d e f ( h a l f w h o l e , X , Y) : s c a l e d e f ( o c t a t o n i c , X , Y ) , ! .
d e f ( h a l f w h o l e s h a r p s , Y , Z ) : s c a l e d e f ( o c t a t o n i cs h a r p s , Y , Z ) , ! .
d e f ( h a l f w h o l e f l a t s , Y , Z ) : s c a l e d e f ( o c t a t o n i c f l a t s , Y , Z ) , ! .
d e f ( w h o l e h a l f , [ c , d , d #, f , f #, g #, a , b ] , none ) : ! .
d e f ( w h o l e h a l f s h a r p s , X , Y) : s c a l e d e f ( w h o l e h a l f , X , Y) .
d e f ( w h o l e h a l f f l a t s , [ c , d , e & , f , g & , a & , a , b ] , none ) : ! .
/
RELATIONSHIP OF SCALE TO KEY
Given a S c a l e , r e t u r n s the c o r r e s p o n d i n g key .
T h i s i n f o r m a t i o n i s u s e d by t h e GUIDO p a r s e r .
141
/
scale key ( Scale , 0) :
scale def ( Scale ,
, none ) .
s c a l e k e y ( T o n i c S c a l e , Key ) :
scale def ( Scale ,
, D e g re e ) ,
g e t s c a l e ( T o n i c S c a l e , [ ( PC , NC ) | R e s t ] ) ,
a b s o l u t e i n t e r v a l c p (PC , NC , KPC , KNC , D e g re e , down ) ,
p i t c h n a m e ( p i t c h (KPC , KNC ) , Key ) , ! .
/
NUMBER OF SHARPS AND FLATS FOR MAJOR KEYS
n e e d e d f o r GUIDO t r a n s l a t i o n
/
key
key
key
key
key
key
key
key
key
key
key
key
key
key
key
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
(0, c ).
(1, g).
(2, d).
(3, a ).
(4, e ).
(5, b).
( 6 , f #).
( 7 , c #).
(1, f ) .
( 2 , b &).
( 3 , e &).
( 4 , a &).
( 5 , d &).
( 6 , g &).
( 7 , c &).
/
SPECIAL TONE DEFINITIONS
/
: m u l t i f i l e
s p e c i a l t o n e d e f /3.
special tone def ( final ,
, c ) : !.
s p e c i a l t o n e d e f ( ambitus , X , Y) : s p e c i a l t o n e d e f ( t o n i c , X , Y) .
s p e c i a l t o n e d e f ( t e n o r , hypo , e ) : ! .
special tone def ( tenor ,
, g) : !.
s p e c i a l t o n e d e f ( t o n i c , hypo , g ) : ! .
special tone def ( tonic ,
, c ) : !.
s p e c i a l t o n e d e f ( leading , phrygian , b &) : !.
s p e c i a l t o n e d e f ( l e a d i n g , hypol y d i a n , f # ) : ! .
142
s p e c i a l t o n e d e f ( l e a d i n g , hypo , f ) : ! .
s p e c i a l t o n e d e f ( l e a d i n g , n a t u r a lm i n o r , b & ) : ! .
special tone def ( leading ,
, b) : !.
% Only u s e d i n TONAL h a r m o n i c p r a c t i c e
s p e c i a l t o n e d e f ( s u p e r t o n i c , major , d ) : ! .
s p e c i a l t o n e d e f ( s u p e r t o n i c , m i n o r , d ) : ! .
s p e c i a l t o n e d e f ( mediant , major , e ) : ! .
s p e c i a l t o n e d e f ( m e d i a n t , m i n o r , e & ) : ! .
s p e c i a l t o n e d e f ( subdominant , major , f ) : ! .
s p e c i a l t o n e d e f ( s u b d o m i n a n t , m i n o r , f ) : ! .
s p e c i a l t o n e d e f ( dominant , m a j o r , g ) : ! .
s p e c i a l t o n e d e f ( dominant , m i n o r , g ) : ! .
s p e c i a l t o n e d e f ( submediant , major , a ) : ! .
s p e c i a l t o n e d e f ( s u b m e d i a n t , m e l o d i cm i n o r , a ) : ! .
s p e c i a l t o n e d e f ( s u b m e d i a n t , m i n o r , a & ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , major , b ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , n a t u r a lm i n o r , b & ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , m i n o r , b ) : ! .
Testing
/
SCALE LIBRARY
M i c h a e l Droettboom
1999
The l i b r a r y h a n d l e s c o l l e c t i o n s o f p i t c h e s . I n t h i s c o n t e x t , t h e
words s c a l e and mode a r e u s e d i n t e r c h a n g e a b l y .
I n P e l o g , s c a l e s names a r e s p e c i f i e d i n t h e form t o n i c p i t c h s e t ,
where t o n i c i s t h e s t a r t i n g p i t c h o f t h e s c a l e , and p i t c h s e t t h e
name o f a s e t o f p i t c h e s .
t o n i c can be any v a l i d p i t c h name .
information .
See t h e p i t c h l i b r a r y f o r more
143
name i s t h e name o f t h e s c a l e .
p i t c h l i s t i s a l i s t o f p i t c h names t h a t make up t h e s c a l e .
T h i s l i s t s h o u l d be p r o v i d e d w i t h c a s t o n i c . The s c a l e l i b r a r y
w i l l do t h e work o f t r a n s p o s i n g t h e s c a l e t o t o n i c s o t h e r t h a n c .
k e y i n t e r v a l i s t h e i n t e r v a l be tween t h e s c a l e s t o n i c and t h e t o n i c
o f t h e m a j o r k e y s i g n a t u r e t h a t t h i s s c a l e s h o u l d be w r i t t e n i n .
For e x a m p l e , t h e t o n i c o f t h e d o r i a n mode i s on t h e s e c o n d
d e g r e e o f t h e m a j o r s c a l e , s o t h e k e y i n t e r v a l v a l u e i s maj 2 .
The s c a l e l i b r a r y a l s o h a n d l e s s c a l e d e g r e e s . S c a l e d e g r e e s can be
s p e c i f i e d a s an i n t e g e r where t h e t o n i c o f t h e s c a l e i s e q u a l t o 1 .
S c a l e d e g r e e s can a l s o be s p e c i f i e d by name .
The P e l o g r u l e programmer can add more s p e c i a l t o n e d e f i n i t i o n s by
adding s p e c i a l t o n e d e f f a c t p r e d i c a t e s to the Pelog r u l e s e t f i l e .
s p e c i a l t o n e d e f p r e d i c a t e s hav e t h e form :
s p e c i a l t o n e d e f ( name , p i t c h s e t , p i t c h ) .
name i s t h e name o f t h e s p e c i a l t o n e .
p i t c h s e t s p e c i f i e s the s c a l e s that the s p e c i a l tone a p p l i e s to .
p i t c h i s the s p e c i a l tone i n that s c a l e with c as t o n i c .
/
/
REQUIRED LIBRARIES
/
: e n s u r e l o a d e d ( pitch ) .
: e n s u r e l o a d e d ( interval ) .
: m u l t i f i l e
s c a l e d e f /3,
s p e c i a l t o n e d e f /3.
/
OPTIONS
These g l o b a l o p t i o n s a r e s e t by t h e GUI s y s t e m .
/
% g l o b a l d e f a u l t ( tag ( s c a l e ) , c major ) .
/
OPERATORS
/
: op ( 1 2 5 , y f x , ) .
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
op ( 1 0 0 ,
yf ,
yf ,
yf ,
yf ,
144
&).
#).
&&).
##).
/
USER PREDICATES
c u r r e n t s c a l e (? s c a l e )
Succeeds i f s c a l e i s the c u r r e n t s c a l e
i n s c a l e (? event , + Tonic S c a l e )
Succeeds i f event i s i n Tonic S c a l e
s c a l e d e g ( ? e v e n t , + s c a l e , ? deg )
S u c c e e d s i f e v e n t i s t h e degt h d e g r e e o f s c a l e
s c a l e d e g ( ? e v e n t , ? deg )
S u c c e e d s i f e v e n t i s t h e Degt h d e g r e e o f t h e c u r r e n t s c a l e
l e a d i n g t o n e (? Pitch , + Scale )
l e a d i n g t o n e (? Pitch )
t o n i c (? Pitch , + Scale )
t o n i c (? Pitch )
/
: op ( 1 5 0 , x f , s c a l e ) .
: op ( 1 5 0 , x f x , s c a l e ) .
s c a l e (P ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s c a l e (P , S c a l e ) .
s c a l e (P , S c a l e ) :
v a r (P ) , % P i t c h i s v a r i a b l e , s o g e n e r a t e p i t c h e s
get scale ( Scale , Pitches ),
member ( Octave , [ 0 , 1 , 1 , 2 , 2 , 3 , 3 , 4 , 4 ,
5, 5, 6, 6, 7, 7, 8, 8]),
member ( ( PC , NC ) , P i t c h e s ) ,
p i t c h n o t e c l a s s o c t a v e (P , PC , NC , Octave ) .
s c a l e (P , S c a l e ) :
n o n v a r (P ) , % P i t c h e s i s n o t v a r i a b l e , s o s i m p l e v e r i f y
get scale ( Scale , Pitches ),
p i t c h n o t e c l a s s (P , PC , NC ) ,
member ( ( PC , NC ) , P i t c h e s ) .
: op ( 1 5 0 , f x , c u r r e n t s c a l e ) .
c u r r e n t s c a l e ( Scale ) :
g l o b a l ( tag ( s c a l e ) , Scale ) .
: op ( 1 5 0 , x f x , s c a l e d e g r e e ) .
% r a i s e d s c al e degree
s c a l e d e g r e e ( P i t c h , r a i s e d Deg ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , 1 ) .
% lowered s c a l e degree
145
s c a l e d e g r e e ( P i t c h , l o w e r e d Deg ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , 1 ) .
% numbered s c a l e d e g r e e
s c a l e d e g r e e ( P i t c h , Deg ) :
i n t e g e r ( Deg ) ,
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s c a l e d e g r e e ( P i t c h , S c a l e , Deg ) .
% named s c a l e d e g r e e ( a s d e f i n e d i n s p e c i a l t o n e d e f )
s c a l e d e g r e e ( P i t c h , Deg ) :
n o t i n t e g e r ( Deg ) ,
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s p e c i a l t o n e ( P i t c h , S c a l e , Deg ) .
% s c a l e d e g r e e i n a nonc u r r e n t s c a l e
s c a l e d e g r e e ( P i t c h , S c a l e , Deg ) :
i n t e g e r ( Deg ) ,
p i t c h n o t e c l a s s ( P i t c h , PC , NC ) ,
get scale ( Scale , Pitches ),
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) .
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , Mod ) :
i n t e g e r ( Deg ) ,
nonvar ( Pitch ) ,
p i t c h n o t e c l a s s ( P i t c h , PC1 , NC ) ,
get scale ( Scale , Pitches ),
PC i s PC1 Mod ,
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) .
m o d i f i e d s c a l e d e g r e e ( P i t c h , S c a l e , Deg , Mod ) :
i n t e g e r ( Deg ) ,
var ( Pitch ) ,
get scale ( Scale , Pitches ),
n t h 1 ( Deg , P i t c h e s , ( PC , NC ) ) ,
PC1 i s PC + Mod ,
p i t c h n o t e c l a s s ( P i t c h , PC1 , NC ) .
s p e c i a l t o n e ( Pitch , Special ) :
g l o b a l ( tag ( s c a l e ) , Scale ) ,
s p e c i a l t o n e ( Pitch , Scale , Special ) .
s p e c i a l t o n e ( Pitch , Scale , Special ) :
s p e c i a l t o n e ( S c a l e , S p e c i a l , PC , NC ) ,
p i t c h n o t e c l a s s ( P i t c h , PC , NC ) .
s p e i c a l t o n e ( c S c a l e , S p e c i a l , PC , NC) :
s p e c i a l t o n e d e f ( S p e c i a l , S c a l e , STN ) ,
p i t c h n a m e (STN , PC , NC ) .
s p e c i a l t o n e ( T o n i c S c a l e , S p e c i a l , PC , NC) :
p i t c h n a m e ( T o n i c , TPC , TNC ) ,
s p e c i a l t o n e d e f ( S p e c i a l , S c a l e , STN ) ,
p i t c h n a m e (STN , SPC , SNC ) ,
PC i s ( SPC + TPC ) mod 1 2 ,
NC i s ( SNC + TNC ) mod 7 ,
a s s e r t a ( ( s p e c i a l t o n e ( T o n i c S c a l e , S p e c i a l , PC , NC ) : ! ) ) .
146
: op ( 1 5 0 , x f , l e a d i n g t o n e ) .
leading tone ( Pitch ) :
s p e c i a l t o n e ( Pitch , leading ) .
: op ( 1 5 0 , x f , t o n i c ) .
tonic ( Pitch ) :
s p e c i a l t o n e ( Pitch , tonic ) .
: op ( 1 5 0 , x f , f i n a l ) .
f i n a l ( Pitch ) :
s p e c i a l t o n e ( Pitch , f i n a l ) .
/
SCALE DEFINITIONS
/
% Converts the s c a l e d e f i n e d i n s c a l e d e f to the binomial r e p r e s e n t a t i o n
% used throughout Pelog . Also t r a n s p o s e s the s c a l e s to t o n i c s ot he r
% t h e n c . The r e s u l t i s a s s e r t e d t o t h e d a t a b a s e t o a v o i d r ec o m p u t a t i o n .
g e t s c a l e ( Tonic S c a l e , P i t c h e s ) :
s c a l e d e f ( S c a l e , BPNs ,
),
p i t c h n a m e l i s t ( BPNs , BPs ) ,
p i t c h n a m e ( T o n i c , TPC , TNC ) ,
c r e a t e s c a l e ( BPs , TPC , TNC , P i t c h e s ) , ! ,
a s s e r t a ( ( g e t s c a l e ( Tonic S c a l e , P i t c h e s ) : ! ) ) .
pitch name list ( [ ] , [ ] ) .
p i t c h n a m e l i s t ( [ PN | N T a i l ] , [ ( PC , NC ) | P T a i l ] ) :
p i t c h n a m e (PN , PC , NC ) ,
p i t c h n a m e l i s t ( NTail , PTail ) .
c r e a t e s c a l e ( [ ( BPC , BNC ) | BT ] , TPC , TNC , [ ( PC , NC ) | T ] ) :
PC i s ( BPC + TPC ) mod 1 2 ,
NC i s ( BNC + TNC ) mod 7 ,
c r e a t e s c a l e (BT , TPC , TNC , T ) .
create scale ( [ ] ,
,
, []).
: m u l t i f i l e
s c a l e d e f /3.
scale
scale
scale
scale
d e f ( major , [ c , d , e , f , g , a , b ] , per 1 ) : ! .
d e f ( h a r m o n i c m i n o r , [ c , d , e & , f , g , a & , b ] , maj 6 ) : ! .
d e f ( n a t u r a l m i n o r , [ c , d , e & , f , g , a & , b & ] , maj 6 ) : ! .
d e f ( m e l o d i c m i n o r , [ c , d , e & , f , g , a & , a , b & , b ] , maj 6 ) : ! .
147
d e f ( o c t a t o n i c , [ c , c #, d #, e , f #, g , a , a # ] , none ) : ! .
d e f ( o c t a t o n i c s h a r p s , X , Y) : s c a l e d e f ( o c t a t o n i c , X , Y) .
d e f ( o c t a t o n i c f l a t s , [ c , d & , e & , e , g & , g , a , b & ] , none ) : ! .
d e f ( h a l f w h o l e , X , Y) : s c a l e d e f ( o c t a t o n i c , X , Y ) , ! .
d e f ( h a l f w h o l e s h a r p s , Y , Z ) : s c a l e d e f ( o c t a t o n i cs h a r p s , Y , Z ) , ! .
d e f ( h a l f w h o l e f l a t s , Y , Z ) : s c a l e d e f ( o c t a t o n i c f l a t s , Y , Z ) , ! .
d e f ( w h o l e h a l f , [ c , d , d #, f , f #, g #, a , b ] , none ) : ! .
d e f ( w h o l e h a l f s h a r p s , X , Y) : s c a l e d e f ( w h o l e h a l f , X , Y) .
d e f ( w h o l e h a l f f l a t s , [ c , d , e & , f , g & , a & , a , b ] , none ) : ! .
/
RELATIONSHIP OF SCALE TO KEY
Given a S c a l e , r e t u r n s the c o r r e s p o n d i n g key .
T h i s i n f o r m a t i o n i s u s e d by t h e GUIDO p a r s e r .
/
scale key ( Scale , 0) :
scale def ( Scale ,
, none ) .
s c a l e k e y ( T o n i c S c a l e , Key ) :
scale def ( Scale ,
, D e g re e ) ,
g e t s c a l e ( T o n i c S c a l e , [ ( PC , NC ) | R e s t ] ) ,
148
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
sharps
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
flats
(0, c ).
(1, g).
(2, d).
(3, a ).
(4, e ).
(5, b).
( 6 , f #).
( 7 , c #).
(1, f ) .
( 2 , b &).
( 3 , e &).
( 4 , a &).
( 5 , d &).
( 6 , g &).
( 7 , c &).
/
SPECIAL TONE DEFINITIONS
/
: m u l t i f i l e
s p e c i a l t o n e d e f /3.
, c ) : !.
s p e c i a l t o n e d e f ( ambitus , X , Y) : s p e c i a l t o n e d e f ( t o n i c , X , Y) .
s p e c i a l t o n e d e f ( t e n o r , hypo , e ) : ! .
special tone def ( tenor ,
, g) : !.
s p e c i a l t o n e d e f ( t o n i c , hypo , g ) : ! .
special tone def ( tonic ,
, c ) : !.
special
special
special
special
special
tone
tone
tone
tone
tone
def ( leading
def ( leading
def ( leading
def ( leading
def ( leading
,
,
,
,
,
phrygian , b &) : !.
hypol y d i a n , f # ) : ! .
hypo , f ) : ! .
n a t u r a lm i n o r , b & ) : ! .
, b) : !.
% Only u s e d i n TONAL h a r m o n i c p r a c t i c e
s p e c i a l t o n e d e f ( s u p e r t o n i c , major , d ) : ! .
s p e c i a l t o n e d e f ( s u p e r t o n i c , m i n o r , d ) : ! .
s p e c i a l t o n e d e f ( mediant , major , e ) : ! .
s p e c i a l t o n e d e f ( m e d i a n t , m i n o r , e & ) : ! .
s p e c i a l t o n e d e f ( subdominant , major , f ) : ! .
s p e c i a l t o n e d e f ( s u b d o m i n a n t , m i n o r , f ) : ! .
s p e c i a l t o n e d e f ( dominant , m a j o r , g ) : ! .
s p e c i a l t o n e d e f ( dominant , m i n o r , g ) : ! .
s p e c i a l t o n e d e f ( submediant , major , a ) : ! .
s p e c i a l t o n e d e f ( s u b m e d i a n t , m e l o d i cm i n o r , a ) : ! .
s p e c i a l t o n e d e f ( s u b m e d i a n t , m i n o r , a & ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , major , b ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , n a t u r a lm i n o r , b & ) : ! .
s p e c i a l t o n e d e f ( s u b t o n i c , m i n o r , b ) : ! .
149
II
Visual Pelog
150
7.1
The Pelog system provides a language in which to specify musical constraints. These
constraints are applied by an interpreter to generate or evaluate musical scores.
7.2
152
guide the solution-searching at various points in the process. This would change
the focus of the program from a counterpoint generator to a tutorial expert system.
Such an approach may have limited utility, however, as most musicians tend to
develop melodies as complete phrases rather than note-by-note. Since the ability to
test complete phrases for integrity within a set of rules already exists in the present
system, the interactive approach is perhaps merely delegating computation to the
user rather than provided the user with a valuable educational experience.
The area that interests me the most is the Pelog language design itself. The
original genesis of this project was the realisation that the logical programming
paradigm is analogous to the specification of musical relationships. Distilling Fux
[11] into finite musical relatinships proved to be the most interesting musicological
part of the project.
7.3
The problem
The Pelog language gains its power from the fact that it is a declarative logical
language derived from Prolog. It is relatively easy to concisely specify musical relationships using Pelog, as proven by the process of specifying modal counterpoint.
However, what is easy to someone with a background in programming is not necessarily easy for the average musician. For example, take the following code for the
rule that specifies a step followed by a step:
r u l e ( Step -$prev2
$prev1
$prev2
step , contour , 1 ) :
: $prev1 a i n t e r v a l step ,
: $event a i n t e r v a l step ,
comment Step followed by a step .
This states that a) the absolute interval between the two notes before the current
note must be a step, b) the absolute interval between current note and the note
before it must be a step, and c) add a comment to the note two notes before this
one reading Step followed by a step. This description is not readily apparent from
the code to someone who has not studied the Pelog library reference. A possible
improvement might be to make it more like English:
t h e a b s o l u t e i n t e r v a l between $ p r e v 2 and $ p r e v 1 i s a s t e p ,
t h e a b s o l u t e i n t e r v a l between $ p r e v 1 and $ e v e n t i s a s t e p ,
add comment t o $ p r e v 2 t h a t r e a d s S t e p f o l l o w e d by a s t e p .
The trouble with this approach is that there are often multiple English sentences
that express the same meaning. One could just as easily have chosen the following
expression:
between $ p r e v 1 and $ p r e v 2 t h e r e i s an a b s o l u t e i n t e r v a l o f a s t e p ,
between $ p r e v 2 and $ e v e n t t h e r e i s an a b s o l u t e i n t e r v a l o f a s t e p ,
S t e p f o l l o w e d by a s t e p s h o u l d be added a s a comment t o $ p r e v 2 .
7.4
153
The solution
Visual programming, as defined here, refers to programming using graphical rather than
textual means. It should not be confused with Microsofts definition of Visual which refers
to a GUI-builder environment on top of a traditional text-based programming system. Visual
programming is not a new idea, but is experiencing somewhat of a renaissance, particularly in
database application development [18], and was the featured topic of a recent issue of Dr. Dobbs
Journal [21].
8.1
Each rule is defined using a special case of a directed graph called a musical constraint graph. These graphs define relationships between musical events. These
events include a current event, the previous eight events in its part and the previous eight events in another part that is occuring simultaneously. These events
correspond to the metavariables used in the text-based Pelog Language. For the
rationale behind this choice, see the Pelog Language Reference (page 2).
The user is initially presented with a set of notes representing these eighteen
events known as the note template (Figure 8.11 ).
Relationships are drawn between these notes in a manner that should be familiar
to musicians with a background in common music notation. Relationships between
events that occur sequentially are drawn horizontally (Figure 8.2), and relationships
between events that occur simultaneously are drawn vertically (Figure 8.3).
More complex (compound) relationships can also be created. (Figure 8.4).
1
All of the example diagrams were produced directly from Visual Pelog by converting then to
Postscript using Tks psprint function.
154
155
156
8.2
Constraint primitives
There are a number of different constraint types available that correspond to the
library predicates defined in the text-based version of the Pelog language. These
include constraints for interval, pitch, range and scale.
There are also logical constraints. These include:
OR: OR is a binary relationship that connects two other constraints. It allows
either or both of the constraints to be active. It may be created between any
two contraints.
NOT: NOT negates the effect of the constraint. The rule succeeds when the
constraint attached to NOT does not succeed. It may be applied to any
constraint.
GREATER/LESS: GREATER/LESS is true when the value of one constraint
is greater or less than another constraint. The GREATER/LESS constraint
can only be drawn between two comparable constraints. For example, two
interval constraints can be constrained so one interval is smaller than the
other. However, it makes no sense for an interval constraint to be compared
to a scale constraint, for example. The same constraint functions as both
greater and less depending on the direction in which one drags when creating
the constraint. Always draw from the constraint with the greater value to the
constraint with the lesser value.
EQUAL: EQUAL is true when the value of two contraints are equal. Like the
GREATER/LESS constraint, the EQUAL constraint may only be drawn
between comparable constraints.
8.3
Some examples
8.3.1
Step-wise motion
157
To constrain all melodic intervals to step-wise motion, you want to constrain the
current note and the previous note to the interval of a step.
Draw an interval constraint between the current note and the previous note and
set its value to a step using the on-screen musical keyboard. The resulting rule is
in Figure 8.5.
8.3.2
Constraining all melodic intervals to a step can be quite limiting. Suppose you want
to also allow melodic intervals of a third. One can use the OR logical constructor
to compose the constraint that melodic intervals can be steps or thirds.
Starting with the step-wise rule defined above, add another interval constraint
between the same two nodes and specify the interval of a third using the on-screen
musical keyboard. Then, draw an OR constraint between the two interval constraints. The resulting rule is in Figure 8.6.
158
8.3.3
159
Suppose you want to avoid any parallel intervals, (i.e. where a given harmonic
interval is repeated on two consecutive beats.) This would imply that the current
harmonic interval and the preceding harmonic interval are note equal.
Draw an interval constraint vertically between the current note and the note
occuring simultaneously. Draw another one between the two notes preceding those.
The interval type does not need to be specified, because we dont care what intervals they are, only that they are equal. The equality is specified by drawing an
EQUALS logical constraint between the two interval constraints. Finally, we want
to disallow this equality, so we can apply the NOT constraint to the EQUALS
constraint.
8.4
Future extensions
The most obvious next step to improve the Visual Pelog language is the implement
the remaining constraints that are part of the core Pelog library.
As well, it may be useful to implement metaconstraints to handle commonly
occuring combinations of the logical constraints such as parallelism or repetition.
Introduction
For this section, it is assumed you already have some basic knowledge of Pelogs
concept of musical constraints. For this, please see The Pelog language on page 2.
This section does not describe how to specify musical constraints using the Visual
Pelog language. For that, please see The Visual Pelog language on page 154.
At the present time, the Visual Pelog system is a prototype with many unimplemented features. Most notably missing is the conduit between Visual Pelog and the
Pelog systems that would allow interpretation of musical scores. In this chapter, all
unimplemented features are marked with a diamond ().
The Visual Pelog environment allows the user to edit Pelog rule sets. This
comprises two main activities:
Organizing the heirarchy of rules: Changing the order and priority of rules
within the rule set.
Editing the musical constraints (rules): Changing the semantics of each rule
in the rule set.
9.2
161
162
Help browser: The help browser provides on-line help on the tools in the tool
palette.
9.3
Not implemented.
9.4
Rule manager
The rule manager displays the rules in the rule set in a heirarchical tree similar to
Microsoft Windows Explorer. The tree display is collapsible, meaning you can hide
and show categories of rules by clicking on the open/close folder button ( ).
9.4.1
Selecting rules
To select a rule, click on it once. Only one rule can be selected at a given time.
9.4.2
Adding rules
To add a new rule to the rule set, click on rule class (group of rules) and press the
Insert key. A new rule called Untitled Rule is added to the rule class.
9.4.3
Moving rules
To move rules from one rule class to another, simply drag them with the mouse to
their new location. To cancel the drag operation, simply drag it over any whitespace
in the Rule Manager.
9.4.4
Deleting rules
9.4.5
Editing rules
To edit a rule, select it and press the Enter / Return key, or simply double-click it.
9.5
Rule editor
163
9.5.1
Graph editor
164
9.5.2
The musical keyboard (Figure 9.4) allows the user to modify properties about the
selected constraint. For example, with the Interval constraint type, the musical
keyboard allows the user to select the specific interval to which the events will be
constrained.
As you slide the mouse over the keys, the pitch of the key at the mouse cursor
is displayed on the left-hand side.
The musical keyboard acts differently based on context. There are three main
modes of operation (consult the constraints on-line documentation in the tool
palette to determine which mode is used for different types of constraints):
Single: Click on a key to select a pitch. Only one key can be selected at a given
time.
Multi: Click on a key to toggle its status. You can select as many keys as necessary.
Range: Click on a starting note and drag to an ending note to select an unbroken
range of keys.
To make the keys themselves larger, press the zoom-in button (
them to their original size, press the zoom-out button ( ).
). To restore
165
9.6
Tool palette
Different types of constraints can be selected from the tool palette. The set of
tools is based on the plug-ins installed on your system. In this way, new types of
constraints can be added to the system at a later date.
Currently, only the basic logical constraints (OR, NOT, =, >) and the Interval Tool are implemented.
Selecting a tool
To select a tool, click on it.
Getting help on a tool
As you bring your mouse over a tool, its name is displayed in the status bar at
bottom of the Tool Palette window. For more detailed help on a given tool, select
the tool and press the question mark button ( ). A description of the tool and its
associated constraint is opened in the on-line help browser.
10
10.1
Specification of problem
The graph layout algorithm (GLA) used by the Visual Pelog rule editor is the most
computationally complex part of the visual system. Its musical constraint graphs
define relationships between a given set of musical events. For a definition of a
musical constraint graph, see page 154.
Specifically, a graph layout algorithm supporting this definition must coordinate
the following constraints.
All layout decisions should be automated. The user need only be concerned
with the semantics of the graph.
All nodes must be visible, therefore no two nodes may overlap.
Spatial relationships between nodes must be meaningful: eg., a node representing a relationship between two other nodes must physically reside as close
as possible to the center of those two nodes.
166
167
10.2
Background
10.2.1
Sugiyama algorithm
The graph layout algorithm used by Visual Pelog is loosely based on the classic
Sugiyama graph layout algorithm [35] as summarized by Ivan Bowman [3]. This
algorithm was designed for laying out heirarchical tree-like structures. It results in
graphs which have the following properties (as summarized in Bowman [3]):
hierarchical layout of vertices
minimized edge crossings
connected vertices are close together
balanced layout of edges
The algorithm proceeds in three steps:
1. Perform a topological sort on the nodes, assigning each to a level. Unlike the
classical definition of a topological sort [7], more than one node may occupy
the same level.
2. Within each level, the vertices are permuted to minimize line crossings.
3. Assign positions to maximize barycenters (i.e. so that children are centered
under their parents and vice versa.)
There are some disadvantages to the basic Sugiyama algorithm that, on its own,
make it unsuitable for the present task.
No constraints. Certain nodes, such as those that make up the note template
need to be constrained. (i.e. The notes that make up each part should be vertically aligned, and the notes occuring simultaneously should be horizontally
aligned.)
Poor stability. Dramatic changes to the layout can occur when minor
changes are made to the graph itself. When too many nodes move, the user
is easily confused and can lose a sense of orientation within the graph.
One-dimensional hierarchical relationship. It assumes a straightforward
one-dimensional heirarchy between all nodes. For the problem at hand, one
needs to specify heirarchies that work in either the horizontal or vertical direction: in effect a double Sugiyama algorithm.
10.2.2
168
Paulisch [25] describes a way to extend the Sugiyama algorithm to support constraints. In his extension, the position of every node can be constrained based the
positions of an arbitrary number of other nodes. Paulisch suggests a number of
constraint types, both simple and exotic. The constraints deemed necessary for the
present problem are as follows:
equal: The node must be on the same level as some other nodes.
less: The node must be on a lesser (northwesterly) level than some other
nodes.
greater: The node must be on a greater (southeasterly) level than some other
nodes.
near: The node must be as close as possible to the artihmetic mean (center)
of the listed nodes.
To support the extension to constraints, the topological sort step of the Sugiyama
algorithm is replaced with a constraint solver. In fact, a topological sort can be performed without using a classical topological sort algorithm [7] simply by converting
all of the parent-child relationships to greater and less constraints.
10.2.3
Another observation made by Paulisch is that the layout of graphs using the
Sugiyama algorithm tends to change dramatically when simple changes are made
to the graph. When too many nodes change positions by too much, users can easily
lose their bearings. This is an important consideration for the present project, as
the graph is to be edited interactively. Paulisch proposed an elaborate solution
to this problem where the allowed movement of a given node is limited by some
-constant and the number of nodes that can move in any iteration is also limited.
While a similar solution may be incorporated in the future, at present, the
stability problem is solved using animation. When a change is made to the graph,
the nodes are gradually animated from their original positions to their new positions.
While occasionally the movement of nodes can be drastic, for the most part this
more nave approach is quite acceptable. The movement of nodes also represents,
in an entertaining way, the dynamic nature of the graph.
10.2.4
10.3
10.3.1
General design
169
10.3.2
The top-level
10.3.3
Before performing the constraint-based topological sort, nodes are moved to their
ideal near locations which is the average (center) of the locations of all the nodes
on the near constraint list.
10.3.4
The topological sort is performed by resolving the equal, greater and less constraints.
The success of this constraint resolution is dependent on how the constraints are
determined at the time of node creation. (see Creating nodes on 170).
At the top-level, all nodes are sorted first on the x axis, then on the y axis.
As the constraints are solved, nodes are always moved southeast, so that the
graph expands outward. If nodes were allowed to move in arbitrary directions, the
graph might implode and explode on itself, creating deadlock in an infinite loop.
For example, to solve the equal constraint, the position of the current node and
the nodes on its equal list are compared. The node with the more northwesterly
position is moved to the location of the node with the more southeasterly position.
10.3.5
170
Conflict resolution
Once the constraint-based topological sort has assigned a level in both dimensions
to all nodes, the entire set of nodes is scanned for any conflicts. A conflict exists
when two or more nodes occupy the same location.
A search is performed for nodes occupying the same location. When a pair is
found, the Node instance will move itself or its partner. The choice of which Node
to move is determined by the average location of all nodes on the near constraint
list. For example, if node a has a more leftward near average than node b, then node
a will move left.
10.3.6
The grid layout subalgorithm maps the logical coordinates as determined by the
topological sort to the physical coordinates on the canvas. Since different nodes
have different physical sizes, the maximum size of the nodes in each row and column
must be determined before assigning physical values to each row and column.
10.3.7
Animation
Once the new physical coordinates of all the nodes has been determined, the nodes
are moved incrementally from their old locations to their new ones while updating
the display. The number of animation steps is determined by a constant.
10.3.8
Creating nodes
An important part of the graph layout algorithm is how the constraints are assigned
when nodes are created. The Visual Pelog system uses three families of nodes, from
which all other nodes are inherited.
Note nodes
Note nodes make up the note template. They must be aligned to other nodes, both
vertically and horizontally, to represent the sequential and concurrent relationships
of the note in the note template. This is performed using the equals constraint. For
example, the current note node and the note that occurs simultaneously must
appear directly below it. Therefore, it is on the equals constraint list on the x-axis.
Binary relations
A binary relation node is used to specify a relationship between two nodes. In
practice, the interval constraint is an example of a binary relation.
If a binary relation node x is defined between nodes a and b, and the relative
location of a and b are a < b, then the relative locations of all three must be
a < x < b. The less and greater constraints are assigned accordingly. The node x
should also be as close as possible to the centre of the two nodes a and b. Therefore,
a and b are added to the near constraint list of x.
171
Unary relations
A unary relation node is used to define a relation involving only one node. In
practice, constraints involving pitch class are often unary relation nodes.
Unary relation nodes should be as close as possible to the nodes they are related
to. If a unary relation node x is defined on a node a, then a is added to the near
constraint list of x.
10.4
10.4.1
RuleEditor.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# R u l e E d i t o r . py
#
#
Top l e v e l c l a s s e s f o r e d i t i n g r u l e s
# V i s u a l Pelog imports
from U t i l i m p o r t
from C o n s t a n t s i m p o r t
i m p o r t Nodes , P a l e t t e , M u s i c W i d g e t s
# Tkinter imports
i m p o r t T k i n t e r , Canvas
##################################################
# RuleEditor
#
# The f r a m e i n wh ic h t h e r u l e e d i t o r r e s i d e s .
#
c l a s s RuleEditorWindow ( T k i n t e r . T o p l e v e l ) :
#
# I n i t i a l i z a t i o n routines
def
i n i t ( s e l f , p a l e t t e , p a r e n t=None , r u l e=None ) :
self . rule = rule
Tkinter . Toplevel .
init (
s e l f , parent ,
c l a s s = RuleEditor )
s e l f . t i t l e ( "Rule Editor " )
# A r e f e r e n c e to the t o o l p a l e t t e that w i l l i n t e r a c t with
# this rule editor
self . palette = palette
# Make t h e w i d g e t s t h a t a r e p u t o f t h e r u l e e d i t o r window
s e l f . make widgets ( r u l e )
172
173
def s c r o l l X ( s e l f , f i r s t , l a s t ) :
s e l f . hscroll . set ( f i r s t , last )
def s c r o l l Y ( s e l f , f i r s t , l a s t ) :
s e l f . v s c r o ll . set ( f i r s t , last )
d e f update name ( s e l f , a r g s ) :
s e l f . r u l e . update name ( s e l f . t e x t . g e t ( 0.0 , end ) )
##################################################
# RuleEditorCanvas
#
# The r u l e g r a p h d i s p l a y w i d g e t
#
c l a s s R u l e E d i t o r C a n v a s ( C u r s o r M a n a g e r , T k i n t e r . Canvas ) :
#
# I n i t i a l i z a t i o n routines
def
i n i t ( s e l f , p a l e t t e=None , m a s t e r=None , k e y b o a r d=None , r u l e=None ) :
self . rule = rule
T k i n t e r . Canvas .
init (
s e l f , master ,
b a c k g r o u n d=RE COLOR CANVAS BACKGROUND ,
c o n f i n e=TRUE)
s e l f . make rubber ()
s e l f . b i n d ( s e q u e n c e="< Motion >" , f u n c= s e l f . motion )
CursorManager .
init ( self )
# A r e f e r e n c e to the t o o l p a l e t t e that w i l l i n t e r a c t with
# this rule editor
self . palette = palette
# A r e f e r e n c e t o t h e ons c r e e n m u s i c a l k e y b o a r d t h a t w i l l
# i n t e r a c t with t h i s e d i t o r
s e l f . keyboard = keyboard
# The two n o d e s t h a t a r e d e t e r m i n e d by d r a g g i n g between
# two n o d e s . These n o d e s a r e s e n t t o t h e c u r r e n t t o o l t o
# c r e a t e new p r e d i c a t e s .
s e l f . s t a r t n o d e = s e l f . f i n i s h n o d e = None
# A l i s t of a l l the nodes i n the canvas
s e l f . nodeList = rule . nodeList
# Create the b o i l e r p l a t e note nodes . . .
i f s e l f . nodeList == []:
s e l f . create basic nodes ()
else :
f o r node i n s e l f . n o d e L i s t :
node . m a s t e r = s e l f
174
node . v i s u a l i z e ( )
# . . . and a n i m a t e them t o t h e i r f i n a l p o s i t i o n
f o r node i n s e l f . n o d e L i s t :
node . f i n a l i z e c o o r d ( )
# C r e a t e s t h e r u b b e r band l i n e
def
make rubber ( s e l f ) :
s e l f . r u b b e r = Canvas . L i n e (
s e l f , 5, 5, 5, 5)
s e l f . r u b b e r [ width ] = 2 . 0
s e l f . r u b b e r [ stipple ] = gray50
# C r e at e s the b o i l e r p l a t e note nodes
def
create basic nodes ( self ):
p r e v 8 = Nodes . NoteNode ( s e l f , $prev8 )
s e l f . r u l e . Top row = p r e v 8
p r e v 7 = Nodes . NoteNode ( s e l f , $prev7 , prevX=p r e v 8 )
p r e v 6 = Nodes . NoteNode ( s e l f , $prev6 , prevX=p r e v 7 )
p r e v 5 = Nodes . NoteNode ( s e l f , $prev5 , prevX=p r e v 6 )
p r e v 4 = Nodes . NoteNode ( s e l f , $prev4 , prevX=p r e v 5 )
p r e v 3 = Nodes . NoteNode ( s e l f , $prev3 , prevX=p r e v 4 )
p r e v 2 = Nodes . NoteNode ( s e l f , $prev2 , prevX=p r e v 3 )
p r e v 1 = Nodes . NoteNode ( s e l f , $prev1 , prevX=p r e v 2 )
e v e n t = Nodes . NoteNode ( s e l f , $event , prevX=p r e v 1 , box=TRUE)
v e r t 8 = Nodes . NoteNode ( s e l f , $vert8 , prevY=p r e v 8 )
s e l f . r u l e . Bottom row = v e r t 8
v e r t 7 = Nodes . NoteNode ( s e l f , $vert7 , prevX=v e r t 8 , prevY=p r e v 7 )
v e r t 6 = Nodes . NoteNode ( s e l f , $vert6 , prevX=v e r t 7 , prevY=p r e v 6 )
v e r t 5 = Nodes . NoteNode ( s e l f , $vert5 , prevX=v e r t 6 , prevY=p r e v 5 )
v e r t 4 = Nodes . NoteNode ( s e l f , $vert4 , prevX=v e r t 5 , prevY=p r e v 4 )
v e r t 3 = Nodes . NoteNode ( s e l f , $vert3 , prevX=v e r t 4 , prevY=p r e v 3 )
v e r t 2 = Nodes . NoteNode ( s e l f , $vert2 , prevX=v e r t 3 , prevY=p r e v 2 )
v e r t 1 = Nodes . NoteNode ( s e l f , $vert1 , prevX=v e r t 2 , prevY=p r e v 1 )
v e r t = Nodes . NoteNode ( s e l f , $vert , prevX=v e r t 1 , prevY=e v e n t )
#
# Event h a n d l e r s
def psprint ( s e l f , args ) :
self . postscript (
f i l e ="e:/ Pelog / Visual /test.ps" )
# P l a c e s d e s c r i p t i v e t e x t a b o u t t h e node i n t h e s t a t u s b a r
d e f d e s c r i b e n o d e ( s e l f , node ) :
s e l f . m a s t e r . s t a t u s [ text ] = node
def undescribe node ( s e l f ) :
s e l f . m a s t e r . s t a t u s [ text ] =
d e f s e t s t a r t n o d e ( s e l f , node ) :
s e l f . rubber . coords ([(5,5), (5,5)])
175
s e l f . s t a r t n o d e = node
d e f s e t f i n i s h n o d e ( s e l f , node ) :
s e l f . rubber . coords ([(5,5), (5,5)])
s e l f . f i n i s h n o d e = node
d e f motion ( s e l f , a r g s ) :
i f s e l f . start node :
s e l f . rubber . coords (
[ ( s e l f . s t a r t n o d e . pt [ 0 ] ,
s e l f . s t a r t n o d e . pt [ 1 ] ) ,
( s e l f . canvasx ( args . x ) ,
s e l f . canvasy ( args . y ) ) ] )
# R e t u r n s t h e node a t p h y s i c a l c o o r d i n a t e s ( x , y )
# I f none e x i s t s , r e t u r n s None
def find node ( s e l f , x , y ) :
x = s e l f . canvasx ( x )
y = s e l f . canvasy ( y )
s e l f . rubber . coords ([(5, 5), (5, 5)])
z = self . find overlapping (x , y , x , y)
if z != ():
r e t u r n s e l f . i t e m s [ z [ 0 ] ] . node
else :
r e t u r n None
# P e r f o r m s an a c t i o n b a s e d on t h e c u r r e n t l y s e l e c t e d t o o l on
# t h e two n o d e s s t a r t n o d e and f i n i s h n o d e
def run tool ( s e l f ) :
d e b u g p r i n t ( " Running tool on:" , s e l f . s t a r t n o d e , s e l f . f i n i s h n o d e )
s e l f . palette . run tool ( s e l f , s e l f . start node , s e l f . finish node )
s e l f . animate ()
s e l f . s t a r t n o d e = s e l f . f i n i s h n o d e = None
#
# User f u n c t i o n s
d e f a d d n o d e ( s e l f , node ) :
s e l f . n o d e L i s t . append ( node )
s e l f . layout nodes ()
# s e l f . animate ()
d e f r e m o v e n o d e ( s e l f , node ) :
s e l f . n o d e L i s t . remove ( node )
s e l f . r e m o v e r e f e r e n c e s ( node )
s e l f . layout nodes ()
s e l f . animate ()
d e f r e m o v e r e f e r e n c e s ( s e l f , node ) :
for n in s e l f . nodeList :
i f n . a == node o r n . b == node :
176
n . delete ()
#
# Graph l a y o u t f u n c t i o n s
# T h i s i s t h e t o p l e v e l g r a p h l a y o u t f u n c t i o n .
# F o l l o w i n g t h e o b j e c to r i e n t e d p a r a d i g m , most o f t h e s m a r t s o f t h e
# a l g o r i t h m a r e embedded i n t h e node o b j e c t s t h e m s e l v e s .
d e f l a y o u t n o d e s ( s e l f , draw =0):
d e b u g p r i n t ( " LAYING OUT NODES ===============" ) # Debugging i n f o r m a t i o n
c o n f l i c t s = TRUE # Assume we w i l l f i n d c o n f l i c t s
while c o n f l i c t s :
# I . Gu e s s a t a good s t a r t i n g l o c a t i o n f o r t h e n o d e s by moving them
#
t o t h e a v e r a g e l o c a t i o n o f t h e n o d e s s p e c i f i e d by t h e n e a r
#
constraint
f o r node i n s e l f . n o d e L i s t :
node . g o t o n e a r ( )
# I I . T o p o l o g i c a l l y s o r t n o d e s b a s e d on t h e r e l a t i o n s h i p s and
#
c o n s t r a i n t s d e f i n e d w i t h e a c h node , t h e n r e s o l v e any c o n f l i c t s
#
( where two n o d e s o c c u p y t h e same c e l l ) . R e p e a t p r o c e s s u n t i l
#
t h e r e a r e no c o n f l i c t s . Each node i s a s s i g n e d a l o c a t i o n on
#
a LOGICAL g r i d .
while c o n f l i c t s :
s e l f . t o p o l o g i c a l s o r t ()
c o n f l i c t s = s e l f . resolve ()
s e l f . t o p o l o g i c a l s o r t ()
c o n f l i c t s = s e l f . resolve ()
# I I I . Map t h e r e s u l t s o f t h e t o p o l o g i c a l s o r t ( l o c a t i o n on a LOGICAL
#
GRID ) t o a l o c a t i o n on t h e PHYSICAL GRID .
s e l f . grid layout ()
# Runs a t o p o l o g i c a l s o r t on a l l n o d e s i n t h e g r a p h
# The a c t u a l work o f c o n s t r a i n t r e a l i z a t i o n i s p e r f o r m e d by t h e
# nodes t h e m s e l v e s
def t o p o l o g i c a l s o r t ( s e l f ) :
d e b u g p r i n t ( " TOPOLOGICAL SORT ===============" )
f o r dimen i n [ 0 , 1 ] : # S o r t x t h e n y a x i
d e b u g p r i n t ( " Dimension " , dimen , " ---------->" )
f o r node i n s e l f . n o d e L i s t :
d e b u g p r i n t ( " Starting at root node :" , node )
node . t o p o l o g i c a l s o r t ( dimen , RE MINGRID , RE MAXGRID)
# r e s o l v e c o n f l i c t s ( when two n o d e s o c c u p y t h e same c e l l )
def r e s o l v e ( s e l f ) :
d e b u g p r i n t ( " RESOLVING ===============" )
177
c o n f l i c t s = FALSE # no c o n f l i c t s y e t . . .
f o r node1 i n s e l f . n o d e L i s t :
# Compare e a c h node i n g r a p h . . .
f o r node2 i n s e l f . n o d e L i s t : # . . . w i t h e v e r y o t h e r node
# I f t h e s e n o d e s a r e n o t t h e SAME node , b u t have t h e same
# l o c a t i o n on t h e LOGICAL GRID . . .
i f n o t ( node1 i s node2 ) and node1 . t p t == node2 . t p t :
c o n f l i c t s = TRUE # . . . t h e n we ve f o u n d a c o n f l i c t !
d e b u g p r i n t ( " Resolving " , node1 , "vs ." , node2 )
node1 . r e s o l v e ( node2 ) # L e t t h e n o d e s t h e m s e l v e s duke i t o u t
break
i f c o n f l i c t s : break
return conflicts
# A d j us t s the l o g i c a l l o c a t i o n s of a l l the nodes i n the graph so that
# e v e r y node i s g r e a t e r t h a n ( 0 , 0 ) . R e t u r n s t h e maximum s o t h a t t h e
# g r i d l a y o u t a l g o r i t h m can do i t s magic .
def adjust min max ( s e l f ) :
gmax = [ RE MINGRID , RE MINGRID ]
gmin = [ RE MAXGRID , RE MAXGRID ]
f o r dimen i n [ 0 , 1 ] : # Do x t h e n y a x i s
# F i n d t h e minimum and maximum i n t h i s d i m e n s i o n
f o r node i n s e l f . n o d e L i s t :
gmin [ dimen ] = min ( node . t p t [ dimen ] , gmin [ dimen ] )
gmax [ dimen ] = max ( node . t p t [ dimen ] , gmax [ dimen ] )
# S h i f t a l l n o d e s by t h e minimum v a l u e
f o r node i n s e l f . n o d e L i s t :
node . t p t [ dimen ] = node . t p t [ dimen ] + 1 ( gmin [ dimen ] )
# C o r r e c t t h e maximum f o r t h i s s h i f t
gmax [ dimen ] = gmax [ dimen ] + 1 ( gmin [ dimen ] )
r e t u r n gmax
# D e t e r m i n e s t h e PHYSICAL c o o r d i n a t e s o f e v e r y node b a s e d on t h e i r
# LOGICAL c o o r d i n a t e s . S i n c e n o d e s a r e n o t o f a f i x e d s i z e , t h i s f u n c t i o n
# e n s u r e s t h a t n o d e s do n o t o v e r l a p e a c h o t h e r .
d e f g r i d l a y o u t ( s e l f , draw =0):
# S h i f t t h e g r a p h t o t h e ( 0 , 0 ) o r i g i n , and o b t a i n t h e o v e r a l l s i z e
gmax = s e l f . a d j u s t m i n m a x ( )
# C r e a t e two empty n e s t e d l i s t s t o s t o r e v a l u e s i n t o
rowcolmax = [ [ ] , [ ] ] # t h e maximum node s i z e i n e a c h row o r column
rowc olsum = [ [ ] , [ ] ] # t h e r u n n i n g sum o / t s i z e s o f e a c h row o r column
f o r dimen i n [ 0 , 1 ] : # do x t h e n y a x i s
f o r x i n x r a n g e ( gmax [ dimen ] + 1 ) :
rowcolmax [ dimen ] . append ( 0 )
rowcols um [ dimen ] . append ( 0 )
# d e t e r m i n e t h e maximum node s i z e i n e a c h row and column
f o r dimen i n [ 0 , 1 ] : # do x t h e n y a x i s
f o r node i n s e l f . n o d e L i s t :
n o d e a t = node . t p t [ dimen ]
rowcolmax [ dimen ] [ n o d e a t ] = (
178
10.4.2
Nodes.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# Nodes . py
#
#
Node c l a s s e s
# V i s u a l Pelog imports
from U t i l i m p o r t
from C o n s t a n t s i m p o r t
import CanvasItems , RuleEditor
179
# Tkinter imports
i m p o r t Canvas
# Standard l i b r a r y imports
i m p o r t math
##################################################
# VisualObject
#
# Any o b j e c t t h a t h a s a v i s u a l r e p r e s e n t a t i o n
# on t h e r u l e e d i t o r c a n v a s i n h e r i t s from V i s u a l O b j e c t
class VisualObject :
spt = (32, 32) # s i z e of object
#
# I n i t i a l i z a t i o n functions
init ( self ):
def
s e l f . pt = [ 0 , 0 ]
# Absolute coordinate of object
s e l f . npt = [ 0 , 0 ]
# New c o o r d i n a t e o f o b j e c t t o a n i m a t e t o
s e l f . s e l e c t e d = FALSE
s e l f . h i g h l i g h t e d = FALSE
def v i s u a l i z e ( s e l f ) :
self . visual = []
def d e v i s u a l i z e ( s e l f ) :
f o r item i n s e l f . v i s u a l :
item . d e l e t e ()
#
# User f u n c t i o n s
def update visual ( s e l f ) :
f o r element in s e l f . v i s u a l :
element . update ( )
def move visual ( s e l f ) :
f o r element in s e l f . v i s u a l :
element . m o v e t o f i n a l ()
def move by factor ( s e l f , factor ) :
f o r element in s e l f . v i s u a l :
element . move by factor ( f a c t o r )
##################################################
# GenericNode
#
# Any o b j e c t t h a t must be p o s i t i o n e d on t h e g r a p h
# u s i n g the graph l a y o u t a l g o r i t h m
c l a s s GenericNode ( V i s u a l O b j e c t ) :
180
#
# I n i t i a l i z a t i o n functions
EQUALITY TYPE = ""
COMPARISON TYPE = ""
n o d e t y p e = generic
a , b = None , None
def
init
( s e l f , m a s t e r , t e x t="-" , e q u a l = ( [ ] , [ ] ) ,
g r e a t e r = ( [ ] , [ ] ) , l e s s = ( [ ] , [ ] ) , near = [ ] ) :
VisualObject .
init ( self )
# R e f e r e n c e t o t h e r u l e e d i t o r c a n v a s t h a t owns t h i s node
s e l f . master = master
s e l f . text = text
s e l f . tpt = [0,0]
# l a b e l t o be drawn on node
# T o p o l o g i c a l c o o r d i n a t e o f node
# constraints
s e l f . equal = equal
self . less = less
self . greater = greater
s e l f . near = near
# t h e d i r e c t i o n i n w hi c h t h e l a s t r e s o l v e o p e r a t i o n
# bouncedo u t a n o t h e r node
self . last resolve = 2
# add t h i s node t o t h e r u l e e d i t o r c a n v a s
s e l f . master . add node ( s e l f )
# c r e a t e a l l t h e v i s u a l e l e m e n t s r e l a t e d t o t h i s node
s e l f . v i s u a l i z e ()
def delete ( s e l f ) :
# remove t h e v i s u a l e l e m e n t s from t h e c a n v a s
s e l f . d e v i s u a l i z e ()
# remove s e l f from node l i s t
s e l f . master . remove node ( s e l f )
# s t r i n g r e p r e s e n t a t i o n o f t h i s node . D i s p l a y e d i n t h e
# s t a t u s b a r when t h e node i s h i g h l i g h t e d
def
repr ( self ):
return s e l f . text
#
# Event h a n d l e r s
# When t h e b u t t o n i s d e p r e s s e d , s e l f i s t h e s t a r t i n g node
# of the drag o p e r a t i o n
def buttonpress ( s e l f , args ) :
181
s e l f . master . s e t s t a r t n o d e ( s e l f )
# When t h e b u t t o n i s r e l e a s e d , we must f i n d t h e node t h a t i t
# was r e l e a s e d on t o d e t e r m i n e t h e f i n i s h i n g node o f t h e d r a g
# operation
def buttonrelease ( s e l f , args ) :
s e l f . master . s e t f i n i s h n o d e ( s e l f . master . f i n d n o d e ( args . x , args . y ))
s e l f . master . r u n t o o l ()
s e l f . m a s t e r . s e t s t a r t n o d e ( None )
s e l f . m a s t e r . s e t f i n i s h n o d e ( None )
def h i g h l i g h t ( s e l f , args ) :
pass
def unhighlight ( s e l f , args ) :
pass
def s e l e c t ( s e l f ) :
pass
def unselect ( s e l f ) :
pass
#
# Graph L a y o u t A l g o r i t h m f u n c t i o n s
# r e t u r n s t h e a v e r a g e LOGICAL p o s i t i o n o f t h e n o d e s
def f i n d a v e r a g e ( s e l f , nodes ) :
i f nodes = = [ ] :
r e t u r n [ RE MAXGRID , RE MAXGRID ]
else :
sum = [ 0 , 0 ]
f o r dimen i n [ 0 , 1 ] : # x t h e n y a x i s
p r i n t nodes
f o r node i n n o d e s :
# b u i l d up sum o f a x i s c o o r d i n a t e s
p r i n t " Summing " , node
sum [ dimen ] = sum [ dimen ] + node . t p t [ dimen ]
# d i v i d e by t h e number o f n o d e s
# r e s u l t must be an i n t e g e r ( LOGICAL c o o r d i n a t e s a r e
# always i n t e g e r s )
sum [ dimen ] = i n t ( math . c e i l ( sum [ dimen ] / l e n ( n o d e s ) ) )
r e t u r n sum
# Moves t h e node t o t h e a v e r a g e LOGICAL p o s i t i o n o f t h e n o d e s
# s p e c i f i e d by t h e n e a r c o n s t r a i n t , i f any .
def go to near ( s e l f ) :
avg = s e l f . f i n d a v e r a g e ( s e l f . n e a r )
i f avg ! = [ RE MAXGRID , RE MAXGRID ] :
f o r dimen i n [ 0 , 1 ] :
s e l f . t p t [ dimen ] = avg [ dimen ]
d e b u g p r i n t ( s e l f , " going to average of :" ,
s e l f . n e a r , "=" , s e l f . t p t [ 0 ] , s e l f . t p t [ 1 ] )
182
# A s s i g n s t h i s node a l o g i c a l c o o r d i n a t e b a s e d on i t s r e l a t i o n s h i p
# t o o t h e r n o d e s s p e c i f i e d by t h e c o n s t r a i n t s g r e a t e r , l e s s and e q u a l .
# C o n s t r a i n t s a r e a l w a y s s a t i s f i e d by moving n o d e s o u t w a rd ( SE ) . T h i s
# makes t h e g r a p h s somewhat l e s s v i s u a ls p a c e e f f i c i e n t , b u t e l i m i n a t e s
# future conflicts
d e f t o p o l o g i c a l s o r t ( s e l f , dimen , tmin , tmax ) :
# E n s u r e t h i s node i n between tmin and tmax
s e l f . t p t [ dimen ] = min ( max ( s e l f . t p t [ dimen ] , tmin ) , tmax )
s e l f . s o l v e e q u a l c o n s t r a i n t ( dimen )
s e l f . s o l v e g r e a t e r c o n s t r a i n t ( dimen )
s e l f . s o l v e l e s s c o n s t r a i n t ( dimen )
d e b u g p r i n t ( s e l f . t e x t , " sorted to:" , s e l f . t p t [ 0 ] , s e l f . t p t [ 1 ] )
# T h i s node must hav e t h e same l o g i c a l c o o r d i n a t e a s e v e r y node l i s t e d i n i t s
# equal constraint .
d e f s o l v e e q u a l c o n s t r a i n t ( s e l f , dimen ) :
f o r e q u a l i n s e l f . e q u a l [ dimen ] : # For e a c h e q u a l c o n s t r a i n t
# I f t h i s node i s c u r r e n t l y n o t m e e t i n g i t s e q u a l c o n s t r a i n t . . .
i f e q u a l . t p t [ dimen ] ! = s e l f . t p t [ dimen ] :
d e b u g p r i n t ( " Equal constraint " )
# t h e n move t h e l e s s e r node (NW) o u tw a rd ( SE ) t o s a t i s f y t h e
# constraint
i f e q u a l . t p t [ dimen ] < s e l f . t p t [ dimen ] :
e q u a l . t o p o l o g i c a l s o r t ( dimen , s e l f . t p t [ dimen ] ,
s e l f . t p t [ dimen ] )
else :
s e l f . t o p o l o g i c a l s o r t ( dimen , e q u a l . t p t [ dimen ] ,
e q u a l . t p t [ dimen ] )
# T h i s node must hav e a g r e a t e r ( SE ) l o g i c a l c o o r d i n a t e t h a n a l l t h e n o d e s
# l i s t e d in i t s greater constraint
def s o l v e g r e a t e r c o n s t r a i n t ( s e l f , dimen ) :
f o r g r e a t e r i n s e l f . g r e a t e r [ dimen ] : # For e a c h g r e a t e r c o n s t r a i n t
# I f t h i s node i s n o t m e e t i n g i t s g r e a t e r c o n s t r a i n t . . .
i f s e l f . t p t [ dimen ] <= g r e a t e r . t p t [ dimen ] :
d e b u g p r i n t ( " Greater constraint " )
# move i t s o i t i s g r e a t e r
s e l f . t o p o l o g i c a l s o r t ( dimen , g r e a t e r . t p t [ dimen ] + 1 ,
RE MAXGRID)
# T h i s node must hav e a l e s s e r (NW) l o g i c a l c o o r d i n a t e t h a n a l l t h e n o d e s
# l i s t e d in i t s l e s s e r constraint
def s o l v e l e s s c o n s t r a i n t ( s e l f , dimen ) :
f o r l e s s i n s e l f . l e s s [ dimen ] : # For e a c h l e s s c o n s t r a i n t
# I f the c o n s t r a i n t i s not being s a t i s f i e d . . .
i f s e l f . t p t [ dimen ] > ] = l e s s . t p t [ dimen ] :
# . . . move t h e o t h e r node o ut ward s o i t i s g r e a t e r t h a n t h i s node
l e s s . t o p o l o g i c a l s o r t ( dimen , s e l f . t p t [ dimen ] + 1 ,
RE MAXGRID)
183
##################################################
# HightlightableNode
#
184
# s u p e r c l a s s o f n o d e s t h a t can be h i g h l i g h t e d
# ( become b o l d when t h e mouse h o v e r s )
class HighlightableNode :
def h i g h l i g h t ( s e l f , args ) :
s e l f . h i g h l i g h t e d = TRUE
s e l f . update visual ()
s e l f . master . d e s c r i b e n o d e ( s e l f )
s e l f . m a s t e r . s e t c u r s o r ( hand2 )
def unhighlight ( s e l f , args ) :
s e l f . h i g h l i g h t e d = FALSE
s e l f . update visual ()
s e l f . master . undescribe node ()
s e l f . master . r e s t o r e c u r s o r ()
##################################################
# SelectableNode
#
# s u p e r c l a s s o f n o d e s t h a t can be s e l e c t e d u s i n g
# the S e l e c t i o n t o o l .
c lass SelectableNode :
def s e l e c t ( s e l f ) :
s e l f . s e l e c t e d = TRUE
s e l f . update visual ()
s e l f . master . keyboard . s e t c a l l b a c k ( s e l f . k e y b o a r d c a l l b a c k )
s e l f . i n i t e d i t w i d g e t s ()
def unselect ( s e l f ) :
s e l f . s e l e c t e d = FALSE
s e l f . update visual ()
s e l f . m a s t e r . k e y b o a r d . s e t c a l l b a c k ( None )
s e l f . destroy edit widgets ()
def i n i t e d i t w i d g e t s ( s e l f ) :
pass
def destroy edit widgets ( s e l f ) :
pass
def keyboard callback ( s e l f , noteList ) :
pass
##################################################
# NoteNode
#
# The s t a t i c n o t e n o d e s i n t h e g r a p h
c l a s s NoteNode ( H i g h l i g h t a b l e N o d e , G e n e r i c N o d e ) :
EQUALITY TYPE = " pitch "
COMPARISON TYPE = " pitch "
n o d e t y p e = note
185
init
( s e l f , master , m e t a v a r i a b l e ,
prevX=None , prevY=None , box=FALSE ) :
# The name o f t h e m e t a v a r i a b l e t h i s node r e p r e s e n t s
s e l f . metavariable = metavariable
# I s t h i s t h e c u r r e n t node ?
s e l f . box = box
# The prevX
i f prevX ==
prevX =
else :
prevX =
and prevY a r g u m e n t s a r e c o n v e r t e d i n t o l e s s c o n s t r a i n t s
None :
[]
[ prevX ]
i f prevY == None :
prevY = [ ]
else :
prevY = [ prevY ]
GenericNode .
init
( s e l f , master ,
t e x t=m e t a v a r i a b l e ,
g r e a t e r =( prevX , prevY ) ,
e q u a l =( prevY , prevX ) )
def v i s u a l i z e ( s e l f ) :
i f s e l f . box :
self . visual = [
CanvasItems . SolidNoteBitmap ( s e l f ) ]
else :
self . visual = [
C a n v a s I t e m s . HollowNoteBitmap ( s e l f ) ]
##################################################
# BinaryRelation
#
# A g e n e r i c b i n a r y r e l a t i o n node from w h i c h o t h e r
# binary operators are derived
c l a s s B i n a r y R e l a t i o n ( HighlightableNode , SelectableNode , GenericNode ) :
bitmap = binary .xbm
# C r e a t e s a new B i n a r y R e l a t i o n between n o d e s a and b
def
i n i t ( s e l f , m a s t e r , a , b , t e x t="-" ) :
self .a, self .b = a, b
less = [[],[]]
greater = [ [ ] , [ ] ]
# R e l a t i o n s h i p s between two n o t e s i n t h e same row s h o u l d
# be c o n s t r a i n e d s o t h e y don t c r o s s t h e o t h e r row
i f a . tpt [1] == b . tpt [ 1 ] : # H o r i z o n t a l r e l a t i o n s h i p
i f a . t p t [ 1 ] = = m a s t e r . r u l e . Top row . t p t [ 1 ] :
186
l e s s = [ [ ] , [ m a s t e r . r u l e . Bottom row ] ]
else :
g r e a t e r = [ [ ] , [ m a s t e r . r u l e . Top row ] ]
# T h i s node s h o u l d be c o n s t r a i n e d between t h e o t h e r
# two n o d e s
f o r dimen i n [ 0 , 1 ] :
i f a . t p t [ dimen ] ! = b . t p t [ dimen ] :
i f a . t p t [ dimen ] < b . t p t [ dimen ] :
g r e a t e r [ dimen ] . append ( a )
l e s s [ dimen ] . append ( b )
else :
g r e a t e r [ dimen ] . append ( b )
l e s s [ dimen ] . append ( a )
GenericNode .
init
( s e l f , m a s t e r , t e x t=t e x t ,
g r e a t e r=g r e a t e r ,
l e s s=l e s s ,
n e a r =[ a , b ] )
# S e t t h e s t a r t i n g p o s i t i o n t o t h e a v e r a g e o f t h e n o d e s a and b
f o r dimen i n [ 0 , 1 ] :
s e l f . p t [ dimen ] = ( a . p t [ dimen ] + b . p t [ dimen ] ) / 2
def v i s u a l i z e ( s e l f ) :
self . visual = [
C a n v a s I t e m s . Edge ( s e l f , s e l f , s e l f . a ) ,
C a n v a s I t e m s . Edge ( s e l f , s e l f , s e l f . b ) ,
CanvasItems . NodeRectangle ( s e l f ) ,
CanvasItems . Label ( s e l f ) ,
C a n v a s I t e m s . NodeBitmap ( s e l f , s e l f . bitm ap ) ]
##################################################
# UnaryRelation
#
c l a s s UnaryRelation ( HighlightableNode , SelectableNode , GenericNode ) :
bitmap = unary .xbm
def
i n i t ( s e l f , m a s t e r , a , t e x t="-" ) :
self .a = a
# I f t h i s node i s c o n n e c t e d t o a n o t e n o d e i n t h e t o p row ,
# we don t want t h i s node t o go b e l o w t h e bottom row , and
# vice versa
i f a . t p t [ 1 ] = = m a s t e r . r u l e . Top row . t p t [ 1 ] :
l e s s = [ [ ] , [ m a s t e r . r u l e . Bottom row ] ]
else :
g r e a t e r = [ [ ] , [ m a s t e r . r u l e . Top row ] ]
GenericNode .
init
( s e l f , m a s t e r , t e x t=t e x t ,
10.4.3
CanvasItems.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# C a n v a s I t e m s . py
#
#
V a r i o u s g e o m e t r i c i t e m s t h a t a r e p l a c e d on t h e
#
Rule E d i t o r canvas
#
#
A l l o f t h e s e i t e m s a r e i n h e r i t e d from t h e i r c o r r e s p o n d i n g
#
T k i n t e r c l a s s e s , b u t have been e x t e n d e d t o automate
#
t h e i r r e l a t i o n s h i p to nodes i n the graph l a y o u t a l g o r i t h m
# V i s u a l Pelog imports
from U t i l i m p o r t
from C o n s t a n t s i m p o r t
# Tkinter imports
i m p o r t Canvas
from T k C o n s t a n t s i m p o r t
##################################################
# CanvasItem
#
# The b a s e c l a s s e s f o r a l l c l a s s e s i n t h i s module
c l a s s CanvasItem :
def
i n i t ( s e l f , node ) :
# The node t h a t t h i s c a n v a s i t e m b e l o n g s t o
s e l f . node = node
# Move t h e i t e m t o i t s c o r r e c t p o s i t i o n
s e l f . move to final ()
# Update any c o l o r o r a p p e a r a n c e a t t r i b u t e s
s e l f . update ( )
# Bind e v e n t s t o t h e c o r r e s p o n d i n g node
187
188
f i l l , text
##################################################
# Bitmap
#
c l a s s Bitmap ( C a n v a s I t e m , Canvas . Bitmap ) :
bitmap = " default .xbm"
def
i n i t ( s e l f , node ) :
Canvas . Bitmap .
init (
s e l f , node . m a s t e r ,
0, 0,
bitmap= @images /+ s e l f . bitmap )
CanvasItem .
i n i t ( s e l f , node )
def update ( s e l f ) :
x , y , z , color = s e l f . get colors ()
s e l f [ foreground ] = c o l o r
c l a s s HollowNoteBitmap ( Bitmap ) :
bitmap = " hollownote .xbm"
c l a s s S o l i d N o t e B i t m a p ( Bitmap ) :
bitmap = " solidnote .xbm"
c l a s s NodeBitmap ( Bitmap ) :
def
i n i t ( s e l f , node , bitmap ) :
s e l f . bitmap = bitmap
Bitmap .
i n i t ( s e l f , node )
##################################################
# NodeRectangle
#
c l a s s N o d e R e c t a n g l e ( C a n v a s I t e m , Canvas . R e c t a n g l e ) :
def
i n i t ( s e l f , node ) :
Canvas . R e c t a n g l e .
init (
s e l f , node . m a s t e r ,
0, 0, 0, 0)
CanvasItem .
i n i t ( s e l f , node )
d e f move ( s e l f , x , y ) :
c1x = x ( s e l f . node . s p t [ 0 ] / 2 )
c1y = y ( s e l f . node . s p t [ 1 ] / 2 )
c2x = c1x + s e l f . node . s p t [ 0 ]
c2y = c1y + s e l f . node . s p t [ 1 ]
s e l f . c o o r d s ( [ ( c1x , c1y ) , ( c2x , c 2y ) ] )
def update ( s e l f ) :
o u t l i n e , width , f i l l , x = s e l f . g e t c o l o r s ()
s e l f [ fill ] = f i l l
s e l f [ outline ] = o u t l i n e
189
190
s e l f [ width ] = w i d t h
##################################################
# Label
#
c l a s s L a b e l ( C a n v a s I t e m , Canvas . Canvas Text ) :
def
i n i t ( s e l f , node ) :
Canvas . CanvasTe xt .
init (
s e l f , node . m a s t e r ,
0, 0,
j u s t i f y = center ,
f i l l =RE COLOR TEXT)
CanvasItem .
i n i t ( s e l f , node )
d e f move ( s e l f , x , y ) :
s e l f . c o o r d s ( [ ( x , y + ( s e l f . node . s p t [ 1 ] / 2 ) 5 ) ] )
def update ( s e l f ) :
x , y , z , color = s e l f . get colors ()
s e l f . c o n f i g ( t e x t= s e l f . node . t e x t )
##################################################
# Edge
#
c l a s s Edge ( C a n v a s I t e m , Canvas . L i n e ) :
i n i t ( s e l f , node , a , b ) :
def
Canvas . L i n e .
init (
s e l f , node . m a s t e r ,
0, 0, 0, 0)
s e l f . lower ()
self .a = a
self .b = b
CanvasItem .
i n i t ( s e l f , node )
def move to final ( s e l f ) :
x1 , y1 = t u p l e ( s e l f . a . p t )
x2 , y2 = t u p l e ( s e l f . b . p t )
s e l f . move ( x1 , y1 , x2 , y2 )
def move by factor ( s e l f ,
s e l f . move (
( s e l f . a . npt [ 0 ]
( s e l f . a . npt [ 1 ]
( s e l f . b . npt [ 0 ]
( s e l f . b . npt [ 1 ]
factor ):
self
self
self
self
. a . pt
. a . pt
. b . pt
. b . pt
[0])
[1])
[0])
[1])
factor
factor
factor
factor
d e f move ( s e l f , x1 , y1 , x2 , y2 ) :
s e l f . c o o r d s ( [ ( x1 , y1 ) , ( x2 , y2 ) ] )
def update ( s e l f ) :
o u t l i n e , width ,
f i l l , x = s e l f . get colors ()
+
+
+
+
self
self
self
self
. a . pt [ 0 ] ,
. a . pt [ 1 ] ,
. b . pt [ 0 ] ,
. b . pt [ 1 ] )
191
11
A collection of custom
Megawidgets implemented in
Python/Tkinter
One of the unexpected bottlenecks in the Visual Pelog project was the number of
custom megawidgets1 that were required. While pure Tcl/Tk is now quite mature
and has a number of ready-made megawidgets bundled with the distribution and
available on the internet, Python/Tkinter remains somewhat lacking in this respect.
The most promising attempt to remedy this situation is Pmw (Python Megawidgets).2 While this project aims to fill some holes, it does not yet offer many of the
standard types of controls one expects from a fully mature GUI toolkit.
The megawidgets that were implemented specifically for this project were:
Tree widget: a heirarchical tree display similar to that found in Microsoft
Windows Explorer.
Help browser: an on-line help browser that supports a very tiny subset of
HTML.
On-screen musical keyboard: a standard Western musical keyboard that can
be used to display and enter pitch-related information.
1
Megawidgets are user-interface elements implemented in the very high-level language itself.
They are made up of the basic widgets that come standard with of the GUI system.
2
Python Megawidgets http://www.dscpl.com.au/pmw/
192
11.1
193
Tree Widget3
The tree widget as implemented here is quite general and is well-suited to extention
using object-oriented inheritance.
11.1.1
A node that contains data. (In the case of Visual Pelog, it contains a rule.)
When the node is double-clicked on or the Enter / Return key is pressed, the
data in the node is launched or opened for editing.
Branch:
A branch node contains other nodes. It can either be open (displaying all its children nodes) or closed. Clicking on the open/close box toggles
the open/close state of the branch.
Nodes are selected by clicking on them. A selected node appears inside a box (Figure
11.2. The selected node can be dragged to other locations in the heirarchy, or it
can be deleted by pressing the Delete key.
3
194
11.1.2
Developer usage
It is important to note the distinction between the class that creates a Tkinter
widget, TreeWidget, and the class that handles the data in the widget, TreeNode.
TreeWidget class
The TreeWidget class inherits from the Tkinter.Canvas widget. To create a new
TreeWidget, simply use the default constructor:
newTree = Tree . TreeWidget ( m a s t e r )
Then, nodes can be arbitrarily added or deleted to the heirarchy using the following
functions. All changes made to the TreeNode structure are automatically reflected
in the TreeWidget. Note that TreeNode is completely polymorphic: leaf nodes can
contain any type of data. The text displayed to the user on the TreeWidget is its
text representation (i.e. returned by the built-in repr() function).
node.append(data)
Adds a leaf containing data to the list nodes
children.
node a.append node(node b) Adds the arbitrary sub-tree node b to the list
of node as children.
node.remove(data)
Removes a node containing data from the list
of nodes children.
node a.remove node(node b) Removes the arbitrary sub-tree node b from
the list of node as children.
When the Enter key is pressed, a leaf node is launched. Launching could entail
opening the data, editing the data or performing some other action. In order to
support this functionality, the data type stored at a leaf must contain a launch()
function. If the data type does not contain a launch() function, the TreeWidget
gracefully does nothing.
11.1.3
Code
Tree.py
195
196
c l a s s TreeWidget ( T k i n t e r . T o p l e v e l ) :
#
# I n i t i a l i z a t i o n routines
def
i n i t ( s e l f , m a s t e r=None ) :
Tkinter . Toplevel .
i n i t ( s e l f , master )
s e l f . make widgets ()
s e l f . load images ()
s e l f . b i n d ( s e q u e n c e=< KeyPress > ,
f u n c= s e l f . k e y p r e s s )
# The t r e e t h a t c o n t a i n s t h e d a t a t o be d i s p l a y e d
s e l f . t r e e = TreeNode ( p a r e n t= s e l f , d a t a="Top" )
# Have t h e t r e e b u i l d i c o n s i n t h i s c a n v a s
self . tree . set parent ( self )
s e l f . s e l e c t e d = None
# d i s p l a y the t r e e
s e l f . build ()
def make widgets ( s e l f ) :
# The w i d g e t a s a w h o l e c o n s i s t s o f a c a n v a s and a s c r o l l b a r
s e l f . c a n v a s = T k i n t e r . Canvas (
s e l f , b a c k g r o u n d= white )
s e l f . v s c r o l l = Tkinter . Scrollbar (
s e l f , o r i e n t= vertical ,
command= s e l f . c a n v a s . y v i e w )
s e l f . c a n v a s [ yscrollcommand ] = s e l f . s c r o l l Y
s e l f . c a n v a s . g r i d ( row = 0 , column = 0 , s t i c k y=nesw )
s e l f . v s c r o l l . g r i d ( row = 0 , column = 1 , s t i c k y=ns )
s e l f . g r i d c o l u m n c o n f i g u r e ( 0 , w e i g h t =1)
s e l f . c a n v a s . b i n d ( s e q u e n c e="< ButtonRelease >" ,
f u n c= s e l f . r e l e a s e )
s e l f . c a n v a s . b i n d ( s e q u e n c e="< Motion >" ,
f u n c= s e l f . motion )
# T h i s l o a d s t h e r e q u i r e d i m a g e s from d i s k
def load images ( s e l f ) :
s e l f .OPENBM = T k i n t e r . Image (
bitmap , d a t a=OPENBM DATA , maskdata=OPENBM MASKDATA,
f o r e g r o u n d= black , b a c k g r o u n d= white )
s e l f . CLOSEDBM = T k i n t e r . Image (
bitmap , d a t a=CLOSEDBM DATA , maskdata=OPENBM MASKDATA,
f o r e g r o u n d= black , b a c k g r o u n d= white )
s e l f . FOLDEROPEN = T k i n t e r . Image (
photo , f o r m a t=GIF , f i l e = images / folderopen .gif )
s e l f . FOLDERCLOSED = T k i n t e r . Image (
197
198
repr ( self ):
r e t u r n s e l f . data
#
# Event h a n d l e r s
# Opens o r c l o s e s a b r a n c h
def open close ( s e l f , args ) :
i f s e l f . open :
s e l f . open = FALSE
else :
s e l f . open = TRUE
s e l f . build ()
def press ( s e l f , args ) :
g l o b a l DRAGGING
self . set selection ( self )
s e l f . o f f s e t = (3, args . y s e l f . loc [ 1 ] )
DRAGGING = TRUE
def enter ( s e l f , args ) :
g l o b a l OVER
199
200
OVER = s e l f
s e l f . t e x t [ fill ] = blue
def leave ( s e l f , args ) :
g l o b a l OVER
OVER = None
s e l f . t e x t [ fill ] = black
def launch ( s e l f , args ) :
i f s e l f . data :
i f launch i n s e l f . d a t a .
s e l f . data . launch ()
class
dict
. keys ( ) :
def i n s e r t ( s e l f ) :
pass
#
# Drawing methods
# B u i l d s t h e e n t i r e t r e e by t u n n e l i n gup t o t h e r o o t
def build ( s e l f ) :
for parent in s e l f . parent :
i f parent :
parent . build ()
# I f a node s p e c i f i c i c o n h a s n o t been s e t , u s e one o f t h e
# standard ones
def get icon ( s e l f ) :
i f s e l f . icon :
return s e l f . icon
else :
i f s e l f . children == []:
r e t u r n s e l f . LEAF
else :
i f s e l f . open :
r e t u r n s e l f . FOLDEROPEN
else :
r e t u r n s e l f . FOLDERCLOSED
# B u i l d s t h i s node a t l o c a t i o n ( x , y )
d e f b u i l d l a y e r ( s e l f , c a n v a s , s e l e c t e d , x , y , p r e v y=None ) :
i f not prevy :
prevy = y
# Draw t h e h o r i z o n t a l l i n e
Canvas . L i n e ( c a n v a s , x + LINE , y , x + ICON , y ,
# Draw t h e i c o n
icon = s e l f . get icon ()
s e l f . bitmap = Canvas . I m a g e I t e m (
c a n v a s , x + ICON , y , image=i c o n )
f i l l = gray50 )
201
# Draw t h e t e x t
s e l f . t e x t = Canvas . Canvas Text (
c a n v a s , x + TEXT , y , a n c h o r=w )
# Draw t h e s e l e c t i o n box , i f f t h i s node i s t h e s e l e c t e d node
i f s e l f == s e l e c t e d :
box = s e l f . t e x t . bbox ( )
s e l f . r e c t a n g l e = Canvas . R e c t a n g l e (
c a n v a s , box , f i l l =COLOR REF HIGHLIGHT ,
o u t l i n e=COLOR REF HIGHLIGHT )
s e l f . rectangle . lower ()
else :
s e l f . r e c t a n g l e = None
# Update t h e t e x t and t h e s i z e o f t h e s e l e c t i o n box
s e l f . update text ()
# Save t h e p h y s i c a l l o c a t i o n o f t h i s node s o t h a t e v e n t s can
# d e t e r m i n e wh i c h node was d r o p p e d on .
s e l f . loc = (x , y)
# A s s i g n e v e n t s t o t h e v i s u a l node i t e m s
f o r w i d g e t i n s e l f . bitm ap , s e l f . t e x t :
w i d g e t . b i n d ( s e q u e n c e="< ButtonPress >" ,
command= s e l f . p r e s s )
w i d g e t . b i n d ( s e q u e n c e="< Double - ButtonPress >" ,
command= s e l f . l a u n c h )
w i d g e t . b i n d ( s e q u e n c e="< Enter >" ,
command= s e l f . e n t e r )
w i d g e t . b i n d ( s e q u e n c e="< Leave >" ,
command= s e l f . l e a v e )
# Draw t h i s node s c h i l d r e n , i f t h e b r a n c h i s open
n e x t y = y + i c o n . h e i g h t ( ) + Y STEP
if self . children != []:
i f s e l f . open :
open = Canvas . I m a g e I t e m (
c a n v a s , x + OPEN BOX , y , image= s e l f .OPENBM)
py = y
for child in s e l f . children :
newy = c h i l d . b u i l d l a y e r ( c a n v a s , s e l e c t e d ,
x + X STEP , n e x t y , p r e v y=py )
py , n e x t y = n e x t y , newy
else :
open = Canvas . I m a g e I t e m (
c a n v a s , x + OPEN BOX , y , image= s e l f . CLOSEDBM)
open . t k r a i s e ( )
open . b i n d ( s e q u e n c e="< ButtonPress >" ,
command= s e l f . o p e n c l o s e )
202
# Draw t h e v e r t i c a l l i n e
Canvas . L i n e (
c a n v a s , x + LINE , y ,
x + LINE , p r e v y , f i l l = gray50 ) . l o w e r ( )
return nexty
# U p da t e s t h e t e x t t h a t i s d i s p l a y e d w i t h t h e node
# I f the r e p r ( data ) i s g r e a t e r than 3 0 c h a r a c t e r s , the t e x t
# i s truncated .
def update text ( s e l f ) :
i f s e l f . text :
x = remove cr ( repr ( s e l f ))
i f len (x ) > 40:
x = x [ 0 : 3 9 ] + "..."
s e l f . t e x t [ text ] = x
i f self . rectangle :
box = s e l f . t e x t . bbox ( )
s e l f . r e c t a n g l e . c o o r d s ( box )
s e l f . rectangle . lower ()
# Moves t h e node t o a new l o c a t i o n . Used when d r a g g i n g
d e f move ( s e l f , x , y ) :
s e l f . bitmap . t k r a i s e ( )
s e l f . text . t k r a i s e ()
s e l f . bitmap . c o o r d s ( [ ( x + ICON s e l f . o f f s e t [ 0 ] ,
y self . offset [1])])
s e l f . t e x t . c o o r d s ( [ ( x + TEXT s e l f . o f f s e t [ 0 ] ,
y self . offset [1])])
# R e s t o r e node t o i t s p r o p e r p o s i t i o n . Used when a d r a g g i n g
# operation is cancelled .
def restore pos ( s e l f ) :
s e l f . offset = (0, 0)
s e l f . move ( s e l f . l o c [ 0 ] , s e l f . l o c [ 1 ] )
# S e t s t h i s node a s t h e s e l e c t e d node
d e f s e t s e l e c t i o n ( s e l f , node ) :
for parent in s e l f . parent :
i f parent :
p a r e n t . s e t s e l e c t i o n ( node )
##################################################
# TESTING CODE
if
203
f o r x in xrange ( 1 , 2 0 ) :
r = R u l e . RuleNode ( p a r e n t=n )
n . append node ( r )
w. mainloop ()
11.2
11.2.1
The Help widget is a very basic on-line help browser (Figure 11.3) similar in
functionality to a web browser (eg. Mosaic, Netscape Communicator, Microsoft
Internet Explorer.)
Help documents are displayed using a mixture of typefaces. Links to other help
documents are displayed in a box. Clicking on this box opens the other document.
Clicking on the Back button returns to the previously viewed document.
11.2.2
Developer usage
This help browser implementation is quite low-featured, but is quite fast with a
small memory footprint. An alternative solution may have also been to provide a
platform-independant way of opening the help docuemnts in a standard web-browser
as a sub-process of the application.
The help browser manages a global database of help documents stored in memory
as strings. There can only be one database per instance of an application and each
document in the database must have a unique string identifier. Documents can be
204
added to the database (usually at application start-up) using the add function:
Help . add ( s u b j e c t , document )
where subject is the unique document identifier and document is a string containing
the body of the document.
Documents are displayed using the show function:
Help . show ( s u b j e c t )
where subject is a document identifier already in the database. Only one help
browser window will be created per application. Therefore, calling show twice will
change the subject of the already existing help browser, and not open a new help
browser all together.
The document strings themselves are formatted using a very tiny subset of
HTML. The supported tags are:
<b>
bold text
<i>
italic text
<bi>
bold and italic text
<tt>
monospaced text
<title>
title-sized text
<a href=subject> reference to another help document
11.2.3
Help.py
Code
# Help B r o w s e r F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# Help . py
#
#
T h i s module p r o v i d e s a v e r y b a s i c Help b r o w s e r .
# App s p e c i f i c i m p o r t s
from U t i l i m p o r t
from C o n s t a n t s i m p o r t
# Python i m p o r t s
import s g m l l i b
# Tkinter imports
import Tkinter
from T k C o n s t a n t s i m p o r t
# To s t o r e a r e f e r e n c e t o a h e l p window , i f any .
helpWindow = None
# Help On Help D e f a u l t page
documents = {
Default :
( "< title >Help Browser </ title >\n"
"<b>(c ) 1999 Michael Droettboom </b>\n"
205
def
206
make widgets ( s e l f ) :
# A b a s e d t e x t and v e r t i c a l s c r o l l b a r c o m b i n a t i o n
s e l f . s c r o l l = Tkinter . Scrollbar (
s e l f , o r i e n t= vertical )
s e l f . t e x t = TkinterHTML ( s e l f , s e l f . s c r o l l )
s e l f . s c r o l l . c o n f i g u r e ( command= s e l f . t e x t . y v i e w )
# A t o o l b a r , c o n t a i n i n g o n l y t h e b u t t o n Back
f r a m e = T k i n t e r . Frame ( s e l f )
s e l f . back = T k i n t e r . Button (
f r a m e , t e x t="Back" ,
command= s e l f . t e x t . back )
s e l f . back . pack ( s i d e=left )
f r a m e . pack ( s i d e=top , expand=yes , f i l l =x )
s e l f . s c r o l l . pack ( s i d e=right , f i l l =y )
s e l f . t e x t . pack ( expand=yes , f i l l =both )
def s e t s u b j e c t ( s e l f , subject ) :
s e l f . t e x t . d i s p l a y t e x t ( s u b j e c t , documents [ s u b j e c t ] )
def s e t t i t l e ( s e l f , subject ) :
s e l f . t i t l e (APP NAME + " Help on " + s u b j e c t )
##################################################
# TkinterHTML
#
# D i s p l a y s f o r m a t t e d t e x t u s i n g a t i n y s u b s e t o f HTML
# The s u p p o r t e d t a g s a r e :
#
<b > : b o l d t e x t
#
<i > : i t a l i c t e x t
#
<b i > : b o l d and i t a l i c t e x t
#
<t t > : monospaced ( t y p e w r i t e r ) t e x t
#
< t i t l e > : t i t l e s i z e d t e x t
#
<a h r e f= s u b j e c t > : r e f e r e n c e t o a n o t h e r s u b j e c t page
#
c l a s s TkinterHTML ( T k i n t e r . Text , s g m l l i b . SGMLParser , C u r s o r M a n a g e r ) :
i n i t ( s e l f , parent , s c r o l l ) :
def
T k i n t e r . Text .
init (
s e l f , parent ,
y s c r o l l c o m m a n d= s c r o l l . s e t ,
s e t g r i d=TRUE ,
w i d t h = 7 0 , h e i g h t = 3 2 , wrap=word )
CursorManager .
init ( self )
s e l f . parent = parent
# Store s a h i s t o r y of v i s i t e d pages .
# Makes t h e Back b u t t o n p o s s i b l e
s e lf . historyStack = []
207
s e l f . reset ()
s e l f . s e t d i s p l a y t a g s ()
# S e t s up t a g s i n Tk w i t h v a r i o u s f o r m a t t i n g p r o p e r t i e s
def
set display tags ( self ):
s e l f . t a g c o n f i g ( default , f o n t=" Helvetica 10" ,
s p a c i n g 1 = 3 , l m a r g i n 1 = 2 , l m a r g i n 2 =2,
r m a r g i n =2)
s e l f . t a g c o n f i g ( title , f o n t=" Helvetica 16 bold" )
s e l f . t a g c o n f i g ( b , f o n t=" Helvetica 10 bold" )
s e l f . t a g c o n f i g ( i , f o n t=" Helvetica 10 italic " )
s e l f . t a g c o n f i g ( bi , f o n t=" Helvetica 10 bold italic " )
s e l f . t a g c o n f i g ( tt , f o n t=" Courier 10" , l m a r g i n 2 =2)
s e l f . t a g c o n f i g ( a , f o n t=" Helvetica 10 bold" ,
b a c k g r o u n d=COLOR REFERENCE)
s e l f . k n o w n t a g s = [ default , title , b , i , bi , tt , a ]
def
def
208
# r e f e r e n c e by c r e a t i n g lambda f u n c t i o n s t h a t w i l l
# c a l l the handler f u n c t i o n s
f o r b i n d i n g i n make :
s e l f . tag bind (
ref , binding [ 1 ] ,
e v a l ( lambda attr : attr. widget . +
b i n d i n g [ 0 ] + (" + r e f + ") ) )
break
# C a l l e d by SGMLParser when i t e n c o u n t e r s an end t a g ( eg . < / b>)
d e f unknown endtag ( s e l f , t a g ) :
i f tag i n s e l f . known tags :
s e l f . remove tag ( tag )
i f t a g == "a" :
s e l f . remove tag ( s e l f . reference )
def s e t s u b j e c t ( s e l f , subject ) :
s e l f . parent . s e t s u b j e c t ( subject )
# E x t e r n a l : Loads t e x t i n t o t h e w i d g e t
def d i s p l a y t e x t ( s e l f , subject , text ) :
# Add t h e new s u b j e c t t o t h e h i s t o r y s t a c k
s e lf . historyStack = [ subject ] + s e l f . historyStack
# c l e a r t h e t a g s t a c k ( we want t o d e f a u l t t o t h e
# default text style )
s e l f . t a g S t a c k = [ default ]
# Change t h e t i t l e o f t h e p a r e n t window t o r e f l e c t
# t h e new s u b j e c t
i f s e l f . parent . s e t t i t l e :
s e l f . parent . s e t t i t l e ( subject )
# Allow changes to the textbox
s e l f . c o n f i g u r e ( s t a t e= normal )
# Delete a l l t e xt in the textbox
s e l f . d e l e t e ( 1.0 , end )
# P a r s e t h e HTML
s e l f . feed ( text )
s e l f . close ()
# Disallow a l l changes to the textbox
s e l f . c o n f i g u r e ( s t a t e= disabled )
# Change t h e s t a t e o f t h e back b u t t o n t o r e f l e c t
# the s i z e of the h i s t o r y stack
s e l f . update back button ()
209
# Go back t o t h e p r e v i o u s s u b j e c t i n t h e h i s t o r y s t a c k
d e f back ( s e l f ) :
i f len ( s e l f . historyStack ) > 1:
newSubject = s e l f . h i s t o r y S t a c k [ 1 ]
s e lf . historyStack = s e lf . historyStack [ 2 : ]
s e l f . s e t s u b j e c t ( newSubject )
s e l f . update back button
# Change t h e s t a t e o f t h e back b u t t o n b a s e d on t h e e m p t i n e s s
# of the h i s t o r y stack
def
update back button ( s e l f ):
i f len ( s e l f . historyStack ) > 1:
s e l f . p a r e n t . back . c o n f i g u r e ( s t a t e= normal )
else :
s e l f . p a r e n t . back . c o n f i g u r e ( s t a t e= disabled )
# H i g h l i g h t t h e r e f e r e n c e when t h e mouse c u r s o r e n t e r s
def h i g h l i g h t r e f ( s e l f , tag ) :
s e l f . s e t c u r s o r ( hand2 )
s e l f . t a g c o n f i g ( t a g , b a c k g r o u n d=COLOR REF HIGHLIGHT )
# U n h i g h l i g h t t h e r e f e r e n c e when t h e mouse c u r s o r l e a v e s
def u n h i g h l i g h t r e f ( s e l f , tag ) :
s e l f . restore cursor ()
s e l f . t a g c o n f i g ( t a g , b a c k g r o u n d=COLOR REFERENCE)
############################################################
# SELF TEST : D i s p l a y t h e d e f a u l t Help s u b j e c t .
if
n a m e == " __main__ " :
show ( " Default " )
11.3
11.3.1
210
The on-screen musical keyboard widget (Figures 11.4 through 11.8) allows the
user to enter pitch-related information in a manner that is intuitive to most users
with a background in Western art music.
The user is presented with a standard musical keyboard. As you slide the mouse
cursor over the keyboard, the pitch name of the key currently being pointed at is
displayed on the left-hand side of the keyboard. The scale of the keyboard can be
increased or decreased using the zoom-in ( ) and zoom-out ( ) buttons (Figure
11.8).
By clicking or dragging on the keyboard, the user can select pitches. How pitches
are selected is determined by the current mode of the keyboard:
SINGLE: (Figure 11.4) Clicking on any key selects that key. Only one key can be
selected at a time.
MULTI: (Figure 11.5) Click a key to select it. Click a selected key to unselect it.
An arbitrary number of keys may be selected at any given time.
RANGE: (Figure 11.6) Click on a starting key and drag to an ending key to select
a contiguous range of keys. Only one range may be selected at any time.
11.3.2
Developer usage
The Keyboard class inherits from the Tkinter Frame class and can be used as such.
To create a new Keyboard instance, use the default constructor:
kb = M u s i c W i d g e t s . Keyboard ( p a r e n t , mode )
211
where parent is the widget that owns the keyboard widget and mode is one of the
constants KB SINGLE, KB MULTI or KB RANGE corresponding to one of the input
modes described above.
To retrieve information from the Keyboard widget, assign a callback function
using:
kb . s e t c a l l b a c k ( f u n c t i o n )
This callback function is called everytime there is a change made to the set of
selected keys. This function must have one argument. Depending on the mode, the
argument will contain:
KB SINGLE: The pitch name of the selected key as a string.
KB MULTI: A list of the pitch names of all the selected keys, each given as a string.
KB RANGE: A list of two pitches. The first element is the highest pitch in the
range, the second element is the lowest pitch in the range.
The widget as a whole can be enabled and disabled (Figure 11.7) like any other
Tkinter widget using:
kb [ state ] = disabled
bk [ state ] = normal
11.3.3
Future plans
The on-screen musical keyboard can be used as the basis for a number of generalpurpose widgets for musical applications. I plan to develop a MIDI library for
Python (see Introduction on page ii) which will allow the Keyboard widget to include:
Selected notes and ranges using an attached MIDI keyboard as well as with
the mouse on-screen.
Visual playback of MIDI sequences by flashing the keys on the on-screen keyboard.
11.3.4
MusicWidgets.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# M u s i c W i d g e t s . py
#
#
A c o l l e c t i o n o f T k i n t e r MegaWidgets r e l a t e d t o m u s i c
# V i s u a l Pelog imports
from U t i l i m p o r t
from C o n s t a n t s i m p o r t
212
# Tkinter imports
from T k C o n s t a n t s i m p o r t
import Tkinter
i m p o r t Canvas
# Constants
WKEY WIDTH = 1 0 # w i d t h o f w h i t e k e y s
WKEY HEIGHT = 4 5 # h e i g h t o f w h i t e k e y s
BKEY WIDTH = 6 # w i d t h o f b l a c k k e y s
BKEY HEIGHT = 3 0 # h e i g h t o f b l a c k k e y s
BKEY HALF = BKEY WIDTH / 2 # h a l f o f t h e w i d t h o f b l a c k k e y s
ZOOM FACTOR = 1 . 2 5 # The f a c t o r t o zoom/unzoom by
############################################################
# Keyboard Widget
#
#
The k e y b o a r d w i d g e t c r e a t e s an ons c r e e n m u s i c a l
#
k e y b o a r d . N ot e s on t h e k e y b o a r d can be s e l e c t e d i n
#
one o f t h e f o l l o w i n g modes :
#
KB SINGLE : One n o t e a t a t i m e , by c l i c k i n g on t h e
#
key
#
KB MULTI : M u l t i p l e n o t e s , by t o g g l i n g k e y s on / o f f
#
KB RANGE : A c o n t i g u o u s r a n g e o f n o t e s by c l i c k i n g
#
and d r a g g i n g on t h e k e y b o a r d .
#
ZOOMIN DATA = "" "# define solid_width 13
#d e f i n e s o l i d h e i g h t 1 3
s t a t i c char s o l i d b i t s [] = {
0 x30 , 0 x00 , 0 x c c , 0 x00 , 0 x32 , 0 x01 , 0 x32 , 0 x01 , 0 x f d , 0 x02 , 0 x f d , 0 x02 ,
0 x32 , 0 x01 , 0 x32 , 0 x01 , 0 x c c , 0 x02 , 0 x30 , 0 x05 , 0 x00 , 0 x0a , 0 x00 , 0 x14 ,
0 x00 , 0 x08 } ;
"""
ZOOMOUT DATA = "" "# define solid_width 13
#d e f i n e s o l i d h e i g h t 1 3
s t a t i c char s o l i d b i t s [] = {
0 x30 , 0 x00 , 0 x c c , 0 x00 , 0 x02 , 0 x01 , 0 x02 , 0 x01 , 0 x79 , 0 x02 , 0 x79 , 0 x02 ,
0 x02 , 0 x01 , 0 x02 , 0 x01 , 0 x c c , 0 x02 , 0 x30 , 0 x05 , 0 x00 , 0 x0a , 0 x00 , 0 x14 ,
0 x00 , 0 x08 } ;
"""
c l a s s Keyboard ( T k i n t e r . Frame ) :
#
# I n i t i a l i z a t i o n routines
def
i n i t ( s e l f , p a r e n t=None , mode=KB SINGLE ) :
s e l f . mode = mode
T k i n t e r . Frame .
init (
s e l f , p a r e n t , h e i g h t =50,
r e l i e f = sunken )
make widgets ( s e l f ) :
# C r e a t e t h e two s t a t u s l a b e l s on t h e l e f t hand s i d e
f r a m e 1 = T k i n t e r . Frame ( s e l f )
s e l f . mousestatus = Tkinter . Label (
m a s t e r=f r a m e 1 , r e l i e f = sunken ,
w i d t h =4)
s e l f . widgetstatus = Tkinter . Label (
m a s t e r=f r a m e 1 , r e l i e f = sunken )
s e l f . m o u s e s t a t u s . g r i d ( row = 0 , column = 0 , s t i c k y=nesw )
s e l f . w i d g e t s t a t u s . g r i d ( row = 1 , column = 0 , s t i c k y=nesw )
# C r e a t e t h e zoomi n / zoomo u t b u t t o n s
f r a m e 2 = T k i n t e r . Frame ( f r a m e 1 )
s e l f . zoomout image = T k i n t e r . Image (
bitmap , d a t a=ZOOMOUT DATA , maskdata=ZOOMOUT DATA,
f o r e g r o u n d=black , b a c k g r o u n d= white )
s e l f . zoomout = T k i n t e r . Button (
f r a m e 2 , command= s e l f . zoom out ,
image= s e l f . zoomout image )
s e l f . z o o m i n i m a g e = T k i n t e r . Image (
bitmap , d a t a=ZOOMIN DATA , maskdata=ZOOMIN DATA ,
f o r e g r o u n d=black , b a c k g r o u n d= white )
s e l f . zoomin = T k i n t e r . Button (
f r a m e 2 , command= s e l f . z o o m i n ,
image= s e l f . z o o m i n i m a g e )
s e l f . zoomout . g r i d ( row = 0 , column = 0 , s t i c k y=nesw )
s e l f . zoomin . g r i d ( row = 0 , column = 1 , s t i c k y=nesw )
f r a m e 2 . g r i d ( row = 2 , column = 0 , s t i c k=nesw )
f r a m e 1 . g r i d r o w c o n f i g u r e ( 0 , w e i g h t =1)
f r a m e 1 . g r i d r o w c o n f i g u r e ( 1 , w e i g h t =1)
# C r e a t e t h e k e y b o a r d c a n v a s i t s e l f and t h e s c r o l l b a r
f r a m e 3 = T k i n t e r . Frame ( s e l f )
s e l f . c a n v a s = Ke yboardCanvas (
f r a m e 3 , mode= s e l f . mode ,
m o u s e s t a t u s= s e l f . m o u s e s t a t u s ,
w i d g e t s t a t u s= s e l f . w i d g e t s t a t u s )
s e l f . h s c r o l l = Tkinter . Scrollbar (
f r a m e 3 , o r i e n t= horizontal ,
command= s e l f . c a n v a s . x v i e w )
s e l f . c a n v a s [ xscrollcommand ] = s e l f . s c r o l l X
s e l f . h s c r o l l . g r i d ( row = 1 , column = 0 , s t i c k y=ew )
s e l f . c a n v a s . g r i d ( row = 0 , column = 0 , s t i c k y=ew )
f r a m e 3 . g r i d c o l u m n c o n f i g u r e ( 0 , w e i g h t =1)
f r a m e 1 . g r i d ( row = 0 , column = 0 , s t i c k y=ns )
213
214
215
s e t i t e m ( s e l f , key , v a l u e ) :
i f k e y == state :
for k in s e l f . keyList :
k . unmark ( )
k [ state ] = v a l u e
else :
T k i n t e r . Canvas . s e t i t e m ( s e l f , k e y , v a l u e )
# Key h e l p e r c l a s s
216
217
c l a s s Key ( Canvas . R e c t a n g l e ) :
#
# I n i t i a l i z a t i o n routines
def
i n i t ( s e l f , p a r e n t , x , name ) :
s e l f . parent = parent
s e l f . en = TRUE
s e l f . build item ( parent , x )
s e l f . b i n d ( s e q u e n c e=< Enter > , command= s e l f . e n t e r )
s e l f . b i n d ( s e q u e n c e=< Leave > , command= s e l f . l e a v e )
s e l f . b i n d ( s e q u e n c e=< ButtonPress > , command= s e l f . c l i c k )
s e l f . b i n d ( s e q u e n c e=< ButtonRelease > , command= s e l f . r e l e a s e )
s e l f . b i n d ( s e q u e n c e=< Motion > , command= s e l f . motion )
s e l f . marked = FALSE
s e l f . name = name
#
# Event h a n d l e r s
def enter ( s e l f , args ) :
i f s e l f . en :
s e l f [ fill ] = COLOR REFERENCE
def leave ( s e l f , args ) :
i f s e l f . en :
i f s e l f . marked :
s e l f [ fill ] = COLOR REF HIGHLIGHT
else :
s e l f [ fill ] = white
def c l i c k ( s e l f , args ) :
i f s e l f . en :
s e l f . parent . clicked on ( s e l f )
def r e l e a s e ( s e l f , args ) :
i f s e l f . en :
s e l f . parent . released on ( s e l f . parent . find key ( args . x , args . y ))
d e f motion ( s e l f , a r g s ) :
i f s e l f . en :
s e l f . parent . motion on ( s e l f . parent . f i n d k e y ( args . x , args . y ))
#
# Settings
def s e t s t a t e ( s e l f , value ) :
i f v a l u e == normal :
i f s e l f . name == "c~0" :
s e l f [ stipple ] = gray25
else :
s e l f [ stipple ] =
s e l f . en = TRUE
e l i f v a l u e == disabled :
s e l f [ stipple ] = error
218
s e l f . en = FALSE
def
repr ( self ):
r e t u r n s e l f . name
def
s e t i t e m ( s e l f , key , v a l u e ) :
i f k e y == state :
s e l f . set state ( value )
else :
Canvas . R e c t a n g l e . s e t i t e m
( s e l f , key , v a l u e )
d e f mark ( s e l f ) :
s e l f . marked = TRUE
s e l f . leave ( [ ] )
d e f unmark ( s e l f ) :
s e l f . marked = FALSE
s e l f . leave ( [ ] )
# WhiteKey h e l p e r c l a s s
c l a s s WhiteKey ( Key ) :
def build item ( s e l f , parent , x ) :
Canvas . R e c t a n g l e .
init (
s e l f , p a r e n t , x , 0 , x + WKEY WIDTH , WKEY HEIGHT 1 )
s e l f [ fill ] = white
s e l f [ outline ] = black
s e l f . lower ()
# BlackKey h e l p c l a s s
c l a s s BlackKey ( Key ) :
def build item ( s e l f , parent , x ) :
Canvas . R e c t a n g l e .
init (
s e l f , p a r e n t , x , 0 , x + BKEY WIDTH , BKEY HEIGHT )
s e l f [ fill ] = black
s e l f [ outline ] = black
s e l f . tkraise ()
def leave ( s e l f , args ) :
i f s e l f . marked :
s e l f [ fill ] = COLOR REF HIGHLIGHT
else :
s e l f [ fill ] = black
##################################################
# TESTING ROUTINES
c l a s s Test ( T k i n t e r . T o p l e v e l ) :
def
init ( self ):
Tkinter . Toplevel .
init (
s e l f , None )
s e l f . make widgets ()
219
12
Implementation of other
components
12.1
Tool palette
The tool palette is implemented as two interacting classes. Palette organizes all the
tools into a window and coordinates the behaviour of all the tools. All tools in the
palette derive from the Tool class. The tool defines its appearance in the palette
window as well as the behaviours performed when new constraints are created etc.
The tools in the palette can be expanded by adding more source files to the plugin directory. These files are automatically imported by the Palette class and added
to the tool palette. In this way, it is possible to expand the system without editing
the system itself. The example plug-in shown below, IntervalTool, implements the
interval constraint.
12.1.1
Palette.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# P a l e t t e . py
#
#
The Tool P a l e t t e Window
from U t i l i m p o r t
i m p o r t T o o l s , Help
from T k C o n s t a n t s i m p o r t
import Tkinter
import sys
220
221
i n i t ( s e l f , p a r e n t=None ) :
Tkinter . Toplevel .
init (
s e l f , parent ,
c l a s s = Palette )
s e l f . t i t l e ( Tools )
s e l f . w m r e s i z a b l e ( w i d t h = 0 , h e i g h t =0)
s e l f . r e a d y = FALSE
s e l f . make widgets ()
s e l f . r e a d y = TRUE
222
s e l f . h e l p b u t t o n = T k i n t e r . Button (
m a s t e r=f r a m e 2 , image= s e l f . q u e s t i o n i m a g e ,
command= s e l f . h e l p o n t o o l )
s e l f . h e l p b u t t o n . g r i d ( row = 0 , column = 0 , s t i c k y= )
s e l f . l a b e l = Tkinter . Label (
m a s t e r=f r a m e 2 , r e l i e f = sunken )
s e l f . l a b e l . g r i d ( row = 0 , column = 1 , s t i c k y=we )
s e l f . c u r t o o l = None
# I m p o r t s a l l o f t h e p l u g g a b l e Tool m o dul e s
# A l l nonb u i l ti n t o o l s i n t h e p a l e t t e a r e s t o r e d i n
# t h e i r own module i n t h e p l u gi n d i r e c t o r y .
p l u g i n d i r = "plug-ins"
def get plugins ( s e l f ) :
import glob
import os . path
i m p o r t imp
# Get a d i r e c t o r y l i s t i n g o f t h e p l u gi n m o d u l e s
p l u g i n s = g l o b . g l o b ( s e l f . p l u g i n d i r+/*. py )
result = []
# For e a c h , i m p o r t t h e module , and add t h e name o f
# i t s c orresponding t o o l c l a s s to the r e s u l t l i s t
for plugin in plugins :
head , pluginName = o s . p a t h . s p l i t ( p l u g i n )
pluginName = s e l f . p l u g i n d i r + "/" + pluginName [ 0 : 3 ]
d e b u g p r i n t ( " Loading plug-in :" , pluginName )
a , b , c = imp . f i n d m o d u l e ( pluginName )
module = imp . l o a d m o d u l e ( pluginName , a , b , c )
r e s u l t . append ( module . Tool )
return result
# Adds a t o o l t o t h e p a l e t t e
def a d d t o o l ( s e l f , frame , t o o l ) :
newtool = t o o l ( s e l f , frame )
newbutton = n e w t o o l . c r e a t e b u t t o n ( )
newbutton . g r i d ( row= s e l f . c u r r o w , column= s e l f . c u r c o l u m n , s t i c k y=nesw )
s e l f . cur column = s e l f . cur column + 1
i f s e l f . cur column > s e l f . columns :
s e l f . cur column = 0
s e l f . cur row = s e l f . cur row + 1
# C a l l e d when a new t o o l i s s e l e c t e d
def change tool ( s e l f , new tool ) :
i f s e l f . c u r t o o l ! = None :
s e l f . cur tool . deactivate ()
12.1.2
Tools.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# T o o l s . py
#
#
The g e n e r i c c l a s s from w hi c h a l l t o o l s a r e d e r i v e d
from U t i l i m p o r t
i m p o r t Nodes
import Tkinter
i m p o r t Help
##################################################
# Tool
#
#
Base c l a s s o f a l l t o o l s
c l a s s Tool :
b u t t o n b i t m a p=" default .xbm"
t o o l n a m e=" Default Tool"
223
i n i t ( s e l f , p a le t te , buttons ) :
self . palette = palette
s e l f . buttons = buttons
def
repr ( self ):
return s e l f . tool name
224
225
if
self . selected :
s e l f . selected . unselect ()
s e l f . s e l e c t e d = node
i f node :
node . s e l e c t ( )
##################################################
# ToolButton
#
#
The b u t t o n i n t h e t o o l p a l e t t e t h a t s e l e c t s
#
the t o o l
#
c l a s s T o o l B u t t o n ( T k i n t e r . Button ) :
spt = (24, 24)
def
i n i t ( s e l f , t o o l , p a l e t t e , bitmap ) :
self . tool = tool
self . palette = palette
T k i n t e r . Button .
init (
self ,
m a s t e r=p a l e t t e ,
d e f a u l t= disabled ,
bitmap= @images /+bitmap ,
command=t o o l . c h o o s e ,
r e l i e f = raised ,
w i d t h= s e l f . s p t [ 0 ] ,
h e i g h t= s e l f . s p t [ 1 ] )
s e l f . b i n d ( s e q u e n c e="< Enter >" ,
f u n c=t o o l . d e s c r i b e )
s e l f . b i n d ( s e q u e n c e="< Leave >" ,
f u n c=t o o l . u n d e s c r i b e )
##################################################
# Select
#
#
The s e l e c t i o n t o o l i s u s e d t o s e l e c t and
#
e d i t or d e l e t e p r e v i o u s l y c r e a t e d nodes .
#
Help . add (
" Selection Tool" ,
( "< title > Selection Tool </ title >\n"
"The <b> Selection Tool </b> allows you to re-edit relationships "
"that have already been added to the rule .\n"
"With the <b> Selection Tool </b> enabled , you can click on "
" existing relationships to bring up a dialog box containing "
"more specific options . For instance , clicking on an <b> interval </b> "
" relationship will allow you to select a specific kind of interval .\n"
"When a relationship is selected , it is displayed in reverse video .\n"
" Pressing the <b> Delete </b> key removes the selected relationship "
"from the rule editor ." ) )
226
c l a s s S e l e c t ( Tool ) :
b u t t o n b i t m a p=" select .xbm"
t o o l n a m e=" Selection "
def run tool ( s e l f , ru l e E di t o r , s t a r t , f i n i s h ) :
self . select ( start )
##################################################
# Equals
#
#
The E q u a l s Tool i s u s e d t o c r e a t e e q u a l s n o d e s
#
t h a t can c o n s t r a i n t h e r e s u l t s o f two o t h e r
#
n o d e s t o be e q u a l .
#
Help . add ( " Equals Tool" ,
( "< title > Equals Tool </ title >\n"
"The <b> Equals Tool </b> constrains the results of two nodes "
"to be equal .\n"
"An <b> equals </b> relationship can only be set up between two "
" nodes with the same result type ." ) )
c l a s s E q u a l s ( Tool ) :
b u t t o n b i t m a p=" equals .xbm"
t o o l n a m e=" Equals "
def run tool ( s e l f , ru l e E di t o r , s t a r t , f i n i s h ) :
i f s t a r t and s t a r t ! = f i n i s h :
i f ( s t a r t . EQUALITY TYPE ! = and
s t a r t . EQUALITY TYPE == f i n i s h . EQUALITY TYPE ) :
node = E quals N o de ( r u l e E d i t o r , s t a r t , f i n i s h )
s e l f . s e l e c t ( node )
else :
error message (
"The nodes specified are not comparable .\n"
"The Equals relationship may only be created between "
"two nodes with the same result type .\n"
"In this case , the result types were :\n"
"
First node : " + s t a r t . EQUALITY TYPE +
"
Second node : " + f i n i s h . EQUALITY TYPE +
"\ nPlease try again or see the help page on the " +
" Equals Tool for more information ." )
c l a s s Eq uals N ode ( Nodes . B i n a r y R e l a t i o n ) :
n o d e t y p e = equals
bitmap = equals .xbm
##################################################
# Greater
#
#
The G r e a t e r Than Tool c r e a t e s G r e a t e r Than
227
n o d e s t h a t c o n s t r a i n t h e r e s u l t o f one node
t o be g r e a t e r t h a n o r l e s s t h a n a n o t h e r node .
The two n o d e s must have t h e same r e s u l t t y p e .
Help . add (
" Greater Than Tool" ,
( "< title > Greater Than Tool </ title >\n"
"The <b> Greater Than Tool </b> constrains the result of one node to be "
" greater than that of another .\n"
"A <b> Greater Than </b> relationship can only be set up between "
"two nodes with the same result type .\n"
"<b>Tip :</b> Drag <i>from </i> the node that you wish to have "
"the greater result <i>to</i> the node that you wish to have "
"the lesser result . In this way , the <b> Greater Than </b> tool"
" doubles as a <b>Less Than </b> tool ." ) )
c l a s s G r e a t e r ( Tool ) :
b u t t o n b i t m a p=" greater .xbm"
t o o l n a m e=" Greater Than"
228
##################################################
# Or
#
#
The Or Tool
Help . add (
"Or Tool" ,
( "< title >Or Tool </ title >\n"
"The <b>Or Tool </b> allows two constraints to be applied "
" independently .\n"
"An <b>Or Node </b> may be created between any two nodes , "
"with the exception of nodes in the note template .\n" ) )
c l a s s Or ( Tool ) :
b u t t o n b i t m a p="or.xbm"
t o o l n a m e="Or"
def run tool ( s e l f , ru l e E di t o r , s t a r t , f i n i s h ) :
i f s t a r t and s t a r t ! = f i n i s h :
i f s t a r t . n o d e t y p e ! = note and f i n i s h . n o d e t y p e ! = note :
node = OrNode ( r u l e E d i t o r , s t a r t , f i n i s h )
s e l f . s e l e c t ( node )
else :
error message (
"The Or relationship may only be created "
" between nodes that are not part of the note "
" template .\n"
" Please try again or see the help page on the "
" Or Tool for more information ." )
c l a s s OrNode ( Nodes . B i n a r y R e l a t i o n ) :
n o d e t y p e = or
bitmap = or.xbm
##################################################
# Not
#
#
The Not Tool
Help . add (
"Not Tool" ,
( "< title >Or Tool </ title >\n"
"\n"
"An <b>Or Node </b> may be created between any two nodes , "
"with the exception of nodes in the note template .\n" ) )
c l a s s Not ( Tool ) :
229
b u t t o n b i t m a p="not.xbm"
t o o l n a m e="Not"
def run tool ( s e l f , ru l e E di t o r , s t a r t , f i n i s h ) :
if start :
i f s t a r t . n o d e t y p e ! = note :
node = NotNode ( r u l e E d i t o r , s t a r t )
s e l f . s e l e c t ( node )
else :
error message (
"The Not node may only be created "
"on a node that is not part of the note "
" template .\n"
" Please try again or see the help page on the "
" Not Tool for more information ." )
c l a s s NotNode ( Nodes . U n a r y R e l a t i o n ) :
n o d e t y p e = not
bitmap = not.xbm
12.1.3
IntervalTool.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# I n t e r v a l T o o l . py
#
#
A t o o l f o r c o n s t r a i n i n g t h e i n t e r v a l between two n o t e s
i m p o r t T o o l s , Nodes , Help
from C o n s t a n t s i m p o r t
Help . add ( " Interval Tool" ,
"" "< title > Interval Tool </ title >
The <b> I n t e r v a l Tool</b>Tool c o n s t r a i n s t h e i n t e r v a l between two n o t e s .
on t h e m u s i c a l k e y b o a r d t o c h o o s e t h e i n t e r v a l . """)
c l a s s Tool ( T o o l s . Tool ) :
b u t t o n b i t m a p=" interval .xbm"
t o o l n a m e=" Interval "
def run tool ( s e l f , ru l e E di t o r , s t a r t , f i n i s h ) :
i f s t a r t ! = None and s t a r t ! = f i n i s h :
node = I n t e r v a l N o d e ( r u l e E d i t o r , s t a r t , f i n i s h )
s e l f . s e l e c t ( node )
c l a s s I n t e r v a l N o d e ( Nodes . B i n a r y R e l a t i o n ) :
EQUALITY TYPE = " interval "
COMPARISON TYPE = " interval "
Drag a r
230
def i n i t e d i t w i d g e t s ( s e l f ) :
s e l f . m a s t e r . k e y b o a r d [ state ] = normal
s e l f . m a s t e r . k e y b o a r d . s e t m o d e (KB RANGE)
def destroy edit widgets ( s e l f ) :
s e l f . m a s t e r . k e y b o a r d [ state ] = disabled
def keyboard callback ( s e l f , keyList ) :
print keyList
12.2
12.2.1
Constants.py
Code
# V i s u a l P e l o g F a l l 1 9 9 9 M i c h a e l Droettboom mdboom@mail . com
############################################################
# C o n s t a n t s . py
#
#
Constants used throughout the V i s u a l Pelog system
##################################################
# Used by t h e g r i d l a y o u t a l g o r i t h m
# ( x , y ) p a i r : The amount o f p a d d i n g between n o d e s
RE PAD = ( 6 , 6 )
# ( x , y ) p a i r : The amount o f p a d d i n g a r o u n d t h e g r a p h
RE CANVAS PAD = ( 4 , 4 )
# The minimum and maximum p o s s i b l e v a l u e s f o r t h e t o p o l o g i c a l s o r t
RE MAXGRID = 10000
RE MINGRID = 10000
##################################################
# Used by t h e r u l e e d i t o r a n i m a t o r
RE ANIMATION FRAMES = 1 0 . 0
##################################################
# C o l o r s ( Yes , i t s t h e U . S . s p e l l i n g , t o be
#
c o n s i s t e n t w i t h Tk )
RE COLOR CANVAS BACKGROUND = white
RE COLOR NORMAL = #0000 AA
RE COLOR NODE = # eeeeee
# Canvas b a c k g r o u n d c o l o u r
# o u t l i n e u n s e l e c t e d nodes
# f i l l u n s e l e c t e d nodes
# o u t l i n e s e l e c t e d nodes
# f i l l i n s e l e c t e d nodes
# o u t l i n e h i g h l i g h t e d nodes
# o u t l i n e h i g h l i g h t e d s e l e c t e d nodes
# Used f o r t h e c o l o r o f t e x t on n o d e s
# Used f o r t h e c o l o r o f t e x t on s e l e c t e d n o d e s
12.2.2
Util.py
Code
from T k C o n s t a n t s i m p o r t
i m p o r t tkMessageBox
DEBUG = TRUE
APP NAME = " Visual Pelog "
APP VERSION = "0.0001 a"
APP AUTHOR = " Michael Droettboom "
d e f d e b u g p r i n t ( kp ) :
i f DEBUG :
f o r x i n kp :
print x ,
p r i n t "\n" ,
def f l i p ( x ) :
i f x == 1:
return 0
else :
return 1
c l a s s CursorManager :
def
init ( self ):
s e l f . c u r s o r = s e l f . c g e t ( cursor )
def s e t c u r s o r ( s e l f , cursor ) :
s e l f . c u r s o r = s e l f . c g e t ( cursor )
s e l f . c o n f i g u r e ( c u r s o r=c u r s o r )
def r e s t o r e c u r s o r ( s e l f ) :
s e l f . c o n f i g u r e ( c u r s o r= s e l f . c u r s o r )
def error message ( s t r i n g ) :
tkMessageBox . s h o w e r r o r (APP NAME , s t r i n g )
def bin2hex ( bin ) :
231
232
hx =
for c in bin :
hx = hx + hex ( o r d ( c ) ) [ 2 : ]
r e t u r n hx
d e f h e x 2 b i n ( hx ) :
b i n =
f o r i i n x r a n g e ( 0 , l e n ( hx ) / 2 ) :
b i n = b i n + c h r ( e v a l ( 0x + hx [ i 2 : ( i 2 ) + 2 ] ) )
return bin
def b i n 2 h e x f i l e ( b i n f i l e , h e x f i l e ) :
open ( h e x f i l e , w ) . w r i t e ( b i n 2 h e x ( open ( b i n f i l e , r ) . r e a d ( ) ) )
def h e x 2 b i n f i l e ( h e x f i l e , b i n f i l e ) :
open ( b i n f i l e , w ) . w r i t e ( h e x 2 b i n ( open ( h e x f i l e , r ) . r e a d ( ) ) )
def f i l t e r c r ( s ) :
i f s == \n o r s == \r o r s == \t :
r e t u r n
else :
return s
def remove cr ( s ) :
return f i l t e r ( f i l t e r c r , s )
III
Appendices
233
The problem
Now that two suitable development tools have been selected, a method of connecting
them must be chosen. Both Tcl/Tk and SWI-Prolog have built-in C-language interfaces. Intuitively, one might assume that connecting the two systems is a simple
matter of calling the others external C procedures. However, certain peculiarities around how SWI-Prologs external interface works make direct communication
through function calls impossible. Some type of connector must be provided.
SWI-Prolog has an extensive set of C functions for doing everything from creating new goals to running queries. However, these functions use custom user-defined
types to send parameters in and out. These types have names like term t and functor t that directly represent Prolog-specific data structures. Unfortunately, Tcl/Tk
has no support for user-defined C data types, and thus can not call these functions
directly.
In order to call C from Prolog, you must provide a specially-designed C function
which will set-up what SWI calls foreign language predicates. These predicates
appear to the Prolog programmer as regular Prolog predicates, but are in fact
linked to external code. The necessary elements of this system, such as callbacks
and function pointers, can not be provided from Tcl/Tk.
A.2
234
235
for Eclipse-Prolog called ProTcXl. Unfortunately, even if this interface were freely
available, it would require considerable effort to make it work with SWI-Prolog.
The external C functions provided by SWI-Prolog are entirely different from those
provided by any other Prolog, even though the general concept is the same. The
connector required for this project, therefore, must be SWI-Prolog-specific.
A.2.1
Tcl/Tk-Prolog Connection
[http://ace.ulyssis.student.kuleuven.ac.be/ jeans/tcltk/conn.html]
Jan Struyf, an undergraduate student in Belgium has created a connection between SWI-Prolog and Tcl/Tk. It allows you to send commands in either direction
that are passed as-is and evaluated by the respective interpreters. A list of the
functions is in Table A.1.
Tcl/Tk Side
p r o l o g e v e n t EventName ( P a r a m e t e r s , . . . )
Prolog Side
t c l n e w ( I n t e r p )
t c l d e l e t e (+ I n t e r p )
t c l e v a l (+ I n t e r p ,+Command, R e s u l t )
tk
tk
tk
tk
n e w (+Opts , I n t e r p )
do one event
n e x t e v e n t (+ I n t e r p , E v e n t )
main loop
tcl
tcl
tcl
tcl
tcl
tcl
e v a l f i l e /1
a b o u t /0
r e s u l t /0
s t r i n g /2
a r i t y /2
e x i t /0
A.3
236
Piping method
A.3.1
Interaction Model
A.3.2
Design
The design of a portable piping interface between Tcl/Tk and Prolog is described
in an article by Andrea DellAmico2 .
A.3.3
Usage
The majority of this usage definition is defined in DellAmico. See Table A.2 for a
list of user functions.
A.3.4
Implementation
http://www.cli.di.unipi.it/help/prolog/BinProlog/tcl
http://www.cli.di.unipi.it/help/prolog/BinProlog/tcl
Tcl-Tk Side
start prolog $process
halt prolog
start test
p AssertPrologGoal
q Query
Prolog Side
start tcl server().
The listener loop reacts to the following terms:
assert prolog(X).
query prolog(X).
halt.
call tcl(X).
237
...
starts the Prolog process. The process must
have the tcl interface library loaded.
halts the Prolog process.
allows the Tcl/Tk side to be run without the
Prolog side for testing what is being sent to
Prolog
adds a goal to the Prolog database
initiates a Prolog query. The result is read in
and interpreted by Tcl. All lines from Prolog
starting with the keyword call tcl are interpreted using eval.
...
starts the listener loop on stdio
adds the goal X to the Prolog database
runs query X and displays the result on stdio.
halts the Prolog system.
provides a convenient way to run Tcl commands. The first element of the list X must
be a Tcl command. The rest of the elements
in the list are sent as arguments to that command.
238
A.3.5
239
pl interface.tcl
Code
##############################################################
# T c l /Tk s i d e o f t h e SWIP r o l o g <> T c l /Tk i n t e r f a c e
#
# W r i t t e n by : M i c h a e l Droettboom
#
yu143345@yorku . ca
# Date :
April , 1999
# Based on an a r t i c l e d e s c r i b i n g a B i n P r o l o g <> T c l /Tk
# i n t e r f a c e by Andrea D e l l Amico
#
h t t p : / /www. c l i . d i . u n i p i . i t / h e l p / p r o l o g / B i n P r o l o g / t c l
##############################################################
# USAGE :
#
# % s t a r t p r o l o g plcon . exe
OR
% start test
# % q { PrologQuery }
# % p { QuietPrologGoal }
# % halt prolog
#
global f
# t e s t s what T c l would s e n d t o P r o l o g
proc s t a r t t e s t {} {
global f
set f stdout
}
# terminates the Prolog process
proc h a l t p r o l o g {} {
global f
w r i t e p r o l o g "halt"
catch { removeinput $f }
catch { c l o s e $f }
}
# c o n n e c t s t o a new P r o l o g p r o c e s s
#
p r o c e s s a s t a n da l o n e P r o l o g e x e c u t a b l e
#
t h i s P r o l o g must c o n t a i n t h e l i b r a r y t c l i n t e r f a c e . p l
proc s t a r t p r o l o g { process } {
global f
s e t f [ open "| $process " r +]
f c o n f i g u r e $f b u f f e r i n g l i n e
puts $f { s t a r t t c l s e r v e r .}
f l u s h $f
puts [ gets $f ]
return $f
240
}
# w r i t e s Something . t o P r o l o g
proc w r i t e p r o l o g { s } {
global f
puts $f $s .
}
# s e n d s t o p r o l o g a g o a l t o be q u i e t l y e v a l u a t e d
proc a s s e r t p r o l o g { l } {
w r i t e p r o l o g " assert_prolog (( $l ))"
}
# s e n d s t o p r o l o g a q u e r y w i t h v a r i a b l e s t o be a n s w e r e d
proc query prolog { l } {
w r i t e p r o l o g " query_prolog (( $l ))"
read prolog
}
# shorthand for c a l l p r o l o g
proc p { l } {
assert prolog $l
}
# shorthand for query prolog
proc q { l } {
query prolog $l
}
# t r u e i f t h e g i v e n l i n e i s t o be e v a l u a t e d a s a t c l command coming from P r o l o g
proc t c l c a l l { l } {
r e t u r n [ e x p r 0 = = [ s t r i n g f i r s t " call_tcl " [ s t r i n g t r i m l e f t $ l ] ] ]
}
# d e t e c t s i f an i n p u t l i n e c o n t a i n s a t c l c a l l
# ( i . e . when i t s t a r t s w i t h c a l l t c l )
# i f yes then i t executes the l i n e
# o t h e r w i s e i t p r i n t s i t out
proc e v a l l i n e { l } {
i f {[ t c l c a l l $l ]} {
return [ eval $l ]
} else {
puts $l
}
}
# r e a d s i n c o m i n g d a t a from P r o l o g u n t i l e n c o u n t e r i n g a s t o p t c l command
proc r e a d p ro l o g {} {
global f
i f { $ f ! = " stdout " } {
v a r i a b l e l go
241
A.3.6
tcl interface.pl
Code
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% P r o l o g s i d e o f t h e SWIP r o l o g <> T c l /Tk i n t e r f a c e
%
%
% W r i t t e n by : M i c h a e l Droettboom
%
yu143345@yorku . ca
% Date :
April , 1999
% Based on an a r t i c l e d e s c r i b i n g a B i n P r o l o g <> T c l /Tk
% i n t e r f a c e by Andrea D e l l Amico
%
h t t p : / /www. c l i . d i . u n i p i . i t / h e l p / p r o l o g / B i n P r o l o g / t c l
%%%%%%%%%%%%%%%%%%%%%%%%
% OPERATOR DEFINITIONS
%
$ i s n e e d e d t o s e n d T c l v a r i a b l e s t o t h e T c l i n t e r p r e t e r
: op ( 2 0 0 , f x , ( $ ) ) .
%%%%%%%%%%%%%%%%%%%%%%%%
% MAIN ENTRY POINT
%
%
USAGE : s t a r t t c l s e r v e r .
%
%
S t a r t s a l i s t e n e r l o o p t o r e a c t t o commands from T c l
% main : s t a r t t c l s e r v e r .
s t a r t t c l s e r v e r :
f o r m a t ( TCL Server started . ) , n l , ! ,
prompt ( , ) , % c r e a t e s a quiet prompt
repeat ,
n l , w r i t e ( tcl_stop . ) , n l ,
t c l i n (X , Vs ) ,
r e a c t (X , Vs ) ,
!.
%%%%%%%%%%%%%%%%%%%%%%%%
% INPUT
%
%
USAGE :
242
r e a c t ( a s s e r t p r o l o g (X ) , ) : ! , a s s e r t (X ) , ! , f a i l .
r e a c t ( q u e r y p r o l o g (X ) , Vs ): q u e r y p r o l o g (X , Vs ) , f a i l .
react ( end of file , ):!.
r e a c t ( h a l t , ): h a l t .
% r u n s a q u e r y , and d i s p l a y s t h e r e s u l t s o f a l l f r e e v a r i a b l e s .
% ad d s " tcl_stop " t o t h e end .
q u e r y p r o l o g (X , Vs ):
X , member (A , Vs ) , w r i t e (A ) , n l , f a i l .
query prolog ( , ):!.
% r e a d s t e r m s from s t d i n
t c l i n (T): t c l i n (T , ) .
t c l i n (T , Vs ):
r e a d t e r m ( L , [ v a r i a b l e n a m e s ( Vs ) ] ) ,
!,
t c l i n 1 (L ,T) .
t c l i n 1 ( [ X | Xs ] , T):
!,
member (T , [ X | Xs ] ) .
t c l i n 1 (T , T ) .
none ( none ) .
%%%%%%%%%%%%%%%%%%%%%%%%
% OUTPUT
%
%
USAGE : c a l l t c l ( L i n e ) .
%
C o n v e r t s t h e term L i n e i n t o o u t p u t t h a t w i l l be p r o c e s s e d a s
%
a T c l command . The f i r s t e l e m e n t o f L i n e must t h e r e f o r e
%
be a p r o c e d u r e o r v a r i a b l e .
c a l l t c l ( L i n e ):
w r i t e ( call_tcl ) , t c l o u t ( L i n e ) .
t c l o u t ( [ X | Xs ] ) :
! , t c l o u t l i s t ( [ X | Xs ] ) .
t c l o u t (T): t c l o u t t e r m (T ) .
t c l o u t l i s t ( L ): member (T , L ) , t c l o u t t e r m (T ) , f a i l .
tcl out list ( ).
243
t c l o u t t e r m (T): p l 2 t c l (T ) , ! .
p l 2 t c l (T): p l 2 t c l (T , L ) , w r i t e l i s t ( L ) .
p l 2 t c l (X , L ): p l 2 t c l (X , L , [ ] ) .
% s e n d i n g f r e e v a r i a b l e s t o T c l makes no s e m a n t i c s e n s e , s o e r r o r .
p l 2 t c l (X)>{v a r (X ) } , ! , { w r i t e ( unexpected input : variable ) , t r u e } , [ error ] .
% s e n d atoms as is .
p l 2 t c l (X)>{a t o m i c (X ) } , ! , [ X ] .
% s e n d atoms p r e c e d e d by $ a s T c l v a r i a b l e r e f e r e n c e s
p l 2 t c l ( $ (X))>!,{V=$ (X ) } , ! , [ V ] .
% send l i s t s i n s i d e { }
p l 2 t c l ( [ X | Xs ]) > ! , [ { ] , p l 2 t c l (X ) , t c l c m d ( Xs ) , [ } ] .
% send e x p l i c i t l y d e f i n e d { }
p l 2 t c l ( { Xs })>!,[ { ] , p l 2 t c l ( Xs ) , [ } ] .
% send e x p l i c i t y d e f i n e d ( )
p l 2 t c l ( ( X , Xs ))>!, p l 2 t c l (X ) , p l 2 t c l ( Xs ) .
t c l c m d ( [ X | Xs ])>!, p l 2 t c l (X ) , t c l c m d ( Xs ) .
tcl cmd ([])>[].
w r i t e l i s t ( L ):
member (X , L ) ,
t c l w r i t e (X ) , w r i t e ( ) ,
fail .
writelist ( ).
t c l w r i t e ( $ (X ) ) : ! , w r i t e ( $ ) , w r i t e (X ) .
t c l w r i t e (X): w r i t e (X ) .
% : s t a r t t c l s e r v e r .
This graphical user interface was designed to automate usage of the core Pelog
system. This application will be made obsolete with the completion of the Visual
Pelog system.
The Pelog system includes a very basic user interface for performing the following
tasks:
editing GUIDO Music Notation files that are used as input and output by the
Pelog system
viewing the files as conventional music notation (cmn)
listening to the files through the MIDI hardware
applying a given set of musical rules to the file using the Pelog system
The Pelog-GUI is designed for the interactive application of pre-defined sets of
musical rules. It does not directly provide the ability to customize or develop rule
sets for Pelog. For that youre better of using an editor with some advanced Prolog
support such as GNU Emacs. Please see The Pelog Language (page 2) for more
information.
For information on how to write GUIDO Music Notation files for the Pelog
system, see GUIDO Music Notation (page 35).
B.1
The installation instructions given here are Microsoft Windows-specific. Please see
Notes about Portability (page 251) for more information.
The Pelog-GUI requires that the following freely available applications are installed on your system:
244
245
B.2
To run the Pelog-GUI, simply run the provided batch file pelog.bat. The easiest
way to do this is by double-clicking on it in the Explorer. This script will start the
Pelog-GUI under Tcl/Tks wish interpreter. This will bring up the opening splash
screen in Figure B.1.
Click anywhere on the splash window to continue to the Pelog-GUI.
B.3
A quick introduction
The section will guide you through all of the basic functionality of the Pelog-GUI.
The Pelog-GUI is very similar to many standard text editors such as Microsoft
Notepad included with Windows. The difference is that the Pelog-GUI is specifically
tailored to edit musical scores used as input to the Pelog interpreter. For more
information about these files, see GUIDO Music Notation (page 35).
Lets enter an score by typing the following line into the editor:
246
{ [ c0 d e f g a b c1 ] }
The outer set of curly brackets { } is used to denote an entire score. The inner
set of square brackets [ ] is used to denote an individual voice (or part) within
that score. You can see that this score contains only one voice. In that voice there
are five notes each represented by a letter. The numbers after the letters select the
octave.
To look at the score as common music notation (cmn) press the button
on
the toolbar. Figure B.2 shows the GUIDO NoteViewer displaying the score that
we have entered:
You can also listen to the score through any available MIDI hardware by pressing
the
This score indicates that we want the starting and ending notes to be c, but the
interpreter can fill-in the rest of the notes as it pleases. It is important to note that
the tilde character is not supported by the view or look functions, and therefore you
cannot view or listen to a score containing note variables.
The next step is to select a set of rules to use when filling in the variable notes.
Pressing the
247
all of the available rule sets on your system1 . For now, lets choose the modal
counterpoint rule set. When you return from the dialog box you will notice that
the text box on the right-hand side of the toolbar shows the name of the rule set
we just selected.
Now we are ready to apply the rules. Press the
button on the toolbar. The
Pelog system will load your score and run it through the rule set. Be patient. This
particular example took 30 seconds to solve on my Intel Pentium 166MHz machine.
When the Pelog interpreter is finished, the results of its work are loaded into a new
Pelog-GUI window.
This output score can be viewed or listened to just like the input score. You
can even revise it, including adding variable notes, and then run it through the
interpreter again.
1
For a detailed explanation of rule sets, see The Pelog Language (page 2.)
B.4
Visual elements
B.4.1
248
The main window should seem quite familiar to anyone accustomed to typical
Windows or Macintosh applications. It consists of a menubar, toolbar and text
editor.
B.4.2
The menubar
B.4.3
The toolbar2
2
These toolbar graphics are from a set developed by IBM/Lotus and freely available from
IBM Ease of Use http://www.ibm.com/easy/. The toolbar images developed by Microsoft are
perhaps more standard, but are licensed for use only in conjunction with Microsofts own software
development products.
B.4.4
249
The editor
B.5
B.5.1
Functions
Basic file and editing functions
The basic file and editing functions should be old hat to anyone familiar with Windows. The behaviour is identical to Microsoft Windows Notepad.
B.5.2
Look
Figure B.3: The GUIDO NoteViewer window opened by the View Look function
For information about using the GUIDO NoteViewer, please see the online help
on the Help menu in the NoteViewer window.
B.5.3
Hear
250
Figure B.4: The Windows Media Player opened by the View Hear function
The Windows Media Player plays MIDI files on currently selected MIDI device.
This MIDI hardware could be a sound card, such as the popular Sound Blaster, or
an external MIDI device such as a keyboard or sound module. This device can be
selected using the Multimedia applet in the Windows Control Panel. See the online
help in the Media Player itself for more information on using Microsoft Windows
Media Player.
Note: At the present time, the gmn2midi application that converts
GUIDO Music Notation format files into standard MIDI files is still
in beta testing. Just from my own informal usage, it has proven to
be quite weak in certain areas. For instance, when playing the Happy
Birthday example above, the bass part is consistently a full beat ahead
of the treble.
B.5.4
Go
251
After the Pelog interpreter is finished applying rules to the file (which may take
quite some time depending on the complexity of the rule set and the number of
variables in the input file), the result is opened in a new Pelog-GUI window. The
name of the results file is identical to the name of the input file with out appended
to the end.
B.6
At the time of this writing, the Pelog-GUI has only been tested on the 32-bit
Microsoft Windows platform. While it is theoretically portable to any platform
Tcl/Tk runs on, certain functions remain Windows-specific:
The Look function uses the GUIDO NoteViewer to display the music in common music notation. This application is currently only available for Windows.3
The Hear function uses two external applications. The first, gmn2midi converts GUIDO music notation format files into Standard MIDI files. This program is currently available for many platforms including Windows, Macintosh
and UNIX. 4 The second application is the Media Player supplied with Windows to play standard MIDI files. This application could be substituted with
any of the commonly available MIDI players available on many platforms.
3
4
Bibliography
[1] , Tcl/Tk Reference Manual. www.scriptics.com
[2] ,
Max:
object-oriented programming environment for
sic and multimedia. Mountain View, CA: Opcode Systems,
http://www.opcode.com/products/max/
muInc.
252
BIBLIOGRAPHY
253
BIBLIOGRAPHY
254
[28] Ross, Peter. Advanced Prolog: Techniques and Examples. Don Mills, ON:
Addison-Wesley, 1989.
[29] Rowe, Neil C. Artificial Intelligence Through Prolog. Englewood Cliffs, NJ:
Prentice Hall, 1988.
[30] Salus, Peter H., ed. Handbook of Programming Languages: Volume III: Little Languages and Tools. Indianapolis, IN: Macmillan Technical Publications,
1998.
[31] Salus, Peter H., ed. Handbook of Programming Languages: Volume IV: Functional and Logic Programming Languages. Indianapolis, IN: Macmillan Technical Publications, 1998.
[32] Schaffer, John & Deron McGee. Knowledge-Based Programming for Music Research. Madison, WI: A-R Editions, 1997.
[33] Schottstaedt, William. (1984) Automatic Counterpoint. Current Directions
in Computer Music Research. Ed. Max V. Mathews and John R. Pierce. Cambridge, MA: MIT Press, 1989.
[34] Sterling, Leon & Ehud Shapiro. The Art of Prolog: Advanced Programming
Techniques. Cambridge, MA: MIT Press, 1986.
[35] Sugiyama, Kozo & Shojiro, Tagawa & Toda, Mitsuhiko. Models for Visual
Understanding of Heirarchical System Structures. IEEE Trans. on Systems,
Man and Cybernetics. Vol SMC-11, No 2, Feb 1981, pp 109-125.
[36] Taube, Rick. Introduction to Common Music.
http://cm.stanford.edu/CCRMA/Software/cm/cm.htm
[37] Thakar, Markand. (1990) Counterpoint: Fundamentals of Music Making. New
Haven, CN: Yale University Press, 1990.
[38] Thompson, Tim. Reference Manual for the KeyKit Language Version 6.2c. San
Jose, CA: AT&T, 1997.
[39] Van Hentenryck, Pascal. Constraint Satisfaction in Logic Programming. Cambridge, MA: MIT Press, 1989.
[40] Vercoe, Barry. CSound Reference Manual. Cambridge, MA: MIT Machine Listening Group, 1999.
[41] Wielemaker, Jan. SWI-Prolog 3.2 Reference Manual. Amsterdam: Department of Social Science Informatics (SWI), University of Amsterdam, 1999.
http://www.swi.psy.uva.nl/projects/SWI-Prolog/
All musical examples were rendered using GUIDO NoteServer for Windows v0.2.
http://www.informatik.tu-darmstadt.de/AFS/GUIDO
BIBLIOGRAPHY
255
All musical constraint graphs were rendered directly using Visual Pelog.
This document was typeset using LATEX and Carl Heinz exceptional listings package.