Timing issue (w/ patterns)

Hi guys, a question about timing.

I’m interested in making the time between evaluation of code and emission of related sound the shorter I can.

I’ve noticed that when I’m using patterns instead of creating synths directly, I always have a little delay.
Here’s what i mean:

Let this SynthDef be our test synth:

(
SynthDef(\test, {
var sig = SinOsc.ar(440)*EnvGen.ar(Env.perc(), doneAction:2);
Out.ar(0, sig!2);
}).add;
)

Now, if I evaluate the following line the sound is generate immediately:

x = Synth(\test);

On the other hand, when I use something like the following snippet of code, there’s always a (little) gap between the code evaluation and the actual emission of sound:

(
z = Pbind(
\instrument, \test,
\dur, Pseq([0.1], 1)
).play;
)

I’ve alos tryied to set the “quant” argument to 0 but nothing seems to change.

(
z = Pbind(
\instrument, \test,
\dur, Pseq([0.1], 1)
).play(quant:0);
)

Why is there this time delay?
Is there something different I can try to solve it?

Thank you very musch for your support, as always

PS: I’ve also noticed that this time delay can be longer if I use different OS (Windows or Ubuntu Studio).
So maybe is question of:

  • different version of SC;
  • drivers
    ??

Three ideas:

  1. You’re declaring the Pbind and automatically converting it into an EventStreamPlayer in the .play method, both of these things take time. It might be better to declare and convert to an EventStreamPlayer first, and then play that instead.

  2. I believe .play defaults to using the default TempoClock, but maybe SystemClock is faster? I’m not sure, I’d try .play(SystemClock) instead of .play()

  3. The Supercollider server has a default latency of 0.2 seconds, which you can see with s.latency. If you set this to 0, you’ll get a bunch of warnings because everything will be behind instantaneous timing, so set it to something like 0.03, or whatever is slightly more than the warnings you tend to get with 0 latency.

2 Likes

You’re not really using patterns in the way they were intended to be used, so that’s kind of what’s going on here. In general patterns work best when you’re writing quantized code. Think of them like Ableton Link. patterns are synchronized to start on the beat, or the bar. That means you can start one pattern with a bass drum, and then a few seconds later a snare, and know that they will play in synch.

Anyway this should work:

(
z = Pbind(
\instrument, \test,
\dur, Pseq([0.1], 1)
).play(quant:Quant(0, -0.2))
)

Why are you doing this out of curiosity? What makes immediate execution so important.

Thank you @spacetimed for your reply.
I will try changing the latency and also modifying the clock.
What about declaring and converting the pattern to an EventStreamPlayer in advance?
What is the code/functions/methods I should use to do it?

Hi @cian, and thank you for your support.
Your explanation is pretty clear and makes totally sense to me: I will try with the .play(quant:Quant(0, -0.2)).

Immediate execution is important to me because this code is part of an interactive music installation I’m creating.
This Pbind (like many others I’m creating in a similar way) is executed just after the user interaction so it is crucial that the sound is played immediately to get an effective user experience.

What are other possible methods I can try to get the desired result if not with Patterns?
Maybe I can use Tasks, Functions of Routines something like that?

Have a look at the server timing help file.

http://doc.sccode.org/Guides/ServerTiming.html

In general latency is your friend, it ensures exact timing as the example in the help file shows. You can use Tasks and Routines also, but then you have to define bundling and latency yourself, Patterns do that automatically. You can deine latency also within Pbind, which might be more practical than defining it globally.

(
z = Pbind(
    \instrument, \test,
    \dur, Pseq([0.1], 1),
    \latency, 0.03
).play;
)

You have to try out, what latency doesn’t end in ‘late’ messages in your setup. 0.05 should mostly be ok, the default of 0.2 seems a bit overcautious nowadays. BTW if you are sequencing with short durations take OffsetOut instaed of Out in your SynthDef.
If a latency of 30 to 50 ms is still too much and you want to sequence something, consider server-side sequencing e.g. with demandrate UGens.

Daniel

1 Like

From general UI experience, and I don’t know if this will help you in this case, if your users need immediate feedback, cheat! Seriously, a lag of some small fraction of a second is long enough to create “did I press the button” anxiety, so play something immediately. Have an innocuous sounds ready that makes it obvious things are working.

I guess it’s rather unlikely that this occurs with a latency of 35 ms, imo most people will hardly notice. But indeed in many cases inaccuracies won’t be audible either, very much depends on the sounds used.

Here some examples for comparison:

(
SynthDef(\test, { |freq = 400, amp = 0.1|
    var sig = Saw.ar(freq) * EnvGen.ar(Env.perc(0.01, 0.2), doneAction: 2) * amp;
    OffsetOut.ar(0, sig!2);
}).add;

p = Pbind(
    \instrument, \test,
    \dur, 0.1,
    \amp, Pseq([0.6, 0.1, 0.3, 0.1], inf),
    \midinote, Pwhite(70, 90, inf),
    \latency, 0.035
);
)


// here the latency is hard to notice

Pfin(17, p).play


// on the other hand inaccuracy here is also not very strong

Pbindf(Pfin(17, p), \latency, 0).play





// however things are different with very short durations and / or other sound structures

Pbindf(Pfin(129, p), \midinote, Pseq([75, 70, 70, 70], inf), \dur, 0.01).play;


// repetitions make inaccuracy clearly perceivable here !

Pbindf(Pfin(129, p), \midinote, Pseq([75, 70, 70, 70], inf), \dur, 0.01, \latency, 0).play;