Difference between Function.play and Function.asBuffer

I am certain it is me the problem but why do I get a stereo cluster out (what I want) in play, 3 sines per side:

b = {SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02])}.play

and a stereo single sine per side with this:

b = {SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02])}.asBuffer(1);
b.play

I tried all sorts of mix methods to make it elegant but couldn’t…

Seems to be an error in asBuffer.

In the source it uses the max size of the SynthDef array as the number of channels, but this doesn’t make sense in your example:

a = SynthDef("stinky", {
	b = SynthDef.wrap({SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02])});
	b.postln;
	b.size.max.postln;
	Out.ar(0, b)
})

sam

You don’t. You get 3 channels each with three sine waves in (play) and you a buffer with 3 channels, each with a unique sine wave (asBuffer).

b = {SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02])}.scope

SinOsc.ar([[440,789,535],[ 888,777,666]], mul: [0.01,0.03,0.02])
-> [ 
      [ a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ],  //[440,789,535] * 0.01
      [ a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ],  //[888,777,666] * 0.03
      [ a BinaryOpUGen, a BinaryOpUGen, a BinaryOpUGen ]   //[440,789,535] * 0.02
]

What you get from the UGen is an array of 3 arrays (each with three elements).
When you give this to Out, the following is inserted…

Mix.ar( SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02]) )
-> [ a Sum3, a Sum3, a Sum3 ] 

Which is why you get three channels - check scope to comfirm.

However, when you call asBuffer the buffer decides to only select the first element from each sub array - what would nested buffers mean anyway?

What exactly are you trying to achieve here? My guess is that you want to apply 0.01 to [440 (left), 888 (right)], 0.03 to [789 (left), 777 (right)], 0.02 to [535 (left), 666 (right)]? … that would look like this…

SinOsc.ar([ [440, 789, 535], [888, 777, 666]], mul: [[0.01, 0.03, 0.02]]).collect{|i| i.sum }

Note the extra brackets around [[0.01, 0.03, 0.02]] and the collect, which does:

[
   sin(440) * 0.01 + sin(789) * 0.03 + sin(535) * 0.02,
   sin(888) * 0.01 + sin(777) * 0.03 + sin(666) * 0.02
]

Now the two are equal…

~f = { SinOsc.ar([ [440, 789, 535], [888, 777, 666]], mul: [[0.01, 0.03, 0.02]]).collect{|i| i.sum } };
n = { ~f.() }.play;
b = { ~f.() }.asBuffer(1);
b.play;

In my opinion, this is mistake in supercollider, when you give a nested array to Out, it should just error and refuse to compile.

2 Likes

Good catch.

I guess it is very confusing that these sound exactly the same:

b = {SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02].dup)}.play

c = {SinOsc.ar([ [440, 789, 535], [888, 777, 666]], mul: [[0.01, 0.03, 0.02]]).collect{|i| i.sum }}.play

but these two don’t:

b = {SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02].dup)}.asBuffer(1);

b.play;

c = {SinOsc.ar([ [440, 789, 535], [888, 777, 666]], mul: [[0.01, 0.03, 0.02]]).collect{|i| i.sum }}.asBuffer(1)

c.play;

Sam

imho, it should always be an error to return, or otherwise attempt to output, a nested array.

thanks both. Indeed my confusion was that I didn’t get the same message (let along my bad nested arrays as a way to get the multichannel expansion to give me what I wanted)

what I wanted to do is this:

SinOsc.ar([[440,789,535],[ 888,777,666]],mul: [0.01,0.03,0.02].dup).flop.sum

which behaves in both cases. What I forgot is that if I execute this code I can see the data structure (nested arrays) and that is good to get the various origami of nested dimensions.

thanks again!

FYI, if you wanted to expand this with more freqs, it might be easier to write it like thus, pairing the freqs with their corresponding amplitudes directly…

[ 
	(freq: [440, 888], mul: 0.01), 
	(freq: [789, 777], mul: 0.03), 
	(freq: [535, 666], mul: 0.02) 
]
.collect(  SinOsc.performWithEnvir(\ar, _)  ).sum
3 Likes

a lot more readable indeed, thanks!