プログラムで楽器の音を作ろうとすると、道はふたつ思い浮かびます。ひとつは本物をマイクで録って貼りつけるサンプリング。もうひとつは、エンベロープの回 でやったように、波形をいちから組み立てていくやり方です。どちらも「音の形」を外側から与える発想です。

ところが、これとはまるで違う第三の道があります。音の形をこちらから与えるのではなく、弦そのものの振る舞いをプログラムの中で回して、鳴るにまかせる。材料はあきれるほど少なくて、ほんのひと吹きのノイズだけ。それを輪に閉じ込めてぐるぐる回すと、はじいた弦の音が、ちゃんと減衰しながら鳴ります。カープラス・ストロングと呼ばれる、1983年に発表された古くて賢い方法です。

本題の前に、この連載について少しだけ。ここでは楽器の音をプログラムで作る仕組みを、少しずつ紹介しています。筆者は個人で音感トレーニングアプリ Harmonize を作っていて、その中で鳴る楽器の音は、録音の再生ではなく、鳴らすたびにプログラムが計算した合成音です。こうした音づくりの基礎は独学で、古いC言語のサウンドプログラミングの教科書から学びました(参考文献に挙げています)。今回のカープラス・ストロングも、その延長にある小さな一手です。

弦をはじくと、何が起きているか

仕組みに入る前に、本物の弦を思い出してみます。ギターでも琴でも、弦をはじくというのは、弦を指でつまんで、でたらめな形にひしゃげさせてから、ぱっと離すことです。離された弦は、そのひしゃげをきっかけに行ったり来たり揺れはじめ、揺れは空気や熱に少しずつエネルギーを奪われて、やがて止まって静かになります。

ここで起きていることは、煎じ詰めるとふたつだけです。ひとつは、最初に一度きりの、でたらめな衝撃が与えられること。もうひとつは、その揺れが時間とともに薄れていくこと。カープラス・ストロングがまねるのは、まさにこのふたつです。でたらめな衝撃はノイズで、薄れていく揺れは、輪の上での平均が受け持ちます。

ノイズを輪に閉じ込めて回す

先に前提をひとつ確かめておきます。コンピューターが音を出すとき、スピーカーへは1秒あたり44100個の数が送られていて、その一つ一つが「その瞬間に膜をどれだけ押し出すか」を表しています。音の正体は、つまるところこの数の列です。サイン波もピアノの音も、毎秒4万個あまりの数の並びにすぎません。ここが分かっていると、これから話す仕組みが素直に飲み込めます。

カープラス・ストロングで用意するのは、その数を何十個か並べた短い列です。ただし端まで来たら先頭へ戻る、輪のようにつながった列を使います。データ構造としてはリングバッファ と呼ばれるもので、位置を指す針がぐるぐる回り、端に来たら先頭へ折り返します。

最初に、この輪の全部のマスをノイズ、つまりでたらめな数で満たします。これが弦をはじいた瞬間のひしゃげにあたります。あとは針を順に進めながら、いま指しているマスの数をそのまま音の1サンプルとして出力し、端まで行ったら先頭に戻ってまた出力していく。ここが肝心なのですが、鳴っているのはこの輪に入っている数そのものです。どこかに別のサイン波があって、それを掛けたり変調したりしているのではありません。輪から順に取り出した数の列が、そっくりそのまま音になります。

では音の高さはどこで決まるのか。輪は端まで行くと先頭へ戻るので、出てくる数の列は、輪の長さぶんの間隔で同じパターンをくり返します。数は1秒に44100個の速さで出ていくので、輪のマスが $L$ 個あれば、1秒あたり $44100 \div L$ 回だけそのパターンがくり返される。このくり返しの回数が、そのまま音の高さ(周波数)です。輪が長ければゆっくりくり返すので低い音、短ければ速くくり返すので高い音。出したい高さ $f_0$ が決まれば、輪の長さを $L = f_s / f_0$ マスにすればよい、というだけの話です。ドとラの違いは、輪を何マスにするか、それだけで決まります。

ただし、もし輪の中身を固定したまま回し続けたら、そのノイズが延々くり返す、ざらついたブザーのような音が同じ高さで鳴りっぱなしになるだけです。カープラス・ストロングが弦の音になるのは、針が通り過ぎるたびにマスの中身を書き換えるからです。数を出力すると同時に、そのマスを「自分と隣の平均」にして、ほんの少し小さくして書き戻す。すると輪の形は、一周ごとに少しずつ変わっていきます。

ノイズを詰めた輪を一周ごとに平均していくと、ギザギザの波がなめらかな波に整いながら縮んでいく三段の図

なぜ平均するだけでノイズが澄んだ音になるのか。隣どうしを平均するという操作は、値が細かく上下している部分ほど強くならします。細かい上下は高い音の成分ですから、平均を通すたびに高い成分が削られていく。これはローパスフィルタ、つまり高い音を通しにくくするフィルタそのものです。はじめのノイズには高い成分から低い成分までひととおり入っていますが、一周ごとに高いほうから消えていき、最後に生き残るのは輪の長さぶんの周期でくり返す成分だけ。それが澄んだ音の高さとして聞こえます。

そして高い成分が先に、低い成分があとに消えるということは、音がはじめは明るく、すぐ丸くなって、やがて消えていくということです。この「高い倍音から先に消える」という振る舞いは、本物の弦や、あとで触れるピアノの音の大事な特徴なのですが、カープラス・ストロングではそれを一つ一つ設計する必要がありません。輪の上で平均するという一手の、いわばおまけとして手に入ってしまいます。

