Visualizer for animation

Hi there, is it possible to have a variable (that controls a value of line in Pen) to be defined by the sound, like a visualizer does? I know there is Amplitude to follow the sound but I do not know how to put its values into the animation. I am interested for the ~z variable below.

the code:

(
var w,v;
var run = true;
var partial=2000;
///////0/////////////////////////////////////////////////////////////////////////////////////////////////
    Window.closeAll;
    w = Window("S",Rect( 600,100,2000,1000),scroll:false).front.alwaysOnTop_(true);
    w.alpha = 1;  w.view.background = Color.black;
	//~slider1= Slider(w,Rect(500,940,850,10)).action_({ arg obj; ~z = obj.value.linlin(0,1,1,3600); ~v.refresh;});
//~slider2= Slider(w,Rect(500,950,850,10)).action_({ arg obj; ~y = obj.value.linlin(0,1,1,360); ~v.refresh;});
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////view
~v = UserView.new(w,Rect(50,100,1800,800)).background_(Color.black);
~v.drawFunc ={
	Pen.translate(900,400);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////lines
	(partial).do{
		~z=400;
	~x=~z.cos; ~y=~z.sin;
			~k=Polar.new(~z,~y*pi).asPoint;~v.refresh;
		~a=Polar.new(~z,~x*pi).asPoint;~v.refresh;
			Pen.width=0.1;
		Color(red:1,green:1,blue:1,alpha:1).set;
		Pen.line(~k,~a);Pen.stroke;~v.refresh;
	//	Pen.line(~a,Polar.new(~z,~y).asPoint);Pen.stroke;~v.refresh;
		Pen.rotate(pi/(partial/2));Pen.stroke;~v.refresh;
			};
////////////////////////////////////////////////////////////////////////////////
};
{ while { run } {~v.background_(Color.black); ~v.refresh; (1/10).wait } }.fork(AppClock);
//~v.background_(Color.black);~v.refresh;
)

First: Please see Code markup: Correct style – if someone copy/pastes the example, it won’t run because some * characters have been interpreted as italics –

The backtick tags described in the link will override text formatting, and then we’ll see ~y*pi as I’m sure you originally wrote.

Then: A SendReply / OSCdef pair is the normal way to get changing data from the server into the language. The OSCdef would prepare variables with the new data and then do defer { ~v.refresh }.

hjh

thank you for the remark and the advice jamshark70.

Somewhat big example can be seen here: Squiggle squiggle

It has a bunch of synths (\genSegxxx) generating control signals onto a bus, then one synth (\squiggletestAutomatic) that reads control signal from that bus, uses them to modulate a sound and also uses SendReply to send them from the server to the language and to visualize them with a Pen.

shihs I will try to make it work, my first try failed. If I understand correctly a Pattern cannot control the Pen variable right? I t needs to be through SendReply?

stupid question i guess

Check out SCintellator quark!

I guess the point is to simplify the flow of control as much as possible.

When using SendReply with an impulse trigger, you’re getting messages a few times a second. Animation needs to update the frame a few times a second – so the cleanest, simplest way is to drive the frame update from the incoming messages.

If you’re simultaneously playing a pattern and using that to trigger updates, it’s messy.

One unfortunate gap in the documentation is that almost all of the Pbind examples use .play, which might lead you to the conclusion that the only way to use Pbind is to play it. That’s not true. You can make a stream from it, and request information from that stream at any time – including in response to OSC, or UserView updates.

// edit: forgot loading the buffer
b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01.wav");

(
var amp = 0;

a = {
	var sig = PlayBuf.ar(1, b, loop: 1);
	var amp = Amplitude.ar(sig, 0.01, 0.25);
	SendReply.ar(Impulse.ar(25), '/amp', amp);
	(sig * 0.1).dup
}.play;

OSCdef(\amp, { |msg|
	amp = msg[3];
	defer { v.refresh };
}, '/amp', s.addr);

p = Pbind(
	\theta, Pseries(0, 0.1, inf),
	\red, sin(Pkey(\theta)) * 0.5 + 0.5,
	\green, sin(Pkey(\theta) + 1) * 0.5 + 0.5,
	\blue, sin(Pkey(\theta) + 2) * 0.5 + 0.5
).asStream;

v = UserView(nil, Rect(800, 200, 500, 400)).front
.drawFunc_({ |view|
	var b = view.bounds.moveTo(0, 0);
	var c = b.center;
	
	var colorEvent = p.next(Event.new);
	var color = Color(colorEvent[\red], colorEvent[\green], colorEvent[\blue]);
	
	var size = amp * b.width * 0.45;
	
	Pen
	.color_(color)
	.fillOval(Rect.aboutPoint(c, size, size));
});

v.onClose = { OSCdef(\amp).free; a.release };
)

