This is very good question.
Fortunately for you, I have a very good answer.
Assuming standard CD quality, your sound level has just over 4 significant digits accuracy, thus this is the level you need to be truly indistiguishable.
Is this level necessary for auditory purposes? Let's assume so.
Your fastest solution, by far, is going to be linear interpolation on a segmented domain. You need two tables. The first hold the sine value of the center of each interval. The second holds the first derivative, aka cosine.
To calculate your sine value, multiply your angle by the spacing you are using (so a unit of one corresponds a segment). The integer portion will give you the index into your table and use the fractional portion (-0.5 to 0.5) for the interpolation.
Now the question becomes "How many entries do I need in my table?"
I'll let you take a crack at that.
I have a better answer (I think, you need to test it).
$$ \sin( x + d ) = \sin( x ) \cos( d ) + \cos( x ) \sin( d ) $$
Back to two tables. One for the broad range ($x$) , and one for the fine range ($d$).
If the range of $d$ is small enough, then you can use (Taylor or find the Remez instead):
$$ \cos(d) \approx 1 - x^2/2 + x^4/24 $$
$$ \sin(d) \approx x - x^3/6 + x^5/120 $$
But that will require more computation.
For uber accuracy, which you don't need, you could do the interpolation thing on the fine table value.
I was curious, so here you go:
import numpy as np
#==========================================================
def main():
N_coarse = 128
N_fine = 128
#---- The coarse table
sc = np.zeros( N_coarse ) # Sine Coarse
cc = np.zeros( N_coarse ) # Cosine Coarse
theSlice = np.pi * 0.5 / N_coarse
theAngle = 0.0
for n in range( N_coarse ):
sc[n] = np.sin( theAngle )
cc[n] = np.cos( theAngle )
theAngle += theSlice
#---- The fine table
sf = np.zeros( N_fine ) # Sine Fine
cf = np.zeros( N_fine ) # Cosine Fine
theSlice /= N_fine
theAngle = 0.0
for n in range( N_fine ):
sf[n] = np.sin( theAngle )
cf[n] = np.cos( theAngle )
theAngle += theSlice
#---- The test
theFactor = N_coarse * 2.0 / np.pi
for a in range( 157 ):
theAngle = a * 0.01
n = theAngle * theFactor
nc = np.floor( n )
nf = np.floor( ( n - nc ) * N_fine )
sine = sc[nc] * cf[nf] + cc[nc] * sf[nf]
print sine, np.sin( theAngle ), sine - np.sin( theAngle )
#==========================================================
main()
This old fart got it right on the first try!
Here are the first few rows of the output, the rest is comparable:
0.0 0.0 0.0
0.00997070990742 0.00999983333417 -2.91234267487e-05
0.0199404285515 0.0199986666933 -5.82381418187e-05
0.0299081647675 0.0299955002025 -8.73354349791e-05
0.0399687249608 0.0399893341866 -2.06092258625e-05
0.0499294807897 0.0499791692707 -4.96884810173e-05
0.059885272753 0.0599640064794 -7.87337263996e-05
0.0699307504776 0.0699428473375 -1.20968599355e-05
0.0798735744039 0.0799146939692 -4.11195652507e-05
0.089808457497 0.089878549198 -7.00917010057e-05
0.0998298073783 0.0998334166468 -3.60926852212e-06
0.109745746461 0.109778300837 -3.25543758203e-05
0.119650774894 0.119712207289 -6.14323952304e-05
0.129543907942 0.12963414262 -9.02346778173e-05