Tim*_*Tim 3 c++ algorithm math signal-processing
我正在使用Perry Cook的Synthesis Toolkit(STK)来生成锯齿和方波.STK包括这个基于BLIT的锯齿振荡器:
inline STKFloat BlitSaw::tick( void ) {
StkFloat tmp, denominator = sin( phase_ );
if ( fabs(denominator) <= std::numeric_limits<StkFloat>::epsilon() )
tmp = a_;
else {
tmp = sin( m_ * phase_ );
tmp /= p_ * denominator;
}
tmp += state_ - C2_;
state_ = tmp * 0.995;
phase_ += rate_;
if ( phase_ >= PI )
phase_ -= PI;
lastFrame_[0] = tmp;
return lastFrame_[0];
}
Run Code Online (Sandbox Code Playgroud)
方波振荡器大致相似.在顶部,有这样的评论:
// A fully optimized version of this code would replace the two sin
// calls with a pair of fast sin oscillators, for which stable fast
// two-multiply algorithms are well known.
Run Code Online (Sandbox Code Playgroud)
我不知道从哪里开始寻找这些"快速双乘算法",我很欣赏一些指针.我可以使用查找表,但我很想知道这些'快速振荡器'是什么.我也可以使用缩写的泰勒系列,但这可能超过两倍.搜索没有发现任何太多,虽然我确实发现了这个近似值:
#define AD_SIN(n) (n*(2.f- fabs(n)))
Run Code Online (Sandbox Code Playgroud)
将其绘制出来表明它实际上不是-1到1范围之外的近似值,因此我认为phase_在-pi到pi的范围内我不能使用它:

这里,正弦是蓝线,紫线是近似值.
对我的代码进行分析后发现,调用sin()是最耗时的调用,所以我真的很想优化这一部分.
谢谢
编辑感谢您详细而多样的答案.我将在周末探索这些并接受一个.
编辑2请匿名关闭选民请在评论中解释他们的投票吗?谢谢.
本质上,正弦振荡器是一个(或多个)变量,随着每个DSP步骤而变化,而不是从头开始重新计算.
最简单的是基于以下三角形标识:(哪里d是常数,因此是cos(d)和sin(d))
sin(x+d) = sin(x) cos(d) + cos(x) sin(d)
cos(x+d) = cos(x) cos(d) - sin(x) sin(d)
Run Code Online (Sandbox Code Playgroud)
然而,这需要两个变量(一个用于sin,一个用于cos)和4个乘法来更新.然而,这仍然比在每一步计算完整正弦要快得多.
Oli Charlesworth的解决方案基于这个通用方程的解决方案
A_{n+1} = a A_{n} + A_{n-1}
Run Code Online (Sandbox Code Playgroud)
寻找形式的解决方案A_n = k e^(i theta n)给出了方程式theta.
e^(i theta (n+1) ) = a e^(i theta n ) + b e^(i theta (n-1) )
Run Code Online (Sandbox Code Playgroud)
这简化为
e^(i theta) - e^(-i theta ) = a
2 cos(theta) = a
Run Code Online (Sandbox Code Playgroud)
给予
A_{n+1} = 2 cos(theta) A_{n} + A_{n-1}
Run Code Online (Sandbox Code Playgroud)
无论您使用哪种方法,您都需要为每个频率使用一个或两个这些振荡器,或者使用另一个触发标识来获得更高或更低的频率.