mjsyts
April 2, 2025, 4:28pm
1
Is there a quark or something I can use to make a Catmull-Rom spline interpolation between a number of points to store in a Buffer
?
I want to index into a wavetable but warp the rate that the phase is reading if that makes sense so it’s more like this instead of a straight line:
The points are (0,0), (0.1, 0.25), (0.5, 0.5), (0.9, 0.75), (1, 1).
That part’s fine, but I want a smooth curve instead of discrete linear segments. I thought of using a Catmull Rom Spline interpolation on the phase index and plugging that into my transfer function with collect
to get an array for a Wavetable
. Then I could put it in a Buffer
to use with Osc
to index into the ugen reading my wavetable buffer.
I think this would be a lot easier if I could understand how exactly the curve argument works in Env
, but the documentation is pretty vague and I’m not sure that’ll get me where I’m trying to go.
On the other hand, a Catmull Rom interpolation is definitely doable, but I really don’t care to work on that at the moment. I’d rather spend the time making music first and worry about the signal processing later.
TXMod
April 2, 2025, 7:01pm
2
Using a ‘sin’ curve with Env gives a smoother curve. Maybe this works for you?:
Env([0, 0.25, 0.5, 0.75, 1], [0.1, 0.4, 0.4, 0.1], 'sin').plot;
Best,
Paul
mjsyts
April 2, 2025, 7:18pm
3
TXMod:
Env([0, 0.25, 0.5, 0.75, 1], [0.1, 0.4, 0.4, 0.1], 'sin').plot;
I’m trying to get something more like this:
TXMod
April 2, 2025, 7:27pm
4
Maybe one of these?
Env([0, 0.5, 1], [0.5, 0.5], [-6, 6]).plot;
Env([0, 0.5, 1], [0.5, 0.5], [-5, 5]).plot;
dietcv
April 2, 2025, 7:35pm
5
That is phase shaping / phase increment distortion. You find some more information in this thread i have written: On the path of the halfAHeart window - phase shaping vs. phase increment distortion
Thats an easingOutIn function also called a seat.
You can check out my collection of unit shapers here:
hey, here is a collection of unit shapers (they are still work in progress, i will add some other functions in the upcoming days). All the params used for these functions are nicely normalized between 0 and 1 and can be modulated at audio rate. To use them you need a ramp signal between 0 and 1 to drive them and the ProtoDef class by @elgiano GitHub - elgiano/ProtoDef: Prototyping classes for SuperCollider .
Maybe there could be a better way to organize those in a dictionary, but im mainly using…
You can get this one from the collection (try out different easing cores), with height 0.5 you get the desired shape (all the params are normalized between 0 and 1 and you can modulate them at Audio rate)
(
var easeIn = { |x|
x * x * x;
};
var easeOutIn = { |x, height = 0.5|
Select.ar(x > 0.5, [
height - (height * easeIn.(1 - (x * 2))),
height + ((1 - height) * easeIn.((x - 0.5) * 2))
]);
};
var cubicSeatReversedToLinear = { |x, shape, height = 0.5|
var mix = shape * 2;
var easeOut = 1 - easeOutIn.(1 - x, height);
easeOut * (1 - mix) + (x * mix);
};
var linearToCubicSeat = { |x, shape, height = 0.5|
var mix = (shape - 0.5) * 2;
var easeIn = easeOutIn.(x, height);
x * (1 - mix) + (easeIn * mix);
};
var cubicSeatToLinearMorph = { |x, shape, height = 0.5|
Select.ar(shape > 0.5, [
cubicSeatReversedToLinear.(x, shape, height),
linearToCubicSeat.(x, shape, height)
]);
};
{
var phase = Phasor.ar(DC.ar(0), 50 * SampleDur.ir);
var sigA = cubicSeatToLinearMorph.(phase, \shapeA.kr(0), \height.kr(0.875));
var sigB = cubicSeatToLinearMorph.(phase, \shapeB.kr(0.5), \height.kr(0.875));
var sigC = cubicSeatToLinearMorph.(phase, \shapeC.kr(1), \height.kr(0.875));
[sigA, sigB, sigC];
}.plot(0.02).superpose_(true).plotColor_([Color.red, Color.blue, Color.magenta]);
)
You could also check out the cubic bezier through two given points here:
1 Like
dietcv
April 2, 2025, 7:51pm
6
if you use the kink transfer function to warp your linear phase, you even get one more control param:
(
var kink = { |phase, skew|
var warpedPhase = Select.ar(BinaryOpUGen('>', phase, skew), [
0.5 * (phase / skew),
0.5 * (1 + ((phase - skew) / (1 - skew)))
]);
Select.ar(BinaryOpUGen('==', skew, 0), [warpedPhase, 0.5 * (1 + phase)]);
};
var easeIn = { |x|
x * x * x * x * x;
};
var easeOutIn = { |x, height = 0.5|
Select.ar(x > 0.5, [
height - (height * easeIn.(1 - (x * 2))),
height + ((1 - height) * easeIn.((x - 0.5) * 2))
]);
};
var quinticSeatReversedToLinear = { |x, shape, height = 0.5|
var mix = shape * 2;
var easeOut = 1 - easeOutIn.(1 - x, height);
easeOut * (1 - mix) + (x * mix);
};
var linearToQuinticSeat = { |x, shape, height = 0.5|
var mix = (shape - 0.5) * 2;
var easeIn = easeOutIn.(x, height);
x * (1 - mix) + (easeIn * mix);
};
var quinticSeatToLinearMorph = { |x, shape, height = 0.5|
Select.ar(shape > 0.5, [
quinticSeatReversedToLinear.(x, shape, height),
linearToQuinticSeat.(x, shape, height)
]);
};
{
var phase = Phasor.ar(DC.ar(0), 50 * SampleDur.ir);
var warpedPhase = kink.(phase, \skew.kr(0.5));
quinticSeatToLinearMorph.(warpedPhase, \shape.kr(0), \height.kr(0.5));
}.plot(0.02);
)
mjsyts
April 3, 2025, 5:18pm
7
Thanks my friend, this is perfect! For some reason I just couldn’t remember the term “phase distortion” but yeah that’s absolutely it.
1 Like