rsc3/doc-schelp/Help-3.12.2/Guides/Sync-Async.html

196 lines
No EOL
12 KiB
HTML

<!doctype html><html lang='en'><head><title>Synchronous and Asynchronous Execution | SuperCollider 3.9.3 Help</title>
<link rel='stylesheet' href='./../scdoc.css' type='text/css' />
<link rel='stylesheet' href='./../frontend.css' type='text/css' />
<link rel='stylesheet' href='./../custom.css' type='text/css' />
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />
<script>
var helpRoot = './..';
var scdoc_title = 'Synchronous and Asynchronous Execution';
var scdoc_sc_version = '3.9.3';
</script>
<script src='./../scdoc.js' type='text/javascript'></script>
<script src='./../docmap.js' type='text/javascript'></script>
<script src='./../prettify.js' type='text/javascript'></script>
<script src='./../lang-sc.js' type='text/javascript'></script>
</head>
<body onload='fixTOC();prettyPrint()'>
<div id='toc'>
<div id='toctitle'>Synchronous and Asynchronous Execution:</div>
<span class='toc_search'>Filter: <input id='toc_search'></span><ul class='toc'></ul></div><div class='contents'>
<div id='menubar'></div>
<div class='header'>
<div id='label'>
<span id='folder'>Guides</span>
| <span id='categories'><a href='./../Browse.html#SC3 vs SC2'>SC3 vs SC2</a></span>
</div><h1>Synchronous and Asynchronous Execution</h1>
<div id='summary'>The problem of simultaneous synchronous and asynchronous execution</div>
</div>
<div class='subheader'>
</div>
<p>Using a program such as SuperCollider introduces a number of issues regarding timing and order of execution. Realtime audio synthesis requires that samples are calculated and played back at a certain rate and on a certain schedule, in order to avoid dropouts, glitches, etc. Other tasks, such as loading a sample into memory, might take arbitrary amounts of time, and may not be needed within a definite timeframe. This is the difference between synchronous and asynchronous tasks.
<p>Problems can arise when synchronous tasks are dependent upon the completion of asynchronous ones. For instance trying to play a sample that may or may not have been completely loaded yet.
<p>In SC2 this was relatively simple to handle. One scheduled synchronous tasks during synthesis, i.e. within the scope of a <code class='code prettyprint lang-sc'>Synth.play</code>. Asynchronous tasks were executed in order, outside of synthesis. Thus one would first create buffers, load samples into them, and then start synthesis and play them back. The interpreter made sure that each step was only done when the necessary previous step had been completed.
<p>In SC3 the separation of language and synth apps creates a problem: How does one side know that the other has completed necessary tasks, or in other words, how does the left hand know if the right is finished? The flexibility gained by the new architecture introduces another layer of complexity, and an additional demand on the user.
<p>A simple way to deal with this is to execute code in blocks. In the following code, for instance, each block or line of code is dependent upon the previous one being completed.<pre class='code prettyprint lang-sc'>// Execute these one at a time
// Boot the server
s.boot;
// Compile a SynthDef and write it to disk
(
SynthDef("Help-SynthDef", { arg out = 0;
Out.ar(out, PinkNoise.ar(0.1))
}).writeDefFile;
)
// Load it into the server
s.loadSynthDef("Help-SynthDef");
// Create a Synth with it
x = Synth.new("Help-SynthDef", s);
// Free the node on the server
x.free;
// Allow the client-side Synth object to be garbage collected
x = nil;</pre>
<p>In the previous example it was necessary to use interpreter variables (the variables a-z, which are declared at compile time) in order to refer to previously created objects in later blocks or lines of code. If one had declared a variable within a block of code (i.e. <code class='code prettyprint lang-sc'>var mySynth;</code>) than it would have only persisted within that scope. (See the helpfile <a href="./../Reference/Scope.html">Scoping and Closure</a> for more detail.)
<p>This style of working, executing lines or blocks of code one at a time, can be very dynamic and flexible, and can be quite useful in a performance situation, especially when improvising. But it does raise the issues of scope and persistence. Another way around this that allows for more descriptive variable names is to use environment variables (i.e. names that begin with ~, so <code class='code prettyprint lang-sc'>~mysynth;</code> see the <a href="./../Classes/Environment.html">Environment</a> helpfile for details). However, in both methods you become responsible for making sure that objects and nodes do not persist when you no longer need them.<pre class='code prettyprint lang-sc'>(
SynthDef("Help-SynthDef", { arg out = 0;
Out.ar(out, PinkNoise.ar(0.1))
}).send(s);
)
// make a Synth and assign it to an environment variable
~mysynth = Synth.new("Help-SynthDef", s);
// free the synth
~mysynth.free;
// but you've still got a Synth object
~mysynth.postln;
// so remove it from the Environment so that the Synth will be garbage collected
currentEnvironment.removeAt(\mysynth);</pre>
<p>But what if you want to have one block of code which contains a number of synchronous and asynchronous tasks. The following will cause an error, as the <a href="./../Classes/SynthDef.html">SynthDef</a> that the server needs has not yet been received.<pre class='code prettyprint lang-sc'>// Doing this all at once produces the error "FAILURE /s_new SynthDef not found"
(
var name;
name = "Rand-SynthDef" ++ 400.0.rand; // use a random name to ensure it's not already loaded
SynthDef(name, { arg out=0;
Out.ar(out, PinkNoise.ar(0.1))
}).send(s);
Synth.new(name, s);
)</pre>
<p>A crude solution would be to schedule the dependant code for execution after a seemingly sufficient delay using a clock.<pre class='code prettyprint lang-sc'>// This one works since the def gets to the server app first
(
var name;
name = "Rand-SynthDef" ++ 400.0.rand;
SynthDef(name, { arg out = 0;
Out.ar(out, PinkNoise.ar(0.1))
}).send(s);
SystemClock.sched(0.05, {Synth.new(name, s);}); // create a Synth after 0.05 seconds
)</pre>
<p>Although this works, it's not very elegant or efficient. What would be better would be to have the next thing execute immediately upon the previous thing's completion. To explore this, we'll look at an example which is already implemented.
<p>You may have realized that first example above was needlessly complex. SynthDef-play will do all of this compilation, sending, and Synth creation in one stroke of the enter key.<pre class='code prettyprint lang-sc'>// All at once
(
SynthDef("Help-SynthDef", { arg out = 0;
Out.ar(out, PinkNoise.ar(0.1))
}).play(s);
)</pre>
<p>Let's take a look at the method definition for SynthDef-play and see what it does.<pre class='code prettyprint lang-sc'> play { arg target,args,addAction=\addToTail;
var synth, msg;
target = target.asTarget;
synth = Synth.basicNew(name,target.server); // create a Synth, but not a synth node
msg = synth.newMsg(target, addAction, args);// make a message that will add a synth node
this.send(target.server, msg); // ** send the def, and the message as a completion message
^synth // return the Synth object
}</pre>
<p>This might seem a little complicated if you're not used to mucking about in class definitions, but the important part is the second argument to <code class='code prettyprint lang-sc'>this.send(target.server, msg);</code>. This argument is a completion message, it is a message that the server will execute when the send action is complete. In this case it says create a synth node on the server which corresponds to the <a href="./../Classes/Synth.html">Synth</a> object I've already created, when and only when the def has been sent to the server app. (See the helpfile <a href="./../Reference/Server-Command-Reference.html">Server Command Reference</a> for details on messaging.)
<p>Many methods in SC have the option to include completion messages. Here we can use SynthDef-send to accomplish the same thing as SynthDef-play:<pre class='code prettyprint lang-sc'>// Compile, send, and start playing
(
SynthDef("Help-SynthDef", { arg out=0;
Out.ar(out, PinkNoise.ar(0.1))
}).send(s, ["s_new", "Help-SynthDef", x = s.nextNodeID]);
// this is 'messaging' style, see below
)
s.sendMsg("n_free", x);</pre>
<p>The completion message needs to be an OSC message, but it can also be some code which when evaluated returns one:<pre class='code prettyprint lang-sc'>// Interpret some code to return a completion message. The .value is needed.
// This and the preceding example are essentially the same as SynthDef.play
(
SynthDef("Help-SynthDef", { arg out=0;
Out.ar(out, PinkNoise.ar(0.1))
}).send(s, {x = Synth.basicNew("Help-SynthDef"); x.newMsg; }.value); // 'object' style
)
x.free;</pre>
<p>If you prefer to work in 'messaging' style, this is pretty simple. If you prefer to work in 'object' style, you can use the commands like <code class='code prettyprint lang-sc'>newMsg</code>, <code class='code prettyprint lang-sc'>setMsg</code>, etc. with objects to create appropriate server messages. The two proceeding examples show the difference. See the <a href="./../Guides/NodeMessaging.html">Node Messaging</a> helpfile for more detail.
<p>In the case of <a href="./../Classes/Buffer.html">Buffer</a> objects a function can be used as a completion message. It will be evaluated and passed the <a href="./../Classes/Buffer.html">Buffer</a> object as an argument. This will happen after the <a href="./../Classes/Buffer.html">Buffer</a> object is created, but before the message is sent to the server. It can also return a valid OSC message for the server to execute upon completion.<pre class='code prettyprint lang-sc'>(
SynthDef("help-Buffer",{ arg out=0, bufnum;
Out.ar(
out,
PlayBuf.ar(1,bufnum,BufRateScale.kr(bufnum))
)
}).load(s);
y = Synth.basicNew("help-Buffer"); // not sent yet
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav", action: { arg buffer;
// synth add its s_new msg to follow
// after the buffer read completes
y.newMsg(s,\addToTail,[\bufnum,buffer])
} // .value NOT needed, unlike in the previous example
);
)
// when done...
y.free;
b.free;</pre>
<p>The main purpose of completion messages is to provide OSC messages for the server to execute immediately upon completion. In the case of <a href="./../Classes/Buffer.html">Buffer</a> there is essentially no difference between the following:<pre class='code prettyprint lang-sc'>(
b = Buffer.alloc(s, 44100,
completionMessage: { arg buffer; ("bufnum:" + buffer).postln; }
);
)
// this is equivalent to the above
(
b = Buffer.alloc;
("bufnum:" + b).postln;
)</pre>
<p>One can also evaluate a function in response to a 'done' message, or indeed any other one, using an <a href="./../Classes/OSCFunc.html">OSCFunc</a>. See its help file for details.<pre class='code prettyprint lang-sc'>(
SynthDef("help-SendTrig",{
SendTrig.kr(Dust.kr(1.0), 0, 0.9);
}).send(s);
// register to receive this message
a = OSCFunc({ arg msg, time;
("This is the done message for the SynthDef.send:" + [time, msg]).postln;
}, '/done', a.addr).oneShot; // remove me automatically when done
b = OSCFunc({ arg msg, time;
[time, msg].postln;
}, '/tr', s.addr);
c = OSCFunc({ arg msg;
"this is another call".postln;
}, '/tr', s.addr);
)
x = Synth.new("help-SendTrig");
b.free;
c.free;
x.free;</pre>
<p><div class='doclink'>helpfile source: <a href='file:///Applications/SuperCollider.app/Contents/Resources/HelpSource/Guides/Sync-Async.schelp'>/Applications/SuperCollider.app/Contents/Resources/HelpSource/Guides/Sync-Async.schelp</a><br>link::Guides/Sync-Async::<br></div></div></body></html>