Efficient sine oscillator

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

itoa wrote:
JCJR wrote:
For instance, if running a 64 sine, 64 cosine harmonic oscillator bank-- Ideally after a few minutes or preferably even indefinitely, all the higher harmonic sin oscillators would cross zero at the same time that the harmonic 1 sin oscillator crosses zero, and all the higher cos oscillators would cross zero at the same time that the harmonic 1 cos oscillator crosses zero.
None of computed oscillators meet these criteria. But isn't the phase drift desired in most cases? Most of natural sounds are slightly inharmonic.
If you want to produce a perfect additive digital buzz, ifft is better.
Thanks itoa. Its just been something want to experiment with one day, if motivation and available time ever coincide. Dunno if an oscillator bank would perform as I imagine, and there seems no better way to find out except write code and discover how bad it sucks.

Ifft is mind numbing in the manipulations to make it work for stretching and pitching. At least it does a number on what is left of my mind. Anyway, I have access to an elastique license, and elastique is vastly superior to any stretching/pitching I ever managed to write. It works fabulous for "reasonable" amounts of stretching/pitching. No need to reinvent the wheel.

Am just curious if a free-running phase locked oscillator bank would sound "smoother" than overlapped manipulated ifft for extreme stretching and pitching. For instance analysis from overlapped conventional fft frames, to establish regular spaced control points, time envelopes for all the sin and cos. Then run the oscillator bank controlled by those smoothed amplitude envelopes. Maybe it would suck or not work at all, or then again, maybe running the osc bank for instance an octave lower than the analysis frequencies, might be cajoled into making a very smooth, non granular sub octave transpose.

Post

