Exprange and delay question

Hey,
new supercollider user here.
My first question would be: Could someone explain what exactly exprange does?

Second question:
(
y = {
var sig, echo;
sig = SoundIn.ar([0,0]);
echo = DelayL.ar(sig, 5, SinOsc.kr(0.1).range(0.1,5));
sig = XFade2.ar(sig, echo, 1);
}.play;
)

I am using my microphone to experiment with this effect on the input. I clap and see what happens. The delay obviously changes but furthermore sometimes I get feedback, I clap once and get two claps back. Does anyone know why this happens?

Thanks so much.

Hi there and welcome.

exprange maps the output to the range using an exponential function -

compare these:

{SinOsc.ar(LFSaw.ar(0.2,1).range(200,2400))*0.1}.play;
{SinOsc.ar(LFSaw.ar(0.2,1).exprange(200,2400))*0.1}.play

the first one will seem to go slower and slower as it reaches to top - this is because the “pitch space” is exponential - we hear intervals as equal when they have the same ratio, not when they have the same difference in herz. So 200-400 is the same “distance” as 300-600. So for a smooth mapping into the pitch space we need to use exprange rather than range.

regarding your synthdef perhaps your speakers are turned on?

Bit of theory (professor hat on) –

One way to understand the relationship between linear and exponential mapping is that exponential mapping “promotes” the math operators used in linear mapping up to the next level:

  • + becomes *
  • - becomes /
  • * becomes **
  • / becomes ** and inverting the exponent (reciprocal)

The simplest linear map is y = intercept + (slope * x). If you have two points on the line, then slope = (y2 - y1) / (x2 - x1) and intercept = y1 - (slope * x1).

Substituting operators for exponential – though here, be a bit careful, because the X axis is considered linear! Only Y is exponential. So you still need to subtract x2 and x1, and raise to the power of the reciprocal.:

  • y = intercept * (base ** x)
  • base = (y2 / y1) ** (x2 - x1).reciprocal
  • intercept = y1 / (base ** x1)
~p1 = Point(1, 1);
~p2 = Point(3, 5);

~base = (~p2.y / ~p1.y) ** (~p2.x - ~p1.x).reciprocal;  // 2.23607...
~intercept = ~p1.y / (~base ** ~p1.x);  // 0.44721

f = { |x| ~intercept * (~base ** x) };

f.(1);  // 1.0
f.(3);  // 5.0  // so endpoints are correct
f.(2);  // 2.236  // X midpoint --> geometric mean

In my interactive multimedia class, I teach the students to normalize X first, so you’re thinking in terms of a 0 to 1 range.

Then the base is simply y2 / y1, and the intercept is y1 – easy.

y2 / y1 should be strictly positive = not touching or crossing 0. If it’s 0, then 0 ** 0 = NaN. Negative numbers to a fractional power are also NaN. So if you’re staying in the real domain, better keep that quotient positive.

hjh