连续波形音频合成器

7
我正在编写一款软件合成器,具有独特的特点:振荡器将拥有一个“连续波形”旋钮,允许用户以连续的方式选择正弦、方波和锯齿波。也就是说,如果旋钮全部向左,输出将是正弦波;如果在中间,输出将是锯齿波;如果全部向右,输出将是方波,而中间位置将输出“插值”的经典波形版本。-- 旋钮位置和波形类型可以更改,但希望有一种连续改变波形的方法 --

我想到了几种实现振荡器的方法:

  1. 设计一个函数,输入旋钮位置,计算实际信号的频谱(振幅和频率的数组),然后使用一系列正弦函数和求和块来实现输出信号。

  2. 与1类似,但是使用反向傅里叶变换而不是正弦函数和求和(此时我不确定它们是否实际上是相同的东西)。

  3. 为每个可能的旋钮位置生成波形表,并使用波表合成技术生成输出信号。

  4. 从2个锯齿波开始(它们都包含偶次和奇次谐波),反转其中一个并将它们相加,用旋钮控制每个波形的振幅。波形将不会

我有几个问题:

A. 我读到技术1非常处理器密集,不太可行。这对像iPad上的ARM处理器是否适用?

B. 无论我最终选择哪种技术,通过在振荡器输出处连接低通滤波器可以简单地解决混叠问题吗?

C. 有没有其他建议来实现这样的振荡器?

D. 有关使用哪个C++工具包的建议?我一直在研究CCMRA的STK,但我不知道是否有其他更合适的库。

祝我好运!;)

编辑:昨晚有人向我指出din。贝塞尔曲线是另一个要考虑的选项。


你是想生成波形的样本还是将它应用到另一个信号中?你是否考虑了特定的“插值”方法? - Dmitry Shkuropatsky
我想将其用作合成器的声音发生器。然后,我会应用过滤和一些效果来使声音更有趣。我考虑过的方法在1到4中描述,不确定你所说的“插值方法”是什么意思。 - Rafael Vega
哇!!感谢大家的回复,现在我有了一些作业 :) 首先我会从简单的开始:使用STK中的正方形、正弦和锯齿振荡器,并简单地在三种声音之间进行交叉淡入淡出,看看听起来如何。如果我仍然需要更复杂的东西,我会探索其他提供的建议。--- 在标记接受答案之前,我会再给它一点时间 --- - Rafael Vega
4个回答

5
我不确定你是否过于复杂化了这个问题。如果我理解正确,你的连续波形旋钮只是将三个波形以不同的比例混合在一起。因此,只需始终生成这三种波形,然后根据你所描述的波形混合比例,使用不同的增益将它们相加。
为了避免混叠,进行带限波形合成,你可能会发现大部分所需内容可以在这里找到。
希望这能有所帮助。

这就是我最终做的,一个简单的淡入淡出器确实起作用了! :) - Rafael Vega

3
这里有针对B的答案(将低通滤波器连接到输出是否可以解决混叠问题?),其中涉及到其他一些问题。
不幸的是,“不行”。混叠是由Nyquist频率(即采样率的一半)以上的谐波频率存在引起的。如果这些谐波存在于您的振荡器波形中,则滤波不能帮助解决该问题。(适当积极的滤波将破坏您生成的波形的特征。)过采样(另一个答案提到了这一点)可以实现,但它很昂贵。
为了避免这种情况,您必须生成“带限制”的波形。也就是说,没有超过某个选择值< Nyquist 的谐波的波形。这样做并不简单。这篇论文 这里 值得一读。解决此问题的两种已知且实用的方法:BLITs(带限脉冲列)和MinBLEPs。这两种方法都试图通过在波形的适当位置插入“内容”来平滑谐波产生的不连续性。
有了这个想法,您的选择开始缩小。可能在易用性和声音之间取得最佳平衡的方法是生成一系列带限制的波表。不过,您仍需要调查某种形式的抗混叠来处理插值波。
iDevice ARM 可以实时进行 DSP。总体建议:编写紧凑的代码、使用内联函数并避免除法。您的渲染循环将每秒调用 44,100 次,因此只要您的代码在 1/44100 秒(0.023ms)内完成,就不会有任何问题。事实上,您应该能够同时运行多个振荡器而没有任何问题。应用商店中所有这些音乐应用程序的存在都证明了这一点。
STK 是一个很棒的介绍性库。(Perry Cook 的书“Real-time Audio Synthesis for Interactive Applications”也是一个良好的基础,并值得一读。)STK 故意不进行优化,我不确定它是否适合生成您的“连续”波形。 kvraudio.commusicdsp.org 应该出现在您的阅读列表中。