itoa wrote:ifft is better.
spent a long time on that, could never generate a sawtooth that sounded right at every frequency, even though i got it to sound stable at most frequencies. could never get anyone to tell me anything about ifft synthesis (there's at least three threads back there i started at various times).
you come and go, you come and go. amitabha neither a follower nor a leader be tagore "where roads are made i lose my way" where there is certainty, consideration is absent.

Post

xoxos wrote:
itoa wrote:ifft is better.
spent a long time on that, could never generate a sawtooth that sounded right at every frequency, even though i got it to sound stable at most frequencies. could never get anyone to tell me anything about ifft synthesis (there's at least three threads back there i started at various times).
One can avoid the typical uglyness by simply using FFT to generate a wavetable, then play it back like any other wavetable (using whatever interpolation method you'd normally use). It's somewhat expensive (and wasteful) to do this if you actually want to morph the spectra, but with a lowish control rate (or just for static waves generated once) it can work quite fine.

Anyway, back the problem of "harmonics that stay in phase": if one specifically wants to have perfectly harmonic, phase-locked spectra with additive synthesis, why not apply some orthogonal thinking (literally, in the time/frequency sense). Basically, the complex sinusoids for the harmonics can be written as e^(i*pi*f*n*t) where f is the frequency of the fundamental, n is the harmonic number, t is time (with time and frequency suitably normalized).

Using the substitution e^(a*b) = (e^a)^b, one can decompose the above the usual way (as additive synthesis is normally explained) as (e^(i*pi*f*n))^t to generate each of the harmonics independently (which will make them drift apparent), but it's equally valid to decompose it as (e^(i*pi*f*t))^n; simply generate the master (fundamental frequency) quadrature and exponentiate (by repeated complex multiply, or something similar) to get the harmonics for each sample separately. Problem solved, everything will stay perfectly phase-locked, takes roughly the same amount of actual calculation, just with less state to maintain over time.

Post

It's conceptually similar to polynomial waveshaper.. but this would require double precision at least.. (Imagine cumulative error after computing 400th partial of 50hz saw wave)
There are other options like DSF or modified FM (using many segments).

But... why using canonical additive synthesis for harmonic sounds? Use subtractive or iFFT wavetables to have full control over partials. (FFT routines are damn fast on SSE).

Nevertheless this is just a variety of buzzing (boring? :). Most sounds are (slightly) inharmonic and this is where additive synthesis shines. Besides of PM, no other options than set of _independent_ oscillators are available. I found modified coupled form the cheapest here.
giq

Post

As described earlier, I'm curious whether an oscillator bank would make a smooth-sounding stretcher/pitcher, for extreme amounts of stretch/pitch. For "normal" amounts of stretch/pitch, popular codes do fabulous, but show bad warts for instance transposing down an octave, or stretching 2X duration.

I wouldn't be locking the frequency of the oscillator bank to the fundamental of a desired note, as in old Synergy synthesizer, or implemented in simpler form with the Kawai K5. It would work exactly the same as FFT/IFFT phase vocoder, except the oscillator would be running at a different base frequency than the fft analysis in the case of pitching, and running at the same base frequency but with stretched smoothed sin/cos control points in the case of stretching.

My expectations may be woefully incorrect, but I would expect it to be slightly artificial-sounding, but on the other hand extremely smooth with no warbling, phasing, granular-sounding artifacts. The phase-slip unavoidable, running the oscillator bank at a different rate than the FFT analysis, would be the cause of some "artificial nature" in the resynthesis. If more unpredictability was added with a non-phase-locked oscillator bank, then that might make it "more artificial" sounding.

I appreciate the advice from itoa and mystran on possible ways to do a phase-locked oscillator bank more efficient. Seems table-lookup would be the strategy of choice. I never expected it to be a speed demon. MyStran's ideas of deriving high harmonics from the fundamental, will try to understand that. Maybe one of these days will find the motivation and time to code it. :)

Post

Sounds like fft phase mangling e.g. spectral freeze :) - fft -> set phases to zero -> ifft with windowing and resampling (for any pitch). Any sound can be "robotized" this way.

As for complex stuff, it's easy... Complex sine is just a rotating 2d vector. You can double rotation speed simply by multiplying by another vector (2nd harmonic) and so on. Google for complex numbers operations and geometry. This helps understanding filters as well. Complex numbers can be expressed as complex exponential (as Mystran did).
giq

Post

itoa wrote: It's conceptually similar to polynomial waveshaper.. but this would require double precision at least.. (Imagine cumulative error after computing 400th partial of 50hz saw wave)
There are other options like DSF or modified FM (using many segments).
Yeah, it's a polynomial waveshaper on complex numbers (to get single sided spectra trivially). It doesn't really need double precision though; if accuracy becomes a problem, you can just substitute a more accurate recurrence relation, there's no need to use actual complex multiplication as-is. But yeah, it's a bit academic.

Post

If you want an iir type algorithmic generation of a sin wave then the best option is the good old trapezoidal integrated linear SVF. The biggest problem will be coefficient calculation if you want to update the sin frequency at audio rate, but if there are large chunks of constant frequency then this is probably the best method.

You can get either an exact -1 to 1 output, or one that ramps down in volume as the frequency is increased. You can seed the algorithm with any phase you want, and then have even hard step modulation from 0.499 to 0.00001 in normalised frequency without a problem

Code: Select all

init coefficients using tan:
   const omega = 2*pi*cutoff/samplerate;
   const g = tan(omega/1);
   g0 = 2/(1 + g^2) = 2*cos (omega/2)^2;
   g1 = g*g0 = sin (omega);

set the initial phase of the sin / cos output:
   ic1eq = cos(phase);
   ic2eq = sin(phase);
And the actual grunt of the inner loop, which is very parallel and perfect for SSE2:

Code: Select all

tick:
   const v1 = g0*ic1eq - g1*ic2eq;
   const v2 = g1*ic1eq + g0*ic2eq;
   ic1eq = v1 - ic1eq;
   ic2eq = v2 - ic2eq;

   cos output 0 to 1 = ic1eq
   sin output 0 to 1 = ic2eq
   cos output ramped = v1
   sin output ramped = v2
Last edited by andy-cytomic on Wed Jun 24, 2015 12:08 pm, edited 2 times in total.
The Glue, The Drop - www.cytomic.com

Post

Coefficient recalculation complexity is near sine approximation formula :). So let the filter be filter.
giq

Post

itoa wrote:Coefficient recalculation complexity is near sine approximation formula :). So let the filter be filter.
Yes, I already covered this in my post, let me re-cap the ups and downs again if it was too hard to take in the first time: If you want audio rate frequency modulation using an iir based method isn't ideal because of the expense of coefficient calculation, but this iir method is very parallel and can generate a huge number of simultaneous sin and cos waves of arbitrary initial phase, fixed amplitude, and fixed frequency for very little run time cost once the setup is done, no wrapping of phase to worry about, no branches, minimal memory access (two states, and two coefficients), and all double precision computation, so perfect for SSE2 implementation.
The Glue, The Drop - www.cytomic.com

