【问题标题】:Resonance algorithm to detect pitches检测音高的共振算法
【发布时间】:2010-11-01 11:32:56
【问题描述】:

我一直在研究检测传入麦克风的音调的不同方法。

看到我想知道它与特定音高的共振有多紧密,我想知道我是否可以做某种基于物理的共振算法。

如果您按住钢琴踏板并在其中唱出一个音调,(并且如果您足够接近其现有音高之一)一个音符会产生共鸣。

我希望能够模拟这种行为。但是我将如何完成任务呢?谁能帮我推进这件事?

【问题讨论】:

  • 要隔离输入频率,您需要傅里叶分解。你知道怎么做吗?只是不想发布对您来说显而易见的东西!
  • 此时对我来说没有什么太明显了,所以请开火!
  • stackoverflow.com/questions/3438429/… 此线程链接到包含有效解决问题的源代码的文章!

标签: algorithm detection pitch


【解决方案1】:

【讨论】:

    【解决方案2】:

    我发现一个有趣的解决方案是简单地将麦克风输入输入到 Karplus Strong 算法中。

    所以 Karplus Strong 通过以下方式模拟弹拨弦:

    • 创建一个循环缓冲区(如果我们以 44.1 kHz 进行采样并且我们希望模拟中间的 A 即 440Hz 的 A4,那么我们的缓冲区大小将是 ~101 个元素)
    • 在 -1 和 1 之间充满静电
    • 走一圈,每次将电流值设置为前两次的平均值,(并向扬声器发射电流值)
    • 可以添加阻尼常数

    现在如果我们将麦克风流添加到这个过程中,那么:

    x = ( ringBuf[prev] + ring theBuf[prev2] ) * 0.5 * 0.998;
    micOut[moPtr++] = x;
    ringBuf[curr] = x + micIn[miPtr++];
    

    它实际上非常准确地模拟了用吉他唱歌。如果你找到了你的音点,它真的会哭泣。

    但是这种方法存在一个严重的问题:考虑由 100 个元素的缓冲区生成的音高,以及由 101 个元素的缓冲区生成的音高。没有办法在这两个值之间产生任何音调。我们仅限于一个离散的工作集音调。虽然这对于低音符来说非常准确(A2 的缓冲区长度约为 400),但我们走得越高,错误就越多:A7 的缓冲区长度约为 12.5。这个错误可能超过一个半音。

    我看不出有什么办法可以解决这个问题。我认为必须放弃这种方法。

    【讨论】:

      【解决方案3】:

      完全基于离散傅立叶变换 (DFT) 的算法有许多缺点。 一个问题是时间分辨率,因为 DFT 对窗口内的样本起作用,您无法确定该窗口内的音高变化。 另一个问题是 DFT 的离散对数频率分辨率对于音高检测器可能不够好。毕竟 DFT 只能找到窗口大小为整数波长的波。

      稍微高级的算法可以做这样的事情:

      1. 粗略检测音高频率(可以使用 DFT 完成)。
      2. 带通信号过滤隔离音调频率。
      3. 计算过滤信号中两个峰值之间的样本数。

      通过计算样本数,您可以获得与样本频率相匹配的音高分辨率。 如果您想要比采样频率更高的分辨率,您可以将函数(例如多项式)拟合到峰值点周围的样本。既然你已经抑制了其他频率,你应该可以做到。

      正如另一个答案所暗示的,您还可以使用自相关来查找信号中的最大信号重复。但是我应该说,实现一个好的自相关音高检测器并非易事。在不知道的情况下,我会假设吉他调音器和类似的廉价电子设备将他们的算法基于带滤波器并结合计算峰值之间的样本距离。

      【讨论】:

      • 您能解释一下第 2 步吗?如何实现带通滤波器?
      【解决方案4】:

      您可以使用阻尼谐波振荡器,将输入作为驱动力。选择振荡器的参数,使其谐振频率与您想要的频率相匹配。

      您会在大多数关于力学的理论物理书籍中找到对阻尼谐振子的分析。

      【讨论】:

        【解决方案5】:

        我发现一种有用的方法是生成两个相隔 90 度的参考波(我称它们为“正弦”和“余弦”),然后在相当短的时间内将输入波形与这些参考波的点积(比如 1/60 秒)输入的延伸。这将为您提供一个有点嘈杂的指标,表明您有多少输入频率与您的参考波同相或异相(使用两个参考波生成的值的平方和的平方根将是幅度)。如果窗口尺寸较小,您会注意到输出相当嘈杂,但如果您使用简单的 FIR 或 IIR 滤波器等过滤输出,您可能会得到相当合理的结果。

        一个很好的技巧是生成两个幅度数:对于第一个,通过两轮滤波运行正弦和余弦幅度,然后计算平方和。第二,通过一轮滤波运行幅度,然后计算平方和,然后通过另一轮滤波运行。

        两个幅度测量都会经历相同的延迟,但第一个比第二个更具选择性;因此,您可以非常清楚地判断频率是“正确开启”还是有点偏离。使用这种方法,可以快速检测 DTMF 音调,同时拒绝甚至偏离几赫兹的音调(偏离音调的音调在“松散”检测器上的接收比在紧密检测器上更强)。

        示例代码:

        双正弦相位,正弦频率; void process_some_waves(double *input, int16 len, 双*正弦相位,双正弦频率, 双 *sine_result, 双 *cosine_result) { 诠释我; 双相,sin_tot,cos_tot; 相位 = *正弦相位; sin_tot = cos_tot = 0; 对于 (i=0; len > i; i++) { sin_tot += 输入[i] * sin(相位); cos_tot += 输入[i] * cos(相位); 相位 += 正弦频率; } *sine_result = sin_tot; *cosine_result = cos_tot; *sine_phase = 相位; } /* 获取缓冲区中的第一个元素,并使用简单的高斯 resp 将其“涂抹”通过缓冲区。 */ void simple_fir_filter(double *buff, int buffsize) { 诠释我; 对于 (i=buffsize-1; i>=2; i--) buff[i] = (buff[i-1] + buff[i-2])/2; } #define FILTER_SIZE1 10 #define FILTER_SIZE2 8 #define SECTION_LENGTH 128 #define FREQ 随便 双 sine_buff1[FILTER_SIZE1], sine_buff2[FILTER_SIZE2]; 双 cos_buff1[FILTER_SIZE1], cos_buff2[FILTER_SIZE2]; 双组合_buff[FILTER_SIZE2]; 双紧振幅,松振幅; 双参考相位; 无效句柄_some_data(双*输入) { /* 将结果放入过滤器缓冲区的第一个元素 */ process_some_waves(输入, SECTION_LENGTH, &ref_phase, FREQ, sine_buff1, cos_buff1); /* 运行第一阶段过滤 */ simple_fir_filter(sine_buff1, FILTER_SIZE1); simple_fir_filter(cosine_buff1, FILTER_SIZE1); /* 每个数组的最后一个元素将保存过滤结果。 */ /* 现在做第二阶段 */ sine_buff2[0] = sine_buff1[FILTER_SIZE1-1]; cosine_buff2[0] = cosine_buff1[FILTER_SIZE1-1]; 组合_buff[0] = sine_buff2[0]*sine_buff2[0] + cosine_buff2[0]*cosine_buff2[0]; simple_fir_filter(sine_buff2, FILTER_SIZE2); simple_fir_filter(cosine_buff2, FILTER_SIZE2); simple_fir_filter(combined_buff, FILTER_SIZE2); 紧振幅 = sine_buff2[FILTER_SIZE2-1]*sine_buff2[FILTER_SIZE2-1] + cosine_buff2[FILTER_SIZE2-1]*cosine_buff2[FILTER_SIZE2-1]; 松散幅度=组合_buff2 [过滤器尺寸2-1]; }

        这里的代码对除数组下标之外的所有数学运算都使用“double”。在实践中,用整数数学代替一些数学几乎肯定会更快。在具有浮点的机器上,我希望最好的方法是将相位保持为 32 位整数并使用约 4096 个“单”正弦值的表(RAM 中的表大小越小,缓存一致性越好表现)。我在定点(整数)DSP上使用了非常类似于上面的代码,并取得了巨大的成功; process_some_waves 中的正弦和余弦计算是在单独的“循环”中完成的,每个“循环”都被实现为带有“重复”前缀的单个指令。

        【讨论】:

          【解决方案6】:

          我一直在阅读傅立叶分析。

          基本上,如果您想从信号中提取频率 f,只需输入正弦波频率 f,将其与原始信号相乘,然后积分

          如果原始信号不包含任何频率为 f 的信号,您应该得到几乎为零。如果确实如此,那么您将测量该频率下信号中的能量。

          虽然它背后有一些相当棘手的数学运算,但直观地说是有道理的:只要看看它,信号中处于频率 f 的所有东西都会对正弦波产生建设性干扰,留下残留物;不在频率 f 的所有事物本质上都可以被视为随机噪声,(即,在与我们的正弦波相乘时,零以上的东西几乎相同数量)没有净效应。一切都取消了。

          这与我的钓鱼目标有关。为了完成我上面的类比:要检查钢琴包含哪些音符,您只需按住踏板并在其中唱一个上升的音调,每当发生共鸣共鸣时,您就可以记下钢琴在该频率上有一个音符。

          当然,这并非没有缺点:如果您按住 C1(这次没有踏板)并唱/弹 C2,C1 将以两倍的基本频率共振,产生 C2 声音。

          类似地弹奏 G2 会使其以三倍的基本频率等共振

          【讨论】:

          • 傅立叶变换的主要问题是它发生在固定块上。并且仅支持基频整数倍的频率。这是否可以取决于您的确切要求。
          • 我注意到了这一点。因此,如果我将傅立叶变换的基频设置为 A0,它将检测所有 A,以及少数其他音符,例如 E2、B4,它们恰好是 A0 的谐波。 (尽管在现代调音系统中,它只会敲定 A 的八度音阶;其他音阶会稍显逊色)。无论我将基本频率设置为什么,我总是会冒着风险,即某个特定的音符正好位于两个箱之间的边界上。在这种情况下会发生什么?每个 Bin 中是否有一半的音符能量?还是这个信号完全丢失了?
          猜你喜欢
          • 2013-12-10
          • 1970-01-01
          • 2013-04-20
          • 2011-08-27
          • 1970-01-01
          • 2017-05-01
          • 2011-09-17
          • 2018-11-09
          • 2015-12-12
          相关资源
          最近更新 更多