简体   繁体   English

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

[英]Web Audio synthesis: how to handle changing the filter cutoff during the attack or release phase?

I'm building an emulation of the Roland Juno-106 synthesizer using WebAudio. 我正在使用WebAudio构建Roland Juno-106合成器的仿真。 The live WIP version is here . 实时WIP版本在这里

I'm hung up on how to deal with updating the filter if the cutoff frequency or envelope modulation amount are changed during the attack or release while the filter is simultaneously being modulated by the envelope. 如果截止频率或包络调制量在攻击或释放期间发生变化,同时滤波器同时被包络调制,我就会挂断如何更新滤波器。 That code is located around here . 该代码位于此处 The current implementation doesn't respond the way an analog synth would, but I can't quite figure out how to calculate it. 当前的实现并没有像模拟合成器那样响应,但我无法弄清楚如何计算它。

On a real synth the filter changes immediately as determined by the frequency cutoff, envelope modulation amount, and current stage in the envelope, but the ramp up or down also continues smoothly. 在真正的合成器上,滤波器立即由包络中的频率截止,包络调制量和当前级确定,但是上升或下降也可以平滑地继续。

How would I model this behavior? 我该如何模拟这种行为?

Brilliant project! 精彩的项目!

You don't need to sum these yourself - Web Audio AudioParams sum their inputs, so if you have a potentially audio-rate modulation source like an LFO (an OscillatorNode connected to a GainNode), you simply connect() it to the AudioParam. 你不需要自己总结这些 - Web Audio AudioParams对它们的输入求和,所以如果你有一个潜在的音频速率调制源,如LFO(一个连接到GainNode的振荡器节点),你只需将它连接到AudioParam即可。

This is the key here - that AudioParams are able to be connect()ed to - and multiple input connections to a node or AudioParam are summed. 这是关键 - AudioParams能够连接()编辑 - 并且对节点或AudioParam的多个输入连接求和。 So you generally want a model of 所以你通常需要一个模型

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

Since cutoff is a frequency, and thus on a log scale not a linear one, you want to do this addition logarithmically (otherwise, an envelope that boosts the cutoff up an octave at 440Hz will only boost it half an octave at 880Hz, etc.) - which, luckily, is easy to do via the "detune" parameter on a BiquadFilter. 由于截止是一个频率,因此在对数标度而不是线性标度上,你想要以对数方式进行这种加法(否则,在440Hz处将截止频率提高一个八度音程的信封只能在880Hz处将其提高半个八度等等。 ) - 幸运的是,通过BiquadFilter上的“detune”参数很容易做到这一点。

Detune is in cents (1200/octave), so you have to use gain nodes to adjust values (eg if you want your modulation to have a +1/-1 octave range, make sure the oscillator output is going between -1200 and +1200). Detune以美分(1200 /倍频程)为单位,因此您必须使用增益节点来调整值(例如,如果您希望调制具有+ 1 / -1倍频程范围,请确保振荡器输出介于-1200和+之间1200)。 You can see how I do this bit in my Web Audio synthesizer ( https://github.com/cwilso/midi-synth ): in particular, check out synth.js starting around line 500: https://github.com/cwilso/midi-synth/blob/master/js/synth.js#L497-L519 . 您可以在我的Web音频合成器( https://github.com/cwilso/midi-synth )中看到我是如何做到这一点的:特别是从第500行开始查看synth.js: https//github.com/ cwilso / midi-synth / blob / master / js / synth.js#L497-L519 Note the modFilterGain.connect(this.filter1.detune); 注意modFilterGain.connect(this.filter1.detune); in particular. 特别是。

You don't want to be setting ANY values directly for modulation, since the actual value will change at a potentially fast rate - you want to use the parameter scheduler and input summing from an LFO. 您不希望直接为调制设置任何值,因为实际值将以可能快的速率变化 - 您希望使用参数调度程序并从LFO输入求和。 You can set the knob value as needed in terms of time, but it turns out that setting .value will interact poorly with setting scheduled values on the same AudioParam - so you'll need to have a separate (summed) input into the AudioParam. 您可以根据需要在时间方面设置旋钮值,但事实证明设置.value与在同一AudioParam上设置预定值的交互性很差 - 因此您需要在AudioParam中有一个单独的(求和)输入。 This is the tricky bit, and to be honest, my synth does NOT do this well today (I should change it to the approach described below). 这是一个棘手的问题,说实话,我的合成器今天做得不好(我应该把它改成下面描述的方法)。

The right way to handle the knob setting is to create an audio channel that varies based on your knob setting - that is, it's an AudioNode that you can connect() to the filter.detune, although the sample values produced by that AudioNode are only positive, and only change values when the knob is changed. 处理旋钮设置的正确方法是创建一个根据旋钮设置而变化的音频通道 - 也就是说,它是一个可以连接()到filter.detune的AudioNode,尽管该AudioNode生成的样本值仅为正,仅在更改旋钮时更改值。 To do this, you need a DC offset source - that is, an AudioNode that produces a stream of constant sample values. 要做到这一点,您需要一个DC偏移源 - 即一个产生常量样本值流的AudioNode。 The simplest way I can think of to do this is to use an AudioBufferSourceNode with a generated buffer of 1: 我能想到的最简单的方法是使用一个生成的缓冲区为1的AudioBufferSourceNode:

   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;
}

Then, just connect that DCOffset into a gain node, and connect your "knob" to that gain's .value to use the gain node to scale the values (remember, there are 1200 cents in an octave, so if you want your knob to represent a six-octave cutoff range, the .value should go between zero and 7200). 然后,只需将DCOffset连接到增益节点,并将“旋钮”连接到该增益的.value,使用增益节点来缩放值(请记住,八度音程中有1200美分,所以如果你想要你的旋钮代表一个六个八度音的截止范围,。值应该在0到7200之间。 Then connect() the DCOffsetGain node into the filter's .detune (it sums with, rather than replacing, the connection from the LFO, and also sums with the scheduled values on the AudioParam (remember you'll need to scale the scheduled values in cents, too)). 然后将DCOffsetGain节点连接()到过滤器的.detune(它与LFO的连接相加而不是替换,并且还与AudioParam上的预定值相加(请记住,您需要以美分来缩放计划值) ,也))。 This approach, BTW, makes it easy to flip the envelope polarity too (that VCF ENV switch on the Juno 106) - just invert the values you set in the scheduler. 这种方法BTW也可以轻松地翻转包络极性(Juno 106上的VCF ENV开关) - 只需反转您在调度程序中设置的值。

Hope this helps. 希望这可以帮助。 I'm a bit jetlagged at the moment, so hopefully this was lucid. 我此刻有点时差,所以希望这是清醒的。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM