Animate Slider in Time

hi list,

I am trying to move a slider continuously according to a given duration, but a have problem with timing:

(
w = Window.new(“A Slider”);
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

Tdef(\sliderPlayer, {
inf.do{
6.do{|i|
a.value = i/6;
(1/6).wait;
}
} }).play(AppClock)

I would like to be able to specify a time - how long it takes for a slider to move from right to left - and have that repeated infinitely.

thanks

m

Hi, would something like this work?

(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
	var dur = 1; //specify duration in seconds
	var framerate = 1/30; //adjust this to taste
	loop {
		a.value = (a.value + (framerate / dur)) % 1;
		framerate.wait;
	}
}).play(AppClock)
)

@jpburstrom beat me to it as I was about to post :wink:
I’ll paste my version here anyway since I just wrote it:

(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
	var dur = 3; // desired duration of complete motion, in seconds
	var minStep = 0.05; // minimum step size in seconds 
	var numSteps = dur / minStep; // number of steps needed
	numSteps.postln;
	inf.do{
		numSteps.do{ |i|
			a.value = i / numSteps;
			minStep.wait;
		}
} }).play(AppClock)
)
1 Like

thanks @jpburstrom and @Bruno
m

in this case i think it’s better to reference the computer clock so that if there’s a hick-up in sclang the slider can restore its position.

compare this method…


(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
  var dur = 3; // desired duration of complete motion, in seconds
  var updateRate = 0.05; // 'resolution'
  var startTime = Main.elapsedTime;
  inf.do{
    var v = Main.elapsedTime - startTime % dur / dur;
    {a.value = v}.defer;
    updateRate.wait;
  } }).play;
)

even with cpu spikes like… 20000.do{|i| i.postln} the slider will find its way back in my version.

(also i wouldn’t relay on AppClock for any critical timing. one can play the Tdef on SystemClock and still {}.defer only the slider update part.)
_f

15 juli 2021 kl. 20:42 skrev Bruno T Ruviaro via scsynth <noreply@m.scsynth.org>:

Bruno
July 15

@jpburstrom beat me to it as I was about to post :wink:
I’ll paste my version here anyway since I just wrote it:

(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
	var dur = 3; // desired duration of complete motion, in seconds
	var minStep = 0.05; // minimum step size in seconds 
	var numSteps = dur / minStep; // number of steps needed
	numSteps.postln;
	inf.do{
		numSteps.do{ |i|
			a.value = i / numSteps;
			minStep.wait;
		}
} }).play(AppClock)
)

3 Likes

thank you @redFrik.

How can I deal with duration change?I would like the task to start with a new duration once the previous cycle is completed.

m

right, had to think about that a little bit. here’s one basic solution…

(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
var durs = [3, 2, 1];
var updateRate = 0.05; // 'resolution'
durs.do{|dur|
var startTime = Main.elapsedTime;
var v = 0;
while({v < dur}, {
v = Main.elapsedTime - startTime;
{a.value = v / dur}.defer;
updateRate.wait;
});
};
{a.value = 0}.defer; //optionally set slider to 0 when done
}).play;
)

but i have a hunch there’s a more elegant way.
_f

15 juli 2021 kl. 23:01 skrev marcin pietruszewski via scsynth <noreply@m.scsynth.org>:

thank you @redFrik.

How can I deal with duration change?I would like the task to start with a new duration once the previous cycle is completed.

m

#|
fredrikolofsson.com musicalfieldsforever.com

Nah, that’s about as clean as it gets. AFAIK we don’t have a doForTime method, so the while is fine.

Could tighten up the while just slightly – it’s legal to do assignments within the condition function:

(
Tdef(\sliderPlayer, {
	var durs = [3, 2, 1];
	var updateRate = 0.05; // 'resolution'
	durs.do { |dur|
		var startTime = Main.elapsedTime;
		var v;
		while({
			v = Main.elapsedTime - startTime;
			v < dur
		}, {
			defer { a.value = v / dur };
			updateRate.wait;
		});
	};
	defer { a.value = 0 }; //optionally set slider to 0 when done
}).play;

hjh

this is what i was hunting for last night…

(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer, {
var durs = [3, 2, 1];
var updateRate = 0.05; // 'resolution'
var idurs = [0] ++ durs.integrate;
var startTime = Main.elapsedTime;
inf.do{
var v = Main.elapsedTime - startTime % idurs.last;
var i = idurs.indexOfGreaterThan(v);
{a.value = v - idurs[i-1] / durs[i-1]}.defer;
updateRate.wait;
};
}).play;
)

simple and compared to the one below even safer as there’s only one time stamp - one time reference - taken at the start. so it will recover and find its way back in the cycle of segments even if there’s a longer disruption (like if scdoc suddenly starts rendering help files or one opens the Quarks gui).
in the example below there are more points of failure as we reset the timer at the beginning of each segment.
_f

