FAUST:自分好みのフェイザーを作ってみた

[フェイザーペダルとオールパスフィルタについての覚書](https://qiita.com/Aogiri-m2d/items/846746c7bf9bdd51ab57)を読んで勉強になったし、実際自分でも試してみて、「お、フェイザーなかなか良いじゃん!」と思い、自分好みのを作ってみました。

# 変更点
## 自作の[biquad filter用のライブラリ](https://qiita.com/ttatsf/items/86d29b58b717b0d57a78)を使う
せっかく作ったし、二次の方が何かと都合がいいらしいので。
## MIDI noteに変換してモデュレーションする
周波数リニアでなく、音階にそった方が自然な感じがするので。
## LRに位相差をつけられるようにする
広がりがでるし、何かもやもやしていい感じ。
## 使いやすいインターフェイス
あくまで自分好みに。他の人が使いやすいかは知らん。

# こうなりました

```C:myPhaser.dsp

declare name "myPhaser";
import("stdfaust.lib");
import("RBJ.lib");

process = phaserMono(0.0), phaserMono(phaseDiff) ;
phaserMono(phaseDiff) = _
  <: _ , apfSeq
  : *(1 - wet), *(wet)
  :> _
    with{
      apfSeq = _, stage
        : seq( i, 12, bypassIfZero( APF( F, Q ) ) )
        : _, ! ;
      F = ba.midikey2hz( note ) ;
      note = centerNote + ampMod * os.oscp(modF, phaseDiff * ma.PI) ;
      centerNote = (hz2midi(ceilingF) + hz2midi(floorF)) / 2.0 ;
      ampMod = ( hz2midi(ceilingF) - hz2midi(floorF)) / 2.0 ;
    };

bypassIfZero( func, x, stage ) =
  ( x : ba.bypass1( stage == 0, func ) ), max( 0, stage - 1 ) ;

hz2midi(f) = 12*ma.log2(f/440.0) + 69.0;

phaserGrp( x ) = hgroup("[0]Phaser", x ) ;
stage     = phaserGrp( hslider("[0]Stage[style:knob]", 6, 1, 12, 1 ) ) ;
Q         = phaserGrp( hslider("[1]Q[style:knob]",0.7,0.1,4.5,0.1) ) ;
modF      = phaserGrp( hslider("[2]modFreq[style:knob][unit:Hz]", 1.0, 0.01, 5.0, 0.01)) ;
floorF    = phaserGrp( hslider("[3]Floor[style:knob][unit:Hz]", 440, 100, 5000, 0)) ;
ceilingF  = phaserGrp( hslider("[4]Ceiling[style:knob][unit:Hz]", 880, 440, 5000, 0)) ;
phaseDiff = phaserGrp( hslider("[5]phaseDiff[style:knob]", 0.5, -1.0, 1.0, 0.1)) ;
wet       = phaserGrp( hslider("[6]Wet[style:knob]", 0.5, 0.0, 0.5, 0.001) ) ;
```

これを 自作の[biquad filter用のライブラリ"RBJ.lib"](https://qiita.com/ttatsf/items/86d29b58b717b0d57a78)と同階層においてFaustLiveでvstに書き出してみました。


![スクリーンショット 2018-02-14 17.00.13.png](https://qiita-image-store.s3.amazonaws.com/0/61597/2d6298ce-349f-6b9f-1aa5-d667c0d94647.png "スクリーンショット 2018-02-14 17.00.13.png")
いい感じで使えてます。
# 追記：なぜかba.hz2midikey()が使えない...
FaustLiveからだとなぜか駄目なのです。何でだろ?
しょうがないので、自前で  ```hz2midi()```  というのを定義してます。元からコピペしてるので、同じものです。
うーん、謎...
ひとつのファイルにまとめれば[オンラインエディタ](http://faust.grame.fr/editor/)も使えるので、RBJ.libからAPFに関係するところをコピペして[エディタ](http://faust.grame.fr/editor/)にかけてみたら、そっちはうまくいきました。さらに謎...

```C:myPhaser2.dsp
declare name "myPhaser2";
import("stdfaust.lib");
//import("RBJ.lib");

process = phaserMono(0.0), phaserMono(phaseDiff) ;
phaserMono(phaseDiff) = _
  <: _ , apfSeq
  : *(1 - wet), *(wet)
  :> _
  with{
    apfSeq = _, stage
      : seq( i, 12, bypassIfZero( APF( F, Q ) ) )
      : _, ! ;
    F = ba.midikey2hz( note ) ;
    note = centerNote + ampMod * os.oscp(modF, phaseDiff * ma.PI) ;
    centerNote = (ba.hz2midikey(ceilingF) + ba.hz2midikey(floorF)) / 2.0 ;
    ampMod = ( ba.hz2midikey(ceilingF) - ba.hz2midikey(floorF)) / 2.0 ;
  };

bypassIfZero( func, x, stage ) =
  ( x : ba.bypass1( stage == 0, func ) ), max( 0, stage - 1 ) ;

//hz2midi(f) = 12*ma.log2(f/440.0) + 69.0;

phaserGrp( x ) = hgroup("[0]Phaser", x ) ;
stage     = phaserGrp( hslider( "[0]Stage[style:knob]", 6, 1, 12, 1 ) ) ;
Q         = phaserGrp( hslider("[1]Q[style:knob]",0.7,0.1,4.5,0.1) ) ;
modF      = phaserGrp( hslider("[2]modFreq[style:knob][unit:Hz]", 1.0, 0.01, 5.0, 0.01)) ;
floorF    = phaserGrp( hslider("[3]Floor[style:knob][unit:Hz]", 440, 100, 5000, 0)) ;
ceilingF  = phaserGrp( hslider("[4]Ceiling[style:knob][unit:Hz]", 880, 440, 5000, 0)) ;
phaseDiff = phaserGrp( hslider("[5]phaseDiff[style:knob]",0.5, -1.0, 1.0, 0.1)) ;
wet       = phaserGrp( hslider("[6]Wet[style:knob]",0.5, 0.0, 0.5, 0.001) ) ;

// 以下、RBJ.libからコピペ

APF( F, Q ) = biquad( b0/a0, b1/a0, b2/a0, a1/a0, a2/a0 )
  with{
    b0 =   1 - alpha ;
    b1 =  -2 * cos( w0 ) ;
    b2 =   1 + alpha ;
    a0 =   1 + alpha ;
    a1 =  -2 * cos( w0 ) ;
    a2 =   1 - alpha ;

    w0 = f_w0( F ) ;
    alpha = f_alpha( w0, Q ) ;
  } ;

biquad  = fi.tf2;

f_A( G ) = 10 ^ ( G / 40 ) ;
f_w0( F ) = 2 * ma.PI * F / ma.SR ;
f_alpha( w0, Q ) = sin( w0 ) / ( 2 * max( 0.001, Q )) ;
```

# 追記2：ついでにNFBも載せてみました
これもまた癖があって良い感じ。
もしかして再帰はじめてかも(自前では)。書いてみたら意味やら使い勝手やらが分かってきた。

```C:myPhaserFB.dsp
declare name "myPhaserFB";
import("stdfaust.lib");
import("RBJ.lib");

process = phaserMono(0.0), phaserMono(phaseDiff) ;
phaserMono(phaseDiff) = _
  <: _ , ( + : apfSeq) ~ *( 0 - nfb )
  : *(1 - wet), *(wet)
  :> _
    with{
      apfSeq = _, stage
        : seq( i, 12, bypassIfZero( APF( F, Q ) ) )
        : _, ! ;
      F = ba.midikey2hz( note ) ;
      note = centerNote + ampMod * os.oscp(modF, phaseDiff * ma.PI) ;
      centerNote = (hz2midi(ceilingF) + hz2midi(floorF)) / 2.0 ;
      ampMod = ( hz2midi(ceilingF) - hz2midi(floorF)) / 2.0 ;
    };

bypassIfZero( func, x, stage ) =
  ( x : ba.bypass1( stage == 0, func ) ), max( 0, stage - 1 ) ;

hz2midi(f) = 12*ma.log2(f/440.0) + 69.0;

phaserGrp( x ) = hgroup("[0]Phaser", x ) ;
stage     = phaserGrp( hslider("[0]Stage[style:knob]", 6, 1, 12, 1 ) ) ;
Q         = phaserGrp( hslider("[1]Q[style:knob]",0.7,0.1,4.5,0.1) ) ;
modF      = phaserGrp( hslider("[2]modFreq[style:knob][unit:Hz]", 1.0, 0.01, 5.0, 0.01)) ;
floorF    = phaserGrp( hslider("[3]Floor[style:knob][unit:Hz]", 440, 100, 5000, 0)) ;
ceilingF  = phaserGrp( hslider("[4]Ceiling[style:knob][unit:Hz]", 880, 440, 5000, 0)) ;
phaseDiff = phaserGrp( hslider("[5]phaseDiff[style:knob]", 0.5, -1.0, 1.0, 0.1)) ;
nfb       = phaserGrp( hslider("[6]NFB[style:knob]", 0.0, 0.0, 1.0, 0.01));
wet       = phaserGrp( hslider("[7]Wet[style:knob]", 0.5, 0.0, 0.5, 0.001) ) ;
```
![スクリーンショット 2018-02-14 18.50.02.png](https://qiita-image-store.s3.amazonaws.com/0/61597/aedb3866-6e24-0b19-b0a1-4d369c3f908a.png "スクリーンショット 2018-02-14 18.50.02.png")