0

私は、マイクに向かって歌われるトーンのピッチを検出するさまざまな方法を検討してきました。

特定のピッチ クラスにどの程度共鳴するかを知りたいので、何らかの物理ベースの共鳴アルゴリズムを実行できないかと考えています。

ピアノのサステイン ペダルを押し下げてトーンを歌った場合 (既存のピッチの 1 つに十分近い場合)、ノートは共鳴して共鳴します。

この動作をモデル化できるようになりたいと思います。しかし、どうすればその仕事に取り掛かることができるでしょうか?これを前進させるのを手伝ってくれる人はいますか?

4

6 に答える 6

2

自己相関関数を見てください。

于 2010-11-01T11:35:05.327 に答える
2

私が見つけた興味深い解決策の 1 つは、マイク入力を Karplus Strong アルゴリズムに単純にフィードすることです。

したがって、Karplus Strong は、次のようにして、弾かれた弦をシミュレートします。

  • 循環バッファーの作成 (44.1 kHz でサンプリングしていて、中央の A、つまり 440 Hz の A4 をシミュレートしたい場合、バッファー サイズは ~101 要素になります)
  • -1 から 1 の間の静電気でいっぱいにする
  • 円を歩き、その都度、現在の値を前の 2 つの平均に設定します (そして現在の値をスピーカーに発します)。
  • 減衰定数を追加できます

このプロセスにマイク ストリームを追加すると、次のようになります。

x = ( ringBuf[prev] + ring theBuf[prev2] ) * 0.5 * 0.998;
micOut[moPtr++] = x;
ringBuf[curr] = x + micIn[miPtr++];

実際、ギターへの歌唱を非常に正確にシミュレートします。トーンスポットをオンにすると、本当に泣きます。

しかし、このアプローチには重大な問題があります。100 要素のバッファーによって生成されるピッチと、101 要素のバッファーによって生成されるピッチを考えてみてください。これら 2 つの値の間にピッチを生成する方法はありません。ピッチの個別の作業セットに制限されています。これは低音 (A2 のバッファー長は ~400) ではかなり正確ですが、高くなるほど誤差が大きくなります: A7 のバッファー長は ~12.5 になります。その誤差はおそらく半音以上です。

この問題に対抗する方法がわかりません。アプローチはやめるべきだと思います。

于 2010-11-17T20:45:42.213 に答える
1

私が見つけた 1 つのアプローチは、90 度離れた 2 つの基準波 (私はそれらを「正弦波」と「余弦波」と呼んでいます) を生成し、入力波形とこれらの基準波の内積をかなり短い間 (たとえば 1 /60 秒) 入力のストレッチ。これにより、参照波に関して同相または逆相である入力周波数の量のややノイズの多い指標が得られます(2つの参照波を使用して生成された値の2乗和の平方根は、振幅です)。ウィンドウ サイズが小さいと、出力にノイズが多いことがわかりますが、単純な FIR または IIR フィルターなどで出力をフィルター処理すると、かなり合理的な結果が得られるはずです。

優れたトリックの 1 つは、2 つの振幅数を生成することです。最初の数値については、正弦波と余弦波の振幅を 2 ​​回のフィルター処理で実行し、平方和を計算します。2 つ目は、1 ラウンドのフィルタリングで振幅を実行し、次に平方和を計算してから、別のラウンドのフィルタリングで実行します。

どちらの振幅測定でも同じ遅延が発生しますが、最初のものは 2 番目のものよりもはるかに選択的です。したがって、周波数が「正しい」か「少しずれている」かを非常に明確に判断できます。このアプローチを使用すると、DTMF トーンをすばやく検出しながら、数 Hz ずれているトーンも拒否できます (オフピッチ トーンは、タイトな検出器よりも「ルーズな」検出器ではるかに強くピックアップされます)。

サンプルコード:

ダブル sine_phase,sine_freq;
void process_some_waves(double *input, int16 len,
  double *sine_phase、double sine_freq、
  double *sine_result, double *cosine_result)
{
  int i;
  二相、sin_tot、cos_tot;
  位相 = *sine_phase;
  sin_tot = cos_tot = 0;
  for (i=0; 長さ > i; i++)
  {
    sin_tot += 入力[i] * sin(フェーズ);
    cos_tot += 入力[i] * cos(フェーズ);
    位相 += sine_freq;
  }
  *sine_result = sin_tot;
  *cosine_result = cos_tot;
  *sine_phase = 位相;
}

/* バッファ内の最初の要素を取得し、単純なガウス応答でバッファを「塗りつぶす」。*/
void simple_fir_filter(double *buff, int buffsize)
{
  int i;

  for (i=buffsize-1; i>=2; i--)
    buff[i] = (buff[i-1] + buff[i-2])/2;
}

#define FILTER_SIZE1 10
#define FILTER_SIZE2 8
#SECTION_LENGTH 128 を定義
#なんでも FREQ を定義

double sine_buff1[FILTER_SIZE1], sine_buff2[FILTER_SIZE2];
double cos_buff1[FILTER_SIZE1], cos_buff2[FILTER_SIZE2];
double combined_buff[FILTER_SIZE2];
ダブル tight_amplitude、loose_amplitude;
ダブルref_phase;