16 juli 2021 kl. 02:11 skrev James Harkins via scsynth <noreply@m.scsynth.org>:

jamshark70
July 16

redFrik:

but i have a hunch there’s a more elegant way.

Nah, that’s about as clean as it gets. AFAIK we don’t have a doForTime method, so the while is fine.

Could tighten up the while just slightly – it’s legal to do assignments within the condition function:

(
Tdef(\sliderPlayer, {
	var durs = [3, 2, 1];
	var updateRate = 0.05; // 'resolution'
	durs.do { |dur|
		var startTime = Main.elapsedTime;
		var v;
		while({
			v = Main.elapsedTime - startTime;
			v < dur
		}, {
			defer { a.value = v / dur };
			updateRate.wait;
		});
	};
	defer { a.value = 0 }; //optionally set slider to 0 when done
}).play;

hjh


Visit Topic or reply to this email to respond. Email replies to this address may be posted publicly and archived on scsynth.org (privacy policy).

You are receiving this because you enabled mailing list mode. To unsubscribe from these emails, click here.

#|
fredrikolofsson.com musicalfieldsforever.com

1 Like

Btw if we want to get really picky, it should be using SystemClock.seconds and not elapsed time.

IMO 99% of the time, it’s the logical time that we want, and also 99% of the time we end up reaching for physical time by mistake :grin:

This would for instance eliminate instability around measuring the start time of each segment.

hjh

These are great @jamshark70 and @redFrik.

I have one more question. Why is ‘dur’ an array? Essentially, I would like to be able to set a new value using Tdef.set method. I have tried substituting durs with envir.dur, but it doesn’t work:

Tdef(\sliderPlayer).set(\dur, [10, 2, 3])

(

Tdef(\sliderPlayer, {|envir|

var durs = envir.dur;

var updateRate = 0.05; // ‘resolution’

var idurs = [0] ++ durs.integrate;

var startTime = Main.elapsedTime;

inf.do{

var v = Main.elapsedTime - startTime % idurs.last;

var i = idurs.indexOfGreaterThan(v);

{a.value = v - idurs[i-1] / durs[i-1]}.defer;

updateRate.wait;

};

}).play;

)

Ideally I would like to be able to set one value, wait for cycle to complete and then use it for new cycle and so on.

Thanks for your help

m

then try this variant.


(
w = Window.new("A Slider");
a = Slider.new(w, Rect(40, 10, 300, 30));

w.front
);

(
Tdef(\sliderPlayer).set(\dur, nil);
Tdef(\sliderPlayer, {|envir|
  var durs, idurs, startTime, i, v;
  var updateRate = 0.05; // ‘resolution’
  inf.do{
    if(envir.dur.isArray and:{envir.dur != durs}, {
      {a.knobColor = Color.green}.defer; //optional
      durs = envir.dur;
      idurs = [0] ++ durs.integrate;
      startTime = Main.elapsedTime;
      while({
        (v = Main.elapsedTime - startTime) < idurs.last
      }, {
        i = idurs.indexOfGreaterThan(v) - 1;
        {a.value = v - idurs[i] / durs[i]}.defer;
        updateRate.wait;
      });
      {a.value = 1.0; a.knobColor = Color.red}.defer; //optional
    });
    updateRate.wait;
  };
}).play;
)

Tdef(\sliderPlayer).set(\dur, [10, 2, 3])
Tdef(\sliderPlayer).set(\dur, [3, 2, 1]) //if executed before the above finishes, this will queue

_f

16 juli 2021 kl. 11:27 skrev marcin pietruszewski via scsynth <noreply@m.scsynth.org>:

These are great @jamshark70 and @redFrik.

I have one more question. Why is ‘dur’ an array? Essentially, I would like to be able to set a new value using Tdef.set method. I have tried substituting durs with envir.dur, but it doesn’t work:

Tdef(\sliderPlayer).set(\dur, [10, 2, 3])

(

Tdef(\sliderPlayer, {|envir|

var durs = envir.dur;

var updateRate = 0.05; // ‘resolution’

var idurs = [0] ++ durs.integrate;

var startTime = Main.elapsedTime;

inf.do{

var v = Main.elapsedTime - startTime % idurs.last;

var i = idurs.indexOfGreaterThan(v);

{a.value = v - idurs[i-1] / durs[i-1]}.defer;

updateRate.wait;

};

}).play;

)

Ideally I would like to be able to set one value, wait for cycle to complete and then use it for new cycle and so on.

Thanks for your help

m

#|
fredrikolofsson.com musicalfieldsforever.com

1 Like

great, thanks @redFrik. It does the trick :slight_smile:

m