2017年1月15日日曜日

2.2.4 Complex sounds Pitch Tracker

複雑な信号のピッチ判定


Sin波だけのピッチ判定については先ほど紹介した通りです。
次に,複数の波が重なった音に対して,この処理を行ってみましょう。

Fig.2.9.をみてください。


[Fig.2.9.]

100Hzの3和音の信号です。
(2つの段階が表れることが分かります)
どの段階でも,波形が上昇する部分で,
3つの0の値を持つことが分かります。
それぞれの0の値の箇所にギリシャ文字をふっています。

もし,この波形を先のピッチトラッカーで分析したなら,
3つの重要なゼロの値をどの段階でも判定するでしょう。
よって,計算結果は,100Hzではなく,300Hzになるはずです。

解決方法として,ローパスフィルターを用いることが挙げられます。
このフィルターは波形を分析して,
周波数を遮断しますが,これがピッチを判定する手法として使えます。

例えば,「first-order Butterworth」を使ってみます。
このアルゴリズムを,今回の波形に用いてみましょう。


[1st step]


まず,先の信号を分析して,だいたい300Hzだということを計算しておきます。
フィルターを適用することで,次のように波形を遮断できます。
Fig.2.10.をみてください。


[Figure 2.10: First step:]
100Hzの3和音の波形に,300Hzのローパスフィルターを当てたところ,
重要な0の値はたった2つになりました。
フィルターのおかげで波形が滑らかになったことがわかります。

[2nd step]

滑らかになった新しい波形を再び Pitch Tracker で分析します。
フィルターの滑らかにする効果によって,
0の値は排除されていきます。

そして新しく返ってきた値は,
全体の数に依存しますが,
200Hz付近になるはずです(正確には200Hzです)。

次に,ローパスフィルターをこの新しい値にセットして,周波数をカットします。
Fig.2.11.をみてください。


[Figure 2.11: Second step:]
100Hzの3和音の波形を,200Hzのローパスフィルターに通した結果です。
重要な0の値はたった1つだけになりました。

[3rd step]

重要な0の値はそれぞれの段階で1つだけになりました。
よって, sinusoid pitch tracker は正しい値を返すことになります。

しかしながら,
安定した結果を計算するためには,もう1つ試行が必要になります。
ローパスフィルターに100Hzをセットして,波形を編集します。

これで,これ以上0の値が変わらないことが明らかです。
そして, pitch tracker は正しい周波数値を返し続けることができます。
Fig.2.12.を見てください。


[Figure 2.12: Third step: ]
100Hzのローパスフィルターで,100Hzの3和音の波形を分析した結果です。
安定した結果を出力するようになります。
(次の段階は,信号が同じである限り変わらないということになります)


[Timing overview. ]


このように,すでに2つのステップを経ることで,
pitch trackerは,毎回の計算で値を更新していき,
正しいピッチを判定することができるとわかりました。
この判別は 0.019 * a 秒以下で実行できます。

実際に,最初に何もフィルターを通さなかった場合の解析区域の時間は 1/300秒でした。
よって, a/300 秒後に値が返ってくることになります。

2つ目のステップはだいたい1/200秒です。
よって,値が返ってくるのは a/200 秒後になります。

そして,値を決定する3つめの段階は1/100秒です。
よって,値が返されるのは a / 100 秒後です。

これら全ての時間を合計したら,
どれくらいかかるか,わかると思います。

これは,そんなに早い処理ではありません。

実際,aの値は10ほどになるでしょう。
(2.2.5.をみてください)
そうすると,処理にかかる時間はだいたい0.2秒ぐらいだと分かります。
それは,75BPMの16分音符の間隔です。
音楽ではよくある間隔になります。


本当に実行されるコードは,
あなたがみてきたように,
Sinusoid Pitch Tracker が解析を始める前に,
100Hzの周波数をカットオフしたフィルターが適応されます。

よって,実際に実行する際には,
たった 0.01 * a秒で解析されるだろうと考えられます。
これは先ほど計算した時間の半分です。


[Changing signal. ]


では,信号のピッチが変化するとどうなるでしょうか。
もしも,下がった場合,
同じように少しの計算を経て,低い値を検出できるでしょう。


もし,ピッチが上がった場合,
新しい波形の基本周波数が,カットオフする周波数よりも高いため,
フィルターは基本周波数の波形を遮断してしまうのではないでしょうか。

しかし,実際には,フィルターはそうは働かず,
信号の”Fundamental’s zero-crossing rate”情報には影響しません。

例のFig.2.13.をみてください。


[Figure 2.13: ]
入力された信号により,ピッチが上がりました。
3和音の波が今200Hzになっています。
そして,ローパスフィルターは古い100Hzの値にセットされています。
情報に損失はなく「sinusoid pitch tracker」は新しい基本周波数を捉えることができています。


[Faust code.]

FAUSTでのこの新しい”pitch tracker”のコードは,
“sinusoid”のものと比較して,
[proces]の定義とライブラリの追加があるだけです。


====================

import("math.lib");
import("filter.lib");
SH(trig,x) = (*(1 - trig) + x * trig) ~ _;
a = hslider("n cycles", 1, 1, 100, 1);

Pitch(a,x) = a * SR / max(M, 1) - a * SR * ( M == 0)
with {
  U = (x' < 0) & ( x >= 0);
  V = +(U) ~ %(int(a));
  W = U & (V == a);
  N = (+(1) : *(1 - W)) ~_;
  M = SH(N == 0, N' + 1);
  };
process=dcblockerat(80) : (lowpass1 : Pitch(a)) ~ max(100);

====================


“filter.lib”ライブラリを,フィルターを用いるために定義し,付け加えています。
その後,[process]の定義において,まず[dcblockerat]を呼び出しました。
これは,ハイパスフィルターで,カットオフする値を80Hzに指定しています。

これはDCオフセットの信号を除去し,
ピッチ判定の影響となるノイズを除去するために機能します。

その後,用いている[lowpass1]フィルターは,
バターワースフィルタのローパスフィルターのことです。

これは[dcblockeart]から信号を受け取り,
Pitch関数から周波数を除去しています。

また[~]演算子を付け加えることで,
入力された値を100の値を比較して選択された値を,
ピッチ関数は max関数を経て値を出力します。

適用したフィルターはカットオフした100Hz以下の値を受け取らないということに気をつけて下さい。

これが動作すること使いやすくなります。
フィルターの初期値は0Hzにもかかわらず,
値がこれに到達する前に,直接出力することができるからです。

最後にSVGダイアグラムをFig.2.14.をみて確認してください。



[Figure 2.14:]
“Universal Pitch Tracker”の[process]のブロックダイアグラムです。


筆者注:
1)OS X 10.11で試しているのですが,lowpass1が見つからないとのエラーがでます。
  別環境で動作確認する必要あり。
  ERROR : undefined symbol : lowpass1
  2行目で"filter.lib"を読み込んでいるはずなのですが。

2)OSX 10.12ではprocess自体が見つからないとのエラーになります。
  対処法がわかり次第更新します。

1 件のコメント:

  1. Hi, to fix the lowpass1 problem use lowpass(1, freq) function. That solved it for me for some other thing that I was doing. Hope it helps.

    返信削除