Javascript 网络音频:Karplus强字符串合成

Javascript 网络音频:Karplus强字符串合成,javascript,audio,html5-audio,web-audio-api,Javascript,Audio,Html5 Audio,Web Audio Api,编辑:对代码和播放器(在Github上)进行了一些清理,以便更容易设置频率 我正在使用该算法调整字符串,但无法正确调整字符串。有人知道吗 如上所述,代码位于Github上:(相关位位于strings.js) Wiki具有以下图表: 所以本质上,我产生噪声,然后得到输出,同时发送到延迟滤波器。延迟滤波器连接到低通滤波器,然后与输出混合。根据维基百科,延迟应该是N个样本,其中N是采样频率除以基频(N=f_s/f_0) 我的代码摘录: 产生噪声(bufferSize为2048,但这不太重要) 我们需

编辑:对代码和播放器(在Github上)进行了一些清理,以便更容易设置频率

我正在使用该算法调整字符串,但无法正确调整字符串。有人知道吗

如上所述,代码位于Github上:(相关位位于
strings.js

Wiki具有以下图表:

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

我的代码摘录:

产生噪声(
bufferSize
为2048,但这不太重要)

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

var delaySeconds = 1/(frequency);
delayNode.delayTime.value = delaySeconds;
创建低通滤波器(据我所知,截止频率不应影响频率,更重要的是字符串是否“听起来”自然):

将噪波连接到输出和延迟节点(
destination=context.destination
且已在前面定义):

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

delayNode.connect(lowpassFilter);
将低通连接至输出并返回至延迟*:

lowpassFilter.connect(destination);
lowpassFilter.connect(delayNode);
有人有什么想法吗?我不知道问题是我的代码、我对算法的解释、我对API的理解,还是API本身的问题(尽管这种可能性很小)



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

我认为问题出在这里。我不认为
DelayNode
实现是为了处理如此紧密的反馈循环而设计的。例如,对于441 Hz的音调,只有100个延迟样本,
DelayNode
实现可能以128或更多的块处理其输入。(delayTime属性是“k-rate”,这意味着对它的更改只在128个样本的块中处理。这不能证明我的观点,但它暗示了这一点。)因此反馈来得太晚,或者只是部分,或者其他什么

编辑/更新:正如我在下面的注释中所述,实际问题是一个周期中的DelayNode在输出和输入之间添加了128个样本帧,因此观察到的延迟比指定的延迟长
128/sampleRate

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

顺便说一句,你(和我)在
1/440
delayTime
中得到的音调(应该是a)似乎是G,在它应该的位置下面有两个半音。将频率加倍会将其提升到a B,比B高四个半音。(我可能会偏离一到两个八度——有点难说。)也许人们可以从像这样的几个数据点(数学上)找出发生了什么,但我不想麻烦了

编辑:这是我的代码,经过认证无bug

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);
var context=新的webkitAudioContext();
无功频率=440;
var脉冲=0.001*context.sampleRate;
var node=context.createJavaScriptNode(4096,0,1);
var N=Math.round(context.sampleRate/频率);
var y=新的浮点数组(N);
var n=0;
node.onaudioprocess=函数(e){
var output=e.outputBuffer.getChannelData(0);
对于(变量i=0;i=0)?Math.random()-0.5:0;
输出[i]=y[n]=xn+(y[n]+y[(n+1)%n])/2;
如果(++n>=n)n=0;
}
}
连接(context.destination);

我只是在摆弄这个,我真的不知道自己在做什么。但是试着将频率设置为241。在我的Mac电脑上会产生一些奇怪的噪音。也许这告诉你了?你似乎对数学和理论更精通了嗯,那很有趣。老实说,除了一门EE课程外,我对这个理论也不太熟悉,所以大部分内容都是拼凑在一起,四处询问。不过,感谢您的帮助,如果我多看看,这可能会给我一些启示。这可能不是问题,因为我认为低通是默认值,但您可能应该在代码中显式设置过滤器类型。。。类似于
lowpassFilter.type=lowpassFilter.LOWPASS
。这可能会使代码更加明确。我会更新的。(不幸的是,它并没有解决这个问题…,不过,谢谢)这真是令人费解。起初,我认为网络音频延迟节点可能无法处理如此低的延迟时间,但如果是这样的话,应该有一个更高的级别,音高不再增加,但事实似乎并非如此。另一件奇怪的事是音高在八度上也不一致。220、440和880的音高在不同的八度音阶中不会产生相同的音符。这让我觉得计算中可能有错误,但我看不出在哪里。这行得通吗?我明天试试。如果真是这样的话,我再怎么强调也不为过,这是多么了不起(我多么感谢你的帮助)。我曾考虑将其实现为一个JavascriptNode,但在上次尝试另一个实验时遇到了性能问题。还有,“不可能存在的bug”是什么?第二,DelayNode实现问题bug报告值得吗?如果是这样的话,我会让你报告它(我自己也会,但你似乎更了解它)并在这里发布链接
bufferSource.connect(destination);
bufferSource.connect(delayNode);
delayNode.connect(lowpassFilter);
lowpassFilter.connect(destination);
lowpassFilter.connect(delayNode);
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);