我能够为音频编写一个基本的正弦波发生器,但我希望它能够从一个频率平滑过渡到另一个频率。如果我只是停止生成一个频率并立即切换到另一个频率,则信号会出现不连续性,并且会听到“咔哒”声。
我的问题是,什么是一个好的算法来生成一个从 250Hz 开始的波,然后过渡到 300Hz,而不引入任何点击。如果算法包括可选的滑行/滑音时间,那就更好了。
我可以想到一些可能的方法,例如过采样后跟低通滤波器,或者可能使用波表,但我确信这是一个足够常见的问题,有一个标准的方法来解决它。
我能够为音频编写一个基本的正弦波发生器,但我希望它能够从一个频率平滑过渡到另一个频率。如果我只是停止生成一个频率并立即切换到另一个频率,则信号会出现不连续性,并且会听到“咔哒”声。
我的问题是,什么是一个好的算法来生成一个从 250Hz 开始的波,然后过渡到 300Hz,而不引入任何点击。如果算法包括可选的滑行/滑音时间,那就更好了。
我可以想到一些可能的方法,例如过采样后跟低通滤波器,或者可能使用波表,但我确信这是一个足够常见的问题,有一个标准的方法来解决它。
我过去使用的一种方法是维护一个相位累加器,该相位累加器用作波形查找表的索引。在每个采样间隔将相位增量值添加到累加器:
phase_index += phase_delta
要更改频率,您可以更改在每个样本处添加到相位累加器的相位增量,例如
phase_delta = N * f / Fs
在哪里:
phase_delta is the number of LUT samples to increment
freq is the desired output frequency
Fs is the sample rate
N is the size of the LUT
这保证了输出波形是连续的,即使您动态更改 phase_delta,例如频率变化、FM 等。
为了更平滑地改变频率(滑音),您可以在适当数量的样本间隔内将 phase_delta 值在其旧值和新值之间倾斜,而不仅仅是立即更改它。
请注意,phase_index
andphase_delta
都有一个整数和一个小数部分,即它们需要是浮点数或定点数。phase_index 的整数部分(模表大小)用作波形 LUT 的索引,小数部分可以可选地用于相邻 LUT 值之间的插值,以获得更高质量的输出和/或更小的 LUT 大小。
创建正弦波的最佳方法之一是使用具有递归更新的复相量。IE
其中 z[n] 是相量,,其中是以弧度为单位的振荡器的角频率,是样本索引。的实部和虚部都是正弦波,它们的相位相差 90 度。如果您需要正弦和余弦,非常方便。单个样本计算只需要 4 个倍数和 4 个相加,并且比任何包含 sin() cos() 或查找表的东西便宜得多。潜在的问题是由于数值精度问题,幅度可能会随时间漂移。但是,有一个相当直接的修复方法。假设。我们知道应该有统一的大小,即
因此,如果情况仍然如此,我们可以每隔一段时间检查一次并进行相应的纠正。确切的修正是
这是一个尴尬的计算,但由于非常接近于一,你可以附近的泰勒展开项,我们得到
所以校正简化为
每隔几百个样本应用这个简单的校正将使振荡器永远保持稳定。
为了连续改变频率,需要相应地更新乘数 W。即使乘数的非连续变化也将保持连续的振荡器功能。如果需要频率斜坡,则可以将更新分解为几个步骤,或者您可以使用相同的振荡器算法来更新乘法器本身(因为它也是一个单位增益复数相量)。
我同意之前使用相位累加器的建议。本质上,控制输入是每步或每个时钟周期(或每个中断或其他)的相位提前量,因此更改该值会更改频率而不会导致相位不连续。然后通过 LUT 或仅计算 sin(theta) 或 cos(theta) 从累积的相位值确定波幅。
这本质上就是通常所说的数控振荡器 (NCO) 或直接数字合成器 (DDS)。对这些术语进行网络搜索可能会比你想知道的让它们运作良好的理论和实践更多。
添加一个额外的累加器可以通过控制相位提前值的变化率来实现频率之间的无缝转换,如您所建议的,如果这也是需要的。这有时称为数字差分分析仪或 DDA。
从这个网站:
为了创建从一个频率到另一个频率或从一个幅度到另一个频率的平滑过渡,必须使用附加部分修改不完整的正弦波,以便在 while 循环的每次迭代后生成的波在 x 轴处结束。
听起来它应该工作。
(实际上,如果它们在过渡时都在 x 轴上同步,我想渐变过渡是不必要的。)