只是想补充一下,使用解决方案1的话,预合成低通滤波器实际上会很有效,因为在对振荡器进行采样之前,你已经知道了所有振荡器的频率,所以你可以直接去除那些频率高于奈奎斯特频率的谐波。 - Thies Heidecke

1
A. 我已经阅读过技术1号非常占用处理器资源,不太可行。这对于像iPad上的ARM处理器是否成立?
这使得一个简单的问题变得复杂(双关语)。Accelerate.framework提供了这些函数的优化变体(fwiw),但它仍然会使一个简单的问题变得复杂。一般来说:设备上的浮点计算速度很慢。浮点实现可能会显著损害您的程序。这可能会导致功能、多声部或质量方面的妥协。如果不知道需求,很难说您是否可以使用浮点计算。
B. 无论我最终选择哪种技术,通过将低通滤波器连接到振荡器的输出端口可以简单地解决混叠问题吗?
这对于在时间域中生成的信号是行不通的,除非您进行过采样。
C. 如何实现这样的振荡器还有其他建议吗?
请参见下文
D. 建议使用哪个C++工具包?我一直在看CCRMA的STK,但我不知道是否有其他更合适的库。

STK更像是一个教学工具而不是为嵌入式合成器设计的工具包。存在更适合的实现。

选项1:编写一个函数,它接受旋钮位置并计算实际信号的频谱(一组振幅和频率数组),然后使用一堆正弦函数和求和块来实现输出信号。

选项2:与选项1类似,但应用反向傅里叶变换而不是正弦和求和(此时我不确定它们实际上是否相同)。

在桌面上相对较慢。

选项4:从两个锯齿波开始(它们都包含偶次和奇次谐波),反转一个并将它们相加,使用旋钮控制每个波的幅度。 波形不会

您可以通过BLIT非冲突生成来相当高效地完成这项工作。 但是,BLIT仅限于少数波形(您可以将其用于Saw和Square)。 您可以回顾历史并问:“2000年左右的硬件和软件合成器是如何解决这个问题的?” 这是其中一种解决方案,另一种解决方案是:

选项3。为每个可能的旋钮位置生成波形表,并使用波表合成技术生成输出信号。
考虑到设备的能力,我建议使用int实现此操作或BLIT。
该表易于理解和实现,并提供良好的声音和CPU结果。它还可以高度配置以进行CPU /内存/质量权衡。
如果您想要无混叠(或接近无混叠),请选择BLIT(或其相关产品)。原因是您需要大量的内存和足够的过采样才能最小化或不产生可听的混叠。
实施:
有许多在线BLIT(和家族)实现。
这里是一个关于表格的餐巾纸草图:
enum { WF_Sine, WF_Saw, WF_Square, WF_COUNT };
enum { TableSize = SomePowerOfTwo };

struct sc_waveform {
    uint32_t at[TableSize];
};

enum { NPitches = Something };

sc_waveform oscs[WF_COUNT][NPitches];

在初始化时,使用加性合成来填充oscs

在播放过程中,可以使用以下任一方法:

  • 插值和过采样从表格中读取
  • 或者对信号进行大量过采样,然后进行下采样(这样CPU效率更高)。

作为参考:我估计在不进行可听的高频分段并且以44.1kHz渲染的情况下,消耗不负责任数量内存的线性插值表格应将您的混叠频率保持在-40 dB或以下。这是一种朴素的蛮力方法!通过稍微努力,您可以做得更好。

最后,如果搜索“矢量合成”,您还应该找到相关信息--您所描述的是其原始形式。


+1 这是一个非常生动的答案,我从中学到了很多。 - Rafael Vega

1

傅里叶变换是线性的,因此对于例如方波和锯齿波的FFT进行交叉淡入每个谐波并线性地将其带回时间域,无论是通过iFFT还是求和正弦函数,都应该与直接交叉淡入锯齿波和方波信号的输出完全相同。我不确定这是否是您想要做的,但如果是,就没有必要进行FFT或计算中间表。

当然,还有许多其他平滑“淡入淡出”波形的方法 - 例如,您可以使用相位失真,其中失真曲线由线性段组成,您可以将其从生成方波的位置移动到生成锯齿波的位置。以一种固有地带宽限制的方式实现这一点可能非常棘手。

在实践中,过采样和滤波或仅滤波通常可以解决混叠问题。使用带限技术更好,因为混叠总会引起一些噪音,但您通常可以将其过滤到足够低,以使其在音频合成中无法听到,这才是最重要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接