您的点击来自两个来源。波表和硬件。
如果你想创建一个简单的小乐器,通过回放波表来产生音调,那么没有办法固定“回放窗口”的大小来重现音调而无需点击。
组成半音阶的频率遵循几何级数,以 12 步实现频率加倍。每个音调的频率是通过将前一个音调乘以 1.05 来产生的。该因素由以下公式计算得出:让我们采用及其在处的八度音程。每个音调都是,所以。但是我们知道。因此,和AF=440HzAO=880HzAn+1=An∗rAO=AF⋅r12AO=AF⋅2r12=2r≈1.059463094359
因此,您可以将整齐地放入波表中,但现在需要个样本才能整齐地放入。不仅那里没有整数,而且即使您可以在整洁的窗口中重现音调,您也会遇到节奏问题,乐曲中每个音符的持续时间,因为比短。AF,AOAF♯≈1.0594AFAF♯
解决此问题的“经典”方法是设置单个正弦波的高分辨率波表,然后以“不同速度”读回,并带有插值。因此,以速度读回(实际上没有插值),但以速度读回,当“针”必须落在已知样本。AFr0=1AF♯r1≈1.0594
但!,这与你的输出流无关。换句话说,您必须设置(或采用)一个流系统,其中样本不断被推送到声卡,并且您具有“全局”时间感。
曾几何时,您通过设置三个指向三个缓冲区的指针(在 C、C++ 中)来做到这一点。我们称它们为。您将声卡设置为在中播放,在此过程中,您的主要代码正在处理缓冲区。当声卡调用其回调以表示录制结束时,您将“循环切换”缓冲区,以便它们现在指向并重新设置声音卡来做同样的工作,而你现在正在处理,现在是你新录制的H,D,LHLDL→D,D→H,H→silenceDH. 这些缓冲区有一个长度,例如 1000 个样本,然后任务变成了找到一个平衡缓冲区长度和响应性的最佳点。
但是正如你所看到的,在这个系统中,样本被连续不断地推送到声卡中。bufferLength样本,您并不真正关心正在播放的内容是否适合缓冲区。您所做的就是迭代您的模型(例如波表),生成样本缓冲区,冻结模型,播放该缓冲区并从顶部再次移动。
现在,显然,上述内容也包括来自音频源的实时输入。如果你所做的只是一个很小的合成器,你可以有两个缓冲区并在它们之间切换,在播放另一个的同时处理一个。
但是,即使您使用市场上一些现成的廉价声卡执行此操作,您仍然可以获得点击,尽管您已经非常努力地完美对齐块之间的零。这是因为所谓的延迟。
换句话说,只是因为你的程序调用了一些操作系统应用程序编程接口函数,上面写着“现在记录!” (或“立即播放!),这并不意味着此命令将通过驱动程序传播,最终音频硬件和录音将开始NOW。录音(和播放)将开始一些NOW!+latency单位时间后和现成的声卡,这曾经是一个可怕的时间。太可怕了,你可以听到通话之间的沉默。
所以,那里的解决方案是转移到像ASIO这样的东西,它最终将整个工作流程打包成一个完整的解决方案,从软件调用到硬件。
例如,一个 8 轨外部声卡可以在 256 个样本的缓冲区上进行 48kHz 立体声播放/录制(绝对没有咔嗒声)。这意味着,声音在持续大约 5 毫秒的块中进行处理。当它们分开超过 40 毫秒时,人耳开始分辨声音,所以这 5 毫秒是非常安全的。这意味着我们可以将电子乐器插入声卡并对其进行实时处理,就像它通过效果盒一样,表演者不会注意到他们演奏的内容和听到的内容之间的任何差异。
如果你试图让H,D,L256 的大小并尝试用现成的(相对流行的)声卡做同样的事情,首先你会 g/et//an/awf/ul/am//ount/of/cli/k/s //(每 5 毫秒一个)其次,驱动程序和操作系统会被大量的函数调用所淹没,很快您就会遇到 Seg Faults、蓝屏、系统冻结、repreprepreprepreprepreppreprepreprepreprepreprepreprepreprepreprepreprepreprepreprepeated waves out of synsynsynsynsynsynsynsynsynsynreprepresync 和其他有趣的副作用。
因此,最重要的是,继续在合成器的 DSP 方面工作,但如果您真的想摆脱咔嗒声,请确保您的声音硬件也能胜任这项任务。
希望这可以帮助。