audio
WebAudioAPI
WebAssembly
DSP
Faust

この記事は Livesense - 関 Advent Calendar 2017 のために書かれてます。
お題は。関といえば関数。関数で音を鳴らしましょう!

Faust って何?

LOGO_FAUST_COMPLET_BLEU.png
Faust Programming Languageです。
About によると、

FAUST (Functional Audio Stream) is a functional programming language specifically designed for real-time signal processing and synthesis. FAUST targets high-performance signal processing applications and audio plug-ins for a variety of platforms and standards.

「FAUST (Functional Audio Stream)はリアルタイムな信号処理と音声合成に特化して設計された関数型プログラミング言語である 」とのこと。
FAUST == Functional AUdio STream だそうです。かっこ良い名前ですね。

何が出来るの?

以下が大きな特徴かと思います。

  • 信号処理を記述することにより出来ることにフォーカスしている
  • 関数の組み合わせによる信号処理の記述
  • 様々な形式へのコンパイル / トランスパイル

信号処理を記述することにより出来ることにフォーカスしている

(音声)信号処理により出来ることしか出来ないので、楽器やエフェクタを作るのには適していますが、曲を作るというようなことは基本的には出来ません。頑張れば出来なくもないと思いますが、信号処理で美しいメロディを作るのは大分大変だと思います。
そのため、以下のようなものを作ることを目的に利用します。

  • VST / AU プラグイン
  • Max / PD オブジェクト
  • SuperCollider モジュール
  • iOS などで利用出来るモジュール
  • WebAudio で利用出来るモジュール
  • スタンドアロンアプリ

関数の組み合わせによる信号処理の記述

言語としての特徴は以下です。

  • オーディオシグナルの 1 サンプルごとに対する処理を記述出来る
  • 関数とブロックダイアグラムにより処理フローを記述する
  • 基本的に全てが入出力を持った関数であり、再帰やパターンマッチなども利用しながら処理を宣言的に記述出来る
    • 高度な型システムのようなものはなく、基本的には数値を処理していくのみ

関数プログラミングと言っても信号処理を行うものであり、基本的には関数により数値演算を繰り返していくのでシンプルな文法であると思います。
そのかわり、信号処理の知識がそれなりに必要な印象です。ただ、標準ライブラリがかなり充実しているのでそれらの組み合わせのみでも面白いものは作れそうです。

様々な形式へのコンパイル / トランスパイル

faust --helpを実行すると、

$ faust --help
FAUST : DSP to C, C++, Rust, LLVM IR, JAVA, JavaScript, asm.js, WebAssembly (wast/wasm), Interpreter compiler, Version 2.5.12
Copyright (C) 2002-2017, GRAME - Centre National de Creation Musicale. All rights reserved. 
....

と表示され、どんなものにコンパイルされるかが分かります。Rust まであるのか…

  • 元々は C++ のコードを生成するのみ
  • LLVM を利用するようになったことにより様々な形式へのコンパイルが可能に
    • C++ に直接 faust のコードを埋め込み、JIT コンパイルで動作させることを可能にする libfaust の存在も
  • 様々なプラットフォームへのコンパイルを行うためにfaust2xxxというコマンド(シェルスクリプト)群が同梱
  • その為に各プラットフォームのエコシステムも必要なものもある
  • ダイアグラム の SVG 出力を行え、分かりやすく視覚的な確認が出来て便利

ドキュメント

詳しくは公式のドキュメントがかなりきちんと纏められて参考になります。また、コンパイラ関連は Github の README が参考になります。

日本語だと @tomoyanonymous さんによる以下記事あたりが丁寧に解説されていてとても参考になります。大体何が出来るか分かります。

はじめよう

FaustLive

前述の通り Faust は利用する環境に合わせてコンパイルして動作させるものなのですが、実際に作りながらトライ & エラーが出来るよう FaustLive というものが用意されています。まずはこれで始めるのが良さそうです。
http://faust.grame.fr/download/#faustlive

FaustLive は以下のような機能を持っています。

  • コンパイラが内包された簡易 GUI ツール
  • エディタは付属していない (のでソースはお好みのエディタで編集)
  • 対象のソースを読み込ませると GUI 付きでスタンドアロンで動作
  • ソースを編集すると自動でコンパイル & 再表示
  • SVG の出力も簡単に行える
  • 各フォーマットへのコンパイルも一応行える

これで試行錯誤して楽器などを作成し、完成したら利用する形式にコンパイルするといった流れがよさそうです。

コンパイラ

こちらから clone してビルドして利用します。各自がんばりましょう。
https://github.com/grame-cncm/faust

どういう風に書くのか?

A Sine Oscillator の例から抜粋。

import("stdfaust.lib");

phasor(f)   = f/ma.SR : (+,1.0:fmod) ~ _ ;
osc(f)      = phasor(f) * 6.28318530718 : sin;
process     = osc(hslider("freq", 440, 20, 20000, 1)) * hslider("level", 0, 0, 1, 0.01);

これでサイン波が鳴ります。読みづらいですね!
細かな文法は先の記事でも解説されていますが、演算子や関数により入出力を繋いでいく他に、以下のような記号を使って信号の流れを記述出来るようにもなっています。

_ // パス
! // 切断
: // 直列
, // 並列
~ // 再帰
<: // 分割
:> // 結合

一見分かりづらいのですがこれらによりとてもシンプルに信号処理を記述出来るようになっています。
また、上記コードの hslider のような GUI でパラメータを設定するようなパーツも揃っており、コンパイルする形式に合わせて GUI も生成されるようです。

