【问题标题】:Efficient generation of sampled waveforms without aliasing artifacts高效生成采样波形,没有混叠伪影
【发布时间】:2021-06-30 18:09:54
【问题描述】:

对于我的一个项目,我正在处理采样声音生成,我需要创建各种频率的各种波形。当波形是正弦时,一切都很好,但当波形是矩形时,问题就来了:听起来好像是八十年代的,随着频率的增加,音符听起来就错了。在第 8 个八度音阶上,每个音符听起来都像是来自某个较低八度音阶的随机音符。

无论我使用以下两种方法中的任何一种,不良影响都是一样的:

  • 生成矩形波形的纯数学方法为sample = sign( secondsPerHalfWave - (timeSeconds % secondsPerWave) ) 其中secondsPerWave = 1.0 / wavesPerSecondsecondsPerHalfWave = secondsPerWave / 2.0

  • 我的首选方式是使用线段来描述波浪的一个周期并沿这些线进行插值。因此,矩形波形由在 y=1.0 处从 x=0 到 x=0.5 的水平线描述(不考虑采样率和频率),然后是在 y= 处从 x=0.5 到 x=1.0 的另一条水平线-1.0。

据我所知,文献认为这些波形生成方法“幼稚”,导致“混叠”,这是所有不良影响的原因。

当我查看生成的波形时,这一切实际上转化为每秒采样值不是每秒波形值的精确倍数,因此每个波形都没有偶数个样本,这反过来意味着1.0级的样本数往往不等于-1.0级的样本数。

我在这里找到了一个特定的解决方案:https://www.nayuki.io/page/band-limited-square-waves,它甚至包含 Java 源代码,听起来确实很棒:所有不良影响都消失了,每个音符听起来都很纯净且频率正确。但是,这个解决方案完全不适合我,因为它的计算成本非常高。 (即使我已经用比 Java 内置函数快十倍的近似值替换了 sin()cos()。)此外,当我查看生成的波形时,它们看起来非常复杂,所以我想知道它们是否可以合法地称为矩形。

所以,我的问题是:

什么是最计算效率最高的方法来生成周期性波形,例如不受混叠伪影影响的矩形波形?

解决方案可能需要的示例:

  1. 在我看来,以离散时间间隔生成正确样本值来描述声波的计算机音频问题与在离散整数 x 坐标处生成正确整数 y 坐标以绘制线条的计算机图形问题有些相关。 Bresenham 线生成算法非常有效,(即使我们暂时忽略它正在处理整数数学的事实),它通过累积某个错误项来工作,该错误项在正确的时间会导致Y坐标。是否可以使用一些类似的机制来计算样本值?

  2. 采样的工作方式被理解为在特定的无限窄时间点读取模拟信号的值。也许更好的方法是考虑读取最后一个样本和当前样本之间的整个模拟信号切片的区域。这样,在矩形波形边缘之前采样 1.0 对采样值的贡献很小,而在边缘之后相当长时间采样 -1.0 会贡献很多,因此自然会产生一个介于两个极值之间的点.这能解决问题吗?这样的算法存在吗?有人试过吗?

请注意,我在这里发布了这个问题,而不是 dsp.stackexchange.com,因为我不想收到带有诸如频带限制、谐波和低通滤波器、拉格朗日插值、直流补偿等荒谬行话的答案.而且我不想要来自纯模拟世界或纯理论外太空的答案,并且没有机会使用数字计算机获得实用且有效的实现。

我是一名程序员,而不是音响工程师,在我这个小程序员的世界里,事情很简单:我有一个样本数组,它们必须都在 -1.0 和 1.0 之间,并且将以一定的速率播放 (44100每秒采样数。)我可以使用算术运算和三角函数,我可以描述线条并使用简单的线性插值,并且我需要非常有效地生成样本,因为同时生成十几个波形并将它们混合在一起消耗的 CPU 时间不得超过总 CPU 时间的 1%。

【问题讨论】:

  • 由于这是一个 DSP 问题,我正准备推荐 dsp.stackexchange.com,直到我阅读了您避免它的原因。我仍然建议这是您的第一个停靠港。令人遗憾的是,社区似乎对行话有负面声誉,但是,如果你不问,你就得不到。您在这里缺少的一个关键术语是nyquist frequency,这是您获得的人工制品的来源。
  • 这听起来像是同样无用的行话,但您可能想要查看的一种特定算法是 PolyBLEP 。阅读Antialiasing Oscillators in Subtractive Synthesis Published in: IEEE Signal Processing Magazine ( Volume: 24, Issue: 2, March 2007)。我可以看一下将其消化成答案,尽管在这种情况下您可能希望为您的问题添加语言标签。
  • 我推荐 JUCE 论坛以及 DSP 的音乐实现 forum.juce.com
  • 也感谢 IEEE 文章的链接。不幸的是,他们要 33 美元给我看。我故意避免添加语言标签,因为我不想限制问题的受众。我对 Java、C#、C++、C、Scala 甚至 Pascal 都很满意。如果你愿意,我也可以来一个 stackoverflow 聊天频道,或者交换电子邮件,我的地址是 michael at michael dot gr。非常感谢您的回复。
  • 基本上任何over nyquist 都会反映导致额外伪影的原因。像完美方波这样的东西实际上是一个无限级数,所以根据定义,它不需要超过你的奈奎斯特。你可以应用一个过滤器来阻止它,只对有限数量的谐波 (resulting in gibbs) 或一些像 PolyBLEP 这样的诡计多端的游戏进行求和。奈奎斯特频率混叠应该为您产生更多结果。关于文章链接的道歉,我忘记了不是每个人都搞砸这些东西有时不是学术界。

