Dur 1/3 problem

I definitively need to be able to change pattern with unfinit dur (like dur:1/3) without “bug”.
The exemple below demonstrate how sometimes (seem cyclic) the pattern-change is ok and sometimes not (out of 0db)…
The routine “r” is here to automate the pattern-change not to forge a Pchain.
You don’t really need to hear what happen (sound bad); the meter illustrate the problem.
Is there any tip to do it good ?

(
SynthDef(\bip, {
	Out.ar(0, SinOsc.ar(666 , 0.5pi, Env.asr(0.01, 1, 0.01,0).ar(2, \gate.kr(1))));
}).add;
waitForBoot(s, {
	Pdef.removeAll;
	Pdef.defaultQuant_(2);
	a = Pbind(\type, Pseq([\note, \rest!5].flat, inf), \instrument, \bip, \dur, 1/3, \sustain, 0.5)!2;
	t = TempoClock(8);
	t.schedAbs(t.nextBar, {t.beatsPerBar_(2)});
	Pdef(\a, a[1]).play(t, quant:2);
	r = Routine{
		var i=0;
		loop{
			Pdef(\a).source_(a[i]);
			"Pdef(\a).source_(a[%])".format(i).postln;
			i = i+1%2;
			4.wait;
		}
	}.play(t, [2, 1]);
	s.meter;
})
)
/*
work fine with finit dur
a = Pbind(\type, Pseq([\note, \rest!3].flat, inf), \instrument, \bip, \dur, 1/2, \sustain, 0.5)!2;
*/

With \dur = 1/3, this is 0.333… let’s chop this to, oh, 3 decimal places. And, to simplify the illustration, let’s reset the source every 1 beat instead of 2.

  • Beat 0, plays normally.
  • Beat 0.333, plays normally.
  • Beat 0.666, plays normally.
  • Beat 0.999 – the quant time for source reset has not been reached, so the pattern has to play normally.
  • Beat 1.000 – now the pattern reset causes another synth to play = two at once.

AFAICS, to make this work, if the pattern’s normal process will get close to a whole beat, it’s necessary (for the source_ call) to push it slightly after the beat. If this is short enough, then it will be imperceptible. (Below I’m using 0.0001 of a beat – at 60 bpm, a tenth of a ms will definitely not be audible.)

Here’s one way. Note that the “late” adjustment (+ precision) doesn’t accumulate, because then.round always takes a whole beat as the reference. Events may be slightly (imperceptibly) late, but also reset every beat to the same amount of lateness. (This technique of adjusting delta while leaving dur alone comes from Pfindur, Ppar, several patterns in the main library.)

It does not work if you leave out + precision – because of rounding error, then.round - now may sometimes be too small, and then you have the same problem.

(
var precision = 0.0001;

~compensateToIntegerBeats = Pfunc { |ev|
	var now = thisThread.beats;
	var then = now + ev.delta;
	
	// if we are very close to a whole beat
	if(then absdif: then.round < precision) {
		ev[\delta] = then.round - now + precision;
	};
	
	ev
};
)

... then, your example, but with this instead:

	a = Pchain(~compensateToIntegerBeats, Pbind(\type, Pseq([\note, \rest!5].flat, inf), \instrument, \bip, \dur, 1/3, \sustain, 0.5)) ! 2;

hjh

1 Like

thank you jamshark70. I will try your code.
since the first post, i use something like Pseq([0.3334, Pseq([1/3], inf)]).

BTW, I think this is related to this issue (not yet resolved):

class library: Psync now rounds up time by telephon · Pull Request #4801 · supercollider/supercollider (github.com)