1/* Part of plumdrum
    2	Copyright 2012-2015 Samer Abdallah (Queen Mary University of London; UCL)
    3	 
    4	This program is free software; you can redistribute it and/or
    5	modify it under the terms of the GNU Lesser General Public License
    6	as published by the Free Software Foundation; either version 2
    7	of the License, or (at your option) any later version.
    8
    9	This program is distributed in the hope that it will be useful,
   10	but WITHOUT ANY WARRANTY; without even the implied warranty of
   11	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12	GNU Lesser General Public License for more details.
   13
   14	You should have received a copy of the GNU Lesser General Public
   15	License along with this library; if not, write to the Free Software
   16	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   17*/
   18
   19:- module(kern_rules,
   20		[	spine/1     	% spine( spine).
   21		,	spine/2     	% spine( spine, record).
   22		,	next_spine/2	% next_spine( spine, spine).
   23		,	colinear/2  	% colinear( spine, spine).
   24		,	fwd/2        	% fwd( position, position).
   25
   26		,	all_spines/2	% all_spines( pred(spine,record), record).
   27		,	xinterp/3   	% xinterp( xinterp, spine, record).
   28		,	interp/2     	% interp( spine, record).
   29		,	tempo/2   		% tempo( nonneg, record).
   30		,	barlines/2  	% barlines( bar_attr, record).
   31		,	barline/3   	% barline( bar_attr, spine, record).
   32		,	barline/2   	% barline( spine, record).
   33      ,  articulation/3 % articulation( artic, spine, record).
   34		,	data/2       	% data( spine, record).
   35		,	note/4      	% note( kern_note, duration, time, spine).
   36		,	kern_note/5 	% kern_note( kern_note, duration, tie, spine, record).
   37
   38		% not dependent on db module
   39		,	data_token/2
   40		,	metre_to_bar_duration/2
   41
   42		,	time//1  % time( time, position, position).
   43		,	token//1 % token( token, position, position).
   44		]).

Kern inference rules

Usage

This module is designed to be called from a module containing a database describing a humdrum object, which may be created from a Humdrum file by using assert_humdrum/3 in humdrum_world.pl

The following predicates must be defined in the calling module.

        spine/4          % spine( xinterp, spine, record, record).
        ref/3            % ref(refcode, lang, text).
        duration/1       % duration( duration).
        num_spines/1     % num_spines( natural).
        num_records/1    % num_records( natural).

        time/2           % time( time, record).
        duration/2       % duration( duration, record).
        new_spine/2      % new_spine( spine, record).
        init_spine/3     % init_spine( xinterp, spine, record).
        change_spine/5   % change_spine( xinterp, xinterp, spine, spine, record).
        term_spine/3     % term_spine( xinterp, spine, record).
        join_spines/4    % join_spines( spine, spine, spine, record).
        split_spines/4   % split_spines( spine, spine, spine, record).

        interp/3         % interp( interp, spine, record).
        data/3           % data( data, spine, record).

Performance interpretation

dynamics

TODO (these are from the kern player - what are they doing here?)

*/

  152:- module_transparent
  153			spine/1
  154		,	spine/2
  155		,	next_spine/2
  156		,	tempo/2
  157		,	colinear/2
  158		,	fwd/2
  159		,	interp/2
  160		,	xinterp/3
  161		,	barlines/2
  162		,	barline/3
  163		,	barline/2
  164      ,  articulation/3
  165		,	data/2
  166		,	all_spines/2
  167		,	note/4
  168		,	kern_note/5
  169		,	tied_note//5
  170		,	next_tied_note/7
  171
  172		,	time//1
  173		,	token//1
		.  175
  176:- use_module(library(humdrum/kern)).
 spine(+S:spine, +R:record) is semidet
spine(-S:spine, -R:record) is nondet
True if spine S exists at record R. Enumerates all spines at all records on backtracking.
  183spine(S,R)     :- xinterp(_,S,R).
 spine(+S:spine) is semidet
spine(-S:spine) is nondet
True if spine S exists anywhere in the current database. Enumerates all spines on backtracking.
  190spine(S)       :- context_module(M), M:spine(_,S,_,_).
 all_spines(P:pred(spine,record), +R:record) is semidet
