Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiSliderView scope/trace new methods #5992

Open
fmiramar opened this issue Mar 10, 2023 · 8 comments
Open

MultiSliderView scope/trace new methods #5992

fmiramar opened this issue Mar 10, 2023 · 8 comments

Comments

@fmiramar
Copy link
Contributor

fmiramar commented Mar 10, 2023

Requested feature

Motivation

Provide SuperCollider with a easy way to scope/trace control signals, specially when building custom GUI.

Description of Proposed Feature

This new method for MultiSliderView was proposed here: https://fredrikolofsson.com/f0blog/extmultisliderview/

Plan for Implementation

//for SC 3.7 or newer
//copy and save as extMultiSliderView.sc in extensions folder
+MultiSliderView {
  add {|val|
    this.value= this.value.copyRange(1, this.value.size-1)++val;
  }
  addFirst {|val|
    this.value= val.asArray++this.value.copyRange(0, this.value.size-2);
  }
}

Example:

//--basic example
(
var n= 100;  //number of sliders
var w= Window("scroll test").front;
var a= MultiSliderView(w, Rect(10, 10, w.bounds.width-20, w.bounds.height-20));
a.elasticMode= 1;
a.value= Array.fill(n, {0});
Routine({
  inf.do{|i|
    0.05.wait;  //scroll speed
    a.addFirst(sin(i/10)*0.5+0.5);  //try .add instead of .addFirst
  };
}).play(AppClock);
CmdPeriod.doOnce({w.close});
)

//--example with sound input
(
s.waitForBoot{
  var n= 100;  //number of sliders
  var w= Window("scroll test audio").front;
  var a= MultiSliderView(w, Rect(0, 0, w.bounds.width, w.bounds.height));
  var b= {
    var src= SoundIn.ar;  //mic input
    SendReply.kr(Impulse.kr(60), '/trk', Amplitude.kr(src, 0.1, 0.1));
    DC.ar(0);  //silence
  }.play;
  a.elasticMode= 1;
  a.valueThumbSize= 8;
  a.indexThumbSize= 1;
  a.drawLines= true;
  a.value= Array.fill(n, {0});
  OSCFunc({|msg| {a.addFirst(msg[3])}.defer}, '/trk');
  CmdPeriod.doOnce({w.close});
};
)
@fmiramar
Copy link
Contributor Author

One small modification. For allowing a pattern to control a GUI (.setProperties) I need to change the code for:

+MultiSliderView {
  add {|val|
    this.value= this.value.copyRange(1, this.value.size-1)++val;
  }
  add_ {|val|
    this.value= this.value.copyRange(1, this.value.size-1)++val;
  }
  addFirst {|val|
    this.value= val.asArray++this.value.copyRange(0, this.value.size-2);
  }
  addFirst_ {|val|
    this.value= val.asArray++this.value.copyRange(0, this.value.size-2);
  }
}

@joshpar
Copy link
Member

joshpar commented Mar 11, 2023

Why not just use Scope at control rate? Here is a sample in code:

s.boot;
t = s.scope;
t.rate_(\control);
s.sendMsg(\c_set, 0, 0.5); // set control bus 0's value to 0.5 ... any control bus can be scoped

You can see here that you can set the rate (the Stethoscope GUI also has this option) and you can see a control value on that bus changes the scope output.

@fmiramar
Copy link
Contributor Author

Thanks Josh!

I see this as a more flexible GUI as it allows methods like .isFilled, .drawLines, .indexIsHorizontal, etc. Moreover it is a simple addition.

I know the back and forth language/server data sharing as proposed is a down point for it (and in favor of scope), but I think that scope's visual possibilities seems to be quite limited. And improving scope isn't in the horizon, right?

@telephon
Copy link
Member

telephon commented Mar 13, 2023

I don't think it is good to use the interface of Array for an object that holds an Array, but has many other functions. I understand that add is already really crowded anyways, but currently, addFirst is only for collections.

By shortening the code, you can write it more conveniently as:

a.value = a.value.drop(1) ++ val
a.value = val ++ a.value.drop(-1)

@scztt
Copy link
Contributor

scztt commented Mar 15, 2023

This would be much better implemented as a new offset property of MultiSliderView, allowing you to basically treat the existing array as a ring buffer. The proposed solution entails copying the entire array every time a new value is added, which really isn't feasible for scope functionality anyway. With an offset paramter, add functionality is relatively simple, something like:

add {
   |value|
   offset = (offset + 1).mod(array.size);
   array[offset] = value;
}

... and on the C++ side, the only requirement is that any reading of the array should be done based on index + offset % size rather than just the index.

Probably this should be named something like push and not add?

@redFrik
Copy link
Contributor

redFrik commented Mar 15, 2023

this would be a great addition. much better than copying yes.
in the standard RingBuffer class there's add, pop and overwrite.

@telephon
Copy link
Member

telephon commented Mar 16, 2023

Ah yes, so you can simply write:

//--basic example
(
var n= 100;  //number of sliders
var w= Window("scroll test").front;
var a= MultiSliderView(w, Rect(10, 10, w.bounds.width-20, w.bounds.height-20));
var r;
a.elasticMode= 1;
a.value = r = RingBuffer.fill(n, {0});
Routine({
	inf.do{|i|
		0.05.wait;  //scroll speed
		a.value = r.overwrite(sin(i/10)*0.5+0.5);
	};
}).play(AppClock);
CmdPeriod.doOnce({w.close});
)

(edit, of course this is not as efficient as having the ring buffer internally)

@telephon
Copy link
Member

It would be great if one could just wrap the ring buffer interface around the internal DoubleArray. This would do it: #3467 (https://github.com/telephon/rfcs/blob/master/rfcs/0002-abstract-object.md), but of course this is more work …

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants