Exploring sound corpora in SC

ah, inserting the 1 there makes it sound now! i believe previously it worked without, but im happy its eventually such a simple solution, which still i wouldn’t have thought of myself!:slight_smile: thank you!

Cool, glad it works now!

That argument got added around May / June, I think, to allow calls to KNearest to override the parameter set at the object level (we couldn’t do this originally for boring C++ reasons).

hi @tedmoore ,
coming back to this code im again running into the aforementioned error. did you eventually manage to find a workaround for it?
thanks,
jan

Hi @jan,

Sorry this has completely escaped my mind and this thread is now quite long. Can you restate the problem so I can take a look at it?

Thanks,

T

Hi @tedmoore ,
yes, it became long indeed!
Its about the error return in the following block of code:
(i just modified that some data is being read from file, but that shouldn’t alter anything)

// 7. Put the "play info" from all the slices of each in cluster into a buffer
(
fork{
	var tmpbuf = Buffer(s);
	~ds_play_info=FluidDataSet(s).read("%playinfo.json".format(~data));
	~play_info_clusters = {Buffer(s)} ! ~clusters.size.postln;
//	~play_info_clusters = {Buffer(s)} ! ~clusters.keys.maxItem; //alternative suggestion also did not work
	~clusters.keysValuesDo{
		arg cluster_i, list;
		list.do{
			arg id, i;
			"cluster %\tid %".format(cluster_i,id).postln;
			~ds_play_info.getPoint(id,tmpbuf);
			s.sync;
			// write the first frame to channel 0, frame "i"
			FluidBufCompose.processBlocking(s,tmpbuf,startFrame:0,numFrames:1,destination:~play_info_clusters[cluster_i.asInteger],destStartFrame:i,destStartChan:0);

			// write the second frame to channel 1, frame "i"
			FluidBufCompose.processBlocking(s,tmpbuf,startFrame:1,numFrames:1,destination:~play_info_clusters[cluster_i.asInteger],destStartFrame:i,destStartChan:1);
		}
	};
	s.sync;
	// {~play_info_clust_5.plot}.defer;
};
)

returns this:

-> a Routine
18
cluster 63	id slice-72
ERROR: FluidBufCompose:  Invalid destination buffer
CALL STACK:
	Object:reportError
		arg this = "FluidBufCompose:  Invalid de..."
	Nil:handleError
		arg this = nil
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Thread>
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Routine>
		arg error = "FluidBufCompose:  Invalid de..."
	Object:throw
		arg this = "FluidBufCompose:  Invalid de..."
	Meta_FluidBufCompose:processBlocking
		arg this = <instance of Meta_FluidBufCompose>
		arg server = <instance of Server>
		arg source = 433
		arg startFrame = 0
		arg numFrames = 1
		arg startChan = 0
		arg numChans = -1
		arg gain = 1
		arg destination = nil
		arg destStartFrame = 0
		arg destStartChan = 0
		arg destGain = 0
		arg freeWhenDone = true
		arg action = nil
	< FunctionDef in closed FunctionDef >
		arg id = "slice-72"
		arg i = 0
	ArrayedCollection:do
		arg this = [*151]
		arg function = <instance of Function>
		var i = 0
	List:do
		arg this = <instance of List>
		arg function = <instance of Function>
	Dictionary:keysValuesArrayDo
		arg this = <instance of Dictionary>
		arg argArray = [*128]
		arg function = <instance of Function>
		var i = 0
		var j = 1
		var key = nil
		var val = nil
		var arraySize = nil
	Dictionary:keysValuesDo
		arg this = <instance of Dictionary>
		arg function = <instance of Function>
	< closed FunctionDef >
		var tmpbuf = <instance of Buffer>
	Routine:prStart
		arg this = <instance of Routine>
		arg inval = 30052.234579042
-> a Routine
18
cluster 63	id slice-72
ERROR: FluidBufCompose:  Invalid destination buffer
CALL STACK:
	Object:reportError
		arg this = "FluidBufCompose:  Invalid de..."
	Nil:handleError
		arg this = nil
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Thread>
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Routine>
		arg error = "FluidBufCompose:  Invalid de..."
	Object:throw
		arg this = "FluidBufCompose:  Invalid de..."
	Meta_FluidBufCompose:processBlocking
		arg this = <instance of Meta_FluidBufCompose>
		arg server = <instance of Server>
		arg source = 452
		arg startFrame = 0
		arg numFrames = 1
		arg startChan = 0
		arg numChans = -1
		arg gain = 1
		arg destination = nil
		arg destStartFrame = 0
		arg destStartChan = 0
		arg destGain = 0
		arg freeWhenDone = true
		arg action = nil
	< FunctionDef in closed FunctionDef >
		arg id = "slice-72"
		arg i = 0
	ArrayedCollection:do
		arg this = [*151]
		arg function = <instance of Function>
		var i = 0
	List:do
		arg this = <instance of List>
		arg function = <instance of Function>
	Dictionary:keysValuesArrayDo
		arg this = <instance of Dictionary>
		arg argArray = [*128]
		arg function = <instance of Function>
		var i = 0
		var j = 1
		var key = nil
		var val = nil
		var arraySize = nil
	Dictionary:keysValuesDo
		arg this = <instance of Dictionary>
		arg function = <instance of Function>
	< closed FunctionDef >
		var tmpbuf = <instance of Buffer>
	Routine:prStart
		arg this = <instance of Routine>
		arg inval = 30187.001940333
