Slipstick synthesis

hey,
has somebody dealt with slipstick synthesis in SC before?
It is modelling a mass at the end of a spring being dragged across a surface that has friction.
For example, an oily hand on a window or sneakers on the gym floor.
I have made these latex balloon textures the other day, which i really like:



and played around with some modal synthesis approaches like the circular membrane to model some metallic percussion which worked out very well and already tried to come up with more interesting exciters then just a noise burst.

EDIT: probably somehow possible with the Fb1_MSD class Fb1_MSD | SuperCollider 3.12.2 Help or Friction | SuperCollider 3.12.2 Help will try these out :slight_smile:

Any ideas?
here are two resources:

https://medias.ircam.fr/x7aa847

4 Likes

Here’s an example from “Designing Sound” by Andy Farnell, that has been translated to Supercollider.

https://en.wikibooks.org/wiki/Designing_Sound_in_SuperCollider/Creaking_door

2 Likes

thanks a lot for the example :slight_smile: i thought of rewriting it in a more generalized way using DynKlank instead of the parallel BPFs (wood filter) + Delay Lines (square panel) to then be able to play around with the frequencies and decays. but its sounds really different. any ideas how to convert the example into a modal synthesis approach using DynKlank instead?

(
SynthDef(\slipstick, {

	var force, noise, inMotion, slipEvents, forceBuildup, evtAmp, evtDecayTime, evts;
	var sig, freqs, rqs, times;
	var f0, ratios, decays, amps;
	var exc;

	//f0 = \freq.kr(125);
	//ratios = [62.5, 125, 250, 395, 560, 790]; //f0 * [0.5, 1, 1.58, 2.24, 2.92, 2, 2.55, 3.16];
	//amps = [1, 1, 2, 2, 3, 3];
	//decays = 1 / 6;
	//decays = [4.52, 5.06, 6.27, 8, 5.48, 7.14, 10.12, 16];

	force = MouseX.kr(0,1).lag(0.1);
	inMotion = force > 0.1;

	force = Impulse.ar(force.linlin(0.1, 1, 1, 1 / 0.003));
	noise = LFDNoise1.ar(50).squared.linexp(-1, 1, 0.5, 2);

	slipEvents = inMotion * force * noise;

	forceBuildup = Phasor.ar(slipEvents, 10 * SampleDur.ir, 0, inf).min(1);

	evtAmp = Latch.ar(Delay1.ar(forceBuildup.sqrt), slipEvents);
	evtDecayTime = evtAmp.sqrt;

	sig = EnvGen.ar(Env.perc(0.001, 1), slipEvents, evtAmp, 0, evtDecayTime * 0.01);

	//sig = DynKlank.ar(Ref([freqs, amps, decays]), exc, 1, 0, \decayScale.kr(0.001));

	//sig = sig + (exc * 0.2);

	// wood filter
	freqs = [62.5, 125, 250, 395, 560, 790];
	rqs   = 1 / [1, 1, 2, 2, 3, 3];
	sig = BPF.ar(sig, freqs, rqs).sum + (sig * 0.2);

	// square panel
	times = [4.52, 5.06, 6.27, 8, 5.48, 7.14, 10.12, 16] * 0.001;
	sig = DelayC.ar(sig, times, times).mean;
	sig = HPF.ar(sig, 125);
	sig * 4;

	sig = Pan2.ar(sig, \pos.kr(0), \amp.kr(1));
	Out.ar(\out.kr(0), sig);
}).play;
)

and is there somebody who knows about the ranges for the methods and outputs of the Friction Ugen? I was trying to figure out if the Fb1_MSD examples from the helpfile could be rewritten with the Friction Ugen.

For example this one:

(
SynthDef(\fb1_msd, {
	var f = 1.5, mass = 0.04, spring = 200, dampen = 0.05;
	var sig, freq;

	sig = Fb1_MSD.kr(f, mass, spring, dampen);
	//sig = Friction.kr(f, 0.5, 0.2, 0.3, 0.001);
	freq = sig[1].linlin(-1, 1, [100, 90], 700);
	sig = SinOsc.ar(freq);

	sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.25));
	Out.ar(\out.kr(0), sig);
}).play
)

