[sdiy] Approximating sine with plain integer math
John Ames
commodorejohn at gmail.com
Thu Apr 7 00:55:37 CEST 2016
I'm working on a softsynth for a project, and one of my goals is to
avoid floating-point math (no particular reason other than just to see
if I can, but there's still architectures out there where it makes a
significant difference, so what the hey.) Initially I had compromised
on that when I needed a sine waveform for my oscillators, but today,
while playing around with a way to make the sawtooth less of a plain
linear ramp, I stumbled onto a way to generate what I think is quite a
satisfactory sine waveform using only a few integer operations. Since
I'm rather pleased with it and it might prove useful to somebody,
here's the 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.
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.
There's probably a few different ways to implement this depending on
what kind of architecture you're doing it on, but here's a basic C
implementation:
#include <stdlib.h>
#include <limits.h>
/* sawToSine - takes a sawtooth sample as a signed integer and returns
the equivalent sine sample. */
int sawToSine(int saw)
{
return ((((saw * saw) / INT_MAX) * (saw / abs(saw))) - saw) * -4;
}
>From a quick check with a graphing calculator (paste:
\left(\left(\left(x^2\cdot
\operatorname{sign}\left(x\right)\right)-x\right)\cdot -4\right)-\sin
\left(x\cdot \pi \right)
into https://www.desmos.com/calculator to see,) it looks like has a
maximum distortion of ~5.5% compared to a true sine, and it's actually
a little "fuller" and rounder, which I like. From a quick code test,
it maintains almost full peak-to-peak range and never overflows.
Whether this is any faster than using floating-point probably depends
on how expensive integer multiplies and divides are on your target
architecture, but I just thought it was interesting and wanted to
share :)
More information about the Synth-diy
mailing list