Top

あちゃぴーの自転車通勤
u-he UHM言語 学習03 FM音源

今回はFM合成をuhmでやってみる。 おそらくuhmと相性のよい合成方法だと思う。

FM音源の基本

まずはオペレータ2個を直列接続した簡単なFM音源から。

u-he uhm

中身的には、基音(サイン波)を出すモジュレータを、基音を出すキャリアに直列接続されている。 レベルはいずれも同じ。 dexedで同じ設定にするときはモジュレータの出力は全開ではなく90ぐらいで同じような音になった。

sin(x + sin(x))

Wave "sin(2 * pi * (phase + sin(2 * pi * phase)))"
u-he uhm

式をそのまま入れれば実現出来てしまう手軽さがuhmにはある。

FM音源の難点としては、作りたい音を数式でイメージするのは困難なので、あらかじめFMシンセで作った音を再現する方が効率的だと思う。

また昔のゲームに使われてたFMサウンドのような音作りはuhmでやるのはよいと思うが、ピアノなどのベロシティに反応してリアルタイムにサウンドが変化する音は、普通にFMシンセを使うべき。 uhmはあくまでもウェーブテーブル作成用スクリプトで、ベロシティなどの演奏情報をリアルタイムに取り込む方法を持っていないので。

おっと、uhmのFM音源は折り返しノイズがないね。ウェーブテーブルだから当然だよね。 FM音源の奇妙な不協和音が入らないので、使い勝手は良いのだが、 あの妙な癖を求める場合はFM音源を使うしかない。 下はYAMAHA DX7のクローンdexedの音。 設定はキャリアで基音、モジュレータを5倍音。 E1~C8までを弾いた音だが、途中から意味不明の音程が鳴り始める。 これが折り返しノイズで、FM音源特有の現象。

音程をスムーズに上げていった場合、周波数スペクトルでリニア表示すると以下のようになっている。ナイキスト周波数で文字通り折り返し、最低周波数まで戻ると、また折り返すということを繰り返しているのが分かる。音作りが難しい理由がここにある。

u-he uhm

uhmで同じ設定でE1~C8までを弾いた音。きれいに上まで音が出る。

uhmのウェーブテーブルの場合、同じように音程をスムーズに上げていった場合、ナイキスト周波数で折り返すことなく、そのまま消えていくことがわかる。

u-he uhm

また式の書き方だが、Hive付属のプリセットを見ると括弧のくくり方が2通り存在する。 どちらでもよいけど、レベルの設定値が変わってくるので、統一したほうがよいと思う。 個人的にはレベルを稼げるphaseと一緒に括る書き方をした。 下のようなレベル値にすると、ほぼ同じ波形となる。

NumFrames = 2 //フレーム数を設定
Wave end=0 "2*phase-1" //0フレームにノコギリ波
Wave start=1 end=1 "sin(2*pi*phase)" //1フレームにサイン波
NumFrames = 2 //フレーム数を設定
Wave end=0 "2*phase-1" //0フレームにノコギリ波
Wave start=1 end=1 "sin(2*pi*phase)" //1フレームにサイン波

3つのオシレータを直列

もう少し分かりやすく各オシレータを1行として表現してみた。

u-he uhm

スクリプトは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

u-he uhm

2個のモジュレータをひとつのキャリアに接続

u-he uhm

これを実現するためにバッファを使ってみる。aux1に波形を保存し、それをキャリアで合成している。

Wave target=aux1 "sin(8 * pi * phase)" //modulator
Wave "sin(2 * pi * phase)" //modulator
Wave "sin(2 * pi * (phase + aux1 + x ))"//carrier

u-he uhm

直列接続を2組並列で使う

別々の音色を作って、それをレイヤー的に使ってみる。

u-he uhm

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"

u-he uhm

1個のモジュレータを2個のキャリアに接続

u-he uhm

バッファを使うとこんな感じ。

Wave target=aux1 "sin(2 *pi *phase *7)"
Wave "sin(1 *pi *(phase + aux1))"
Wave "sin(2 * pi * (phase + aux1)) + x"

u-he uhm

フィードバック

u-he uhm

YAMAHA DX7には、ひとつだけフィードバックがあり、オペレータ1個でノコギリ波からノイズまで作れる。 uhmでそれを実現する方法はUrsさん自ら示してくれていた。 実際のDX7のフィードバックは、t-1、t-2のサンプルの平均化でサウンドを作っているという。 uhmで実現する場合、ここではlowpass()を使って疑似的に近いサウンドを得ている。

Wave "sin( 2*pi*phase + 2.2 * lowpass(y,0.95,0) )"

u-he uhm

FM合成におけるオペレータの接続

uhmでは何個でもオペレータを使えるし、接続方法の縛りもなく、スクリプトならではの柔軟性が心地よい。 さらにここからエンベロープや他のテクニックを駆使することで、実践的な音が作れる。 FM特有の折り返しノイズがないというのは、個人的に衝撃的であり、そもそも、あのノイズがFMらしさと思っていた節もあったので、少し考え方を改める必要がありそうだ。 あくまでもウェーブテーブルで、20Hzぐらいのサンプルを1周期分だけ作っているということを忘れてはいけない。それ以降はサンプリング的な考え方が適用される。

PD(phase distortion)音源

上記は数式的に波形を作っているが、よりハードウェア的な考え方でも作ることが出来る。 FM音源はサイン波を使っているが、カシオのPD音源はコサイン波を使っている似たような合成方法。 PD音源を数式といよりはプロセスに従って作ってみた。

まずは何の変哲もないコサイン波。ここだけ数式。ただし-cosにしている。phaseはフレーム内を0~1に変化。

Wave "cos(2 * pi * phase)*-1"
u-he uhm

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"
u-he uhm

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"
u-he uhm
uhm言語