触ってみる

言葉で追うより、鳴らして見たほうが早いので、デモを用意しました。上のドレミを押すと、その音程ぶんの長さの輪にノイズを詰めて鳴らします。真ん中の輪の絵は、いま輪の中に入っている値をそのまま描いたものです。押した直後はギザギザのノイズ、赤い針が一周するたびに角が取れて、波がなめらかな形に整いながら少しずつ縮んで消えていく ── さきほどの三段の図が、リアルタイムで動くのを見られます。

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

いちばん下のスペクトログラムを見ると、高いほうの成分から順に暗くなっていくのがわかります。これがさっきの「高い倍音から先に消える」です。それから、明るさのつまみを右いっぱいに回してみてください。一周ごとの平均をほとんどやめてしまうと、音は弦ではなく、金属的な反響のようになります。輪を平均する一手こそが、弦らしさの正体だったわけです。

このとき輪は、平均という味つけを失って、ただの遅延ライン(信号を一定時間ためて送り返す仕掛け)にフィードバックがかかっただけの状態に戻ります。これはギターアンプに載っているスプリングリバーブ ── バネに信号を通し、伝わって返ってくる音を拾う装置 ── とそっくり同じ構造です。あの金属的な「ボヨーン」が顔を出すのは、そのためです。

この素のままの音は、ギターともピアノとも少し違う、板に弦を張っただけのような、どこか金属質な響きに聞こえたかもしれません。実際この音は、昔から琴やハープシコードにたとえられてきました。共鳴する箱(ボディ)をまだ一切持っていない、弦の生の振動だからです。

そこで効いてくるのが、はじきの柔らかさのつまみです。真っ白なノイズをそのまま詰めると、はじいた瞬間に高い成分がびっしり入るので、硬いピックで弾いたような金属的なアタックになります。詰める前にノイズを少しならして角を丸めておくと、やわらかい所を指の腹ではじいたような音になり、金属質が抜けてナイロン弦のような丸みが出てきます。輪を回す仕組みはまったく同じまま、はじき方を変えるだけで、琴とナイロン弦のあいだを行き来できるのです。

どう作るか

仕組みが単純なだけあって、中身もほんの十数行です。輪をノイズで満たし、針を回しながら「出力して、隣と平均して書き戻す」を繰り返す。それだけです。

/* カープラス・ストロング(C風の擬似コード) */
int   L = round(fs / f0);              /* 輪の長さ=音程を決める */
float buf[L];
for (i = 0; i < L; i++) buf[i] = noise();   /* ノイズをひと吹き詰める */

int i = 0;
for (n = 0; n < length; n++) {
    output[n] = buf[i];                        /* いまの値を出力 */
    int j = (i + 1) % L;                       /* 隣(輪なので端は先頭へ戻る) */
    buf[i] = decay * 0.5f * (buf[i] + buf[j]); /* 隣と平均し、少し縮めて書き戻す */
    i = j;                                     /* 針をひとつ進める */
}

数式で書けば、いま鳴っている値 $y[n]$ は、輪の長さ $L$ だけ前の値とそのひとつ前の平均、

$$y[n] = \rho \cdot \tfrac{1}{2}\big(y[n-L] + y[n-L-1]\big)$$

というだけの漸化式です。$\rho$(ロー)は1よりわずかに小さい係数で、これが余韻の長さを決めます。ブラウザで動かすときは、この計算を弾いた瞬間にまとめて回して数秒ぶんの波を作り、それを再生するのが手軽です。上のデモも、押されるたびにこの十数行を回して音を組み立てています。

フィルタで作るか、輪で作るか

同じ「ノイズを共鳴させて音程を持たせる」でも、共鳴のさせ方にはいくつか流儀があります。ひとつは、ノイズを決まった帯域だけ通すフィルタにかけて響かせるやり方。尺八の息づかいや、弦をこする弓の音といった音色は、この「ノイズ+固定のフィルタ」で作られることが多いものです。もうひとつが、いま見てきたカープラス・ストロングのように、音程に合わせた長さの輪でノイズを回すやり方です。同じ「ノイズ+共鳴」から出発しても、フィルタ側に寄れば息まじりの笛に、輪側に寄ればはじいた弦に化ける。どちらを選ぶかで音の素性が変わるのが、音作りの地味に楽しいところです。

参考文献

  • K. Karplus, A. Strong, “Digital Synthesis of Plucked-String and Drum Timbres,” Computer Music Journal, vol. 7, no. 2 (1983), pp. 43–55. DOI: 10.2307/3680062 。この方法を最初に発表した原論文。
  • D. A. Jaffe, J. O. Smith, “Extensions of the Karplus-Strong Plucked-String Algorithm,” Computer Music Journal, vol. 7, no. 2 (1983), pp. 56–69. DOI: 10.2307/3680063 。はじき方や減衰の作り込みなど、素のアルゴリズムを実用的に広げた続編。
  • 青木直史『サウンドプログラミング入門――音響合成の基本とC言語による実装 』技術評論社(2013)。音作りの各方式をC言語の実装つきで解説する、数少ない日本語の定番書。
  • デモの実装には Web Audio API を使用。弾いた瞬間に生成した波形を AudioBuffer にして再生しています。

ノイズをひと吹き、輪に閉じ込めて回すだけ。まずはデモで、はじく強さを変えながら、琴とナイロン弦のあいだを行き来してみてください。