2017/07/25

初めてのFaust

Faust環境を整えたので、簡単なシンセサイザを作りながらFaustを使うときのワークフローを組み立てていきます。

プロジェクトの作成

プロジェクトのディレクトリを作ります。

mkdir saw
cd saw

エディタにはAtomを使います。language-faustをインストールして便利な補完を使います。

atom saw.dsp

saw.dspを編集して音を出します。以下はFaust Libraries Documentationにあった例です。

import("stdfaust.lib");
process = os.osc(440);

編集した.dspをFaustLiveに渡して動作確認します。

FaustLive saw.dsp

GUI

GUIについてはFaust Quick Referenceの3.5.6 User Interface Elementsに載っています。

saw.dspに音量を調節するスライダを追加します。

import("stdfaust.lib");

osc = os.osc(440);
process = osc * hslider("amp", 0.1, 0.0, 1.0, 0.01);

hslider(str, cur, min, max, step) となっています。 cur は初期値です。

オシレータ

saw.dspという名前にしたのでのこぎり波を出すように修正します。oscillators.libsawtooth を使います。

import("stdfaust.lib");

amp = hslider("amp", 0.1, 0.0, 1.0, 0.01);
pan = hslider("pan", 0.5, 0.0, 1.0, 0.01);
freq = hslider("freq", 0.5, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;

osc = os.sawtooth(freq);
process = osc * amp : sp.panner(pan);

lin2LogGain でスライダの値を対数スケールに変換しています。 : は式の左から右に信号を送る演算子です。

panner で信号をステレオにしています。

エンベロープ

envelopes.libar を使います。

import("stdfaust.lib");

amp = hslider("amp", 0.1, 0.0, 1.0, 0.01);
pan = hslider("pan", 0.5, 0.0, 1.0, 0.01);
freq = hslider("freq", 0.5, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;

attack = hslider("attack", 0.001, 0.0, 1.0, 0.001);
release = hslider("release", 0.2, 0.0, 1.0, 0.001);
gate = button("gate");

env = en.ar(attack, release, gate);
osc = os.sawtooth(freq);
process = env * osc * amp : sp.panner(pan);

フィルタ

filters.libresonlp を使います。フィルタ用のエンベロープも用意します。

import("stdfaust.lib");

amp = hslider("amp", 0.1, 0.0, 1.0, 0.01);
pan = hslider("pan", 0.5, 0.0, 1.0, 0.01);
freq = hslider("freq", 0.5, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;

ampAttack = hslider("amp attack", 0.001, 0.0, 1.0, 0.001);
ampRelease = hslider("amp release", 0.2, 0.0, 1.0, 0.001);
gate = button("gate");

cutoff = hslider("cutoff", 0.5, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;
resonance = hslider("resonance", 0.5, 0.01, 1.0, 0.01) : ba.lin2LogGain * 100;
filterAttack = hslider("filter attack", 0.001, 0.0, 1.0, 0.001);
filterRelease = hslider("filter release", 0.2, 0.0, 1.0, 0.001);
filterEnvAmount = hslider("filter envelope amount", 0.5, 0.0, 1.0, 0.01)
  : ba.lin2LogGain * 980 + 20;

filterEnv = en.ar(filterAttack, filterRelease, gate);
filter = fi.resonlp(
  cutoff + filterEnvAmount * filterEnv,
  resonance,
  1);

ampEnv = en.ar(ampAttack, ampRelease, gate);
osc = os.sawtooth(freq);
process = ampEnv * osc * amp : filter : sp.panner(pan);

仕上げ

オシレータを派手にします。2つに増やしてデチューンしたものを倍音の間隔でさらに重ねます。音の高さを pink_noisenoise で揺らしています。

...

osc(f) = os.sawtooth(f)
  + os.sawtooth(f * (1.0 + 0.1 * no.pink_noise));
chord(numHarmo) = sum(i, numHarmo, osc((i + no.noise) * freq / numHarmo))
  / numHarmo;
process = ampEnv * chord(7) * amp : filter : sp.panner(pan);

osc(f) = os.sinosc(f); のような書き方は関数の宣言です。

sum(ident, numiter, expression)expressionnumiter 回ループして足し合わせます。現在のループ回数は ident に格納され expression から参照できます。 sum 以外のループは Faust Quick Reference -> 3.4.1 Diagram Expressions -> Iterations (version 0.9.100では23ページ目) に載っています。

最後にデフォルトのパラメータを調整して完成です。

import("stdfaust.lib");

amp = hslider("amp", 0.5, 0.0, 1.0, 0.01);
pan = hslider("pan", 0.5, 0.0, 1.0, 0.01);
freq = hslider("freq", 0.4, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;

ampAttack = hslider("amp attack", 0.7, 0.0, 1.0, 0.001);
ampRelease = hslider("amp release", 0.8, 0.0, 1.0, 0.001);
gate = button("gate");

cutoff = hslider("cutoff", 0.5, 0.0, 1.0, 0.01) : ba.lin2LogGain * 980 + 20;
resonance = hslider("resonance", 0.2, 0.01, 1.0, 0.01) : ba.lin2LogGain * 100;
filterAttack = hslider("filter attack", 0.25, 0.0, 1.0, 0.001);
filterRelease = hslider("filter release", 0.9, 0.0, 1.0, 0.001);
filterEnvAmount = hslider("filter envelope amount", 0.7, 0.0, 1.0, 0.01)
  : ba.lin2LogGain * 980 + 20;

filterEnv = en.ar(filterAttack, filterRelease, gate);
filter = fi.resonlp(
  cutoff + filterEnvAmount * filterEnv,
  resonance,
  1);

ampEnv = en.ar(ampAttack, ampRelease, gate);
osc(f) = os.sawtooth(f)
  + os.sawtooth(f * (1.0 + 0.1 * no.pink_noise));
chord(numHarmo) = sum(i, numHarmo, osc((i + no.noise) * freq / numHarmo))
  / numHarmo;
process = ampEnv * chord(7) * amp : filter : sp.panner(pan);