5 ms·
> I also checked my approximation against the "real" floating point sine operation, and found a maximum error of 0.012% for any i16 value, which is more than cl
by jerrre 4y ago
> I also checked my approximation against the "real" floating point sine operation, and found a maximum error of 0.012% for any i16 value, which is more than close enough for my ears!
0.012% error ~= -78dB to put it in respect with other audio stuff, which in most cases should be more than low enough. For reference the dynamic range of 16bit audio is around 90dB
- tails4e 4y agoI'm not near a computer to check, but a 256 entry look up table, would have 1.4 degrees resolution, so using this I believe the difference between sin of 0 and sin of 1.4 is about 2%, so the error would be +/- 1%. Using a quarter wave table reduces the error. By approx 4, but this still. Means 0.25% error. Or am I way off?
- chris_overseas 4y agoIn the code you'll see there is also linear interpolation being performed between the LUT entries, hence the reduced error compared to just using the 256 LUT entry values directly.
- tails4e 4y agoThanks, I missed the linear interp part. I might look into this more for DDS, would be interesting to see how small a lut could be for 90db performance.
- zokier 4y agoFound interesting post that implements computed approximation, apparently it was faster than interpolated lut in his case. https://hackaday.io/project/28597-the-delta-flyer/log/145736-building-a-better-sine-function https://hackaday.io/project/28597-the-delta-flyer/log/145736...
- willis936 4y agoWouldn't cubic interpolation have less error at the cost of performance?
- jamesmunns 4y agoLikely! But as mentioned in my top level comment, performance was the key metric I was going for here :)
- Gordonjcp 4y agoThe code isn't particularly clear (nothing written in Rust is) but it looks like the output is using linear interpolation to compute the values in between steps. If you use a lookup table then you're approximating a sine as a bunch of piecewise linear segments with surprisingly little error, and linear interpolation works well on those. You don't have any harmonics to worry about and the curve is incredibly smooth, so there are no odd effects to take into account. A while ago I wrote some Python code to demonstrate this effect in calculating frequency from pitch in synthesizers by interpolating a table of semitone steps. The error was negligible up to around C6, which is the top key of most 5-octave keyboards, and only a handful of Hz at C8 which is higher than most folk need. I can't find the code now but when I do I'll post it.
- tails4e 4y agoThanks, didn't see that, but it makes sense.
- jamesmunns 4y agoI sort of disagree that this isn't clear, but maybe Rust has warped my brain! I did try and comment my intent along the way, but if anything was unclear, I'd be happy to explain.
- willis936 4y agoThis is a worst case estimate. Quantization error can be reduced with higher sample rates and better output filters. Not to zero, of course, and more bits is better, but there are tradeoffs.
- jcelerier 4y agoI once met someone who was consistently, 100% of the time, able to pick the correct answer in a blind test comparing puredata and max/msp's sine wave generator (iirc one uses a wavetable and the other uses math.h sin) ; would be interesting to see how low would be the difference
- jamesmunns 4y agoThanks! That's cool to know. I did my error calculations by brute forcing the whole 16-bit range, basically: for i in (i16::MIN..=i16::MAX) { let a = (i as f32).sin() as i16; let b = lut_sin(i); let diff = abs_diff(a, b); } Which found the largest difference to be 4, and I got 0.012% from (4/32768).