I am sampling a sine wave at 48 kHz, the frequency range of my sine wave can vary from 0 to 20000 Hz with a step of about 100 Hz. I am using a lookup table approach. So I generate 4096 samples for a sine wave for 4096 different phases. I think the general idea behind this to increment the step size and use different step sizes for different frequncy. So I do the following (pseudo code). But I am not sure how the step size is going to be related to the frequency I want to generate the samples of the sine wave of? For example if my frequency is 15000 Hz what would be the step size that I have to traverse? Is my sample size (4096) too low for this?
// Pseudocode
uint16_t audio_sample[4096] = {...};
NSTEP = freq; //???How is the step size going to be related to the freq here
for(int i = 0; i < 4096; i = i+NSTEP)
{
sine_f(i) = audio_sample[i];
}
Thanks in advance.
You're on the right track - first we need to generate a sine wave LUT:
const int Fs = 48000; // sample rate (Hz)
const int LUT_SIZE = 1024; // lookup table size
int16_t LUT[LUT_SIZE]; // our sine wave LUT
for (int i = 0; i < LUT_SIZE; ++i)
{
LUT[i] = (int16_t)roundf(SHRT_MAX * sinf(2.0f * M_PI * (float)i / LUT_SIZE));
} // fill LUT with 16 bit sine wave sample values
Note that we only need to generate this LUT once, e.g. during initialisation.
Now that we have a sine wave LUT we can use it to generate any frequency we wish to using a phase accumulator:
const int BUFF_SIZE = 4096; // size of output buffer (samples)
int16_t buff[BUFF_SIZE]; // output buffer
const int f = 1000; // frequency we want to generate (Hz)
const float delta_phi = (float) f / Fs * LUT_SIZE;
// phase increment
float phase = 0.0f; // phase accumulator
// generate buffer of output
for (int i = 0; i < BUFF_SIZE; ++i)
{
int phase_i = (int)phase; // get integer part of our phase
buff[i] = LUT[phase_i]; // get sample value from LUT
phase += delta_phi; // increment phase
if (phase >= (float)LUT_SIZE) // handle wraparound
phase -= (float)LUT_SIZE;
}
Note: for higher quality output you can use linear interpolation between the LUT values at phase_i
and phase_i + 1
, but the above approach is good enough for most audio applications.