mimiumができるまで mimiumができるまで

publish : 2020-03-08

last update : 2020-03-08

2019年度は、6月に未踏IT人材育成・発掘事業というものに採択されひたすら音楽プログラミング言語を作っていました。未踏そのものについての記事はこちら

https://github.com/mimium-org/mimium

mimiumという名前の音楽プログラミング言語です。要するにコンパイルすると音が鳴るんですが、それなりに低レベルの信号処理も楽譜レベルの処理も両方一つの言語で書けるようなものを目指して現在も開発中です。

具体的な言語仕様とかは未踏の成果報告会のプレゼンを改めてまとめたものを別に作っていますのでまた後日。今回はおおよそ1年かけて音楽プログラミング言語ができるまで何があったかの備忘録です。


年始、路頭に迷う

2019年の初頭は、修士課程がどうにか終わり、フリーサンスでどうにか食って行けそうならそれがいいなあとか思っていたら全然仕事が集まる気配もなく、とりあえず内部進学なら入学金がかからない、福岡の方が生活コストが安いということで博士になし崩し的に雪崩れ込んでしまい、さてどうやって生きて行こうかなあ、とぼんやり思っていたのでした。

修士ではひたすら作品を作ったり演奏をしまくり、合計3つの作品を修論にまとめようとして結果空中分解してしまったことやら、そもそもその作品を一つ作っては論文を書き、みたいなリズムに疲れてきたこととかもあったので、どうせ最低3年かかる博士に入ってしまったからには、もう少し長いスパン、3年かけないと実現できないような何かをやるのが良いのではないだろうか、と考えたのでした。

そこで出てきたのが音楽プログラミング言語をつくるということでした。もともとは2018年の秋(修士2年の後半)にNYのSFPCという学校に留学していた時にやろうかなあと考え始めていたものの実現できていないアイデアで、いっそやるなら本格的にやるかとなりました。

加えて、そのソースコードをGUIを用いていい感じに編集できるようにしたらいい感じにプログラマブルなDAWが作れて最高やんけ、というアイデアのもと博士研究計画書を即席で  書き上げ、博士の入試面接では偉めの先生から雑すぎる研究ロードマップに対して「これは研究"計画"ではないよねえ・・・」などとツッコまれつつ、ひとまず博士に滑り込んだのでした。


本題から若干それた。未踏は指導教員が学生時代に採択されていたこともあり、とりあえず出すだけ出してみるかー、という気分になっており、2月くらいから提出物を作り始めていた記憶。

ちなみにこの時点での言語処理系に対する知識はほぼゼロと言ってもよく、既存の音楽系言語のソースコードをチョロっと読んだことがある程度でした。今考えるとやばすぎる。なので提案書自体は言語の細かい仕様案というよりはGUIを用いたエディターを含め、大きな全体像のプロトタイプを9ヶ月で作るという提案でした。

言語を作るぞー、というのを具体的に始めたのはMatzさんの「言語のしくみ」という本を買った辺りだと記憶しているのですが、Amazonの注文履歴を見たら2次審査より後でした。思ったより遅い。あとこの時点で「きつねさんでもわかるLLVM」という、同人発ながら日本語唯一のLLVM解説書を買っていたのは最終的にLLVMと格闘することになったので非常に正しい選択でした。

同時並行してparsimmon.jsというjsのパーサコンビネータライブラリを使ってFaustのパーサー部分だけを自作してみるという遊びをしていたらしいです。

jsで一通り満足できたあたりでC++でのプロジェクトづくりを始めます。ここで大人しくパーサーのライブラリにflex/bisonを選んでおけばよかったもののできるだけモダンな感じにしたいという初心者にあるまじき背伸びをしたのが悪く、CppCmbというパーサコンビネータライブラリを選んで作り始めてしまいました。

https://github.com/LPeter1997/CppCmb

CppCmbはライブラリとしては非常にエレガントな作りをしていたのですが如何せん全くの開発途上で、まず正規表現でのマッチが無いという状況でした。実用的なツールにチャレンジングなライブラリを選ぶな。

