How can you tell when sclang has finished loading file

I have a client application in C++ that communicates with sclang which is run with the -D and script that defines SynthDefs. The client then uses OSC to dynamically create/delete synths and controls them. This works great, but I’m not really sure what is the best way to know when sclang has completed loaded the script? What if the client is run first? Or what if the sclang is already complete?

I did have a solution before where I would load each synth and try again if there were errors. But due to some recent refactoring to make the client handle calls asynchronously, this method seems more cumbersome.

Is there a standard way to know when the script is loaded?

Thanks,
Paul

If you’re only loading synths, there’s two ways to do this:

(
var cond = Condition();
var sync = Server.default.addr.makeSyncResponder(cond);

fork {
    "sending synthdef".postln;
    SynthDef(\foo, {
        Out.ar(0, SinOsc.ar(100))
    }).add(completionMsg: ["/sync", sync]);

    "sent synthdef, waiting to load".postln;

    cond.wait;

    "synthdef is loaded".postln;

    // send response
}
)  
(
fork {
    "sending synthdef".postln;
    SynthDef(\foo, {
        Out.ar(0, SinOsc.ar(100))
    }).add;

    "sent synthdef, waiting to load".postln;

    Server.default.sync;

    "synthdef is loaded".postln;

    // send "completed" response
}
)  

… where, once the synthdef is loaded on the server, you send an OSC response to your application telling it that the request was completed. Obviously the Server:sync version is a little simpler - I don’t think there are any downsides to using that, so it’s probably the way to go.

Thanks @scztt , but what if sclang has already run and the client is run after the message is sent? I would never see the message in that case?

Btw, I’m looking at “compiling” the synths to scsyndef files and using d_load.

Also related to your solution, I’m also defining options for server and defining some other things. I’m hoping that Notify won’t complete until this happens, but I’m not really sure. Wondering if this also affects your proposed solution?

Here are some sample things I’m doing:

s = Server.default;

s.options.bindAddress = "0.0.0.0";

s.options.numAnalogInChannels = 8; // can be 2, 4 or 8
s.options.numAnalogOutChannels = 2; // can be 2, 4 or 8
s.options.numDigitalChannels = 16;
s.options.maxLogins = 4;

...
s.boot;

s.waitForBoot
{
	~previousSampler = nil;
	~sendFootTrigger = { | k, v |
		~previousSampler !? {|p| p.set(\gate, 0) };   // remove old if exists
		~previousSampler = Synth(\playSample, [\buf: k, \vol: v]); // fill with your args
	};

	o = OSCFunc({ 
		arg msg, time;
		
		// [time, msg].postln; 
		if(msg[2] == -1 && msg[3] == 40, { ~sendFootTrigger.value(msg[5], msg[4]) });
  	},'/trg');
}

CondVar with a predicate is a good way to go.

fork {
    var cond = CondVar.new;
    var defsLoaded = false;
    var clientDidSignal = false;
    var clientAddr;
    var predicate = { defsLoaded and: { clientDidSignal } };

    OSCdef(\waitForSignal, { |msg, time, addr|
        clientDidSignal = true;
        clientAddr = addr;
        cond.signalAll;
    }, '/clientAsksAreYouReady');

    fork {
        ... load your synthdefs ...

        s.sync;
        defsLoaded = true;
        cond.signalAll;
    };

    cond.wait(predicate);

    OSCdef(\waitForSignal).free;

    clientAddr.sendMsg(\defsAreReady);
};

It would be something along those lines.

If the script is run first, then defsLoaded will be true and clientDidSignal will be false – so execution blocks at cond.wait until the sclang script gets the message from the client.

If the client is run first, I think it should do a loop and ping the SC script repeatedly until it comes back. (Why? If the client pings during SC’s class library compilation, the request won’t be received. So, keep trying until success.) cond.wait will block until the first clientAsksAreYouReady after defs are loaded.

hjh

1 Like

@jamshark70 I think I understand the example, but I don’t think it would work if the client needs to be started multiple times? Maybe that requirement is a must have, but one thing I realized is that while creating the OSC client code and testing, it was useful to simulate the client starting fresh each time in a loop as a stress test. One of my main concerns is stability specially when /tr can happen while asynchronous commands are waiting.

You can initiate a SynthDef-load cycle by any means you want. There’s nothing in that example that says you can do it only when SC starts up.

One way might be to load SynthDefs in response to a message from the client. When the client starts up, it would repeatedly ping the SC script. Eventually (maybe right away, or maybe after script init) the script would get this message, start loading SynthDefs, and when it’s finished, it can send a message back to the client saying it’s done. In this case, the sync would be even simpler because clientDidSignal would be guaranteed to be true (because it doesn’t start loading until the client signalled).

You could also remove the OSCdef while sending SynthDefs, to avoid triggering the load process while a load is already underway. Then put it back in place afterward, to be ready for the next client signal.

Viewed from that perspective, if the script loads SynthDefs immediately upon startup, this introduces complication that would be eliminated by loading on demand (by OSC message)… and this makes it possible to meet the other requirement of starting the client repeatedly without restarting the script.

hjh