このコードのダイアグラムを SVG 出力させたもの(を PNG に変換したもの)はこちら。
process.png

分かりやすいですね!

作ってみた

シンプルなシンセっぽいものをいくつか作ってみます。
さっきの例ではオシレータの波形生成を実際に演算する実装でしたが、オシレータやフィルターなど標準的なものは標準ライブラリとしてまとめられているので実際にはこれらを利用していきます。
基本的なシンセや物理モデリングなどかなり膨大なモジュールが標準ライブラリには収められています。

リングモジュレータ

シンセといえばリングモジュレータ。矩形波とサイン波でリングモジュレーションしてみます。

import("stdfaust.lib");

square = hslider("Square Freq", 500, 1, 1000, 0.01) : os.square;
sine = hslider("Sine Freq", 500, 1, 1000, 0.01) : os.oscsin;
process = (square, sine) : *;

オシレータバンク

par 関数で並列なダイアグラムを簡単に作れるので作ってみたやつです。

import ("stdfaust.lib");

vol = hslider("Global Volume", 1, 0, 1, 0.01);
freq = hslider("Global Frequency", 500, 0, 5000, 0.01);
n = 8;

osc(i) = os.oscsin(f) * (1.0 / n)
     with {
          index = hslider("Index %i", i * 0.1, 0, 30, 0.01);
          f = freq * index;
     };
oscbank = par(i, n, osc(i)) :> _;
process = oscbank * vol <: _;

2 OP FM シンセ

みんな大好き FM シンセ。一番シンプルな 2 op の FM シンセを試してみました。
標準ライブラリの中に DX7 をモデリングしたものも既に入っているので本格的な FM シンセを使いたい方はそちらもどうぞ。

import("stdfaust.lib");

freq = hslider("Frequency", 500, 1, 10000, 0.01);
ratio = hslider("Harmonicity Ratio", 1.20, 0, 5, 0.01);
index = hslider("Frequency Index", 20, 0, 32, 0.01);
modulator = os.oscsin(mfreq) * mamp
    with {
        mfreq = freq * ratio;
        mamp = mfreq * index;
    };
carrier = os.oscsin(cfreq) with { cfreq = freq + modulator; };
process = carrier;

ビートボックス

キック / スネア / オープンハイハット / クローズハイハットの 4 パートを適当に作ってみたやつです。
各パートにボタンを付けて押すと発音します。エンベロープの練習です。

import ("stdfaust.lib");

kick = os.osc(freq) * env
    with {
       gate = button("kick");
       fenv = en.ar(0.0001, 0.2, gate);
       freq = 20 + fenv * 100;
       env = en.ar(0.0001, 0.3, gate);
    };

snare = no.noise : fi.resonbp(freq, q, 1) * env
    with {
       gate = button("snare");
       freq = 800;
       q = 5;
       env = en.ar(0.0001, 0.2, gate);
    };

ch = no.noise : fi.resonhp(freq, q, 1) * env
    with {
       gate = button("ch");
       freq = 12000;
       q = 8;
       env = en.ar(0.0001, 0.05, gate);
    };

oh = no.noise : fi.resonhp(freq, q, 1) * env
    with {
       gate = button("oh");
       freq = 10000;
       q = 10;
       env = en.ar(0.0001, 0.2, gate);
    };

process = (kick,snare,ch,oh) :> sp.panner(0.5);

WebAssembly

上記のようなコードを様々な形式で扱えるものにコンパイル出来るのですが、WebAssembly にも割と最近対応されたようです。
http://faust.grame.fr/news/2017/01/13/faust-webassembly.html

先ほど作ったものを WebAssembly に変換してみます。

コンパイル方法

faust から WebAssembly にコンパイルする方法は 2 種類用意されていています。

  • $ faust -lang wasm xxx.dsp: faust 単体で WebAssembly のバイナリにコンパイル
  • $ faust -lang wast xxx.dsp: wast を生成する

また、これらをラップし、GUI で動作するところまでのものを生成してくれるコマンドは以下。

  • faust2webaudiowasm
  • faust2webaudiowast

両方とも wasm ファイルとともに wasm ファイルをロードし実行する JavaScript も含めた HTML(& CSS) を生成します。
(faust2webaudiowastはコンパイルの過程で binaryen が必要です)

コンパイルしてみる

今回は直接 wasm を吐き出す方でやってみます。
上記で作った FM シンセで試してみましょう。

$ faust2webaudiowasm fm.dsp
$ ls
fm.dsp fm.html fm.wasm

生成されました。簡単ですね!
以下が成果物です。(!!! 音量注意 !!!)

https://reprimande.github.io/faust-demo/fm/fm.html

GUI のデザインに思うところがないわけでもないですが WebAssembly を利用して音が鳴っています。
JavaScript の中身を確認してみると、ScriptProcessor の中で WebAssembly の処理を実行しているようなコードが生成されていました。
WebAssembly は信号処理の演算として使われているわけですね。なるほど。

まとめ

  • 簡潔な記述で信号処理を記述出来る
    • 関数プログラミングといっても高度な概念はそんなにないので難しくない
    • 複雑なことをするには信号処理の知識の方が必要
  • 様々なプラトフォームへのコンパイルを行える
    • 信号処理の記述のみに専念出来る
    • ひとつ作れば色々な環境で動かせる

ちょっとした自分専用のプラグインなどを作るのにとても便利なツールだと思いました。
みなさんも音関連のプラグインなど作りたい時に利用するとよいかと思います。
Rust をちょっと深掘りしたかった…