Hey, Simon,
I wanted you to know I've still been enjoying working on this problem, but what I'm doing is very buggy so far
I was planning to add a smoother shape to the phase and frequency updates and choose a new triggering function for the synch_osc~ abstraction, but I realized this synchronizing approach wouldn't work well over this range. It only synchs up once per cycle, which at 30.87 Hz is 32 ms long. Even if it could synch up in 2 cycles (optimistic), latency would be 64 ms.
So, over the bass frequencies, we need some way to track things continuously I spent a lot of time with adaptive filtering, so that's where my thoughts went. My proposed scheme and patch are to have a low freq 2-pole bandpass filter L below the range and a high freq bandpass filter H above it, so that over the 2 octave range of each bass string, you get one signal +90 degrees out of phase and the other with -90. So, if you can find the ratio between their amplitudes, you can find the frequency.
I wrote up something about it here: https://www.mathcha.io/editor/Bw20zszkt6JsJ8FnDYkxdHnop5y6C9QLxL4FvdgLeE
and added code into staging area for sharing: https://github.com/czhenry/czbs
I can simplify that monster that's in pll~.pd right now. Maybe that will help. So far, it's been a week and I still don't understand what I'm seeing in the graphs for debugging.
On Tue, Mar 14, 2023 at 3:25 AM Simon Iten itensimon@gmail.com wrote:
thanks again for this, these explanations make it much clearer.
yes i also thought that one pll per string might be the ticket.
i noticed that the CPU toll on BELA is quite high with puredata and fexpr~
maybe (if i take this further) i have to try and implement this in C directly (or as a pd external) to make it more cpu friendly.
a sync osc would be a great external i think.
it is actually only 5 strings on my bass, so more of a pentaphonic pickup :) my bad.. the term hexaphonic is used among guitarists/bassists to describe a split-string pickup interchangeably.
my bass is standard 5 string tuning:
B: 30.87 HZ E: 41.2 A: 55 D: 73.4 G: 97.99
every string has a two octave range.
i did a version of my “gr300” ish guitar to sawtooth converter in puredata with building blocks and it works but your pll is less prone to octave jumping somehow even with the same input filtering applied…
On 13 Mar 2023, at 22:01, Charles Z Henry czhenry@gmail.com wrote:
On Mon, Mar 13, 2023 at 8:32 AM Charles Z Henry czhenry@gmail.com wrote:
On Mon, Mar 13, 2023 at 7:41 AM Simon Iten itensimon@gmail.com wrote:
i do not currently grasp the math in the fexpr~ objects, but will try to get into it…
It's a simple kind of dynamical system. It responds based on its inputs but also its internal state when receiving inputs. That's why [fexpr~] works well for this. You can add additional expressions and reference them from each other. There can be internal hidden state variables, etc....
Right now, I just left the outlets all hanging out of synch_osc~ for debugging. Connect up some plots (maybe longer block sizes would be better) to see what's in the
I'd say it's actually the *simplest* kind of dynamical system for phase locking (and a very artificial one). It's just a discretized, noise-less little phasor. e.g.
[fexpr~ modf($y1+1.0/44100)] is just a phasor that counts from 0 up to 1 over 44100 samples
So, if I want, I'll change that to use [samplerate~] and pass in 1/samplerate as a float $f2 [fexpr~ modf($y1+$f2)]
Now, add frequency in $y2, and multiply [fexpr~ modf($y1+$y2*$f2); 80] It's a 80 Hz phasor.
Next, I extend the phase that's stored in $y1 so it accumulates over time between impulses from $x1 in $y3 [fexpr~ modf($y1+$y2*$f2); 80; if($x1==1, $y1, $y3+$y2*$f2])
So, now I have phase and freq as functions of time, and I'll add in some updates to the terms and put them in $y4, $y5 so that the updates get added in when receiving an impulse from $x1 [fexpr~ modf($y1+$y2*$f2+$y4*$x1); 80 + $y5*$x1; if($x1==1, $y1, $y3+$y2*$f2]); ($y4) phase update function of $y3; ($y5) freq update function of $y3 ]
With some re-ordering of things and adding in parameters and scaling coefficients with $f inlets, that's basically what's in synch_osc~ right now
The choice of the phase and frequency update functions is what causes the synchronization.
For anyone who's interested in some light reading (LOL), I noticed Izhikevich has put "Dynamical Systems in Neuroscience" (~500 pgs) in pdf at https://www.izhikevich.org/publications/ Chapter 10 is on synchronization and it has a section on phase-locking. It took me over a year to read this, much of the math eludes me also
I want to make a version that's good for your hexaphonic pickup, you know that just works well as a sensor and has clean output. synch_osc~.pd has problems with low frequencies and I know where that comes from I think it has to be 6 PLL's, each tuned to the range of each string. Can you tell me what the range is on your instrument?
Best, Chuck