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);