我有这张照片:http://bourt.com/color/slide.html。左键单击圆圈将产生声音。我希望在我松开鼠标按钮之前,点击次数保持恒定,并且随着我向上移动圆圈,使点击之间的间隔变小,直到点击形成音调为止。
我可以用简单的音调(http://bourt.com/color/slide1.html来获得所需的要点,但我希望LFO触发用于创建点击的信封。
但是我还不太清楚如何模仿用LFO触发信封的行为。据我了解,一种选择是使用AnalyserNode.getFloatTimeDomainData()
,查找峰值并相应安排点击次数。另一个办法是完全放弃LFO概念,并通过手动将单击添加到缓冲区中来将ScriptProcessor用作临时混合器。但这对于在概念上非常简单的事情来说实在是太糟糕了。
因此,我仍然希望可以使用振荡器以某种方式触发点击,并根据圆的位置改变该振荡器的频率。是吗?
(我知道The Tale Of Two Clocks,但是我认为这种安排不适用于滑块的上部,在这种上部,单击声非常接近,以至于产生声音。)
答案 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);