我能够编写一个用于音频的基本正弦波发生器,但是我希望它能够从一个频率平稳过渡到另一个频率。如果我只是停止产生一个频率并立即切换到另一个频率,则信号将出现中断,并且会听到“喀哒”声。
我的问题是,有什么好的算法可以产生一个始于例如250Hz的波,然后过渡到300Hz,而不会引起任何喀哒声。如果算法包括可选的滑行/滑音时间,那就更好了。
我可以想到一些可能的方法,例如过采样后跟低通滤波器,或者使用波表,但是我确信这是一个足够普遍的问题,因此有一种标准的解决方法。
我能够编写一个用于音频的基本正弦波发生器,但是我希望它能够从一个频率平稳过渡到另一个频率。如果我只是停止产生一个频率并立即切换到另一个频率,则信号将出现中断,并且会听到“喀哒”声。
我的问题是,有什么好的算法可以产生一个始于例如250Hz的波,然后过渡到300Hz,而不会引起任何喀哒声。如果算法包括可选的滑行/滑音时间,那就更好了。
我可以想到一些可能的方法,例如过采样后跟低通滤波器,或者使用波表,但是我确信这是一个足够普遍的问题,因此有一种标准的解决方法。
Answers:
我过去使用的一种方法是维护一个相位累加器,该累加器用作波形查找表的索引。在每个采样间隔处将相位增量值添加到累加器:
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
这样即使在动态更改phase_delta(例如,频率变化,FM等)的情况下,也可以确保输出波形是连续的。
为了使频率变化(滑音)更平滑,您可以在适当数量的采样间隔内将phase_delta值在其旧值和新值之间倾斜,而不仅仅是立即更改它。
请注意,phase_index
和phase_delta
都具有整数和小数部分,即它们必须是浮点或定点。phase_index(模数表大小)的整数部分用作波形LUT的索引,并且小数部分可以可选地用于相邻LUT值之间的插值,以获得更高质量的输出和/或更小的LUT大小。
产生正弦波的最佳方法之一是使用具有复数递归更新的复数相量。即
其中,z [n]是相量,,用ω为以弧度为单位的振荡器和的角频率Ñ样本索引。z [ n ]的实部和虚部均为正弦波,它们的相位相差90度。如果同时需要正弦和余弦,则非常方便。单个样本计算仅需要4的倍数和4的加法,比包含sin()cos()或查找表的任何内容便宜很多。潜在的问题是,由于数值精度问题,振幅可能会随时间漂移。但是,有一个相当简单的方法可以修复该问题。假设z [ n。我们知道 z [ n ]应该具有单位大小,即
因此,我们仍然可以每隔一段时间检查一次,并进行相应的更正。确切的更正是
这是一个尴尬的计算,但由于是非常接近的团结可以近似的1 / √与泰勒展开周围条款X=1,我们得到
因此校正简化为
每隔几百个样本进行一次简单的校正将使振荡器永远保持稳定。
为了连续改变频率,需要相应地更新乘数W。即使乘法器不连续地变化,也将保持连续的振荡器功能。如果需要频率斜坡上升,则可以将更新细分为几个步骤,也可以使用相同的振荡器算法来更新乘法器本身(因为它也是单位增益复数相量)。
从此站点:
为了创建从一个频率到另一个频率或从一个振幅到另一个振幅的平滑过渡,必须使用附加部分修改不完整的正弦波,以便在while循环的每次迭代之后生成的波在x轴处结束。
听起来应该可行。
(实际上,如果它们在过渡时都在x轴上同步,则我认为没有必要进行逐步过渡。)
我同意使用相位累加器的先前建议。本质上,控制输入是每步或每个时钟周期(或每个中断等)的相位超前量,因此更改该值可以更改频率而不会出现相位不连续的情况。然后,通过LUT或仅通过sinθ或cosθ的计算,从累积的相位值中确定波幅。
这实际上就是通常所说的数控振荡器(NCO)或直接数字合成器(DDS)。用这些术语进行网络搜索可能会产生比您想了解的更好的理论和实践知识。
如果需要的话,通过控制相位超前值的变化率,添加一个附加的累加器也可以允许在频率之间进行无缝转换,如您所建议的那样。有时称为数字差分分析仪或DDA。