void handle_some_data(double *input)
{
  /* フィルター バッファーの最初の要素に結果を格納します */
  process_some_waves(input, SECTION_LENGTH, &ref_phase, FREQ, sine_buff1, cos_buff1);

  /* フィルタリングの最初の段階を実行します */

  simple_fir_filter(sine_buff1, FILTER_SIZE1);
  simple_fir_filter(cosine_buff1, FILTER_SIZE1);

  /* 各配列の最後の要素は、フィルタリングの結果を保持します。*/
  /* 第 2 段階を実行します */

  sine_buff2[0] = sine_buff1[FILTER_SIZE1-1];
  cosine_buff2[0] = cosine_buff1[FILTER_SIZE1-1];
  結合された_buff[0] = sine_buff2[0]*sine_buff2[0] + cosine_buff2[0]*cosine_buff2[0];
  simple_fir_filter(sine_buff2, FILTER_SIZE2);
  simple_fir_filter(cosine_buff2, FILTER_SIZE2);
  simple_fir_filter(combined_buff, FILTER_SIZE2);

  タイトな振幅 = sine_buff2[FILTER_SIZE2-1]*sine_buff2[FILTER_SIZE2-1] +
                    cosine_buff2[FILTER_SIZE2-1]*cosine_buff2[FILTER_SIZE2-1];
  Loose_amplitude = combined_buff2[FILTER_SIZE2-1];
}

ここのコードでは、配列の添え字以外のすべての計算に「double」を使用しています。実際には、数学の一部を整数演算に置き換える方がほぼ確実に高速です。浮動小数点を使用するマシンでは、位相を 32 ビット整数として保持し、〜 4096 の「単一」サイン値のテーブルを使用するのが最善の方法だと思います (RAM 内のテーブル サイズが小さいほど、キャッシュの一貫性が向上します)。パフォーマンス)。上記のようなコードを固定小数点 (整数) DSP で使用すると、大きな成功を収めました。process_some_waves の正弦計算と余弦計算は別々の「ループ」で行われ、各「ループ」は「繰り返し」プレフィックスを持つ単一の命令として実現されました。

于 2010-11-17T00:27:27.803 に答える
1

完全に離散フーリエ変換 (DFT) に基づくアルゴリズムには、多くの欠点があります。問題の 1 つは時間分解能です。DFT はウィンドウ内のサンプルに作用するため、そのウィンドウ内のピッチの変化を特定することはできません。もう 1 つの問題は、DFT の離散対数周波数分解能であり、ピッチ検出器には十分ではない可能性があります。結局のところ、DFT はウィンドウ サイズの整数波長の波のみを検出します。

少し高度なアルゴリズムでは、次のようなことができます。

  1. ピッチ周波数を大まかに検出します (DFT で実行できます)。
  2. ピッチ周波数を分離するためのバンドパス信号。
  3. フィルター処理された信号の 2 つのピーク間のサンプル数をカウントします。

サンプル数をカウントすることで、サンプル周波数に一致するピッチ解像度が得られます。サンプル周波数よりもさらに高い分解能が必要な場合は、多項式などの関数をピーク ポイント付近のサンプルに適合させることができます。他の周波数を抑制したので、それができるはずです。

別の回答が示唆するように、自己相関を使用して信号内の最大信号繰り返しを見つけることもできます。ただし、優れた自己相関ピッチ検出器を実装することは簡単ではありません。知らず知らずのうちに、ギター チューナーや同様の安価な電子機器は、バンド フィルターとピーク間のサンプル距離のカウントを組み合わせたアルゴリズムに基づいていると思います。

于 2010-11-03T08:48:05.410 に答える
1

入力を駆動力として減衰調和振動子を使用できます。オシレータのパラメータを選択して、共振周波数が必要な周波数と一致するようにします。

減衰調和振動子の分析は、力学に関するほとんどの理論物理学の本に記載されています。

于 2010-11-03T08:56:00.217 に答える
0

フーリエ解析について調べています。

基本的に、信号から周波数 f を抽出したい場合は、正弦波の周波数 f を投入し、それを元の信号で乗算して積分します。

元の信号に周波数 f のものが何も含まれていない場合、ほとんどゼロになるはずです。そうであれば、その周波数での信号のエネルギー量を測定できます。

その背後にはかなりトリッキーな数学がありますが、直感的には理にかなっています。それを見るだけで、周波数 f の信号内のすべてが残留物を残す正弦波と建設的に干渉します。周波数 f にないものはすべて、本質的にランダム ノイズと見なすことができます (つまり、ゼロより上にあるものとゼロより下にあるものはほとんど同じ量です)。正弦波を乗算しても正味の効果はありません。すべてがキャンセルされます。

これは私が釣りをしていたものと相関しています。上記の類推を完成させると、ピアノに含まれる音を確認するには、ペダルを押したまま上昇する音を歌います。共鳴共鳴が発生するたびに、ピアノがその周波数の音を持っていることを書き留めることができます。

もちろん、これには欠点がないわけではありません。C1 を押したまま (今回はペダルを踏んでいない)、C2 を歌ったり演奏したりすると、C1 はその基本周波数の 2 倍で共鳴して C2 サウンドを生成します。

同様に G2 を演奏すると、基本周波数の 3 倍で共鳴します。

于 2010-11-03T08:22:32.847 に答える