On Sat, Nov 24, 2018 at 10:49:46AM +0000, Will Godfrey wrote:
>The safe way is of course:
>
>int i = (int) floorf (p);
>float f = p - i;
I'd been mulling over *exactly* that point for
some time. My reasoning being
that in the latter case, if the integer was slightly wrong then using it for
the subtraction should give a remainder that was also slightly wrong, but in a
direction that tends to compensate for the error.
How can an int be 'slightly wrong' ? :-)
The advantage of the 'safe' way is that you always have p == i + f.
The other thing, is why do we need the floorf? Or in
the original example
(which was taken from working code) truncf?
A straight cast to int seems to give exactly the same result, and is at least
twice as fast with -O3 and about 4 times as fast unoptimised.
We want f >= 0, so rounding must be towards -inf. Casting will truncate
(i.e. round towards zero). This gives the correct result only if p >= 0.
That may be all you need, but I wouldn't like to depend on it.
There is a way to avoid all float to int conversions, at least outside
the per-sample loops.
Suppose you have a wavetable of size L, current position is float p,
and increment is float f. To generate N samples you'd have something
like:
for (i = 0; i < N; i++)
{
k = floorf (p);
u = p - k;
// use k, u to interpolate in wavetable
p += f;
if (p >= L) p -= L;
}
To avoid floorf() inside the loop, instead of maintaining p and f
as floats, split both of them from the start into an integer and
float part:
k = floorf (p);
u = p - k;
kf = floorf (f);
uf = f - kf;
for (i = 0; i < N; i++)
{
// use k, u to interpolate in wavetable
k += kf;
u += uf;
if (u >= 1.0f)
{
k += 1;
u -= 1;
}
if (k >= L) k -= L;
// or k &= L-1 if L is a power of 2.
}
Ciao,
--
FA