そこらへんで一通り苦しんだあと、大人しくbison(yacc)を導入してとりあえずインタプリタで動く感じのものを作ってみようということで5月後半ぐらいまでゴリゴリ開発を進めていきました。

この辺はかなり健康状態が良く、朝超早起きをして、3日に一回ランニングをして昼まで開発、ご飯食べて18時くらいまで開発して帰るというまるで真人間みたいな生活をしておりました。 

おかげで進捗状態もかなり良く、採択の通知をいただいた頃には簡単なパーサーができていたりして、7月後半のブースト会議の時にはコア機能である関数の時間指定実行の簡易実装が済んでいました。とりあえずくっつけたMIDIドライバもあったのでなんとなく音も鳴らせるという状態です。 

泥沼(インタプリタの限界)

問題はブースト会議直前にたどり着いた音響合成の仮実装のパフォーマンスがあまりに悪かったことでした。どのくらい悪いかというとサイン波を合成するのに70%くらいCPUを食われる有様です。

この時の実装はパースしたてのASTを直接トラバースしている動的型付けのインタプリタという状態で、変数もstd::unordered_mapに直接格納しているだけなので、1秒間に48000回ハッシュマップにアクセスすりゃそれはまあ仕方ない、という状況なのでした。

この時点でのGUIエディタとかの進捗に関してはゼロだったので、選択肢は以下のようになりました。

  1. 音響合成は諦めて後の実装でどうにかし、エディタのプロトタイピングを頑張る
  2. 多少エディタの進捗を犠牲にしてでも、気合で処理系を最適化しリアルタイム音響合成を実現する

ちなみにこの時は2の最適化に関してはLLVMを導入する以外に頭になかったのですが、作った後になってもっと段階的な最適化の方法があったんだなーということがわかってきて、それを知っていたらもう少し選択肢の種類は増えていたかもしれません。


ブースト会議が終わったあたりから採択前に決まっていた仕事やらイベントが立て続けに降ってきました。

結構気が狂ってる詰め込み方でした。未踏とりつつこれだけいろいろやってしまったのは正直かなり反省しているのですが、全部先に決まっちゃってたので諦めて言語処理系の理論をこの間にいろいろ詰め込んでいました。

具体的には時間を取り扱う言語理論の話で、時相論理とか、pi-calculusとかtimed automataとかとか。ただこの辺りのお勉強が物凄い役に立ったかと言われるとちょっと微妙で、最終的に作りたいものがある程度一般的に使われているコンピューターの場合ハードウェアレベルでの割り込み処理とか、OSのタスクスケジューリングとか、オーディオドライバーとの連携とかの制約の事を考えないとどうしようもないことがいっぱいあるからです。OSレイヤーのことをもっと真面目にやる方が有益だったと今となっては思うところ。


この辺りのイベントが一通り終わったあたりで、GUIの話は一旦やめて、未踏期間はとにかく言語に集中しましょうという形で腹を括ることにしました。

問題はパフォーマンス向上のためにLLVMを使うかどうか、いや使うしかほぼ道は無いんだけどAPIが沼ということだけ聞いていたので使いたくねえなあと足踏みをしていたところでこんなニュースが飛び込んできます。

中学生が開発した言語「Blawn」が最多受賞!「U22-プログラミングコンテスト2019」最終審査会レポート

中学生がおおよそ1ヶ月でLLVMバックエンドのプログラミング言語を作ったとのことでぶったまげました。これでとりあえず俺でもできるという自信半分、お前これ中学生でも出来んのになんでやらないのというプレッシャー半分でLLVM投入に踏み切るいいきっかけになりました。

ここからはもうひたすら開発開発です。

LLVMバックエンドの言語というのソースコードを抽象構文木にして、最終的にLLVM IRという中間表現に変換することで成立するのですが、LLVM IRにいきなり落とすにはギャップがあるので間にさらに中間表現を挟みました。

