Skip to content

Commit 99333a7

Browse files
tremblaptedmooreJames Bradburyweefuzzy
authored
[release] 1.0.5 (#181)
* removed redundant link to learn website in mfcc and bufmfcc * typo * ParamDump: add overload for allocator-aware string (#173) * Feature/peaks (#176) * working placeholders * placeholders for *sineFeature * revised help file and working code example * added MIDI and linMag * add padding help * change of interface (sortBy to order) * typo in example code * typo in header * bruteforce DataSet kNearest doc and example code (#177) * DataSet kNearest doc and example code * improvements post-review * more interactive KDTree radius+numNeighbour example, as per other CCEs * feature/kdtree-distance-with-optional-k * (buf)sines: consistent naming of interface Co-authored-by: Ted Moore <ted@tedmooremusic.com> Co-authored-by: James Bradbury <github@jamesbradbury.net> Co-authored-by: Owen Green <gungwho@gmail.com>
1 parent 216c7cd commit 99333a7

File tree

6 files changed

+70
-16
lines changed

6 files changed

+70
-16
lines changed

doc/BufSines.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666

6767
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefactss, but is more CPU intensive and might reject quick pitch material.
6868

69-
:control trackingMethod:
69+
:control trackMethod:
7070

7171
The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive [^"Hungarian"]( Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018. ) one.
7272

doc/DataSet.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,14 @@
8989

9090
Merge sourceDataSet in the current DataSet. It will update the value of points with the same identifier if overwrite is set to 1. ​To add columns instead, see the 'transformJoin' method of FluidDataSetQuery.
9191

92+
:message kNearest:
93+
94+
:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the DataSet.
95+
96+
:arg k: The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.
97+
98+
Returns the identifiers of the ``k`` points nearest to the one passed. Note that this is a brute force distance measure, and comparatively inefficient for repeated queries against large datasets. For such cases, :fluid-obj:`KDTree` will be more efficient.
99+
92100
:message print:
93101

94102
Post an abbreviated content of the DataSet in the window by default, but you can supply a custom action instead.

doc/KDTree.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
:discussion:
88
:fluid-obj:`KDTree` facilitates efficient nearest neighbour searches of multi-dimensional data stored in a :fluid-obj:`DataSet`.
99

10+
k-d trees are most useful for *repeated* querying of a dataset, because there is a cost associated with building them. If you just need to do a single lookup then using the kNearest message of :fluid-obj:`DataSet` will probably be quicker
11+
1012
Whilst k-d trees can offer very good performance relative to naïve search algorithms, they suffer from something called “the curse of dimensionality” (like many algorithms for multi-dimensional data). In practice, this means that as the number of dimensions of your data goes up, the relative performance gains of a k-d tree go down.
1113

1214
:control numNeighbours:
@@ -34,13 +36,17 @@
3436

3537
:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the :fluid-obj:`DataSet` the tree was fitted to.
3638

37-
:arg k: The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.
39+
:arg k: (optional) The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.
40+
41+
:arg action: A function that will run when the query returns, whose argument is an array of distances.
3842

3943
Returns the identifiers of the ``k`` points nearest to the one passed.
4044

4145
:message kNearestDist:
4246

4347
:arg buffer: A |buffer| containing a data point to match against. The number of frames in the buffer must match the dimensionality of the :fluid-obj:`DataSet` the tree was fitted to.
48+
49+
:arg k: (optional) The number of nearest neighbours to return. The identifiers will be sorted, beginning with the nearest.
4450

4551
:arg action: A function that will run when the query returns, whose argument is an array of distances.
4652

doc/Sines.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141

4242
The minimum duration, in spectral frames, for a sinusoidal track to be accepted as a partial. It allows to remove bubbly pitchy artefacts, but is more CPU intensive and might reject quick pitch material.
4343

44-
:control trackingMethod:
44+
:control trackMethod:
4545

4646
The algorithm used to track the sinusoidal continuity between spectral frames. 0 is the default, "Greedy", and 1 is a more expensive [^"Hungarian"]( Neri, J., and Depalle, P., "Fast Partial Tracking of Audio with Real-Time Capability through Linear Programming". Proceedings of DAFx-2018. ) one.
4747

example-code/sc/DataSet.scd

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,4 +250,29 @@ fork{
250250
}
251251
)
252252

253+
::
254+
strong::Nearest Neighbour Search in a DataSet::
255+
256+
Note: A FluidDataSet can be queried with an input point to return the nearest match to that point. Note: This feature is can be computationally expensive on a large dataset, as it needs to compute the distance of the queried point to each point in the dataset. If you need to perform multiple nearest neighbour queries on a fluid.dataset~ it is recommended to use FluidKDTree. This facility is most useful with smaller, ephemeral datasets such as those returned by FluidDataSetQuery.
257+
258+
code::
259+
260+
// create a small DataSet...
261+
f = FluidDataSet(s)
262+
// and fill it with a grid of data
263+
f.load(Dictionary.newFrom(["cols", 2, "data", Dictionary.newFrom(9.collect{|i|["item-%".format(i), [i.div(3), i.mod(3)] / 2]}.flatten(1))]))
264+
265+
// the data looks like this
266+
// (item-0 -> [ 0.0, 0.0 ]) (item-1 -> [ 0.0, 0.5 ]) (item-2 -> [ 0.0, 1.0 ])
267+
// (item-3 -> [ 0.5, 0.0 ]) (item-4 -> [ 0.5, 0.5 ]) (item-5 -> [ 0.5, 1.0 ])
268+
// (item-6 -> [ 1.0, 0.0 ]) (item-7 -> [ 1.0, 0.5 ]) (item-8 -> [ 1.0, 1.0 ])
269+
270+
// create a query buffer...
271+
b = Buffer.alloc(s,2)
272+
273+
// and fill it with a point
274+
b.sendCollection([1,0]);
275+
276+
// and request 9 nearest neighbours
277+
f.kNearest(b,9,{|x|x.postln;})
253278
::

example-code/sc/KDTree.scd

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ ds.dump({
2121
defer{
2222
view.highlight_(id);
2323
};
24-
24+
2525
{
2626
var start = Index.kr(slicePoints,index);
2727
var end = Index.kr(slicePoints,index+1);
@@ -40,15 +40,8 @@ ds.dump({
4040
strong::radius and num neighbours::
4141
code::
4242

43-
// set some initial values
44-
(
45-
~numNeighbours = 3;
46-
~tree.radius_(0.04);
47-
)
48-
49-
50-
// then make the plot, once it's up and you're clicking around,
51-
// change the numbers and re-run the code above to see the differences
43+
// Make a plot; once it's up and you're clicking around, change the numbers of
44+
// queried neighbours and the permitted radius to see the different behaviours
5245
(
5346
var ds = FluidDataSet(s).load(
5447
Dictionary.newFrom([
@@ -61,18 +54,40 @@ var ds = FluidDataSet(s).load(
6154
)
6255
])
6356
);
64-
~tree = FluidKDTree(s).fit(ds);
57+
58+
~tree = FluidKDTree(s);
59+
~tree.numNeighbours = 3;
60+
~tree.radius_(0.04);
61+
~tree.fit(ds);
62+
6563
ds.dump({
6664
arg dict;
65+
var nn, nnd;
6766
var xybuf = Buffer.alloc(s,2);
6867
defer{
69-
FluidPlotter(dict:dict,mouseMoveAction:{
68+
w = Window(\KDTree,Rect(0,0,705,500)).front;
69+
StaticText(w,Rect(500,5,200,20)).string_("numNeighbours:");
70+
TextField(w,Rect(500,25,200,20)).string_(~tree.numNeighbours.asString).action_{|x|~tree.numNeighbours = x.value.asInteger};
71+
StaticText(w,Rect(500,45,200,20)).string_("radius:");
72+
TextField(w,Rect(500,65,200,20)).string_(~tree.radius.asString).action_{|x|~tree.radius = x.value.asFloat};
73+
StaticText(w,Rect(500,85,200,20)).string_("neighbours:");
74+
nn = TextView(w, Rect(500,105,200,40)).string_("").editable_(false);
75+
StaticText(w,Rect(500,145,200,20)).string_("distances:");
76+
nnd = TextView(w, Rect(500,165,200,200)).string_("").editable_(false);
77+
FluidPlotter(w, Rect(5,5,490,490), dict:dict,mouseMoveAction:{
7078
arg view, x, y;
7179
xybuf.setn(0,[x,y]);
72-
~tree.kNearest(xybuf,~numNeighbours,{
80+
~tree.kNearest(xybuf,action:{
7381
arg id;
7482
defer{
7583
view.highlight_(id);
84+
nn.string = id.asString;
85+
};
86+
});
87+
~tree.kNearestDist(xybuf,action:{
88+
arg id;
89+
defer{
90+
nnd.string = id.asString;
7691
};
7792
});
7893
});

0 commit comments

Comments
 (0)