7

我开始编写一个具有特殊特性的软合成器:振荡器将有一个“连续波形”旋钮,允许用户以连续的方式选择正弦波、方波和锯齿波。也就是说,如果旋钮一直向左,则输出为正弦波,如果在中间,则为锯齿波,如果一直向右,则为方波然后中间位置将输出经典波的“插值”版本的波。-- 旋钮位置和波形类型可以改变,但需要一种连续的方式来改变波形 --

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

  1. 想出一个函数来获取旋钮位置并计算实际信号的频谱(幅度和频率的数组),然后使用一堆正弦函数和一个求和块来实现输出信号。

  2. 类似于 1. 但应用反向傅里叶变换而不是正弦和求和(好的,此时我不确定它们是否实际上是同一件事。)

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

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

我有几个问题:

A. 我读过技术 1 是处理器密集型的,而且不是真正可行的。这是否适用于 ARM 处理器,例如 iPad 上的处理器?

B. 无论我最终选择何种技术,是否可以简单地通过将低通滤波器连接到振荡器的输出端来解决混叠问题?

C. 关于如何实现这种振荡器的任何其他建议?

D. 关于使用哪个 C++ 工具包有什么建议吗?我一直在看 CCRMA 的 STK,但我不知道是否还有其他更合适的库。

祝我好运!;)

编辑:昨晚有人指点我吃饭。贝塞尔曲线是另一个需要考虑的选项。

4

4 回答 4

5

我不确定你是不是把这件事复杂化了。如果我理解正确,您使用连续波形旋钮所做的一切就是有效地将不同数量的 3 个波形混合在一起。因此,只需始终生成所有 3 个波形,然后根据您描述的波形混合将它们与不同的增益相加。

对于频带受限的波形合成以避免混叠,您可能会在此处找到所需的大部分内容。

希望有帮助。

于 2012-01-26T21:28:32.597 回答
3

这是 B 的答案(可以简单地通过将低通滤波器连接到输出来解决混叠问题吗?)它涉及其他一些点。

不幸的是,答案是否定的。混叠是由高于奈奎斯特频率(即采样率的一半)的谐波频率的存在引起的。如果这些谐波出现在振荡器的波形中,则滤波无济于事。(适当的激进过滤会破坏您生成的波的特征。)过采样(另一个答案提到了这一点)可以,但它很昂贵。

为避免这种情况,您必须生成“带限”波形。也就是说,没有高于某个选定值的谐波的波形 < Nyquist。这样做并非易事。这里的这篇论文值得一读。有两种既定的实用方法可以解决这个问题:BLITs(Band Limited Impulse Train)和 MinBLEPs。这两种方法都试图通过在波形的适当点插入“填充”来消除谐波产生的不连续性。

考虑到这一点,您的选择开始缩小。轻松和声音之间的最佳折衷可能是生成一系列频带限制波表。不过,您仍然需要研究某种形式的抗锯齿来处理插值波。

iDevice ARM 非常有能力实时执行 DSP。一般建议:编写紧凑的代码,使用内联函数并避免除法。您的渲染循环每秒将被调用 44,100 次,因此,只要您的代码在 1/44100 秒(0.023 毫秒)内完成,您就没有问题。在实践中,您应该能够同时运行多个振荡器而完全没有问题。应用商店中所有这些音乐应用的存在就是证明。

STK 是一个很棒的介绍库。(Perry Cook 的书“Real-time Audio Synthesis for Interactive Applications”也是一个很好的基础,值得一读。)虽然 STK 故意没有优化,我不确定它是否适合生成“连续”波形. kvraudio.commusicdsp.org应该在您的阅读列表中。

于 2012-01-26T21:32:32.073 回答
1

傅里叶变换是线性的,因此采用例如方波和锯齿波的 FFT 并对每个谐波进行线性交叉渐变,然后通过 iFFT 或正弦求和将其带回时域,应该给出与刚刚交叉渐变锯和方信号直接。我不确定这是否是您想要做的,但如果是,则无需进行 FFT 或计算中间表。

当然,还有许多其他方式可以在波形之间平滑“衰减” - 例如,您可以使用相位失真,使用由线性段组成的失真曲线,您可以从生成正方形的位置移动到生成锯齿的位置。以固有的频带限制的方式实现这可能非常棘手。

在实践中,混叠通常可以使用过采样和过滤或仅过滤来解决。使用频带限制技术会更好,因为混叠总是会导致一些噪音,但您通常可以将其过滤到足够低以听不见,这对音频合成很重要。

于 2012-01-26T21:20:14.670 回答
1

A. 我读过技术 1 是处理器密集型的,而且不是真正可行的。这是否适用于 ARM 处理器,例如 iPad 上的处理器?

这使得一个简单的问题变得复杂(双关语)。Accelerate.framework 提供了这些函数的优化变体(fwiw),但它仍然使一个简单的问题复杂化。作为一般说明:设备上的浮点计算很慢。浮点实现可能会大大损害您的程序。它可能会显着影响功能、复音或质量。在不知道要求的情况下,很难说您是否可以使用浮点计算。

B. 无论我最终选择何种技术,是否可以简单地通过将低通滤波器连接到振荡器的输出端来解决混叠问题?

除非您过采样,否则这不适用于时域中生成的信号。

C. 关于如何实现这种振荡器的任何其他建议?

见下文

D. 关于使用哪个 C++ 工具包有什么建议吗?我一直在看 CCRMA 的 STK,但我不知道是否还有其他更合适的库。

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

选项 1. 提出一个函数,该函数获取旋钮位置并计算实际信号的频谱(幅度和频率的数组),然后使用一堆正弦函数和一个求和块来实现输出信号。

选项 2。类似于 1。但应用反向傅立叶变换而不是正弦和求和(好的,此时我不确定它们是否实际上是同一件事。)

这在台式机上相对较慢。

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

对于无别名生成,您可以非常有效地执行此操作(例如,使用 BLIT)。但是,BLIT 仅限于少数波形(您可以将其用于 Saw 和 Square)。您可以回顾历史并询问“他们是如何在 2000 年左右的硬件和软件合成器中解决这个问题的”。这是一种解决方案。另一个是:

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

考虑到设备的功能,我推荐这个或 BLIT 的 int 实现。

该表易于理解和实施,并提供良好的声音和 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 高效的)。

供参考:我会估计一个表格的线性插值,它消耗了不负责任的内存量(考虑到可用的数量)而没有过采样,如果你不想听到最高分音,你的混叠频率应该保持在 -40 dB 或以下以 44.1kHz 渲染。这是一种天真的蛮力方法!你可以通过一些额外的工作做得更好。

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

于 2012-01-27T09:42:35.657 回答