Forums

Guidance on Low Frequency Oscilator (LFO) implementation

Started by Jaime Andres Aranguren Cardona February 28, 2004
Hi, guys.

I need your advice on the implementation of an LFO, with variable Fo
(oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling
at Fs = 44.1kHz.

To be done with an ADSP-21065L in floating point (EzKit).

Due to these ranges, a LUT (look up table) doesn't seem to be a viable
solution.

At this moment, I have implemented an oscillator based on the
relation:

cos(A+B) = 2cos(A)cos(B)-cos(B-A)

With A = 2*pi*Fo/Fs

For initialization, or when Fo is changed, I calculate cos(B) using
25th order McLaurin series, giving me an error of < 0.0007% for |x| <=
PI/2... But there is a little, but significative problem: the smaller
the value of Fo, the closer its cosine is to 1, and the oscillator
doesn't oscillate at all!!! Even for Fo = 1.0, I don't get the right
oscillation frequency, it's somewhat higher.

However, I've found that for Fo >= 100 Hz. the oscillation frequency
is exactly what is expected!!!!! I tried with Fo up to 20000 Hz, with
no problemas at all.

That makes me thing of using different approaches, coupled with my
existing oscillator design, now running at higher ("valid") Fo:

1. Mixing its output with another oscillator and using the relation:

cos(A)*cos(B) = 1/2[cos(A-B)+cos(A+B)]

Then filtering to get rid of cos(A+B), and scaling. A and B should
chosen in such a way that A-B = Fo. One drawback seems to be the fact
that, due to the fact that Fo is really small, A and B should be very
close, and I am not sure if the final effect will be to have an
oscillator at exactly Fo.

2. Runnimng the oscillator at a frequency N times Fo, then
downsampling by N.

All of your opinions and suggestions are _VERY_ welcome.

Regards,

JaaC
On Sun, 29 Feb 2004 13:59:29 +1300, Jaime Andres Aranguren Cardona wrote:

Since you are using floating point arithmetic, it may be worthwhile using
( cos(A)-1 ) as a term and compensating for the -1 term by adding in
cos(B). (I read "A" as being the increment)
Another technique which may be useful is to create a coarse waveform at a
much lower frequency (eg Fs/64) to lock the freuency to the correct value
and extrapolate from this. Hope this is of some use.
> Hi, guys. > > I need your advice on the implementation of an LFO, with variable Fo > (oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling > at Fs = 44.1kHz. > > To be done with an ADSP-21065L in floating point (EzKit). > > Due to these ranges, a LUT (look up table) doesn't seem to be a viable > solution. > > At this moment, I have implemented an oscillator based on the relation: > > cos(A+B) = 2cos(A)cos(B)-cos(B-A) > > With A = 2*pi*Fo/Fs > > For initialization, or when Fo is changed, I calculate cos(B) using 25th > order McLaurin series, giving me an error of < 0.0007% for |x| <= > PI/2... But there is a little, but significative problem: the smaller > the value of Fo, the closer its cosine is to 1, and the oscillator > doesn't oscillate at all!!! Even for Fo = 1.0, I don't get the right > oscillation frequency, it's somewhat higher. > > However, I've found that for Fo >= 100 Hz. the oscillation frequency is > exactly what is expected!!!!! I tried with Fo up to 20000 Hz, with no > problemas at all. > > That makes me thing of using different approaches, coupled with my > existing oscillator design, now running at higher ("valid") Fo: > > 1. Mixing its output with another oscillator and using the relation: > > cos(A)*cos(B) = 1/2[cos(A-B)+cos(A+B)] > > Then filtering to get rid of cos(A+B), and scaling. A and B should > chosen in such a way that A-B = Fo. One drawback seems to be the fact > that, due to the fact that Fo is really small, A and B should be very > close, and I am not sure if the final effect will be to have an > oscillator at exactly Fo. > > 2. Runnimng the oscillator at a frequency N times Fo, then downsampling > by N. > > All of your opinions and suggestions are _VERY_ welcome. > > Regards, > > JaaC
-- http://homepages.paradise.net.nz/morganas SmorPgan. ashAleMy@SparadiPse.neAt.nMz (no SPAM no space)
Jaime Andres Aranguren Cardona wrote:

> I need your advice on the implementation of an LFO, with > variable Fo (oscillation frequency), in the range 0.01Hz <= Fo > <= 2.0 Hz, sampling at Fs = 44.1kHz.
> At this moment, I have implemented an oscillator based on the > relation: > > cos(A+B) = 2cos(A)cos(B)-cos(B-A) > > With A = 2*pi*Fo/Fs
> the smaller the value of Fo, the closer > its cosine is to 1, and the oscillator doesn't oscillate at > all!!! Even for Fo = 1.0, I don't get the right oscillation > frequency, it's somewhat higher.
The recursive system has roundoff problems when the poles are very near DC, z = 1. I suggest you run the LFO at quite a low rate and resample. Depending on just what is modulated how ;) you might not need an exact value for each audio sample, and could get away with lower-order interpolation over subblocks of a few to a few dozen samples -- use your ears there. The LFO and similar things would be computed in between. The process adds one subblock length to the control latency, though. What do you use the LFO for? In a synth, note-on LFO phase may actually be relevant. Martin -- Please help refine my English usage! -= Send your critique by email. =- 2 + 2 = 5, for sufficiently large values of 2
Jaime Andres Aranguren Cardona wrote:
> Hi, guys. > > I need your advice on the implementation of an LFO, with variable Fo > (oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling > at Fs = 44.1kHz. > > To be done with an ADSP-21065L in floating point (EzKit). > > Due to these ranges, a LUT (look up table) doesn't seem to be a viable > solution.
Question: Why wouldn't DDS be the simplest way to do this? Must floating point be used? Is it some sort of homework problem? Just curious. Good day! -- _____________________ Christopher R. Carlen crobc@earthlink.net Suse 8.1 Linux 2.4.19
Chris Carlen wrote:

> Jaime Andres Aranguren Cardona wrote: > >> Hi, guys. >> >> I need your advice on the implementation of an LFO, with variable Fo >> (oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling >> at Fs = 44.1kHz. >> >> To be done with an ADSP-21065L in floating point (EzKit). >> >> Due to these ranges, a LUT (look up table) doesn't seem to be a viable >> solution. > > > > Question: > > Why wouldn't DDS be the simplest way to do this? Must floating point be > used? Is it some sort of homework problem? > > Just curious. > > Good day!
Chris, I'm confident that Jaime would make it clear if his problem were homework. Jaime, Is there a reason that sin(n+1) = A*sin(n) + B*cos(n) and cos(n+1) = A*cos(n) - B*sin(n), where A = cos(delta) and B = sin(delta), both constants for any given frequency. A quick normalization step keeps the amplitude at unity. You've seen this before, but I could repeat it in detail if you need me to. At the frequencies that interest you, approximating cos(delta) as 1 and sin(delta) as delta might be completely acceptable. Jerry -- Engineering is the art of making what you want from things you can get. &#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;&#2013266095;
Hello,

First of all, thank you all for comments and suggestion.

My comments, inlined in your replies.

Jerry Avins <jya@ieee.org> wrote in message news:<404276ee$0$3079$61fed72c@news.rcn.com>...
> Chris Carlen wrote: > > > Jaime Andres Aranguren Cardona wrote: > > > >> Hi, guys. > >> > >> I need your advice on the implementation of an LFO, with variable Fo > >> (oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling > >> at Fs = 44.1kHz. > >> > >> To be done with an ADSP-21065L in floating point (EzKit). > >> > >> Due to these ranges, a LUT (look up table) doesn't seem to be a viable > >> solution. > > > > > > > > Question: > > > > Why wouldn't DDS be the simplest way to do this? Must floating point be > > used? Is it some sort of homework problem?
Well, floating point is what I have in '065L (32bit fixed point too). No, no homework problem. OK, I admit it, it seems to be.
> > > > Just curious. > > > > Good day! > > Chris, > > I'm confident that Jaime would make it clear if his problem were > homework. >
Thanks, Jerry.
> Jaime, > > Is there a reason that sin(n+1) = A*sin(n) + B*cos(n) > and cos(n+1) = A*cos(n) - B*sin(n), > > where A = cos(delta) and B = sin(delta), both constants for any given > frequency. A quick normalization step keeps the amplitude at unity. > You've seen this before, but I could repeat it in detail if you need me > to.
I have the article from DSP Tricks section in IEEE SP Mag, by Clay S. Turner, titled "Recursive Discrete-Time Sinusoidal Oscillators" (what a great article!), which I suppose will clarify many doubts to me. Indeed, I also thought of using the approach you suggest (coupled, standard quadrature oscillator, in C. S. Turner's words), but as with my first approach, I thought it would also require of double precision arithmetic to run fine at those low frequencies.
> At the frequencies that interest you, approximating cos(delta) as 1 > and sin(delta) as delta might be completely acceptable.
That's the good news, then!!! I'll try with it, and will post my results back. I did MATLAB simulation with god results, but as the internal representation is in doubles (or am I wrong), I thought of changing the approach. Maybe the Fixed Point Toolbox might help, I'll try. Anyway, I've tried other approaches. For example, usage of a (not too big) table. Fo = 50Hz, Fs=44.1kHz. But reading it with one of the onchip timers at a slower rate, to update a variable. This variable is read at 44.1kHz. With this approach I can have waveform at a lower Fo, sampled at 44.1kHz. But the waveform looks like a stairway, I'll need some sort of interpolation or lowpass filtering. All of your comments regarding this approach, and the others, or any otherr one, are very welcome. I need them! And again, TIA. Regards, JaaC
> > Jerry
Is this any help..

Just use complex multiplication..

        X'(N+1)=X(N).P where P  = exp(j.2.pi.F/Fs)
                            |X| = 1 (nominally)

then normalise to keep magnitude stable..

  delta = ((|X'|^2) - 1)/2  (delta is real)
  X = X' - delta.X'

I've never generated such low frequencies though. My guess is you'd be
better off with 32 bit fixed point arithmetic (rather than floating
point). I think you'll need 32 bits because the phase change per sample
for O.O1 Hz at 44.1 KHz Fs is v.small (1.4E-6 radians according to my
calculations).

Also, I don't think you need anything fancy to calculate the sin and
cos of such small values, just use..
 sin(x)=x
 cos(x)=1-(x^2)/2

Regards
--
Adrian Hey  

Jaime Andres Aranguren Cardona wrote:

> Hi, guys. > > I need your advice on the implementation of an LFO, with variable Fo > (oscillation frequency), in the range 0.01Hz <= Fo <= 2.0 Hz, sampling > at Fs = 44.1kHz. > > To be done with an ADSP-21065L in floating point (EzKit). > > Due to these ranges, a LUT (look up table) doesn't seem to be a viable > solution. > > At this moment, I have implemented an oscillator based on the > relation: > > cos(A+B) = 2cos(A)cos(B)-cos(B-A) > > With A = 2*pi*Fo/Fs > > For initialization, or when Fo is changed, I calculate cos(B) using > 25th order McLaurin series, giving me an error of < 0.0007% for |x| <= > PI/2... But there is a little, but significative problem: the smaller > the value of Fo, the closer its cosine is to 1, and the oscillator > doesn't oscillate at all!!! Even for Fo = 1.0, I don't get the right > oscillation frequency, it's somewhat higher. > > However, I've found that for Fo >= 100 Hz. the oscillation frequency > is exactly what is expected!!!!! I tried with Fo up to 20000 Hz, with > no problemas at all. > > That makes me thing of using different approaches, coupled with my > existing oscillator design, now running at higher ("valid") Fo: > > 1. Mixing its output with another oscillator and using the relation: > > cos(A)*cos(B) = 1/2[cos(A-B)+cos(A+B)] > > Then filtering to get rid of cos(A+B), and scaling. A and B should > chosen in such a way that A-B = Fo. One drawback seems to be the fact > that, due to the fact that Fo is really small, A and B should be very > close, and I am not sure if the final effect will be to have an > oscillator at exactly Fo. > > 2. Runnimng the oscillator at a frequency N times Fo, then > downsampling by N. > > All of your opinions and suggestions are _VERY_ welcome. > > Regards, > > JaaC
Adrian Hey <ahey@NoSpicedHam.iee.org> wrote in message news:<c1uhki$7os$1$8302bc10@news.demon.co.uk>...
> Is this any help.. > > Just use complex multiplication.. > > X'(N+1)=X(N).P where P = exp(j.2.pi.F/Fs) > |X| = 1 (nominally) > > then normalise to keep magnitude stable.. > > delta = ((|X'|^2) - 1)/2 (delta is real) > X = X' - delta.X'
Hello, Adrian. Your suggestion seems to be interesting. However, I have a doubt: what do you mean by X'? Maybe a dumb question, excuse me, please.
> > I've never generated such low frequencies though. My guess is you'd be > better off with 32 bit fixed point arithmetic (rather than floating > point). I think you'll need 32 bits because the phase change per sample > for O.O1 Hz at 44.1 KHz Fs is v.small (1.4E-6 radians according to my > calculations).
Good suggestion. However, the rest of the processing is done in floating poin format. I think it wont be a problem to do the LFO generation in 32 bit, bixed point, then to use the assembler instructions to switch the obtained values to floating point. Or I could use 40 bit floating point (32 bit mantissa, 8 bit exponent) instead. Any opinions?
> > Also, I don't think you need anything fancy to calculate the sin and > cos of such small values, just use.. > sin(x)=x > cos(x)=1-(x^2)/2
Up to which values of x is this aproximation valid? My higher F will be 2 Hz, equivalent to 2.85e-4 radians... I think it's still small enough. but want to be sure.
> > Regards > -- > Adrian Hey
JaaC
> > At the frequencies that interest you, approximating cos(delta) as 1 > > and sin(delta) as delta might be completely acceptable. > > That's the good news, then!!!
Well, I knew they are acceptable as an aproximation... good news if this aproximation works fine for the LFO. I'll try JaaC
Hello,

Jaime Andres Aranguren Cardona wrote:

> Adrian Hey <ahey@NoSpicedHam.iee.org> wrote in message > news:<c1uhki$7os$1$8302bc10@news.demon.co.uk>... >> Is this any help.. >> >> Just use complex multiplication.. >> >> X'(N+1)=X(N).P where P = exp(j.2.pi.F/Fs) >> |X| = 1 (nominally) >> >> then normalise to keep magnitude stable.. >> >> delta = ((|X'|^2) - 1)/2 (delta is real) >> X = X' - delta.X' > > Hello, Adrian. > > Your suggestion seems to be interesting. However, I have a doubt: what > do you mean by X'? Maybe a dumb question, excuse me, please.
X' is the un-normalised complex oscillator output. Assume has the correct phase but (slightly) incorrect magnitude (due to finite precision arithmetic and any small errors in |P|). Say.. X'=X.(1+delta) where |X|=1 and delta is a small scalar value We want to calculate X from X'. Multiplying both sides by (1-delta) and discarding quadratic terms gives.. X'.(1-delta)=X So if we can calculate delta we can also calculate X from X'. We know |X|=1, so (discarding quadratic terms).. |X'|^2 = (|X|^2).(1+2.delta) = 1+2.delta so.. delta = ((|X'|^2)-1)/2 You could just use.. X = X'.(1-delta) but the (1-delta) factor may be >1, which would be unrepresantable in a format like "1.31", so instead use.. X = X' - delta.X'
>> I've never generated such low frequencies though. My guess is you'd be >> better off with 32 bit fixed point arithmetic (rather than floating >> point). I think you'll need 32 bits because the phase change per sample >> for O.O1 Hz at 44.1 KHz Fs is v.small (1.4E-6 radians according to my >> calculations). > > Good suggestion. However, the rest of the processing is done in > floating poin format. I think it wont be a problem to do the LFO > generation in 32 bit, bixed point, then to use the assembler > instructions to switch the obtained values to floating point. Or I > could use 40 bit floating point (32 bit mantissa, 8 bit exponent) > instead. Any opinions?
I think 40 bit floating point should be as good as (but no better than) 32 bit fixed point. Normal single precision floating point (24 bit mantissa) would be worse.
>> >> Also, I don't think you need anything fancy to calculate the sin and >> cos of such small values, just use.. >> sin(x)=x >> cos(x)=1-(x^2)/2 > > Up to which values of x is this aproximation valid? My higher F will > be 2 Hz, equivalent to 2.85e-4 radians... I think it's still small > enough. but want to be sure.
Well I dunno exactly. Your oscillator output won't be perfect, it will have non-zero bandwidth due to imprecise arithmetic (which I guess you could model as random phase drift in the output samples). It shouldn't be to hard to quantify these effects (I won't try it here though). But ultimately this, and your precision & stability requirements will determine whether 32 bit arithmetic is good enough (and whether or not the above approximations are good enough). Regards -- Adrian Hey