Synthdef Wrap Woes

Preformatted textHi list,

More than ten years ago I made an FFT kind of matrix thing, using Instr and Patch. In the meantime, life happened and since up until a couple of months ago I barely touched SC3. I’m slowly getting used to Supercollider these days, and I’m loving it all over again.

I managed to refractor the barebones of my code using Synthdef.wrap instead of using felix’s crucial library. I remember even back in the day having problems with order of execution and integrating it with vanilla sc3, witch is a pity because it worked like a charm otherwise.

The idea of the patch is to have just one FFT and IFFT process, but with interchangeable PV ugens in between, to facilitate experimentation: FFT --> PV_ugen --> PV Ugen --> IFFT, where the PV_ugens are selectable from a list.

So far the refractor is kinda working, but with these caveats:

  1. The first time I press Play on a clean server, it complains with a Synthdef not found message.

  2. I press Stop. I press Play again, and this time it sounds.

  3. I click Stop. Change some ugen in the chain.

  4. I click Play. The process sounds, but with the old pv ugen in the chain.

  5. I click Stop. I don’t change anything. I click play, and this time the correct ugen in the chain sounds.

  6. etc.

So far I have been {}.defer ing blocks of stuff more or less at random with no results. I understand there is a mismatch with what the server has vs. what I’m sending from the language, but I can’t see it at this point… Please help!

This is the patch. Please note that it takes its stereo input from channels 4 and 5:

(

var pvProcess, makeEffect;
var win;
var synth, btnPlay, sldMagstretch, sldMagshift;
var sldBinstretch, sldBinshift;
var sldWipe, sldWidth, btnTrigger;
var btnChain1, btnChain2;

~fxgroup = Group.tail(s);

pvProcess = [
	{{ arg chain, whatever, magstretch, magshift;
		chain = PV_MagShift( chain, magstretch, magshift )
	}},
	{{ arg chain, whatever, binstretch, binshift;
		chain = PV_BinShift( chain, binstretch, binshift )
	}},
	{{arg chain, whatever, wipe, width=0.1, trigger;
		chain = PV_BinScramble( chain, wipe, width, trigger )
	}}
];

makeEffect = { |name, func, func2, lags, numChannels = 2|

	SynthDef(name, { | i_bus = 0, gate = 1, inbus=4, hopsize=0.25, wintype=0, out=0 |
		var in, chain, output;
		in = In.ar( inbus, 2);

		chain = FFT({LocalBuf(2048, 1)}.dup, in, hopsize, wintype);

		chain = SynthDef.wrap(func, lags, [chain, chain]);
		chain = SynthDef.wrap(func2, lags, [chain, chain]);

		output = Out.ar(out, 0.1 * IFFT(chain));

	} ).add;
};

// GUI

win = Window("FTT Matrix", Rect(580, 450, 460, 150)).front.alwaysOnTop_(true);
btnPlay = Button().states_([["Play"], ["Stop"]]);
btnChain1 = PopUpMenu().items_([ "magshift", "binshift", "Scramble" ]).valueAction_(0);
btnChain2 = PopUpMenu().items_([ "magshift", "binshift", "Scramble" ]).valueAction_(1);

sldMagstretch = LayoutValueSlider(nil, nil, 1, [0.1, 4]);
sldMagshift = LayoutValueSlider(nil, nil, 0, [0, 100]);

sldBinstretch = LayoutValueSlider(nil, nil, 1, [0.1, 4]);
sldBinshift = LayoutValueSlider(nil, nil, 0, [0, 100]);

sldWipe = LayoutValueSlider(nil, nil, 1, [0, 1]);
sldWidth = LayoutValueSlider(nil, nil, 0.1, [0, 1]);
btnTrigger = Button().states_([["Trigger"]]);

win.layout = VLayout(
	btnPlay, 10,
	btnChain1,
	btnChain2, 20,
	StaticText().string_("MagShift"),
	HLayout(
		sldMagstretch.asView, StaticText().string_("Stretch"), 20,
		sldMagshift.asView, StaticText().string_("Shift"),
	), 10,

	StaticText().string_("BinShift"),
	HLayout(
		sldBinstretch.asView, StaticText().string_("Stretch"), 20,
		sldBinshift.asView, StaticText().string_("Shift"),
	), 10,

	StaticText().string_("Scramble"),
	HLayout(
		sldWipe.asView,
		sldWidth.asView,
		btnTrigger,
	),
);

btnPlay.action_{
	if (btnPlay.value == 1) {
		{makeEffect.value(\fftMatrix,
			pvProcess[btnChain1.value].value,
			pvProcess[btnChain2.value].value,
			nil,
			2
		)}.defer;

		synth = Synth(\fftMatrix, [

			magstretch: sldMagstretch.value,
			magshift: sldMagshift.value,
			binstretch: sldBinstretch.value,
			binshift: sldBinshift.value,
			wipe: sldWipe.value,
			width: sldWidth.value,
			trigger: btnTrigger.value,

	], ~fxgroup, \addToTail) }
	{ synth.free }
};

sldMagstretch.action_{
	if (btnPlay.value==1, {synth.set(\magstretch, sldMagstretch.value)})
};

sldMagshift.action_{
	if (btnPlay.value==1, {synth.set(\magshift, sldMagshift.value)})
};

sldBinstretch.action_{
	if (btnPlay.value==1, {synth.set(\binstretch, sldBinstretch.value)})
};

sldBinshift.action_{
	if (btnPlay.value==1, {synth.set(\binshift, sldBinshift.value)})
};

sldWipe.action_{
	if (btnPlay.value==1, {synth.set(\wipe, sldWipe.value)})
};

sldWidth.action_{
	if (btnPlay.value==1, {synth.set(\width, sldWidth.value)})
};

btnTrigger.action_{
	{synth.set(\trigger, btnTrigger.value)}
};

)

Kindest regards, Mario.

I think this has nothing to do with SynthDef:wrap.

It looks like you’re deferring the wrong thing. You should not defer SynthDef creation.

You need to allow a little time for the server to process the SynthDef before creating the synth.

The recommended pattern is:

fork {
	... create synthdef ...
	s.sync;
	... create Synth ...
}

All of the symptoms you describe are explained if you consider that the SynthDef is not ready when you do Synth(...), so Synth must then use the last available SynthDef.

hjh

Thanks for your answer James, I solved the problems with your suggestions.

Mario