rsc3/doc-schelp/Help-3.12.2/Guides/Multichannel-Expansion.html

205 lines
No EOL
15 KiB
HTML

<!doctype html><html lang='en'><head><title>Multichannel Expansion | 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 = 'Multichannel Expansion';
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'>Multichannel Expansion:</div>
<span class='toc_search'>Filter: <input id='toc_search'></span><ul class='toc'><li class='toc1'><a href='#Multiple%20channels%20as%20Arrays'>Multiple channels as Arrays</a></li>
<ul class='toc'></ul><li class='toc1'><a href='#Multichannel%20expansion'>Multichannel expansion</a></li>
<ul class='toc'><li class='toc2'><a href='#Expanding%20methods%20and%20operators'>Expanding methods and operators</a></li>
<ul class='toc'></ul><li class='toc2'><a href='#Using%20flop%20for%20multichannel%20expansion'>Using flop for multichannel expansion</a></li>
<ul class='toc'></ul></ul><li class='toc1'><a href='#Pitfalls'>Pitfalls</a></li>
<ul class='toc'></ul><li class='toc1'><a href='#Protecting%20arrays%20against%20expansion'>Protecting arrays against expansion</a></li>
<ul class='toc'></ul><li class='toc1'><a href='#Reducing%20channel%20expansion%20with%20Mix'>Reducing channel expansion with Mix</a></li>
<ul class='toc'></ul></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#Server'>Server</a>&#8201;&gt;&#8201;<a href='./../Browse.html#Server>Nodes'>Nodes</a> | <a href='./../Browse.html#UGens'>UGens</a>&#8201;&gt;&#8201;<a href='./../Browse.html#UGens>Multichannel'>Multichannel</a></span>
</div><h1>Multichannel Expansion</h1>
<div id='summary'>Explaining multichannel expansion and representation</div>
</div>
<div class='subheader'>
</div>
<h2><a class='anchor' name='Multiple%20channels%20as%20Arrays'>Multiple channels as Arrays</a></h2>
<p>Multiple channels of audio are represented as <a href="./../Classes/Array.html">Array</a>s.<pre class='code prettyprint lang-sc'>s.boot;
// one channel
{ Blip.ar(800,4,0.1) }.play;
// two channels
{ [ Blip.ar(800,4,0.1), WhiteNoise.ar(0.1) ] }.play;</pre>
<p>Each channel of output will go out a different speaker, so your limit here is two for a stereo output. If you have a supported multi channel audio interface or card then you can output as many channels as the card supports.
<p>All <a href="./../Classes/UGen.html">UGen</a>s have only a single output. This uniformity facilitates the use of array operations to perform manipulation of multi channel structures.
<p>In order to implement multichannel output, UGens create a separate UGen known as an <a href="./../Classes/OutputProxy.html">OutputProxy</a> for each output. An OutputProxy is just a place holder for the output of a multichannel UGen. OutputProxies are created internally, you never need to create them yourself, but it is good to be aware that they exist so you'll know what they are when you run across them.<pre class='code prettyprint lang-sc'>// look at the outputs of Pan2:
Pan2.ar(PinkNoise.ar(0.1), FSinOsc.kr(3)).dump;
play({ Pan2.ar(PinkNoise.ar(0.1), FSinOsc.kr(1)); });</pre>
<h2><a class='anchor' name='Multichannel%20expansion'>Multichannel expansion</a></h2>
<p>When an <a href="./../Classes/Array.html">Array</a> is given as an input to a unit generator it causes an array of multiple copies of that unit generator to be made, each with a different value from the input array. This is called multichannel expansion. All but a few special unit generators perform multichannel expansion. Only Arrays are expanded, no other type of Collection, not even subclasses of Array.<pre class='code prettyprint lang-sc'>{ Blip.ar(500,8,0.1) }.play // one channel
// the array in the freq input causes an Array of 2 Blips to be created :
{ Blip.ar([499,600],8,0.1) }.play // two channels
Blip.ar(500,8,0.1).postln // one unit generator created.
Blip.ar([500,601],8,0.1).postln // two unit generators created.</pre>
<p>Multichannel expansion will propagate through the expression graph. When a unit generator constructor is called with an array of inputs, it returns an array of instances. If that array is the input to another constructor, then another array is created, and so on.<pre class='code prettyprint lang-sc'>{ RLPF.ar(Saw.ar([100,250],0.05), XLine.kr(8000,400,5), 0.05) }.play;
// the [100,250] array of frequency inputs to Saw causes Saw.ar to return
// an array of two Saws, that array causes RLPF.ar to create two RLPFs.
// Both RLPFs share a single instance of XLine.</pre>
<p>When a constructor is parameterized by two or more arrays, then the number of channels created is equal to the longest array, with parameters being pulled from each array in parallel. The shorter arrays will wrap.
<p>for example, the following:<pre class='code prettyprint lang-sc'>Pulse.ar([400, 500, 600],[0.5, 0.1], 0.2)</pre>
<p>is equivalent to:<pre class='code prettyprint lang-sc'>[ Pulse.ar(400,0.5,0.2), Pulse.ar(500,0.1,0.2), Pulse.ar(600,0.5,0.2) ]</pre>
<p>A more complex example based on the Saw example above is given below. In this example, the <a href="./../Classes/XLine.html">XLine</a> is expanded to two instances, one going from 8000 Hz to 400 Hz and the other going in the opposite direction from 500 Hz to 7000 Hz. These two XLines are 'married' to the two Saw oscillators and used to parameterize two copies of <a href="./../Classes/RLPF.html">RLPF</a>. So on the left channel a 100 Hz Saw is filtered from 8000 Hz to 400 Hz and on the right channel a 250 Hz Saw is filtered from 500 Hz to 7000 Hz.<pre class='code prettyprint lang-sc'>{ RLPF.ar(Saw.ar([100,250],0.05), XLine.kr([8000,500],[400,7000],5), 0.05) }.play;</pre>
<h3><a class='anchor' name='Expanding%20methods%20and%20operators'>Expanding methods and operators</a></h3>
<p>Many operators and methods also multichannel expand. For example all common math operators:<pre class='code prettyprint lang-sc'>{ Saw.ar([100,250]) * [0.5,0.8] }.play;
{ Saw.ar(LFNoise1.kr(1).range(0,100) + [100,250]) }.play;</pre>
<p>Also the various UGen convenience functions like <code class='code prettyprint lang-sc'>.clip2</code>, <code class='code prettyprint lang-sc'>.lag</code> and <code class='code prettyprint lang-sc'>.range</code> :<pre class='code prettyprint lang-sc'>{ Saw.ar(LFNoise1.kr(1).range(100,[200,300])) }.play;
{ Saw.ar(LFPulse.kr(1).range(100,[200,300]).lag([0.1,0.2])) }.play;</pre>
<p>The expansion is handled by wrapper-methods defined in <a href="./../Classes/SequenceableCollection.html">SequenceableCollection</a>.
<p>You can use <a href="./../Classes/Object.html#-multiChannelPerform">Object: -multiChannelPerform</a> to do multichannel expansion with any method on any kind of object:<pre class='code prettyprint lang-sc'>["foo","bar"].multiChannelPerform(\toUpper);</pre>
<p>The shorter arrays wrap:<pre class='code prettyprint lang-sc'>["foo","bar","zoo"].multiChannelPerform('++', ["l","ba"])</pre>
<h3><a class='anchor' name='Using%20flop%20for%20multichannel%20expansion'>Using flop for multichannel expansion</a></h3>
<p>The method flop swaps columns and rows, allowing to derive series of argument sets:<pre class='code prettyprint lang-sc'>(
SynthDef("help_multichannel", { |out=0, freq=440, mod=0.1, modrange=20|
Out.ar(out,
SinOsc.ar(
LFPar.kr(mod, 0, modrange) + freq
) * EnvGate(0.1)
)
}).send(s);
)</pre>
<p><pre class='code prettyprint lang-sc'>(
var freq, mod, modrange;
freq = Array.exprand(8, 400, 5000);
mod = Array.exprand(8, 0.1, 2);
modrange = Array.rand(8, 0.1, 40);
fork {
[\freq, freq, \mod, mod, \modrange, modrange].flop.do { |args|
args.postln;
Synth("help_multichannel", args);
0.3.wait;
}
};
)</pre>
<p>Similarly, <a href="./../Classes/Function.html#flop">Function:flop</a> returns an unevaluated function that will expand to its arguments when evaluated:<pre class='code prettyprint lang-sc'>(
SynthDef("blip", { |out, freq|
Out.ar(out,
Line.ar(0.1, 0, 0.05, 1, 0, 2) * Pulse.ar(freq * [1, 1.02])
)
}).add;
a = { |dur=1, x=1, n=10, freq=400|
fork { n.do {
if(x.coin) { Synth("blip", [\freq, freq]) };
(dur / n).wait;
} }
}.flop;
)
a.value(5, [0.3, 0.3, 0.2], [12, 32, 64], [1000, 710, 700]);</pre>
<h2><a class='anchor' name='Pitfalls'>Pitfalls</a></h2>
<p>Some UGens create stereo output from mono input, and might not behave as expected regarding multichannel expansion.
<p>For example, <a href="./../Classes/Pan2.html">Pan2</a> :<pre class='code prettyprint lang-sc'>{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]) }.play;</pre>
<p>The expectation here might be that the two sines would get individual pan positions. And they do, but Pan2 expands into two stereo ugens nested in an outer array, resulting in a total of four output channels. <code class='code prettyprint lang-sc'>play</code> will add an <a href="./../Classes/Out.html">Out</a> UGen for each of them, resulting in both Pan2's writing to the same output bus:<pre class='code prettyprint lang-sc'>Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5])
// prints:
// [ [ an OutputProxy, an OutputProxy ], [ an OutputProxy, an OutputProxy ] ]</pre>
<p>In this case, the solution is simply to sum the nested four channels into a single stereo-channel:<pre class='code prettyprint lang-sc'>{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]).sum }.play;</pre>
<p>If we take a look at the resulting UGen graph of the code above, we can see that it is correct. The two Pan2 is mixed together to create a single stereo output:<pre class='code prettyprint lang-sc'>{ Pan2.ar(SinOsc.ar([500,600]),[-0.5,0.5]).sum }.asSynthDef.dumpUGens
// prints:
// [ 0_Control, scalar, nil ]
// [ 1_SinOsc, audio, [ 500, 0 ] ]
// [ 2_Pan2, audio, [ 1_SinOsc, -0.5, 1 ] ]
// [ 3_SinOsc, audio, [ 600, 0 ] ]
// [ 4_Pan2, audio, [ 3_SinOsc, 0.5, 1 ] ]
// [ 5_+, audio, [ 2_Pan2[0], 4_Pan2[0] ] ]
// [ 6_+, audio, [ 2_Pan2[1], 4_Pan2[1] ] ]
// [ 7_Out, audio, [ 0_Control[0], 5_+, 6_+ ] ]</pre>
<h2><a class='anchor' name='Protecting%20arrays%20against%20expansion'>Protecting arrays against expansion</a></h2>
<p>Some unit generators such as <a href="./../Classes/Klank.html">Klank</a> require arrays of values as inputs. Since all arrays are expanded, you need to protect some arrays by a <a href="./../Classes/Ref.html">Ref</a> object. A Ref instance is an object with a single slot named 'value' that serves as a holder of an object. <code class='code prettyprint lang-sc'>Ref.new(object)</code> is one way to create a Ref, but there is a syntactic shortcut. The backquote <code class='code prettyprint lang-sc'>`</code> is a unary operator that is equivalent to calling <code class='code prettyprint lang-sc'>Ref.new(something)</code>. So to protect arrays that are inputs to a Klank or similar UGens you write:<pre class='code prettyprint lang-sc'>Klank.ar(`[[400,500,600],[1,2,1]], z)</pre>
<p>You can still create multiple Klanks by giving it an array of Ref'ed arrays.<pre class='code prettyprint lang-sc'>Klank.ar([ `[[400,500,600],[1,2,1]], `[[700,800,900],[1,2,1]] ], z)</pre>
<p>is equivalent to:<pre class='code prettyprint lang-sc'>[ Klank.ar(`[[400,500,600],[1,2,1]], z), Klank.ar(`[[700,800,900],[1,2,1]], z)]</pre>
<p>Also the Refs multichannelExpand when passed to a Klank:<pre class='code prettyprint lang-sc'>Klank.ar(`[[[400,500,600], [700,800,900]],[1,2,1]], z)</pre>
<p>, which is is equivalent to:<pre class='code prettyprint lang-sc'>[ Klank.ar(`[[400,500,600],[1,2,1]], z), Klank.ar(`[[700,800,900],[1,2,1]], z)]</pre>
<h2><a class='anchor' name='Reducing%20channel%20expansion%20with%20Mix'>Reducing channel expansion with Mix</a></h2>
<p>The <a href="./../Classes/Mix.html">Mix</a> object provides the means for reducing multichannel arrays to a single channel.<pre class='code prettyprint lang-sc'>Mix.new([a, b, c]) // array of channels</pre>
<p>or<pre class='code prettyprint lang-sc'>[a, b, c].sum</pre>
<p>is equivalent to:<pre class='code prettyprint lang-sc'>a + b + c // mixed to one</pre>
<p>Mix is more efficient than using + since it can perform multiple additions at a time. But the main advantage is that it can deal with situations where the number of channels is arbitrary or determined at runtime.<pre class='code prettyprint lang-sc'>// three channels of Pulse are mixed to one channel
{ Mix.new( Pulse.ar([400, 501, 600], [0.5, 0.1], 0.1) ) }.play</pre>
<p>Multi channel expansion works differently for Mix. Mix takes one input which is an array (one not protected by a Ref). That array does not cause copies of Mix to be made. All elements of the array are mixed together in a single Mix object. On the other hand if the array contains one or more arrays then multi channel expansion is performed one level down. This allows you to mix an array of stereo (two element) arrays resulting in one two channel array. For example:<pre class='code prettyprint lang-sc'>Mix.new( [ [a, b], [c, d], [e, f] ] ) // input is an array of stereo pairs</pre>
<p>is equivalent to:<pre class='code prettyprint lang-sc'>// mixed to a single stereo pair
[ Mix.new( [a, c, e] ), Mix.new( [b, d, f] ) ]</pre>
<p>Currently it is not recursive. You cannot use Mix on arrays of arrays of arrays.
<p>Here's a final example illustrating multi channel expansion and Mix. By changing the variable 'n' you can change the number of voices in the patch. How many voices can your machine handle?<pre class='code prettyprint lang-sc'>(
{
var n;
n = 8; // number of 'voices'
Mix.new( // mix all stereo pairs down.
Pan2.ar( // pan the voice to a stereo position
CombL.ar( // a comb filter used as a string resonator
Dust.ar( // random impulses as an excitation function
// an array to cause expansion of Dust to n channels
// 1 means one impulse per second on average
Array.fill(n, 1),
0.3 // amplitude
),
0.01, // max delay time in seconds
// array of different random lengths for each 'string'
Array.fill(n, {0.004.rand+0.0003}),
4 // decay time in seconds
),
Array.fill(n,{1.0.rand2}) // give each voice a different pan position
)
)
}.play;
)</pre>
<div class='doclink'>helpfile source: <a href='file:///Applications/SuperCollider.app/Contents/Resources/HelpSource/Guides/Multichannel-Expansion.schelp'>/Applications/SuperCollider.app/Contents/Resources/HelpSource/Guides/Multichannel-Expansion.schelp</a><br>link::Guides/Multichannel-Expansion::<br></div></div></body></html>