この辺りの実装はgoでmincamlのLLVM実装をしたgocamlのプロジェクトが非常に非常に参考になりました。

Go でつくる汎用言語処理系 実装戦略

あと元々のmincamlの実装である住井英二郎さんの速攻MinCamlコンパイラ概説も。ちなみにこれは15年前くらいの未踏のプロジェクト「美しい日本のMLコンパイラ」の成果物です。未踏で未踏に助けられました。

あと未踏同期の艮 鮟鱇氏のセルフホストOCamlコンパイラaqamlの実装とかもだいぶ参考になりました。

はりぼて自作OCamlコンパイラAQamlでセルフホストしてみた

この辺の影響もあってmimiumの言語仕様は、見てくれは手続き型っぽいですがそれなりに関数型っぽいこともできるようになっています(肝心の高階関数とかまだコンパイラが結構バグってて使い物にならないのですが)。

肝心のLLVM APIに関しては日本語資料で役立つものはいくつか単発でqiitaの記事があるくらいでやはりdoxygenされてる公式ドキュメントを頑張って読むのがほとんどでした。

概念レベルではこのサイトとか

Mapping High Level Constructs to LLVM IR

あとこの本とか(最終的に3回ぐらいしか読み返さなかった気もするが)

特に厳しいのはJIT関係。LLVMのJITは何も無いJIT、MC JIT、Orc JITというのが古い順に並べてあります。基本新しいやつの方が性能はいいし古いのはだんだんDeprecateされていくはずなので新しいのを使いたいのですが、「きつねさんでもわかるLLVM」はMC JIT以前の本なのでもうあんまり現実的ではなく(JIT以外のAPIに関してはまだそれなりに参考にできる)、Orc JITに関しては新しすぎて英語でもドキュメントがロクに見つからない始末です。

ただ、タイミングがいいことに2019年9月ぐらいにリリースされたLLVM9.0にLLJITとLLlazyJITというOrcJITを簡単に使えるようラップしたクラスが登場しました。mimiumではLLJITを使った KaleidoScope(LLVMのチュートリアル言語)用JITクラスがドキュメントは整っていないもののexampleには収録されていたので、それをあれこれしながらどうにか使うことができました。綱渡りです。

ちなみにLLVM10ではORCv1がDeprecateされてORCv2というのに移行するらしいですが、、、、


12月、未踏のお金を注ぎ込んでMBP16inchを買い、7年間使い続けてメインHDDをぶっ壊し、9ヶ月くらい外付けSSDにOSを入れて使っていたMBP2012に別れを告げます。コア数が倍以上になった結果1時間かかっていたLLVMのビルドが15分になりました。力isパワーというやつです。

そして年末、とうとう音が鳴りました。

1月最初の週にあった成果報告会前(プレゼン練習を除いて)最後のMTGでは本当にこのサイン波と、FFIがちょうど実装し終わったのでホワイトノイズが鳴らせるようになったくらいしかできなかったのですが、ここから最後の追い上げです。

self(関数内でその関数の最後の返り値の取得)の実装

@をつけて関数を時間指定して実行

LibSndFileからオーディオファイル読み込み(含配列アクセスの簡易実装)

あとロゴを作った


こう見るとmimiumの言語特有の機能の実装ほとんど1月にやってますね。インタプリタで仮実装しておいた貯金が役立った部分は結構ありますが。思った以上にタスクスケジューラーとかはそのまんま使いまわせた記憶があります。

そして、これと並行してプレゼン作りとプレゼン用デモコードの実装。

プレゼン用の動画を記録したのは最終的に2,3日前ぐらいにアキバのホテルでやる感じになりました。プレゼン前日にひとまずリリースバイナリをzipファイルに詰め込みgithubにv0.0.0としてアップロード。無事ファーストリリースです。

これからひとまずWebサイト整えたり、ドキュメント作ったりLinux/Win対応したり学会に論文投稿したりまあ色々あるのですが、ゆっくりやっていきます。