【问题标题】:Web Audio: Karplus Strong String Synthesis网络音频:Karplus 强弦合成
【发布时间】:2012-10-20 14:58:26
【问题描述】:

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

我是使用Karplus Strong string synthesis 算法的trying to synthesize 字符串,但我无法正确调整字符串。有人有什么主意吗?

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

Wiki 有以下图表:

所以本质上,我生成了噪声,然后将其输出并同时发送到延迟滤波器。延迟滤波器连接到低通滤波器,然后与输出混合。根据维基百科,延迟应该是 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 上,实际上在低通和输出之间有一个增益节点,但这并不会真正对输出产生太大影响。

【问题讨论】:

  • 我只是在摆弄这个,我真的不知道我在做什么。但是尝试将频率设置为 241。在我的 Mac 上会产生一些奇怪的噪音。也许这告诉你一些事情?你似乎更精通数学和理论。 :)
  • 嗯,这很有趣。老实说,除了一门EE课程外,我对理论也不太熟悉,大部分都是拼凑和四处询问。不过,感谢您的帮助,如果我再四处寻找,这可能会提供一些见解。
  • 这可能不是问题,因为我认为 Lowpass 是默认设置,但您可能应该在代码中明确设置过滤器类型...类似于lowpassFilter.type = lowpassFilter.LOWPASS
  • 这可能会使代码更加明确。我会更新的。 (不幸的是,它并没有解决问题......,谢谢)
  • 这真是令人费解。起初我以为 Web Audio 延迟节点可能无法处理如此低的延迟时间,但如果是这样的话,应该有一个更高的水平,其中音高不再增加,但似乎并非如此。另一个奇怪的事情是音高在八度音阶上也不一致。 220、440 和 880 的音高在不同的八度音阶中不会产生相同的音符。这使我认为某处的计算可能有错误,但我看不到在哪里。

标签: javascript audio html5-audio web-audio-api


【解决方案1】:

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

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

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

顺便说一句,你(和我)用delayTime1/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);

【讨论】:

  • 这行得通吗?我明天试试。如果是这样,我不能夸大这有多棒(以及我多么感谢帮助)。我曾考虑将其实现为 JavascriptNode,但在我上次尝试另一个实验时遇到了性能问题。
  • 另外,“不可能存在的错误”是什么?其次,DelayNode 实现问题 bug 报告是否值得?如果是这样,我会麻烦你报告它(我自己会,但似乎你更了解它)并在此处发布链接。
  • 该错误没有在代码中保留对生成音频节点的引用。过于简单化:context.createJavaScriptNode().connect(context.destination) 没有保留对 JS 节点的引用,它会被垃圾收集。我认为这是一个错误,希望我会记得向 Chromium 提交。
  • 我不确定我是否会将 DelayNode 行为视为错误。也许它是一个文档/规范错误 - 规范可能应该指定延迟时间的限制,或者指定一种实现方式来表达一个。
猜你喜欢
  • 1970-01-01
  • 2017-12-17
  • 1970-01-01
  • 2020-11-27
  • 2014-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
相关资源
最近更新 更多