u-he UHM言語 学習03 FM音源
今回はFM合成をuhmでやってみる。 おそらくuhmと相性のよい合成方法だと思う。
FM音源の基本
まずはオペレータ2個を直列接続した簡単なFM音源から。
中身的には、基音(サイン波)を出すモジュレータを、基音を出すキャリアに直列接続されている。 レベルはいずれも同じ。 dexedで同じ設定にするときはモジュレータの出力は全開ではなく90ぐらいで同じような音になった。
sin(x + sin(x))
Wave "sin(2 * pi * (phase + sin(2 * pi * phase)))"
式をそのまま入れれば実現出来てしまう手軽さがuhmにはある。
FM音源の難点としては、作りたい音を数式でイメージするのは困難なので、あらかじめFMシンセで作った音を再現する方が効率的だと思う。
また昔のゲームに使われてたFMサウンドのような音作りはuhmでやるのはよいと思うが、ピアノなどのベロシティに反応してリアルタイムにサウンドが変化する音は、普通にFMシンセを使うべき。 uhmはあくまでもウェーブテーブル作成用スクリプトで、ベロシティなどの演奏情報をリアルタイムに取り込む方法を持っていないので。
おっと、uhmのFM音源は折り返しノイズがないね。ウェーブテーブルだから当然だよね。 FM音源の奇妙な不協和音が入らないので、使い勝手は良いのだが、 あの妙な癖を求める場合はFM音源を使うしかない。 下はYAMAHA DX7のクローンdexedの音。 設定はキャリアで基音、モジュレータを5倍音。 E1~C8までを弾いた音だが、途中から意味不明の音程が鳴り始める。 これが折り返しノイズで、FM音源特有の現象。
音程をスムーズに上げていった場合、周波数スペクトルでリニア表示すると以下のようになっている。ナイキスト周波数で文字通り折り返し、最低周波数まで戻ると、また折り返すということを繰り返しているのが分かる。音作りが難しい理由がここにある。
uhmで同じ設定でE1~C8までを弾いた音。きれいに上まで音が出る。
uhmのウェーブテーブルの場合、同じように音程をスムーズに上げていった場合、ナイキスト周波数で折り返すことなく、そのまま消えていくことがわかる。
また式の書き方だが、Hive付属のプリセットを見ると括弧のくくり方が下のように2通り存在する。 ここではレベル値が同等となるように書いてみた。
Wave "sin(2 * pi * phase) *0.157" //modulator
Wave "sin(2 * pi * (phase + x))" //carrier
Wave "sin(2 * pi * phase) *1" //modulator
Wave "sin(2 * pi * phase + x) " //carrier
どちらでもよいけど、レベルの設定値が大きく変わってくるので、統一したほうがよいと思う。 個人的にはレベルを稼げるphaseと一緒に括る書き方を基本とした。
3つのオシレータを直列
もう少し分かりやすく各オシレータを1行として表現してみた。
スクリプトは1行目から音を生成して、その下の行にxとして結果が入る。 一番下行がキャリアとなって音が出力される。 各オシレータ共に基音になっている。これを倍音にしたければ2*piの部分の数値をいじればよい。 1、2行目の最後の掛けている数値はレベル。
Wave "sin(2 * pi * phase)*1" //modulator
Wave "sin(2 * pi * (phase + x))*1" //modulator
Wave "sin(2 * pi * (phase + x))" //carrier
2個のモジュレータをひとつのキャリアに接続
これを実現するためにバッファを使ってみる。aux1に波形を保存し、それをキャリアで合成している。
Wave target=aux1 "sin(8 * pi * phase)" //modulator
Wave "sin(2 * pi * phase)" //modulator
Wave "sin(2 * pi * (phase + aux1 + x ))"//carrier
直列接続を2組並列で使う
別々の音色を作って、それをレイヤー的に使ってみる。
aux1にはじめの波形を保存し、最後の行で合成をしてみた。
Wave target=aux1 "sin(4 * pi * (phase + sin(11 * pi * phase)))"
Wave "sin(2 * pi * (phase + sin(2 * pi * phase)))"
Wave "aux1*0.2 + x"
1個のモジュレータを2個のキャリアに接続
バッファを使うとこんな感じ。
Wave target=aux1 "sin(2 *pi *phase *7)"
Wave "sin(1 *pi *(phase + aux1))"
Wave "sin(2 * pi * (phase + aux1)) + x"
フィードバック
YAMAHA DX7には、ひとつだけフィードバックがあり、オペレータ1個でノコギリ波からノイズまで作れる。 uhmでそれを実現する方法はUrsさん自ら示してくれていた。 実際のDX7のフィードバックは、t-1、t-2のサンプルの平均化でサウンドを作っているという。 uhmで実現する場合、ここではlowpass()を使って疑似的に近いサウンドを得ている。
Wave "sin( 2*pi*phase + 2.2 * lowpass(y,0.95,0) )"
FM合成におけるオペレータの接続
uhmでは何個でもオペレータを使えるし、接続方法の縛りもなく、スクリプトならではの柔軟性が心地よい。 さらにここからエンベロープや他のテクニックを駆使することで、実践的な音が作れる。 FM特有の折り返しノイズがないというのは、個人的に衝撃的であり、そもそも、あのノイズがFMらしさと思っていた節もあったので、少し考え方を改める必要がありそうだ。 あくまでもウェーブテーブルで、20Hzぐらいのサンプルを1周期分だけ作っているということを忘れてはいけない。それ以降はサンプリング的な考え方が適用される。
PD(phase distortion)音源
上記は数式的に波形を作っているが、よりハードウェア的な考え方でも作ることが出来る。 FM音源はサイン波を使っているが、カシオのPD音源はコサイン波を使っている似たような合成方法。 PD音源を数式といよりはプロセスに従って作ってみた。
まずは何の変哲もないコサイン波。ここだけ数式。ただし-cosにしている。phaseはフレーム内を0~1に変化。
Wave "cos(2 * pi * phase)*-1"
Envelopeコマンドを使って、読み出し用のテーブルとする。 Tが時間で0~2048。 Lがレベルで0から1となっている。 そしてphaseの部分がenv(index)に置き換わっている。 この状態では波形は上と全く変わっていない。
Envelope Curve=linear L0=0 T1=2048 L1=1
Wave "cos(2 * pi * env(index))*-1"
Envelopeのカーブに1点追加して、その点をT1=10 L1=0.5とした。 これによってコサイン波を読み出す位置が変化する。 Tは0~2048までのフレームのサンプルの位置に相当するが、0.5はコサイン波のx軸に相当し、ちょうど一番盛り上がっている部分となる。つまりサンプル10の位置で、1となり、その後は緩やかに-1まで下がっていく。 これで疑似的なノコギリ波を生成できる。実際のプロセスに近い方法で作ってみるとカシオがコサイン波を使った理由が理解できる。
Envelope Curve=linear L0=0 T1=1 L1=0.5 T2=2047 L2=1
Wave "cos(2 * pi * env(index))*-1"