如何从固定大小的查找表中查找不同频率的正弦值?

as3*_*unt 10 c embedded trigonometry signal-processing lookup-tables

我正在采样48 kHz的正弦波,我的正弦波的频率范围可以在0到20000 Hz之间变化,步长约为100 Hz.我正在使用查找表方法.因此,我为4096个不同阶段生成4096个正弦波样本.我认为这背后的一般思想是增加步长并针对不同的频率使用不同的步长.所以我做了以下(伪代码).但我不确定步长是如何与我想要生成正弦波样本的频率相关的?例如,如果我的频率为15000 Hz,那么我必须遍历的步长是多少?我的样本量(4096)是否太低了?

 // 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];
 }
Run Code Online (Sandbox Code Playgroud)

提前致谢.

Pau*_*l R 16

你走在正确的轨道上 - 首先我们需要生成一个正弦波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
Run Code Online (Sandbox Code Playgroud)

请注意,我们只需要生成一次该LUT,例如在初始化期间.

现在我们有一个正弦波LUT,我们可以用它来产生我们希望使用相位累加器的任何频率:

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;
}
Run Code Online (Sandbox Code Playgroud)

注意:对于更高质量的输出,您可以在phase_i和的LUT值之间使用线性插值phase_i + 1,但上述方法对于大多数音频应用来说已经足够了.

  • 我认为您在LUT和输出缓冲区之间感到困惑-我故意将它们设置为不同的大小,以说明它们是独立且无关的事实。当相位超过LUT的大小时,我们通过减去LUT_SIZE使其环绕。 (2认同)

Aki*_*nen 5

通过查找表方法,可能希望有效地使用存储器,并且仅存储正弦波的第一象限。

Then second quadrant = sin[180 - angle]  ; // for angles 90..180 degrees
third quadrant = -sin[angle-180];          // for 180..270
fourth quadrant = -sin[-angle+360]         // for 270..360
Run Code Online (Sandbox Code Playgroud)

我建议使用线性插值法,但是也可以使用基于矢量旋转的方法来生成正弦波(同时生成正弦和余弦)

x_0 = 1, y_0 = 0;
x_n+1 = x_n * cos(alpha) - y_n * sin(alpha)
y_n+1 = x_n * sin(alpha) + y_n * cos(alpha), 
Run Code Online (Sandbox Code Playgroud)

其中alpha = 频率的相位差 == 2pi * fHz / Ts,其中fHz是要产生的频率(以赫兹为单位),Ts是采样时间(或1 / Ts =采样频率,例如44100 Hz)。

这就导致了共振反馈滤波器方法,其传递函数f(z)在单位圆(z = e ^ jomegaT)具有两个共轭极点。

y[n] = y[n-1] - 2*cos(alpha) * y[n-2], with
y[0] = 0,
y[1] = sin(alpha)
Run Code Online (Sandbox Code Playgroud)

有趣的是,可以随时更改Alpha(cos(alpha))。这种IIR滤波器方法的缺点是,按照定义,它是不稳定的。浮点,尤其是定点误差的积累会导致幅度的指数衰减或指数增长。但是,可以通过允许轻微的相位失真来解决该问题。

代替在CORDIC旋转中具有已知的每次迭代放大系数:

K = sqrt(1+sin(alpha)^2);

x' = x - y*sin(alpha);
y' = y + x*sin(alpha);

one can do

x' = x - y * sin(alpha);
y'' = y + x' * sin(alpha);
Run Code Online (Sandbox Code Playgroud)

即使对于定点算法,它也不会为(x',y'')产生完美的圆,但会产生稳定的椭圆。(请注意,这是假设alpha值相对较小,这也意味着频率相对较低。)