Pmono and DC.ar to send Out stepped CV patterns

I would like to send “fast” stepped CV patterns to my sound card to be used in processes outside of Supercollider.
I came up with the following code (the 1/0 pattern is just for testing):

(
SynthDef(\CVout, {
	arg out = 0, value = 0;
	Out.ar(out, (DC.ar(1) * value));
}).add;

b = Pmono(
	\CVout,
	\value, Pseq([1, 0],inf),
	\dur, 1/50,
	).play;
)

When I look at the resulting signal in a recording the transitions are not stepped but have an for me unexpected short fade at the transitions.
Should I use another combination of Ugens/Patterns to achieve stepping?
Thanks
David

An additional problem is checking whether your audio card allows outputting DC signals (from the manufacturer’s perspective, not to blow up your speakers or something). I remember that was a problem depending of the hardware.

True, I have a dedicated eurorack module:

1 Like

This is because when modulating an audio-rate signal by a control-rate signal most server side ugens interpolate the control-rate signal. In this case, the ugen would be the *. It is also worth noting that if you go with control-rate signals, your sampling rate will effectively be ControlRate.ir.

I’m away from the computer right now, but I have a hunch that demand rate ugens won’t do this.

A while ago, I wrote a server plugin that does control to audio rate conversion without the interpolation (which is essentially what you need). I seem to have lost it, but I imagine there is a better maintained version somewhere around.

Thanks Jordan, do you mean that the Pmono “value” values appear as a control-rate signal when multiplied by the DC.ar ugen and would first have to be converted into audio rate to not cause interpolation?

SynthDef args are control rate by default.

These control rate values are being converted to audio rate – the interpolation happens at the moment of conversion.

It’s a bit of a bind: OSC messages to the server can set control buses and control rate synth inputs, but not audio rate. So you can’t avoid the conversion (and attendant interpolation) by these data channels.

Another potential workaround (and this is slightly naughty) would be to allocate a one-frame Buffer, and set the buffer’s value from the client side, and read the value using Index.ar. I believe this would avoid interpolation (but haven’t tested).

At 44.1 kHz, the ramp occupies about 1.45 ms. Is this audible? If you can see it but not hear it, maybe it isn’t a major problem?

hjh

Thank you for the clarification and the potential workaround.

Your last point is a valid one, this process came to my attention only when investigating another, not related, issue.
I can probably work around the ramp but it would still be nice to have the option to convert rates without interpolation when the situation so requires.

I will definitely try the naughty Buffer route if only to stumble around the less lit corners of the Supercollider.

I ended up with the following code, based on the one-frame Buffer workaround idea.
It almost delivers the perfect stepped signal.
As visible in the screenshot there is a short period where the signal stays stuck close to the intended value before hitting it.
Is there something in my code that could cause this?

(
SynthDef(\dcBuffer, {
	arg out = 0, value = 0;
	var buff = LocalBuf.new(2,1);
	BufWr.ar(DC.ar(1) * value, buff, DC.ar(0));
	Out.ar(out, Index.ar(buff, 0));
}).add;
)

(
b = Pmono(
	\dcBuffer,
	\value, Pseq([0.0, 0.25, 0.5, 0.75, 1.0],inf),
	\dur, 1/50,
).play;
)

you could also use server side sequencing with Demand Ugens:

(
{
	var trig = Impulse.ar(50);
	var sequence = Demand.ar(trig, 0, Dseq([0.0, 0.25, 0.5, 0.75, 1.0], inf));
	sequence;
}.plot(0.3);
)

grafik

1 Like

Thanks for the tip.
I am though planning on using parts of the pattern system that are not available as part of the Demand Ugen system.

1 Like

fair enough! what exactly are you planning to use?

maybe a bit offtopic, but i think this is a cool technique, you could create a stepped sequence by using a Phasor and create different impulse trains by adjusting div or curve and getting a stepped sequence by using .floor which is totally in sync with the main ramp.



(
{
	var rate, div, phase, warpedPhase, trigger, wrappedPhase, warpedTriggers, stepped, quantized;

	rate = 100;
	div = 8;
	phase = Phasor.ar(DC.ar(0), rate * SampleDur.ir);

	warpedPhase = phase.lincurve(0, 1, 0, 1, 4);
	trigger = HPZ1.ar(warpedPhase) < 0 + Impulse.ar(0);

	wrappedPhase = (warpedPhase * div).wrap(0, 1);
	warpedTriggers = HPZ1.ar(wrappedPhase) < 0 + Impulse.ar(0);

	stepped = (warpedPhase * div).floor;
	quantized = stepped / div;

	[warpedPhase, trigger, wrappedPhase, warpedTriggers, quantized];
}.plot(0.021);
)

Note, though, that a perfect digital stairstep will feature Gibbs effect wiggling at the corners, after conversion to analog. A brief ramp would reduce that and be more accurate as a control signal (though the default block size may be a bit too long for the ramp).

An analog control voltage, I’m pretty sure, won’t instantaneously jump, but would also have a short ramp of its own (much shorter than kr though).

hjh

@dietcv: Wow, that is a great technique, copied and filed!

@jamshark70: I am actually staying in the digital domain and communicating with Max over Loopback. I am sending along a pulse per Pmono event to trigger a signal to number conversion in Max for further use of the SC Pattern data there.
That’s why its nice to have a clean step. I could also just delay the pulse so the “data signal” is sampled once plateaued, but this way I’m learning more about Supercolliders innards.

Ah ok, that’s fair then. If I were using the data in the control domain in Max, I’d probably just use OSC though.

hjh