看到这么多答案有点奇怪,但没有一个能用 C 语言给出实际答案或解释如何以及为什么这样做。
一般的想法是保持一个相位增加一个步长,该步长是从频率和采样率计算出来的。这样,您将永远不会出现相位不连续。
这样做时,必须非常小心浮点变量中累积的舍入误差,因为无论数字有多大,相对舍入误差基本保持不变,但绝对舍入误差的增加取决于数量的大小。(在文章What Every Computer Scientist Should Know About Floating-Point Arithmetic中有深入而沉重的解释。)
如果您只是继续将步长添加到相位,它很快就会达到太大的幅度,因此必须对其进行检查。由于我们处理的是单个波形,我们可以通过环绕或取模将相位限制在 0.0 和 1.0 之间。最简单且可能最有效的方法是对相位使用无符号整数,并让 C 编译器处理回绕。C 很烦人,因为很多算术运算都没有定义,但是无符号整数环绕是按照我们想要的方式定义的,所以我们可以利用它。
下面的程序使用上述技术在标准输出上输出波形。相位保持在一个无符号整数中,并以根据请求的频率和采样率计算的另一个整数递增。每当相位超过整数大小设置的任何限制时,它将自动回绕为零。
然后将整数相位缩放到正弦函数的所需索引。这可以很容易地更改为使用波表或插值查找。
由于便携性和兼容性,某些部分异常复杂。在固定设置中,可以简化很多内容以提高可读性。同样,您可能希望在某些地方添加更好的舍入。
#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;
}