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.libの sawtooth を使います。
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.libの ar を使います。
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.libの resonlp を使います。フィルタ用のエンベロープも用意します。
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_noise と noise で揺らしています。
... 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) は expression を numiter 回ループして足し合わせます。現在のループ回数は 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);