It's a little weird to see so many answers but none that presents an actual answer in C or explains how and why to do it.
The general idea is to maintain a phase that is incremented by a step size that is calculated from the frequency and sample rate. This way you will never get a phase discontinuity.
When doing this, one has to be very careful with accumulated round-off errors in floating point variables because, while the relative round-off error stays mostly the same no matter how large the numbers are, the absolute round-off error increases depending on the magnitude of the number. (There is an in-depth and heavy explanation in the article What Every Computer Scientist Should Know About Floating-Point Arithmetic.)
If you simply keep adding your step size to the phase, it will soon reach magnitudes that are too large, so it has to be kept in check. Since we are dealing with a single waveform we can limit the phase to between 0.0 and 1.0 exclusive by wrapping, or modulo. The easiest and likely most efficient way to do this is to use an unsigned integer for the phase, and let the C compiler take care of the wraps. C is annoying because a lot of arithmetic operations are not defined, but unsigned integer wrap-around is defined the way we want it to, so we can take advantage of that.
The program below outputs a waveform on standard output using the technique described above. The phase is kept in an unsigned integer, incremented with another integer calculated based on the requested frequency and sample rate. Whenever the phase goes above whatever limit set by the integer size, it will automatically wrap to zero.
The integer phase is then scaled to the wanted index into the sine function. This could easily be changed to use a wave-table or an interpolating lookup.
Some parts are unusually complicated because of portability and compatibility. In a fixed setting, a lot can be simplified for readability. Likewise there are places where you might want to add better rounding.
#include <math.h>
#include <stdio.h>
#define M_TWOPI 6.283185307179586476925286766559
/*
* The phase must be an unsigned integer.
* 'maxphase' is for example 65536 if phase_t is 16 bits.
*/
typedef unsigned long phase_t;
double maxphase = (double)((phase_t)0-(phase_t)1)+1.0;
double fs = 44100;
phase_t hz_to_delta( double hz )
{
return maxphase*hz/fs+0.5;
}
float sample_phase( phase_t phase )
{
return sin( phase/maxphase*M_TWOPI );
}
int main( void )
{
long i;
phase_t delta, iphase = 0;
delta = hz_to_delta( 500.0 );
for( i=0; i<fs; ++i )
printf( "%e\n", sample_phase( iphase += delta ) );
delta = hz_to_delta( 1000.0 );
for( i=0; i<fs; ++i )
printf( "%e\n", sample_phase( iphase += delta ) );
return 0;
}