Stuttering sample playback using OSC on OS X

Hello! I’m experiencing some stuttering sample playback on OS X. @Geoffroy on lines suggested I ask here for help.

I’ve built a simple sampler application. A stripped down version that shows the issue looks like this:

s.waitForBoot({
~b1 = Buffer.read(s, "1.wav");

SynthDef(\sampler, {
        arg obs=0, buf, rate=1, amp=1;
        var sig;
        sig = PlayBuf.ar(1, buf, rate, \t_tr.kr(1, 0), doneAction: Done.freeSelf);
        Out.ar(obs, sig);
}).add;

~handler = {
        arg msg, time, addr, recvPort;
        Synth(\sampler, [\buf, ~b1.bufnum]);
};

n = NetAddr.new("127.0.0.1");
o = OSCFunc(~handler, '/sampler/1', n, 49162);
});

I’m running this sclang script from the command line (on OS X 10.14 on a macbook pro) with sclang sampler.scd.

To reproduce the issue I’m seeing, in a separate terminal window I’m triggering the sample via OSC using sendosc in a steady pulse with while true; do sendosc 127.0.0.1 49162 /sampler/1; sleep 0.5; done.

If I do anything fairly intensive on my machine while this is running (for example open a new browser tab for Gmail in FF), the audio stutters. It sounds like some of the OSC events are not arriving in time, and sometimes get backed up before all firing at once.

I’ve run the code in the IDE as well, and the issue is also present there. While stuttering, the CPU load reported in the IDE window is under 1%. The sound doesn’t glitch, it’s almost as if the messages are getting delayed and then several are arriving close together.

Is this just something to be expected when running supercollider under OS X with other things happening on the machine, or is there something wrong in the way I’m using sclang/OSC? Are there any tips for achieving more solid timing?

As a test: try running your script within the IDE, and then in the same IDE:

o = NetAddr("127.0.0.1", 49162);

r = fork {
    loop {
        o.sendMsg('/sampler/1');
        0.5.wait;
    }
};

// do intensive things

// when finished testing
r.stop;

If the behavior is good, then you know sclang sending to itself is fine.

You could also run your script in a terminal window, and then run my test in the IDE, to find out if one sclang sending to another sclang is also fine.

If all tests show the timing problem, then we’d have to conclude that the problem is in message receipt. (Or, if sclang to the same instance is fine but sclang to another sclang instance is not, that would also point to message receipt as the problem.)

If both sclang → sclang tests are fine but your sendosc case is not, then it would point to a problem in sendosc.

I haven’t seen major problems in the timing of sclang responding to incoming OSC. There’s always slight jitter but not enough to cause messages to get backed up for hundreds of ms. (Jitter should come mostly from the network layer – that’s unavoidable. FWIW of the three supported platforms, OSC response timing seems to be more solid in OSX than the other two.) So I have a feeling the issue is probably in the sender’s timing.

hjh

Thank you @jamshark70 - that’s given me some things to try.

I’ve done what you’ve suggested and used your sclang fork/timer snippet to send the messages to both the same interpreter (within the IDE) and my script running in a terminal. In both situations I hear the same stuttering/late messages as before. This is happening when opening new browser tabs and opening new applications.

I wonder what the issue can be. Do you have any other suggestions for things I could do to find out?

That’s very strange. Timing of OSC message receipt has generally been reliable for me. Even when I first started using SC3 on a g4 powerPC chip, I don’t remember seeing that level of poor performance – and on a modern multicore Intel chip, a busy Firefox window and a not-very-busy sclang process should be serviced by different cores.

Jitter should be on the order of a few ms.

Ok, let’s take the OSC–>sclang part out of it (differential testing, we know it always breaks when the flow is going through an OSCFunc, but we don’t know if it breaks when it isn’t).

r = fork {
    loop {
        Synth(\sampler, [\buf, ~b1.bufnum]);
        0.5.wait;
    }
};

// do intensive things

// when finished testing
r.stop;

If that shows the same behavior, then there’s some configuration problem on the machine causing OSC messages to be delayed. I have no idea what that could be (I’ve never seen it before).

If it doesn’t, then scsynth receiving OSC is fine while sclang receiving isn’t… which would be extremely weird.

I don’t have a good guess. This isn’t a common problem. Maybe specific to that machine?

hjh

Thanks again @jamshark70! I ran the following code:

s.waitForBoot({
	~b1 = Buffer.read(s, "1.wav");

	SynthDef(\sampler, {
		arg obs=0, buf, rate=1, amp=1;
		var sig;
		sig = PlayBuf.ar(1, buf, rate, \t_tr.kr(1, 0), doneAction: Done.freeSelf);
		Out.ar(obs, sig);
	}).add;


	r = fork {
		loop {
			Synth(\sampler, [\buf, ~b1.bufnum]);
			0.5.wait;
		}
	};

	// r.stop;
})

I’m still experiencing the same lagged/delayed/glitchy problem as before. So it looks like something on my machine is delaying OSC messages between sclang and scserver. I’ve tried stopping some services that were running on my machine (things like Google Drive and Dropbox sync) but that doesn’t seem to have helped.

