WebAudio中的WaveShaper节点-如何模拟失真效果?

20

正在使用WebAudio API并尝试添加失真效果!问题是,我不确定如何进入WaveShaper的“curve”参数。

简单来说,'oscidis'是程序先前创建的WaveShaper节点。Oscidisv是我目前静态设置为0的值。

const wsCurve = new Float32Array();

if (oscidisv >= -1 && oscidisv < 1) {
  const k = (2 * oscidisv) / (1 - oscidisv);
  for (let i = 0; i < 16; i += 1) {
    // LINEAR INTERPOLATION:
    // x = (c - a) * (z - y) / (b - a) + y
    // given
    // a = 0, b = 2048, z = 1, y = -1, c = i
    const x = ((i - 0) * (1 - -1)) / (16 - 0) + -1;
    wsCurve[i] = ((1 + k) * x) / (1 + k * Math.abs(x));
  }
}

oscidis.curve.value = wsCurve;

问题是 - 无论我放什么(-=),都听不到声音上的任何区别。即使失真调到最大(1),我也没有注意到任何真正的失真。你们知道有关更明显的失真波形塑造函数的信息吗?或者我在WebAudio API中的操作是否正确?

2个回答

47

以下是我使用过的一个基于多种白皮书和相关资料中发现的函数:

const DEG = Math.PI / 180;

function makeDistortionCurve(k = 50) {
  const n_samples = 44100;
  const curve = new Float32Array(n_samples);
  curve.forEach((_, i) => {
    const x = (i * 2) / n_samples - 1;
    curve[i] = ((3 + k) * x * 20 * DEG) / (Math.PI + k * Math.abs(x));
  });
  return curve;
}

如果我告诉你我知道 3 + k20 的来源,那我是在说谎 —— 但它确实有效。

amount 的值基本上可以是任何正数,但我发现在需要多少扭曲的情况下,0-100是一个相当不错的范围。

如果你有兴趣看看这些函数的样子,我建了一个小工具来帮助我将其可视化: http://kevincennis.github.io/transfergraph/


1
喜欢TransferGraph工具! 你可能也想要看一下正弦波通过该传递函数会是什么样子。 - cwilso
你真是太棒了。完全就是我要找的东西。现在我只想知道 - 我在哪里可以获得更多这些失真曲线方程?我想尝试一些不同的! - PinkElephantsOnParade
4
它们确实很难找。大多数声音效果算法都是专有的。像Amplitube、Line6等公司会为此投入大量时间和金钱,并需要保持竞争优势,因此很难找到好的信息。尽管真正高端的东西使用的技术比简单的波形整形器要复杂得多...但还是如此。我最好的建议是搜索一些关于“吉他放大器波形整形器”之类的内容,看看会出现什么结果。这就是我所做的。但是市面上真的没有太多选择。 - Kevin Ennis
8
如果你选择用弧度制的1弧度角(而不是20度),并使用PI代替神奇数字3,那么函数将穿过(-1,-1)和(1,1),并且该函数简化为(PI + k) * x / (PI + k * abs(x))。这个角度实际上只是一个输出缩放因子(也可以是1),而PI实际上只是一个输入缩放因子,这样就可以解决一些函数中的迷惑问题了。 - Grumdrig
4
好的。谢谢@Grumdrig。顺便说一下,对于吉他的失真效果,假设你想模拟放大器的声音,添加一个带有扬声器箱脉冲响应的“ConvolverNode”会很有帮助。如果你在谷歌上搜索一下,不难找到一些。否则,你得到的就像是将吉他插入失真效果器并直接连接到控制台,通常效果不太好。混响器将产生一种你实际上在移动空气的效果。 - Kevin Ennis
显示剩余2条评论

11

我采纳了上面一些建议并重构了函数。我还将类型数组中的样本数量从44K减少到256,这使得浏览器更加愉悦 :-()

let distortionFilter = audioCtx.createWaveShaper();
distortionFilter.curve = makeDistortionCurve();

function makeDistortionCurve(amount=20) {
    let n_samples = 256, curve = new Float32Array(n_samples);
    for (let i = 0 ; i < n_samples; ++i ) {
        let x = i * 2 / n_samples - 1;
        curve[i] = (Math.PI + amount) * x / (Math.PI + amount * Math.abs(x));
    }
    return curve;
} 

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