octahedron

LemとSKKとCommon Lispでたたかうプログラマのブログ

mimiumさわってみた

mimiumさわってみた

 先週8/21にmimiumのv0.1.0がリリースされ、Ubuntu向けバイナリがでていたので触ってみました。

mimium.org

mimiumって?

 2019年のIPAの未踏プロジェクトに採択された音楽プログラミング言語です。未踏プロジェクトの成果報告会時のバージョンがたしかv0.0.1で、絶賛開発中の音楽プログラミング言語です。成果報告会での発表資料はこちらで、発表の動画はこちらから見れます。また作者の松浦さんによる開発記がこちらで読めます。

 発表によれば

楽家のための
音/音楽を簡潔に記述でき
高い実行性能を持ち
多様なプラットフォームで動作する
プログラミング言語を作りました。

とのことです。先行する音楽プログラミング言語とそれに対する利点等は未踏プロジェクトの成果報告会発表資料等をご覧ください。

 名前はMInimal Musical midIUMの略だそうです。

インストール

 mimiumの公式ドキュメントにあるように現在では導入するのに3通りの方法があります。すなわち

  1. homebrewでインストールする
  2. ビルド済みのバイナリをインストールする
  3. ソースからビルドする

 ぼくの環境はUbuntu 20.04でせっかくバイナリが配布されたところですので、2の方法でやっていきます。

 まずはmimiumのGitHubリポジトリのリリースページからバイナリを適当なところにダウンロードしてunzipしてください。そうすると展開結果はこんなかんじになります:

