FM合成の回 では、sin 2つから音色の森が生えるという仕組みを見ました。今回は少し毛色を変えて、開発の実録を書きます。テーマは「本物のトロンボーンに寄せる作業を、勘に頼らず測って前に進める工程に変える」ことです。

合成の方式そのもの——倍音の足し算 、FM、ノコギリ波をフィルタで削る減算合成——は、教科書にもこの連載にも書いてあります。ところが方式を知っていることと、実在の楽器の音を当てられることのあいだには、けっこうな距離があります。パラメータの海の中から「トロンボーンのあの音」にたどり着く手順は、教科書にはあまり載っていません。この記事は、その距離をどう詰めたかという作業記録です。

材料は身内から出します。筆者は Harmonize という音感トレーニングアプリを個人開発していて、正解音のガイドとして鳴る楽器音は、録音の再生ではなくすべて自前の合成です。そのラインナップにトロンボーンを足したときの一部始終が、ちょうどこの話の生きた実例になっています。

「おとなしい」では直せない

最初に作った版(v1と呼びます)は、いちおうトロンボーンには聞こえました。ノコギリ波をローパスフィルタで丸めた、金管の顔をした音です。ところが実機で鳴らしてみると、感想はひとこと「おとなしい」。本物のトロンボーンが持っている、中低音の張り、音の芯が前に出てくる感じがないのです。

問題は、この感想が改良の役に立たないことです。「おとなしい」から何をどうするのか。フィルタを開く、倍音を足す、音量を上げる——手はいくらでも思いつきますが、どれが正解かは感想からは決められません。勘でつまみをいじり、聴いて、またいじる。これを繰り返しても、良くなったのか悪くなったのかさえ、だんだん分からなくなってきます。耳は「何かが違う」と気づくことにかけては優秀なのですが、どこを・どちらへ・どれだけ直すかを言い当てるのは苦手なのです。

そこで方針を変えました。耳の感想を、測定値に翻訳するのです。

「おとなしい」を数値に翻訳する改良ループの図。実トロンボーンの録音をFFTで実測して目標数値カードを作り、合成→再測定→目標と比較→レシピ調整のループを数値が合うまで回す

やることは図のとおりで、まず手本になる実物の音を FFT——音の波形を「どの高さの成分がどれだけ含まれているか」に分解する計算——にかけて、目指すべき音の姿を数値のカードにします。あとは自分の合成音も同じ物差しで測り、カードと見比べながらレシピを直していく。目標が数値なので、一手ごとに「近づいたか、遠ざかったか」がはっきり分かります。音色作りが、当てずっぽうから測定のある工学になります。

手本を測って、目標数値を立てる

都合のいいことに、手本はすぐ近くにありました。アプリには効果音として、本物のトロンボーンで演奏された録音がひとつ同梱されています。この録音を FFT にかけて、倍音——基音(音程を決めるいちばん低い成分)の2倍、3倍……の高さで一緒に鳴っている成分——の大きさを測りました。

結果は少し意外なものでした。いちばん大きいのは基音ではありません。第2倍音が基音より 5dB も大きく、第3倍音が基音より 3dB 小さいだけでほぼ肩を並べ、第4倍音が 9dB 落ち。エネルギーの大半は 500〜1000Hz の帯域に集まっていました。トロンボーンの「張り」の正体は、基音の上に乗った中域の倍音たちが主役を張っていることだったのです。

もうひとつ、全体をひとつの数字に要約する指標として、スペクトル重心も測りました。スペクトル重心とは、周波数成分をエネルギーの重みで平均した「音の明るさの重心」で、高い成分が多い音ほど重心は高くなります。フィルタの回 で「高い成分を削ると音がこもる」という話をしましたが、その明るさ・こもり具合を1個の数値で言い表せる物差しです。手本の実測値は約 1.7kHz。こうして目標のカードがそろいました。第2倍音 +5dB、第3倍音 −3dB、第4倍音 −9dB、重心 1.7kHz。ここから先の作業は、この4つの数字を当てにいくゲームになります。

レシピ ── ノコギリ波を3系統に分けて削る