all_spines(P:pred(spine,record), -R:record) is nondet
True if P is true for all spines of record R.
  197all_spines(P,R) :-
  198	setof(S,call(P,S,R),SX), % find all spines for which P is true at R
  199	setof(S,spine(S,R),SX).  % make sure this equals all spines at R
 colinear(+S1:spine, +S2:spine) is semidet
colinear(+S1:spine, -S2:spine) is nondet
Succeeds if S2 is either the same spine as S1 or can be reached going forward in time via splits, joins, or representation changes.
  210colinear(S,S) :- spine(S).
  211
  212colinear(S1,S2) :-
  213	spine(S1),
  214	context_module(M),
  215	colinear1(M,[S1],S2).
  216
  217colinear1(M,SX1,S2) :-
  218	next_spine_set(M,SX1,SX2),
  219	(	member(S2,SX2)
  220	;	(	var(S2) -> SX3=SX2
  221		;	include(after(M,S2),SX2,SX3)
  222		),
  223		colinear1(M,SX3,S2)
  224	).
  225
  226after(M,S1,S2) :-
  227	M:spine(_,S1,S1_starts,_),
  228	M:spine(_,S2,_,S2_ends),
  229	S1_starts > S2_ends.
 next_spine(-S1:spine, -S2:spine) is nondet
True if spine S1 evolves into S2 without any intervening spines.
  236next_spine(S1,S2) :- context_module(M), next_spine(M,S1,S2).
  237
  238next_spine(M,S1,S2) :- M:change_spine(_,_,S1,S2,_).
  239next_spine(M,S1,S2) :- M:split_spines(S1,S1a,S1b,_), (S2=S1a;S2=S1b).
  240next_spine(M,S1,S2) :- M:join_spines(S1,_,S2,_); M:join_spines(_,S1,S2,_).
  241
  242next_spine_set(M,SX1,SX2) :-
  243	setof(SS, S1^(member(S1,SX1), next_spine(M,S1,SS)), SX2).
 fwd(+Pos:position, -Pos:position) is nondet
Step forwards one record following current spine.
position ---> (spine,record).
  252fwd((S1,R1),(S2,R2)) :-
  253	succ(R1,R2),
  254	spine(S2,R2),
  255	colinear(S1,S2).
 xinterp(-X:xinterp, +S:spine, +R:record) is semidet
xinterp(-X:xinterp, -S:spine, -R:record) is nondet
True if X is the exclusive interpretation for spine S which must exists at record R.
  264xinterp(X,S,R) :- context_module(M), M:spine(X,S,I,J), between(I,J,R).
 interp(-S:spine, -R:record) is nondet
True if record R is an interpretation record and S is a spine that exists at record R.
  271interp(S,R)  :- context_module(M), M:interp(_,S,R).
 barline(-B:bar_attr, +S:spine, +R:record) is semidet
barline(-B:bar_attr, -S:spine, -R:record) is nondet
True if spine S at record R is a bar line with attributes B.
  278barline(B,S,R) :- context_module(M), M:data(tok(bar(B)),S,R).
 barlines(-B:bar_attr, +R:record) is semidet
barlines(-B:bar_attr, -R:record) is nondet
True if all spines at record R are bar lines with attributes B.
  285barlines(B,R)  :- all_spines(data(tok(bar(B))),R).
 articulation(-A:artic, +S:spine, +R:record) is semidet
articulation(-A:artic, -S:spine, -R:record) is nondet
True if spine S at record R includes articulation marking A.
  292articulation(A,S,R) :- 
  293   context_module(M), 
  294   M:data(tok(Toks),S,R),
  295   member(articulation(A), Toks).
 tempo(-T:nonneg, +R:record) is semidet
tempo(-T:nonneg, -R:record) is nondet
True if all spines at record R are metronome interpretations with tempo T beats per second.
  303tempo(Tempo,R) :- all_spines(interp(metro(Tempo)),R).
 barline(+S:spine, +R:record) is semidet
barline(-S:spine, -R:record) is nondet
True if spine S at record R is a bar line.
  310barline(S,R) :- barline(_,S,R).
 data(+S:spine, +R:record) is semidet
data(-S:spine, -R:record) is nondet
True if spine S at record R is a data token.
  317data(S,R)    :- context_module(M), M:data(_,S,R).
 note(-P:kern_note, -D:duration, -T:time, -S:spine) is nondet
True if a note with pitch P and duration D is initiated at time T in spine S. This also applies to tied notes which may continue in the same spine or a colinear spine. If a tied note continues in a non-colinear spine, a warning is printed.
See also
- colinear/2
  330note(Note,Total_Dur,Time,S) :-
  331	context_module(M),
  332	kern_note(Note,Dur,Tie,S,R),
  333	M:time(Time,R),
  334	(	Tie=not_tied -> Total_Dur=Dur
  335	;	Tie=open 
  336	-> (	tied_note(cont, Note, Time, Dur, S, 0, Total_Dur) -> true
  337		;	format('* ERROR: tied note (~w) at ~w NOT FOUND.\n',[Note,(S/R)]),
  338			Total_Dur=Dur
  339		)
  340	).
  341
  342
  343tied_note( close, _, _, Dur, _) --> !, add(Dur).
  344tied_note( cont, Note, Time, Dur, S) --> 
  345	% jump ahead exactly Dur in time
  346	% and look for a colinear data token
  347	add(Dur),
  348	{	%context_module(M),
  349		add(Dur,Time,Time1),
  350		(	next_tied_note(colinear, Note, Time1, S, S1, Dur1, Tie)
  351		;	next_tied_note(kern_rules:true, Note, Time1, S, S1, Dur1, Tie)
  352		), !
  353	},
  354	tied_note(Tie,Note,Time1,Dur1,S1).
  355
  356add(X,Y,Z) :- Z is X+Y.
  357
  358next_tied_note(Constraint, Note, Time1, S,S1, Dur1, Tie) :-
  359	context_module(M),
  360	M:time(Time1,R1), 
  361	M:data(Data,S1,R1), Data\=tok(bar(_)),
  362	call(Constraint,S,S1),
  363	kern_note(Note,Dur1,Tie,S1,R1),
  364	(Tie=cont;Tie=close).
  365
  366true(_,_).
 kern_note(-P:kern_note, -D:duration, -T:tie, -S:spine, -R:record) is nondet
True if data token at spine S in record R is a kern note with pitch P and duration D. T indicates whether the note is part of a tie as follows:
kern_note ---> rest; pitch(pitch).

tie ---> not_tied   % atomic note, not part of a tie
       ; open       % onset of tied note
       ; cont       % continuation of tied note
       ; close      % final segment of tied note
       .
  384kern_note(Event,Dur,Tie,S,R) :-
  385	context_module(M),
  386	M:data(Data,S,R),
  387	xinterp(kern,S,R),
  388	data_token(Data,Tok),
  389	(	kern_pitch(Tok,Pitch) -> Event=pitch(Pitch)
  390	;	kern_rest(Tok)        -> Event=rest
  391   ),
  392	kern_duration(Tok,Dur),
  393	kern_tie(Tok,Tie).
  394
  395
  396kern_tie(Sigs,Tie) :- (member(par(Tie,tie),Sigs) -> true; Tie=not_tied).
 data_token(+D:data, -T:token) is nondet
True when T is a token or subtoken in the given data term.
  401data_token(tok(T),T) :- !, T\=bar(_).
  402data_token(sub(TX),T) :- member(T,TX).
 metre_to_bar_duration(+M:metre, -D:duration) is det
Compute duration in whole notes of a bar in the given metre.
  407metre_to_bar_duration(metre(N,D),BD) :- BD is N rdiv D. 
  408
  409
  410% (Slice,Record) DCG 
 time(-Time:rational, +Loc:spine_rec, -Loc:spine_rec) is det
  413time(T,(S,R),(S,R)) :- context_module(M), M:time(T,R).
 token(-Token, +Loc:spine_rec, -Loc:spine_rec) is det
  416token(T,(S,R),(S,R)) :- context_module(M), M:data(X,S,R), data_token(X,T)