• ### samphold-ing a previous signal value

I would like to create a Pd equivalent of SuperCollider's LFDNoise1 LFO (random line segments).

It's basically like this. In Pd, I can see how to do almost all of it, except for sample/holding the previous random value.

``````(
a = {
// pd: phasor~
var phasor = LFSaw.ar(1) * 0.5 + 0.5,  // 0-1
// pd: [rzero~ 1] --> [<~ 0]
trig = HPZ1.ar(phasor) < 0,  // 1 when phasor drops
// pd: [samphold~]
nextEndpoint = Latch.ar(WhiteNoise.ar, trig),
// pd: I don't know how to do this
prevEndpoint = Latch.ar(Delay1.ar(nextEndpoint), trig),
// pd: easy math
line = (nextEndpoint - prevEndpoint) * phasor + prevEndpoint;

// simple test signal: map bipolar LFO exponentially to freq
SinOsc.ar(400 * (2 ** line), 0, 0.1).dup
}.play;
)

a.free;
``````

I made one failed attempt using [phasor~] --> [rzero~ 1] --> [*~ -1] --> [threshold~] --> [random], but if the phasor jumps to zero in the middle of a control block, then the random calculation is out of sync and the output glitches slightly. So I need to keep all of it in the signal domain (no control objects).

Thanks,
hjh

• Posts 14 | Views 3142
• @ddw_music Would [samphold~] with a [reset( message banged in from [sig~], without the [phasor~] do that....... not the previous sample, but the next...?
David.

• @whale-av hm, I'm not sure.

The problem I ran into is that phasor~ usually jumps to 0 in the middle of a block. The random number needs to flip at that exact moment -- if the line formula is `(b-a) * phase + a`, then the previous random number needs to go into `a` and a new random number into `b`, and if this happens synchronously with the phasor reset, then the output was almost exactly the previous endpoint, and becomes exactly `a`, so it runs continuously.

If there are any control objects involved, then they don't fire until the next audio block boundary, which is too late. So I think I can't use any messages at all for this. (I had tried that at first but there are very obvious discontinuities in the output.)

Sometime later I may try a short delay line. SC happens to have the convenience of a dedicated one-sample delay. Or... hm... I just realized I could use a 0 ms delay but arranged as feedback.

(Should also say, I was using phasor~ to allow modulating the LFO frequency. I could do it all in the control domain feeding a [line~] but it won't update the LFO rate until the next endpoint.)

hjh

• A few solutions, the last one thanks to whale-av.

Random ramp / random voltage / smooth a ramp with lop~

• @Il-pleut thanks, but I was avoiding line~ on purpose. Say the LFO rate drops to 0.001... you'll be waiting a thousand seconds before it can speed up again. (SC has that problem in LFNoise1; LFDNoise1 was added to address that. It's the latter behavior I'm trying to emulate.)

I might try the lop~ approach later. I think I'd tried something like it before, for a different technique, but it didn't work out so well because noise~ hasn't enough low frequency energy.

hjh

• @ddw_music P.S. There were a few [random~] externals...... [random1~] [random_fl] [random_icg~]...... in the cxc library...... which might be available for your distro through Deken.
David.

• I'm not quite following this discussion, so I'm going to throw out a naive idea in the hope that someone will point out what I'm not understanding. Apologies in advance.

• I reread @ddw_music's 2nd post and thought maybe this is relevant? (I'm sure I'm still not understanding something...)

• @jameslo I think the fexpr~ trick is the missing piece -- the right answer is probably a combination of your two approaches. I'll probably delay the output of one samphold~ and feed that into a second samphold~ (both driven by the phasor~), which would be a literal translation of the SC code.

Thanks! I hadn't thought of fexpr~ as a way to get a one-sample delay.

hjh

• OK, well I'm still in the dark, but if you manage to get something working in PD please post it back so I can see what we've been discussing!

• Here's the final version.

At first, it didn't seem to work -- the "old" and "new" [samphold~] objects were outputting the same value. Eventually I figured out that it was because I was trying to clean up the patch's appearance by passing the [phasor~] through a [send~] / [receive~] pair (going into the "old" [samphold~]). Apparently (which I didn't realize at first) this introduces a one-block delay -- so I was reading the "old" value too late.

Using direct signal connections for all of them, this does exactly what I want.

This is part of a multimode LFO abstraction that I'm giving to students. I'll go ahead and attach that -- -1 .. +1 range, LFO shapes are sine, triangle, sawtooth, pulse, random sample-hold and random with linear interpolation.

Thanks for the tips! Learned something.
hjh

• Oh I see! And I've learned something too: that second patch I posted was an adaptation of a larger audio-rate S&H shift register, and I just assumed I had to oversample in order for the shift operation to look like it occurred in one clock. But your way does it in one clock without the fuss.

I've always wanted to learn SC, maybe now's the time to start.

• Here's a version without [fexpr~] which is a rather expensive object:
(The [sig~] and scope are there only for concept proofing)

• @alexandros Ah OK, I had overlooked [rzero_rev~] -- I suppose even without that, I might have thought of [rzero~ -1] and subtracting the input -- (signal - (-1 * signal[-1])) - signal -- but I didn't. [rzero_rev~] is a good trick.

hjh

Posts 14 | Views 3142
Internal error.

Oops! Looks like something went wrong!