3

我正在为微控制器编写一个音调发生器程序。

我使用硬件定时器来触发中断并检查是否需要在给定音符的特定时刻将信号设置为高或低。

我使用的硬件非常有限,所以我运行计时器越慢,我做其他事情的时间就越多(串行通信,加载下一个要生成的音符等)。

我需要找到我应该运行计时器以获得最佳结果的频率,即生成一个足够准确的频率并且仍然有时间计算其他内容。

为了实现这一点,我需要找到我需要播放的所有频率的近似值(在某个百分比值内,因为频率越高,人耳注意到错误的值越不精确)LCM:该值将是运行硬件计时器的频率。

是否有足够简单的算法来计算这样的数字?(编辑,我将澄清“足够简单”:足够快,可以在 t << 1 秒内运行。在 8 位 AVR 微控制器上运行不到 50 个值,最坏的情况下可以在几十行中实现。)

4

2 回答 2

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^oo八度音阶的位置)。对于这些中的每一个 - 一劳永逸地手动检查并找到一个附近的整数,该整数具有许多小的质因数。例如,如果 o = 5.4然后f = 675.58——四舍五入到 675;如果o = 5.8然后f = 891.44- 舍入到 890。将这 50 个数字组合成一个排序数组,使用二进制搜索将每个频率替换为数组中最接近的频率。

于 2016-01-18T00:25:06.780 回答
0

一个主意:

  1. 将频率范围投影到更小的区间

假设您的频率范围是 20 到 20000,并且您的目标是 2% 的准确度,那么您将计算出 1-50 的范围。它必须是非线性变换以保持较低频率的准确性。目标是更快地计算结果并拥有更小的 LCM。

  1. 使用素因数表轻松计算缩小范围内的 LCM

将预先计算的素因数幂存储在一个数组中(大小约为 50x7,范围 1-50),然后将其用于 LCM:数字的 LCM 是该数字的每个素因数的最高幂的乘积一起。它易于编码且运行速度极快。

  1. 反向执行第一步以获得最终数字。
于 2016-01-17T23:05:46.047 回答