[sdiy] Approximating sine with plain integer math
commodorejohn at gmail.com
Thu Apr 7 02:06:19 CEST 2016
On 4/6/16, Richie Burnett <rburnett at richieburnett.co.uk> wrote:
> Neat. It's got quite a high distortion for a sinewave as you said, and has
> only odd harmonics present. The waveform you have actually generated is the
> integral of a triangle waveform.
> As a triangle is the integral of a squarewave and has a steeper spectral
> roll-off, your waveform is the integral of the triangle waveform, and has an
> even steeper spectral roll-off:
Hah, interesting. I figured from looking at a graph that it might be
something in the square/triangle family, but I'm not up on my
higher-level math enough to work out the details as you did.
> You can play similar tricks with generating successive integrals of a
> sawtooth waveform, to get from sawtooth to parabola and then to something
> that looks sine-like and has a -18dB/oct rolloff, but has both odd and even
> harmonics present. Technically inferior to your approximation because the
> THD is higher, but might sound nicer with even harmonics present!?!?
Interesting notion. For my purposes I like my "sine" to sound closer
to a triangle than not, so I suspect that I'd prefer it without the
even harmonics, but it'd be interesting to compare.
> Did you consider a lookup-table with linear interpolation? ...that would be
> my method of choice for sinewave generation if I wanted low distortion. You
> can also get excellent results with low-order polynomial approximations with
> or without range-reduction techniques to exploit the inherent symmetries of
> the sine function. These can all be implemented with fixed-point
> arithmetic. No need to go to floating point.
Yeah, those are valid approaches as well, but my other goals for the
project are (A) pure algorithmic synthesis (so no lookup tables -
again, no particular reason other than just because it's my whim) and
(B) a little deviation from strictly-mathematically-accurate (just
enough to give it a little unique character; I don't care much for
softsynths that sound too vanilla "perfect," which is why I was trying
to come up with a simple method to distort the sawtooth in the first
place.) So I think the method I stumbled upon is probably well-suited
to my design goals.
> Good luck with your project!
Anyway, to follow up on the issues with my implementation, I worked
out a simple fix to eliminate the overflow issue; it reduces the
amplitude by ~0.012% of maximum and doesn't noticeably change the
waveshape compared to my initial implementation. The new algorithm:
1. Generate a sawtooth in the range -max to +max (i.e. -32768 to 32767
for a 16-bit integer.)
2. Square it.
3. Divide by +max (i.e. 32767.)
4. Multiply by the sign of the original value.
5. Subtract the original value.
5.5. Reduce the magnitude by 1 (i.e. add 1 if it's positive, subtract
1 if it's negative.)
6. Multiply by 4.
7. The result is inverted (or 180 degrees out-of-phase, if you want to
look at it that way,) so re-invert it if this is a problem for you. Or
just multiply by -4 in step 6.
Here's the fixed C version:
int sawToSine(int saw)
return (((((saw * saw) / INT_MAX) * ((saw < 0) ? -1 : 1)) - saw) +
((saw < 0) ? -1 : 1)) * -4;
More information about the Synth-diy