7

编辑:稍微清理了代码和播放器(在 Github 上),以便更容易设置频率

我正在尝试使用Karplus Strong 字符串合成算法来合成字符串,但我无法正确调整字符串。有谁有想法吗?

如上链接,代码在 Github:https ://github.com/achalddave/Audio-API-Frequency-Generator (相关位在 中strings.js)。

Wiki 有以下图表:

Karplus 强弦合成图

所以本质上,我产生了噪声,然后将其输出并同时发送到延迟滤波器。延迟滤波器连接到低通滤波器,然后与输出混合。根据维基百科,延迟应该是 N 个样本,其中 N 是采样频率除以基频 ( N = f_s/f_0)。

我的代码摘录:

产生噪音(bufferSize是 2048,但这不应该太重要)

var buffer = context.createBuffer(1, bufferSize, context.sampleRate);
var bufferSource = context.createBufferSource();
bufferSource.buffer = buffer;

var bufferData = buffer.getChannelData(0);
for (var i = 0; i < delaySamples+1; i++) {
    bufferData[i] = 2*(Math.random()-0.5); // random noise from -1 to 1
}

创建延迟节点

var delayNode = context.createDelayNode();

我们需要延迟f_s/f_0样品。但是,延迟节点以秒为单位获取延迟,因此我们需要将其除以每秒样本数,我们得到(f_s/f_0) / f_s,即1/f_0

var delaySeconds = 1/(frequency);
delayNode.delayTime.value = delaySeconds;

创建低通滤波器(据我所知,截止频率不应该影响频率,更多的是字符串“听起来”是否自然):

var lowpassFilter = context.createBiquadFilter();
lowpassFilter.type = lowpassFilter.LOWPASS; // explicitly set type
lowpassFilter.frequency.value = 20000; // make things sound better

将噪声连接到输出和延迟节点(destination = context.destination之前已定义):

bufferSource.connect(destination);
bufferSource.connect(delayNode);

将延迟连接到低通滤波器:

delayNode.connect(lowpassFilter);

将低通连接到输出并返回延迟*:

lowpassFilter.connect(destination);
lowpassFilter.connect(delayNode);

有没有人有任何想法?我不知道问题是我的代码、我对算法的解释、我对 API 的理解,还是(尽管这不太可能)是 API 本身的问题。


*请注意,在 Github 上,实际上在低通和输出之间有一个增益节点,但这对输出并没有太大的影响。

4

1 回答 1

8

这就是我认为的问题所在。我不认为该DelayNode实现旨在处理如此紧密的反馈循环。例如,对于 441 Hz 音调,这只有 100 个延迟样本,并且DelayNode实现可能以 128 个或更多块的形式处理其输入。delayTime属性是“k-rate”,意味着对它的更改仅在 128 个样本的块中进行处理。这并不能证明我的观点,但它暗示了这一点。)所以反馈来得太晚了,或者只是部分地,或者其他的东西。

编辑/更新:正如我在下面的评论中所说,实际问题是一个循环中的延迟节点在输出和输入之间添加了 128 个样本帧,因此观察到的延迟128 / sampleRate比指定的长几秒。

我的建议(以及我已经开始做的)是实施整个 Karplus-Strong,包括您自己的延迟线 a JavaScriptNode(现在称为 a ScriptProcessorNode)。这并不难,一旦我摆脱了一个不可能存在但不知何故存在的恼人错误,我将发布我的代码。

顺便说一句,你(和我)用delayTimeof 1/440(应该是A)得到的音调似乎是G,比它应该的低两个半音。将频率加倍将其提高到 B,高四个半音。(我可能会偏离一个或两个八度音阶 - 很难说。)也许人们可以从像这样的更多数据点中弄清楚(数学上)发生了什么,但我不会打扰。

编辑:这是我的代码,经认证无错误。

var context = new webkitAudioContext();

var frequency = 440;
var impulse = 0.001 * context.sampleRate;

var node = context.createJavaScriptNode(4096, 0, 1);
var N = Math.round(context.sampleRate / frequency);
var y = new Float32Array(N);
var n = 0;
node.onaudioprocess = function (e) {
  var output = e.outputBuffer.getChannelData(0);
  for (var i = 0; i < e.outputBuffer.length; ++i) {
    var xn = (--impulse >= 0) ? Math.random()-0.5 : 0;
    output[i] = y[n] = xn + (y[n] + y[(n + 1) % N]) / 2;
    if (++n >= N) n = 0;
  }
}

node.connect(context.destination);
于 2013-01-23T19:39:09.083 回答