我正在为微控制器编写音频发生器程序。
我使用硬件定时器触发中断,并检查是否需要在给定音符的特定时刻将信号设置为高或低。
我使用非常有限的硬件,所以我运行计时器的速度越慢,我就越需要做其他事情(串行通信,加载下一个要生成的音符等)。
我需要找到运行定时器以获得最佳结果的频率,即生成足够准确且仍有时间计算其他内容的频率。
为了达到这个目的,我需要找到一个近似值(在某个百分比值内,因为频率越高,他们需要对人耳的值不精确才能注意到错误)我需要的所有频率的LCM播放:此值将是运行硬件计时器的频率。
是否有足够简单的算法来计算这样的数字? (编辑,我将澄清“足够简单”:足够快以在时间t <1秒内运行,在8位AVR微控制器上少于50个值,并且在最坏情况下可在几十行中实现。)< / p>
答案 0 :(得分:1)
LCM(a,b,c) = LCM(LCM(a,b),c)
因此,您可以在循环中计算LCM,一次引入一个频率。
此外,
LCM(a,b) = a*b/GCD(a,b)
通过使用欧几里得算法,和GCD很容易计算而无需任何因子分析。
为了使这成为近似LCM的算法,可以将诸如将10 Hz和更高频率的倍数的较低频率舍入为50 Hz的倍数。另一个更有原则的想法是首先将频率转换为八度音(我认为公式为f
映射到log(f/16)/log(2)
)这将给出0到10之间的数字(或稍微高一点 - 但是超过10的任何东西几乎都超出了人类的听力,所以你可以向下舍入。您可以将0-10分为50个间隔0.0,0.2,0.4,......并且对于每个数字提前计算与该八度音程相对应的频率(f = 16*2^o
其中o
是八度)。对于这些中的每一个 - 一劳永逸地进行,并找到附近有一些小的素因子的圆数。例如,如果o = 5.4
然后f = 675.58
- 舍入到675; if o = 5.8
然后f = 891.44
- 舍入到890.将这50个数字组合成一个排序数组,使用二进制搜索将每个频率替换为数组中最接近的频率。
答案 1 :(得分:0)
一个想法:
让我们说你的频率范围从20到20000,你的目标是2%的准确度,你计算1-50范围。它必须是一个非线性变换,以保持较低频率的准确性。目标是更快地计算结果并使LCM更小。
将预先计算的素因子幂存储在一个数组中(范围1-50的大小约为50x7),然后将其用于LCM:数字的LCM是乘以每个素数因子的最高幂的乘积这个数字在一起。它易于编码且运行速度极快。