Post

andy-cytomic wrote:If you want an iir type algorithmic generation of a sin wave then the best option is the good old trapezoidal integrated linear SVF.
While it's definitely my favorite biquad filter structure too, for straight quadrature sine generation (or spectral calculations, FFT type stuff, etc) I normally use a slightly more simple recurrence that basically saves two (scalar) adds (p is the phase increment = 2*pi*f/fs, etc):

Code: Select all

coeff0 = -2*square(sin(p/2))
coeff1 = sin(p)
// loop
tmpX = oldX * coeff0 + oldY * coeff1
tmpY = oldY * coeff0 - oldX * coeff1
newX = oldX + tmpX;
newY = oldY + tmpY;
Or written in terms of std::complex (which better illustrates the simplicity ;)):

Code: Select all

  complex value = complex(cos(init), sin(init));
  complex coeff(-2*square(sin(p/2)), sin(p));
  // loop with
  value += coeff * value;
I originally picked this up when writing an FFT (don't ask) and needed a recurrence that would work for recursive twiddle calc with large FFTs; I was doing some stuff in the millions of points range, which will just blow up in doubles if you used stock rotations. Nice thing is it's easy to derive double angle coeffs on the fly too (no additional trig required), so the just two sin() calls per transform with minimal state to keep. It works great for other sine generation duties too though, so has become my go-to formula for the purpose.
Last edited by mystran on Fri Jun 06, 2014 1:40 am, edited 3 times in total.

Post

I may be wrong, but if you're going to use continuous multiplies to create rotations like this, it's probably best to normalize your results once in a while, even when using doubles. The slight errors are compounded through millions of iterations.
It reminds me of a story where someone left a hardware reverb on for days on end, and it started to make annoying humming sounds that slowly got louder and louder - a floating point rounding error where an oscillator got larger and larger over time perhaps?

Post

DaveHoskins wrote:I may be wrong, but if you're going to use continuous multiplies to create rotations like this, it's probably best to normalize your results once in a while, even when using doubles. The slight errors are compounded through millions of iterations.
Well, yeah. If you're going to be running for potentially infinite periods, then you definitely want to normalize at least occasionally (or just use a table lookup or waveshape from triangle if it's not at audio rates; often accuracy is irrelevant in practice; I actually like to waveshape from a triangle for audio too as I like the extra harmonics, but then you need BLEPs so it's not really that cheap anymore). The difference is mostly that trivial cos/sin type rotation tends to run into problems pretty fast, while many of the better alternatives will accumulate error dramatically slower.

Post

Thanks itoa for the modified and good oscillator.
Is there a way to make an envelope for it by scaling the values inside the recursion itself?

Post

x = x - e*y;
y = e*x + y;

Of course just scale x and y .. e.g. for decay add x*=d; y*=d; to the loop.
Or cheat a bit and scale only one variable (the oscillator will self-stabilize) :)
giq

Post Reply

Return to “DSP and Plugin Development”