标签: audio signal-processing sampling waveform


【解决方案1】:

我不确定,但您可能对别名的性质有一些误解。我将此基于您将术语放在引号中,并来自以下引用:

当我查看生成的内容时,这一切实际上转化为什么 波形是每秒采样值不是精确的倍数 每秒波数的值,因此每个波没有偶数 样本数,这反过来意味着在 1.0 级通常不等于-1.0 级的样本数。

样本/秒和波/秒根本不必是精确的倍数!可以播放低于奈奎斯特的所有音高。所以我不清楚你对此有何看法。

方波的特征声音来自奇次谐波的存在,例如,对于 440 (A5) 的音符,方波声音可以通过组合 440、1320、2200、3080、3960、等等以 880 的增量进行。这就引出了一个问题,有多少奇次谐波?从理论上讲,我们可以去无穷大,以获得方波上可能的最尖角。如果您只是在音频流中“绘制”它,则进程将继续远远超出 Nyquist 数。

但存在一个问题,高于奈奎斯特值的谐波无法以数字方式准确再现。尝试这样做会导致混叠。因此,要获得与系统能够产生的声音一样好的方波,就必须避免理论上完美方波中存在的高次谐波。

我认为最常见的解决方案是使用低通滤波算法。这些计算肯定比仅仅计算正弦波(或进行 FM 合成,这是我的主要兴趣)更占用 CPU。我对 DSP 的数学也很薄弱,并且担心 CPU 开销,因此很长一段时间都避免使用这种方法。但它是相当可行的,值得多看一看,恕我直言。

另一种方法是使用加法合成,并根据需要包含尽可能多的正弦谐波,以获得所需的音质。那么问题是你添加的谐波越多,你做的计算就越多。此外,必须跟踪高次谐波,因为它们限制了您可以演奏的最高音符。例如,如果使用 10 个谐波,则 500Hz 音符将包含 10500Hz 的内容。这低于 44100 fps(即 22050 Hz)的奈奎斯特值。但是,在谐波含量超过限制并开始混叠之前,您只能使用 10 次谐波提高另一个八度音阶(将所有内容加倍)等等。

您可能考虑的另一个解决方案是为您的方波创建一组查找表 (LUT),而不是即时计算多个正弦波。要在表中创建值,请遍历并添加正弦谐波中的值,这些值将在您使用给定表的范围内安全地保持在 Nyquist 之下。我认为一个包含 1024 个值的表格来编码单个周期可能是一个很好的初步猜测,因为它会起作用。

例如,我在猜测,但是八度音阶 C4-C5 的表可能使用 10 个谐波,C5-C6 的表只有 5 个,C3-C4 的表可能有 20 个。我不记得这是什么策略/技术被称为,但我记得它有一个名字,它是一种公认​​的处理情况的方式。根据过渡声音的方式和您想要的高端内容的数量,您可以使用更少或更多的 LUT。

可能还有其他方法可以考虑。 Aliasing 上的维基百科条目描述了一种它称为“带通”的技术,该技术似乎有意使用别名。我不知道那是关于什么的,或者它与你引用的文章有什么关系。

【讨论】:

  • 当我写“1.0级的样本数通常不等于-1.0级的样本数”时,我的意思是在相同频率的波序列中。这是导致混叠现象的实际物理缺陷,所有抗混叠技术都试图以不同程度的成功和不同的计算成本来准确地减轻这种情况。你可以在上面堆放尽可能多的理论和行话,但这就是归根结底的原因。
  • 我应该进一步限定这一点,因为它有点间接。实际的问题只是波长不能完全被样本数整除。 1.0 和 -1.0 的不同样本数量(使用简单方法生成波形时)是由此产生的结果,观察者在对波形进行故障排除时可能首先注意到它们。观察者可能会注意到的下一件事是,在相同频率的波中,从峰值到峰值的样本数量可能相差一个。
  • 这是一个有趣的论点,我想知道它在数学上是否正确。在我看来,当我们在一个波上获得相同数量的正负数据值时,可能会发生混叠,并且没有混叠的信号可能是不平衡的。我尽可能使用简单的语言。您是否能够遵循我所引用的逻辑,或者您是否立即将其驳回?
  • 可以以 44100 fps 的帧速率听到 440 Hz 的波,尽管它们之间的比率为 100.227272727。我没有按照你的逻辑。
  • 我想我明白你在说什么;我很想怀疑这样的信号是否存在,但我真的不知道。但我所知道的是,我所描述的锯齿完全符合图形中锯齿的概念:如果你尝试绘制相距 3.5 像素的平行线,就会出现锯齿。
【解决方案2】:

Soundpipe 库具有频率表的概念,它是一种数据结构,用于保存预计算的波形,例如正弦波。您可以使用所需的波形初始化频率表并通过oscilator 播放。甚至还有一个名为 oscmorph 的模块,它允许您在两个或多个波表之间进行变形。

这是一个如何生成正弦波的示例,取自 Soundpipe 的 documentation

int main() {
    UserData ud;
    sp_data *sp;
    sp_create(&sp);
    sp_ftbl_create(sp, &ud.ft, 2048);
    sp_osc_create(&ud.osc);

    sp_gen_sine(sp, ud.ft);
    sp_osc_init(sp, ud.osc, ud.ft);
    ud.osc->freq = 500;
    sp->len = 44100 * 5;
    sp_process(sp, &ud, write_osc);

    sp_ftbl_destroy(&ud.ft);
    sp_osc_destroy(&ud.osc);
    sp_destroy(&sp);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2019-09-05
    • 2020-02-01
    • 2013-09-25
    相关资源
    最近更新 更多