I guess I might have to resign myself to this being specific to my machine - perhaps a future clean reinstall or something will make it go away. I thought I’d share the code and what I’ve seen in case it helps anyone in the future!

This definitely isn’t expected.

It’s possible to set process priority in macOS: https://www.techrepublic.com/article/how-to-adjust-cpu-priority-using-apples-terminal/

Try giving sclang and scsynth a lower “nice” value = higher priority, should get attention from the CPU sooner than Firefox. (Or maybe there is some system utility that is raising Firefox priority?) (Both sclang and scsynth set some threads to high priority already but I’m not sure if that includes OSC receipt.)

In any case, if the problem occurs when another app is busy, that points to process priorities being different on your machine from every other machine I’ve used.

hjh

Very strange @jamshark70 - I set the nice value for the scserver and sclang processes to -5 using sudo renice -5 -p $PID, but it hasn’t helped to improve the stuttering. I can still reliably stutter the audio seriously by opening a new application or browser tab.

I’m afraid you’ll need advice from a Mac specialist then… I don’t recall hearing of this problem before, in SC.

Any Mac users able to chime in?

hjh

Thank you for all your help in diagnosing the problem!

I’ve just tried running your code in post 1 on one IDE tab, and code in post 2 in another tab, then messing with Firefox (opening browser intensive sites) and I can reproduce the problem with my 2012 Macbook on 10.12.6.

A few things to try:

  1. Set a larger hardware buffer size, e.g. Server.default.options.hardwareBufferSize = 2048
  2. Set an extremely large latency, e.g. Server.default.latency = 2.0
  3. Try the internal server: Server.default = s = Server.internal.
  4. Create an aggregate device in Audio MIDI Setup, and use this as your input AND output in SC. Try different combinations of devices, Clock Source, and Drift Correction settings, to see if you see any improvements.
  5. Switch to a lower display resolution, and check “Reduce Transparency” and “Reduce Motion” in your Accessibility settings. The only thread on OSX that can compete with the audio thread is the display thread - this should reduce the workload required for display. It’s unlikely that this is the cause, but if e.g. you had a display heavy app that was falling back to CPU for some part of it’s display callback, it might be here. If this were the case, you’d also see very high CPU usage in WindowServer that would correspond with your dropouts.

Changing the process priority via renice is unlikely to help - on mac the audio thread is already has special priority that’s managed by the OS, and we manually set the priority on other server threads.

This may be a clock problem? sclang schedules messages based on your system time - this system time has to be reconciled with your audio clock, and there’s some tricky stuff happening to make this work (because the audio clock and the system clock are not in sync, and can drift). If your system clock OR your audio clock are wonky, it would cause the problems you’re seeing. For example, the Norns device (which runs SC) had a super serious bug where, when it connected to the WIFI, it would sync it’s clock - this would cause the system clock to jump a long ways in the future/past, which would in turn completely break scheduling for the server.

True, unless the issue is in the thread sending or receiving OSC messages. (From the initial post, I got the impression that this isn’t a problem with the audio thread.)

Since the OP is using non-timestamped messages, changing the latency wouldn’t have an effect.

Also, raising the hardware buffer size would reduce glitches in the audio thread (maybe not the issue here) and also make the timing of non-timestamped notes less exact. But it’s a valid test to see if maybe the audio thread is contributing to the problem in a way that I’m not understanding at the moment.

hjh

Did you get stuttering only with this example ?
Did you check with other audio applications ?
There’s a known issue with stuttering on built-in out - I have a MacBook Pro from 2018, it’s only usable with interface actually.

Thanks for all the suggestions @scztt!

I’ve tried them all individually as much as I can. I think there might be an improvement using the internal server. Notes below.

Set a larger hardware buffer size, e.g. Server.default.options.hardwareBufferSize = 2048

Perhaps a small improvement, but I can reliable stutter the audio by opening Gmail in a Firefox tab, or searching using spotlight.

The stutter doesn’t sound like a audio buffer not being filled in time, it sounds like a delay to the events arriving.

Set an extremely large latency, e.g. Server.default.latency = 2.0

No improvement.

Server.default = s = Server.internal

I think this does seem to help, it seemed more stable when opening new browser tabs and applications.

Server.default.options.device_(“Aggregate Device”);

Clock source: Built in microphone
Drift correction: Yes

Clock source: Built in microphone
Drift correction: No

Clock source: Built in output
Drift correction: Yes

Clock source: Built in output
Drift correction: No

No improvement.

check “Reduce Transparency” and “Reduce Motion” in your Accessibility settings

No improvement.

Switch to a lower display resolution

I don’t seem to have that option under displays on OS X 10.14.6 on my macbook pro.

I’m glad it’s not something unique to my machine, thanks for testing @Geoffroy.

I have experienced audio glitches with some other audio apps, for example vcvrack on this machine. In that case, I dialled down the animations etc and it helped. The quality of the glitching was quite different in that case though, this situation sounds more like a large jitter or delay in events firing rather than the audio buffer not being filled in time. But it is quite hard to tell.