Web音频合成:如何处理在攻击或发布阶段更改过滤器关闭?

时间:2022-09-06 17:06:33

I'm building an emulation of the Roland Juno-106 synthesizer using WebAudio. The live WIP version is here.

我正在用WebAudio制作罗兰Juno-106合成器的仿真。live 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.

我很担心如何更新过滤器,如果在攻击或释放过程中截止频率或包络调制量发生了变化,而同时过滤也被包络调制。代码就在这附近。当前的实现没有像模拟synth那样响应,但是我不知道如何计算它。

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?

我该如何建模这种行为?

1 个解决方案

#1


10  

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音频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. So you generally want a model of

这是这里的关键——AudioParams可以被连接()到——对一个节点或AudioParam的多个输入连接进行求和。所以你通常想要一个模型

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.

因为截止频率,因此在对数尺度不是线性的,你想这么做除了对数(否则,信封,促进截止在440 hz只会增加一个八度半音阶在880赫兹,等等)——幸运的是,很容易通过BiquadFilter“降低”参数。

Detune is in cents (1200/octave), so you have to use gain nodes to adjust values (e.g. if you want your modulation to have a +1/-1 octave range, make sure the oscillator output is going between -1200 and +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. Note the modFilterGain.connect(this.filter1.detune); in particular.

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

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. 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. 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).

您不希望直接为调制设置任何值,因为实际值将以潜在的快速速度变化—您希望使用参数调度器并从LFO输入和。您可以根据时间的需要设置旋钮值,但事实证明,设置.value将与在同一个AudioParam上设置计划值产生很差的交互作用——因此您需要在AudioParam中有一个单独的(汇总的)输入。这是一个棘手的问题,老实说,我的synth今天做得不好(我应该把它改为下面描述的方法)。

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. To do this, you need a DC offset source - that is, an AudioNode that produces a stream of constant sample values. The simplest way I can think of to do this is to use an AudioBufferSourceNode with a generated buffer of 1:

处理旋钮设置的正确方式是创建一个音频通道,根据你的旋钮设置不同——也就是说,它是一个AudioNode filter.detune可以连接(),虽然产生的样本值,AudioNode只有积极,当旋钮改变,只有改变值。要做到这一点,您需要一个直流偏移量源——也就是说,一个产生恒定样本值流的音频ode。我能想到的最简单的方法是使用带有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). 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)). 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.

然后,连接DCOffset到获得节点,并连接“旋钮”获得的value使用获得节点规模的值(记住,有1200美分一个八度,所以如果你想让你的旋钮代表six-octave截止,. value应该在0和7200之间)。然后将()DCOffsetGain节点连接到过滤器的.detune中(它与来自LFO的连接进行求和,而不是替换),并与AudioParam上的计划值进行求和(请记住,您也需要将计划值按美分计算)。顺便说一句,这种方法使得翻转包络极性也很容易(在Juno 106上VCF ENV开关)——只需反转调度器中设置的值。

Hope this helps. I'm a bit jetlagged at the moment, so hopefully this was lucid. :)

希望这个有帮助。我现在有点落后了,所以希望这是清晰的。:)

#1


10  

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音频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. So you generally want a model of

这是这里的关键——AudioParams可以被连接()到——对一个节点或AudioParam的多个输入连接进行求和。所以你通常想要一个模型

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.

因为截止频率,因此在对数尺度不是线性的,你想这么做除了对数(否则,信封,促进截止在440 hz只会增加一个八度半音阶在880赫兹,等等)——幸运的是,很容易通过BiquadFilter“降低”参数。

Detune is in cents (1200/octave), so you have to use gain nodes to adjust values (e.g. if you want your modulation to have a +1/-1 octave range, make sure the oscillator output is going between -1200 and +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. Note the modFilterGain.connect(this.filter1.detune); in particular.

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

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. 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. 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).

您不希望直接为调制设置任何值,因为实际值将以潜在的快速速度变化—您希望使用参数调度器并从LFO输入和。您可以根据时间的需要设置旋钮值,但事实证明,设置.value将与在同一个AudioParam上设置计划值产生很差的交互作用——因此您需要在AudioParam中有一个单独的(汇总的)输入。这是一个棘手的问题,老实说,我的synth今天做得不好(我应该把它改为下面描述的方法)。

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. To do this, you need a DC offset source - that is, an AudioNode that produces a stream of constant sample values. The simplest way I can think of to do this is to use an AudioBufferSourceNode with a generated buffer of 1:

处理旋钮设置的正确方式是创建一个音频通道,根据你的旋钮设置不同——也就是说,它是一个AudioNode filter.detune可以连接(),虽然产生的样本值,AudioNode只有积极,当旋钮改变,只有改变值。要做到这一点,您需要一个直流偏移量源——也就是说,一个产生恒定样本值流的音频ode。我能想到的最简单的方法是使用带有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). 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)). 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.

然后,连接DCOffset到获得节点,并连接“旋钮”获得的value使用获得节点规模的值(记住,有1200美分一个八度,所以如果你想让你的旋钮代表six-octave截止,. value应该在0和7200之间)。然后将()DCOffsetGain节点连接到过滤器的.detune中(它与来自LFO的连接进行求和,而不是替换),并与AudioParam上的计划值进行求和(请记住,您也需要将计划值按美分计算)。顺便说一句,这种方法使得翻转包络极性也很容易(在Juno 106上VCF ENV开关)——只需反转调度器中设置的值。

Hope this helps. I'm a bit jetlagged at the moment, so hopefully this was lucid. :)

希望这个有帮助。我现在有点落后了,所以希望这是清晰的。:)