EvoGrain:L-system とグラニュラー合成によるアルゴリズム音響デザイン
Nathan Ho は EvoGrain を作った。オフラインの Rust プログラムで、L-system をランダムに生成し、規則をタートルコマンドとして解釈し、得られたグレイン集合を音声に合成し、適応度関数が気に入ったものだけを残す。記事は 50 個の規則を持つランダム文法から閉ループ合成まで、各段階を一通り辿っている。
日本語
コピー


数年前、私はこの場所にL-system とグラニュラー合成についての記事を書いた。当時からあまり満足していなかったし、今読み返すと恥ずかしいとすら思う。この記事はその不甲斐なさを正すためのもので、その試みははるかに野心的なプロジェクトだ。L-system とグラニュラー合成で音声を生成する。以下では、EvoGrain というこのシステムをどう組み上げたのか、そして構築の過程で私が何を経験したのかを、順を追って詳しく説明する。上で出力例を聴けるし、記事の後半にも例をいくつか用意した。
背景
ここでは、私が影響を受けた資料をいくつか挙げる。
Iamus

Iamus1は、マラガ大学の研究チームが2010 年ごろに開発したプロジェクトで、アルゴリズム作曲の旧時代——アルゴリズム芸術がろくでもない理由でにわかに注目を集める前の時代——において、説得力を持つもののひとつだ。制作者たちは、楽譜の生成に一切の人的介入はなく、Iamus は「自ら独自のスタイルを発展させた初のコンピュータ」だと主張している。そのスタイルはポスト調性の現代クラシック音楽の範疇に入る。
Iamus の宣伝文句は、私がこのプロジェクトで最も面白いと思う部分を覆い隠してしまっている。Iamus は人間の既存作品で訓練されておらず、完全にルールベースなのだ。だからHello World(2011)を聴いてみるといい。これが最初に公開初演された作品だ。この録音を評価する際には、明らかな事実を考慮に入れなければならない——演奏しているのは生身の音楽家であり、彼らは避けがたく独自の解釈を加えている。作曲の観点から見るとやや目的を見失っているかもしれないが、コーパスの支えを持たない産物としてはかなり印象的だ。Iamus のウェブサイトにはかつて生成された楽譜の無料ダウンロードが大量にあったそうだが、今どこで見つけられるのかはわからない。
当然、どう動いているのか気になる。Iamus のソースコードと詳細な技術解説は公開されたことがない。このプロジェクトは Melomics Spain SL という企業の商用製品と結びついていたが、その企業はもう存在しないらしい。2 このプロジェクトの手法について最も詳しい記述は、Vico らが『Handbook of Artificial Intelligence for Music』に寄稿した章にある。1 同じチームによる短めの論文もいくつかあり、目を通す価値がある。3 4 これらの論文がプロジェクトの実態をかなり正確に反映していると仮定すると、要点はこうだ。Iamus は Lindenmayer システム(L-system)と進化アルゴリズムを使う。L-system が有限の記号列を生成し、その記号が「音高を半音上げる」「音符を出力する」「楽器を切り替える」といった命令列を経て楽譜に符号化される。L-system は決定論的で、ちょうどひとつの楽譜を符号化する。
進化アルゴリズムは L-system の規則を遺伝子型(遺伝物質)、生成された楽譜を表現型(生物体の外的形質)として扱い、楽譜を適応度関数で評価する。選択、交叉、変異の演算子は一般的なものが使われる。この進化システムはどうやら人間が誘導する進化にも対応しているらしい。5
Iamus の論文から得た、私にとって最も有用な着想は間接符号化だ。ここでは遺伝子型と表現型の関係が非常に複雑で、L-system の各記号の意味と、それらがどう楽譜を生成するかによって決まる。これらの設計判断は音楽的に面白い結果が得られるかどうかを左右する決定的な要素であり、適応度関数や進化計算の部分よりも重要なのかもしれない。
その他の音楽 L-system
L-system によるアルゴリズム作曲には資料が豊富にある。一般読者向けの良い本は Tom Johnson のSelf-Similar Melodiesだ。6 この本で使われる L-system はごく単純で、聴き手が構造をはっきり聴き取れること、いわゆる「プロセス音楽」を目指している。Mason と Saffle の論文7は似た手法を用いているが、生成された旋律が他の作曲家の旋律の統計的特徴をどう模倣するかも示している。Jones は別の道を行き、8 L-system を時間と音高の空間の分割に使っている。DuBois の博士論文9はさらに古い例をいくつか挙げ、演奏者とのリアルタイムのやり取りを含む自身のシステムを説明している。
L-system と音響合成の組み合わせはあまり探られていない。私が見つけた中で最良の資料は、Stelios Manousakis によるMusical L-Systemsに関する学位論文だ。10 彼の研究の大部分は L-system で直接波形を生成するもので、ここで私がやっていることとは少し違う。ただ、彼は L-system とグラニュラー合成も組み合わせており、その部分はこのプロジェクトで私がやっていることとかなり近いように見える。私の理解が正しければ、違いのひとつは彼のシステムがリアルタイム向けに設計されているのに対し、これから説明するシステムはオフラインで、非リアルタイムという媒体の利点を活かそうとするものだ。
EvoGrain
本節では、私が書いた L-system グラニュラーシンセサイザー、仮称 EvoGrain を紹介する。EvoGrain はオフラインのプログラムで、数秒から30 秒程度のオーディオバッファを合成する。その産物は完成した楽曲ではなく、サンプリングや変形に使う素材だ。もちろん、結果そのものが面白く聴こえるなら、それはおまけだ。
私の見方では、EvoGrain はシンセサイザーとアルゴリズム作曲システムの中間に位置する。(アルゴリズム音響デザインシステムの先行研究については11を参照。)EvoGrain は真に自律的であることも目指している。ユーザーに公開するパラメータはごくわずかで、挙動の大部分はランダムだ。EvoGrain を繰り返し実行すれば、多様な結果が得られるはずだ。
EvoGrain は完全にルールベースのシステムとして設計されている。外部の訓練データは使わず、既存の音や音楽スタイルを意図的に模倣することもしない。
EvoGrain のアーキテクチャは次のとおり。
- L-system ランダマイザーが L-system の規則と初期タートル状態を生成する。このランダマイザーの確率分布と挙動が出力に大きく影響する。
- L-system を実行し、タートルコマンドの列を生成する。
- タートルがコマンド列を解釈し、グレインの集合を生成する。各グレインは開始時刻、長さ、音高、合成パラメータを含むキーと値のデータの組だ。
- いくつかの暫定的なグローバル処理でグレイン集合を「調整」し、音楽性を高める。
- グレイン集合をオーディオファイルに合成し、可視化用のグラフィックスを描画する。
- グレイン集合の情報を統計的に分析し、適応度の値を算出する。
- 異なる L-system に対してこの流れ全体を繰り返し、基本的な最適化プロセスでその中から最良の結果を選び出す。
データフロー図は次のとおり。

以下では各ステップを節ごとに説明する。一番下の層から上へと進むと理解しやすい。
合成:グレイン集合から音声へ
EvoGrain には基本的なグラニュラー合成エンジンが組み込まれている。DSP と合成の部分はあえてシンプルで凡庸にした。このプロジェクトで注目してほしいのはグレインの配置とパラメータだからだ。
シンセサイザーの仕事は、グレイン集合(グレインの集まり)からステレオ音声ファイルを生成することだ。1 つの集合には通常 2 万個ほどのグレインが含まれる。各グレインは次のキーと値のデータから合成される。
- グレインの開始時刻。
- 長さ。
- 振幅。
- ステレオパン。
- 距離:リバーブのセンド量。
- 振幅エンベロープ:矩形、減衰、逆減衰、Hann 窓。
- 音高。
- 合成エンジン:基本波形なら「Basic」、2-op サイン波の位相変調なら「PM」。
- 音色パラメータ:
- Basic エンジン:波形はサイン波か三角波を選べる。最初は矩形波とノコギリ波も Basic エンジンに加えていたが、あまり良い結果にならなかったので外した。
- PM エンジン:index と ratio。
- 隣接開始間隔(IOI):次のグレインの開始時刻までの公称距離。(シンセサイザーは実際にはこれを使わないが、グレインのパラメータとして扱っておくと都合がよい。)
オシレータにはアンチエイリアス処理をしていない。まあいい。聴いて問題ない。
振幅補正をいくつか入れた。音高が基本周波数 1000 Hz を超えると音量が徐々に下がり、耳に刺さる高域を和らげる。長さが長い場合も振幅をわずかに下げる。
「距離」パラメータの主な役割は、グレインをリバーブへ送り込むことだ。ステレオ入力・ステレオ出力の 16x16 FDN リバーブを実装した。混合行列のアイデアは Menzer と Faller の論文12に基づき、エネルギー保存型の LFO 変調オールパスフィルタを使っている。13 このリバーブは出来が良いとは言えないし、調整にもほとんど時間をかけていないが、実用には足りる。リバーブを入れるかどうかはしばらく迷ったが、入れてみると結果が聴き取りやすくなった。ヘッドホンで開発中に何十個も試聴することを考えれば、これはかなり重要だ。EvoGrain はミックス済みのステレオファイルを出力するほか、dry、wet(リバーブ前)、wet(リバーブ後)のファイルも別々に出力するので、ユーザーが自分でミックスできる。
振幅(dB)、音高(MIDI ノート番号)、長さ、IOI、PM の比率といったグレインパラメータは、エンコード時に実際の値ではなくパラメータの対数を使っている。グレインパラメータから合成パラメータへの写像は他の非線形なものでもよく、たとえば音高パラメータをある音階に量子化するといったこともできる。
調整:グレイン集合を改善する
「調整」とは、生成後にグレイン集合を編集する操作を私がそう呼んでいるものだ。調整は L-system パイプラインの標準的な工程ではないが、結果の音楽性を高めてくれる。現在行っている調整の手順は次のとおり。
- グレイン全体の時間的な広がりを最初から最後までスケールし、目標の長さに収める。
- すべてのグレインの長さをまとめてスケールし、中央値を 20 ミリ秒にする(値はユーザーが変更できる)。
- 多数のグレインが重なり合っている場合は振幅を少し下げ、ダイナミックレンジが大きくなりすぎないようにする。コンプレッサーに少し似ている。
もっと複雑な調整も可能で、たとえばパンを補正して中央に寄せるといったこともできる。調整の各ステップはすべて決定的に保つことを勧める。
解釈:コマンド列からグレイン集合へ
L-system を軽く検索すれば、それを使って生成された美しいフラクタルの数々が見つかる。文字列書き換えシステムから図形出力へ至る間の工程を担うのが turtle graphics だ。教科書でこのモデルを説明するときは、2D 空間にいるタートルを渡し、その状態には位置や向き、場合によってはその他の情報が含まれ、そこへ「1 単位前進」「30 度回転」「色を緑に設定」といったコマンドを送る。コマンド列を解釈すると状態が変わり、最終的に図形が描かれる。
EvoGrain のやり方は本質的にグラフィカルではないが、turtle graphics にかなり近い。EvoGrain のタートルは次の要素からなる。
- グレインの各パラメータ(開始時刻、IOI、長さ、パン、振幅エンベロープなど)を保持する「タートルの状態」。
- タートルの状態を格納する FIFO スタック。初期状態は空。
タートルへのコマンドは次のとおり。
Grain(g):タートルの現在の状態にある全パラメータでグレインを 1 つ生成し、開始時刻を IOI の分だけ先へ進める。MoveParam(param, delta)(m):ある数値パラメータを相対量だけ動かす。たとえばMoveParam(Pitch, +2)は音高を 2 半音上げる。このコマンドは次の節で変更する。SetParam(param, value)(s):ある数値パラメータを絶対値に設定する。FlipPan(p):パンパラメータを左右反転する。SetEnvelope(type)(e):振幅エンベロープを設定する。SetSynthEngine(engine)(y):合成エンジンを設定する。SetBasicWaveform(waveform)(w):Basic エンジンの波形を設定する。Push([):すべてのパラメータ(時刻を含む)をスタック(FIFO)にプッシュする。Pop(]):スタックの先頭の項目をポップし、タートルの現在のパラメータをその項目のパラメータに設定する。PushSequence(<):「Push」と同じ。PopSequence(>):スタックの先頭の項目をポップし、タートルの現在のパラメータをその項目のパラメータに設定する。ただしタートルの現在の時刻パラメータは変えない。
明らかに、Grain、MoveParam、SetParam がここでは最も重要な 3 つだ。Grain コマンドを 10 個続けて解釈すると、完全に同一で時間的に均等に並んだ 10 個のグレインが得られる。その間に小さな増分を持つ MoveParam コマンドを挟めば、各グレインが互いに異なり、滑らかな音型が形づくられる。さらに、絶対的なパラメータ変化(SetParam)と相対的なパラメータ変化(MoveParam)のバランスもある。MoveParam が主体ならパラメータは徐々に動き、SetParam が主体ならグレインの跳躍が多くなる(L-system の文脈では、一貫した具体的な値へ跳ぶことが多い)。これら 3 種類のコマンドの相対的な出現頻度には音楽的な意味があり、変えればその効果はすぐに聴き取れる。
四つの「括弧コマンド」Push/Pop/PushSequence/PopSequence は反復構造を作るのに重要だ(もっとも、L-system 自体も反復を作れる)。鍵になるのは Push と Pop で、これによってタートルは時間をさかのぼって移動でき、ポリフォニーが生まれる。
コマンドはいくらでもいい加減でかまわない。たとえば FlipPan を加えたのは、L チャンネルと R チャンネルの間を行き来する音が心地よかったからだけだ。よく設計されたコマンド群は、システムが良い結果を出しやすくする。タートルコマンドはほかにも思いつくだろう。私が考えたが試していないものには、次のようなものがある。速度を設定し、それ以降の MoveParam コマンドの増分に掛け合わせることで、タートルの動きを遅くしたり速くしたり反転させたりするもの。SetParam の変種で、新しい値へ一気に跳ぶのではなく現在値と新値の平均へ移動するもの。スタックの現在のサイズでグレインのパラメータに影響を与えるもの。音高を音階に量子化する機能を有効化・無効化するコマンド。そして時間上にマーカーを作り、近くのグレインを変化させる(引き寄せたり反発させたりする)コマンド。
境界の扱い
MoveParam には大きな問題がある。タートルがとんでもないパラメータ値に踏み込み、そのまま無限の彼方まで突き進むのを止めるロジックが何もないのだ。SetParam や Push/Pop のコマンドを多めに使えば緩和できるが、根絶はできない。素朴な方法は、範囲外のパラメータを境界内にクリップすることだが、これだとタートルが境界に張り付いてしまい、とりわけ音高ではひどい結果になる。境界の間でタートルを鏡映する方法もあるが、その場しのぎに見えるうえ動きが音楽的でなく、タートルが二つの境界の間を行ったり来たりすることが多い。パラメータの逸脱を放置し、より上位の層で処理する——そういう問題を起こした L-system を捨てたりペナルティを与えたりする——という手もある。
だが私が最も気に入っているのは Manousakis の「しきい値」演算子だ。相対パラメータ設定のロジックをもっと複雑にするという発想である。MoveParam に新しいフィールドを加える。
MoveParam(param, delta, reset):パラメータを相対量deltaだけ動かす。結果の値が範囲外なら、パラメータを絶対値resetにリセットする。
「リセット」値には、常に境界内に収まる値を選ぶべきだ。場当たり的な策ではあるが、ジャズの即興演奏者の振る舞いを考えれば納得がいく。楽器の最高音域まで駆け上がったら、通常の音域へ跳び戻すのは自然なことだ。とりわけ極端な音域が疲労を伴うならなおさらである。
とはいえ、それでもタートルは境界の極値まで突っ走ってしまい、私はこれが音楽的でないと感じ続けた。この問題を解決するためにファジー論理を使った。各パラメータに台形関数 f(x) を割り当てる。完全に境界内なら 1、完全に範囲外なら 0、その間は線形に遷移する。
MoveParam(param, delta, reset):パラメータを相対量 delta だけ動かす。x を結果の値とする。パラメータを x f(x) + reset (1 - f(x)) に設定する。
つまり f(x) = 0(境界内)ならパラメータは通常どおり動き、f(x) = 1(範囲外)ならリセットし、それ以外は二つの挙動の間を線形に補間する。この方法が気に入っているのは、境界に近づくにつれて各パラメータ上のグレインの周辺分布が徐々に狭まっていくからだ。
「境界への抱擁」はアルゴリズム音楽でよく知られた問題だ。Brown らによるアルゴリズム旋律生成の論文14 がこの問題を論じ、旋律生成に「ラバーバンド」を使っている。新しい音を加えるたびに、音高を音域の中心と平均するのだ。この「ラバーバンド」も上のファジー論理の手法の着想元になっている。タートルに境界を設定するのは身勝手なことではない。自己への気遣いの実践なのだ。
拡張:L-system からコマンド列へ
L-system を紹介した良質な資料は多い。たとえば Prusinkiewicz の 1986 年の論文だ。15 ここでも用語を整理するために L-system をもう一度たどっておく。L-system は 記号 のアルファベットから成り、記号は書き換えの対象となる 変数 か、書き換えシステムが触らない 終端記号 のどちらかである。ここでは終端記号がそのままタートルへのコマンドになる。L-system にはさらに、公理 として指定された一つの変数と、各変数をちょうど一つの記号列(後継)へ写す 規則 の集合がある。L-system を実行するときは公理だけを含む文字列から始め、規則に従って変数を書き換え、世代ごとに新しい文字列を生成していく。ここで扱う L-system は決定的で文脈自由な(「DOL-system」)ものだ。
多くの L-system はどんどん長くなる文字列を際限なく生成し続けるが、ここでは有限の結果が必要になる。一つの方法は有限世代だけ反復すること。私が最終的に使ったのは Iamus の論文にヒントを得た手法で、各規則に 予算 を割り当てる。正の整数で、その規則を何回呼び出せるかを定める。規則がその回数を使い切ると、対応する変数は空文字列に置き換えられる。
L-system の例の多くは有限のアルファベットを使うが、これはそうではない。コマンドが SetParam(Pitch, +60.234) のような引数を取れるからだ。ただしコマンドは終端記号であり、規則はコマンドについて何も知らない。
ここまでに述べたすべての段階——拡張、解釈、調整、合成——は決定的であり、L-system が生成する音声ファイルを明示的に符号化している。確率的 L-system の文献はかなりあるが、この文脈では決定性が最善だ。ランダム性という交絡因子を挟まずに、各段階を調整し、変更が結果にどう影響するかを観察できるのはよいことである。
ランダム化:L-system の生成
ここまで L-system から最終的な音声ファイルまでの流れを逆にたどってきた。次は流れの最初の段階、L-system そのものの生成だ。この段階はきわめて開かれており、私のやり方に改良や拡張の余地があるのは間違いない。
L-system の最大の利点は、階層的な出力をモデル化できることにある。音楽の多くは多かれ少なかれ階層的だ。作品全体がセクションに分かれ、セクションがフレーズに分かれ、フレーズが音型に分かれ、音型が音符に分かれる。作品全体を生成するつもりはないが、EvoGrain の出力で最も魅力的なのは、完成した作品が持ちうるのと同じような、明確なフラクタル的構造を備えている点だと思う。16
L-system の例の多くは手書きで、規則と記号が数個しかない。ここでの L-system はもっと大規模だ。規則が 50 個あり、各規則は最大で数百個の記号を含む。ここでの 50 個の変数——L-system の中にだけ存在し、他の記号に置き換えられる記号——は数字だけで表し、#0、#1、#2……と名付ける。このうち #0 が公理だ。記号は二つのグループに分かれる。#0–#19 はマクロ変数で、グレイン集合の中で最大の構造を構成する変数とみなす。#20–#49 はマイクロ変数で、グレインが数個しかない小さな音型を生む。20–30 の境界が適切だと思うが、マクロ変数とマイクロ変数の数はどちらも調整できる。
ランダマイザは次の論理で L-system の規則を生成する。
- マクロ変数 #n の後継は約 15 個の記号からなる文字列で、各記号は:
- 90% の確率で、#(i + 1) から #(i + 4) まで(両端を含む)からランダムに選んだマクロ変数。
- 10% の確率で、ランダムなタートルコマンド。
- マイクロ変数の後継は 50–500 個の記号からなる文字列で、各記号は:
- 95% の確率で、ランダムなタートルコマンド。
- 5% の確率で、マイクロ変数。
つまり #0 が作品全体で、#1 が最上位の構造、#2 がその次の層、以下同様になる。ただし完全な入れ子にはならない。#0 が階層を飛ばして直接 #4 を呼べるからだ。後ろのマクロ変数はマイクロ変数に展開でき、マイクロ変数同士は無限に相互参照できる。
後継の記号数は生成されるコマンド列の長さに影響し、最終的には結果のグレイン数を決める。だからここを正しく調整することがかなり重要になる。
ランダムなタートルコマンドを生成するには、帽子から直接引く。確率は MoveParam と Grain が最も多く、SetParam が比較的少なく、他のコマンドはさらに珍しくなるように選んである。括弧コマンド [ ] < > は他のすべてのコマンドの後に挿入し、必ず対になるようにする。挿入点の組をランダムに選び、ランダムな Dyck word を生成するのだ。17 どれほど必要かは分からないが、対にならないと気持ちが悪い。
SetParam と MoveParam のコマンドは数値フィールドを持ち、ランダマイザが値を与えなければならない。値の設定のために、合成パラメータごとに二つの確率分布を手で定義した。絶対分布と、グレインのパラメータごとの増分分布だ。たとえば音高なら、絶対分布は MIDI ノート番号 30 から 120 までの一様分布、増分分布は標準偏差 3.0 の正規分布になる。ランダマイザが SetParam(Pitch, <value>) を作るときは絶対分布から値をサンプリングし、MoveParam(Pitch, <delta>, <reset>) の増分は増分分布からサンプリングする(リセット値は絶対分布から取る)。ほとんどの増分分布は 0 について対称だが、振幅(dB 単位)の分布は偏っていて、振幅はグレインごとに増えるより減る方が起こりやすい。これは減衰するエコーを漠然と模倣していて、聞いていて悪くない。これらの分布を L-system ランダマイザだけが知っていて、コマンドを生成するのに使う。タートルはこれらをサンプリングしない。コマンド列の解釈は決定的だからだ。
例として、EvoGrain がランダムに生成した L-system の規則を以下に示す。これは本稿の冒頭にも現れ、後でグレイン集合 #6 としても登場する。各コマンドは一文字で表すので、簡潔さのために SetParam(s)、MoveParam(m)などのパラメータは示さない。これらの規則を直接見てもグレイン集合の理解にはあまり役立たず、ランダマイザの挙動が分かるだけだ。
#0 [budget 5] -> #1<#1#2<#4#2>m#1>#4#4#1#4[#4]#1
#1 [budget 6] -> #5<g[#5]#2g<#3>#3>
#2 [budget 9] -> [#3#3[#4]#5[m]#4<#3<#4[g]#3>#3>#3]
#3 [budget 11] -> m<#6#7>#7<#6>#6#7#4#4<#6#6[#6#7]#4#7>#6
#4 [budget 10] -> #7#7[#7#7]#6<#6#5<#5[#5m]#8#7[#7]#6>#7[#7]#6#8>#8#8
#5 [budget 13] -> [#6[#6<m>#8[#6]#7[#9#7]#8]#7]
#6 [budget 14] -> #10<#10<#8#8>#10#7#8#8<g#8gg#9>#8#9>
#7 [budget 12] -> <#9#9<#10m[#9]#9#9#9>#9[#11]#9#10>#8#8#9
#8 [budget 9] -> [#10[#12[m]#9]#10#9#11]#11#10#10[#12]#11<#12>#10
#9 [budget 10] -> #11#10<#11#10#10#12#10#11#13#10#12#13#12m>#11#13m#12g#10
#10 [budget 8] -> <#14#14[#13]#11<#13>#13>#14#14<#11[#12]#13>#13[#13#12[m]#12]
#11 [budget 10] -> #15#15#13#15#12[#15#15#12]#13#15#12#14#15#14[#14#12<#12>#12]
#12 [budget 9] -> #15#15[#15]#15<#15>#16<#14#13>
#13 [budget 13] -> <#16>#16#16[#16<#16[m]g>#17]
#14 [budget 8] -> <#16#15<#16>#18>#16[#17]#15
#15 [budget 11] -> <#18m<#18>#18[#17[#18[#16<#17#17>#16]#17]#16]#16>
#16 [budget 8] -> [#18#19#18<#17#17#19>#18]#19#17m#17#17#19#19#17#18[#17]#19#19#19
#17 [budget 17] -> #20<g#19#18#21>#21[#18]
#18 [budget 15] -> #19g#20<#22>#20#22[m#20#20#20]#20#20
#19 [budget 17] -> #20<#20#23#22#22#21#21#20#21#23[#22#23[#23]#20#20]#23#21#20>
#20 [budget 11] -> #21gmmmggmmggmmmmmmm#36gm#22mgggmmgm#25#26mm#31<#35[#30mg]m#23#33gmgmmmmmmgmmmmmmmm#34mgmmmmmm<mm[mmmmgmmmgmgm]mgmmgmm#21mmmggmmmmmgmggmm#32gmmgmgmmgmgggmmgmmmm[g#43mmgmmmmgmmgmmmgmm#46mggggmm#20#27gmmggmgmmggmwymmmmmm]mmmgmmmmmmmmgmm>wgmmmmmmmmmm#41m#31mmmmggggmggmy#37>mmmmmmmmgmmgmmgmgmmmggg#35gmgm
#21 [budget 17] -> mggmmgmmmgmmmmm<mmgmmmmgmgmmmmgmmmmgmmg<mmmmmmmmmmmgmmmmm[gm]gmgmgmmmmmgmmm#36mmmmggmm>mgmmmmggmmmmmgmmmm#30m>gmmmgmmmmmgmgmmmmmmmmgmgmgmgmgmmmmmmmmmmmmgmmmmgmmmmmgm#24mg#24mmmmmmmmg#40mmmmgmmmgmmmmmmgm#42g#43#47mmmmgmmy#22wggmgmgggmmmgmgmmmmgmmmmmgmgmmgmmgwmmmmmmmmgmmgmmmggmmgmmmgmmmmgmgg
#22 [budget 13] -> m[mmgmggmmgmmmmmmmmmgmmmmgmmggs]mgmmmgm[mmgmg#37mmgggmmmmgmmmmmmmmmmmmmgmmmmmmmgmmmmggemmmggmgm#42mmsggmmmgmmmggggmmmpmmgg#22gmmmm[mmmmmmm]#34gmmmgmgmmmggggmg#31m]mmmgmmmmmmmmmmgm#28mmmgmmggmggmmmmggmmmmmm
#23 [budget 8] -> mmmgmm#42ggggmmmmmgmmmsmmmmmmmgggmmmmmmm#31#20ggmmg<mmmgmmgmmmmggmm#27mmpmmm#32mmgmggmmm#37mmmggggmmgmgmmgmmmmmgg#41mmmmmggmgm#46gmgmgmmmmggmgmgsmmmmgmmgmgmmmmmmmmp#26mgmmmmmgggmmgm#22mgmmmmgmmgmmmmgmgmggggmg<gmgmmggmmmmgmm>#36mmggmmmmmgmmmmmmmmgmmmmmmmmmgmggmmmgmmmggmggm>mmggmmmgmmmgmggm#43gmmmmmmmmmgg#43mggmgmmm#39mggmmmmmmgmmmmmmmmmgmmmmgmmgmgmm
#24 [budget 9] -> mmmm<mmmggmmmgmmggmgmm#37mgmgmmmm[mmgmmmmggmmgm]gmmmmmmmmmmmg[mgmmmmmmygmmgmmmmmgmmmg#31m#32ggmmm#47m<gm>m#35gmggmmm#40gmgpmmmmmgmgmmm#34mmmmmm#37#42sm#36mmmgggmmgmgm#47m[ggmmmmmm#26mggmmmmgmmm]mmg[mgm#47mmmmmmm#40mmggmmmm#31gmgmgmmmmmmmg#29gmmmmmmgmmmgmmg#45mmm#44#36mgmmmmm<mmm#38<mgggmgmmgmmmm#25mmmmmggmmmmmmmmmmm#44mmggmg#33ggmmm#46gmggmg>mm#28#45gmmgmgmggmmmmmmgmggmgmmmm#27mgmmmmmmm#30mm#37gmgmmmmmgmmmgmgggm>mgmmmmmmmmmmgmmggggymmgmgmmgmmmmmmggmgmmmmmmmggmmmmmmmmmmgmmm]ggm#48mmmmgmgmmmmmmsmymmmeggmmgmmmmmmmmggmgmmmmgmmmmmm]mmmm>gm#33megmmgmmmmmmmmmgmmmgmmmmgmmmmmmm
#25 [budget 14] -> gmmmmmmggmmmgm<gmggmmmmmgmgmmmmmmmmmm#22mmmmmgmmgmmmmmg#34>mmgmgygmmgmegmmmmg<mm<m#25mmmmgg>m<mmm>g>mgmm[mgmm]gmgmmm[mgmm]mm<mgmggmgmmmm>gymmggm
#26 [budget 13] -> ggmmmmmgmgggmmmm<mgmgmggmmmmmgmmmmmmmmmgmm#33mggg[mmgmmm#37mmgmmgmmgg#25]#44gmm#40<mmmm#34gmmmggmggmmmgm#41mgmmgmmgmmmgmmmmmgg>mgmmmm>gmgmggmgmmgggmgw<gmmgmmgg>mmmgmgwgmmggm[mmgsmmgmgm]mmmm#38gmmmmmmmgm#27mmggmmmmmmmmgmmmgmgmggmg#44mgmmmmmmmmmpmggmmmgmmmmmmmmg#47mgmmmm#28mmmmmgymmmgmmmgmgmgmmmmmmmmmmgmmmmygmmmmgmmmggmgmm#43gg
#27 [budget 7] -> ggmm[mggmmmmgm#27ggmgmmmmmmm]#28mgmmmmmmmmmmmmmmmmmgmmmg<gmmmgmmggmmmmggmmggmmm#26#36mmggmmmp#37mmmmmm#31ggmmmmmmmmwmggmgggmmmmgmmgmmmgmmmggmmg#24mmmmmmmmmgm#20m#38gmmg#25#22mmmmgm>mmmmmmmgmmmmmmgmmgmgmgggmg#38#44mgmmgmmmmmgmm#44mmmmmmmmmgmgmmmgm#47mmmgmm[gmmmmg#47mmmgggggmmmgmm[mmmmmpmmgmgmmmmmgmgmmmgggmm#27m]mmmmmmggmgwgg#42mm#31ggmmmgmmgm]gmmmmggmmmmmmmgggmg#24ggmmm
#28 [budget 11] -> mmmmgmmgmmmmgmmg#41mmmmgmmmmmmmmmmmgmgmmgmgmmmmmmgmmmmmmmmgmmgmggmgmgmgmgemmmmgpmmmggmgggmg#39<mmmgggymmmgmgggmmmg#40mmmgmmgsmmgmggmmgmmgmmmmmgm#47mgmmmmgmmm<gmggmgm[mmmmmgmgg#44mmg]m>mgmmmggmgym>mmgggmgmmmm#42ggmgmmg<mmmmggmggmmyggmmggmmmmmggmmmmmmggggmmggggggmmgmmggmmmgmmgmgsggmmmmgmmmmgmm#32m#20gmmmgmmmgm<gmgmmmym#48mmggg>mmgmmmgmmmmmmmmmmmmgmmmmggmmmmmmmmmmm#32mmgmgmmmmg#31mgmmmmmmggggmggggmmgmmmmgmmgggggmmmgmmmm>mgmmmmmgmmmmmmggmmmgmggmmmmmmmmm#30g#29gmm#31
#29 [budget 14] -> <mmmmmmmmmmmmmg[mm<mmmgmgmmgmmmgm>m]ygmmmmwgmmmmmgmmmmgmmmmmmmmmgmmmmmmmg#31ggmggmgggmg#41<mmmmmmmmmggmggwgmmg#20mggmmgmmmgmmggemgmggmgmmmmmmmgmmgmmm#39ggmmgmgmgmmm#20mgmmgy#49mmm#20gmmgmmmmgmmmgggmmmgm#22mmmmgmmggmggmmgmmgmgm#40gmgm#21gmm>mmmmmgmmmmgmmymggmmggggg>gmg#29gymmygmmmm#27#37m#44m#33mmmgmm
#30 [budget 7] -> mmmm<mmmmmgggm[mmmmggmgmgmmmmgmmm]gmmmmmgmmmmgmggmmmgmmmgmmgmmmgmmm>g#40gmmmmsgmmm#49mmmmm#21gmmmmemmmmmgmmmmgmmmgmgmgmggmmmm#37#45mmm#25mmmgmm#28wmmmggg#29#36ggm#20mm
#31 [budget 9] -> mggmgmggmm[mmm#32mmgmmgmgg#39ggmmmmmm#22mmm#21gmggmmgm#21mmgmggm#38ggmmmg#27mmmm#25gmgmmggmmmmgm#39ggmmmmmmmmmmmmg#35mmmwgmmmmmmmmmmsmmgm#24mmmmmgmmggmmm#49gmmmgmmmggmmgmmgmmmygmgm#48m#41gmggggwmmmgggmmmgmmmgmmmmmgm#46mgmgmmmmmmmmmmmgmmmmmmmmmmmmmmmsmmgm#27mmg[mgmggggmgwgmmmggmg#22mggmgmmmmgmmmmmmggmm#23gmgmmmmmgmmmmmgg]mmmmmgmm#24mg]#23gmgmmggmmmmm#48
#32 [budget 13] -> mmmmgggg#39mmmmmmmgmgmmmmgmggmgmmmmmgmm#25g#48mmgmmm[mmmmgggggggmmm#34gmm#34mm#28mmmmmmgmmmmgmm#31mmmmmmmg#35mmgmmm#46mmmmmmgmmmmgmmmm#42gmmmmmmggmmmmgggmmmm]ymgmmmwggmmmmmm#35mmemmmsmmmm#23mgmmmmm[gmmgmmgmm#27mmm#44mgmmmg#44#44mmgmgmmmmgmgg#28mmmmgmgmmmmmgmmgmmgmgmgmgmmmgmgmmgmmmmg]gmgmgmmmm<#47mgmmgmmmgmmmmmmmm#35mmmmmmgm#26mgmmgmgmmgmgmmmmgmmgmgmmmmmm#36mmgmgmmgmmmmgmmym#23gwgmmmmgmmmggm#40mmmmgmmgm#40mmmmmgmmmgmgmmgmgmmmgmmgmmmmmmgmg#42mgmmmmggmmmmm#25mmmmmmgmm>mmmmmmmmmmmmgggmgmmggmm#28gmmgmg
#33 [budget 5] -> gmggmggmmm[m#42g<mgmgmmgmgmmmm#27m>ggm#22mmmgggggmmgmggmggm#27#42mmmmmggmggmmgmmg#29mmggmmmmgmgm#47mmmmmmmmmmmmgmg#22gmm#21mmmgmggmmmggmmmmgmmmmgp#28#33gmgmmwmgmmmmggmgmgmgmmmmgmmmggmmmmmmmmm#24mmpmmgmgmgmmmmmggmmgmgmmgmmmmgmgmmgmggmgmgwmmmmmmmmmmmmm]mm#33mgmmmmmmmggmgmmmgemg#33gmmmmmmgmmgmmmmgg
#34 [budget 10] -> mggg#24gmmm#28mmggmmgmgmmmmgmmgmggmmmgm#33ggmmggmmmmmmmgm#39mmmmgmmmggmmmmg#49mmg<mgsmmmmggmmmmmmmmmgmmmgmmmmmm>mmg#43gmmm#49mm<mmmgmmmggmmmmmgmmegmmgg#36mgmmmm>mmmgmmm<g>mmmmgm#46m[ggmmgggmggmm#49mmmmmmmmmmmmgmm#22gmggmmmgmmmgmmmmmmmm#31ggmmggmmmgmmmmmmg#26mmgmgmggmmgmmmmmmmmm[mm]mmmmg]msmggmmwm[ge]m#40mgmmmggmgmmmgmmmmmmmgmmmgm
#35 [budget 13] -> gmm[gm<mgmgmm#28gmmmm[mgm]mmgm#46>mmmgmgmgmg#36mgm#48m#24gggmm<mmmmmggm[mmg[mmg]gm]m#41>m]gm[gmmm#22mmmmmmmmm]mmygmm#44mmmgmggmmmm
#36 [budget 13] -> mmmgmmm[gmm]gmmm#42mmmm[ggggmm<gmmmg>gp#26mmggg<m>mm]mmg[g#42<mg>mmmm]mwg
#37 [budget 11] -> ggmmgmg#38g#43gmgm#26mmmmgmmmmgm#29#28ymmmmmgggmmmggggmmmmgw#20mgmmm[m[mgmgm#24mmmmggggmmmgmmmmmmgmmmgmm[g#49mmmmgmggmmmg#21]mmmgmmmmmmmggmmmmmmmgpmgmgmmmgmpgmmm[gs#30mmmm]gmmgm]mmgmm#33mmm[#35mmmmmm]gmgme]mmmmmmgggggmmm#45gmmm
#38 [budget 9] -> mmmgg#26mps#46mmmmsgggmgg#45gm[gm[mmgmmmggmmmgmmmggmgm#35mmwgmm#34mm[mmmgmmmmm#33mmmmmm#25m]mmgmgmmmggm<gggm<gg>mmmmmmgmmmgmmggmmggg[gmgmmgmmmmmgmm<mgg>mmgmgmgmggm]mmmmmmgm<mgmmgm>mgmmggm#32mgmmmmmmmm>mgmgmmgmmgmmmmmmmmmmmmmg#32mmmmgg<mmmgmmmem>mm<mmgmmmgmm>mmg#41gg#40mmmmmmmmmmmggmmmm#28mmgmm#29m#32g]m]mmmmgg
#39 [budget 10] -> mmgmmmgmgmgmmmmmmgmmmmmmmmmmgmmggm#33gmmg[ggmmmmmmmgmmgmmmmgmmggmm#21<m#30#32mgmmmgmmmmgmgmgggm<#29#35mmgmmmgmmmmgm#41mmmmggggmmmmggmmgmgmgmmggmgggmmmmmggmmmmmmmmmmmmgmm>mgmmgggmmgmmwmmmmg#24mmmmmmmgmgmggmmmmmgmmmmmmmgp#45ggmmmggmmmmgmm#40gmgmmmmmmm#21mgggmmmgm#47mmgmmgmmgm#25mmmmmmmmggmmmmmmmgmggmmggmmmmmmmymgggmmmgmggmmmm>mmmmmgmmmmmmggmmmmggmmmmgmgggmg]mmmggmmggmgmm
#40 [budget 15] -> #21mmm<gmggmgm[ggggmmgmg#28mgmgmm]wgmmmmmgggmmmmgm#23g>mmmgmmm#42#23mmmsmgmmmgggmmmmmmgm#42mmm<m#48ggmmggmgmgmmgm>m#38mm<mmsmmgmmmg<mgmgmmmmggmmgmm>mmg#24gmmm#33ggmg#31m>mmmgmggmmmmm<mmmmgmmmgmgmmmgmmmmm>mmggmmmmgmgmmm#32ggg#23mmmgmgmmgmgmmmmmgmmmggmggmmmmgmmmmmggmgmmgmg#32mmgmmmggmmmmg
#41 [budget 18] -> mwpmmm#45gmgg[mmg#35m[mmgms#23mmmmm<mggggmgmgmmmmmmgmmmmmmmmmggmmmgmggmmmmgmmemmmggmmmg>mmmgmmmmmmm#43gmmmmmgmmm#27mgmmmmmmmm#37mmmmmgm#41mmmmmmmmmmm#48mmmmmgm#22mmgmgmmmgmggmmmmg#21mgmmmmmgmmmmgmm#20mgmmmmmmmmmm#44mmgm]gg#29gmmmmgmgmm[mmmmgmgmmgmmmmmmmmmwmm<gmmmmmmggmmmmgmmmmgmmgmmmw>mmmgg]mmgmmgmmmgmmmgmmmm<egmmymmmm#23mmgmgmmmggmmmwgmmmmmmmgmmm#23ggmgmwgmmmmmggmm[mmmmm]msg#32mgm>mmmmg#22mgmgmmmmpwg#45gmg]mmm#37mgggggmmmgg#41mmgmmm#31mgg
#42 [budget 9] -> mmmmmmmm#44mggmgmmmgmmmmggmmgmm#35[mgmmmm#22mmgmmw#36gmmmmmmmmgmm#34gmmggmmmgmmmmg<ggmmmggmg#27mmgmpmgmmmm#36mmggmym#34gmmmmgmgmgmmmmmm[mmgmmwgmmmgmgmgmmmgmmmgmmm#31mmmggmmgggmmmmmmggmmmmmmgggmmmmmgm]mgmmmmmmgg#45mmgmmgmmmgmmmmgmm#20mgmmggmmmmm[mmgggmmgmmgmmgmmmmgmmmmmmm#33gm[gmmmm#30ggmggmgmmggmgpmg#43mmmmmgmmmmgmmmgmmmmymmm#44gmmg#47mgmmm#28#40mmmmmmmgmgmmmgmmmmmmgmmmgmggmmgm]mm#36mgmgmgmgmmg#49mggmmmmmgmgggmmm]gm#34mgmgmmm>m#29mmmmm]mmmggmmmmmmgmgmmgmmmm#20mmmgggmgmggymgmmgmggmm#21gmgmmmmmmmmmmmm
#43 [budget 8] -> #21m[gmmmgmgmpgmmmgemmmgmm[m#33g#35mggmmmmm]sgmmmgggmmmgmmgggmgggmmmmmmgmmmmmmmmggmmmmmmgmgggmm#35mggmmmmgmm#39gmg#21wmmmmgmgmmmmmmpgmmg#33mmmmgmg[mmmggmmgmgmmmmmgmgmmmmmmmmmgmg#38mmmm#22mmmmmgmmmgmmmmmgmgwmmmmmm<mmmmmmpmmmmggmemmmmmwmgmmmmmgmmgmmmmm#48mmmmmgmmmmmmmgmm#49mmgmmgmmmgmm#26mmmm[sm#44gmmmmmggmggmmm]mm#29mmg<mmmmgmmgmmggmg#22mmmmgmmggmmmmgmmmmgmmmmmmmgmmmgmwmgmmmggmmmmmmm#33#45gmm<mmmm#43mgmmmmmmm#25mmmmmmg#37mmmmmmmmmmmggggmgmmm#48mmmm#20mmmm#38mm#42mgmm>mg>mgmmmgmmmmmmgmggmm#40>gggmmgmyg#36mmgmmmmmpmmmmmmmmgmgmggmmggmgmmmmgpgmm#47mmmm]#30mm]gmmmgggmmgmmgmgmmmggmmgm#36ggmmgmmmmmmmmmgmmgmmmggmm
#44 [budget 18] -> gmgm[mmmmmgmmgmmmmmmg#33gmmmgmgmmmgmggg#34mggmmmgmmgmgmgmmmm#39mmmmgmmgmmmmmmmmmmmmmggmmmmmmmmmmmmmmmmmgmmmm<ggmmmgmmmmmmmmgggmgmm<mmgmggmmgmmgmggmmggmmmmgmgmmg#43mmgmgm>mmgmg#26mmmmgmmmmmmmmmmgm>mmmmpmgmmmgmggmmmggm#24#22mmmmggmmmgmmmmmmgmgmmmmggmgmmgmgmgg<mmmgmmpgmmmmmmmggmmmgmmmmgmmgmmmmmggmmmg>mmmggmmmsgmmmgmmmmmmgmgmmmmygmmmgmmmmmmg#35g#20mmmgmmmmggmmgmgmmwmmmmmmmmmmgmmmm#33mmmmmmgmmmmgmgmm#34mmmmmm]mmmmmggpmggmmsmmmmgmmgg#43m#39mmggmmg#37m#35mgmmmmgmmggmgm#28mgmggmmmgmgmgggmmgmgmgm
#45 [budget 10] -> g[gm#24gggg#21gmmmmmggggmmmg#21m#49<gmmmg#43mmgmg<mmmmgmgmmmmmmm#42gggmmm#49m>gmmmmmgmmmggggggmmgmmmmmmmg#49mmggm[mmgm]gm#46gmmggm#32mm>g]mmegmsg[gmgmmgmmmggmmmgggmgmmgm]mmg#28mmmggggmgmmmmgmgmmm[gmgmmm]mmmm#48mgggmgmmgmmmggmmmmmgyg
#46 [budget 12] -> mmmmmm#36mmmmmmgmgmmmmgg#39mmmgmmmmmmmmmmmg[gmgmmmg]mggmgggmmmggmmwmmmgggmggmmmmmmg#32gmmgmmgm<mmmgmmmm>pggmgmmmm#42g#42gmmmggggmmmmmmgmmmgmmggmggm#25m<ggmmmmmmgmmmmmm>gmmmmmmmgmgmgmmmm#20ggmggmgm#21mmgggmgggmmmmmmm#42gmmmmgmgmgmmmmg#30m
#47 [budget 10] -> mgmmmmggg#39gmgmmmmmmgmmgmmggmmmmmmmmgmmmggggmmgmgmmmgmgmmmggm[gmgmgmggm#47#30mmmggmggmmmmmmmmgm]mmmmmm#39ggmm#40mgggmmmmgmgmmwmggmmmm#30m<mmmmmmmggmg#24mm#49mmmmmgmmmmmgemgmmmgggmgmgmgmmmg>#40ggmmm#44mmsmgwmmsmm#22mggmmmmmggm#31mmmmmmmgmgggggmmggmmmmmmmgmmmgmmmgmmgmmmmgmmmmgmgmggmgmy#39gmmmmmmggmmmmmgmmmmmmgmmmgmmg#38mmmmmmgmm#31mmmgmm
#48 [budget 11] -> mmmmmg#35gmgmmmmgmgmmmmsggmmmm#28gmgmmmmmmmmmm#32gmmggmgmmmg#43gmmgggmmmmmgmgggmmgmmgmym#20mgmmgmmgmmgm[ggmmmmmmmmmmm#21mggmmmmmmmmmmgmgm#29mmggmmgmgmmgmmgmmmmmmmmmggmgmmmmmmmmgggg#32m#22gmmmmmmmmwgmggpmmggmmggmmmgmmmmmmmgggmgmm<mgmmmmmgmmggmmmmmmmgmmmmmmgmmmgmmmgggmmmmmmmmmmmmgg#32mm>gmmmggmgmmmgmmgmmmmgmmmgmmmggmm#46mmgmgmm#41mmmgmmmgmmmgmsmmmmmgmggmm#24mgmmmmgmmgmmmmmmgmgmmmmmmmm#28mgmmm#38gmgmgmmmmmymgmmmmgm<m#31g>#36mmgmmmmmmmmgmgmmgmmgmmggmmmmmggmmmggmmgmgmmmmmmmgymmmmmmggmmmymmmmgmmmmmmmg]gmmggmmmmmmmmmmmmmmmmmm#23mm#26gmmgmmmmgg
#49 [budget 7] -> mmmmggm[mmmmmmmgmgmmmmmmmm#28mgmmggmgmmmmggmmm#44mmgmmgmmmmgggggmmgmmmpmmmgmmmm<m#21mgmmgggmmmmmmgmmmgmm[mgmgm]mmmgmgmmgmmmgmm#43mmmm#46gmmggg#23mgmegg#21m#28m#23mgm>mmmg#49mmmgmgmmmmmgmm#30gmmggmmmgmm[m]mmm#34mgggmg#49g#41gmmgm#43mgm]mmgmmmm#35mmmmmgmmggmm
評価:適応度関数
実際に走らせてみる以外に、L-system から最終的にどんなグレイン集合が得られるかを予測するのは難しい。したがって、その集合が basura である可能性は十分にある。グレインが少なすぎたり、多すぎたりするのだ。適応度関数はここで導入される。与えられたグレイン集合の良し悪しを評価することで、ループ全体を閉じる。
適応度を計算するときは、まずいくつかの記述統計量を求める。
- 1 秒あたりの平均グレイン数(総グレイン数 / 秒数)
- 音高の中央値と第 90 パーセンタイル
- 「重なり係数」の中央値(各グレインのデュレーション / IOI)
各特徴について台形関数を定義し、好ましい値により高い点を与える。たとえば音高の中央値は C4(中央ハ)付近がよく、F#6 を超えてほしくない。そこで台形関数を定義する。60 で 0 から始まり、70 で 1 に上がり、80 まで 1 を保ち、90 で 0 に下がる。グレインの実際の音高の中央値をこの台形関数に代入し、0 から 1 のスコアを得る。他の統計量も同じように採点し、最後に重み付き和を取って最終的な適応度指標とする。
この適応度関数を設計するにあたって優先したのは、実装が容易で、かつ好ましくない出力を避けるのに役立つ指標だ。現状の適応度関数は明らかなゴミを弾くだけで、美的な評価にはまったく届いていない。より精緻な指標は実現可能で、計算美学の手法を使えば主観的により良いグレイン集合を見つけられるだろう。具体的にどの指標を使うべきかは、自分でもまだ考えがまとまっていない。いずれにせよ、この方向を探求したい人には多目的最適化を勧める。一つの適応度値が明らかにひどい出力を識別して捨て、もう一つが美しさを選ぶのだ。
適応度関数に必要なのは、グレイン集合がひとつだけだ。先に音声を合成してから振幅やスペクトル重心などを計算し、それを時系列分析にかける手もある。2 年ほど前、このプロジェクトの失敗した試みで実際にそうやったことがある。だが適応度関数の設計が難しく、パラメータ調整も直感的でなく、合成と分析の 2 段階がイテレーションをかなり遅くした。それでも、合成アルゴリズムをフィードバックループに組み込むというアイデアの美的な可能性は、やはり魅力的だ。
探索:適応度が最も高い L-system を探す
自作ソフトウェアには「EvoGrain」という仮の名前を付けた。着想の中心は進化計算プロジェクトの Iamus だが、結局遺伝的アルゴリズムは使っていない。『アルゴリズム設計マニュアル』は、複雑さが増す割に割に合わないという理由で、最適化に GA を使うなとはっきり忠告している。ひとつの意見にすぎないが、私は真剣に受け止めた。そもそも EvoGrain の目的は最適解を見つけることではなく、聴いて心地よい多様な結果をまとめて出すことにある。つまりここでは最も基本的な最適化アルゴリズムで十分ということで、実際その通りだった。
最終的に採用した探索アルゴリズムはこうだ。まずランダムな L-system を 100 個生成してプールを作り、それぞれの適応度を評価して最も高いものを選ぶ。次に単純なランダムサーチを 10 回繰り返す。L-system の規則をひとつランダムに選んで再ランダム化し、新しい L-system の適応度が元より高ければ置き換える。適応度関数は各台形の適応度の和なので、到達可能な最大適応度が存在し、そこに達したら早期終了できる。(台形の適応度関数は計算芸術を評価するための優れた部品だという直感がある。芸術的な判断では、唯一の最適値ではなく、満足できる値の範囲が問題になることが多い。)
EvoGrain の初期に放棄したプロトタイプから得た教訓は、適応度関数や進化の側で過剰に設計しないことだ。そこが本当に見どころというわけではなく、このプロジェクトの労力の大半はランダマイザ、タートル命令セット、シンセサイザーに注がれている。
実装と結果
EvoGrain はオフラインのコマンドライン Rust プログラムで、約 2,000 行のコードからなる。専用の DSP ライブラリは一切使わず、WAV の出力にはhound クレートだけを使っている。出力ファイルは次のとおり。
- L-system。JSON にシリアライズされ、他のすべてのファイルを作るための完全なレシピになる。
- ステレオ WAV ファイル 4 つ。ドライ音、リバーブ入力、リバーブ出力、ドライ音とリバーブを混ぜた出力。
- テキスト形式の「成績表」。grains/sec や適応度など人間が読める統計量を表示し、パラメータ調整やデバッグに役立つ。
- 散布図(
plottersクレートで描画)。横軸が時間、縦軸が音高、色が合成エンジンを表す。
グレイン集合は予測できない。反復構造を持ち、耳にも目にも模様として見えるパターンを生むこともあれば、塊や雲のようなものになることもある。特定の時刻にグレインが縦に並んで密集することもある。いずれにせよ、そこには何らかの形がある。任意の位置から始まり任意の位置で終わる無限のテクスチャではなく、全体構造が常に存在し、しかも音声の終わりを先読みする(正確に言えば、音声ファイルがいつ終わるかを決める)。
EvoGrain は自分が掲げた目標のうち、「多様な出力を生む」以外はすべて達成したと思う。この点は議論の余地がある。大量の EvoGrain の出力を聴いてきたおかげで、私も多少は目が肥えた。私の耳には、結果の違いはかなりはっきりしている。だが、ただのこもったピーピー音にしか聞こえないと言われても、無理はないと思う。判断はあなたに任せる。
例
以下は EvoGrain の出力から選別せずに取った 10 例で、すべて同じデフォルトパラメータを使っている。横軸が時間、縦軸が音高、ピンクが Basic 合成エンジン、青が PM エンジンを表す。
ランダム化の繰り返しから探索、音声出力の生成まで、システム全体で数秒しかかからない。だからこれらの例も合わせて 1 分足らずで終わる。
グレイン集合 0