合成の方式には、倍音が全部入ったノコギリ波からフィルタで不要な成分を削り出す、減算合成を使いました。素材のノコギリ波には基音の整数倍の倍音がひととおり含まれているので、あとは「どこを残し、どこを削るか」の設計がそのまま音色になります。

トロンボーンのレシピの信号チェーン図。ノコギリ波を胴鳴りLPF 2.8kHz、フォルマントBPF 620Hz、950Hzの3系統に分け、重み0.35/0.6→1.0/0.7で混ぜ、tanhドライブで潰して音量エンベロープへ

鍵になるのはフォルマントです。フォルマントとは、楽器の管や人の口の形で決まる「よく響く固定の帯域」のことで、音程を変えても動かないのが特徴です。人の声の母音がこれで決まるという話はフォルマント合成の回 に書きましたが、金管楽器も同じで、管とベル(先端の朝顔)の共鳴が特定の帯域をいつも持ち上げています。そこでノコギリ波を3系統に分け、それぞれ別のフィルタを通しました。1系統目は 2.8kHz のローパスフィルタで、音の体になる低域〜中域をそのまま残す「胴鳴り」。2系統目は 620Hz を中心とするバンドパスフィルタ(決まった帯域だけを通すフィルタ)で、手本の主役だった第2倍音のあたりを持ち上げる第1フォルマント。3系統目は 950Hz の第2フォルマントで、第3倍音の張りを受け持ちます。3つを 0.35 : 0.6〜1.0 : 0.7 の重みで混ぜると、目標の倍音バランスの土台ができます。

混ぜたあとに、もうひと味あります。tanh 関数で波形の頭を軽く潰すドライブ(サチュレーション)です。波形の形が変わることは倍音の構成が変わることなので、潰せば潰すほど倍音が増えて音は張り出します。ギターの歪みと同族の現象で、金管の「ブリッ」とした質感はここから出ます。さらに時間方向の演出として、音量は 45ms かけてふわっと膨らみ、明るさ(第1フォルマントの重みとドライブ)はそれより遅い 100ms で開くようにしました。金管のスウェルは音量が先、明るさが後から追いかけてくるからです。おまけに出だしの 10ms だけ、息の当たるノイズ(チフ)を薄く混ぜてあります。音量の輪郭が楽器の顔を決めるという話はエンベロープの回 のとおりで、ここでも効いています。

測って直す ── v1 の 1.2kHz から v2 の 1.8kHz へ

さて、冒頭の「おとなしい」v1 を、この物差しで測り直してみると、スペクトル重心は 1.2kHz でした。目標は 1.7kHz ですから、0.5kHz も低い。あいまいだった耳の感想が、「中域から上の倍音が痩せていて、重心が半キロヘルツ足りない」という具体的な数値のずれに翻訳された瞬間です。

ずれの正体が分かれば、打ち手は絞れます。第1フォルマント 620Hz を強め、v1 にはなかった第2フォルマント 950Hz の系統を追加して、中域の倍音を支配的にする。逆に胴鳴り側は、ローパスを 2.8kHz まで開きつつ混ぜる重みを 0.35 に下げて、基音が主役に居座らないようにする。そしてドライブを強め、倍音列の減り方を実測のカーブに寄せる。この3手を入れて測り直した v2 は、重心 1.8kHz。倍音バランスも目標の +5 / −3 / −9dB にほぼ乗り、聴いた感じも中域が前に出る張りのある音になりました。

目標の倍音バランスとv1・v2の答え合わせの棒グラフ。実測目標は第2倍音が最大で重心1.7kHz、v1は基音が主役で重心1.2kHz、v2は目標の破線にほぼ乗り重心1.8kHz

改良に使った一手一手は、どれも教科書に載っている普通の道具です。特別だったのは道具ではなく、目標が数値になっていたこと。「おとなしい」を相手に戦うのと、「重心をあと 0.5kHz 上げる」を相手に戦うのとでは、作業の確かさがまるで違います。ちなみにこの実測とレシピ調整のループ、筆者はふだん AI アシスタントと対話しながら回しています。測定値という共通言語があると、人にも AI にも「どこをどれだけ直すか」が正確に伝わるのです。

触ってみる

