7

編集:コードとプレーヤー(Github上)を少し整理して、頻度を設定しやすくしました

Karplus Strong 弦合成アルゴリズムを使用して弦を合成しようとしていますが、弦を正しくチューニングできません。誰にもアイデアはありますか?

上記のリンクのように、コードは Github にあります: https://github.com/achalddave/Audio-API-Frequency-Generator (関連するビットは にありますstrings.js)。

Wiki には次の図があります。

Karplus Strong String 合成図

基本的に、私はノイズを生成し、それが出力され、同時に遅延フィルターに送られます。遅延フィルタはローパス フィルタに接続され、出力と混合されます。ウィキペディアによると、遅延は N サンプルである必要があります。ここで、N はサンプリング周波数を基本周波数で割ったものです ( N = f_s/f_0)。

私のコードからの抜粋:

ノイズの生成 ( bufferSize2048 ですが、それほど重要ではありません)

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サンプル分遅延する必要があります。ただし、遅延ノードは遅延を秒単位で取るため、それを 1 秒あたりのサンプル数で割る必要があり(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 では、実際にはローパスと出力の間に Gain Node がありますが、実際には出力に大きな違いはありません。

4

1 に答える 1

8

ここが問題だと思います。DelayNodeこの実装は、このようなタイトなフィードバック ループを処理するようには設計されていないと思います。たとえば、441 Hz のトーンの場合、これはわずか 100 サンプルの遅延であり、DelayNode実装はおそらく 128 以上のブロックで入力を処理します。(このdelayTime属性は「k-rate」です。これは、変更が 128 サンプルのブロックでのみ処理されることを意味します。これは私の主張を証明するものではありませんが、それを示唆しています。) したがって、フィードバックが遅すぎるか、部分的にしか得られません。か何か。

編集/更新: 以下のコメントで述べているように、実際の問題は、サイクル内の DelayNode が出力と入力の間に 128 サンプル フレームを追加するため、観測される遅延が128 / sampleRate指定よりも長くなることです。

私のアドバイス (および私が始めたこと) は、独自の遅延ラインを含む Karplus-Strong 全体をJavaScriptNode(現在は として知られているScriptProcessorNode) に実装することです。難しいことではありません。おそらく存在するはずのない迷惑なバグを取り除いたら、コードを投稿します。

ちなみに、あなた (と私) がdelayTime( 1/440A であると思われる) の音で得られる音は G のように見えます。周波数を 2 倍にすると、4 半音高い B になります。(1 オクターブか 2 オクターブずれている可能性があります。わかりにくいです。) おそらく、このようなデータ ポイントをあと 2、3 点から (数学的に) 何が起こっているかを理解することができますが、気にする必要はありません。

編集:これが私のコードで、バグがないことが証明されています。

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 に答える