ウェーブシェーピング、シンセでよく聞く用語ですが、実際の仕組みはよくわかっていませんでした。
実装してみないと、信号処理として何をしているかはわからないものが多いよね~
JUCEチュートリアルの「Add distortion through waveshaping and convolution」を進めていきます。ウェーブシェーピングとコンボリューションでディストーションを付加するようなチュートリアルのようです。
まずは、導入部分の「What is Waveshaping?」の項目について、勉強しながら、理解を深めていこうと思います。
公式のチュートリアルページはこちらになります。
こんな人の役に立つかも
・ウェーブシェーピングの仕組みを知りたい人
・JUCEのチュートリアル「Add distortion through waveshaping and convolution」を進めている人
・ハードクリッピングとソフトクリッピングについて知りたい人
ウェーブシェーピングについて
ウェーブシェーピングは、ある波形を別の形状に変化させるような変換を行うようなプロセスとのことです。
こちらのサイトの図が非常にわかりやすいです。
例えば、シンプルなsin波を変換して、複雑な波形に変換するようなそんな処理を行っています。
波形の歪み(ディストーション)を得るための一つの方法としても、ウェーブシェーピングの仕組みが利用できます。より具体的には、サイン波を入力として、矩形波を出力に変換するような関数を利用することで波形を歪ませることができます。
このような変換は、符号関数と呼ばれる数学的な操作があり、sgnと表現されるようです。
サイン波を矩形波に近づけるには、「sgn(sin(x))」と変換すればよいです。これが、ウェーブシェーピングという手法でサイン波を矩形波に近づけるということみたいです。※これは、プログラムではなく、数学的な表現です。
次に、ここでひとつ起きる問題が、「ハードクリッピング」という現象です。この操作でできた矩形波は、1から-1のゲインに変化する際に、波形に角ができています。
現実のスピーカーでこのような波形はノイズとなってしまいます。
以前のウェーブテーブルのチュートリアルで、矩形波を実装したときも、値が突然変化するような波形をプログラムしていたので、厳密には、このハードクリッピングが起きていたような感じでした。矩形波以外に、金属的なピッチ感のない音が混ざっていました。
そこで、この角に丸みを持たせるようにするソフトクリッピングを適用してみます。
チュートリアルによると、「tanh(n*sin(x))」という式を適用することで、エッジに丸みを帯びたソフトクリッピングの矩形波が実現できるということでした。
まずは、tanhの効果です。サイン波により丸みを与えるために、tanh(ハイパボリックタンジェント)に通してみます。
ここからは、GoogleCoraboratoryで、Pythonを使いmatplotlibでグラフ化して波形の変化を見ていきたいと思います。
scopeSize = 512;
import matplotlib.pyplot as plt
import math
#sinの描画
list = []
for num in range(1,scopeSize):
list.append(math.sin(math.radians(num/scopeSize*360)))
plt.xlabel("scopeSize")
plt.ylabel("gain")
plt.plot(list)
#tanhにsinの値を入れる
list2 = []
for x in list:
list2.append(math.tanh(x))
plt.xlabel("scopeSize")
plt.ylabel("gain")
plt.plot(list2)
青色の線のサイン波がより丸みを帯びたオレンジの波形に変化しているのがわかります。
次に、tanhに入れる値をブーストして、1と-1のゲインの頭をクリッピングさせます。プログラムでは、クリッピングを1と-1上限で1と-1に張り付くような条件にしていますので、クリッピングとは若干イメージが変わるかもしれませんがとりあえずそのようにしてみました。
boost変数を大きくしていくと、上限下限で頭が切り取られた波形となり、矩形波に近づいていきました。
#tanhに入れる値をブーストする
list3 = []
boost = 10
for x in list:
boost_val = (math.tanh(x))*boost
if(boost_val > 1):
boost_val = 1
if(boost_val < -1):
boost_val = -1
list3.append(boost_val)
plt.xlabel("scopeSize")
plt.ylabel("gain")
plt.plot(list3)
なんとなくソフトクリッピングの意味が分かってきました。
計算では、切り取ってしまうので、若干エッジが立つと思うのですが、この辺りは実装してみてどのように聴こえるかを確認してみたいなと思いました。
JUCEではこれらの操作を、「dsp::WaveShaper」で実現できるようです。