先に正直に言っておくと、これで本物のトロンボーンと聞き分けられなくなるわけではありません。合成だけで生楽器の質感にそっくり寄せるのは本当に難しく、ここでやったのは「本物に一歩近づけたか」を数値で測れるようにして、改良を前に進めたことです。それでも v1風から v2 への切り替えで重心が動き、張りが出てくる変化は、耳でもグラフでもはっきり分かります。

このレシピをブラウザの Web Audio API で再現したデモを用意しました。鍵盤を押すとノコギリ波1本の減算合成でトロンボーン風の音が鳴り、その合成音をその場で FFT にかけて、倍音バランスとスペクトル重心を実測し続けます。倍音バランスのグラフには目標値の +5 / −3 / −9dB が赤い破線で引いてあるので、合成音が手本に「当たっている」かどうかがひと目で分かります。

いちばんの見どころは v1風⇔v2 の切り替えです。v1風はフォルマント1本・基音寄り・ドライブ弱めという初版の性格を再現した設定で、音を鳴らしたまま切り替えると、同じ音程のまま音色だけが入れ替わります。切り替えた瞬間、棒グラフが赤破線に乗り、重心の針が右へ動く——耳で聞こえる「張りが出た」が、測定値の変化として同時に見えます。

うまく表示されないときは、デモを別タブで開く

ひとつ種明かしをしておくと、目標にいちばんよく当たるのは「レ」のあたりの音です。フォルマントは音程を変えても動かない固定の帯域なので、第2倍音が 620Hz に乗る音域——つまり手本の録音と同じあたり——でレシピはいちばん正確に働きます。低いドでは基音寄りに、高いドでは並びが変わって、当たり方が少しずつずれていく。実物の楽器も音域によって音色が変わりますから、これはむしろフォルマント方式の正直なふるまいです。なお、ブラウザと iOS アプリでは FFT の条件が違うため、重心の数値そのものは本家の実測と完全には一致しません。それでも v1風と v2 の差、つまり改良でどれだけ重心が動いたかは、そのまま体験できます。

どう実現するか

信号の流れは図のとおり3系統の並列なので、Web Audio API なら標準ノードの配線だけで組めます。フィルタは BiquadFilterNode——教科書どおりの2次 IIR フィルタ(双二次フィルタ)で、アプリの実装でも同じ形のフィルタを十数行で自作しています。

const osc = ac.createOscillator();
osc.type = 'sawtooth';                       // 倍音全部入りの素材
osc.frequency.value = f0;

const lpf = ac.createBiquadFilter();         // 胴鳴り
lpf.type = 'lowpass';  lpf.frequency.value = 2800;
const f1 = ac.createBiquadFilter();          // 第1フォルマント
f1.type = 'bandpass';  f1.frequency.value = 620;  f1.Q.value = 2.5;
const f2 = ac.createBiquadFilter();          // 第2フォルマント
f2.type = 'bandpass';  f2.frequency.value = 950;  f2.Q.value = 2.0;

const shaper = ac.createWaveShaper();        // tanh ドライブ
shaper.curve = tanhCurve(2.4);               // y = tanh(d·x)/tanh(d) のテーブル

const mix = ac.createGain();
osc.connect(lpf); osc.connect(f1); osc.connect(f2);
connectWeighted(lpf, mix, 0.35);             // 3系統を重み付きで合流
connectWeighted(f1,  mix, 1.0);              // (GainNode を挟むだけの補助関数)
connectWeighted(f2,  mix, 0.7);
mix.connect(shaper);                         // → エンベロープ → 出力へ

重みの数字そのもの(0.35 / 0.6→1.0 / 0.7)に普遍的な意味はありません。これは「この手本の録音の +5 / −3 / −9dB」を狙って追い込んだ結果の値であって、別のトロンボーン、別の奏者の録音を手本にすれば、また別の数字に落ち着くはずです。持ち帰ってほしいのはレシピの定数ではなく、実測して目標を数値にすれば、そこへ寄せる作業は誰でも回せるという手順のほうです。

参考文献

音色作りは、測った瞬間から前に進み始めます。まずはデモで、v1風と v2 を切り替えながら、棒グラフが赤破線に乗る瞬間を確かめてみてください。