.
├── COPYRIGHT
├── LICENSE.md
├── README.md
├── bin
│   ├── mimium
│   └── mimium_llloader
├── lib
│   ├── libmimium_backend_rtaudio.so
│   ├── libmimium_builtinfn.so
│   ├── libmimium_compiler.a
│   ├── libmimium_runtime_jit.a
│   ├── libmimium_scheduler.so
│   └── libmimium_utils.a
├── mimium_logo_slant.svg
├── share
│   └── cmake
│       └── mimium
│           ├── mimium-config-noconfig.cmake
│           └── mimium-config.cmake

 このうちbinの中身をパスの通ったところに、libの中身を動的リンクライブラリのパスの通ったところに置きます。たとえばドキュメントにあるように/usr/local/配下にインストールしましょう。まずはsudo cp ./bin/* /usr/local/binとしてバイナリをコピーします。/usr/local/binはパスが通っているのでこれだけでOKです。つぎにsudo cp -r ./lib /usr/local/lib/mimiumとして.soを置きます。GNU/Linuxでは/usr/local/libはデフォルトで動的リンクライブラリのパスに含まれていません。また動的リンクライブラリのパスは再帰的に見てくれないので、以下のようにして/usr/local/lib/mimiumをパスに追加します。

$ echo '/usr/local/lib/mimium' | sudo tee -a /etc/ld.so.conf.d/mimium.conf
/usr/local/lib/mimium
$ sudo ldconfig

 こうするとmimiumを使えるようになります。

$ mimium
Error: Specify file name, repl mode is not implemented yet
return code: 0

触ってみる

 では実際に触ってみます。
 まずはリポジトリのexamplesフォルダに入っているデモを眺めたり実行してみたりして言語の雰囲気を楽しみましょう。ちなみにいくつかの.mmmファイルは実行するとno such file or directoryとエラーになりますがこれはmimiumが.wavファイルをロードできる言語でありプログラム中で指定した.wavファイルがないからです。適当に用意して名前を変えてやると動きます。

 ぱっと見て触ってわかるのは以下のようなことです。

  • サンプリングレートは48kHz
  • ALGOL系の文法
    • Rustみがある
  • 変数の型宣言は不要
  • 関数定義はfn name(args...) -> type {body...}の形式
    • 関数の返り値の型は(いまのところ)付けておいたほうがよい
    • よく見たら無名関数がある(adsr.mmm参照)
  • 関数呼びだしはname(args...)の形式
  • トップレベル(関数ブロックの外)で処理を実行可能
  • dspという名前の関数を定義すると、それが信号処理の毎サンプルを生成する関数になる
  • ifは式
  • nowという「現在のサンプル数」を返す特別な変数がある
    • (代入したらどうなるんだとワクワクして試したらError: syntax error, unexpected =, expecting end of fileと出るので、パーサで特別扱いしてそうな感じ)
  • selfという「selfが書かれている関数の直前の呼び出しで(直前のサンプルで?)返した値」を返す特別な変数がある
    • (いま発表資料を見ていて気が付いた。Faustにも(わかりにくいながら)ある機能らしい)
    • nowと同様、特別な予約語
  • 関数の実行タイミングをスケジューリングすることが可能
    • 関数呼び出しの後ろに@サンプル数とつけると指定したサンプル数のときに関数が実行される
    • kick_on()@10000(10000サンプルのときにkick_on()を呼ぶ)みたいな感じ
    • たぶんその文(式?)の実行時に引数を評価してスケジューラのキューにつっこむイメージ?
    • @の後ろには括弧で囲んで式も置くことができるので、サンプリングレートに応じた値とかを設定できたりする
  • sin()等いくつか組み込みの関数があるっぽい

と、このような感じです。仕組みとしては.mmmのプログラムをLLVM IRにコンパイルして、それを走らせて信号処理している感じであるっぽい。

 さて、音を取り敢えず鳴らしてみます。音を鳴らすためのしくみ等は説明しないので、拙作記事『音プログラムをつくる』あたりを参考にしてください。以下のmimiumコードをmimiumコマンドに食わせると、440Hzのサイン波が鳴ります。

$ cat test.mmm
sample_rate = 48000
pi = 3.141592
freq = 440

fn dsp(time) {
    sec = time / sample_rate
    ph = sec * pi * freq
    println(sec)
    return sin(ph)
}
$ mimium test.mmm
# ポーーーーー

 次に、リズミカルにノイズを鳴らすには以下のようにします。@によるスケジューリングのおかげでリズムマシンもわりと簡単につくれてしまいます。

// noise
noise_note_on = 0
fn noise_on() -> void {
    noise_note_on = 1
}
fn noise_off() -> void {
    noise_note_on = 0
}

fn trig_noise(dur) -> void {
    noise_on()@now
    noise_off()@(now+10)
    trig_noise(dur)@(now+dur)
}

fn dsp(time) -> float {
    n = 0.5 * (if (noise_note_on) random() else 0)
    k = 0.7 * (if (kick_note_on) kick() else 0)
    h = 0.7 * (if (hihat_note_on) hihat() else 0)
    return n + k + h
}

trig_noise(sample_rate / 2)@0
trig_noise(sample_rate / 8)@(sample_rate * 8)

 拙作のAltairKotoをつくったときは「シーケンサわからん」「シーケンサめんどくさい」と唸っていたので、これはお手軽に音楽ができそうです。

 ちなみに現在は「状態を閉じこめたオブジェクト」(たとえばクラスやレキシカルクロージャなど)に類するものはまだないので、上のシーケンサ的な状態を持つプログラムを複数つくるときには、実際に状態変数を必要な数だけ定義する必要があります。そのうち高階関数等が動くようになって楽になるかも的な話をされていました。

 以下のようにすると.wavファイルを再生できます。loadwav関数の戻り値は配列なのでwav[pos]のようにして値を取り出します。いまのところmimium全体がモノラル出力なので、.wavファイルも1chのものをご用意ください。

wavpath = "./ev.wav"
wav = loadwav(wavpath)
wavlen = loadwavsize(wavpath)
pos = 0

fn dsp() {
  v = wav[pos]
  pos = (pos + 1) % wavlen
  return v
}

まとめ

 mimiumのUbuntu版バイナリがでたので、ちょっと触ってみました。
 音楽のインフラを目指す言語ということで、mimiumのランタイムとPCMデータをパッケージして配布する(音楽は実行時の要素で変わる)というような、ただ再生するだけではない音楽の世界がmimiumによって形作られていく、かもしれません。
 今後の発展に期待です。