My version below. I find that modulating force and maybe stickiness gives the apparent pitch. The apparent pitch is coming from the speed of impulses not the BPFs. Using too much pitched resonance sounds “wrong” as it doesn’t correlate with the apparent pitch of the impulses themselves. You could play with the BPF formants as well of course, but that’s more atmosphere than pitch I think.


SynthDef(\stickslip,{
	arg out = 10,
	inertia = 0.1,
	decayScale = 1,
	stickiness = 6,
	randomness = 1
	;
	var force = EnvGen.kr(Env([0,1,0,1,0],[1,1,1,2]));
	var forcex = Lag.kr(force,0.1);
	var inMotion = forcex > inertia; // static friction: nothing at all below a certain force
	var iforce = (1.1 - forcex) * stickiness;
	var sfreq = ((iforce * 0.01) + 0.003);
	var sfrict = randomness * (iforce * 0.001 * TRand.ar(0.001,1,111));
	var freq = inMotion * (1 / (sfreq + sfrict));
	var events = Impulse.ar(freq);

	var forceBuildup = Phasor.ar(events,
		10 * SampleDur.ir, 0, inf).min(1);
	var evtAmp = Latch.ar(
		Delay1.ar(forceBuildup.sqrt),
		events);
	var evtDecayTime = evtAmp.sqrt;
	var evts = EnvGen.ar(
		Env.perc(0.001, 1),
		events,// gate
		evtAmp, // level scale
		0, // level bias
		evtDecayTime * decayScale * 0.01// timeScale
	).scope;
	Out.ar(out,evts)
}).add

SynthDef(\door,{arg in = 10;
	var freqs = [62.5,125,250,395,560,790];
	var qs = 1 / [1,1,2,2,3,3];
	var input = In.ar(in);
	var forms = BPF.ar(input,freqs,qs).sum;
	var sig = forms + (input * 0.2);
	var times = [4.52, 5.06, 6.27, 8,
		5.48, 7.14, 10.12, 16] * 0.001;
	var filt = HPF.ar(DelayC.ar(sig, times, times).mean,
		125) * 4;

	Out.ar(0,Pan2.ar(filt,0))
}).add


~squarepanel = { |input|
	var times, filt;
	// times in milliseconds, converted to seconds:
	times = [4.52, 5.06, 6.27, 8, 5.48, 7.14, 10.12, 16] * 0.001;
	filt = DelayC.ar(input, times, times).mean;
	filt = HPF.ar(filt, 125);
	filt * 4
};

Synth(\door,[]);
Synth(\stickslip,[\decayScale, 1, \inertia, 0.1, \lag, 0.1,
	\stickiness,3, \randomness, 1])

thanks a lot for the explanation :slight_smile:
right now i have several approaches more or less doing the same thing and i would like to generalize them.
Is there a way to rewrite the slipstick-door example with the Friction UGen (ive tried my best to understand the paper which is referenced in the helpfile).
The chaotic slipping behaviour for the case external force > static friction should be even more “precise” in terms of physical modelling then using Sample and Hold.
Any ideas?

EDIT:
to start with the generalizing process ive renamed some of the variables for my own clarity. are these accurate?
is there somekind of angle introduced with 1.1 - extForce via the force triangle?

frictForce = (1.1 - extForce) * \stickiness.kr(6);
SynthDef(\stickslip, {

	var extForce, inMotion;
	var frictForce, stringFreq, stringFrict;
	var trig, trigRate;
	var sig, phasor, amp, decay;

	extForce = EnvGen.kr(Env([0, 1, 0, 1, 0], [1, 1, 1, 2])).lag(0.1);
	inMotion = extForce > \statFric.kr(0.1);

	frictForce = (1.1 - extForce) * \stickiness.kr(6);
	stringFreq = ((frictForce * 0.01) + 0.003);
	stringFrict = \randomness.kr(1) * (frictForce * 0.001 * TRand.ar(0.001, 1, 111));

	trigRate = inMotion * (1 / (stringFreq + stringFrict));
	trig = Impulse.ar(trigRate);

	phasor = Phasor.ar(trig, 10 * SampleDur.ir, 0, inf).min(1).sqrt;
	phasor = Delay1.ar(phasor);
	amp = Latch.ar(phasor, trig);
	decay = amp.sqrt;
	sig = EnvGen.ar(Env.perc(0.001, 1), trig, timeScale: decay * \decayScale.kr(0.01));

	sig = sig * amp;

	Out.ar(\out.kr(0), sig)
}).add;