1
Pelog: A Musical Constraint System Michael Droettboom COSC4080 Independent Study Project Advisor: Dr. Peter Roosen-Runge November 30, 1999
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 . . . . . .
I
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
Pelog
1 The 1.1 1.2 1.3 1.4
ii ii iii iii iv iv iv v v v
1
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 doesn’t 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
i
CONTENTS
2.3
2.4
ii
2.2.1 Species rules . . . . . . . . . . . . 2.2.2 Static rules . . . . . . . . . . . . . 2.2.3 Horizontal rules . . . . . . . . . . . Vertical rules . . . . . . . . . . . . . . . . 2.3.1 Horizontal / Vertical rules . . . . . 2.3.2 Extensions of Modal Counterpoint Implementation . . . . . . . . . . . . . . . 2.4.1 modal counterpoint.pl . . . . . . .
3 GUIDO music notation format 3.1 Scores . . . . . . . . . . . . . 3.2 Parts . . . . . . . . . . . . . . 3.3 Events . . . . . . . . . . . . . 3.4 Notename . . . . . . . . . . . 3.5 Accidentals . . . . . . . . . . 3.6 Octave . . . . . . . . . . . . . 3.7 Duration . . . . . . . . . . . . 3.8 Tags . . . . . . . . . . . . . . 3.8.1 Key . . . . . . . . . . 3.8.2 Scale . . . . . . . . . . 3.8.3 Range . . . . . . . . . 3.8.4 Meter . . . . . . . . . 3.8.5 Accent pattern . . . . 3.8.6 Comment style . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
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
7 Motivations for a visual language 7.1 Summary of core Pelog system . 7.2 The next step . . . . . . . . . . . 7.3 The problem . . . . . . . . . . . 7.4 The solution . . . . . . . . . . . . 8 The 8.1 8.2 8.3
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 . . . .
. . . .
. . . .
Visual Pelog language Musical Constraint Graphs . . . . . . Constraint primitives . . . . . . . . . . Some examples . . . . . . . . . . . . . 8.3.1 Step-wise motion . . . . . . . . 8.3.2 Step-wise or third-wise motion 8.3.3 Avoiding parallel intervals . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
. . . . . .
. . . .
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
Future extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
160 160 160 162 162 162 162 162 162 162 162 163 164 165
10 Graph Layout Algorithm 10.1 Specification of problem . . . . . . . . . . . . . . . . . . . . . . 10.2 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.1 Sugiyama algorithm . . . . . . . . . . . . . . . . . . . . 10.2.2 Constraint-based extensions to the Sugiyama algorithm 10.2.3 The problem of graph stability . . . . . . . . . . . . . . 10.2.4 The problem of two-dimensionality . . . . . . . . . . . . 10.3 GLA Implementation . . . . . . . . . . . . . . . . . . . . . . . . 10.3.1 General design . . . . . . . . . . . . . . . . . . . . . . . 10.3.2 The top-level . . . . . . . . . . . . . . . . . . . . . . . . 10.3.3 The near constraint . . . . . . . . . . . . . . . . . . . . 10.3.4 Constraint-based topological sort . . . . . . . . . . . . . 10.3.5 Conflict resolution . . . . . . . . . . . . . . . . . . . . . 10.3.6 Mapping physical coordinates to logical coordinates . . 10.3.7 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . 10.3.8 Creating nodes . . . . . . . . . . . . . . . . . . . . . . . 10.4 Code related to the graph layout algorithm . . . . . . . . . . . 10.4.1 RuleEditor.py . . . . . . . . . . . . . . . . . . . . . . . . 10.4.2 Nodes.py . . . . . . . . . . . . . . . . . . . . . . . . . . 10.4.3 CanvasItems.py . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . .
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
Visual Pelog environment Introduction . . . . . . . . . . . . . A Visual Pelog session . . . . . . . Opening and saving rule sets . . . Rule manager . . . . . . . . . . . . 9.4.1 Selecting rules . . . . . . . 9.4.2 Adding rules . . . . . . . . 9.4.3 Moving rules . . . . . . . . 9.4.4 Deleting rules . . . . . . . . 9.4.5 Editing rules . . . . . . . . Rule editor . . . . . . . . . . . . . 9.5.1 Graph editor . . . . . . . . 9.5.2 Using the musical keyboard Tool palette . . . . . . . . . . . . .
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
12 Implementation of other components 12.1 Tool palette . . . . . . . . . . . . . . 12.1.1 Palette.py . . . . . . . . . . . 12.1.2 Tools.py . . . . . . . . . . . . 12.1.3 IntervalTool.py . . . . . . . . 12.2 Constants and Utility functions . . . 12.2.1 Constants.py . . . . . . . . . 12.2.2 Util.py . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
220 220 220 223 229 230 230 231
III
Appendices
233
A Tcl/Tk-Prolog Connector System A.1 The problem . . . . . . . . . . . . A.2 C-language connector method . . . A.2.1 Tcl/Tk-Prolog Connection . A.3 Piping method . . . . . . . . . . . A.3.1 Interaction Model . . . . . A.3.2 Design . . . . . . . . . . . . A.3.3 Usage . . . . . . . . . . . . A.3.4 Implementation . . . . . . . A.3.5 pl interface.tcl . . . . . . . A.3.6 tcl interface.pl . . . . . . . B The B.1 B.2 B.3 B.4
graphical user interface Installing the GUI . . . . . Running the GUI . . . . . . A quick introduction . . . . Visual elements . . . . . . . B.4.1 The main window . B.4.2 The menubar . . . . B.4.3 The toolbar . . . . . B.4.4 The editor . . . . . . B.5 Functions . . . . . . . . . . B.5.1 Basic file and editing B.5.2 Look . . . . . . . . . B.5.3 Hear . . . . . . . . . B.5.4 Go . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 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
B.6 Notes about portability . . . . . . . . . . . . . . . . . . . . . . . . . 251
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 don’t 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 Mozart’s 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 Sch¨onberg-style 1
often at the penalty of death
vii
INTRODUCTION
viii
Twelve-Tone Serial Composition [26]. It is also possible to implement entirely new systems of musical constraints. The original textual language for musical constraints has been extended to an editable visual representation, Visual Pelog (see part II). The Visual Pelog system allows the user to specify musical constraints as graphs of unary and binary relationships between musical events. For some users, this visual representation, with immediate feedback about logical errors and an interactive help system, may be more intuitive than using the text-based language. Musical phrases are input in a musical notation language called GUIDO [15]. This language has been extended to allow certain events to be variable. These variable events are “filled-in” by the Pelog interpreter.
0.2
Purpose
Pelog serves a two-tiered purpose: • Using pre-built add-ins known as Pelog rule sets, musicians can use Pelog as a counterpoint generation and evaluation system. • For the novice programmer with a musicological bent, Pelog is a musical constraint specification language that can be used to test the validity of a given musical framework or develop entirely new musical relationships. Visual Pelog extends this purpose by increasing the accessibility of the language Pelog does not pretend to produce beautiful music. At this stage, it is perceived as a test-bed for research into musical systems. Often the logic used to describe musical constraints is very “fuzzy” leaving a lot of room for individual interpretation. One only needs to look at Gradus ad Parnassum [11] or any undergraduate counterpoint classroom for an example of this. Translating these constraints for use by a computer forces one to be meticulously precise. This exercise alone can be very illuminating. Having a computer apply these rules to musical phrases reveals the weaknesses of a given musical constraint system in a very automatic and efficient way. Pelog can therefore assist is asking questions such as “How precise were Fux’s definitions of modal counterpoint? If imprecise, what additional information is needed to match his intent?”
0.3
Guide to this document
This document is divided into two main parts: 1. Pelog: The core Pelog language and musical constraint interpreter. (page 2) 2. Visual Pelog: The Visual Pelog extensions to the language. (page 151) Each part is broken down into chapters for end users of the system, followed by chapters dealing with the implementation of the system.
INTRODUCTION
ix
The appendices contain reports on research conducted that proved to be obsolete to the project as a whole. For a general overview of the project, I suggest reading this introduction and the first chapter of each part.
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 Pelog’s Modal Counterpoint application. Pelog’s advantage however2 , is in the declarative nature and maintainability of the musical constraints themselves. While Schottsteadt’s 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 Cope’s 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 Cope’s 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. Cope’s 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
This is postulation, as Schottsteadt’s source code is not readily available
INTRODUCTION
x
that effective music could be generated using constraints exclusively. (Composer and Professor James Tenney, with whom I have studied, always asserts that a good composer must also be an avid listener.) Perhaps the ideal system, then, would be a combination of EMI and Pelog: a system that has both constraints and a history of external musical experience. (Give our “brainy baboon” a collection of Bach Chorale recordings and he might stand a better chance.)
0.5 0.5.1
Future Directions Pelog
Pelog is still very much a ninety-pound weakling, and there’s 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 The Pelog system is completely extendible and allows the end user to modify existing rule sets or create new ones from scratch. Editing rule sets requires at least a working knowledge of Prolog. I recommend Programming in Prolog [5] as a good starting point, and it should provide you with everything you need to understand this section. It is also helpful to use a programmer’s text editor with good support for Prolog, such as GNU Emacs. This section provides a tutorial introduction to developing rule sets followed by a reference of built-in functions available to the Pelog programmer.
1.1
What is a rule set?
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
CHAPTER 1. THE PELOG LANGUAGE
3
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 Pelog’s 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 ) .
CHAPTER 1. THE PELOG LANGUAGE
1.3
4
How rules are applied to the score
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 it’s 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.
Figure 1.1: Example score showing time-path You may have noticed above that there are no metavariables provided for events after the current event. This is because those events that have not been visited yet may contain variables. It is good practice to determine the current event based only on the previous events because they have already been pinned-down as non-variable. This will eliminate unnecessary backtracking due to “erroneous assumptions about the future.” The order in which the rules are applied to each event is a little bit more complex. All the rules are divided into classes. All the rules within each class are presumed to be mutually exclusive. (Though it is the rule programmer’s responsibility to ensure this.) For example, you may have the rule “melodic interval of a second” and “melodic interval of a third” in the same class called “melodic interval.” These two rules should be in the same class because it is impossible for an event to have both a melodic interval of a second and a melodic interval of a third at the same time (i.e. they are mutually exclusive.) It would not be a good idea to put “melodic interval of a second” and “in c major” in the same class because it is possible for an event to fulfill both rules simultaneously. The concept of class is important because it allows the interpreter to be “lazy.” If an event passes the rule “melodic interval of a second,” the rule “melodic interval of a third” does not need to be checked because we already know it to be false. Within each class, each rule is assigned a weight. Rules with lower weights
CHAPTER 1. THE PELOG LANGUAGE
5
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. Let’s 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
Rule ordering for efficiency
Given this, what is the best way to order the rules to maximize efficiency?
1.4.1
Put the rule class with the fewest branches first
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
CHAPTER 1. THE PELOG LANGUAGE
6
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
When the given class and weighting system doesn’t apply, do it yourself
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. Let’s 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
Table 1.2: Modal counterpoint rules related to chronological position rule class has been implemented as one rule with a number of helper predicates. Don’t forget that a Pelog rule set file is really just a Prolog program in disguise. /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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)
CHAPTER 1. THE PELOG LANGUAGE
7
; 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).
CHAPTER 1. THE PELOG LANGUAGE
8
% 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
Pelog library reference
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 Prolog’s 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 O’Keefe. • 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
CHAPTER 1. THE PELOG LANGUAGE
9
comment(+event, +comment) If comment is instantiated, comment is added to event’s comment list. If comment is a variable, comment unifies with any comment currently in event’s 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.
CHAPTER 1. THE PELOG LANGUAGE
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
The relationship between two pitches. In Pelog, intervals can be specified in the form type~size, where type is a threecharacter atom specifying the type of interval. The available types are listed in Table 1.3. type maj min per dim aug chr sem tri
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 Prolog’s anonymous variable, the underscore ‘ ’. Therefore, ~3 will specify dim~3, min~3, maj~3 and aug~3.
CHAPTER 1. THE PELOG LANGUAGE type unison step skip consonant dissonant
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.
CHAPTER 1. THE PELOG LANGUAGE
12
eventa : eventb contour contour is much faster than the equivalent eventa : eventb absolute interval contour contour
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
Each part in a score contains a single monophonic line. A part can be referenced to by number, in which case the top part is part 1, the next highest is part 2, etc. A part can also be referenced by a name defined by the part def fact predicate. The built-in part names are shown in Table 1.5. only top bottom cantus firmus inner soprano alto tenor bass
Table 1.5: Built-in part names Additional part names can be defined by adding part def predicates to the rule set file. part def predicates have the form:
CHAPTER 1. THE PELOG LANGUAGE
13
part def(name, partno, numparts) name is the name of the part partno is the corresponding part number numparts is the number of parts The built-in part defs are listed below: part part part part part part part part part
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
CHAPTER 1. THE PELOG LANGUAGE
14
?event pitch name ?name pitch name(?event, ?name) Succeeds if event has pitch name.
1.5.7
Range
Range can be specified in a number of ways. • As an interval. See the interval library (page 10) for more information. • As a set of two events lowevent : highevent. When using this form, the two events must be instantiated. • By name Please note that all events used with the range predicates must be instantiated. The built-in range definitions are in Table 1.6. soprano alto tenor bass Table 1.6: Built-in ranges Additional named ranges can be defined by adding range def predicates to the Pelog rule set file. range def predicates have the form: range def(name, lowpitch, highpitch) name is the name of the range. lowpitch is the lowest pitch in the range, given in the form pitchname~octave. See the pitch library (page 13) for details. highpitch is the highest pitch in the range The built-in range defs are given below: range range range range
def ( soprano , c ˜ 1 , a ˜3). def ( alto , f ˜ 0, d˜3). def ( tenor , c ˜ 0, a ˜1). def ( bass , f ˜ −1, d ˜0).
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.
CHAPTER 1. THE PELOG LANGUAGE
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 scale’s 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.
CHAPTER 1. THE PELOG LANGUAGE
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
CHAPTER 1. THE PELOG LANGUAGE
17
The built-in scale defs are listed below. 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 ) : − ! .
scale scale scale scale scale scale scale scale scale scale scale scale scale
d e f ( d o r i a n , [ c , d , e & , f , g , a , b & , b ] , maj ˜ 2 ) : − ! . d e f ( hypo−d o r i a n , X , Y ) : − s c a l e ( d o r i a n , X , Y ) , ! . d e f ( p h r y g i a n , [ c , d & , e & , f , g , a & , b & ] , maj ˜ 3 ) : − ! . d e f ( hypo−p h r y g i a n , X , Y ) : − s c a l e ( p h r y g i a n , X , Y ) , ! . d e f ( l y d i a n , [ c , d , e , f #, g , a , b ] , p e r ˜ 4 ) : − ! . d e f ( hypo−l y d i a n , X , Y ) : − s c a l e ( l y d i a n , X , Y ) , ! . d e f ( m i x o l y di a n , [ c , d , e , f , g , a , b &, b ] , per ˜ 5 ) : − ! . d e f ( hypo−m i x o l y d i a n , X , Y ) : − s c a l e ( m i x o l y d i a n , X , Y ) , ! . d e f ( a e o l i a n , [ c , d , e & , f , g , a & , b & , a , b ] , maj ˜ 6 ) : − ! . d e f ( hypo−a e o l i a n , X , Y ) : − s c a l e ( a e o l i a n , X , Y ) , ! . d e f ( l o c r i a n , [ c , d & , e & , f , g & , a & , b & ] , maj ˜ 7 ) : − ! . d e f ( i o n i a n , X , Y) : − s c a l e ( major , X , Y ) , ! . d e f ( hypo−i o n i a n , X , Y ) : − s c a l e ( i o n i a n , X , Y ) , ! .
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 c−s 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 e−t o n e , [ c , d , e , f #, g #, a # ] , none ) : − ! . s c a l e d e f ( w h o l e−t o n e−s h a r p s , X , Y ) : − s c a l e ( w h o l e−t o n e , X , Y ) , ! . s c a l e d e f ( w h o l e−t 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 c−a , 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 c−b , [ 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 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 ) . 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 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−X , Y , Z ) : − s c a l e d e f ( o c t a t o n i c−X , 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 ) : − ! .
s c a l e d e f ( b l u e s , [ c , d , e &, e , f , g &, g , a , b & ] , u n i s ) : − ! .
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.
CHAPTER 1. THE PELOG LANGUAGE
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 , hypo−l 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 l−m 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 ) : − ! .
CHAPTER 1. THE PELOG LANGUAGE special special special special special
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 c−m 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 l−m 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
An Example Application: Modal Counterpoint The first stage of this project will concern itself exclusively with modal counterpoint. The system as a whole will be designed, however, to be extendible to tonal, twelvetone and other forms of counterpoint.
2.1
Sources
There is an avalanche textbooks on modal counterpoint. To simplify the process of breaking down modal counterpoint into logical predicates, and to be somewhat standard and correct, the sources selected for this project, as a whole, meet the following criteria: • definitive and widely-accepted standard • precise definitions, avoiding concepts like ”feeling” and ”affect” • complete description of counterpoint, containing no simplifications [33] Schottstaedt, William. (1984) ”Automatic Counterpoint.” Current Directions in Computer Music Research. Ed. Max V. Mathews and John R. Pierce. Cambridge, Mass.: MIT Press, 1989. This article describes a system, similar to the present project, for solving counterpoint exercises. While no implementation of the system is given, it has a concise summary of Fux [11] as a list of simple rules [pp. 200202]. The majority of the rules below are derived directly from this list. I have translated many of these rules from somewhat logically ambiguous language (often quoted directly from Fux) into phrases that better resemble Prolog clauses. 20
CHAPTER 2. MODAL COUNTERPOINT
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 Fux’s 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.
CHAPTER 2. MODAL COUNTERPOINT
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 Fux’s 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
CHAPTER 2. MODAL COUNTERPOINT
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.
CHAPTER 2. MODAL COUNTERPOINT
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
Horizontal rules involve melodic relationships between notes occurring sequentially. Referencing between different melodic lines is not necessary to apply these rules. In this section about “horizontal rules,” “interval” refers to “melodic interval.” Melodic intervals • Augmented intervals Diminished intervals Chromatic intervals Intervals larger than per5, excluding min6, per8 Downward min6 • Three notes or more notes outlining a tritone: Given three notes {a, b, c}, the interval a-c is a tritone and a-b and a-c are in the same direction • Interval of per8 • Two skips that do not form a triad • Interval of maj6 or min6 Direction / Contour A skip is a melodic interval greater than a maj2. A step is a melodic interval of maj2 or min2. • Three consecutive skips in whole or half notes • Two consecutive skips in quarter notes • Two eighth notes with skip • Skip followed by motion in same direction • Skip followed by skip in the opposite direction • Skip followed by a skip • Skip preceded by motion in same direction • Step up followed by step down • Step down followed by step up
CHAPTER 2. MODAL COUNTERPOINT
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 doesn’t contain an imitative sequence of three notes • Melody doesn’t contain an imitative sequence of four notes • Melody doesn’t contain an imitative sequence of five notes • Melody doesn’t 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
CHAPTER 2. MODAL COUNTERPOINT
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
Horizontal / Vertical rules
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.
CHAPTER 2. MODAL COUNTERPOINT
27
• Parallel motion: Harmonic interval is equal to the preceding harmonic interval. Rules which exhibit some of the properties of parallel motion are: • Harmonic interval of per8 followed be harmonic interval of per5 • Harmonic interval is maj6 or min6 and next melodic intervals are in similar motion • Parallel motion of any interval over more than three consecutive tones • Harmonic interval is maj10 or min10 and next harmonic interval is per8 • Harmonic interval is per8 or unis and previous melodic interval is a skip • Harmonic interval is per5 or per8 and next melodic intervals are in similar motion • Harmonic interval is unis and next melodic interval is a skip Direct motion Direct motion occurs when two voices traveling in the same direction melodically land on an undesirable harmonic interval. Krenek [20] calls “direct” motion “similar” motion. • Direct motion to per5 or per8 • Direct motion to dim5 • Direct motion to per4 or per5 • Direct motion to maj6 or min6 • Direct motion to dim5 Scale degrees Certain harmonic intervals are undesirable when the pitches involved are at certain degrees in the scale. • Harmonic interval is unis or per8 and pitch is “leading tone” • Harmonic interval is unis or per8 and scale degree is third, fifth or sixth Passing tone • Dissonance must be in a passing tone: Harmonic interval is { min2, maj2, per4, min7, maj7 } (dissonant), notes preceding and following do not have a melodic interval of min3 or maj3. Passing tones must occur on unaccented beats.
CHAPTER 2. MODAL COUNTERPOINT
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 doesn’t contain a cross-part imitative sequence of three notes • Melody doesn’t contain a cross-part imitative sequence of four notes • Melody doesn’t contain a cross-part imitative sequence of five notes • Melody doesn’t contain a cross-part imitative sequence of six notes Miscellaneous • All voices have skips at once
2.3.2
Extensions of Modal Counterpoint
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 ’ ,
CHAPTER 2. MODAL COUNTERPOINT
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 ) ) .
CHAPTER 2. MODAL COUNTERPOINT
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 .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
CHAPTER 2. MODAL COUNTERPOINT
31
SPECIAL CASES FOR MODES C e r t a i n modes hav e r u l e s o f t h e i r own ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ r u l e ( ’ Aeolian ’ , ’ special cases for modes’ , 0 ) : − c u r r e n t s c a l e ˜ a e o l i a n −> m c a e o l i a n s p e c i a l ( $event , $prev1 ) ; true . % I f l e a d i n g t o n e i s a p p r o a c h e d from b e l o w , i t must be r a i s e d . m c a e o l i a n s p e c i a l (E , P) :− E leading tone , not P s c a l e d e g r e e 6 , ((P s c a l e d e g r e e r a i s e d ˜6, P comment ’ Aeolian raised 6 th’ ) ; true ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 .
CHAPTER 2. MODAL COUNTERPOINT
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 ;
CHAPTER 2. MODAL COUNTERPOINT
33
( $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’ ) . r u l e ( ’X - skip : same direction ’ , ’ contour ’ , 1 ) : − $prev2 = s t a r t ; ( $prev2 : $prev1 contour X, $prev1 : $event a i n t e r v a l skip contour X, $ p r e v 2 comment ’X - skip : same direction ’ ) . r u l e ( ’Skip - X: opposite direction ’ , ’ contour ’ , 3 ) : − $prev2 = s t a r t ; ( $prev2 : $prev1 a i n t e r v a l skip contour X, $prev1 : $event contour Y, $ p r e v 2 comment ’Skip - X: opposite direction ’ , X \= Y ) . r u l e ( ’ Repeat - Step’ , ’ contour ’ , 3 ) : − $prev2 = s t a r t ; ( $prev2 : $prev1 contour s t a t i c , $prev1 : $event a i n t e r v a l step , $ p r e v 2 comment ’ Repeat - Step’ ) . /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ OUTLINING A TRITONE ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ % Outlining a tritone r u l e ( ’ Ensure not’ , ’ outline a tritone ’ , 1 ) : − $prev2 = s t a r t ; (( $prev2 : $prev1 contour X, $ p r e v 1 : $ e v e n t c o n t o u r X) −> ( not $ p r e v 2 : $ e v e n t a i n t e r v a l t r i ˜ ) ; true ) . % Two s k i p s t h a t form a t r i a d r u l e ( ’Two skips that form a triad’ , ’ contour ’ , 5 ) : − ( $prev2 : $prev1 a i n t e r v a l ˜3 contour X, $prev1 : $event a i n t e r v a l ˜3 contour X ) , ! .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 ) : −
CHAPTER 2. MODAL COUNTERPOINT
34
$prev1 : $event a i n t e r v a l unis .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 ) .
r u l e ( ’ Avoid four’ , ’ repeat patterns ’ , 2 0 ) : − $ p r e v 5 comment ’ repeated four-note sequence ’ , not m c r e p e a t p a t t e r n ( $ p r e v 7 , $ p r e v 6 , $ p r e v 5 , $ p r e v 4 , $ p r e v 3 , $ p r e v 2 , $ p r e m c r e p e a t p a t t e r n (A , B , A , B ) . m c r e p e a t p a t t e r n (A , B , C , A , B , C ) . m c r e p e a t p a t t e r n (A , B , C , D , A , B , C , D ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 .
3
GUIDO music notation format
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
CHAPTER 3. GUIDO MUSIC NOTATION FORMAT
3.1
36
Scores
The entire score must be enclosed in curly braces “{ }”.
3.2
Parts
Each part, (i.e. a monophonic voice,) is enclosed in square brackets “[ ]”. For scores with more than one part, the parts are separated by a comma “,”. { [ c d e d c ], [ c e g g c] }
3.3
Events
Any note, rest or variable note is considered an event. In general, no white space should be used within each event description. The general syntax for representing notes is: notename accidentals octave duration For rests (events without sounding pitch): duration
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
There are different systems of notenames: 1. c d e f g a h/b (diatonic or german) 2. c cis d dis e f fis g gis a ais b (chromatic) 3. cd re mi fa sol la si/ti (solfege)
3.5
Accidentals
accidentals is one of the following: # (sharp) raises note by one semi-tone & (flat) lowers note by one semi-tone ## (double sharp) raises note by two semi-tones && (double flat) lowers note by two semi-tones
CHAPTER 3. GUIDO MUSIC NOTATION FORMAT
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
duration is specified in one of the following forms: 1. * enum / denom dotting 2. * enum dotting 3. / denom dotting where enum and denom are positive integers and dotting is either empty, “.” or “..”. When either enum or denom are omitted, it is given a value of 1. If the entire duration is omitted, it is assumed to be identical to the last duration specified. The default duration is 1/4 (quarter note).
3.8
Tags
Tags provide additional information about the score. They are of the form: \ i d
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
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 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.
CHAPTER 3. GUIDO MUSIC NOTATION FORMAT
3.8.2
38
Scale
scale
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.
CHAPTER 3. GUIDO MUSIC NOTATION FORMAT
3.8.3
39
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
sets the time signature to s where s is of the form numerator / denominator .
3.8.5
Accent pattern
\accented beats
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
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.
CHAPTER 3. GUIDO MUSIC NOTATION FORMAT
Figure 3.1: Pelog-GUI with referenced comments
40
4
Tools 4.1
Selecting the tools
This project requires the following categories of development tools: 1. Prolog interpreter 2. GUI development system 3. A system connecting these two tools The specific implementations of these tools must meet the following requirements: • Free for educational use • Runs on the chosen development platform (32-bit Microsoft Windows), with support for porting to other platforms • As industry-standard as possible, to ensure maintainability and portability • Creates stand-alone distributions that do not require that the entire development system is installed on the user’s machine • Supports rapid application development (RAD) allowing edits without recompilation
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 IDE’s, 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
GUI development system
Chosen tool Tcl/Tk [www.scriptics.com] Tcl/Tk is a very efficient and increasingly popular language for the automation of simple tasks and the creation of graphical user interfaces. In particular, Tcl/Tk makes it easy to create new graphical widgets by combining the functionality of existing ones. Since it is an interpreted
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] Java’s 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 the two
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. It’s simple, easy to debug and directly ports to UNIX. It doesn’t 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
SWI-Prolog has proven to be a very high quality tool. Robust. It crashed only once during the development of Pelog, and that was due to an unrelated disk access error. Most common errors such as memory overflows due to infinite recursion are caught before crashing the operating system. Standard. SWI-Prolog’s high level of support for the Edinburgh Prolog standard allowed the Pelog system to reuse code from a number of different sources. For example, the binary tree structure that is so pivotal to the central Pelog data structure (page 45) was “borrowed” from the Quintus Prolog library. It ran in SWIProlog with absolutely no modification. The standard also made it possible to take advantage of the great number of tutorial texts on Edinburgh Prolog. As someone who is relatively new to Prolog, it would have been far more difficult to complete this project in a more proprietary language using only proprietary documentation. (The author of SWI-Prolog, Jan Wielemaker, is currently implementing the recently adopted ISO Prolog standard into SWI-Prolog.)
4.2.2
Tcl/Tk
Tcl/Tk was much less impressive. Instability on the Windows platform. The current Windows implementation of Tcl/Tk 8.1 has a memory allocation bug that does not free memory and system resources upon exit of the Tcl shell. During the development of the Pelog GUI, it was typically only possible to restart the Tcl shell about four times before the entire operating system would crash, taking all of my unsaved work with it. It made the modify-and-test development cycle, for which scripting languages are so revered, virtually useless. While the severity of this problem should be a high priority, according to the Tcl/Tk FAQ file on the web, this problem has existed at least since version 7.6. Since no application should require frequent rebooting of the operating system, Tcl as it currently stands in highly inadequate for full-scale commercial development for Windows. Language issues. Tcl does not scale well. Most Tcl programs, including those provided as examples, rely on archaic language features such as global variables. While it is possible to divide a project into multiple source files, there is no method to deal with name space problems. For this reason, the Tcl language would impose a considerable amount of unnecessary manual housekeeping on part of the programmer to go beyond the complexity of a simple program such as the Pelog-GUI.
5
A Data Structure for Encoding Incomplete Musical Fragments in Prolog 5.1
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 Brinkman’s 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 McGee’s 45
CHAPTER 5. DATA STRUCTURE
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.
Figure 5.1: An example score
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.
CHAPTER 5. DATA STRUCTURE
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 , , , ) )
Table 5.1: Internal representation of example score
CHAPTER 5. DATA STRUCTURE
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.
Figure 5.2: Index values of the example score
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. O’Keefe 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.
CHAPTER 5. DATA STRUCTURE
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.
CHAPTER 5. DATA STRUCTURE
Figure 5.3: Pitch and note class values in the example score
Figure 5.4: Duration values in the example score
50
CHAPTER 5. DATA STRUCTURE
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.
Figure 5.5: Time values in the example score
5.2.6
Previous and Next
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
CHAPTER 5. DATA STRUCTURE
Figure 5.6: Previous references in the example score
Figure 5.7: Next references in the example score
52
CHAPTER 5. DATA STRUCTURE
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 & McGee’s [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.
Figure 5.8: The time-path of the example score
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)
CHAPTER 5. DATA STRUCTURE
54
Figure 5.9: The vertical relationships of the example score
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 other field is reserved for future use.
6
Code and Testing 6.1
The Pelog-GUI
6.1.1
pelog-gui.tcl
Code # # PELOG−GUI # ############################################### # 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
CHAPTER 6. CODE AND TESTING
56
s e t toolbarframe $Parent . buttons frame $ t o o l b a r f r a m e pack $ t o o l b a r f r a m e \ −a n c h o r nw \ −s i d e t o p \ −expand 0 \ −f i l l x \ −padx 5 \ −pady 3 # Menu d e f i n i t i o n s s e t m e n u f i l e $menuframe . f i l e s e t Menu String ( $menufile ) { {{ t e a r o f f } { no } { } } {{command } { New } \ {−command f i l e n e w − a c c e l e r a t o r "Ctrl+N" −u n d e r l i n e 0 } } {{command } { Open . . . } \ {−command f i l e o p e n − a c c e l e r a t o r "Ctrl+O" −u n d e r l i n e 0 } } {{command } { Open URL . . . } \ {−command f i l e o p e n u r l − a c c e l e r a t o r "Ctrl+U" −u n d e r l i n e 5 } } {{command } { R e v e r t } \ {−command f i l e r e v e r t − a c c e l e r a t o r "Ctrl+R" −u n d e r l i n e 0 } } {{ s e p a r a t o r } { } { } } {{command } { Save } \ {−command f i l e s a v e − a c c e l e r a t o r "Ctrl+S" −u n d e r l i n e 0 } } {{command } { Save As . . . } \ {−command f i l e s a v e a s − a c c e l e r a t o r "Ctrl+W" −u n d e r l i n e 5 } } {{ s e p a r a t o r } { } { } } {{command } { E x i t } \ {−command f i l e e x i t − a c c e l e r a t o r "Alt+F4" −u n d e r l i n e 1 } } } construct menu $menufile F i l e $Menu String ( $menufile ) s e t m e n u e d i t $menuframe . e d i t s e t Menu String ( $menuedit ) { {{ t e a r o f f } { no } { } } {{command } { Cut } \ {−command e d i t c u t − a c c e l e r a t o r "Ctrl+X" −u n d e r l i n e 2 } } {{command } { Copy } \ {−command e d i t c o p y − a c c e l e r a t o r "Ctrl+C" −u n d e r l i n e 0 } } {{command } { P a s t e } \ {−command e d i t p a s t e − a c c e l e r a t o r "Ctrl+V" −u n d e r l i n e 0 } } {{command } { C l e a r } \ {−command e d i t c l e a r − a c c e l e r a t o r "Del" −u n d e r l i n e 3 } } } construct menu $menuedit E dit $Menu String ( $menuedit ) s e t menuview $menuframe . v i e w s e t M e n u S t r i n g ( $menuview ) { {{ t e a r o f f } { no } { } } {{command } { Look } \
CHAPTER 6. CODE AND TESTING
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
. new " images / filenew .gif" \ . open " images / fileopen .gif" \ . s a v e " images / filesave .gif" \ . spacer0 . c u t " images / editcut .gif" \ . copy " images / editcopy .gif" \ . p a s t e " images / editpaste .gif" \ . spacer1 . l o o k " images / viewlook .gif" \ . h e a r " images / viewhear .gif" \ . spacer2 . go " images / rulesapply .gif" \ . spacer3
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
CHAPTER 6. CODE AND TESTING
construct spacer $toolbarframe . spacer4 t e x t $ t o o l b a r f r a m e . t e x t − h e i g h t 1 − padx 5 pack $ t o o l b a r f r a m e . t e x t \ −a n c h o r w \ −s i d e l e f t \ −expand 1 \ −f i l l x # Main window s e t t i n g s wm g e o m e t r y . m ai nfr a m e 6 4 0 x480 wm m i n s i z e . m ain fr a m e 3 2 0 2 4 0 # Text e d i t o r c o n s t r u c t e d i t o r . m a i n fr a m e # Ready t o go . . . wm d e i c o n i f y . m ain f ram e file new update rule set update } ############################################### # MENU CONSTRUCTION ############################################### # c o n s t r u c t m e n u ad ds a new menu t o t h e menubar p r o c c o n s t r u c t m e n u { Name l a b e l c m d l i s t } { g l o b a l PG menubutton $Name \ −t e x t $ l a b e l \ −u n d e r l i n e 0 i n c r PG ( menuCount ) ; s e t newmenu $Name . m$ PG ( menuCount ) $Name c o n f i g u r e −menu $newmenu c a t c h " destroy $newmenu " e v a l "menu $newmenu " e v a l [ l i s t a d d i t e m s t o m e n u $newmenu $ c m d l i s t ] pack $Name \ −a n c h o r nw \ −expand 0 \ −i p a d x 4 \ −s i d e l e f t } # a d d i t e m s t o m e n u ad ds i n d i v i d u a l i t e m s t o a g i v e n menu # i n t h e menu b a r . T h i s p r o c e d u r e i s c a l l e d by c o n s t r u c t m e n u p r o c a d d i t e m s t o m e n u { menubutton c m d L i s t } {
58
CHAPTER 6. CODE AND TESTING global
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 \
CHAPTER 6. CODE AND TESTING −a n c h o r nw \ −s i d e l e f t \ −expand 0 \ −f i l l x } # c r e a t e s a spacer f o r the t o o l b a r p r o c c o n s t r u c t s p a c e r { Name } { l a b e l $Name − t e x t " " pack $Name \ −a n c h o r nw \ −s i d e l e f t \ −expand 0 \ −f i l l x } ############################################### # EDITOR CONSTRUCTION ############################################### # c r e a t e s t h e t e x t e d i t o r and s c r o l l b a r p r o c c o n s t r u c t e d i t o r { Name } { f r a m e $Name . t e x t F r a m e pack $Name . t e x t F r a m e \ −a n c h o r sw \ −expand 1 \ − f i l l both \ −s i d e bottom f r a m e $Name . t e x t F r a m e . r i g h t pack $Name . t e x t F r a m e . r i g h t \ −a n c h o r s e \ −expand 0 \ −f i l l y \ −s i d e r i g h t s c r o l l b a r $Name . t e x t F r a m e . r i g h t . v e r t S c r o l l b a r \ −command " $Name . textFrame .left.text yview " \ −w i d t h 1 2 pack $Name . t e x t F r a m e . r i g h t . v e r t S c r o l l b a r \ −a n c h o r c e n t e r \ −expand 0 \ −f i l l y \ −s i d e r i g h t f r a m e $Name . t e x t F r a m e . l e f t pack $Name . t e x t F r a m e . l e f 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
60
CHAPTER 6. CODE AND TESTING
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 ###############################################
CHAPTER 6. CODE AND TESTING # c l e a r s t h e t e x t e d i t o r and f i l e n a m e proc f i l e n e w {} { g l o b a l PG 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 s e t PG ( f i l e ) "" update titlebar update } # loads a f i l e i n t o the e d i t o r proc f i l e o p e n {} { g l o b a l PG file new set
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
CHAPTER 6. CODE AND TESTING
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 ) ]
CHAPTER 6. CODE AND TESTING
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
CHAPTER 6. CODE AND TESTING
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 ? } ]
CHAPTER 6. CODE AND TESTING
66
i f { $outcome == " cancel " } { r e t u r n } save temp s e t o u t f i l e [ s t r i n g t r i m r i g h t $ PG ( f i l e ) . gmn ] o u t . gmn c a t c h "exec plcon .exe -t [ main ]. -g go (’ temp.gmn ’, ’ $outfile ’, $_PG( rules ))." i f { $ PG ( h a s s l a v e ) == "" } { s e t PG ( r u n s l a v e ) [ i n t e r p c r e a t e r u n s l a v e ] r u n s l a v e e v a l { l o a d { } Tk} r u n s l a v e e v a l { s o u r c e p e l o g−g u i . t c l } runslave eval splash button s e t PG ( h a s s l a v e ) y e s } $ PG ( r u n s l a v e ) e v a l s e t PG ( f i l e ) $ o u t f i l e $ PG ( r u n s l a v e ) e v a l f i l e r e v e r t } proc r u l e s b r o w s e {} { g l o b a l PG PG ( r u l e s ) [ 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 . p r s \ −f i l e t y p e s {{{ Pelog r u l e s e t } { . prs }}} \ −p a r e n t . m ai n fr am e \ −t i t l e { S e l e c t a Pelog r u l e s e t . . . } \ − i n i t i a l f i l e $ PG ( r u l e s ) ] update rule set
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
CHAPTER 6. CODE AND TESTING
. m ain f ram e . b u t t o n s . t e x t d e l e t e 1 . 0 end . m ain f ram e . b u t t o n s . t e x t i n s e r t end $ PG ( r u l e s ) update } p r o c s h o w e r r o r { message } { tk messageBox \ −t y p e ok \ −message $message \ −p a r e n t . m ai n fr am e \ −i c o n e r r o r \ −t i t l e { Error } } ############################################### # INIT FUNCTIONS ############################################### f o r e a c h g l o b a l v a r [ i n f o g l o b a l s ∗PG ∗ ] { global $globalvar c a t c h " unset $globalvar " dummy } f o r e a c h g l o b a l v a r [ i n f o g l o b a l s ∗ pg ∗ ] { global $globalvar c a t c h " unset $globalvar " dummy } set set set set set set set set set
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 ) ""
c a t c h " destroy . mainframe " destroy . toplevel splash
6.2 6.2.1
The Core Interpreter pelog.pl
Code /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ PRELIMINARY MAIN MODULE
67
CHAPTER 6. CODE AND TESTING
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.
t e s t : − go ( ’ thakar -p176.gmn’ , ’ thakar - p176out .gmn’ , ’rule-sets/ modal_counterpoint . go ( I n F i l e , O u t F i l e , R u l e F i l e ) : − load rules ( RuleFile ) , ! , l o a d s c o r e ( I n F i l e , T , TimePath ) , ! , a l l s o l u t i o n s (T , TimePath ) , b e s t r e s u l t ( Penalty , Best ) , n l , n l , w r i t e ( ’ SUCCEEDED ! ==========================>’ ) , n l , w r i t e ( ’ Overall Penalty : ’ ) , w r i t e ( P e n a l t y ) , n l , n l , p r i n t t r e e ( Best ) , ! , s a v e s c o r e ( O u t F i l e , T , TimePath ) . p r i n t t r e e (T) : − t r e e t o l i s t (T , L i s t ) , f o r a l l ( member ( I t e m , L i s t ) , ( w r i t e ( I t e m ) , n l ) ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 ,
CHAPTER 6. CODE AND TESTING
69
w r i t e ( ’Event tree as input : ’ ) , n l , p r i n t t r e e (T ) , n l , n l , w r i t e ( ’Tags : ’ ) , l i s t i n g ( global ), a p p l y r u l e s (T , TimePath , R u l e s , p e n a l t y ( 0 , P ) ) , s t o r e r e s u l t (P , T ) . apply rules ( , [ ] , , p e n a l t y (P , P ) ) . a p p l y r u l e s (T , [ ( , I n d e x ) | TimePath ] , R u l e s , p e n a l t y ( P e n In , PenOut ) ) : − g e t l a b e l ( Index , T, E ) , e v e r t i c a l (E , V ) , a p p l y r u l e s 0 (T , E , V , R u l e s , p e n a l t y ( P e n I n , PenOut0 ) ) , block ( Index , a p p l y r u l e s (T , TimePath , R u l e s , p e n a l t y ( PenOut0 , PenOut ) ) , Y) −> (Y == f a i l −> f a i l ; t r u e ) . a p p l y r u l e s 0 (T , E , [ ] , R u l e s , P e n a l t y ) : − p a c k a g e p a r a m e t e r (T , E , [ ] , Param ) , a p p l y r u l e s 1 ( E , Param , R u l e s , P e n a l t y ) . a p p l y r u l e s 0 (T , E , [ V e r t ] , R u l e s , P e n a l t y ) : − p a c k a g e p a r a m e t e r (T , E , V e r t , Param ) , a p p l y r u l e s 1 ( E , Param , R u l e s , P e n a l t y ) . a p p l y r u l e s 0 (T , E , [ VertHead | V e r t T a i l ] , R u l e s , p e n a l t y ( P e n I n , PenOut ) ) : − p a c k a g e p a r a m e t e r (T , E , VertHead , Param ) , a p p l y r u l e s 1 ( E , Param , R u l e s , p e n a l t y ( P e n I n , PenOut0 ) ) , a p p l y r u l e s 0 (T , E , V e r t T a i l , R u l e s , p e n a l t y ( PenOut0 , PenOut ) ) .
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 ) ,
CHAPTER 6. CODE AND TESTING
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 i−math ) . l o a d e d ( message ) . loaded ( preprocessor ). l o a d e d ( r u l e−d e f ) . loaded ( score ). loaded ( trees ).
bi-math.pl
Code /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ BI−DIRECTIONAL MATH LIBRARY G e n e r a t i v e math f u n c t i o n s
CHAPTER 6. CODE AND TESTING
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 g−p 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 ) ,
CHAPTER 6. CODE AND TESTING Y < Z, g e t i n t e g e r (F ) , F \= 0, X i s Z ∗ F + Y. g e t i n t e g e r ( I ) :− member ( I , [ 0 , 1 , − 1 , 2 , − 2 , 3 , − 3 , 4 , − 4 , 5 , − 5 , 6, −6, 7, −7, 8, −8, 9, −9, 10, −10, 11, −11, 12, −12]).
Testing 1 ? − [ b i−math ] . b i−math 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
CHAPTER 6. CODE AND TESTING X = 0 Y = 0 ; X = 1 Y = 1 ; X = 2 Y = 2 ; No 11 ?− f a c t o r ( 1 , Y , 3 , F ) . Y = 1 F = 0 ; No 12 ?− f a c t o r ( − 1 , Y , − 3 , F ) . Y = −4 F = −1 ; No 13 ?− f a c t o r (X , 3 , 5 , F ) . X = 3 F = 0 ; X = 8 F = 1 ; X = −2 F = −1 ; X = 13 F = 2 ; X = −7 F = −2 ; X = 18 F = 3 ; X = −12 F = −3 ; X = 23 F = 4 ; X = −17 F = −4 ;
73
CHAPTER 6. CODE AND TESTING X = 28 F = 5 ; X = −22 F = −5 ; X = 33 F = 6 ; X = −27 F = −6 ; X = 38 F = 7 ; X = −32 F = −7 ; X = 43 F = 8 ; X = −37 F = −8 ; X = 48 F = 9 ; X = −42 F = −9 ; X = 53 F = 10 ; X = −47 F = −10 ; X = 58 F = 11 ; X = −52 F = −11 ; X = 63 F = 12 ; X = −57 F = −12 ;
6.2.4 Code
preprocessor.pl
74
CHAPTER 6. CODE AND TESTING
75
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ PREPROCESSOR FOR RULE FILES M i c h a e l Droettboom May 1 9 9 9 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗ T h i s p r e−p 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 ’ meta−v 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 e−t i m e t o t h e a c t u a l v a r i a b l e s . These meta−v 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 META−VARIABLES 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 ” meta−v 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 . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
CHAPTER 6. CODE AND TESTING
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 .
CHAPTER 6. CODE AND TESTING
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 e−s 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 ) .
r u l e ( ’ First -Last- Penultimate ’ , c h r o n o l o g i c a l , a r g s (A , B , C , D , E , F , G, H, I , J , K, L)) :− ( A first event −> m c f i r s t p i t c h (A) ; A last event −> m c l a s t p i t c h (A) ; K penultimate event −> m c p e n u l t i m a t e p i t c h (A) ; true ). r u l e ( ’ leading tone leads to tonic ’ , t e n d e n c y , a r g s (A , B , C , D, E , F , G, H, I , J , K, L)) :− ( B leading tone −> A t o n i c
CHAPTER 6. CODE AND TESTING
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 N˜M ). 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 N˜M ). 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 ( (
CHAPTER 6. CODE AND TESTING
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 comment’Step - 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 comment’X - 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 ) .
CHAPTER 6. CODE AND TESTING
80
r u l e d e f ( ’ leading tone leads to tonic’ , t e n d e n c y , 0 ) . r u l e d e f ( ’ Aeolian ’ , ’ special cases for modes ’ , 0 ) . r u l e d e f ( ’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). rule_def (’ R e p e a t e d n o t e ’, ’ melodic intervals ’ , 1 ) . r u l e d e f ( ’Less good intervals ’ , ’ melodic i n t e r v a l s ’, 10). rule_def (’ I n s c a l e ’, scale , 0). rule_def (’Good i n t e r v a l s ’, ’ h a r m o n i c i n t e r v a l s ’, 0). rule_def (’ D i m i n i s h e d 5 t h ’, ’ harmonic intervals ’ , 0 ) . r u l e d e f ( ’ Perfect intervals ’ , ’ harmonic i n t e r v a l s ’, 10). rule_def (’ U n i s o n ’, ’ h a r m o n i c i n t e r v a l s ’, 20). r u l e d e f ( ’ Penultimate maj6 or min3’ , ’ avoid harmonic intervals ’ , 0 ) . r u l e d e f ( ’ Avoid’ , ’two leading tones’ , 0 ) . r u l e d e f ( ’Step - step’ , contour , 1 ) . r u l e d e f ( ’X - skip : same direction ’ , c o n t o u r , 1 ) . r u l e d e f ( ’Skip - skip : opposite direction ’ , c o n t o u r , 3 ) . r u l e d e f ( ’ Ensure not’ , ’ outline a tritone ’ , 1 ) . r u l e d e f ( ’Two skips t h a t form a t r i a d ’, contour , 5). rule_def (’ A v o i d t h e r e p e a t ’, ’ r e p e a t f i r s t ’, 0). rule_def (’ L e t i t r e p e a t ’, ’ r e p e a t f i r s t ’, 10). r u l e d e f ( ’ Avoid two’ , ’ repeat patterns ’ , 0 ) . r u l e d e f ( ’ Avoid three ’ , ’ repeat patterns ’ , 1 0 ) . r u l e d e f ( ’ Avoid four’ , ’ repeat patterns ’ , 2 0 ) . r u l e d e f ( ’In range ’ , r a n g e , 0 ) . Yes
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 ) .
CHAPTER 6. CODE AND TESTING
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 e−s 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 t−L a s t−P e n u l t i m a t e , c h r o n o l o g i c a l ) ] ] ]
CHAPTER 6. CODE AND TESTING
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 , end−m 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) : −
CHAPTER 6. CODE AND TESTING
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 ) .
CHAPTER 6. CODE AND TESTING
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
trees.pl is from the Quintus Prolog library.
6.3 6.3.1
The GUIDO parser/grammar parser.pl
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’ ) . /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
CHAPTER 6. CODE AND TESTING
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 ] ) } ) ;
CHAPTER 6. CODE AND TESTING
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) )
CHAPTER 6. CODE AND TESTING ; ;
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 ) } .
% PREDICATES TO HANDLE CERTAIN TAGS h a n d l e t a g (X , Y) : − % This i s f o r tags not r e l a t e d to p a r t s n o n v a r (X ) , member (X , [ k e y , s c a l e , p i t c h s y s t e m , m e t e r , l a b e l , t i t l e , composer , c o m m e n t s t y l e a t o m t o t e r m (Y , Y0 , ), g l o b a l s e t ( t a g (X ) , Y0 ) . h a n d l e t a g (X , Y) : − % This i s f o r tags that are r e l a t e d to p a r t s n o n v a r (X ) , member (X , [ r a n g e ] ) , global ( part , P), ), a t o m t o t e r m (Y , Y0 , g l o b a l s e t ( t a g ( ( X) ˜P ) , Y0 ) . handle tag ( ,
) . % 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 } .
CHAPTER 6. CODE AND TESTING
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 ,
CHAPTER 6. CODE AND TESTING
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 )
CHAPTER 6. CODE AND TESTING ; ;
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 ”
CHAPTER 6. CODE AND TESTING
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 ) .
CHAPTER 6. CODE AND TESTING
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
tag ([]) −−> []. tag ( [ ( range ˜ , )| ]) −−> !, []. tag ( [ ( s c a l e , )| ]) −−> !, []. t a g ( [ ( X , Y ) | T a i l ]) −−> [ ’\\’], [ X ], [’<\"’], [ Y ], [’\"> ’], gg tag ( Tail ) .
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 ) ,
CHAPTER 6. CODE AND TESTING
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 ( [ end−m 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
CHAPTER 6. CODE AND TESTING
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 r−t 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:
CHAPTER 6. CODE AND TESTING
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 ) end−m 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 ) .
CHAPTER 6. CODE AND TESTING
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 f−Tag
{ [ \ 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 g−1 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
CHAPTER 6. CODE AND TESTING
97
\ 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 −1∗1/2 \ t e x t<"37">t e x t c 0 ∗ 1 / 2 ] }
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 )
CHAPTER 6. CODE AND TESTING
98
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 , G2927 , G2928 , G2929 ) 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 , G2977 , G2978 , G2979 ) 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 , G3015 , G3016 , G3017 ) 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 , G3053 , G3054 , G3055 ) 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 , G3091 , G3092 , G3093 ) 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 , G3141 , G3142 , G3143 ) 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 , G3179 , G3180 , G3181 ) e v e n t ( 2 3 , 1 , 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 , G3229 , G3230 , G3231 ) 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 , G3267 , G3268 , G3269 ) 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 , G3305 , G3306 , G3307 ) 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 , G3343 , G3344 , G3345 ) 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 , G3393 , G3394 , G3395 ) 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 , G3652 , G3653 , G3654 ) 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 , G3690 , G3691 , G3692 ) 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 , G3728 , G3729 , G3730 ) 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 , G3766 , G3767 , G3768 ) 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 , G3804 , G3805 , G3806 ) 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 , G3842 , G3843 , G3844 ) 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 , G3880 , G3881 , G3882 ) 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 , G3918 , G3919 , G3920 ) 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 , G3956 , G3957 , G3958 ) 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 , G3994 , G3995 , G3996 ) end−m a r 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 2 o u t . gmn Succeeded .
simple3 IN % s i m p l e GUIDO e x a m p l e
CHAPTER 6. CODE AND TESTING
99
% t h i r d enhancement % adding s l u r s voice % and n e w L i n e−Tag { [ \ 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 g−1 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 −1∗1/2 \ t e x t<"37">t e x t c 0 ∗ 1 / 2 ] }
CHAPTER 6. CODE AND TESTING
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 ,
CHAPTER 6. CODE AND TESTING
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 ) end−m 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
The Pelog library main.pl
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 ) .
CHAPTER 6. CODE AND TESTING :− :− :− :− :− :− :− :− :−
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 ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
CHAPTER 6. CODE AND TESTING
103
ADDING COMMENTS TO EVENTS Comment l i s t s a r e i m p l e m e n t e d a s i m p e r f e c t P r o l o g l i s t s ( where the l a s t element in the l i s t i s a v a r i a b l e ) . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ add add add add
comment ( E , Comment ) : − e comment ( E , [ Comment | ] ) , ! . comment ( E , Comment ) : − e comment ( E , X ) , add comment0 ( Comment , X ) , ! . comment0 ( Comment , [ Comment | ] ) : − ! . comment0 ( Comment , [ | T a i l ] ) : − add comment0 ( Comment , T a i l ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 USER−READABLE 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 r−r 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
CHAPTER 6. CODE AND TESTING
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 ,
CHAPTER 6. CODE AND TESTING
6.4.3
105
event.pl
Code /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ EVENT LIBRARY M i c h a e l Droettboom May 1 9 9 9 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ T h i s i s t h e c o r e module o f t h e e v e n t d a t a s t r u c t u r e . For more i n f o r m a t i o n on t h i s d a t a s t r u c t u r e , p l e a s e s e e t h e i m p l e m e n t a t i o n documentation . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ REQUIRED LIBRARIES ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ :− :− :− :−
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 ) ,
CHAPTER 6. CODE AND TESTING
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 t−i 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 on−t 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 ( event ( Index , , , , , p a r t ( event ( , Part , , , , , pitch ( event ( , , Pitch , , , duration ( event ( , , , Duration time ( event ( , , , , Time , , prev ( event ( , , , , , Prev , v e r t i c a l ( event ( , , , , , ,
,
,
) , Index ) , Part ) . , , , , , ) , Pitch , , , , , , , ), , , , , ) , Time ) . , , , , ) , Prev ) . Vertical , , , , ), ,
,
,
,
,
,
).
,
). Duration ) .
Vertical ).
CHAPTER 6. CODE AND TESTING e e e e
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 e−t 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 e−t 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 ) .
get prev event ( , start , start ) :− !. g e t p r e v e v e n t (T , E , EP ) : − e p r e v (E , IPre v ) , g e t p r e v e v e n t 0 (T , I P r e v , EP ) . get prev event0 ( , start , start ) :− !. g e t p r e v e v e n t 0 (T , N , EP ) : − i n t e g e r (N ) , ! , g e t l a b e l (N , T , EP ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 , [ ] ) .
CHAPTER 6. CODE AND TESTING
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
The g l o b a l l i b r a r y can be u s e d t o e x t e n d t h e GUIDO p a r s e r t o r e c o g n i z e new t a g s . S i m p l y add a g l o b a l d e f a u l t p r e d i c a t e t o y o u r Pelog r u l e s e t f i l e . g l o b a l d e f a u l t p r e d i c a t e s a r e o f t h e form : g l o b a l d e f a u l t ( t a g ( tagname ) , d e f a u l t ) tagname i s t h e name o f t h e t a g d e f a u l t i s the d e f a u l t v a l u e f o r the tag i f the tag i s not present in the input f i l e The v a l u e o f t h e t a g can be u s e d i n y o u r r u l e s a t r u n t i m e u s i n g the tag p r e d i c a t e . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ PREDICATE SETTINGS ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ :− m u l t i f i l e global /2, g l o b a l d e f a u l t /2.
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 , ˜ ) .
CHAPTER 6. CODE AND TESTING
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 ’ .
CHAPTER 6. CODE AND TESTING
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 e−c 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 )
For e x a m p l e , min ˜ 3 i s a m i n o r t h i r d and maj ˜ 7 i s a m a j o r s e v e n t h . The sem t y p e r e t u r n s t h e s i z e f i e l d a s t h e number o f s e m i t o n e s , r e g a r d l e s s o f n o t e names . T h e r e f o r e , maj ˜ 3 i s e q u i v a l e n t t o sem ˜ 4 . Note t h a t e i t h e r t h e t y p e o r s i z e f i e l d may be l e f t v a r i a b l e u s i n g P r o l o g ’ s anonymous v a r i a b l e , t h e u n d e r s c o r e . Therefore , ˜3 w i l l s p e c i f y dim ˜ 3 , min ˜ 3 , maj ˜ 3 and aug ˜ 3 . I n t e r v a l s can a l s o be s p e c i f i e d u s i n g o t h e r k e y w o r d s : type −−−− unison step skip consonant dissonant
meaning −−−−−−− U n i s o n ( t h e two g i v e n n o t e s hav e t h e same p i t c h ) A second Any i n t e r v a l l a r g e r t h a n a s e c o n d Any t h i r d s , f i f t h s , s i x t h s o r o c t a v e s t h a t a r e n o t d i m i n i s h e d o r augmented Any s e c o n d s , f o u r t h s o r s e v e n t h s
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
CHAPTER 6. CODE AND TESTING
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 ) : − ! ,
CHAPTER 6. CODE AND TESTING
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 ) ,
CHAPTER 6. CODE AND TESTING
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 ) .
CHAPTER 6. CODE AND TESTING i n t e r v a l ( I , up , PC , NC) : − ( g l o b a l ( tag ( p it c h s y s t e m ) , d i a t o n i c ) ; g l o b a l ( t a g ( p i t c h s y s t e m ) , german ) ; g l o b a l ( tag ( pi t c h s y s t e m ) , s o l f e g e ) ; g l o b a l ( tag ( pi t c h s y s t e m ) , a l l ) ) , i n t e r v a l t o n a l ( I , PC , NC ) . i n t e r v a l ( sem˜ I , up , I , ) : − g l o b a l ( tag ( pi t c h s y s t e m ) , atonal ) . i n t e r v a l ( sem˜ I , down , PC , ) : − g l o b a l ( tag ( pi t c h s y s t e m ) , atonal ) , o p p o s i t e ( I , PC ) . i n t e r v a l ( I , down , PC , NC) : − % a u t o m a t i c a l l y r e f l e c t i n t e r v a l s to % downard c o n t o u r i n t e r v a l ( I , up , PC1 , NC1 ) , o p p o s i t e ( PC1 , PC ) , o p p o s i t e (NC1 , NC ) . % BASIC INTERVALS WITHIN THE FIRST OCTAVE i n t e r v a l b a s i c ( per ˜ 1 , 0 , 0 ) . % p e r f e c t unison i n t e r v a l b a s i c ( chr ˜ 1 , 1 , 0 ) . % chromatic i n t e r v a l interval interval interval interval
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
CHAPTER 6. CODE AND TESTING
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
CHAPTER 6. CODE AND TESTING g e t i n t e g e r (F ) , member ( I L o c a l , [ 3 , 5 , 6 , 8 ] ) , factor ( I , ILocal , 7 , F), i n t e r v a l t o n a l ( Type ˜ I , PC , NC ) , Type \= dim , Type \= aug , Type \= t r i . i n t e r v a l t o n a l ( d i s s o n a n t , PC , NC) : − i n t e r v a l t o n a l ( t r i ˜ , PC , NC ) . i n t e r v a l t o n a l ( d i s s o n a n t , PC , NC) : − g e t i n t e g e r (F ) , member ( I L o c a l , [ 2 , 4 , 7 ] ) , factor ( I , ILocal , 7 , F), i n t e r v a l t o n a l ( ˜ I , PC , NC ) .
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
CHAPTER 6. CODE AND TESTING
X = pitch (79, 47) ; X = pitch (79, 47) ; X = pitch (91, 54) ; X = pitch (91, 54) ; X = pitch (103, 61) ; X = pitch (103, 61) ; X = pitch (115, 68) ; X = pitch (115, 68) ; X = pitch (127, 75) ; X = pitch (127, 75) ; X = pitch (139, 82) ; X = pitch (139, 82) ; X = pitch (151, 89) ; No ?− p i t c h ( 1 2 , 7 ) : X a i n t e r v a l dim ˜ 6 c o n t o u r C . X = pitch (19, 12) C = up ; X = pitch (5, 2) C = down ; No ?− p i t c h ( 0 , 0 ) : p i t c h ( 2 , 1 ) a i n t e r v a l I . I = maj ˜ 2 c o n t o u r up ; I = s t e p c o n t o u r up ; I = d i s s o n a n t c o n t o u r up ; No ?− p i t c h ( 0 , 0 ) : p i t c h ( 4 , 2 ) a i n t e r v a l I . I = maj ˜ 3 c o n t o u r up ; I = s k i p c o n t o u r up ;
117
CHAPTER 6. CODE AND TESTING
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
CHAPTER 6. CODE AND TESTING
119
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ This l i b r a r y d e a l s with i n f o r m a t i o n about i n d i v i d u a l p a r t s ( monophonic l i n e s ) i n t h e s c o r e . A p a r t can be r e f e r e n c e d t o by number , i n w h ic h c a s e t h e t o p p a r t i s p a r t 1 , t h e n e x t h i g h e s t i s p a r t 2 , e t c . A p a r t can a l s o be r e f e r e n c e d by a name d e f i n e d by t h e p a r t d e f f a c t p r e d i c a t e . A d d i t i o n a l p a r t names can be d e f i n e d by a d d i n g p a r t d e f p r e d i c a t e s t o t h e r u l e s e t f i l e . p a r t d e f p r e d i c a t e s hav e t h e form : p a r t d e f ( name , p a r t n o , numparts ) name i s t h e name o f t h e p a r t p a r t n o i s t h e c o r r e s p o n d i n g p a r t number numparts i s t h e number o f p a r t s ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ /∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ REQUIRED LIBRARIES ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/ :− e n s u r e l o a d e d ( event ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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) : −
CHAPTER 6. CODE AND TESTING
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 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ I n P e l o g , p i t c h names f o l l o w t h e same c o n v e n t i o n a s GUIDO ( s e e t h e GUIDO Doc u me nt ati on f o r more i n f o r m a t i o n . ) A p i t c h name i s d e f i n e d a s a l e t t e r { c , d , e , f , g , a , b } o p t i o n a l l y f o l l o w e d by an a c c i d e n t a l {#, &, ##, &&}. An o c t a v e i s an i n t e g e r where o c t a v e 1 i s t h e o c t a v e c o n t a i n i n g A440Hz .
CHAPTER 6. CODE AND TESTING
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 ) .
CHAPTER 6. CODE AND TESTING
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 h−C l a s s ) % non−c o n t i n u o u s p i t c h−c l a s s n o t e c l a s s ( ? P i t c h , ? Note−C l a s s ) % non−c o n t i n u o u s n o t e−c 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 ) ,
CHAPTER 6. CODE AND TESTING
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 h−C l a s s , ? Note−C 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
CHAPTER 6. CODE AND TESTING
124
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 PITCHES f o r DIATONIC , GERMAN and ATONAL . pitch pitch pitch pitch pitch pitch pitch pitch
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 ) .
% DIATONIC and GERMAN p i t c h t o n a l (T , PC , NC) : − p i t c h b a s i c (T , PC , NC ) . p i t c h t o n a l ( (T) & , PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 1 ) mod 1 2 . p i t c h t o n a l ( (T) # , PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) ,
CHAPTER 6. CODE AND TESTING PC i s ( PC1 + 1 3 ) mod 1 2 . p i t c h t o n a l ( (T)&&, PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 0 ) mod 1 2 . p i t c h t o n a l ( (T)##, PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 4 ) mod 1 2 .
% 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
CHAPTER 6. CODE AND TESTING
126
1999 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ I n P e l o g , p i t c h names f o l l o w t h e same c o n v e n t i o n a s GUIDO ( s e e t h e GUIDO Doc u m e n ta t io n f o r more i n f o r m a t i o n . ) A p i t c h name i s d e f i n e d a s a l e t t e r { c , d , e , f , g , a , b } o p t i o n a l l y f o l l o w e d by an a c c i d e n t a l {#, &, ##, &&}. An o c t a v e i s an i n t e g e r where o c t a v e 1 i s t h e o c t a v e c o n t a i n i n g A440Hz . ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 ) .
CHAPTER 6. CODE AND TESTING
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 h−C l a s s ) % non−c o n t i n u o u s p i t c h−c l a s s n o t e c l a s s ( ? P i t c h , ? Note−C l a s s ) % non−c o n t i n u o u s n o t e−c 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)
CHAPTER 6. CODE AND TESTING
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 ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
CHAPTER 6. CODE AND TESTING
129
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ PRIVATE PREDICATES ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗/
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ USER and GUIDO PITCH NAMES p i t c h n a m e ( ? Text , ? P i t c h−C l a s s , ? Note−C 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 PITCHES f o r DIATONIC , GERMAN and ATONAL . pitch pitch pitch pitch pitch pitch pitch pitch
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 ) .
CHAPTER 6. CODE AND TESTING
% DIATONIC and GERMAN p i t c h t o n a l (T , PC , NC) : − p i t c h b a s i c (T , PC , NC ) . p i t c h t o n a l ( (T) & , PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 1 ) mod 1 2 . p i t c h t o n a l ( (T) # , PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 3 ) mod 1 2 . p i t c h t o n a l ( (T)&&, PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 0 ) mod 1 2 . p i t c h t o n a l ( (T)##, PC , NC) : − p i t c h b a s i c (T , PC1 , NC ) , PC i s ( PC1 + 1 4 ) mod 1 2 .
% 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
CHAPTER 6. CODE AND TESTING pitch pitch pitch pitch pitch
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 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ Range can be s p e c i f i e d i n a number o f ways . − As an i n t e r v a l . See t h e i n t e r v a l l i b r a r y ( page 2 2 ) f o r more information . − As a s e t o f two e v e n t s l o w e v e n t : h i g h e v e n t . must be i n s t a n t i a t e d .
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 ) .
CHAPTER 6. CODE AND TESTING
132
: − 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 ( i n t e r v a l ) .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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
CHAPTER 6. CODE AND TESTING
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 ) .
CHAPTER 6. CODE AND TESTING
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 , LPN˜LO , UPN˜UO ) , 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
def ( soprano , c ˜ 1 , c ˜3). def ( alto , f ˜0, g ˜2). def ( tenor , c ˜0, a ˜1). def ( bass , f ˜ −1, f ˜1).
/∗∗∗ 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
X = event ( 4 , 1 , p i t c h ( 5 , 3 ) , dur ( 1 / 4 ) , time ( 9 0 0 0 , 1 2 0 0 0 ) , 3 , [ 1 0 , 1 2 ] , 5 , G1331 , G1332 , G1333 ) : e v e n t ( 8 , 1 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 2 ) , t i m e ( 1 6 5 0 0 , 2 2 5 0 0 ) , 7 , [ 1 3 , 1 0 ] , end , G1507 , G1508 , G1509 ) ; X = p e r ˜ 5 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’ , 3 , X ) .
P a r s e OK
X = event ( 3 , 1 , p i t c h ( 9 , 5 ) , dur ( 1 / 4 ) , time ( 6 0 0 0 , 9 0 0 0 ) , 2 , [ 1 2 ] , 4 , G1272 , G1273 , G1274 ) : e v e n t ( 1 , 1 , p i t c h ( 1 2 , 7 ) , d u r ( 1 / 4 ) , t i m e ( 0 , 3 0 0 0 ) , s t a r t , [ ] , 2 , G1196 , G1197 , G1198 ) ;
CHAPTER 6. CODE AND TESTING
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
X = event ( 1 1 , 2 , p i t c h ( 0 , 0 ) , dur ( 1 / 2 ) , time ( 1 8 0 0 0 , 2 4 0 0 0 ) , 1 0 , [ 8 , 1 3 ] , end , G1699 , G1700 , G1701 ) : e v e n t ( 9 , 2 , p i t c h ( 4 , 2 ) , d u r ( 1 / 2 ) , t i m e ( 0 , 6 0 0 0 ) , s t a r t , [ 1 ] , 1 0 , G1599 , G1600 , G1601 ) ; X = maj ˜ 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 w i t h i n ( ’../ example .gmn’ , 1 1 , a l t o ) .
P a r s e OK
No 1 2 ? − t e s t r a n g e w i t h i n ( ’../ example .gmn’ , 1 1 , t e n o r ) .
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
CHAPTER 6. CODE AND TESTING
136
p i t c h s e t can be any p i t c h s e t name d e f i n e d i n a s c a l e d e f c l a u s e . The P e l o g r u l e s e t programmer can add more s c a l e d e f i n i t i o n s by adding s c a l e d e f f a c t p r e d i c a t e s to the r u l e s e t f i l e . scale def p r e d i c a t e s hav e t h e form : s c a l e d e f ( name , p i t c h l i s t , k e y i n t e r v a l ) . 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 ) .
CHAPTER 6. CODE AND TESTING
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 deg−t 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 Deg−t 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 ) .
CHAPTER 6. CODE AND TESTING
: − 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 non−c 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
CHAPTER 6. CODE AND TESTING
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 e−c 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 ) : − ! .
CHAPTER 6. CODE AND TESTING
140
s c a l e 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 ) : − ! . s c a l e d e f ( n a t u r a l m i n o r , [ c , d , e & , f , g , a & , b & ] , maj ˜ 6 ) : − ! . s c a l e 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 ) : − ! . s c a l e d e f ( d o r i a n , [ c , d , e & , f , g , a , b & , b ] , maj ˜ 2 ) : − ! . s c a l e d e f ( hypo dorian , X , Y) : − s c a l e d e f ( dorian , X , Y ) , ! . s c a l e d e f ( p h r y g i a n , [ c , d & , e & , f , g , a & , b & ] , maj ˜ 3 ) : − ! . s c a l e d e f ( hypo phrygian , X , Y) :− s c a l e d e f ( phrygian , X , Y ) , ! . s c a l e d e f ( l y d i a n , [ c , d , e , f #, g , a , b ] , p e r ˜ 4 ) : − ! . s c a l e d e f ( h y p o l y d i a n , X , Y) :− s c a l e d e f ( l y d i a n , X , Y ) , ! . s c a l e d e f ( m i x o l y di a n , [ c , d , e , f , g , a , b &, b ] , per ˜ 5 ) : − ! . s c a l e d e f ( hypo mixolydian , X , Y) :− s c a l e d e f ( mixolydian , X , Y ) , ! . s c a l e d e f ( a e o l i a n , [ c , d , e & , f , g , a & , b & , a , b ] , maj ˜ 6 ) : − ! . s c a l e d e f ( hy po ae o li an , X , Y) :− s c a l e d e f ( a e o l i a n , X , Y ) , ! . % Note : l o c r i a n w i l l n o t work w i t h modal c o u n t e r p o i n t : i t h a s % no p e r f e c t f i f t h . s c a l e d e f ( l o c r i a n , [ c , d & , e & , f , g & , a & , b & ] , maj ˜ 7 ) : − ! . s c a l e d e f ( i o n i a n , X , Y) : − s c a l e d e f ( major , X , Y ) , ! . s c a l e d e f ( hypo ionian , X , Y) :− s c a l e d e f ( i o n i a n , X , Y ) , ! . 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 ( chromatic sharps , X , Y) :− s c a l e d e f ( chromatic , 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 e t o n e , [ c , d , e , f #, g #, a # ] , none ) : − ! . s c a l e d e f ( whole tone sharps , X , Y) :− s c a l e d e f ( whole tone , X , Y ) , ! . s c a l e d e f ( w h o l e t 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 c a , X , Y) :− s c a l e d e f ( pentatonic , X , Y ) , ! . scale def ( pentatonic b , [ c , d , e , g , a ] , unis ) :− !. scale scale scale scale scale scale scale scale scale
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 c−s 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 ) : − ! .
s c a l e d e f ( b l u e s , [ c , d , e &, e , f , g &, g , a , b & ] , u n i s ) : − ! .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 .
CHAPTER 6. CODE AND TESTING
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 , hypo−l y d i a n , f # ) : − ! .
CHAPTER 6. CODE AND TESTING
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 l−m 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 c−m 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 l−m 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
p i t c h s e t can be any p i t c h s e t name d e f i n e d i n a s c a l e d e f c l a u s e . The P e l o g r u l e s e t programmer can add more s c a l e d e f i n i t i o n s by adding s c a l e d e f f a c t p r e d i c a t e s to the r u l e s e t f i l e . scale def p r e d i c a t e s hav e t h e form : s c a l e d e f ( name , p i t c h l i s t , k e y i n t e r v a l ) .
CHAPTER 6. CODE AND TESTING
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 , ˜ ) .
CHAPTER 6. CODE AND TESTING :− :− :− :−
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 deg−t 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 Deg−t 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
CHAPTER 6. CODE AND TESTING
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 non−c 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 ) : − ! ) ) .
CHAPTER 6. CODE AND TESTING
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 e−c 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 ) : − ! .
s c a l e d e f ( d o r i a n , [ c , d , e & , f , g , a , b & , b ] , maj ˜ 2 ) : − ! . s c a l e d e f ( hypo dorian , X , Y) : − s c a l e d e f ( dorian , X , Y ) , ! . s c a l e d e f ( p h r y g i a n , [ c , d & , e & , f , g , a & , b & ] , maj ˜ 3 ) : − ! .
CHAPTER 6. CODE AND TESTING
147
s c a l e d e f ( hypo phrygian , X , Y) :− s c a l e d e f ( phrygian , X , Y ) , ! . s c a l e d e f ( l y d i a n , [ c , d , e , f #, g , a , b ] , p e r ˜ 4 ) : − ! . s c a l e d e f ( h y p o l y d i a n , X , Y) :− s c a l e d e f ( l y d i a n , X , Y ) , ! . s c a l e d e f ( m i x o l y di a n , [ c , d , e , f , g , a , b &, b ] , per ˜ 5 ) : − ! . s c a l e d e f ( hypo mixolydian , X , Y) :− s c a l e d e f ( mixolydian , X , Y ) , ! . s c a l e d e f ( a e o l i a n , [ c , d , e & , f , g , a & , b & , a , b ] , maj ˜ 6 ) : − ! . s c a l e d e f ( hy po ae o li an , X , Y) :− s c a l e d e f ( a e o l i a n , X , Y ) , ! . % Note : l o c r i a n w i l l n o t work w i t h modal c o u n t e r p o i n t : i t h a s % no p e r f e c t f i f t h . s c a l e d e f ( l o c r i a n , [ c , d & , e & , f , g & , a & , b & ] , maj ˜ 7 ) : − ! . s c a l e d e f ( i o n i a n , X , Y) : − s c a l e d e f ( major , X , Y ) , ! . s c a l e d e f ( hypo ionian , X , Y) :− s c a l e d e f ( i o n i a n , X , Y ) , ! . 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 ( chromatic sharps , X , Y) :− s c a l e d e f ( chromatic , 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 e t o n e , [ c , d , e , f #, g #, a # ] , none ) : − ! . s c a l e d e f ( whole tone sharps , X , Y) :− s c a l e d e f ( whole tone , X , Y ) , ! . s c a l e d e f ( w h o l e t 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 c a , X , Y) :− s c a l e d e f ( pentatonic , X , Y ) , ! . scale def ( pentatonic b , [ c , d , e , g , a ] , unis ) :− !. scale scale scale scale scale scale scale scale scale
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 c−s 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 ) : − ! .
s c a l e d e f ( b l u e s , [ c , d , e &, e , f , g &, g , a , b & ] , u n i s ) : − ! .
/∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ 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 ] ) ,
CHAPTER 6. CODE AND TESTING
148
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. , c ) :− !.
special tone def ( final ,
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 &) :− !. hypo−l y d i a n , f # ) : − ! . hypo− , f ) : − ! . n a t u r a l−m 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 ) : − ! .
CHAPTER 6. CODE AND TESTING
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 c−m 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 l−m 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
Motivations for a visual language This chapter is a modified version of some mid-project reflections.
7.1
Summary of core Pelog system
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
The next step
There are three main avenues for improvement: 1. The library of predicates for musical relationships that support the Pelog language. 2. The interpreter that applies sets of rules to musical scores. 3. The design of the Pelog language itself. Of these areas, the one with perhaps the least room for improvement is the library. Most of these predicates are direct translations of Brinkman’s [4] research on musical data structures. While there is certainly room for extensions to the library, the core predicates as they are now are unlikely to change significantly. The interpreter is currently quite cumbersome. The fundamental algorithm is quite complex and still performs a lot of redundant checking despite the high factors of improvement that have been achieved through more intelligent backtracking. One possible solution is to apply constraint programming theory to the problem [39]. This task is by no means trivial, as it would likely involve porting the existing code to a flavour of Prolog that supports constraints. Another solution that was discussed was to make the interpreter more interactive. A user would be able to 151
CHAPTER 7. MOTIVATIONS FOR A VISUAL LANGUAGE
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 .
This is an extreme example, but it illustrates that a natural language approach does not eliminate the necessity for a programmer to study and follow certain conventions.
$
CHAPTER 7. MOTIVATIONS FOR A VISUAL LANGUAGE
7.4
153
The solution
The best solution would be do develop a visual programming1 environment. It will have two primary functions: 1. Organize rules within the heirarchy of levels, classes and rule sets. (Rule Manager) 2. Visually edit the musical relationships within each rule. (Rule Editor) A visual environment will push the Pelog project further toward its original goal of providing an easy-to-use environment in which musicians can specify musical relationships. While the core interpreter still requires a significant amount of work, this new direction is more interesting from a research perspective and more likely to break new ground.
1
“Visual programming,” as defined here, refers to programming using graphical rather than textual means. It should not be confused with Microsoft’s 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. Dobb’s Journal [21].
8
The Visual Pelog language The Visual Pelog language is a visual extension of the text-based Pelog musical constraint language (page 2). Each problem domain, (eg. Modal Counterpoint, Twelve-Tone Counterpoint) is defined by a rule-set. A rule-set is made of up of a number of individual rules. The rules within the rule set can be categorized by priority, class and a number of other factors which determine how the rules will be applied by the interpreter to a given musical score.
8.1
Musical Constraint Graphs
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 Tk’s psprint function.
154
CHAPTER 8. THE VISUAL PELOG LANGUAGE
Figure 8.1: The basic note template.
−
Figure 8.2: An example horizontal relationship
−
Figure 8.3: An example vertical relationship
155
CHAPTER 8. THE VISUAL PELOG LANGUAGE
156
−
−
−
Figure 8.4: An example compound relationship
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.
CHAPTER 8. THE VISUAL PELOG LANGUAGE
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.
Figure 8.5: Rule for step-wise motion.
8.3.2
Step-wise or third-wise motion
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.
CHAPTER 8. THE VISUAL PELOG LANGUAGE
Figure 8.6: Step-wise or third-wise melodic motion
158
CHAPTER 8. THE VISUAL PELOG LANGUAGE
8.3.3
159
Avoiding parallel intervals
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 don’t 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.
Figure 8.7: No parallel intervals rule.
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.
9
The Visual Pelog environment 9.1
Introduction
For this section, it is assumed you already have some basic knowledge of Pelog’s 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
A Visual Pelog session
The Visual Pelog environment (Figure 9.1 is made up of a number of separate windows: Rule manager: The rule manager is a heirarchical tree of all the rules in the rule set. Rule editor: The rule editor allows editing of the semantics of individual rules. Tool palette: The tool palette contains a number of items that can be added to rules in the rule editor. 160
CHAPTER 9. THE VISUAL PELOG ENVIRONMENT
Figure 9.1: A Visual Pelog session
161
CHAPTER 9. THE VISUAL PELOG ENVIRONMENT
162
Help browser: The help browser provides on-line help on the tools in the tool palette.
9.3
Opening and saving rule sets
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
To delete a rule, select it and then press the Delete key.
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
The rule editor window (Figure 9.2) is made up of four sections: Description: Entry for a natural-language description of what the rule does. Graph editor: An area where the rule itself is edited. Status bar: Displays information about the item on the graph that the mouse is currently over. Musical keyboard: Used to specify properties about items in the graph.
CHAPTER 9. THE VISUAL PELOG ENVIRONMENT
163
Figure 9.2: The rule editor window.
9.5.1
Graph editor
For information on creating musical constraints using musical constraint graphs, please see The Visual Pelog language on page 154. Adding constraints To add constraints, make sure that the desired constraint type is selected in the tool palette (Figure 9.5). If the constraint is a unary constraint (i.e. constrains one event on its own) simply click on the event you wish to constrain. If the constraint is a binary constraint (i.e. constrains the relationship between two notes) click and drag between the two events you wish to constrain. After creating the constraint, you will be able to edit specifics of that constraint using the musical keyboard at the bottom of the rule editor window. Selecting constraints Choose the Selection Tool ( ) in the tool palette. Click on the constraint you wish to select. The selected constraint will appear in reverse video (Figure 9.3). There can only be only selected constraint at a given time.
CHAPTER 9. THE VISUAL PELOG ENVIRONMENT
164
Figure 9.3: A selected constraint.
Obtaining information about a constraint Move the mouse over a constraint. Detailed information about that constraint will be displayed in the status bar. Deleting constraints Select the constraint you wish to delete and press the Delete key.
9.5.2
Using the musical keyboard
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.
Figure 9.4: The musical keyboard
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 constraint’s 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
CHAPTER 9. THE VISUAL PELOG ENVIRONMENT
165
Figure 9.5: The tool palette.
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
A specialized graph layout algorithm for musical constraint graphs Graph layout theory is a large and active field of study [10] [8]. In general, research has indicated that while GLA’s can be classified into a small number of categories, extensive customization is usually necessary for each unique graph problem domain. Recent attempts at generalized graph drawing systems have been successful only due to their support for numerous different layout algorithms (GraphEd [13]) or their ability to perform minute customizations on individual constraints (EDGE [25]). Likewise, the present graph layout system is specific to the problem of what shall be called “musical constraint graphs.”
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
CHAPTER 10. GRAPH LAYOUT ALGORITHM
167
• The algorithm must work in two dimensions: vertical relationships must be drawn vertically, and horizontal relationships horizontally. • Changes to the graph must be made elegantly, so that the user does not get ‘lost.’
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.
CHAPTER 10. GRAPH LAYOUT ALGORITHM
10.2.2
168
Constraint-based extensions to the Sugiyama algorithm
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
The problem of graph stability
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 na¨ıve approach is quite acceptable. The movement of nodes also represents, in an entertaining way, the dynamic nature of the graph.
10.2.4
The problem of two-dimensionality
To apply the constraint-based Sugiyama algorithm in two dimensions, the constraint-based sort is simply performed on one axis, and then the results are applied to a sort on the other axis.
CHAPTER 10. GRAPH LAYOUT ALGORITHM
10.3
Implementation of the algorithm
10.3.1
General design
169
The present GLA is implemented in an object-oriented style. The algorithm, on the most basic level, is an interaction between the rule editor, which maintains a list of nodes and operates globally on the nodes, and the nodes themselves, which perform their own topological sorting and constraint resolution. An attempt was also made to uncouple the algorithm itself from the underlying graphics architecture (in this case Tkinter.) This allows ths system to be trivially ported to other graphics libraries. One of the less obvious of such abstractions is the differentiaion between logical and physical coordinates. Logical coordinates are the location of the node based on the topological sort and constraint resolution steps. These logical coordinates are mapped to physical coordinates, in a manner that avoids any collisions, only after all of the basic layout has been completed.
10.3.2
The top-level
The main process is as follows: 1. Enter a loop that continues until there are no conflicts. • Support the near constraint by moving all nodes to the average (center) of their near nodes. • Perform a constraint-based ‘topological’ sort on each of the nodes. • Resolve any conflicts (where two nodes occupy the same location.) 2. Map the logical coordinates from step 1 to physical coordinates.
10.3.3
The near constraint
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
Constraint-based topological sort
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.
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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
Mapping physical coordinates to logical coordinates
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.
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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
Code related to the graph layout algorithm
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 )
CHAPTER 10. GRAPH LAYOUT ALGORITHM
172
# P r e s s i n g ’ p ’ w i l l export the canvas as a P o s t S c r i p t f i l e s e l f . b i n d ( s e q u e n c e=’’ , f u n c= s e l f . c a n v a s . p s p r i n t ) # A l l other k e y s t r o k e s are sent to the c u r r e n t l y s e l e c t e d t o o l s e l f . b i n d ( s e q u e n c e=’< KeyPress >’ , f u n c=p a l e t t e . t o o l k e y p r e s s ) # C r e a t e s t h e w i d g e t s i n t h e r u l e e d i t o r window def make widgets ( s e l f , r u l e ) : # The t e x t box u s e d t o d e s c r i b e t h e r u l e i n E n g l i s h s e l f . t e x t = T k i n t e r . Text ( h e i g h t = 3 , m a s t e r= s e l f ) if self . rule : s e l f . t e x t . i n s e r t ( ’0.0’ , r e p r ( s e l f . r u l e ) ) s e l f . t e x t . g r i d ( row = 0 , column = 0 , s t i c k y=’nwe’ ) s e l f . t e x t . b i n d ( s e q u e n c e="< KeyRelease >" , f u n c= s e l f . update name ) # The m u s i c a l k e y b o a r d s e l f . k e y b o a r d = M u s i c W i d g e t s . Keyboard ( s e l f ) s e l f . keyboard . g r i d ( row = 5 , column = 0 , s t i c k y=’nesw’ , c olumnspan =2) # The r u l e e d i t o r i t s e l f and i t s c o r r e s p o n d i n g s c r o l l b a r s s e l f . canvas = RuleEditorCanvas ( p a l e t t e= s e l f . p a l e t t e , m a s t e r= s e l f , k e y b o a r d= s e l f . k e y b o a r d , r u l e=r u l e ) s e l f . c a n v a s . g r i d ( row = 1 , column = 0 , s t i c k y=’nesw’ ) 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 . v s c r o l l . g r i d ( row = 1 , column = 1 , s t i c k y=’ns’ ) s e l f . h s c r o l l = Tkinter . Scrollbar ( s e l f , 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 . h s c r o l l . g r i d ( row = 2 , column = 0 , s t i c k y=’we’ ) 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 . g r i d r o w c o n f i g u r e ( 1 , w e i g h t =1) 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 . c a n v a s [ ’ yscrollcommand ’ ] = s e l f . s c r o l l Y # The s t a t u s b a r s e l f . status = Tkinter . Label ( m a s t e r= s e l f , r e l i e f =’ sunken ’ ) s e l f . s t a t u s . g r i d ( row = 4 , column = 0 , c olum nspan = 2 , s t i c k y=’we’ ) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Event h a n d l e r s
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 on−s 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
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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)])
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 :
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 t−o 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 ===============" )
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 ] = (
CHAPTER 10. GRAPH LAYOUT ALGORITHM
178
max ( node . s p t [ dimen ] , rowcolmax [ dimen ] [ n o d e a t ] ) ) # S t o r e a r u n n i n g t o t a l o f e a c h row o r column s i z e , a d d i n g p a d d i n g # a l o n g t h e way . # row ( x ) = sum ( row ( 1 ) . . . row ( x − 1 ) ) + 2 ∗ ( p a d d i n g ∗ x ) overallsum = [0,0] f o r dimen i n [ 0 , 1 ] : # do x t h e n y a x i s sum = RE CANVAS PAD [ dimen ] f o r x i n x r a n g e ( gmax [ dimen ] + 1 ) : sum = sum + ( rowcolmax [ dimen ] [ x ] / 2 ) + RE PAD [ dimen ] rowcols um [ dimen ] [ x ] = sum sum = sum + ( rowcolmax [ dimen ] [ x ] / 2 ) + RE PAD [ dimen ] o v e r a l l s u m [ dimen ] = sum + RE CANVAS PAD [ dimen ] # S t o r e t h e c o r r e s p o n d i n g PHYSICAL c o o r d i n a t e s w i t h e a c h node f o r dimen i n [ 0 , 1 ] : f o r node i n s e l f . n o d e L i s t : node . n p t [ dimen ] = ro w c o l sum [ dimen ] [ node . t p t [ dimen ] ] s e l f [ ’ scrollregion ’ ] = s e l f . bbox ( ’all’ ) # A n i m a t e s a l l t h e n o d e s from t h e i r o l d PHYSICAL p o s i t i o n t o t h e i r # new PHYSICAL p o s i t i o n def animate ( s e l f ) : # I t e r a t i o n f o r each frame of the animation f o r f a c t o r i n x r a n g e ( RE ANIMATION FRAMES ) : f = ( f a c t o r / RE ANIMATION FRAMES) f o r node i n s e l f . n o d e L i s t : node . m o v e b y f a c t o r ( f ) s e l f . m a s t e r . u p d a t e ( ) # u p d a t e T k i n t e r window # We’ r e done a n i m a t i n g , s o u p d a t e a l l t h e o l d c o o r d i n a t e s # t o t h e new c o o r d i n a t e s 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 ( ) d e b u g p r i n t ( node , ":" , node . t p t )
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
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 ) :
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 # bounced−o 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 ) :
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 ] )
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 l−s 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)
CHAPTER 10. GRAPH LAYOUT ALGORITHM
183
# D e c i d e s where t o move n o d e s when two n o d e s o c c u p y t h e same # LOGICAL c o o r d i n a t e b a s e d on t h e a v e r a g e p o s i t i o n o f t h e # n o d e s t h a t t h i s node i s c o n s t r a i n e d t o be ’ n e a r . ’ resolve options def = [ ( ’ selfavgy > node2avgy ’ , # Up ’node2 . topological_sort (1, RE_MINGRID , self.tpt [1] - 1)’ ) , ( ’ selfavgx < node2avgx ’ , # R i g h t ’ node2 . topological_sort (0, node2 .tpt [0] + 1, RE_MAXGRID )’ ) , ( ’ selfavgy < node2avgy ’ , # Down ’ node2 . topological_sort (1, node2 .tpt [1] + 1, RE_MAXGRID )’ ) ] # p r e−c o m p i l e t h e s e o p t i o n s resolve options = [] for option in res olve options def : e x p r e s s i o n = c o m p i l e ( o p t i o n [ 0 ] , ’ dynamic code’ , ’eval’ ) s t a t e m e n t = c o m p i l e ( o p t i o n [ 1 ] , ’ dynamic code’ , ’exec’ ) r e s o l v e o p t i o n s . append ( ( e x p r e s s i o n , s t a t e m e n t ) ) d e f r e s o l v e ( s e l f , node2 ) : s e l f a v g x , s e l f a v g y = s e l f . f i n d a v e r a g e ( s e l f . near ) n o d e 2 a v g x , n o d e 2 a v g y = s e l f . f i n d a v e r a g e ( node2 . n e a r ) # Try a l l t h e p o s s i b l e o p t i o n s i n o r d e r , s t a r t i n g w i t h # the d i r e c t i o n a f t e r s e l f . l a s t r e s o l v e r e s o l v e d = FALSE f o r d i r in range ( 0 , len ( s e l f . r e s o l v e o p t i o n s ) ) : go = ( d i r + s e l f . l a s t r e s o l v e + 1 ) % 3 i f e v a l ( s e l f . r e s o l v e o p t i o n s [ go ] [ 0 ] ) : e x e c s e l f . r e s o l v e o p t i o n s [ go ] [ 1 ] s e l f . l a s t r e s o l v e = go r e s o l v e d = TRUE break # i f none o f t h e o p t i o n s were t a k e n , j u s t p i c k t h e # f i r s t one i f not r e s o l v e d : go = ( s e l f . l a s t r e s o l v e + 1 ) % 3 e x e c s e l f . r e s o l v e o p t i o n s [ go ] [ 1 ] s e l f . l a s t r e s o l v e = go # U p da t e s t h e new p o s i t i o n i n t o t h e o l d p o s i t i o n def f i n a l i z e c o o r d ( s e l f ) : i f ( s e l f . npt != s e l f . pt ) : s e l f . pt [ 0 ] , s e l f . pt [ 1 ] = s e l f . npt [ 0 ] , s e l f . npt [ 1 ] s e l f . move visual ()
################################################## # HightlightableNode #
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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’
CHAPTER 10. GRAPH LAYOUT ALGORITHM
185
spt = (16, 16) def
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 ] :
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 ,
CHAPTER 10. GRAPH LAYOUT ALGORITHM n e a r =[ a ] ) # 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 a t o f node a f o r dimen i n [ 0 , 1 ] : s e l f . p t [ dimen ] = a . p t [ dimen ] 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 ) , 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 ) ]
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
CHAPTER 10. GRAPH LAYOUT ALGORITHM s e l f . b i n d ( s e q u e n c e="< Enter >" , command= s e l f . node . h i g h l i g h t ) s e l f . b i n d ( s e q u e n c e="< Leave >" , command= s e l f . node . u n h i g h l i g h t ) s e l f . b i n d ( s e q u e n c e="< ButtonRelease >" , command= s e l f . node . b u t t o n r e l e a s e ) s e l f . b i n d ( s e q u e n c e="< ButtonPress >" , command= s e l f . node . b u t t o n p r e s s ) # Move t h i s i t e m t o t h e l o c a t i o n o f t h e node def move to final ( s e l f ) : s e l f . move ( s e l f . node . p t [ 0 ] , s e l f . node . p t [ 1 ] ) # Move between t h e o l d and new p o s i t i o n o f t h e node # by a g i v e n f a c t o r . Used f o r a n i m a t i o n . def move by factor ( s e l f , factor ) : s e l f . move ( ( ( s e l f . node . n p t [ 0 ] − s e l f . node . p t [ 0 ] ) ∗ f a c t o r + s e l f . node . p t [ 0 ] ) , ( ( s e l f . node . n p t [ 1 ] − s e l f . node . p t [ 1 ] ) ∗ f a c t o r + s e l f . node . p t [ 1 ] ) ) # Move t h e i t e m t o a g i v e n p o i n t d e f move ( s e l f , x , y ) : s e l f . coords ( [ ( x , y ) ] ) # 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 b a s e d on # t h e c u r r e n t s t a t e o f t h e c o r r e s p o n d i n g node def update ( s e l f ) : pass # Does some c o l o u r s e l e c t i o n magic def g e t c o l o r s ( s e l f ) : i f s e l f . node . s e l e c t e d : i f s e l f . node . h i g h l i g h t e d : o u t l i n e = RE COLOR HIGHSELECT else : o u t l i n e = RE COLOR SELECTED f i l l = RE COLOR NODE SELECTED t e x t = RE COLOR TEXT SELECTED width = 2 else : i f s e l f . node . h i g h l i g h t e d : o u t l i n e = RE COLOR HIGHLIGHTED width = 2 text = outline else : o u t l i n e = RE COLOR NORMAL width = 1 t e x t = RE COLOR TEXT f i l l = RE COLOR NODE
188
CHAPTER 10. GRAPH LAYOUT ALGORITHM r e t u r n o u t l i n e , width ,
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
CHAPTER 10. GRAPH LAYOUT ALGORITHM
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 ] )
CHAPTER 10. GRAPH LAYOUT ALGORITHM s e l f [ ’fill’ ] = o u t l i n e s e l f [ ’ width ’ ] = w i d t h
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
CHAPTER 11. CUSTOM MEGAWIDGETS
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
End user usage
Figure 11.1: An example tree widget. The tree widget displays structured information in a vertically-oriented heirarchical tree (Figure 11.1). This type of widget should be familiar to users of Microsoft Windows, as it mimics the behaviour of Microsoft Windows Explorer as closely as possible. Nodes appear as two types: Leaf:
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
A fairly exhaustive internet-search revealed no ready-made general purpose tree widgets of this type implemented in Python. Ironically, within days of creating my own implemntation from scratch, two separate tree widgets appeared on the Python language website. www.python.org
CHAPTER 11. CUSTOM MEGAWIDGETS
194
Figure 11.2: The appearance of a selected node.
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 )
where master is the window that will own the TreeWidget. TreeNode To add nodes to the TreeWidget, you must first get the root TreeNode from the TreeWidget: r o o t = newTree . g e t n o d e ( )
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 node’s children. node a.append node(node b) Adds the arbitrary sub-tree node b to the list of node a’s children. node.remove(data) Removes a node containing data from the list of node’s children. node a.remove node(node b) Removes the arbitrary sub-tree node b from the list of node a’s 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
CHAPTER 11. CUSTOM MEGAWIDGETS
195
# 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 ############################################################ # Tree . py # # A g e n e r a l−p u r p o s e t r e e w i d g e t f o r T k i n t e r from U t i l i m p o r t ∗ # Tkinter imports i m p o r t T k i n t e r , Canvas from T k C o n s t a n t s i m p o r t ∗ from C o n s t a n t s i m p o r t ∗ #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Constants Y STEP = 2 X STEP = 18 FONT = ’- adobe - helvetica - medium -r- normal -*-11-80-100-100- p-56- iso8859 -1’ LINE = 5 OPEN BOX = 5 ICON = 22 TEXT = 35 OPENBM MASKDATA = "" "# define solid_width 9\n# define solid_height 9 s t a t i c unsigned char s o l i d b i t s [] = { 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 , 0 x f f , 0 x01 } ; """ OPENBM DATA = "" "# define open_width 9\n# define open_height 9 s t a t i c unsigned char open bits [] = { 0 x f f , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x7d , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x01 , 0 x f f , 0 x01 } ; """ CLOSEDBM DATA = "" "# define closed_width 9\n# define closed_height 9 s t a t i c unsigned char c l o s e d b i t s [] = { 0 x f f , 0 x01 , 0 x01 , 0 x01 , 0 x11 , 0 x01 , 0 x11 , 0 x01 , 0 x7d , 0 x01 , 0 x11 , 0 x01 , 0 x11 , 0 x01 , 0 x01 , 0 x01 , 0 x f f , 0 x01 } ; """ OVER = None DRAGGING = FALSE ################################################## # TreeWidget # # A widget that d i s p l a y s a nested s t r u c t u r e of # TreeNodes i n a h e i r a r c h i c a l manner #
CHAPTER 11. CUSTOM MEGAWIDGETS
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 (
CHAPTER 11. CUSTOM MEGAWIDGETS
197
’ photo ’ , f o r m a t=’GIF’ , f i l e =’ images / folderclosed .gif’ ) s e l f . LEAF = T k i n t e r . Image ( ’ photo ’ , f o r m a t=’GIF’ , f i l e =’ images /leaf.gif’ ) 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 ) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Event h a n d l e r s # Moves a l e a f node a s i t i s b e i n g d r a g g e d d e f motion ( s e l f , a r g s ) : i f s e l f . s e l e c t e d and DRAGGING : s e l f . s e l e c t e d . move ( a r g s . x , a r g s . y ) # Drops a l e a f node i n t o i t s new p a r e n t def r e l e a s e ( s e l f , args ) : g l o b a l DRAGGING DRAGGING = FALSE if self . selected : i f OVER and OVER ! = s e l f . s e l e c t e d : s e l f . t r e e . remove node ( s e l f . s e l e c t e d ) OVER . a p p e n d n o d e ( s e l f . s e l e c t e d ) else : s e l f . selected . restore pos () def keypress ( s e l f , args ) : i f a r g s . keysym == ’ Delete ’ : # D e l e t e a node if self . selected : s e l f . t r e e . remove node ( s e l f . s e l e c t e d ) e l i f a r g s . keysym == ’ Return ’ : # E d i t a r u l e if self . selected : s e l f . s e l e c t e d . l a u n c h ( None ) e l i f a r g s . keysym == ’ Insert ’ : # I n s e r t a r u l e if self . selected : s e l f . selected . i n s e r t () #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Information gathering def get tree ( s e l f ) : return s e l f . tree def g e t s e l e c t i o n ( s e l f ) : r e t u r n s e l f . s e l e c t e d . data d e f s e t s e l e c t i o n ( s e l f , node ) : s e l f . s e l e c t e d = node s e l f . build () #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Methods # Draws t h e t r e e on t h e c a n v a s
CHAPTER 11. CUSTOM MEGAWIDGETS def build ( s e l f ) : # D e l e t e a l l t h e c a n v a s i t e m s from b e f o r e s e l f . c a n v a s . d e l e t e ( ’all’ ) s e l f . t r e e . b u i l d l a y e r ( s e l f . canvas , s e l f . s e l e c t e d , 0 , 0 ) s e l f . c a n v a s [ ’ scrollregion ’ ] = s e l f . c a n v a s . bbox ( ’all’ ) ################################################## # TreeNode # # Each b r a n c h o r l e a f o f t h e t r e e i s a TreeNode # Each node c o n t a i n s d a t a . For b r a n c h e s , t h i s # i s most o f t e n a s t r i n g . The name shown f o r # t h e node i s r e p r ( d a t a ) . c l a s s TreeNode : #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # I n i t i a l i z a t i o n routines i n i t ( s e l f , p a r e n t=None , d a t a="NO DATA" , i c o n=None ) : def s e l f . parent = [ parent ] self . children = [] s e l f . open = FALSE s e l f . icon = icon s e l f . data = data s e l f . set images () def set images ( s e l f ) : i f s e l f . parent [ 0 ] : s e l f .OPENBM = s e l f . p a r e n t [ 0 ] . OPENBM s e l f . CLOSEDBM = s e l f . p a r e n t [ 0 ] . CLOSEDBM s e l f . LEAF = s e l f . p a r e n t [ 0 ] . LEAF s e l f . FOLDEROPEN = s e l f . p a r e n t [ 0 ] . FOLDEROPEN s e l f . FOLDERCLOSED = s e l f . p a r e n t [ 0 ] . FOLDERCLOSED else : s e l f .OPENBM = s e l f . CLOSEDBM = s e l f . LEAF = None s e l f . FOLDEROPEN = s e l f . FOLDERCLOSED = None def s e t c h i l d r e n f r o m l i s t ( s e l f , l i s t ) : if list : f o r item i n l i s t : i f l e n ( item ) == 0: node = s e l f . append ( i t e m ) else : node = s e l f . append ( i t e m [ 0 ] ) node . s e t c h i l d r e n f r o m l i s t ( i t e m [ 1 ] ) def set parent ( s e l f , parent ) : s e l f . p a r e n t . append ( p a r e n t ) s e l f . set images () def s et ic on ( s e l f , icon ) : s e l f . icon = icon
198
CHAPTER 11. CUSTOM MEGAWIDGETS #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Node methods d e f append ( s e l f , d a t a ) : node = TreeNode ( p a r e n t= s e l f , d a t a=d a t a ) s e l f . a p p e n d n o d e ( node ) r e t u r n node d e f a p p e n d n o d e ( s e l f , node ) : s e l f . c h i l d r e n . append ( node ) s e l f . children . sort () print self . children s e l f . build () d e f remove ( s e l f , d a t a ) : f o r node i n s e l f . c h i l d r e n : i f node . d a t a == d a t a : s e l f . c h i l d r e n . remove ( node ) else : node . remove ( d a t a ) s e l f . build () d e f r e m o v e n o d e ( s e l f , node ) : for n in s e l f . children : i f n == node : s e l f . c h i l d r e n . remove ( node ) else : n . r e m o v e n o d e ( node ) s e l f . build () def
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
CHAPTER 11. CUSTOM MEGAWIDGETS
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 g−up 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 ’ )
CHAPTER 11. CUSTOM MEGAWIDGETS
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 )
CHAPTER 11. CUSTOM MEGAWIDGETS
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
n a m e == " __main__ " : import Rule w = TreeWidget ( ) n = w. g e t t r e e ()
CHAPTER 11. CUSTOM MEGAWIDGETS
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
Help Browser Widget
11.2.1
End user usage
Figure 11.3: The Help Browser displaying an example document.
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
CHAPTER 11. CUSTOM MEGAWIDGETS
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: bold text italic text bold and italic text monospaced text title-sized text 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" "(c ) 1999 Michael Droettboom \n"
CHAPTER 11. CUSTOM MEGAWIDGETS
205
"This is a very basic help browsing system that manages a " " collection of internal document strings and displays them " "with hyperlinks in a window .\n" "The document strings are formatted using a very tiny subset " "of HTML . The Supported tags are :\n\n" "" "\tb: < b>bold text\n" "\ti: < i> italic
text\n" "\t
bi : < bi>bold and italic bi> text\n" "\t
tt : < tt> monospaced ( teletype ) text\n" "\t
title : < title >title -sized title > text\n" "\t
a href =’< bi> subject bi >’: < a href =’ Default Page 2’>" " reference to another subject page\n" ) , ’ Default Page 2’ : ( "< title >Help Browser Page 2 title >\n" " Congratulations ! You’ve made it to the next page .\n" "
Click here to go back .\n" ) } ######################################## # D i s p l a y s t h e g i v e n s u b j e c t i n a h e l p window . # Only one h e l p window i s open f o r one a p p l i c a t i o n # at a time . d e f show ( s u b j e c t ) : g l o b a l helpWindow i f helpWindow == None : helpWindow = Window ( s u b j e c t ) helpWindow . m a i n l o o p ( ) else : helpWindow . s e t s u b j e c t ( s u b j e c t ) ######################################## # Add a h e l p s u b j e c t t o t h e d a t a b a s e d e f add ( s u b j e c t , document ) : g l o b a l documents documents [ s u b j e c t ] = document ################################################## # Window # # The main window t o d i s p l a y h e l p # c l a s s Window ( T k i n t e r . T o p l e v e l ) : def i n i t ( s e l f , s u b j e c t=" Default " ) : # C r e a t e t h e main Help window Tkinter . Toplevel . init ( s e l f , None , c l a s s =’Help’ ) s e l f . make widgets () s e lf . set subject ( subject )
CHAPTER 11. CUSTOM MEGAWIDGETS
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 o l d t e x t # : i t a l i c t e x t # : b o l d and i t a l i c t e x 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 # : 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 = []
CHAPTER 11. CUSTOM MEGAWIDGETS
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
add tag ( s e l f , tag ) : s e l f . t a g S t a c k . append ( t a g )
def
remove tag ( s e l f , tag ) : s e l f . t a g S t a c k . remove ( t a g )
# F u n n e l s d a t a from t h e SGMLParser t o t h e T k i n t e r Text box def handle data ( s e l f , data ) : s e l f . i n s e r t ( ’end’ , d a t a , t u p l e ( s e l f . t a g S t a c k ) ) # C a l l e d by t h e SGMLParser when i t e n c o u n t e r s a s t a r t t a g ( eg . < b>) def unknown starttag ( s e l f , tag , a t t r s ) : # I f i t ’ s one o f t h e t a g s we s u p p o r t , p u t i t on t h e t a g s t a c k i f tag i n s e l f . known tags : s e l f . add tag ( tag ) # I f t h i s i s a r e f e r e n c e , we have some work t o do i f t a g == "a" : for x in attrs : i f x [ 0 ] = = ’href’ : s e l f . make reference tag (x [1]) def
make reference tag ( s e l f , ref ): # Make a new t a g w i t h t h e same name a s t h e # s u b j e c t being r e f e r e n c e d to s e l f . add tag ( ref ) self . reference = ref # The g e n e r i c b i n d i n g s t o b i n d t h i s t a g t o make = [ ( ’ set_subject ’ , ’< ButtonPress >’ ) , ( ’ highlight_ref ’ , ’< Enter >’ ) , ( ’ unhighlight_ref ’ , ’< Leave >’ ) ] # Make t h e b i n d i n g s s p e c i f i c t o t h i s p a r t i c u l a r
CHAPTER 11. CUSTOM MEGAWIDGETS
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 ()
CHAPTER 11. CUSTOM MEGAWIDGETS
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
On-screen musical keyboard Widget
11.3.1
End user usage
Figure 11.4: Keyboard widget showing SINGLE selection
Figure 11.5: Keyboard widget showing MULTI selection
CHAPTER 11. CUSTOM MEGAWIDGETS
210
Figure 11.6: Keyboard widget showing RANGE selection
Figure 11.7: Keyboard widget in disabled mode
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 )
Figure 11.8: Keyboard widget zoomed in
CHAPTER 11. CUSTOM MEGAWIDGETS
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 ∗
CHAPTER 11. CUSTOM MEGAWIDGETS
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 on−s 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 ’ )
CHAPTER 11. CUSTOM MEGAWIDGETS s e l f . make widgets () s e l f . s t a t e a l l ( ’ disabled ’ ) def
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 zoom−i n / zoom−o 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
CHAPTER 11. CUSTOM MEGAWIDGETS f r a m e 3 . g r i d ( row = 0 , column = 1 , s t i c k y=’ew’ ) s e l f . g r i d c o l u m n c o n f i g u r e ( 1 , w e i g h t =1) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Event h a n d l e r s 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 zoom in ( s e l f ) : s e l f . c a n v a s . s c a l e (ZOOM FACTOR) d e f zoom out ( s e l f ) : s e l f . c a n v a s . s c a l e ( 1 / ZOOM FACTOR) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Settings 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 . s t a t e a l l ( value ) else : T k i n t e r . Frame . s e t i t e m ( s e l f , k e y , v a l u e ) def s t a t e a l l ( s e l f , value ) : s e l f . zoomin [ ’ state ’ ] = v a l u e s e l f . zoomout [ ’state ’ ] = v a l u e s e l f . c a n v a s [ ’state’ ] = v a l u e def s e t c a l l b a c k ( s e l f , func ) : s e l f . canvas . c a l l b a c k = func d e f s e t m o d e ( s e l f , mode ) : s e l f . c a n v a s . s e t m o d e ( mode ) # Keyboard Canvas h e l p e r c l a s s c l a s s KeyboardCanvas ( 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 r e n t=None , mode=KB SINGLE , m o u s e s t a t u s=None , w i d g e t s t a t u s=None ) : s e l f . mouse status = mouse status s e l f . widget status = widget status s e l f . x w i d t h = WKEY WIDTH ∗ 4 9 + 1 T k i n t e r . Canvas . init ( s e l f , parent , w i d t h=WKEY WIDTH ∗ 4 9 , h e i g h t=WKEY HEIGHT , s c r o l l r e g i o n = ( 0 , 0 , s e l f . x w i d t h , WKEY HEIGHT ) ) s e l f [ ’ cursor ’ ] = ’ hand1 ’ s e l f . make keys ( ) s e l f . s e t m o d e ( mode )
214
CHAPTER 11. CUSTOM MEGAWIDGETS
215
def make keys ( s e l f ) : k e y s = [ ’c’ , ’c#’ , ’d’ , ’d#’ , ’e’ , ’f’ , ’f#’ , ’g’ , ’g#’ , ’a’ , ’a#’ , ’b’ ] location = 0 self . keyList = [] f o r octave in xrange (−3, 4): f o r i v o r y in keys : i f l e n ( i v o r y ) == 2: k = BlackKey ( s e l f , l o c a t i o n ∗ WKEY WIDTH − BKEY HALF , i v o r y + "~" + r e p r ( o c t a v e ) ) e l i f l e n ( i v o r y ) == 1: k = WhiteKey ( s e l f , l o c a t i o n ∗ WKEY WIDTH , i v o r y + "~" + r e p r ( o c t a v e ) ) location = location + 1 s e l f . k e y L i s t . append ( k ) def scale ( s e l f , factor ) : T k i n t e r . Canvas . s c a l e ( s e l f , ’all’ , 0 , 0 , f a c t o r , 1 ) s e l f . xwidth = s e l f . xwidth ∗ f a c t o r s e l f [ ’ scrollregion ’ ] = ( 0 , 0 , s e l f . x w i d t h , WKEY HEIGHT) #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Event h a n d l e r s d e f c l i c k e d o n ( s e l f , key ) : i f s e l f . mode == KB SINGLE : if self . selected != []: s e l f . s e l e c t e d [ 0 ] . unmark ( ) s e l f . s e l e c t e d = [ key ] k e y . mark ( ) e l i f s e l f . mode == KB MULTI : i f k e y . marked : s e l f . s e l e c t e d . remove ( k e y ) k e y . unmark ( ) else : s e l f . s e l e c t e d . append ( k e y ) k e y . mark ( ) e l i f s e l f . mode == KB RANGE : s e l f . top = key s e l f . bottom = k e y for k in s e l f . keyList : k . unmark ( ) k e y . mark ( ) s e l f . d r a g g i n g = TRUE d e f motion on ( s e l f , key ) : i f k e y ! = None and s e l f . m o u s e s t a t u s ! = None : s e l f . m o u s e s t a t u s [ ’text’ ] = k e y . name i f s e l f . mode == KB RANGE and s e l f . d r a g g i n g and k e y ! = None : s e l f . bottom = k e y k e y . mark ( )
CHAPTER 11. CUSTOM MEGAWIDGETS mark = FALSE for k in s e l f . keyList : i f mark : k . mark ( ) else : k . unmark ( ) i f k == s e l f . t o p : mark = n o t mark k . mark ( ) i f k == s e l f . bottom : mark = n o t mark k . mark ( ) d e f r e l e a s e d o n ( s e l f , key ) : s e l f . bottom = k e y s e l f . d r a g g i n g = FALSE i f self . callback : i f s e l f . mode == KB RANGE : s e l f . c a l l b a c k ( [ s e l f . t o p , s e l f . bottom ] ) else : self . callback ( self . selected ) def find key ( s e l f , x , y ) : x = s e l f . canvasx ( x ) z = self . find overlapping (x , y , x , y) if z != (): return s e l f . items [ z [−1]] else : r e t u r n None #−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− # Settings d e f s e t m o d e ( s e l f , mode ) : s e l f . mode = mode self . selected = [] i f mode == KB RANGE : s e l f . t o p = None s e l f . bottom = None s e l f . d r a g g i n g = FALSE for k in s e l f . keyList : k . unmark ( ) def
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
CHAPTER 11. CUSTOM MEGAWIDGETS
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’
CHAPTER 11. CUSTOM MEGAWIDGETS
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 ()
CHAPTER 11. CUSTOM MEGAWIDGETS
219
def make widgets ( s e l f ) : s e l f . k e y b o a r d = Keyboard ( s e l f , mode=KB MULTI ) s e l f . k e y b o a r d . pack ( expand=’true’ , a n c h o r=NW, f i l l =’both’ ) s e l f . k e y b o a r d [ ’state ’ ] = ’ normal ’ if
n a m e == " __main__ " : t = Test ( ) t . mainloop ()
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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
221
QUESTION DATA = "" "# define E__Pelog_Visual_images_q_width 10 #d e f i n e E P e l o g V i s u a l i m a g e s q h e i g h t 1 0 s t a t i c char E P e l o g V i s u a l i m a g e s q b i t s [] = { 0 x78 , 0 x00 , 0 x f c , 0 x00 , 0 x c c , 0 x00 , 0 xc0 , 0 x00 , 0 x60 , 0 x00 , 0 x30 , 0 x00 , 0 x30 , 0 x00 , 0 x00 , 0 x00 , 0 x30 , 0 x00 , 0 x30 , 0 x00 } ; """ ################################################## # PaletteWindow # c l a s s PaletteWindow ( T k i n t e r . T o p l e v e l ) : standard = [ Tools . S e l e c t , Tools . Equals , Tools . Greater , T o o l s . Or , T o o l s . Not ] i f s y s . p l a t f o r m == ’ win32 ’ : c o lu m ns = 2 else : c o lu m ns = 1 def
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
def make widgets ( s e l f ) : s e l f . cur column = 0 s e l f . cur row = 0 # C r e a t e a l i s t o f t h e t o o l s t h a t w i l l be # added plugins = s e l f . get plugins () tools = s e l f . standard + plugins # Add t h e t o o l s t o t h e f r a m e f r a m e 1 = T k i n t e r . Frame ( m a s t e r= s e l f ) for tool in tools : s e l f . a d d t o o l ( frame1 , t o o l ) f r a m e 1 . pack ( f i l l =BOTH , expand = 1 , a n c h o r=N) # C r e a t e t h e s t a t u s b a r and h e l p b u t t o n f r a m e 2 = T k i n t e r . Frame ( m a s t e r= s e l f ) f r a m e 2 . g r i d c o l u m n c o n f i g u r e ( 1 , w e i g h t =1) f r a m e 2 . pack ( f i l l =X , expand = 1 , a n c h o r=S ) s e l f . q u e s t i o n i m a g e = T k i n t e r . Image ( ’ bitmap ’ , d a t a=QUESTION DATA , maskdata=QUESTION DATA , f o r e g r o u n d=’black’ , b a c k g r o u n d=’ white ’ )
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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 non−b u i l t−i 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 g−i 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 g−i 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 ()
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS s e l f . cur tool = new tool new tool . activate () def describe ( s e l f , s t r i n g ) : s e l f . l a b e l [ ’text’ ] = s t r i n g def undescribe ( s e l f ) : i f s e l f . c u r t o o l ! = None : self . cur tool . describe ([]) else : s e l f . l a b e l [ ’text’ ] = ’’ def help on tool ( s e l f ) : i f s e l f . c u r t o o l ! = None : s e l f . cur tool . help on tool () # Caused by c l i c k o r d r a g on t h e R u l e E d i t o r # Most o f t e n , t h i s w i l l c r e a t e a new node i n t h e e d i t o r 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 e l f . c u r t o o l ! = None : self . cur tool . run tool ( ruleEditor , start , finish ) def tool keypress ( s e l f , args ) : i f s e l f . c u r t o o l ! = None : s e l f . c u r t o o l . k e y p r e s s ( a r g s . keysym ) p a l e t t e = PaletteWindow ( )
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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS def
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
def create button ( s e l f ) : s e l f . button = ToolButton ( s e l f , s e l f . buttons , s e l f . button bitmap ) r e t u rn s e l f . button def choose ( s e l f ) : s e l f . palette . change tool ( s e l f ) def describe ( s e l f , args ) : self . palette . describe ( self ) def undescribe ( s e l f , args ) : s e l f . palette . undescribe () def activate ( s e l f ) : s e l f . b u t t o n . c o n f i g u r e ( r e l i e f =’ sunken ’ ) s e l f . s e l e c t e d = None s e l f . i n i t i a l i z e t o o l () def deactivate ( s e l f ) : s e l f . b u t t o n . c o n f i g u r e ( r e l i e f =’ raised ’ ) if self . selected : s e l f . selected . unselect () s e l f . s e l e c t e d = None s e l f . c l e a r t o o l () def i n i t i a l i z e t o o l ( s e l f ) : pass def c l e a r t o o l ( s e l f ) : pass 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 ) : d e b u g p r i n t ( "Drawn from" , s t a r t , "to" , f i n i s h ) def help on tool ( s e l f ) : Help . show ( s e l f . t o o l n a m e + " Tool" ) d e f k e y p r e s s ( s e l f , key ) : i f k e y == ’ Delete ’ : d e b u g p r i n t ( " Deleting :" , s e l f . s e l e c t e d ) s e l f . selected . delete () s e l f . s e l e c t e d = None d e f s e l e c t ( s e l f , node ) :
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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 Selection Tool allows you to re-edit relationships " "that have already been added to the rule .\n" "With the Selection Tool enabled , you can click on " " existing relationships to bring up a dialog box containing " "more specific options . For instance , clicking on an interval " " 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 Delete key removes the selected relationship " "from the rule editor ." ) )
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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 Equals Tool constrains the results of two nodes " "to be equal .\n" "An equals 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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS # # #
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 Greater Than Tool constrains the result of one node to be " " greater than that of another .\n" "A Greater Than relationship can only be set up between " "two nodes with the same result type .\n" "Tip : Drag from the node that you wish to have " "the greater result to the node that you wish to have " "the lesser result . In this way , the Greater Than tool" " doubles as a Less Than 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"
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 . COMPARISON TYPE ! = ’’ and s t a r t . COMPARISON TYPE == f i n i s h . COMPARISON TYPE ) : node = G r e a t e r 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 ) else : error message ( "The nodes specified are not comparable .\n" "The ’ Greater Than ’ 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 . COMPARISON TYPE + " Second node : " + f i n i s h . COMPARISON TYPE + "\ nPlease try again or see the help page on the " + "’ Greater Than Tool ’ for more information ." ) c l a s s G r e a t e r N o d e ( Nodes . B i n a r y R e l a t i o n ) : n o d e t y p e = ’ greater ’ bitmap = ’ greater .xbm’ def v i s u a l i z e ( s e l f ) : Nodes . B i n a r y R e l a t i o n . v i s u a l i z e ( s e l f ) s e l f . update arrow dir () def move visual ( s e l f ) : Nodes . B i n a r y R e l a t i o n . m o v e v i s u a l ( s e l f ) s e l f . update arrow dir () def update arrow dir ( s e l f ) : i f s e l f . a . pt [0] >= s e l f . b . pt [ 0 ] :
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
228
s e l f . v i s u a l [ 4 ] [ ’ bitmap ’ ] = ’ @images /less.xbm’ else : s e l f . v i s u a l [ 4 ] [ ’ bitmap ’ ] = ’ @images / greater .xbm’
################################################## # Or # # The Or Tool Help . add ( "Or Tool" , ( "< title >Or Tool title >\n" "The Or Tool allows two constraints to be applied " " independently .\n" "An Or Node 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 Or Node may be created between any two nodes , " "with the exception of nodes in the note template .\n" ) ) c l a s s Not ( Tool ) :
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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 I n t e r v a l ToolTool 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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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
Constants and Utility functions
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
RE COLOR SELECTED = ’# AA0000 ’ RE COLOR NODE SELECTED = ’ black ’
# 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
RE COLOR HIGHLIGHTED = ’#0000 ff’ RE COLOR HIGHSELECT = ’# FF0000 ’
# 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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
RE COLOR TEXT = ’black’ RE COLOR TEXT SELECTED = ’ white ’
# 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
COLOR REFERENCE = ’# DDDDFF ’ COLOR REF HIGHLIGHT = ’# AAAAFF ’ ################################################## # On−s c r e e n k e y b o a r d c o n s t a n t s KB SINGLE = 0 KB MULTI = 1 KB RANGE = 2
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
CHAPTER 12. IMPLEMENTATION OF OTHER COMPONENTS
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
A
Tcl/Tk-Prolog Connector System A.1
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 other’s external C procedures. However, certain peculiarities around how SWI-Prolog’s 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
C-language connector method
What appears to be needed, therefore, is a small dynamically-linked library written in C that will provide an interface between Tcl/Tk and Prolog. It would have “wrapper” functions, around the external functions of both SWI-Prolog and Tcl/Tk, that would perform all of the necessary data conversion. Not surprisingly, there are a number of products that do exactly that. SICStus Prolog includes a Tcl/Tk interface right out of the box. There is also a product
234
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
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
Table A.1: Functions provided by Jan Struyf’s Tcl/Tk-Prolog Connection This system seems quite robust, works with a number of different versions of Tcl/Tk and SWI-Prolog, and full source code is provided. Unfortunately, the system is currently not ported to anything but MS-Windows. Committing to using this system, therefore, means not only committing to a specific implementation and version of Prolog, but also to the Windows platform.
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
A.3
236
Piping method
Another solution to the Prolog-Tcl/Tk connector problem is presented in an article by Andrea Dell’Amico at the University of Pisa1 . This article describes a connection between BinProlog and Tcl/Tk using piping. It takes advantage of the fact that both Prolog and Tcl/Tk have strong meta-programming elements.
A.3.1
Interaction Model
The Prolog system is run as a subprocess under Tcl/Tk. (Therefore, Prolog is a slave to Tcl/Tk, though this could easily be reversed.) Assertions or queries are sent to Prolog along the pipe just as a user would enter them from the keyboard. When a query is sent to Prolog, Tcl/Tk listens to the pipe and retrieves and interprets the input. If the input begins with the keyword call tcl, it is sent directly to Tcl’s eval function, which runs the rest of the input as a Tcl command. While this may not seem as elegant as the C-language connector solution, it does have some distinct advantages: • implemented entirely in Tcl/Tk and Prolog. • uses only standard methods of input/output, avoiding the external C functions which change across implementations and platforms. • provides as equally as much functionality as the Struyf C-language connector implementation. The primary disadvantage to this approach is that all interactions between Prolog and Tcl/Tk must go through an additional parsing step. Dell’Amico shows, however, that this performance hit is quite insignificant relative to the system as a whole. The higher flexibility over how and when data is sent, the tighter integration within the core tools and the almost instant portability of this system makes it superior to a highly Prolog-specific C-language connector.
A.3.2
Design
The design of a portable piping interface between Tcl/Tk and Prolog is described in an article by Andrea Dell’Amico2 .
A.3.3
Usage
The majority of this usage definition is defined in Dell’Amico. See Table A.2 for a list of user functions.
A.3.4
Implementation
Implementing the above solution turned out to be a relatively straight-forward task, though there were a few problems to solve. 1 2
http://www.cli.di.unipi.it/help/prolog/BinProlog/tcl http://www.cli.di.unipi.it/help/prolog/BinProlog/tcl
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
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.
Table A.2: User functions of SWI-Prolog-Tcl/Tk Piping Interface
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
238
Problem 1: Poor piping implementation in Windows 9x The piping implementation proposed by Dell’Amico uses Tcl’s addinput3 command to call a procedure whenever there is data available to be read from the Prolog process. This is an event-driven approach: “whenever there is something to read, read it.” Unfortunately, event-driven input from pipes is not supported under Windows 9x. Under UNIX and Windows NT, piping is an entirely transparent process that is analogous to reading and writing from a file. In Windows 9x, however, there are restrictions on what you can do with pipes. There are no events triggered when the buffer contains data. Also, there is no end-of-file marker when there is nothing in the buffer. There is a workaround for this, however. In the following Windows 9x-compatible implementation, data is only read from Prolog after running a query. The result of every query, whether it fails or not, ends in the keyword stop tcl. This keyword is used as a virtual end-of-file marker, telling Tcl to stop reading from the Prolog process. The problem with this workaround is that Prolog can not trigger Tcl events in the background. For the purposes of this project, this is not a major drawback, since the interaction model is primarily: 1. user initiates a query 2. Tcl sends query to Prolog 3. Prolog responds to query, outputting a result 4. Tcl retrieves the result of the query and displays it to the user Note also that my workarounds for Windows 9x are theoretically upward-compatible to Windows NT and UNIX. Another option would be to use Microsoft’s DDE (Dynamic Data Exchange) standard in place of piping. DDE is supported by both SWI-Prolog and Tcl/Tk. However, using it would eliminate any of the portability advantages of the piping method. Problem 2: Using stdio in SWI-Prolog SWI-Prolog is a somewhat non-standard console application. It does not use stdio, (the stream which is most often used for piping,) for normal user interaction. You cannot, for example, create an user input file and pipe it into SWI-Prolog at the command line. A workaround for this is to create a listener-loop that reads input from stdio. Terms read by the listener-loop are then interpreted as standard Prolog terms. Output from queries must likewise be explicitly printed to stdio using the write/1 predicate. Once these two snags were worked out, the rest of the connector implementation followed easily. To ensure maximum portability, only library predicates defined as part of the ISO standard Prolog were used. 3
renamed fileevent as of Tcl/Tk v 8.0
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
A.3.5
239
pl interface.tcl
Code ############################################################## # T c l /Tk s i d e o f t h e SWI−P 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 d−a 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
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
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
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
241
f o r { } { $ l ! = " tcl_stop " } { } { f l u s h $f gets $f l eval line $l } } }
A.3.6
tcl interface.pl
Code %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % P r o l o g s i d e o f t h e SWI−P 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 :
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM % % % % % %
242
There a r e f o u r commands t h a t a r e a c c e p t e d from T c l : a s s e r t p r o l o g (X ) . −− add a g o a l t o t h e d a t a b a s e q u e r y p r o l o g (X ) . −− r u n a p r o l o g q u e r y t h e keyword " tcl_stop " i s added t o t h e end o f a l l o u t p u t . h a l t or e n d o f f i l e −− q u i t s t h e i n t e r p r e t e r
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 ( ).
APPENDIX A. TCL/TK-PROLOG CONNECTOR SYSTEM
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 .
B
The graphical user interface
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 you’re 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
Installing the GUI
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
APPENDIX B. THE GRAPHICAL USER INTERFACE
245
• Tcl/Tk version 8.1 http://www.scriptics.com/ • SWI-Prolog http://www.swi.psy.uva.nl/projects/SWI-Prolog/ • GUIDO NoteViewer http://www.informatik.tu-darmstadt.de/AFS/GUIDO • gmn2midi http://www.informatik.tu-darmstadt.de/AFS/GUIDO Once all these applications have been successfully installed on your system, you will need to ensure that Pelog-GUI can find all of the required executables by including them in your PATH environment variable. The executables that the Pelog-GUI needs are: • wish81.exe (Tcl/Tk) • plcon.exe (SWI-Prolog) • gmnview.exe (GUIDO NoteViewer) • gmn2midi.exe (gmn2midi) An example PATH environment variable might look like this: PATH C:\PROGRA~1\TCL\BIN; C:\PROGRA~1\SWI-PR~1\BIN; C:\PROGRA~1\GUIDO Please consult the Microsoft Windows on-line documentation for more information on setting your PATH environment variable.
B.2
Running the GUI
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/Tk’s 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). Let’s enter an score by typing the following line into the editor:
APPENDIX B. THE GRAPHICAL USER INTERFACE
246
Figure B.1: The Pelog-GUI splash window
{ [ 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
button on the toolbar. So far, we haven’t done anything terribly exciting. The real power of the Pelog system is in applying musical rules to scores. Before we can do that, however, we will need to insert some variable notes into our score. Variable notes are notes with unspecified pitch that the interpreter will fill-in in by applying musical rules. Variable notes are represented with the tilde “~” character. Change the score to look like this: { [ c ˜ ˜ ˜ ˜ ˜ ˜ c2 ] }
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
button in the toolbar brings up a dialog box containing
APPENDIX B. THE GRAPHICAL USER INTERFACE
247
Figure B.2: The GUIDO NoteViewer displaying the example score
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.)
APPENDIX B. THE GRAPHICAL USER INTERFACE
B.4
Visual elements
B.4.1
The main window
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 Microsoft’s own software development products.
APPENDIX B. THE GRAPHICAL USER INTERFACE
B.4.4
249
The editor
The editor is a standard text editor identical in functionality and behaviour to Microsoft Windows Notepad.
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
The Look function is available from the View menu or by pressing on the toolbar. This opens the GUIDO NoteViewer to display the contents of the editor in common music notation (cmn). Notice that Look displays exactly what is in the editor when it is activated, not the saved version of the file.
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
The Hear function is available from the View menu or by pressing on the toolbar. This opens the Windows Media Player to play the contents of the editor. Like the Look function, Hear plays exactly what is in the editor when it is activated.
APPENDIX B. THE GRAPHICAL USER INTERFACE
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
The Go function is available from the Rules menu or by pressing the toolbar button. This function applies the currently selected Pelog rule set to the currently opened input file. The rule set can be chosen with a file dialog by selecting the Rules Select Rule Set... menu item or pressing the toolbar button. The rule set name can also be typed in directly to the text box at the right-hand side of the toolbar.
APPENDIX B. THE GRAPHICAL USER INTERFACE
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
Notes about portability
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
See http://www.informatik.tu-darmstadt.de/AFS/GUIDO for more information. See http://www.informatik.tu-darmstadt.de/AFS/GUIDO for more information.
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.
[3] Bowman, Ivan. “Lecture Notes on ‘Methods for Visual Understanding of Heirarchical Systems’ Sugiyama et al.” University of Waterloo, ON, CS746G. http://plg.uwaterloo.ca/ itbowman/CS746G/Notes/Sugiyama1981/ [4] Brinkman, Alexander. Pascal Programming for Music Research. Chicago, IL: University of Chicago Press, 1990. [5] Clocksin, William F. & Christopher S. Mellish. Programming in Prolog. New York: Springer-Verlag, 1987. [6] Cope, David. Experiments in Musical Intelligence. Madison, WI: A-R Editions, 1989. [7] Cormen, Thomas H. & Leiserson, Charles E. & Rivest, Ronald L. Introduction to Algorithms. Cambridge, MA: MIT Press: 1997. [8] Di Battista, Giuseppe & Peter Eades & Roberto Tamassia & Ioannis G. Tollis. Graph Drawing: Algorithms for the Visualization of Graphs. Englewood Cliffs, NJ: Prentice Hall, 1999. [9] Eades, Peter & Kang Zhang, eds. Software Visualization. Singapore: World Scientific, 1996. [10] Eppstein, David. Geometry in Action. University of California Irvine. http://www.ics.uci.edu/ eppstein/gina/gdraw.html [11] Fux, Johann Joseph. (1725) The Study of Counterpoint from Johann Joseph Fux’s Gradus ad Parnassum, translated and edited by Alfred Mann. New York: W. W. Norton & Co., 1943. [12] Haus, Goffredo. Music Processing. Madison, WI: A-R Editions, 1992.
252
BIBLIOGRAPHY
253
[13] Himsolt, M. “GraphEd - A Graphical Platform for the Implementation of Graph Algorithms” Graph Drawing, Proceedings of DIMACS International Workshop GD’94. Lecture Notes in Computer Science 894. New York: Springer-Verlag, 1995. [14] Hoos, Holger & Keith Hamel & K. Renz & J. Kilian. “The GUIDO Music Notation Format - A Novel Approach for Adequately Representing Score-level Music.” ICMC’98 Proceedings. [15] Hoos, Holger H. & Keith Hamel. The GUIDO Music Notation Format Version 1.0: Specification Part I: Basic GUIDO. Technical Report TI 20/97. Darmstadt, Germany: Technische Universit¨at Darmstadt, 1997. http://www.informatik.tu-darmstadt.de/AFS/GUIDO/ [16] Horton, William. The Icon Book: Visual Symbols for Computer Systems and Documentation. New York: John Wiley & Sons, 1994. [17] Hylands, Christopher & Edward A. Lee & H. John Reekie. “The Tycho User Interface System.” Tcl/Tk Workshop ’97 Proceedings. Ed. Joseph A. Konstan & Brent Welch. Berkeley, CA: USENIX Association, 1997. [18] Ibrahim, Bertrand. “Visual Languages and Visual Programming.” WWW Virtual Library. [19] Kamada, Tomihisa. Visualizing Abstract Objects and Relations — A Constraint-Based Approach. Singapore: World Scientific, 1989. [20] Krenek, Ernst. (1953) Modal Counterpoint in the Style of the Sixteenth Century. New York: Boosey & Hawkes, 1959. [21] Lafever, Dave. “Visual Programming and Assistive Technology.” Dr. Dobb’s Journal. Aug. 1999. [22] Lee, Geoff. Object-Oriented GUI Application Development. Englewood Cliffs, NJ: PTR Prentice Hall, 1993. [23] Lutz, Mark. Programming Python. Sebastopol, CA: O’Reilly & Associates, 1996. [24] O’Keefe, Richard. The Craft of Prolog. Cambridge, MA: MIT Press, 1990. [25] Paulisch, Frances Newbery. EDGE: The Design of an Extendible Graph Editor. Lecture Notes in Computer Science Series, Ed. Gerhard Goos & Juris Hartmanis. New York: Springer-Verlag, 1991. [26] Randel, Don Michael, ed. The New Harvard Dictionary of Music. Cambridge, MA: Belknap Harvard, 1996. [27] Reekie, H. John & Edward A. Lee. “The Tycho Slate: Complex Drawing and Editing in Tcl/Tk.” Tcl/Tk Workshop ’98 Proceedings. Ed. Don Libes & Michael McLennan. Berkeley, CA: USENIX Association, 1998.
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.