グレイン集合 1

グレイン集合 2

グレイン集合 3

グレイン集合 4

グレイン集合 5

グレイン集合 6

グレイン集合 7

グレイン集合 8

グレイン集合 9

おわりに
EvoGrain は研究プロジェクトだ。新しいサウンドデザインの秘密兵器を売り込むのではなく、この構想で何ができるかを見せることが主眼だ。ソースコードは公開しているので、使いたければ使ってほしい(ソースコード)。
EvoGrain のプロトタイプは半日ほどで書き上げ、出てきた音はそれなりに聴けるものだった。週末ひとつを費やせば、音はかなり良くなった。これは招待状だ。自分の手で試してほしい。内部には調整すべきパラメータが大量にあり、大小さまざまな判断も多い。実装が違えば、出てくる音色も変わる。
今後の方向性について、いくつか考えていることがある。
- 音楽性を落とさずに、このシステムを簡略化できるだろうか。ここでいう「簡略化」とは、言葉で説明しやすい、あるいはコードで実装しやすいということだ。
- グレインの生成にはどんな合成エンジンでも使える。サンプルバッファをスキャン再生すれば、間違いなくいい結果が出る。EvoGrain ではこの手法を意図的に避けた。あまりにズルが効きすぎるからだ。バッファのグレイン化でいい音を出すのはとても簡単だが、サンプルなしでもいい結果を出したかった。グレイン単位の空間化にはまだ拡張の余地がある。ステレオ出力だけでも、Haas 効果によるパンニングやバイノーラル仮想化で大いに工夫できる。
- インタラクションをもっと増やす。ユーザーが L-system を直接編集できるようにするのもいい。あるいは、与えられた L-system に対してルールを少し変えたランダムな変異をいくつか生成し、その中から好きなものを選ばせる、という手もある。
- 逆モデリング。ある論文18では、SVG 画像を入力として、その画像に近い結果を生成する L-system を逆算する手法が述べられている。正直、自分には黒魔術にしか見えない。このシステムではさらに、図形のルールをユーザーが変更して画像を編集することまでできる(!)。この発想は音楽や音声に使えるのか。試してみるしかない……
- 遺伝的アルゴリズムの導入。適応度関数の最適化に最も効くからという理由ではなく、単純に面白くてクールだからだ。やるからには徹底して、ヌクレオチド塩基、コドン、リーディングフレーム、ゲノミクスを備えた完全な人工生命シミュレータを実装してはどうか19。
脚注
2 Wayback Machine の記録によると、melomics.com が最後に正常稼働していたのは 2018 年初頭だ。今ではこのサイトはカジノ広告で埋め尽くされている。なかなか興味深いブランド転換である。
16 もちろん、フラクタルと音楽のアナロジーは音楽のスタイルや文化に大きく依存する。ついでに言っておくと、フラクタルは必ずしも自己相似ではない。フラクタルは細部のあらゆる階層で同じに見える必要はない。
上記 17 件の引用元を含む完全な参考文献リストは原文にある。