Drawing a Variable Skew Envelope

Hi all -
I’d like to have an envelope of 5 points, that I can adjust accordingly in time. I’d like to be able to enter some numerical values to skew the lines between the points, to smooth or sharpen the transition.

I am having some trouble figuring this out - but I’m not exactly sure how to describe the error. Skewing at the moment is collapsing the envelope. I’ve tried a few other ideas, but none of it is quite working as expected.

Any insight into this problem would be appreciated. Thank you.

(
w = Window("", Rect(100, 100, 400, 400)).front;
~skew = 1;
~skew2 = 1;
~envView = Env([0, 0.5, 1, 0.5, 0], [2, 2,  2,  2],  [2, -2, 2, -2]);
	//nil node issues

		StaticText(w, Rect(80, 10, 40, 10)).string_("skew");
		

        NumberBox(w, Rect(80, 20, 80, 30)).action_({|t|
			~skew1 = t.value;
		    ~envView = Env(~envo.value[1], ~envo.value[0],[~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
			~envo.setEnv(~envView);
		
		});
		NumberBox(w, Rect(80, 50, 80, 30)).action_({|t|
			~skew2 = t.value;
		    ~envView = Env(~envo.value[1], ~envo.value[0],[~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
			~envo.setEnv(~envView);
		});


		~envo = EnvelopeView(w, Rect(10, 100, 200, 100))
		 .drawLines_(true)
		.selectionColor_(Color.red)
		.drawRects_(true)
		.resize_(5)
		.step_(0.05)
		.setEnv(~envView)
	    .action_({
		~envo.value.postln;
		~envView = Env(~envo.value[1], ~envo.value[0],[~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
	});


Button(w, Rect(230, 100, 40, 40))
.string_("play")
.action_({
	{SinOsc.ar(400, 0, 0.1) * EnvGen.ar(~envView, timeScale: 4, doneAction:2)}.play;
});

Button(w, Rect(290, 100, 40, 40))
.string_("view")
.action_({
~envView.plot;
});

)

there is a small typo. ~skew = 1 should be ~skew1 = 1 i think.

1 Like

I think the problem is this line:

EnvelopeView method ‘value’ returns 2 arrays of x and y values in the range 0-1 which are both the same length. But the levels and times inputs for Env can be outside this range and levels needs to have 1 more value than times (since the first node will always have a time of 0 so that’s not added to the times array).

Also with EnvelopeView, you may want to set keepHorizontalOrder to true - otherwise the node order can become messed up.

Hope that helps.
Paul

1 Like

In particular, I think the x values for “times” need to be differentiated – the Env times array does not consist of absolute x values, but rather the differences between them. @crl see the .differentiate message.

I think it’s a bit more accurate to describe it as: all envelope nodes proceed from “where you are now” toward a target value over a given amount of time, with a given curve. The node is fully described by target value, time, curve. The first node is then made of levels[1], times[0] and curves[0]. When the EnvGen starts, however, there is no “where you are now” based on any prior envelope segment, so an initial value must be given – this is levels[0]. I feel like this makes it a bit easier to talk about loop and release nodes (but others might feel differently).

hjh

1 Like

Alright - I’ve updated the code accordingly and placed it below- but I’m still missing some thing here. I think I’m not understanding this comment:

I start with a 5 level values and 4 time values, but that ~envView line somehow produces 5 levels and 5 time values. Would the idea be to append an extra level or remove a time value? Maybe there’s something else I’m missing? Thank you for the input.

(
w = Window("", Rect(100, 100, 400, 400)).front;
~skew1 = 1;
~skew2 = 1;
~envView = Env([0, 0.5, 1, 0.5, 0], [2, 2,  2,  2],  [2, -2, 2, -2]);
	//nil node issues

		StaticText(w, Rect(80, 10, 40, 10)).string_("skew");
		

        NumberBox(w, Rect(80, 20, 80, 30)).action_({|t|
			~skew1 = t.value;
		    ~envView = Env(~envo.value[1], ~envo.value[0].differentiate, [~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
			~envo.setEnv(~envView);
		
		});
		NumberBox(w, Rect(80, 50, 80, 30)).action_({|t|
			~skew2 = t.value;
		    ~envView = Env(~envo.value[1], ~envo.value[0].differentiate,[~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
			~envo.setEnv(~envView);
		});


		~envo = EnvelopeView(w, Rect(10, 100, 200, 100))
        .keepHorizontalOrder_(true)
		.drawLines_(true)
		.selectionColor_(Color.red)
		.drawRects_(true)
		.resize_(5)
		.step_(0.05)
		.setEnv(~envView)
	    .action_({
		~envo.value.postln;
		~envView = Env(~envo.value[1], ~envo.value[0].differentiate,[~skew1, ~skew2.neg, ~skew2, ~skew1.neg] );
	});


Button(w, Rect(230, 100, 40, 40))
.string_("play")
.action_({
	{SinOsc.ar(400, 0, 0.1) * EnvGen.ar(~envView, timeScale: 1, doneAction:2)}.play;
});

Button(w, Rect(290, 100, 40, 40))
.string_("view")
.action_({
~envView.plot;
});

)
 ~envo.value[0].size;
 ~envo.value[1].size;

The idea, as in my earlier post, is to differentiate the time array.

(
e = EnvelopeView(nil, Rect(800, 200, 500, 400)).front
.setEnv(Env([0, 1, 0.8, 0], [0.1, 0.3, 0.6]));
)

e.value[0]
-> [0.0, 0.1, 0.4, 1.0]

So note the relative durations put into the Env – 0.1, 0.3, 0.6 – and how they show up in the EnvelopeView.

Time from Env Time in EnvelopeView
0.0
+0.1 0.1
+0.3 0.4
+0.6 1.0

The EnvelopeView has integrated (summed) the duration values, to get absolute x coordinates.

e.value[0].differentiate
-> [0.0, 0.1, 0.3, 0.6]

So, almost there, but it keeps the first 0.0 – so the formula is e.value[0].differentiate.drop(1).

These values are scaled so that the sum is 1.0, so you’d have to scale those by the total desired duration.

hjh

1 Like

If you would like a fairly rich implementation of an envelope gui with variable curves, check out the envelope module in xynthii