Why does it matter how I execute this code?

2019-07-25 01:46发布

问题:

I have built the following simple synth structure which creates a synth and routes its output through an effects unit:

b = Bus.audio(numChannels: 2);

SynthDef(
    "mySynth",
    {
        |freq, amp, gate = 1|
        var vol = 0.5;
        var audio = Pulse.ar(freq, 0.5);
        var env = EnvGen.kr(Env.perc, doneAction:2);
        audio = Pan2.ar(audio, MouseX.kr(-1, 1));
        Out.ar(b, audio * env);
    }
).add;


SynthDef(
    "effects",
    {
        var audio = In.ar(b, 2);
        audio = LPF.ar(audio, MouseY.kr(200, 1000));
                    //TODO: Implement some crazy, revolutionary effects
            Out.ar(0, audio);
    }
).add;

// **** Dividing line for executing the code ****

e = Synth(\effects);

p = Pbind(*[
    instrument: \mySynth,
    scale: #[0, 2, 4, 5, 7, 9, 11],
    degree: Pseq([3,        3,      9,      9,      2,      9,      9,      3,      5,      7], inf),
    dur:        Pseq([0.2,  0.2,    0.2,    0.1,    0.1,    0.2,    0.2,    0.2,    0.1,    0.1], inf),
    amp:        Pseq([1,        0.6,    0.9,    0.3,    0.4,    0.9,    0.6,    0.85,   0.3,    0.4], inf),
]);

p.play;

This only produces audible output when I execute the code in a particular way:

  • I can execute each block individually, in order, and I get audible output.
  • I can execute the first blocks up to the 'dividing line' comment, then the following blocks, and I get audible output.
  • If I execute all the code together, I don't get audible output.

I'm guessing there has to be some delay between declaring a SynthDef and then instantiate it using Synth(), while the server does set setup stuff. Can anyone shed any light?

回答1:

It is because you can't just "add" SynthDefs to the server and create an instance of said synth in the same execution. If you "play" the synths as they are executed then an instance of them gets added to the server so that when you call the Synth up for execution it will already be loaded. Working code is included below.

(
b = Bus.audio(numChannels: 2);

SynthDef(
    "mySynth",
    {
        |freq, amp, gate = 1|
        var vol = 0.5;
        var audio = Pulse.ar(freq, 0.5);
        var env = EnvGen.kr(Env.perc, doneAction:2);
        audio = Pan2.ar(audio, MouseX.kr(-1, 1));
        Out.ar(b, audio * env);
    }
).play;

SynthDef(
    "effects",
    {
        var audio = In.ar(b, 2);
        audio = LPF.ar(audio, MouseY.kr(200, 1000));
                    //TODO: Implement some crazy, revolutionary effects
            Out.ar(0, audio);
    }
).play;



// **** Dividing line for executing the code ****

e = Synth(\effects);

p = Pbind(*[
    instrument: \mySynth,
    scale: #[0, 2, 4, 5, 7, 9, 11],
    degree: Pseq([3,        3,      9,      9,      2,      9,      9,      3,      5,      7], inf),
    dur:        Pseq([0.2,  0.2,    0.2,    0.1,    0.1,    0.2,    0.2,    0.2,    0.1,    0.1], inf),
    amp:        Pseq([1,        0.6,    0.9,    0.3,    0.4,    0.9,    0.6,    0.85,   0.3,    0.4], inf),
]);

p.play;
)


回答2:

I usually get around this with the Server.sync() method. It pauses execution of the enclosing thread (e.g. a Routine) until all asynchronous server commands have been completed. This includes sending SynthDefs and allocating Buffers. You can pass a Condition argument to Server.sync() for more explicit control.

so for example, you can execute this block in one go:

s = Server.local;
s.boot;
s.doWhenBooted({
    Routine {
        SynthDef.new(\sine, {
            arg out=0, hz=220, dur=4.0;
            var snd, amp;
            snd = SinOsc.ar(hz);
            amp = EnvGen.ar(Env.linen(0.1, dur, 0.1), doneAction:2);
            Out.ar(out, (amp*snd).dup);
        }).send(s);
        s.sync; // waits here
        x = Synth.new(\sine);
    }.play;
});


回答3:

I'm sure you're right that it's to do with the delay between declaring the synthdef and it being ready.

I'm not really experienced enough with sclang to immediately tell you exactly how you should change your code (I generally use scsynth via OSC, only using sclang to write SynthDefs), but you should be able to do something with the optional completionMsg argument to SynthDef.add.