hi list,
does somebody have a patched version of a PLL (phase locked loop) in PD? or building blocks of it...
cheers
Synchronizing oscillators is a good topic and there should be some better options with digital alone than with simulations of the analog circuits.
Synchronization in natural systems involves a phase-resetting oscillator. The key behavior is the oscillator responds to input events, by adjusting its phase by an amount that depends on the current state of the oscillator. This is enough to produce synchronization within a small range of nearby frequencies, by itself, but adjusting frequency is also possible.
What did you have in mind? Any specific behavior it needs to have?
On Tue, Mar 7, 2023, 4:01 PM Simon Iten itensimon@gmail.com wrote:
hi list,
does somebody have a patched version of a PLL (phase locked loop) in PD? or building blocks of it...
cheers
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
thanks for your answer.
as a first project i wanted to explore the possibilites in treating the output of a hexaphonic bass pickup with puredata. a pll oscillator with the bass string as input (filtered to remove unwanted harmonics) sprang to mind.
other topics i am currently investigating:
-pitch to voltage from the different strings at low latency (difficult with bass frequencies, i took a gr300 guitar synth approach) -proper attack detection -adaptive filtering of the individual strings overtones to remove octave jumps from the pitch to voltage block
On Fri, 10 Mar 2023, 00:48 Charles Z Henry, czhenry@gmail.com wrote:
Synchronizing oscillators is a good topic and there should be some better options with digital alone than with simulations of the analog circuits.
Synchronization in natural systems involves a phase-resetting oscillator. The key behavior is the oscillator responds to input events, by adjusting its phase by an amount that depends on the current state of the oscillator. This is enough to produce synchronization within a small range of nearby frequencies, by itself, but adjusting frequency is also possible.
What did you have in mind? Any specific behavior it needs to have?
On Tue, Mar 7, 2023, 4:01 PM Simon Iten itensimon@gmail.com wrote:
hi list,
does somebody have a patched version of a PLL (phase locked loop) in PD? or building blocks of it...
cheers
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
Hi Simon,
I took up the approach that I was thinking of and got something working today. It's a good first draft So, what I've attached here is a phase-resetting, frequency adaptive oscillator with 0-1 phasor output. I'm calling it "synch_osc~" for synchronizing oscillator
It has a thresholded rising edge detector on the input with (hardcoded) 20 samples of de-bounce to avoid noisy triggers. Then, once an input spike is received, the oscillator triggers an update to its phase and frequency
The update amounts vary based on what the phase is currently.
If the phase is <0.5, then we're receiving pulses at more than double the current freq. In that case, don't update phase, update frequency. If the phase is >0.5 and <2.0, then we're within one octave. The maximum phase update amount is (1-phase) The maximum frequency update amount is ((1-phase)/phase)*freq Then, if the phase is >2.0, then the input freq is more than 1 octave lower. In this case, don't update phase, update frequency.
If it receives no input spikes, then the frequency remains constant and it just outputs a nice steady phasor waveform
It needs to have some parameters set. For some range of parameters, it may not work at some frequencies The parameters are: threshold initial frequency phase update coefficient (0-1) --values close to 1 are strongly synchronizing freq update coefficient (0-1) --affects convergence speed when close to 0, but it's acceptable at low values also --creates jitter and excess noise when close to 1
Works like a PLL pretty much! I might have some more fun with it this next week, and make a sub-harmonic synchronizing oscillator version
Best, Chuck
On Fri, Mar 10, 2023 at 1:38 AM Simon Iten itensimon@gmail.com wrote:
thanks for your answer.
as a first project i wanted to explore the possibilites in treating the output of a hexaphonic bass pickup with puredata. a pll oscillator with the bass string as input (filtered to remove unwanted harmonics) sprang to mind.
other topics i am currently investigating:
-pitch to voltage from the different strings at low latency (difficult with bass frequencies, i took a gr300 guitar synth approach) -proper attack detection -adaptive filtering of the individual strings overtones to remove octave jumps from the pitch to voltage block
On Fri, 10 Mar 2023, 00:48 Charles Z Henry, czhenry@gmail.com wrote:
Synchronizing oscillators is a good topic and there should be some better options with digital alone than with simulations of the analog circuits.
Synchronization in natural systems involves a phase-resetting oscillator. The key behavior is the oscillator responds to input events, by adjusting its phase by an amount that depends on the current state of the oscillator. This is enough to produce synchronization within a small range of nearby frequencies, by itself, but adjusting frequency is also possible.
What did you have in mind? Any specific behavior it needs to have?
On Tue, Mar 7, 2023, 4:01 PM Simon Iten itensimon@gmail.com wrote:
hi list,
does somebody have a patched version of a PLL (phase locked loop) in PD? or building blocks of it...
cheers
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
On Fri, Mar 10, 2023 at 2:21 PM Charles Z Henry czhenry@gmail.com wrote:
It has a thresholded rising edge detector on the input with (hardcoded) 20 samples of de-bounce to avoid noisy triggers. Then, once an input spike is received, the oscillator triggers an update to its phase and frequency
This part had a major bug. LOL was working in spite of the de-bounce, not because of it
The buggy code: [fexpr~ ($x1[-1]<$f2)&($x1[0]>$f2)] | [fexpr~ if($y2>1, 0, $x1); if($y1==1, 0, $y1+1/20)]
makes no sense...
Here's the syntax that works:
[fexpr~ ($x1[-1]<$f2)&($x1[0]>$f2)] | [fexpr~ if($y2<1, 0, $x1); if($y1>0, 0, $y2+1.0/20)]
and probably some other variations too.
If the phase is <0.5, then we're receiving pulses at more than double the current freq. In that case, don't update phase, update frequency. If the phase is >0.5 and <2.0, then we're within one octave. The maximum phase update amount is (1-phase) The maximum frequency update amount is ((1-phase)/phase)*freq Then, if the phase is >2.0, then the input freq is more than 1 octave lower. In this case, don't update phase, update frequency.
The cool part is this can all be implemented at a basic level in just a few lines of code with fexpr~! (I started in on it just to write this part, figuring it takes me 20 min)
fexpr~ modf($y1+$f5*$y2+$f3*$x1*$y3); if($y6!=$y6[-2], $y6, $y2+$f4*$x1*$y4); if($y5<0.5, 0, if($y5<2, 1-$y5, 0)); if($y5<0.5, $y2, if($y5<2, (1-$y5)/$y5*$y2, -1.414*$y2/sqrt($y5))); if($x1[-1]==1, $y1+$f5*$y2, $y5+$f5*$y2); if($y6!=$f2, $f2, $y6)
But just so difficult to explain, I created a mathcha document for it, if you expected to be able to start modifying it and tuning it up for use.
https://www.mathcha.io/editor/OBgdJIo6UzgsgZHp8K1VSjK188YTN4eVJyHrz1q8G
work in progress
hi charles,
thanks for doing this!!
i copied your first patch (with the changes you suggested) to my bela and hooked up my hexaphonic bass pickup to it (well one string to start), and it works pretty well!
i will play with the threshold settings etc. as it is now, the pitch seems to go down a half step every time i stop a note on the bass so that the pll osc is a semitone off. not sure this will be a problem in a “proper” implementation, since i mostly want the pll to follow my playing (so i will just mute the osc when i don’t play)
i do not currently grasp the math in the fexpr~ objects, but will try to get into it…
is it hard to implement a detune factor? so that we could adjust the pll transposition (octave up/down, fifth etc.)
thanks so much already!!
On 11 Mar 2023, at 19:49, Charles Z Henry czhenry@gmail.com wrote:
If the phase is <0.5, then we're receiving pulses at more than double the current freq. In that case, don't update phase, update frequency. If the phase is >0.5 and <2.0, then we're within one octave. The maximum phase update amount is (1-phase) The maximum frequency update amount is ((1-phase)/phase)*freq Then, if the phase is >2.0, then the input freq is more than 1 octave lower. In this case, don't update phase, update frequency.
The cool part is this can all be implemented at a basic level in just a few lines of code with fexpr~! (I started in on it just to write this part, figuring it takes me 20 min)
fexpr~ modf($y1+$f5*$y2+$f3*$x1*$y3); if($y6!=$y6[-2], $y6, $y2+$f4*$x1*$y4); if($y5<0.5, 0, if($y5<2, 1-$y5, 0)); if($y5<0.5, $y2, if($y5<2, (1-$y5)/$y5*$y2, -1.414*$y2/sqrt($y5))); if($x1[-1]==1, $y1+$f5*$y2, $y5+$f5*$y2); if($y6!=$f2, $f2, $y6)
But just so difficult to explain, I created a mathcha document for it, if you expected to be able to start modifying it and tuning it up for use.
https://www.mathcha.io/editor/OBgdJIo6UzgsgZHp8K1VSjK188YTN4eVJyHrz1q8G
work in progress
On Mon, Mar 13, 2023 at 7:41 AM Simon Iten itensimon@gmail.com wrote:
hi charles,
thanks for doing this!!
You're welcome. I guess what I really meant to say is: I have a few days off of work and what I wanted to do was play with synchronizing oscillators. I just didn't know why yet.
i copied your first patch (with the changes you suggested) to my bela and hooked up my hexaphonic bass pickup to it (well one string to start), and it works pretty well!
i will play with the threshold settings etc. as it is now, the pitch seems to go down a half step every time i stop a note on the bass so that the pll osc is a semitone off. not sure this will be a problem in a “proper” implementation, since i mostly want the pll to follow my playing (so i will just mute the osc when i don’t play)
The threshold should be adjusted, just above the noise level, so you don't get unrelated triggers.
It's not very good at the low frequencies yet, but I suspect it comes from the choice of rising edge detector. Also, the phase updates are abrupt, and they could be smoothed out.
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....
is it hard to implement a detune factor? so that we could adjust the pll transposition (octave up/down, fifth etc.)
thanks so much already!!
I know it's pretty buggy right now, but I wanted to get something out for you to try quickly. I was pleased with how strongly synchronizing it is. Drag the initial frequency to 0.3 Hz or up to 20000Hz and it still goes right back to the input frequency.
I think there will be a few different versions of this oscillator, with different ranges of frequencies and effects, as I play with it this week.
On 11 Mar 2023, at 19:49, Charles Z Henry czhenry@gmail.com wrote:
If the phase is <0.5, then we're receiving pulses at more than double the current freq. In that case, don't update phase, update frequency. If the phase is >0.5 and <2.0, then we're within one octave. The maximum phase update amount is (1-phase) The maximum frequency update amount is ((1-phase)/phase)*freq Then, if the phase is >2.0, then the input freq is more than 1 octave lower. In this case, don't update phase, update frequency.
The cool part is this can all be implemented at a basic level in just a few lines of code with fexpr~! (I started in on it just to write this part, figuring it takes me 20 min)
fexpr~ modf($y1+$f5*$y2+$f3*$x1*$y3); if($y6!=$y6[-2], $y6, $y2+$f4*$x1*$y4); if($y5<0.5, 0, if($y5<2, 1-$y5, 0)); if($y5<0.5, $y2, if($y5<2, (1-$y5)/$y5*$y2, -1.414*$y2/sqrt($y5))); if($x1[-1]==1, $y1+$f5*$y2, $y5+$f5*$y2); if($y6!=$f2, $f2, $y6)
But just so difficult to explain, I created a mathcha document for it, if you expected to be able to start modifying it and tuning it up for use.
https://www.mathcha.io/editor/OBgdJIo6UzgsgZHp8K1VSjK188YTN4eVJyHrz1q8G
work in progress
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
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
i noticed that the CPU toll on BELA is quite high with puredata and fexpr~
If you are not going full-polyphonic, you could try to disable unused [fexpr~] objects based on the envelope's amplitude, either via [switch~] or using [start( and [stop( messages to [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.
I concur this would be the best way to optimise CPU performance and it shouldn't be too hard. Let me know if you need help.
Best, Giulio
Simon Iten wrote on 14/03/2023 03:25:
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
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
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
Hi Charles, SImon, list,
On 27/03/2023 04:51, Charles Z Henry wrote:
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.
The [hilbert~] abstraction that comes with Pd has two all-pass biquad filters that give outputs approximately 90 degrees out of phase from each other, so calling its two outputs x and y, phase can be approximated with atan2(y, x) and amplitude with sqrt(x^2 + y^2). I don't know whether it works as-is for bass frequencies though - maybe needs a [deephilbert~] version?
On Mon, Mar 27, 2023 at 3:33 AM Claude Heiland-Allen claude@mathr.co.uk wrote:
The [hilbert~] abstraction that comes with Pd has two all-pass biquad filters that give outputs approximately 90 degrees out of phase from each other, so calling its two outputs x and y, phase can be approximated with atan2(y, x) and amplitude with sqrt(x^2 + y^2). I don't know whether it works as-is for bass frequencies though - maybe needs a [deephilbert~] version?
I'm starting in on looking at that with ELSE's [biplot] abstraction. So far, just copying/pasting things, like coeff from hilbert~ biquads (2 in series for the left outlet, 2 in series for the right). I'll need to modify for adding/subtracting the phase results from each other and to reduce the range to see the lowest freq in detail https://github.com/czhenry/czbs/blob/master/analyze_hilbert.pd
hi charles,
interesting approach!
unfortunately i have a very busy few weeks with some (musical) deadlines, so i did not do much work on this.
i tried my version of the gr300 guitar synth patch again for bass and it works quite well but needs a lot of filtering (maybe also adaptive filtering) to avoid octave jumps.
this are the brains of one channel… it is a rather simple approach but with adequate filtering it should work well for a constrained range. it sure worked well on the gr300 in the 80ies, but that was in analog territory. my implementation works exceptionally well for vocals with a little filtering on the input, and there is very little lag.
On 27 Mar 2023, at 16:43, Charles Z Henry czhenry@gmail.com wrote:
On Mon, Mar 27, 2023 at 3:33 AM Claude Heiland-Allen claude@mathr.co.uk wrote:
The [hilbert~] abstraction that comes with Pd has two all-pass biquad filters that give outputs approximately 90 degrees out of phase from each other, so calling its two outputs x and y, phase can be approximated with atan2(y, x) and amplitude with sqrt(x^2 + y^2). I don't know whether it works as-is for bass frequencies though - maybe needs a [deephilbert~] version?
I'm starting in on looking at that with ELSE's [biplot] abstraction. So far, just copying/pasting things, like coeff from hilbert~ biquads (2 in series for the left outlet, 2 in series for the right). I'll need to modify for adding/subtracting the phase results from each other and to reduce the range to see the lowest freq in detail https://github.com/czhenry/czbs/blob/master/analyze_hilbert.pd
Pd-list@lists.iem.at mailing list UNSUBSCRIBE and account-management -> https://lists.puredata.info/listinfo/pd-list
On 27 Mar 2023, at 05:51, Charles Z Henry czhenry@gmail.com wrote:
I spent a lot of time with adaptive filtering, so that's where my thoughts went.
maybe you could shed some lights on this and i could incorporate this into my approach to filter out overtones from the strings and prevent octave jumps.
On Mon, Mar 27, 2023 at 10:43 AM Simon Iten itensimon@gmail.com wrote:
On 27 Mar 2023, at 05:51, Charles Z Henry czhenry@gmail.com wrote:
I spent a lot of time with adaptive filtering, so that's where my thoughts went.
maybe you could shed some lights on this and i could incorporate this into my approach to filter out overtones from the strings and prevent octave jumps.
As a framework, it means expressing some kind of goal as a function and then seeking the minimum of the error achieving it. So, that's where I start: I write down my goal verbally and the scope of what it applies to. Then, you have to come up with a formula involving some measurable things or signals for how well the goal is accomplished and over what timeframe (weighting the inner product).
Adaptive filtering has a lot of types that come from all the choices you can make, but the framework looks like this:
the gradient vector dE/dw and update with: w(n)=w(n) - c(dE/dw(n)) Sometimes with complex cost functions, gradient descent is the best you can do. Make c a constant that is small enough, and this works sort of 3. Use the 2nd derivative of E (d^2E/dw^2) and use its inverse operator on the vector dE/dw to make a Gauss-Newton method w(n)=w(n-1) - c (d^2E/dw^2)^-1 (dE/dw) with c between 0 and 1. This is much better. For error functions with a quadratic form and c=1, this method reaches the optimum value in a single update. Take care to regularize (d^2E/dw^2) to avoid dividing by 0. Other than the behavior near zeros of (d^2E/dw^2), this method has strong convergence properties. If the filter has N weights, then (d^2E/dw^2) is an NxN matrix. With complex cost functions, this is very hard to derive and to invert.
The choice of filter type goes right along with forming the cost function. Just try some things out and see where it leads