How could I make a Phase-Locked Loop?
hey, there is an already existing thread on PLL Phase locked loops
and there is also a C++ Ugen from @nathan GitHub - nhthn/pll
One thing you should be aware of when dealing with single sample feedback which is needed for a Phased-Locked-Loop (PLL) is, that in all creative coding environments including (SC, Max, PD) samples are handled in chunks called blocks or vectors for efficiency reasons. So in these environments it can be challenging or simply not possible to implement single-sample feedback. But you could try out LocalIn / LocalOut which sets up a feedback loop according to the blocksize, which is per default 64 samples.
In Pd this is actually very easy: just put a [block~ 1]
in your subpatch. Same goes for upsampling and downsampling. SC is really the outlier in that it can’t do any of this. (At least single sample feedback can be faked with lots of trickery; I think @dkmayer has some helpers in his miscellaneous Quark. Up/downsampling, however, is outright impossible outside of C++ code).
Maybe a good opportunity to consider Faust, also. Here’s @madskjeldgaard’s post on it: [Getting started with Faust for SuperCollider | Mads Kjeldgaard]
At risk of drifting off topic (if it does, can split the thread later), but perhaps one way to handle this is to add a /s_new_block
command with an extra parameter for this synth’s blocksize.
hjh
That could be nice! Of course, this could also include an upsampling/downsampling factor.
Now, running a Synth at a different block size and/or samplerate in isolation is trivial. The actual issue is how to interface with the rest of the world, in particular reading from resp. writing to busses. The internal I/O UGens could be updated to support the necessary reblocking and resampling, but existing external UGens that use busses would break when put in a Synth with a different block size and/or samplerate from the nominal one. We would need some helper functions for reblocking and resampling so extensions don’t need to roll their own.
I assume we can split the topic
I am sorry for the late reply. Yes this example from the old post is pretty fun but I think I am not understanding LocalIn, LocalOut.
(
Ndef(\pll_snippet, {
var oscRef, oscExt, output, pc, freqfbk=100;
//freqfbk = LocalIn.ar(1,0) * LFDNoise0.ar(3).range(20, 600);
freqfbk = LocalIn.ar(1, (\freq1.ar(100)).lag(\lag.ar(0.15)));
oscRef = LFPulse.ar(freqfbk, 0, 0.5);
oscExt = LFPulse.ar(\freq2.ar(130).lag(\lag.ar(0.15)), 0, 0.5);
pc = (oscRef+oscExt)%2;
pc = pc.floor.lag(\lag.ar(0.15));
//pc = OnePole.ar(pc,0.99999);
pc = pc.linexp(0,1,1,10000);
//pc.poll;
LocalOut.ar(pc);
DC.ar(0)+pc;
oscRef * Clip.ar(\amp.ar(1),0,1);
}
A one UGen would be a really elegant solution something really simple like this logic operation. Or maybe something more ready to use in A-196 PLL style. This can already sound really nice and close to analog sounding sometimes. Thank you!
just create some feedback between bottom tree and top tree variables. don’t forget to drop feedback using feedback attenuators