网络音频合成:如何处理在攻击或释放阶段更改滤波器截止?

Posted

技术标签:

【中文标题】网络音频合成:如何处理在攻击或释放阶段更改滤波器截止?【英文标题】:Web Audio synthesis: how to handle changing the filter cutoff during the attack or release phase? 【发布时间】:2015-05-03 21:32:49 【问题描述】:

我正在使用 WebAudio 构建 Roland Juno-106 合成器的仿真。 The live WIP version is here.

如果在起音或释放期间改变了截止频率或包络调制量,同时滤波器同时被包络调制,我对如何处理更新滤波器感到困惑。 That code is located around here。当前的实现没有像模拟合成器那样响应,但我不太清楚如何计算它。

在真正的合成器中,滤波器会立即发生变化,具体取决于频率截止、包络调制量和包络中的当前阶段,但上升或下降也会平稳地继续。

我将如何为这种行为建模?

【问题讨论】:

很棒的项目!我的 Juno-106 上的 VCA 快要死了……很高兴看到有人模拟,尤其是在浏览器中。 您能否在包络处于活动状态时调整包络的起音和释放时间而不会导致包络值跳跃? 是的——参见amp envelope here 和filter envelope here 的代码。基本上我使用超时来跟踪包络阶段,如果包络值在斜坡期间发生变化,我会更新包络值,然后重新触发noteOn/noteOff。平滑音频由 API 处理。您可以在当前的实时版本中听到这一点。不过,在预定的斜坡期间更新滤波器截止值并不那么简单。 【参考方案1】:

很棒的项目!

您不需要自己对这些求和 - Web Audio AudioParams 对它们的输入求和,因此如果您有一个潜在的音频速率调制源,例如 LFO(连接到 GainNode 的 OscillatorNode),您只需将其连接()到音频参数。

这是这里的关键 - AudioParams 能够被连接()到 - 并且对节点或 AudioParam 的多个输入连接求和。所以你一般想要一个

的模型
filter cutoff = (cutoff from envelope) + (cutoff from mod/LFO) + (cutoff from cutoff knob)

由于截止是一个频率,因此在对数刻度上不是线性频率,因此您希望以对数方式进行此加法(否则,在 440Hz 时将截止频率提高一个八度的包络只会在 880Hz 时将其提高半个八度等) - 幸运的是,通过 BiquadFilter 上的“detune”参数很容易做到这一点。

失谐以美分(1200/八度)为单位,因此您必须使用增益节点来调整值(例如,如果您希望调制具有 +1/-1 八度范围,请确保振荡器输出介于 - 1200 和 +1200)。您可以在我的 Web 音频合成器 (https://github.com/cwilso/midi-synth) 中看到我是如何做到这一点的:特别是从第 500 行开始查看 synth.js:https://github.com/cwilso/midi-synth/blob/master/js/synth.js#L497-L519。注意 modFilterGain.connect(this.filter1.detune);特别是。

您不希望直接为调制设置任何值,因为实际值可能会以较快的速度变化 - 您希望使用参数调度程序和来自 LFO 的输入求和。您可以根据需要根据时间设置旋钮值,但事实证明,设置 .value 与在同一 AudioParam 上设置预定值的交互效果不佳 - 因此您需要在 AudioParam 中单独(汇总)输入。这是一个棘手的问题,老实说,我的合成器今天做得不好(我应该将其更改为下面描述的方法)。

处理旋钮设置的正确方法是创建一个根据您的旋钮设置而变化的音频通道 - 也就是说,它是一个 AudioNode,您可以将其连接()到 filter.detune,尽管由此产生的样本值AudioNode 只是正数,只有在旋钮改变时才会改变值。为此,您需要一个 DC 偏移源 - 即产生恒定样本值流的 AudioNode。我能想到的最简单的方法是使用 AudioBufferSourceNode,生成的缓冲区为 1:

   function createDCOffset() 
    var buffer=audioContext.createBuffer(1,1,audioContext.sampleRate);
    var data = buffer.getChannelData(0);
    data[0]=1;
    var bufferSource=audioContext.createBufferSource();
    bufferSource.buffer=buffer;
    bufferSource.loop=true;
    bufferSource.start(0);
    return bufferSource;

然后,只需将该 DCOffset 连接到增益节点,并将您的“旋钮”连接到该增益的 .value 以使用增益节点来缩放值(请记住,一个八度音程中有 1200 美分,所以如果你想要你的旋钮代表六个八度的截止范围,.value 应该在零和 7200 之间)。然后将 DCOffsetGain 节点 connect() 到过滤器的 .detune 中(它与来自 LFO 的连接相加,而不是替换,并且还与 AudioParam 上的预定值相加(请记住,您需要以美分为单位缩放预定值, 也))。顺便说一句,这种方法也可以轻松翻转包络极性(Juno 106 上的 VCF ENV 开关) - 只需反转您在调度程序中设置的值。

希望这会有所帮助。我现在有点时差,所以希望这是清醒的。 :)

【讨论】:

以上是关于网络音频合成:如何处理在攻击或释放阶段更改滤波器截止?的主要内容,如果未能解决你的问题,请参考以下文章

Python Selenium - 如何处理在WebDriverWait完成之后才显示的警报?

如何处理在域模型中没有表示的查询?

如何处理在 php mysql 中返回多个值的内部查询?

GraphQL:如何处理在自己的类型定义中引用类型?

如何处理在 Angular CLI 中安装对等依赖项?

您如何处理在 IIS 上运行的网站的计划任务?