/* Listing 1. A bare-bones segmentation and pc-set classification system. */ /* Prolog program to accompany "Logic-Programming Models of Music: A Semiotic Evaluation" */ /* John Roeder, Perspectives of New Music 57/1-2 (2019): 375-402. */ /* The program may be run in the free online SWISH Prolog interpreter. */ /* (see swish.swi-prolong.org). */ /* In the left pane of the SWISH window, click the + button, then the "create program" button. Then paste */ /* the contents of this file into the box labeled "Your Prolog rules and facts go here…" */ /* In the lower right pane, you may then enter queries, such as those at the end of this file */ /* Predicate calculus definitions for segmentation; compare Roeder 1988 ****************/ /* Assume representation of piece as a set of event(Pitch,Instrument,Attack,Duration) **/ /* some binary relations of events *****************************************************/ same_instrument(X,Y) :- instrument(X,I),instrument(Y,I). same_attack(X,Y) :- attack(X,A), attack(Y,A). same_pc(X,Y) :- pitch(X,P1), pitch(Y,P2), P1 mod 12 =:= P2 mod 12. sound_together(X,Y) :- attack(X,A1), attack(Y,A2), duration(X,D1), duration(Y,D2), A1 < A2 + D2, A2 < A1 + D1. /* relations of an event X to its properties *******************************************/ attack(X,A) :- exists_event(X), X=.. [event,_,_,A,_]. duration(X,D) :- exists_event(X), X=.. [event,_,_,_,D]. pitch(X,P) :- exists_event(X), X=.. [event,P,_,_,_]. instrument(X,I) :- exists_event(X), X=.. [event,_,I,_,_]. exists_event(X) :- event(P,I,A,D), X =.. [event, P,I,A,D]. /* set classification ************************************************************/ set_class(Event_list,Label) :- same_set(Perm, Event_list), /* for some Perm with same events as Event_list */ pc_interval_series(Perm, I), /* the series of events in Perm has pc-int-series I*/ inf(Label, I). /* which is an interval normal form named Label.*/ /* subsidiary predicates for set classification */ /* same_set generates permutations (1st argument) of given List (2nd arg) */ same_set([], []). same_set([H1|T1], List):- member(H1, List), delete(H1, List, Rest), same_set(T1, Rest). member(Event, [Event|_]). member(Event, [_|List]):- member(Event, List). delete(X, [X|L], L). delete(X, [Y|L1], [Y|L2]):- delete(X, L1, L2). /* relation of an event list to the corresponding series of pc intervals */ pc_interval_series([_],[]). pc_interval_series([E1, E2|Et], [H|T]) :- pc_interval(E1, E2, H), pc_interval_series([E2|Et], T). pc_interval(A, B, I) :- pitch(A,Pa), pitch(B,Pb), I is (Pb - Pa) mod 12. /* (intransitive) temporal relations of events, for sample queries 2 and 3 below. */ order(First,Last):- attack(First,A1), attack(Last,A2), A1 < A2. order(First, Middle, Last) :- attack(First,A1), attack(Middle,A2), attack(Last,A3), A1 < A2, A2 < A3. event_between(X,W,Y):- /* W is between X and Y in the same instrument */ same_instrument(X,Y), X \== Y, same_instrument(X,W), W \== X, W \== Y, order(X,W,Y). /* ********************************************************************/ /* Sample score encoding for analysis. (Example 1 of the article).*****/ /* Kurtág, Microlude for String Quartet, Op. 13, no. 2 */ /* C1 = 0; quarter note = 1; */ /* the score's relative durations are rendered precisely here */ event(25,viola,0,48). /* tremolo viola pedal C#3 throughout */ event(62,violin_2,8,4). event(43,violin_2,12,4). event(57,violin_1,13,3). event(76,violin_1,16,2). event(18,cello,16,2). event(41,cello,18,2). event(51,cello,20,4). event(62,violin_2,21,3). event(12,cello,24,4). event(54,cello,30,7). event(44,violin_2,32,8). event(55,violin_2,32,8). event(57,violin_1,32,6). event(54,violin_1,32,8). event(41,cello,37,3). event(58,violin_1,38,2). event(23,cello,42,2). event(14,cello,42,2). /* (partial) list of interval normal forms and their Forte set-class numbers */ inf('2-1', [1]). inf('2-2', [2]). inf('2-3', [3]). inf('2-4', [4]). inf('2-5', [5]). inf('2-6', [6]). inf('3-1', [1,1]). inf('3-2', [1,2]). inf('3-2', [2,1]). /* inversions have retrograde infs */ inf('3-3', [1,3]). inf('3-3', [3,1]). inf('3-4', [1,4]). inf('3-4', [4,1]). inf('3-6', [2,2]). inf('3-7', [2,3]). inf('3-7', [3,2]). inf('3-8', [2,4]). inf('3-8', [4,2]). inf('3-9', [2,5]). inf('3-10', [3,3]). inf('3-11', [3,4]). inf('3-11', [4,3]). inf('3-12', [4,4]). inf('4-1', [1,1,1]). /* etc. To keep this demo small, not all set classes are listed here. */ /****************** SAMPLE QUERIES ************************************************/ /* QUERY 1 finds primary segments; for same_pc substitute any defined binary relation */ /* To pose this query in SWISH, enter "query1(Segment)." in the lower right pane. */ query1(Segment) :- setof(X, same_pc(_,X), Segment). /* QUERY 2 finds dyad repetitions. */ /* To pose this query in SWISH, enter "query2(D1, D2)." in the lower right pane. */ query2([X,Y],[A,B]) :- same_instrument(X,Y), order(X,Y), not(event_between(X,_,Y)), same_instrument(A,B), A \== X, B \== Y, order(A,B), not(event_between(A,_,B)), same_pc(X,A), same_pc(Y,B). /* QUERY 3 find trichords of the same type imbricated within instrumental event-series */ /* To pose this query in SWISH, enter "query3(Trichord1, Trichord2, Label)." in the lower right pane. */ imb_trichord_type([X,Y,Z],Label):- same_instrument(X,Y), same_instrument(Y,Z), order(X,Y,Z), not(event_between(X,_,Y)), not(event_between(Y,_,Z)), set_class([X,Y,Z], Label). query3(A,C,Label):- imb_trichord_type(A,Label), imb_trichord_type(C,Label), not(same_set(A,C)). /* QUERY 4: finds instance where the same two pcs sound together */ /* To pose this query in SWISH, enter "query4(Dyad1, Dyad2)." in the lower right pane. */ query4([X,Y], [A,B]) :- sound_together(X,Y), X \== Y, sound_together(A,B), A \== X, A \== Y, same_pc(X,A), same_pc(Y,B). /* QUERY 5: classify all primary segments of the same type (here, same_attack)*/ /* Other binary relations can be substituted, such as same_instrument, but this system */ /* will not find any results unless the rest of interval normal forms are declared */ /* To pose this query in SWISH, enter "query5(Segment, Label)." in the lower right pane. */ query5(Segment, Label) :- setof(X, same_attack(_,X), Segment), set_class(Segment, Label).