如何使用WebAudio API模仿模块化综合的VC触发行为?

时间:2019-05-20 12:39:06

标签: javascript web audio synthesis

我有这张照片:http://bourt.com/color/slide.html。左键单击圆圈将产生声音。我希望在我松开鼠标按钮之前,点击次数保持恒定,并且随着我向上移动圆圈,使点击之间的间隔变小,直到点击形成音调为止。

我可以用简单的音调(http://bourt.com/color/slide1.html来获得所需的要点,但我希望LFO触发用于创建点击的信封。

在VCV中很容易做到,这就是我制作原型的地方: enter image description here

但是我还不太清楚如何模仿用LFO触发信封的行为。据我了解,一种选择是使用AnalyserNode.getFloatTimeDomainData(),查找峰值并相应安排点击次数。另一个办法是完全放弃LFO概念,并通过手动将单击添加到缓冲区中来将ScriptProcessor用作临时混合器。但这对于在概念上非常简单的事情来说实在是太糟糕了。

因此,我仍然希望可以使用振荡器以某种方式触发点击,并根据圆的位置改变该振荡器的频率。是吗?

(我知道The Tale Of Two Clocks,但是我认为这种安排不适用于滑块的上部,在这种上部,单击声非常接近,以至于产生声音。)

2 个答案:

答案 0 :(得分:0)

使用逻辑电路(例如:基于LFO)确实很痛苦。我建议您使用基于setTimeout的方法来注册将来的点击,同时仍将声音时钟用作触发播放的参考。我曾经用JS构建了一个audiotracker,这是最直接的解决方案。我自己的代码很乱,但是我找到了一个很好的例子over here

答案 1 :(得分:0)

尝试了setTimeout()调度方法后,很明显,实现此目标的唯一方法是使用ScriptProcessorNode并将点击本身添加到音频缓冲区中。这类似于计划方法,但是所有样本都必须手动处理,这很痛苦,但这是我最终要做的。

对于那些好奇的人来说,这是代码的样子(重要部分):

var clickProc       = null;
var clickBuffer     = null;

var clickSample     = 0;
var remSamples      = 0;

var remBuffer       = null;
var remBufferLength = 0;


remBuffer = new Float32Array(2048);
clickProc = ac.createScriptProcessor(2048, 1, 1);


clickProc.onaudioprocess = function (e)
{
    var output = e.outputBuffer.getChannelData(0);
    var click  = clickBuffer.getChannelData(0);


    for (var i = 0; i < output.length; i++)
        output[i] = 0;


    if (remBufferLength > 0)
    {
        for (i = 0; i < remBufferLength; i++)
            output[i] = remBuffer[i];

        remBufferLength = 0;
    }


    for (i = 0; i < remBuffer.length; i++)
        remBuffer[i] = 0;


    if (clickSample >= output.length)
        clickSample -= output.length;


    while (clickSample < output.length)
    {
        var c;

        for (c = 0; c < click.length && clickSample < output.length; c++, clickSample++)
            output[clickSample] = Math.min(Math.max(0, output[clickSample] + click[c]), 1);

        remSamples = click.length - c;

        clickSample -= c;
        clickSample += Math.max(1, Math.floor(clickInterval * ac.sampleRate));


        if (c < click.length)
        {
            var remLength = click.length - c;

            for (i = 0; i < remLength; i++, c++)
                remBuffer[i] = Math.min(Math.max(0, remBuffer[i] + click[c]), 1);

            remBufferLength = Math.max(remBufferLength, remLength);
        }
    }
};

clickProc.connect(ac.destination);