Daniel@0
|
1 /* Part of DML (Digital Music Laboratory)
|
Daniel@0
|
2 Copyright 2014-2015 Samer Abdallah, University of London
|
Daniel@0
|
3
|
Daniel@0
|
4 This program is free software; you can redistribute it and/or
|
Daniel@0
|
5 modify it under the terms of the GNU General Public License
|
Daniel@0
|
6 as published by the Free Software Foundation; either version 2
|
Daniel@0
|
7 of the License, or (at your option) any later version.
|
Daniel@0
|
8
|
Daniel@0
|
9 This program is distributed in the hope that it will be useful,
|
Daniel@0
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
Daniel@0
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
Daniel@0
|
12 GNU General Public License for more details.
|
Daniel@0
|
13
|
Daniel@0
|
14 You should have received a copy of the GNU General Public
|
Daniel@0
|
15 License along with this library; if not, write to the Free Software
|
Daniel@0
|
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
Daniel@0
|
17 */
|
Daniel@0
|
18
|
Daniel@0
|
19 :- module(computations,
|
Daniel@0
|
20 [ computation/3
|
Daniel@0
|
21 , computation_memo/3
|
Daniel@0
|
22 , vamp/3
|
Daniel@0
|
23 , transform/2
|
Daniel@0
|
24 , transform_param/3
|
Daniel@0
|
25 , sparse_to_dense/4
|
Daniel@0
|
26 , fold_commutative/3
|
Daniel@0
|
27 , map_reduce/4
|
Daniel@0
|
28 , map_reduce/5
|
Daniel@0
|
29 , unzip/3
|
Daniel@0
|
30 , pair/3
|
Daniel@0
|
31 , add/3, mul/3, div_by/3
|
Daniel@0
|
32 , fst/3, snd/3
|
Daniel@0
|
33 , with_csv_rows/3
|
Daniel@0
|
34 , csv_op/3
|
Daniel@0
|
35 , (*)/4
|
Daniel@0
|
36 , array_list/2
|
Daniel@0
|
37 , microtone_map/4
|
Daniel@0
|
38 , rows_cols/3
|
Daniel@0
|
39 , tempo_curves_stats/3
|
Daniel@0
|
40 , map_edges/3
|
Daniel@0
|
41 , csv_pitch_count_prob/5
|
Daniel@0
|
42 , pitch_hist_prob/4
|
Daniel@0
|
43 , pitch_name_number/2
|
Daniel@0
|
44 , pitch_number_name/2
|
Daniel@0
|
45 , freq_note_number/2
|
Daniel@0
|
46 , histof/4
|
Daniel@0
|
47 , histof/3
|
Daniel@0
|
48 , weighted_histof/5
|
Daniel@0
|
49 , weighted_histof/4
|
Daniel@0
|
50 ]).
|
Daniel@0
|
51
|
Daniel@0
|
52 :- use_module(library(rdfutils)).
|
Daniel@0
|
53 :- use_module(library(dcg/basics)).
|
Daniel@0
|
54 :- use_module(library(dcg_core)).
|
Daniel@0
|
55 :- use_module(library(dcg_macros)).
|
Daniel@0
|
56 :- use_module(library(csvutils)).
|
Daniel@0
|
57 :- use_module(library(listutils)).
|
Daniel@0
|
58 :- use_module(library(lambda)).
|
Daniel@0
|
59 :- use_module(library(memo)).
|
Daniel@0
|
60 :- use_module(library(mlserver)).
|
Daniel@0
|
61 :- use_module(library(sandbox)).
|
Daniel@0
|
62 :- use_module(library(backend_json)).
|
Daniel@0
|
63 :- use_module(library(real)).
|
Daniel@0
|
64
|
Daniel@0
|
65 :- volatile_memo pitch_name_number(+atom,-integer).
|
Daniel@0
|
66
|
Daniel@0
|
67 :- initialization <-library(pracma).
|
Daniel@0
|
68
|
Daniel@0
|
69 :- rdf_meta vamp(?,r,r).
|
Daniel@0
|
70
|
Daniel@0
|
71 %% vamp(+T:transform_class, +R:uri, -X:uri) is nondet.
|
Daniel@0
|
72 %% vamp(-T:transform_class, -R:uri, -X:uri) is nondet.
|
Daniel@0
|
73 %
|
Daniel@0
|
74 % See transform/2 for values transform_class type.
|
Daniel@0
|
75 vamp(Class,In,Out) :-
|
Daniel@0
|
76 transform(Class,F),
|
Daniel@0
|
77 computation(F,In,Out).
|
Daniel@0
|
78
|
Daniel@0
|
79 %% transform(+T:transform_class, -R:uri) is det.
|
Daniel@0
|
80 %% transform(-T:transform_class, -R:uri) is nondet.
|
Daniel@0
|
81 %
|
Daniel@0
|
82 % Mapping between short transform descriptors and full VAMP transform URIs for
|
Daniel@0
|
83 % transforms currently known to the system. Currently recognised transform classes are:
|
Daniel@0
|
84 % ==
|
Daniel@0
|
85 % transform_class ---> transcription % equivalent to transcription(0)
|
Daniel@0
|
86 % ; transcription({0,1}) % 0: semitone, 1:microtonal
|
Daniel@0
|
87 % ; beats({beatroot,qm}) % beats using one of two plugins
|
Daniel@0
|
88 % ; beats % beats using any plugin
|
Daniel@0
|
89 % ; tempo
|
Daniel@0
|
90 % ; chords
|
Daniel@0
|
91 % ; chord_notes
|
Daniel@0
|
92 % ; key
|
Daniel@0
|
93 % ; tonic
|
Daniel@0
|
94 % ; chromagram
|
Daniel@0
|
95 % ; mfcc.
|
Daniel@0
|
96 % ==
|
Daniel@0
|
97 transform(Class,Transform) :-
|
Daniel@0
|
98 ground(Class), !,
|
Daniel@0
|
99 transforms(Class,Transforms),
|
Daniel@0
|
100 member(Transform,Transforms).
|
Daniel@0
|
101 transform(Class,Transform) :-
|
Daniel@0
|
102 transform1(Class,Transform).
|
Daniel@0
|
103
|
Daniel@0
|
104 % memoised collection of all transforms
|
Daniel@0
|
105 :- volatile_memo transforms(+ground,-list(atom)).
|
Daniel@0
|
106 transforms(Class,Transforms) :-
|
Daniel@0
|
107 findall(T,transform1(Class,T),Transforms).
|
Daniel@0
|
108
|
Daniel@0
|
109 %% transform1(-Class:transform_class,-R:uri) is nondet.
|
Daniel@0
|
110 % Searches the RDF database for resources of class vamp:Transform which
|
Daniel@0
|
111 % match the various transform classes. See transform/2.
|
Daniel@0
|
112 transform1(beats,Transform) :- transform1(beats(_),Transform).
|
Daniel@0
|
113 transform1(transcription,Transform) :- transform1(transcription(0),Transform).
|
Daniel@0
|
114 transform1(transcription(Fine),Transform) :-
|
Daniel@0
|
115 transform1(notes,Transform),
|
Daniel@0
|
116 transform_param(Transform,finetune,Lit),
|
Daniel@0
|
117 literal_number(Lit,Fine).
|
Daniel@0
|
118
|
Daniel@0
|
119 transform1(Class,Transform) :-
|
Daniel@0
|
120 def_transform(Class,Plugin,Output),
|
Daniel@0
|
121 rdf(Transform,vamp:plugin,Plugin),
|
Daniel@0
|
122 rdf(Transform,vamp:output,Output).
|
Daniel@0
|
123
|
Daniel@0
|
124 :- rdf_meta transform_param(r,r,-).
|
Daniel@0
|
125 transform_param(Transform,ParamId,Value) :-
|
Daniel@0
|
126 rdf(Transform,vamp:parameter_binding,Binding),
|
Daniel@0
|
127 rdf(Binding,vamp:parameter,Param),
|
Daniel@0
|
128 rdf(Param,vamp:identifier,literal(ParamId)),
|
Daniel@0
|
129 rdf(Binding,vamp:value,literal(Value)).
|
Daniel@0
|
130
|
Daniel@0
|
131
|
Daniel@0
|
132 :- rdf_meta def_transform(-,r,r).
|
Daniel@0
|
133
|
Daniel@0
|
134 % transform class, plugin, output
|
Daniel@0
|
135 def_transform(notes, vamp_plugins:'silvet#silvet', vamp_plugins:'silvet#silvet_output_notes').
|
Daniel@0
|
136 def_transform(pitch_activation, vamp_plugins:'silvet#silvet', vamp_plugins:'silvet#silvet_output_pitchactivation').
|
Daniel@0
|
137 def_transform(silvet_timefreq, vamp_plugins:'silvet#silvet', vamp_plugins:'silvet#silvet_output_timefreq').
|
Daniel@0
|
138 def_transform(beats(beatroot), vamp_plugins:'beatroot-vamp#beatroot', vamp_plugins:'beatroot-vamp#beatroot_output_beats').
|
Daniel@0
|
139 def_transform(beats(qm), vamp_plugins:'qm-vamp-plugins#qm-tempotracker', vamp_plugins:'qm-vamp-plugins#qm-tempotracker_output_beats').
|
Daniel@0
|
140 def_transform(tempo, vamp_plugins:'qm-vamp-plugins#qm-tempotracker', vamp_plugins:'qm-vamp-plugins#qm-tempotracker_output_tempo').
|
Daniel@0
|
141 def_transform(onset_dfn(tempo), vamp_plugins:'qm-vamp-plugins#qm-tempotracker', vamp_plugins:'qm-vamp-plugins#qm-tempotracker_output_detection_fn').
|
Daniel@0
|
142 def_transform(chords, vamp_plugins:'nnls-chroma#chordino', vamp_plugins:'nnls-chroma#chordino_output_simplechord').
|
Daniel@0
|
143 def_transform(chord_notes, vamp_plugins:'nnls-chroma#chordino', vamp_plugins:'nnls-chroma#chordino_output_chordnotes').
|
Daniel@0
|
144 def_transform(harmonic_change, vamp_plugins:'nnls-chroma#chordino', vamp_plugins:'nnls-chroma#chordino_output_harmonicchange').
|
Daniel@0
|
145 def_transform(key, vamp_plugins:'qm-vamp-plugins#qm-keydetector', vamp_plugins:'qm-vamp-plugins#qm-keydetector_output_key').
|
Daniel@0
|
146 def_transform(key_strength, vamp_plugins:'qm-vamp-plugins#qm-keydetector', vamp_plugins:'qm-vamp-plugins#qm-keydetector_output_keystrength').
|
Daniel@0
|
147 def_transform(tonic, vamp_plugins:'qm-vamp-plugins#qm-keydetector', vamp_plugins:'qm-vamp-plugins#qm-keydetector_output_tonic').
|
Daniel@0
|
148 def_transform(mode, vamp_plugins:'qm-vamp-plugins#qm-keydetector', vamp_plugins:'qm-vamp-plugins#qm-keydetector_output_mode').
|
Daniel@0
|
149 def_transform(mfcc, vamp_plugins:'qm-vamp-plugins#qm-mfcc', vamp_plugins:'qm-vamp-plugins#qm-mfcc_output_coefficients').
|
Daniel@0
|
150 def_transform(mfcc_means, vamp_plugins:'qm-vamp-plugins#qm-mfcc', vamp_plugins:'qm-vamp-plugins#qm-mfcc_output_means').
|
Daniel@0
|
151 def_transform(onsets, vamp_plugins:'qm-vamp-plugins#qm-onsetdetector',vamp_plugins:'qm-vamp-plugins#qm-onsetdetector_output_onsets').
|
Daniel@0
|
152 def_transform(onset_dfn, vamp_plugins:'qm-vamp-plugins#qm-onsetdetector',vamp_plugins:'qm-vamp-plugins#qm-onsetdetector_output_detection_fn').
|
Daniel@0
|
153 def_transform(onset_smoothed_dfn, vamp_plugins:'qm-vamp-plugins#qm-onsetdetector',vamp_plugins:'qm-vamp-plugins#qm-onsetdetector_output_smoothed_df').
|
Daniel@0
|
154 def_transform(chromagram, vamp_plugins:'qm-vamp-plugins#qm-chromagram', vamp_plugins:'qm-vamp-plugins#qm-chromagram_output_chromagram').
|
Daniel@0
|
155 def_transform(chromameans, vamp_plugins:'qm-vamp-plugins#qm-chromagram', vamp_plugins:'qm-vamp-plugins#qm-chromagram_output_chromameans').
|
Daniel@0
|
156 def_transform(chromagram(upper), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_chroma').
|
Daniel@0
|
157 def_transform(chromagram(bass), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_basschroma').
|
Daniel@0
|
158 def_transform(chromagram(both), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_bothchroma').
|
Daniel@0
|
159 def_transform(spectrogram(semitone), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_semitonespectrum').
|
Daniel@0
|
160 def_transform(spectrogram(log_freq), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_logfreqspec').
|
Daniel@0
|
161 def_transform(spectrogram(tuned), vamp_plugins:'nnls-chroma#nnls-chroma', vamp_plugins:'nnls-chroma#nnls-chroma_output_tunedlogfreqspec').
|
Daniel@0
|
162 def_transform(melody, vamp_plugins:'mtg-melodia#melodia', vamp_plugins:'mtg-melodai#melodia_output_melody').
|
Daniel@0
|
163 def_transform(spectrogram(const_q), vamp_plugins:'qm-vamp-plugins#qm-constantq', vamp_plugins:'qm-vamp-plugins#qm-constantq_output_constantq').
|
Daniel@0
|
164 def_transform(segments, vamp_plugins:'qm-vamp-plugins#qm-segmenter', vamp_plugins:'qm-vamp-plugins#qm-segmenter_output_segmentation').
|
Daniel@0
|
165 def_transform(speech_music, vamp_plugins:'bbc-vamp-plugins#bbc-speechmusic-segmenter',
|
Daniel@0
|
166 vamp_plugins:'bbc-vamp-plugins#bbc-speechmusic-segmenter_output_segmentation').
|
Daniel@0
|
167 def_transform(speech_music_dfn, vamp_plugins:'bbc-vamp-plugins#bbc-speechmusic-segmenter',
|
Daniel@0
|
168 vamp_plugins:'bbc-vamp-plugins#bbc-speechmusic-segmenter_output_skewness').
|
Daniel@0
|
169
|
Daniel@0
|
170
|
Daniel@0
|
171 %% computation_memo(+Transform:uri,+Input:uri,-Output:uri) is det.
|
Daniel@0
|
172 % Memoised functional Relation between transforms, inputs and outputs.
|
Daniel@0
|
173
|
Daniel@0
|
174 :- multifile do_computation/3.
|
Daniel@0
|
175
|
Daniel@0
|
176 :- rdf_meta computation_memo(r,r,r).
|
Daniel@0
|
177 computation_memo(Fn,Input,Output) :-
|
Daniel@0
|
178 must_be(atom,Fn),
|
Daniel@0
|
179 must_be(atom,Input),
|
Daniel@0
|
180 must_be(var,Output),
|
Daniel@0
|
181 ( computation(Fn,Input,Output) -> true
|
Daniel@0
|
182 ; memo:timed(computations:do_computation(Fn,Input,Output),comp(_,Time,Dur)),
|
Daniel@0
|
183 format_time(atom(Timestamp),'%FT%T%:z',Time),
|
Daniel@0
|
184 memo:hostname(Host),
|
Daniel@0
|
185 phrase( ( vamp:computation_triples(Comp,Input,Fn,Output),
|
Daniel@0
|
186 vamp:rdf(Comp,dml:'comp/time',literal(type(xsd:dateTime,Timestamp))),
|
Daniel@0
|
187 vamp:rdf(Comp,dml:'comp/duration',literal(type(xsd:float,Dur))),
|
Daniel@0
|
188 vamp:rdf(Comp,dml:'comp/host',literal(Host))
|
Daniel@0
|
189 ), Triples,[]),
|
Daniel@0
|
190 forall(member(rdf(S,P,O),Triples), rdf_assert(S,P,O,vamp_memo))
|
Daniel@0
|
191 ).
|
Daniel@0
|
192
|
Daniel@0
|
193
|
Daniel@0
|
194 %% computation(-Transform:uri,-Input:uri,-Output:uri) is nondet.
|
Daniel@0
|
195 % Relation between transforms, inputs and outputs using RDF database
|
Daniel@0
|
196 % of existing computations.
|
Daniel@0
|
197
|
Daniel@0
|
198 :- rdf_meta computation(r,r,r).
|
Daniel@0
|
199 computation(Fn,Input,Output) :- nonvar(Output), !,
|
Daniel@0
|
200 rdf(Comp,dml:'comp/output',Output),
|
Daniel@0
|
201 rdf(Comp,dml:'comp/function',Fn),
|
Daniel@0
|
202 rdf(Comp,dml:'comp/input',Input).
|
Daniel@0
|
203
|
Daniel@0
|
204 computation(Fn,Input,Output) :- nonvar(Input), !,
|
Daniel@0
|
205 rdf(Comp,dml:'comp/input',Input),
|
Daniel@0
|
206 rdf(Comp,dml:'comp/function',Fn),
|
Daniel@0
|
207 rdf(Comp,dml:'comp/output',Output).
|
Daniel@0
|
208
|
Daniel@0
|
209 computation(Fn,Input,Output) :-
|
Daniel@0
|
210 rdf(Comp,dml:'comp/input',Input),
|
Daniel@0
|
211 rdf(Comp,dml:'comp/function',Fn),
|
Daniel@0
|
212 rdf(Comp,dml:'comp/output',Output).
|
Daniel@0
|
213
|
Daniel@0
|
214 % ------------ Framework for doing computations on CSV files -----------
|
Daniel@0
|
215 :- meta_predicate with_csv_rows(2,+,-).
|
Daniel@0
|
216 with_csv_rows(Pred,CSV,Result) :-
|
Daniel@0
|
217 insist(uri_to_csv(CSV,Rows)),
|
Daniel@0
|
218 insist(call(Pred,Rows,Result), failed_on_csv(Pred,CSV)).
|
Daniel@0
|
219
|
Daniel@0
|
220 csv_op(Op,CSV,Result) :-
|
Daniel@0
|
221 ( memoise(Op)
|
Daniel@0
|
222 -> csv_op_memo(Op,CSV,Result) % ,_-ok)
|
Daniel@0
|
223 ; with_csv_rows(row_op(Op),CSV,Result)
|
Daniel@0
|
224 ),
|
Daniel@0
|
225 debug(computations(item),'Done csv_op(~q,~q).',[Op,CSV]).
|
Daniel@0
|
226
|
Daniel@0
|
227 sandbox:safe_primitive(computations:csv_op(_,_,_)).
|
Daniel@0
|
228
|
Daniel@0
|
229 :- persistent_memo csv_op_memo(+ground,+atom,-ground).
|
Daniel@0
|
230 csv_op_memo(Op,CSV,Result) :- with_csv_rows(row_op(Op),CSV,Result).
|
Daniel@0
|
231
|
Daniel@0
|
232 :- initialization time(memo_attach(memo(computations2),[])).
|
Daniel@0
|
233
|
Daniel@0
|
234 memoise(pitch_hist(_)).
|
Daniel@0
|
235 memoise(freq_hist(_,_)).
|
Daniel@0
|
236 memoise(tempo_hist(_,_)).
|
Daniel@0
|
237 memoise(uniform_tempo(_)).
|
Daniel@0
|
238 memoise(uniform_tempo_r(_)).
|
Daniel@0
|
239 memoise(normalised_tempo(_)).
|
Daniel@0
|
240 memoise(normalised_tempo_r(_)).
|
Daniel@0
|
241
|
Daniel@0
|
242 row_op(id,Rows,Rows) :- !.
|
Daniel@0
|
243 row_op(column(N),Rows,Vals) :- !, maplist(arg(N),Rows,Vals).
|
Daniel@0
|
244 row_op(array,Rows,Array) :- !, maplist(row_list(_),Rows,Array).
|
Daniel@0
|
245 row_op(chord_hist,Rows,Hist) :- !, histof(Chord,T,member(row(T,Chord),Rows),Hist).
|
Daniel@0
|
246 row_op(pitch_hist(none),Rows,Hist) :- !, histof(Pitch,t(T,Dur),note(Rows,T,Dur,Pitch),Hist).
|
Daniel@0
|
247 row_op(pitch_hist(W),Rows,Hist) :- !, weighted_histof(Weight,Pitch,t(T,Dur),weighted_note(W,Rows,T,Dur,Pitch,Weight),Hist).
|
Daniel@0
|
248 row_op(beat_times,Rows,Times) :- !, row_op(column(1),Rows,Times).
|
Daniel@0
|
249 row_op(onset_times,Rows,Times) :- !, row_op(column(1),Rows,Times).
|
Daniel@0
|
250 row_op(tempo,Rows,Tempo) :- !, maplist(row_pair(1,2),Rows,Tempo).
|
Daniel@0
|
251 row_op(uniform_tempo(DT),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), uniform_sample(ml,cubic,DT,Tempo,Samples).
|
Daniel@0
|
252 row_op(uniform_tempo_r(DT),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), uniform_sample(r,cubic,DT,Tempo,Samples).
|
Daniel@0
|
253 row_op(uniform_tempo(Meth,DT),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), uniform_sample(ml,Meth,DT,Tempo,Samples).
|
Daniel@0
|
254 row_op(uniform_tempo_r(Meth,DT),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), uniform_sample(r,Meth,DT,Tempo,Samples).
|
Daniel@0
|
255 row_op(normalised_tempo(N),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), normalised_sample(ml,N,Tempo,Samples).
|
Daniel@0
|
256 row_op(normalised_tempo_r(N),Rows,Samples) :- !, row_op(tempo,Rows,Tempo), normalised_sample(r,N,Tempo,Samples).
|
Daniel@0
|
257 row_op(tempo_hist(DT,Map),Rows,Edges-Counts) :- !,
|
Daniel@0
|
258 row_op(uniform_tempo(DT),Rows,_-Tempo),
|
Daniel@0
|
259 M===Map,
|
Daniel@0
|
260 [arr(Counts), arr(Edges)] ===
|
Daniel@0
|
261 deal(accumhist(flatten(feval(M,Tempo)),1,cardr(M)), flatten(edges(M))).
|
Daniel@0
|
262
|
Daniel@0
|
263 row_op(tempo_hist_r(DT,Map),Rows,Edges-Counts) :- !,
|
Daniel@0
|
264 map_to_r_edges(Map,REdges),
|
Daniel@0
|
265 row_op(uniform_tempo_r(DT),Rows,_-Tempo),
|
Daniel@0
|
266 Counts <- table(cut(Tempo,breaks=REdges)),
|
Daniel@0
|
267 Edges <- REdges.
|
Daniel@0
|
268 % memberchk(counts=Counts,Hist),
|
Daniel@0
|
269 % memberchk(breaks=Edges,Hist).
|
Daniel@0
|
270
|
Daniel@0
|
271 row_op(freq_hist(Map1,W),Rows,Counts) :-
|
Daniel@0
|
272 column(transcription,freq,J),
|
Daniel@0
|
273 ( W=none
|
Daniel@0
|
274 -> maplist(arg(J),Rows,Freqs), Weights=1
|
Daniel@0
|
275 ; column(transcription,W,I),
|
Daniel@0
|
276 rows_cols([J,I],Rows,[Freqs,Weights])
|
Daniel@0
|
277 ),
|
Daniel@0
|
278 Map===Map1, % evaluate map and keep in Matlab workspace
|
Daniel@0
|
279 X=feval(Map,12*log2(Freqs)-(12*log2(440)-69)),
|
Daniel@0
|
280 array_list(accumhist(flatten(X),flatten(Weights),cardr(Map)),Counts).
|
Daniel@0
|
281
|
Daniel@0
|
282 row_op(freq_hist_r(Map1,W),Rows,Counts) :-
|
Daniel@0
|
283 column(transcription,freq,J),
|
Daniel@0
|
284 map_to_r_edges(Map1,REdges),
|
Daniel@0
|
285 Pitches=12*log2(Freqs)-(12*log2(440)-69),
|
Daniel@0
|
286 ( W=none
|
Daniel@0
|
287 -> maplist(arg(J),Rows,Freqs),
|
Daniel@0
|
288 Hist <- hist(Pitches,breaks=REdges,plot=0)
|
Daniel@0
|
289 ; column(transcription,W,I),
|
Daniel@0
|
290 rows_cols([J,I],Rows,[Freqs,Weights]),
|
Daniel@0
|
291 Hist <- hist(Pitches,Weights,breaks=REdges,plot=0)
|
Daniel@0
|
292 ),
|
Daniel@0
|
293 memberchk(counts=Counts,Hist).
|
Daniel@0
|
294
|
Daniel@0
|
295 map_edges(r,Map,Edges) :-
|
Daniel@0
|
296 map_to_r_edges(Map,Expr),
|
Daniel@0
|
297 Edges <- Expr.
|
Daniel@0
|
298 map_edges(ml,Map,Edges) :-
|
Daniel@0
|
299 array_list(edges(Map),Edges).
|
Daniel@0
|
300
|
Daniel@0
|
301 map_to_r_edges(expmap(Min,Max,N),sapply(seq(log(Min),log(Max),len=N+1),exp)).
|
Daniel@0
|
302 map_to_r_edges(binmap(Min,Max,N),seq(Min-HalfWidth,Max+HalfWidth,len=N+1)) :- HalfWidth=(Max-Min)/(2*(N-1)).
|
Daniel@0
|
303
|
Daniel@0
|
304 column(Format, Name, Number) :- csv(Format,Row), arg(Number,Row,Name).
|
Daniel@0
|
305 csv(transcription, row(time,dur,freq,vel,pitch)).
|
Daniel@0
|
306
|
Daniel@0
|
307 gather(P,Rows,Xs) :- findall(X,(member(R,Rows),call(P,R,X)),Xs).
|
Daniel@0
|
308
|
Daniel@0
|
309
|
Daniel@0
|
310 microtone_map(Min,Max,Res,binmap(Min,Max,N)) :- N is (Max-Min)*Res+1.
|
Daniel@0
|
311
|
Daniel@0
|
312 % qfreq(Q,Rows,T,Dur,QFreq) :- member(row(T,Dur,Freq,_,_),Rows), qlogfreq(Q,Freq,QFreq).
|
Daniel@0
|
313 % weighted_qfreq(dur,Q,Rows,T,Dur,QFreq,Dur) :- member(row(T,Dur,Freq,_,_),Rows), qlogfreq(Q,Freq,QFreq).
|
Daniel@0
|
314 % weighted_qfreq(vel,Q,Rows,T,Dur,QFreq,Vel) :- member(row(T,Dur,Freq,Vel,_),Rows), qlogfreq(Q,Freq,QFreq).
|
Daniel@0
|
315 % qlogfreq(Q,In,Out) :- B is 12/log(2), Out is 69+round(Q*B*(log(In)-log(440)))/Q.
|
Daniel@0
|
316 % goal_expansion(qlogfreq(Q,In,Out), Out is 69+round(Q*B*(log(In)-A))/Q) :- B is 12/log(2), A=log(440).
|
Daniel@0
|
317
|
Daniel@0
|
318 uniform_sample(DT,In,Out) :- uniform_sample(ml,linear,DT,In,Out).
|
Daniel@0
|
319
|
Daniel@0
|
320 uniform_sample(_,_,_,[Time-Val],[Time]-[Val]) :- !.
|
Daniel@0
|
321 uniform_sample(Lang,Meth,DT,Pairs,Times1-Vals1) :-
|
Daniel@0
|
322 unzip(Pairs,Times,Vals),
|
Daniel@0
|
323 aggregate(max(T), member(T,Times), MaxT),
|
Daniel@0
|
324 interp1(Lang,Meth,0:DT:MaxT,Times,Vals,Times1,Vals1).
|
Daniel@0
|
325
|
Daniel@0
|
326 normalised_sample(N,In,Out) :- normalised_sample(ml,N,In,Out).
|
Daniel@0
|
327
|
Daniel@0
|
328 normalised_sample(_,N,[Time-Val],Times-Vals) :- !,
|
Daniel@0
|
329 rep(N,Time,Times),
|
Daniel@0
|
330 rep(N,Val,Vals).
|
Daniel@0
|
331 normalised_sample(Lang,N,Pairs,Times1-Vals1) :-
|
Daniel@0
|
332 unzip(Pairs,Times,Vals),
|
Daniel@0
|
333 aggregate(max(T), member(T,Times), MaxT),
|
Daniel@0
|
334 interp1(Lang,cubic,linspace(0,MaxT,N),Times,Vals,Times1,Vals1).
|
Daniel@0
|
335
|
Daniel@0
|
336 interp1(ml,Meth,TSpec,Times,Vals,Times1,Vals1) :-
|
Daniel@0
|
337 length(Times,N),
|
Daniel@0
|
338 (N<4 -> Method=q(linear); Method=q(Meth)),
|
Daniel@0
|
339 T1===flatten(TSpec),
|
Daniel@0
|
340 [arr(Times1), arr(Vals1)]===deal(T1,interp1(Times,Vals,T1,Method)).
|
Daniel@0
|
341 interp1(r,Meth,TSpec,Times,Vals,Times1,Vals1) :-
|
Daniel@0
|
342 ml_r(TSpec,RTSpec),
|
Daniel@0
|
343 length(Times,N),
|
Daniel@0
|
344 (N<4 -> Method = +linear; Method = +Meth),
|
Daniel@0
|
345 Times1 <- RTSpec,
|
Daniel@0
|
346 Vals1 <- interp1(Times,Vals,Times1,Method).
|
Daniel@0
|
347
|
Daniel@0
|
348 ml_r(X1:DX:X2, seq(X1,X2,DX)).
|
Daniel@0
|
349 ml_r(linspace(X1,X2,N), seq(X1,X2,len=N)).
|
Daniel@0
|
350
|
Daniel@0
|
351 array_list(Array,List) :- arr(List)===flatten(Array).
|
Daniel@0
|
352
|
Daniel@0
|
353 :- meta_predicate '*'(2,2,+,-).
|
Daniel@0
|
354 *(F1,F2,X,Y) :- call(F1,X,Z), call(F2,Z,Y).
|
Daniel@0
|
355
|
Daniel@0
|
356 note(Rows,T,Dur,NN) :- member(row(T,Dur,_,_,Pitch),Rows), pitch_name_number(Pitch,NN).
|
Daniel@0
|
357
|
Daniel@0
|
358 weighted_note(dur,Rows,T,Dur,NN,Dur) :- member(row(T,Dur,_,_,Pitch),Rows), pitch_name_number(Pitch,NN).
|
Daniel@0
|
359 weighted_note(vel,Rows,T,Dur,NN,Vel) :- member(row(T,Dur,_,Vel,Pitch),Rows), pitch_name_number(Pitch,NN).
|
Daniel@0
|
360 weighted_note(dur*vel,Rows,T,Dur,NN,Weight) :-
|
Daniel@0
|
361 member(row(T,Dur,_,Vel,Pitch),Rows), pitch_name_number(Pitch,NN),
|
Daniel@0
|
362 Weight is Dur*Vel.
|
Daniel@0
|
363
|
Daniel@0
|
364
|
Daniel@0
|
365 tempo_curves_stats(ml,Curves, _{means:Means,std_devs:StdDevs}) :-
|
Daniel@0
|
366 Data===arr(Curves),
|
Daniel@0
|
367 array_list(mean(Data,2),Means),
|
Daniel@0
|
368 array_list(std(Data,0,2),StdDevs).
|
Daniel@0
|
369
|
Daniel@0
|
370 tempo_curves_stats(r,Curves, _{means:Means,std_devs:Stds}) :-
|
Daniel@0
|
371 data <- Curves,
|
Daniel@0
|
372 Means <- apply(data,2,mean),
|
Daniel@0
|
373 Stds <- apply(data,2,sd).
|
Daniel@0
|
374
|
Daniel@0
|
375 :- meta_predicate histof(-,0,-)
|
Daniel@0
|
376 , histof(-,-,0,-)
|
Daniel@0
|
377 , weighted_histof(-,-,0,-)
|
Daniel@0
|
378 , weighted_histof(-,-,-,0,-)
|
Daniel@0
|
379 .
|
Daniel@0
|
380
|
Daniel@0
|
381 %% histof(@Dom:A,+Goal:callable,-Hist:list(pair(A,natural))) is nondet.
|
Daniel@0
|
382 % Compile a histogram over values taken by the variable Dom while enumerating
|
Daniel@0
|
383 % all solutions of Goal. Repeated solutions of Goal with the same values
|
Daniel@0
|
384 % count as distinct observations. See also histof/4.
|
Daniel@0
|
385 histof(Dom,Goal,Hist) :-
|
Daniel@0
|
386 setof(Dom-N,aggregate(count,Goal,N),Hist).
|
Daniel@0
|
387
|
Daniel@0
|
388 %% histof(@Dom:A,@Disc:_,+Goal:callable,-Hist:list(pair(A,natural))) is nondet.
|
Daniel@0
|
389 % Compile a histogram over values taken by the variable Dom while enumerating
|
Daniel@0
|
390 % all solutions of Goal. The value of Disc is used to discriminate between
|
Daniel@0
|
391 % solutions of Goal with the same value of Dom. See also histof/3 and aggregate/4
|
Daniel@0
|
392 % for more information about discriminator variables.
|
Daniel@0
|
393 histof(Dom,Disc,Goal,Hist) :-
|
Daniel@0
|
394 setof(Dom-N,aggregate(count,Disc,Goal,N),Hist).
|
Daniel@0
|
395
|
Daniel@0
|
396 weighted_histof(W,Dom,Goal,Hist) :-
|
Daniel@0
|
397 setof(Dom-N,aggregate(sum(W),Goal,N),Hist).
|
Daniel@0
|
398
|
Daniel@0
|
399 weighted_histof(W,Dom,Disc,Goal,Hist) :-
|
Daniel@0
|
400 setof(Dom-N,aggregate(sum(W),Disc,Goal,N),Hist).
|
Daniel@0
|
401
|
Daniel@0
|
402 sparse_to_dense(Min,Max,Hist,Counts) :-
|
Daniel@0
|
403 s_to_d(Min,Max,Hist,Counts).
|
Daniel@0
|
404
|
Daniel@0
|
405 s_to_d(I,Max,[],[]) :- I>Max, !.
|
Daniel@0
|
406 s_to_d(I,Max,[],[0|Counts]) :- !, succ(I,J), s_to_d(J,Max,[],Counts).
|
Daniel@0
|
407 s_to_d(I,Max,[I-C|Hist],[C|Counts]) :- !, succ(I,J), s_to_d(J,Max,Hist,Counts).
|
Daniel@0
|
408 s_to_d(I,Max,Hist,[0|Counts]) :- succ(I,J), s_to_d(J,Max,Hist,Counts).
|
Daniel@0
|
409
|
Daniel@0
|
410
|
Daniel@0
|
411 add(X,Y,Z) :- Z is X+Y.
|
Daniel@0
|
412
|
Daniel@0
|
413 :- meta_predicate
|
Daniel@0
|
414 map_reduce(1,2,3,-),
|
Daniel@0
|
415 map_reduce(1,2,3,-,-),
|
Daniel@0
|
416 fold_commutative(3,+,-).
|
Daniel@0
|
417
|
Daniel@0
|
418 %% map_reduce(+Generator:pred(-R), +Mapper:pred(+R,-A), +Reducer:pred(+A,+A,-A), -Result:A, -Errors:list(error_report(R))) is det.
|
Daniel@0
|
419 %% map_reduce(+Generator:pred(-R), +Mapper:pred(+R,-A), +Reducer:pred(+A,+A,-A), -Result:A) is semidet.
|
Daniel@0
|
420 %
|
Daniel@0
|
421 % Simple implementation of map-reduce: Mapper is applied to each item produced by Generator
|
Daniel@0
|
422 % and the results all combined using Reducer. Mapper should be a deterministic predicate.
|
Daniel@0
|
423 % Failures and exceptions encountered in the mapping phase are reported in Errors.
|
Daniel@0
|
424 % However, if the items are successfully mapped, this predicate fails.
|
Daniel@0
|
425 % Any choice points left by mapper after its first solution are cut.
|
Daniel@0
|
426 %
|
Daniel@0
|
427 % ==
|
Daniel@0
|
428 % error_report(R) ---> failed(R); error(R,exception).
|
Daniel@0
|
429 % ==
|
Daniel@0
|
430 map_reduce(Finder,Mapper,Reducer,Result) :-
|
Daniel@0
|
431 map_reduce(Finder,Mapper,Reducer,Result,_).
|
Daniel@0
|
432
|
Daniel@0
|
433 map_reduce(Finder,Mapper,Reducer,Result,Errors-Failures) :-
|
Daniel@0
|
434 setof(X,call(Finder,X),Xs),
|
Daniel@0
|
435 maplist(safe_call(Mapper),Xs,Ys),
|
Daniel@0
|
436 partition_ok(Ys,Ok,Errors,Failures),
|
Daniel@0
|
437 insist(fold_commutative(Reducer,Ok,Result)).
|
Daniel@0
|
438
|
Daniel@0
|
439 %% safe_call(+P:pred(+A,-B), +X:A, -Y:result(A,B)) is det.
|
Daniel@0
|
440 %
|
Daniel@0
|
441 % Call binary predicate P with arguments of type A and B. The result
|
Daniel@0
|
442 % term Y is of type
|
Daniel@0
|
443 % ==
|
Daniel@0
|
444 % result(A,B) ---> ok(B); failed(A); error(A,exception).
|
Daniel@0
|
445 % ==
|
Daniel@0
|
446 % and encodes the result of the call, including the input value that
|
Daniel@0
|
447 % caused any failure or exception.
|
Daniel@0
|
448 safe_call(Mapper,X,Z) :-
|
Daniel@0
|
449 ( catch((call(Mapper,X,Y), Z=ok(Y)), Ex,
|
Daniel@0
|
450 (Ex=abort_map -> throw(map_aborted); Z=error(X,Ex))), !
|
Daniel@0
|
451 ; Z=failed(X)
|
Daniel@0
|
452 ).
|
Daniel@0
|
453
|
Daniel@0
|
454 partition_ok([],[],[],[]).
|
Daniel@0
|
455 partition_ok([In|Ins],Goods,Bads,Uglies) :-
|
Daniel@0
|
456 ( In=ok(X)
|
Daniel@0
|
457 -> Goods=[X|Goods1], partition_ok(Ins,Goods1,Bads,Uglies)
|
Daniel@0
|
458 ; In=error(_,_)
|
Daniel@0
|
459 -> Bads=[In|Bads1], partition_ok(Ins,Goods,Bads1,Uglies)
|
Daniel@0
|
460 ; In=failed(X)
|
Daniel@0
|
461 -> Uglies=[X|Uglies1], partition_ok(Ins,Goods,Bads,Uglies1)
|
Daniel@0
|
462 ).
|
Daniel@0
|
463
|
Daniel@0
|
464 fold_commutative(Op,Items,Result) :-
|
Daniel@0
|
465 Items=[I1|Rest],
|
Daniel@0
|
466 seqmap(Op,Rest,I1,Result), !.
|
Daniel@0
|
467
|
Daniel@0
|
468 freq_note_number(F,N) :- N is 69+round(12*log(F/440)/log(2)).
|
Daniel@0
|
469
|
Daniel@0
|
470 pitch_name_number(Name,Number) :-
|
Daniel@0
|
471 atom_codes(Name,Chars),
|
Daniel@0
|
472 phrase(note(Number),Chars).
|
Daniel@0
|
473
|
Daniel@0
|
474 pitch_number_name(Number,Name) :-
|
Daniel@0
|
475 phrase(note(Number),Chars),
|
Daniel@0
|
476 atom_codes(Name,Chars).
|
Daniel@0
|
477
|
Daniel@0
|
478 :- use_module(library(clpfd)).
|
Daniel@0
|
479 note(Num) -->
|
Daniel@0
|
480 [Nom], ({Mod=0}; [0'#],{Mod=1}),
|
Daniel@0
|
481 { PC in 0..11,
|
Daniel@0
|
482 Num #= 12*(Oct+1)+PC+Mod,
|
Daniel@0
|
483 nom_semis(Nom,PC)
|
Daniel@0
|
484 },
|
Daniel@0
|
485 integer(Oct).
|
Daniel@0
|
486
|
Daniel@0
|
487 nom_semis(0'C,0).
|
Daniel@0
|
488 nom_semis(0'D,2).
|
Daniel@0
|
489 nom_semis(0'E,4).
|
Daniel@0
|
490 nom_semis(0'F,5).
|
Daniel@0
|
491 nom_semis(0'G,7).
|
Daniel@0
|
492 nom_semis(0'A,9).
|
Daniel@0
|
493 nom_semis(0'B,11).
|
Daniel@0
|
494
|
Daniel@0
|
495 unzip(Pairs,Xs,Ys) :- maplist(pair,Xs,Ys,Pairs).
|
Daniel@0
|
496 pair(X,Y,X-Y).
|
Daniel@0
|
497
|
Daniel@0
|
498 row_pair(I,J,Row,X-Y) :- arg(I,Row,X), arg(J,Row,Y).
|
Daniel@0
|
499 row_list(N,Row,List) :- functor(Row,_,N), Row=..[_|List].
|
Daniel@0
|
500 rows_cols(Is,[],Cols) :- !, maplist(nil,Is,Cols).
|
Daniel@0
|
501 rows_cols(Is,[R|Rs],Cols) :-
|
Daniel@0
|
502 ( maplist(arg_cons(R),Is,Tails,Cols)
|
Daniel@0
|
503 -> rows_cols(Is,Rs,Tails)
|
Daniel@0
|
504 ; fail % rows_cols(Is,Rs,Cols)
|
Daniel@0
|
505 ).
|
Daniel@0
|
506
|
Daniel@0
|
507 arg_cons(Row,I,T,[X|T]) :- arg(I,Row,X).
|
Daniel@0
|
508 nil(_,[]).
|
Daniel@0
|
509
|
Daniel@0
|
510 fst(F,K1-V,K2-V) :- call(F,K1,K2).
|
Daniel@0
|
511 snd(F,K-V1,K-V2) :- call(F,V1,V2).
|
Daniel@0
|
512 div_by(K,X,Y) :- Y is X/K.
|
Daniel@0
|
513
|
Daniel@0
|
514 mul(X,Y,Z) :- Z is round(X*Y).
|
Daniel@0
|
515
|
Daniel@0
|
516 :- dynamic pitch_hist_table/5, pitch_hist_tabled/1.
|
Daniel@0
|
517
|
Daniel@0
|
518 csv_pitch_count_prob(W,CSV,Pitch,Count,Prob) :-
|
Daniel@0
|
519 must_be(ground,W),
|
Daniel@0
|
520 ( pitch_hist_tabled(W) -> true
|
Daniel@0
|
521 ; table_pitch_hist(W)
|
Daniel@0
|
522 ),
|
Daniel@0
|
523 pitch_hist_table(W,CSV,Pitch,Count,Prob).
|
Daniel@0
|
524
|
Daniel@0
|
525 table_pitch_hist(W) :-
|
Daniel@0
|
526 retractall(pitch_hist_table_cached(W)),
|
Daniel@0
|
527 forall( browse(csv_op_memo(pitch_hist(W),CSV,Hist)),
|
Daniel@0
|
528 ( retractall(pitch_hist_table(W,CSV,_,_,_)),
|
Daniel@0
|
529 forall( pitch_hist_prob(Hist,Pitch,Count,Prob),
|
Daniel@0
|
530 assert(pitch_hist_table(W,CSV,Pitch,Count,Prob))))),
|
Daniel@0
|
531 assert(pitch_hist_tabled(W)).
|
Daniel@0
|
532
|
Daniel@0
|
533 pitch_hist_prob(Hist,Pitch,Count,Prob) :-
|
Daniel@0
|
534 unzip(Hist,_,Counts),
|
Daniel@0
|
535 sumlist(Counts,Total),
|
Daniel@0
|
536 member(Pitch-Count,Hist),
|
Daniel@0
|
537 Prob is Count/Total.
|