Schedule the return of the instance's method?

I tried to encapsulate a Flucoma’s FluidBufOnsetSlice class into a compact custom one and I had sever issues.

I wanted this new class to simply return a dictionary with slice points in different formatting (in frames, fractions and seconds). The main problem with these functions is that it require some time to allocate several buffers, fill them, finish processing with FluidBufOnsetSlice and then extract slice values from a buffer to an array. After small research I found that it is impossible to delay the return (^) as it discussed in these topics: 1 & 2. What can be the solution for this?

Get {
	*slices { | bufpath = "", metric = 0, threshold = 0.5 |
		var process, buffer, indices, frames, secs, fracs;
		buffer = Buffer.read(Server.local, bufpath);
		indices = Buffer(Server.local);
		FluidBufOnsetSlice.processBlocking(
			server: Server.local,
			source: buffer,
			metric: metric,
			indices: indices,
			threshold: threshold,
			action: {
					"found % slice points".format(indices.numFrames).postln;
					"with an average length of % seconds per slice".format(buffer.duration /  indices.numFrames).postln;
				fork {
					frames = Array.zeroFill(indices.numFrames);
					indices.numFrames.do({ | i | indices.get(i, { | msg | frames.put(i, msg)});

					});
					2.wait; // also how to calculate the right time to wait for do/get combo to finish?
					secs = frames / buffer.sampleRate;
					fracs = frames / buffer.numFrames;
					Post.nl; "done".postln;
					^(frames: frames, secs: secs, fracs: fracs);
				};
			});
	}
}
1 Like

Hi @wehrk,

You could pass an action function into the method call for slices so that when all the analysis, forking, etc, is complete you can specify a function to be executed. You can even pass that function the results of the method call as one or more arguments. Something like this (untested code):

Get {
	*slices { | bufpath = "", metric = 0, threshold = 0.5, action |
		var process, buffer, indices, frames, secs, fracs;
		buffer = Buffer.read(Server.local, bufpath);
		indices = Buffer(Server.local);
		FluidBufOnsetSlice.processBlocking(
			server: Server.local,
			source: buffer,
			metric: metric,
			indices: indices,
			threshold: threshold,
			action: {
					"found % slice points".format(indices.numFrames).postln;
					"with an average length of % seconds per slice".format(buffer.duration /  indices.numFrames).postln;
				fork {
					frames = Array.zeroFill(indices.numFrames);
					indices.numFrames.do({ | i | indices.get(i, { | msg | frames.put(i, msg)});

					});
					2.wait; // also how to calculate the right time to wait for do/get combo to finish?
					secs = frames / buffer.sampleRate;
					fracs = frames / buffer.numFrames;
					Post.nl; "done".postln;
					action.value((frames: frames, secs: secs, fracs: fracs));
				};
			});
	}
}
1 Like

Thank you, @tedmoore. Your solution is working. I just noted that action approach you provided leaves the class more flexible than if it just returned an Event with a data. More complex function can be written on-the-fly without ripping up the class if needed. In spite of all this I will study more on writing classes and come here to share other ideas on this topic.

1 Like