Adding SynthDefs / Synths in classes?

hey there,

i’m starting to look into writing classes an have got the feeling that i haven’t totally grasped the applying concepts…
i’d like to write a class that defines .init and .start functions, which would then add SynthDefs to the Server and then plays, stops and frees them. the server is passed to the object as an argument.
how would you go about adding synthdefs and then using them to spawn synths in an instance function of a class? is this even the right approach to achieving such a goal or would there be a more efficient implementation? just using SynthDef(…).add and then Synth.new(…) doesn’t seem to work / returns a ‘SynthDef not found’ message.

thank you for your help!

moritz

It sounds like your issue is caused by SynthDef.add being an asynchronous operation (like anything that changes or queries the state of the Server). You’ll have this issue whether you’re calling SynthDef.add followed by Synth.new in a method or in any other Function.

The solution is essentially the same in either case, but it’s slightly more complex to implement in a method. With a Function, you’d either call fork to create a Routine or wrap the Function in a Routine directly. Either will allow you to call instance methods of Server that block the thread pending the results of asynchronous operations (like SynthDef.add). The simplest one of these methods is sync:

{
    SynthDef.new(\foo, { /* graph function */ }).add;

    s.sync; // wait for async actions to complete before proceeding

    Synth.new(\foo)
}.fork

Without the sync, the client calls SynthDef.add, which hands work off to the Server, then continues evaluation without waiting for the Server-side operation to complete, which is why you’re getting “SynthDef not found.”

If you want to do this in a method of some Class you’re writing, you can’t fork the method itself, but you can fork a Function from within the method:

init {|server| // pass Server as an arg
    server = server ? Server.default; // fall back on default Server if nil

    {
        server.waitForBoot({ // make sure Server is booted
            SynthDef.new(\foo, { /* graph function */ }).add;

            server.sync;

            bar = Synth.new(\foo) // assuming your class has an instance var "bar"
        })
    }.fork
}

As for whether this is the right approach, I don’t think of it as being “idiomatic sclang”, and it does have some gotchas – because a method like the example above does all its work through side effects of the forked Function and because you can’t reason deterministically from the client about how long the Server will take to complete a task, it’s probably best to restrict this technique to methods that do not need to return values. Also, because forking a Function creates a Routine, you can’t return a value from it as you would a normal Function, though you can assign values to variables above the scope of the forked Function, such as the instance variables of your Class.

Having said all that, I personally think the benefits outweigh the disadvantages if you’re careful and I personally use this pattern quite a lot. Hope that helps!

2 Likes

thank you so much for your reply and the clarification - i guess using server.sync in my method should work fine for my usecase.

1 Like

If you know the SynthDefs’ design in advance, you can use this pattern:

MyClass {
    // variables
    *initClass {
        StartUp.add {
            SynthDef(...).add;
            // as many as you need
        }
    }
    // other methods
}

The server doesn’t even have to be booted – they will be sent when the server boots.

If the structure is not known until play time, then you have two choices:

  • fork as catniptwinz said, or
  • Have a separate method to prepare the SynthDef, before your playing method makes the synth.

hjh

4 Likes

That’s a great idea; I should’ve mentioned that I tend to do this in the “init” method only because I’m usually constructing SynthDefs based on the values of arguments passed to init.