Let `.range` accept `curve`, deprecate `.curverange`

Hi!
I am learning about the .range and .curverange methods for UGens and wondering about why it aren’t the same; like .range having a curve parameter that defaults to 0.
What are your thoughts on this?
Cheers!

2 Likes

.range calls .linlin, curverange calls lincurve and exprange calls .linexp … (the names here are asymmetrical in a not so lovely way imho!)

…so absolutely - I do think .range(min:0, max:1, curve:\lin) would be nice -

I confess that 7 years in I somehow missed ‘rangecurve’!

…and why not fold \exp in while we’re there. so `.range(0.01, 100, \exp)

…would have to “soft deprecate” the explicit methods so as not to break anything…

…might even be nice to have all the Env.shapeNames available?

2 Likes

I just learned about curverange as well. range with a third argument is definitely easier to use/remember.

2 Likes

And what about the Line UGen? I think it would also make sense for it to accept a curve parameter (and thus make it more powerful, and soft deprecate XLine)…

It’s not possible to replicate the exact behavior of XLine (or linexp or the \exp curve shape) using curve. The formulas are very different, so I would not be in favor even of soft deprecation here.

EnvGen.kr(Env([start, end], [time], curve)) is Line with a curve factor, btw, and EnvGen is retriggerable – i.e. a more powerful line generator already exists.

hjh

but the curve arg in Env can be set to \exp

But wouldn’t you rather write Line.kr(start, end, time, 3) than EnvGen.kr( Env( [start, end], time, 3))?

Adding curve to Line could provide a nice onramp for students perhaps: use Line - try different curves - for multiple segments you have to define an Env and use EnvGen (but you already know about curves and the interface is consistent.) Line for a single segment and Env for multiple segments or retriggering is cleaner than the current division between Line XLine and EnvGen

On a side note in the days before IDE’s having related Classes whose names differ as to their first letter may have made sense - now though if you type Line you don’t see XLine as an option - a curve flag on the other hand would be discoverable (as would a Class called LineX but anyhow…)

The ship kinda sailed already, though, because there are already 3 arguments following “time” – putting curve 4th would break code (and annoy maintainers of other language clients).

FWIW I think neither Line nor XLine is particularly useful because of the lack of a trigger input. They’re convenient for one-off special cases, but I’m not sure that over-generalizing them is necessary.

LinePlusPlus {
	// removing mul/add because you already give the range explicitly
	*ar { |start = 0, end = 1, time = 1, curve = \lin, trig = 1, doneAction = 0|
		^EnvGen.ar(
			Env([start, start, end], [0, time], curve),
			trig,
			doneAction: doneAction
		)
	}
}

OK, done (except kr).

hjh

What about putting curve last to preserve compatibility? Usage: Line.kr(start, end, time, curve:3).

  • Pros:
    • Allows for trying things out faster
    • Would align with the proposed extension for range.
  • Cons: ?

I see your point, but don’t fully agree.

When considering changes such as this, it’s easy to underestimate the cost/risk, and overestimate the benefits. This got SC into trouble before; 8-9 years ago, I was running into a lot of weird bugs and edge cases because of an accumulation of things like “wouldn’t it be nice if…?” “Yeah, sure, go ahead” (combined with insufficient testing etc etc). This has led me to a default position of skepticism about changing existing interfaces.

I can be convinced. But I think “wouldn’t it be nice…” is not quite enough. What alternatives have been considered? Has it been thought through from a design perspective, rather than a patching-a-hole perspective? Does this connect to future needs (where a quick fix might get in the way)? In a professional software dev setting, these are questions that a program manager would answer. We don’t have program managers – so we have to learn to think like program managers.

For example, I did suggest an alternative – of course, the name LinePlusPlus is facetious and should be changed, but that approach:

  • gives you access to all Env segment shapes (where a curve UGen input would not let you use \lin, \exp, \sin etc);
  • and supports retriggering.

So… changing Line would have a benefit, but also a cost (mainly with non-SC clients, whose class definitions may not be in sync with the latest SC UGen binaries – I bet you didn’t think of this! I usually don’t either. Sciss had to fight long and hard to bring visibility to that aspect). An extended Line** pseudo-UGen has more benefit, and minor maintenance cost.

Just saying, consider more angles.

hjh

1 Like

I like the LinePlusPlus idea - perhaps Segment would be a good name…

…on the other hand it might be simpler to have an Env constructor instead: Env.line ?

@Asmatzaile here is the post by @Sciss explaining the issue with changing the number of arguments in UGens Keeping SuperCollider evolving with minimal impact on users work - #69 by Sciss. I do think that this is something that should be remedied if it is not too hard rather than we have to freeze all UGen interfaces forever.

1 Like

Thanks @jamshark70 for the thorough answer and @semiquaver for linking the UGens arguments post by sciss!

I think Segment is a good idea for the name. Concerning the names of the arguments; would they be initial, target, time and curve? I think that initial and target are more inline with the concept of segment than start and and end… what about you?

Btw @jamshark70 should the topic be split to have a separate discussion for .range / .curverange and Line / Segment?

I’d say, not yet…?

The problems are:

  • Access (by e.g. ZIN(i)) to input slots that may not have been specified in the SynthDef. Currently, e.g., SinOsc has two inputs, and we rely on the class library to write two inputs into the SynthDef. If someone added an input, then the SC classlib would write three inputs for it, but a different language client that hadn’t been updated would write two. New UGen code trying to run that synth would do ZIN(2) and… what then? Segfault? I wasn’t able to reproduce a segfault in that case in Linux, but I don’t know about other OSes.
  • If a new input were not initialized, would it get garbage data or substitute 0, or…? We’d need a policy on that.

Plugins/classlib: Support multiple RandIDs in one SynthDef by jamshark70 · Pull Request #6159 · supercollider/supercollider · GitHub suggests a workaround (for a boolean input – a float value would need a bit different conditional), though I’m not sure it’s ideal as a policy for the future.

hjh

Does this UGen adding arguments thing also affect .range? If not, I could try to make the PR for that, there seems to be an agreement in that it’s more discoverable and that leverages cognitive load.