hjh

Controlling Pen from a pattern is possible too, again by having the pattern send out OSC messages, and reacting to them in the visualization code. In this case both sending of the OSC and receiving of the OSC happens in the language(in the previous example, the OSC was generated on the server, and then reacted to in the language). There’s an example of that approach here: granular AM synthesis and another one here: Generating Graphics and Music From The Dragon Curve .

Edited to add: Admittedly, these examples are not “as small as possible educational examples” but rather “almost real-life applications”. I remember I struggled for quite a while to find out how to do these seemingly basic things back when I started with supercollider because there were no examples in the documentation (that I knew of) that demonstrate these techniques. Basically they are only obvious once you already know about them :slight_smile: I’m now thinking that perhaps the official documentation could be extended with some basic examples of visualization techniques (although I’m not sure exactly where to add them).

1 Like

when I send a variable ~z it works fine:

TempoClock.default.tempo_(120/20);

(
var w,v;
var run = true;
var partial=2000,radius;
~z=1;
(
SynthDef.new(\id, {
	arg dur=1;
	var x=1,y=1,t;
	var k=0, sig,sig1,env,env1,s;
	var sum=0,sum1=0;
1.do{
		dur=dur+1;
		~z=((dur+1)/~z);
	x=~z.cos.abs; y=~z.sin.abs;
	k=Complex(x,y).abs;
	t=Complex(x,y).angle.abs;
	env=EnvGen.ar(Env.perc(0.01,8,0.05/y,[-4,-2]),doneAction:2);
	env1=EnvGen.ar(Env.perc(0.01,8,0.05/x,[-4,-2]),doneAction:2);

	sig=FSinOsc.ar(x*880,mul:env1,iphase:x*pi);
sig1=FSinOsc.ar(y*881,mul:env,iphase:y*pi);
sum1=sum1+sig1; sum=sum+sig;
	};
		SendReply.ar(Impulse.ar(25), '/z', ~z);
		Out.ar(0,Rotate2.ar(sum*env,sum1*env,LFSaw.ar(t)));

}).add;
);

( Pdef(\1, Pbind(\instrument, \id,
	//\dur,5;
	\dur,Pbrown(1,7,1,inf),
			)).play(quant:1));

OSCdef(\amp, { |msg|
	~z = msg[3];
	defer { ~v.refresh };
}, '/z', s.addr);

    Window.closeAll;
    w = Window("S",Rect( 600,100,2000,1000),scroll:false).front.alwaysOnTop_(true);
    w.alpha = 1;  w.view.background = Color.black;
	//~slider1= Slider(w,Rect(500,940,850,10)).action_({ arg obj; ~z = obj.value.linlin(0,1,1,3600); ~v.refresh;});
~v = UserView.new(w,Rect(50,100,1800,800)).background_(Color.black);
~v.drawFunc ={
	~z=~z*50;~v.refresh;
	Pen.translate(900,400);
	(partial).do{
	~x=~z.cos; ~y=~z.sin;
			~k=Polar.new(~z,~x*pi).asPoint;~v.refresh;
		~a=Polar.new(~z,~y*pi).asPoint;~v.refresh;
			Pen.width=0.1;
		Color(red:1,green:1,blue:1,alpha:1).set;
		Pen.line(~k,~a);Pen.stroke;~v.refresh;
	//	Pen.line(~a,Polar.new(~z,~y).asPoint);Pen.stroke;~v.refresh;
		Pen.rotate(pi/(partial/2));Pen.stroke;~v.refresh;
			};
};
{ while { run } {~v.background_(Color.black); ~v.refresh; (1/10).wait } }.fork(AppClock);
//~v.background_(Color.black);~v.refresh;
)

the thing is when I use Amplitude in the same way this Error pops out.

(
var w,v;
var run = true;
var partial=2000,radius;
(
SynthDef.new(\id, {
	arg dur=1;
	var x=1,y=1,t,z=1;
	var k=0, sig,sig1,env,env1,s;
	var sum=0,sum1=0;
1.do{
		dur=dur+1;
		z=((dur+1)/z);
	x=z.cos.abs; y=z.sin.abs;
	k=Complex(x,y).abs;
	t=Complex(x,y).angle.abs;
	env=EnvGen.ar(Env.perc(0.01,8,0.05/y,[-4,-2]),doneAction:2);
	env1=EnvGen.ar(Env.perc(0.01,8,0.05/x,[-4,-2]),doneAction:2);

	sig=FSinOsc.ar(x*880,mul:env1,iphase:x*pi);
sig1=FSinOsc.ar(y*881,mul:env,iphase:y*pi);
sum1=sum1+sig1; sum=sum+sig;
	};
	~a = Amplitude.ar(sum, 0.01, 0.25);
		SendReply.ar(Impulse.ar(25), '/z', ~a);
		Out.ar(0,Rotate2.ar(sum*env,sum1*env,LFSaw.ar(t)));

}).add;
);

( Pdef(\1, Pbind(\instrument, \id,
	//\dur,5;
	\dur,Pbrown(1,7,1,inf),
			)).play(quant:1));

OSCdef(\amp, { |msg|
	~a = msg[3];
	defer { ~v.refresh };
}, '/z', s.addr);

    Window.closeAll;
    w = Window("S",Rect( 600,100,2000,1000),scroll:false).front.alwaysOnTop_(true);
    w.alpha = 1;  w.view.background = Color.black;
	//~slider1= Slider(w,Rect(500,940,850,10)).action_({ arg obj; ~z = obj.value.linlin(0,1,1,3600); ~v.refresh;});
~v = UserView.new(w,Rect(50,100,1800,800)).background_(Color.black);
~v.drawFunc ={
	~a=~a*100;~v.refresh;
	Pen.translate(900,400);
	(partial).do{
	~x=~a.cos; ~y=~a.sin;
			~k=Polar.new(~a,~x*pi).asPoint;~v.refresh;
		~a=Polar.new(~a,~y*pi).asPoint;~v.refresh;
			Pen.width=0.1;
		Color(red:1,green:1,blue:1,alpha:1).set;
		Pen.line(~k,~a);Pen.stroke;~v.refresh;
	//	Pen.line(~a,Polar.new(~z,~y).asPoint);Pen.stroke;~v.refresh;
		Pen.rotate(pi/(partial/2));Pen.stroke;~v.refresh;
			};
};
{ while { run } {~v.background_(Color.black); ~v.refresh; (1/10).wait } }.fork(AppClock);
//~v.background_(Color.black);~v.refresh;
)

error:

ERROR: Message 'cos' not understood.
RECEIVER:
Instance of Point {    (0x7fcbd9b85f88, gc=70, fmt=00, flg=00, set=02)
  instance variables [2]
    x : Float 27.929933   1C2FB1AE 403BEE10
    y : Float -4.730890   8567D375 C012EC6E
}
ARGS:
CALL STACK:
	DoesNotUnderstandError:reportError
		arg this = <instance of DoesNotUnderstandError>
	Nil:handleError
		arg this = nil
		arg error = <instance of DoesNotUnderstandError>
	Thread:handleError
		arg this = <instance of Thread>
		arg error = <instance of DoesNotUnderstandError>
	Object:throw
		arg this = <instance of DoesNotUnderstandError>
	Object:doesNotUnderstand
		arg this = <instance of Point>
		arg selector = 'cos'
		arg args = [*0]
	< FunctionDef in closed FunctionDef >  (no arguments or variables)
	Integer:do
		arg this = 2000
		arg function = <instance of Function>
		var i = 1
	UserView:doDrawFunc
		arg this = <instance of UserView>
^^ The preceding error dump is for ERROR: Message 'cos' not understood.
RECEIVER: Point( 27.92993332066, -4.7308903545971 )

don not get why that is.

a, I see I was using ~a two times. anyway everything works fine now, thank you.

Perhaps some performance considerations: since you call ~v.refresh manually, you can set ~v.animate = false (that is the default value I guess, so that’s what you do now). I don’t think you need to call ~v.refresh so often (unless you have a particular reason to do so?). E.g. in the ~v.drawFunc I would call it once at the end.

Alternatively, you can set ~v.animate = true and then I think you don’t need to call ~v.refresh at all (but in my experience, using ~v.animate = true is very resource hungry).

1 Like

shiihs raises a good point.

The above line is calling refresh repeatedly… and here:

This is also calling refresh repeatedly.

Why do you need two of them?

A: You don’t. You only need one.

Which is better?

The OSCdef one is synced with new data from the server. The while loop is not synced. So IMO the OSCdef makes sense and the while loop isn’t necessary.

I’d delete the while loop.

hjh

1 Like