作ればわかる、FM音源
この記事はSpeee Advent Calendar 2017 15日目の記事です。
前日は@cho3による「サンタクロースの正体を暴く」でした。
動機
昔からFM音源の音は好きだったが、どういう原理で音作りをしているのか詳しくは知らなかった。
FM音源がどんな音色を出すかについては、ここに貼った動画を見てほしい。
一言で言うと80年代の音。
とはいえ90年代以降も多用されていて、メガドライブの内蔵音源や、ガラケーの着メロを鳴らしていたのもFM音源だ。
とある記事(アナログシンセとはまったく違う、“誰でもわかるFM音源”講座 : 藤本健の“DTMステーション”)を読んでいたら、仕組みについてはなんとなく理解できたので、実際に動くものをWebAudioとReactで作ってみることにした。
そうFM音源とは周波数変調をかける音源であり、OP2でOP1にビブラートをかけるというか、くすぐるんです。OP1はもともとポーっと鳴っていた音が、くすぐったいのでピーとかジーとがギャーって鳴るんです
本稿では一般的なFM音源の原理については詳しく触れないので、気になる方は記事を読んでみてほしい。
記事中に出てくるReface DXのUIはグラフィカルでFM音源の音作りも直感的にできそうだ(欲しい)。
以前、DX7の実機でパラメータをいじったときは全然理解できなかった。
オペレータを作る
FM音源において音を構成する最小要素、オペレータから作っていく。
OscillatorNodeとGainNodeをまとめたものにEG(ADSR)を追加してOperator classを作った。
ADSRは数値だけだと直感的にエディットしづらいので可視化した。
SVGを使ったのは、DOMで記述できReactとの相性が良いため。
オペレータのルーティングをする
Operatorには変調をかけられる側(キャリア)とかける側(モジュレータ)があり、それぞれノードを接続する先が違う。
キャリアは音を出すので出力に直結するGainNodeに、モジュレータは変調をかけるのでキャリアのOscillatorNode(のfrequency)に接続する。
例えば、後述するElectric Pianoのルーティングは以下のようになる。
※ nodeの可視化にはFirefoxのWeb Audio エディターを使った
勘のいい読者ならお気づきと思うが、このルーティングはFM音源シンセサイザーにおけるアルゴリズムとほぼ同じだ。
音(ノート)を鳴らす
キーを押すとOscillatorNodeの発振が開始されて音が出る。
今回作ったFM音源は4OPなので1音発音するのに4つのOscillatorNodeのstart()
を呼ぶ。
このとき、各オペレータに内包されているGainNodeに対し、ADSRのパラメータによるレベルの時間的変化をスケジュールする。 これによって、ゆっくり立ち上がる(長いattack)音や、鋭さを伴った(短いdecayと低いsustain)変調、キーを話したときの余韻(長いsustain)といった表現が可能になる。
WebAudio APIのOscillatorNodeは1回鳴らすごとに使い捨てなので、発音のたびに4つのOperatorを内包するNoteオブジェクトを生成し、アルゴリズムに沿ってNodeの接続をした後にOscillatorNodeの再生を行っている。
音色を作る
FM音源の音色作りは変調による波形の変化が極端なため、一般的には難解とされるが「くすぐり」を意識しながら作っていくとそれらしい音ができあがる。
1. Electric Piano
いかにもFM音源といった感じのエレクトリックピアノ。
バリエーションとして 4. Toy Piano
も作った
- carrier #0 に 減衰の速い(
decay
を短めにした) modulator #1 をかけて「コツン」という感じの立ち上がりを与える - 音に厚みを出すため、デチューンした carrier #2 を用意し、金属的な響きを加えるために
freq. ratio
を大きめにとった modulator #3 をかける
2. Organ
オーソドックスなオルガン。
- carrier #0 ~ #3を並列に並べ、それぞれを別の
freq. ratio
で鳴らす
3. Synth Bass
クセの強いシンセベース。
- carrier #0 に modulator #1~3 をかける。modulatorごとに異なる
ADSR
を設定し、音色に時間的な変化を与えている
別途LPFをかけるとそれらしくなりそう
DEMO
※ A~Lのキーを鍵盤に見立てて演奏ができます
Chrome(63以降)推奨。 FirefoxだとAudioParam.cancelAndHoldAtTime()が使えないのでリリースが不自然になる問題が残っている。
今回のdemoのビルドにはParcelを使った。
production buildするときにこのバグを踏んだのだけれど、翌日にリリースされた1.2.0で修正されていた。
Unable to `build` index.html · Issue #8 · parcel-bundler/parcel
TODO
- とりあえず音が出るよう作ったのでリソースの管理が雑なのをなんとかする
- オペレータにフィードバックを追加する
- フィルタを追加する
- アルゴリズムをグラフで可視化する
所感
難解だと思っていたFM音源も、自分で作ることによって理解が進み、ある程度までは思い通りの音を作れるようになった。
D-50のLA音源や物理モデル音源など、原理が気になる音源方式は無数にあるので、機会があればそれらも挑戦してみたい。
明日は@yoppiによる「GoとECSの格闘技について」 お楽しみに!