[sdiy] Approximating sine with plain integer math
rsdio at audiobanshee.com
rsdio at audiobanshee.com
Thu Apr 7 05:22:11 CEST 2016
On Apr 6, 2016, at 7:57 PM, John Ames <commodorejohn at gmail.com> wrote:
> On 4/6/16, rsdio at audiobanshee.com <rsdio at audiobanshee.com> wrote:
>> Your code is fine from -180 degrees to +180 degrees, which sort of solves
>> the 270-degree overflow. It seems to be valid for exactly one period of the
>> ramp wave input, from negative peak to positive peak, then it falls apart.
> Yes, I suppose I should've been clearer on that. If you graph the
> function provided in the original post, it goes completely off the
> rails outside of the -1 to +1 range, since the square of X gets larger
> than 1. However, in application, the sawtooth input never exceeds that
> range anyway.
>
> (Also, the overflow would've been at +90 degrees in your reckoning, so
> definitely an issue.)
I can't see the overflow in Grapher.app, but I suppose it could be outputting +1.0 when the maximum fixed-point value would be 1 code less than that. How much overflow are you seeing? I haven't bothered to try the C code, I just typed the functions into Grapher.app and zoomed in.
> On 4/6/16, Brian Willoughby <brianw at audiobanshee.com> wrote:
>> I've been using a State Variable Filter (a.k.a. Quadrature Oscillator) with
>> fixed resonance to generate sine waves at arbitrary frequencies. You should
>> be able to implement the SVF in fixed point or integer math, avoiding
>> floating point as you desire.
>>
>> My understanding is that using the SVF as a sine oscillator is fairly low
>> distortion, but the distortion increases as the frequency approaches
>> Nyquist. I have never measured the distortion of this code, but it would be
>> vary interesting to compare to your 5% odd-harmonic distortion, especially
>> if the distortion can be mapped versus frequency.
>>
>> I've used SVF sine generation for FM, but not for pure synthesis. Does
>> anyone have experience with the SVF in this application? I optimized my code
>> to remove all of the excess calculations needed to produce all 4 outputs of
>> the SVF, since I only needed the 1 output.
> Interesting. Do you have any more information on this technique? My
> only attempt to use filtering to derive a sine wave (from a triangle)
> had the unfortunate side-effect of decreasing the amplitude rather
> drastically as the frequency approached Nyquist.
I'm not using the State Variable Filter to alter another waveform, I'm using it purely as an oscillator, with no input. The code has to initialize the state variables with the appropriate constants, otherwise it doesn't oscillate. Fortunately, the gain in a digital SVF is perfect, so there aren't the same problems as found in analog implementations of the SVF for oscillation, where the gain has to be slightly more than 1.0 with non-linear distortion as feedback to keep the gain from going crazy, since the exact gain varies from one circuit to the next; with temperature; aging; etc.
I dug up the following code, but it's not from a shipping product, and I'm not sure I remember how it works. When I say that I "optimized" the code by removing excess calculations, that doesn't mean the code is completely optimized. There are many levels of optimization that could be done to speed this up. I merely deleted lines of code that produced values that were never used. The next step in optimization would be to use vector instructions to perform as many operations as possible per processor clock cycle (e.g. AltiVec, SSE, Neon, etc.). Also: note that this uses the sin() function from the standard math library, but that's only used at initialization time. Obviously, this is double-precision floating point code, but you should see how to build a fixed-point version with the same operations. I think that the "K" parameter is a constant offset for DC biasing, but I can't recall.
/*
* svf.h
* rsdio
*
* Created by Sound Consulting on 2008/03/22.
* Copyright 2008 Sound Consulting. All rights reserved.
*
*/
#ifndef __svf_h__
#define __svf_h__
#include <CoreAudio/CoreAudioTypes.h>
class svf
{
public:
svf(double Fc, double Fs, double amp);
void reset();
void vfill(Float32 *A, unsigned K, unsigned N);
protected:
double freq;
double rate;
double f;
double a;
double sinZ;
double cosZ;
};
#endif __svf_h__
/*
* svf.cpp
* rsdio
*
* Created by Sound Consulting on 2008/03/22.
* Copyright 2008 Sound Consulting. All rights reserved.
*
*/
#include "svf.h"
#include <vecLib/vDSP.h>
#define FM_DEVIATION 75000
svf::svf(double Fc, double Fs, double amp) : f(f), a(amp)
{
freq = Fc;
rate = Fs;
f = 2. * sin(M_PI * freq / rate);
reset();
}
void svf::reset(void)
{
sinZ = 0;
cosZ = a;
}
void svf::vfill(Float32 *A, unsigned K, unsigned N)
{
// REVIEW: not nearly optimized
while (N--)
{
sinZ += f * cosZ;
cosZ -= f * sinZ;
*A = sinZ;
A += K;
}
}
More information about the Synth-diy
mailing list