-> a Routine
cluster 63	id slice-72
ERROR: FluidBufCompose:  Invalid destination buffer
CALL STACK:
	Object:reportError
		arg this = "FluidBufCompose:  Invalid de..."
	Nil:handleError
		arg this = nil
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Thread>
		arg error = "FluidBufCompose:  Invalid de..."
	Thread:handleError
		arg this = <instance of Routine>
		arg error = "FluidBufCompose:  Invalid de..."
	Object:throw
		arg this = "FluidBufCompose:  Invalid de..."
	Meta_FluidBufCompose:processBlocking
		arg this = <instance of Meta_FluidBufCompose>
		arg server = <instance of Server>
		arg source = 471
		arg startFrame = 0
		arg numFrames = 1
		arg startChan = 0
		arg numChans = -1
		arg gain = 1
		arg destination = nil
		arg destStartFrame = 0
		arg destStartChan = 0
		arg destGain = 0
		arg freeWhenDone = true
		arg action = nil
	< FunctionDef in closed FunctionDef >
		arg id = "slice-72"
		arg i = 0
	ArrayedCollection:do
		arg this = [*151]
		arg function = <instance of Function>
		var i = 0
	List:do
		arg this = <instance of List>
		arg function = <instance of Function>
	Dictionary:keysValuesArrayDo
		arg this = <instance of Dictionary>
		arg argArray = [*128]
		arg function = <instance of Function>
		var i = 0
		var j = 1
		var key = nil
		var val = nil
		var arraySize = nil
	Dictionary:keysValuesDo
		arg this = <instance of Dictionary>
		arg function = <instance of Function>
	< closed FunctionDef >
		var tmpbuf = <instance of Buffer>
	Routine:prStart
		arg this = <instance of Routine>
		arg inval = 30243.730297792

thanks,
jan

originally i mentioned it here:

Hi Jan,

Thanks for the reminder! It turns out that the error is because there are some empty clusters. The code was allocating enough buffers for only the clusters that weren’t empty! I’ve replaced ~clusters.size (which represented only non-empty clusters) with ~kmeans.numClusters which will be how many KMeans is trying to find. Now there will be a buffer for every cluster index.

I had to change something in step 5 too (assign the KMeans to a variable), so I include the updated code for steps 5-7 below. Let me know if that solves it all for you!

Cheers,

T

// 5. Cluster the MFCCs output
(
~labels = FluidLabelSet(s);
~kmeans = FluidKMeans(s,100).fitPredict(~ds,~labels,{"kmeans complete".postln});
)

// 6. Take out labels information and organize in a dictionary
(
~labels.dump({
	arg dict;
	~clusters = Dictionary.new;
	dict["data"].keysValuesDo{
		arg id, cluster;
		cluster = cluster[0].asInteger;
		if(~clusters[cluster].isNil,{~clusters[cluster] = List.new});
		~clusters[cluster].add(id);
	};
	~clusters.keysValuesDo{
		arg k, v;
		"points in cluster %: %".format(k,v).postln;
	};
});
)

// 7. Put the "play info" from all the slices of each in cluster into a buffer
(
fork{
	var tmpbuf = Buffer(s);
	~play_info_clusters = {Buffer(s)} ! ~kmeans.numClusters;
	~clusters.keysValuesDo{
		arg cluster_i, list;
		list.do{
			arg id, i;
			"cluster % / %\tid %".format(cluster_i,~kmeans.numClusters,id).postln;
			"\tnumber of buffers in ~play_info_clusters: %".format(~play_info_clusters.size).postln;
			~ds_play_info.getPoint(id,tmpbuf);
			s.sync;
			// write the first frame to channel 0, frame "i"
			FluidBufCompose.processBlocking(s,tmpbuf,startFrame:0,numFrames:1,destination:~play_info_clusters[cluster_i.asInteger],destStartFrame:i,destStartChan:0);

			// write the second frame to channel 1, frame "i"
			FluidBufCompose.processBlocking(s,tmpbuf,startFrame:1,numFrames:1,destination:~play_info_clusters[cluster_i.asInteger],destStartFrame:i,destStartChan:1);
		}
	};
	s.sync;
	// {~play_info_clust_5.plot}.defer;
};
)

Hi Ted,
inserting ~kmeans.numClusters makes the block of code work without problems at all.
as i’ve modified the other steps a bit ill let report back if theres anything down (rather, up) the road if inserting your changes… so far this seems to solve it, thank you!
Jan

1 Like

Hi @jan, sounds good!

So there is a little follow up question. Usually i’d write the play info buffers as files for later usage:

~play_info_clusters.size.do{|i| ~play_info_clusters.at(i).write("%%.wav".format(~data,i),headerFormat:"WAV",sampleFormat:"float32")}

this of course returns a lot of errors given the empty buffers being written, e.g.:

File '/Users/jan/Sound/Recordings/pquin/data/5.wav' could not be opened: Format not recognised.

could one write only the non-empty buffers?

Hi @jan,

If the buffer is empty it’s .numFrames attribute should be nil so in your loop, check for that and only write it to disk if the .numFrames is not nil. There’s even a .notNil method you can call!

Best,

T

Thanks for the hint @tedmoore, will try this!

1 Like