【TidalCycles】オリジナルエフェクトの追加方法

# はじめに

TidalCyclesは、Haskellライクなコードによってリズムパターンを生成できるソフトウェアです。
個人的にTidalCyclesで作曲していて、記事も書いているのでこちらもぜひみてください！

https://qiita.com/s2hap/items/620082b82a4bbe475823

# TidalCyclesのエフェクト

デフォルトエフェクトの使い方などについては公式のドキュメントに詳しく書いてあるので以下をみてください。

https://tidalcycles.org/docs/reference/audio_effects

# エフェクトの追加

デフォルトエフェクトだけでなく、自分で新たに定義したエフェクトを追加した場合があります。

今回は新たに`reverb`エフェクトを追加できるようにしようと思います。


:::note info
この方法だと、音が再生されている間のみしかエフェクトがかかりません。
そのため、リバーブやディレイなどはこの方法ではなく、globalEffectsとして定義したほうが良いです。以下の記事も参考にしてみてください。
:::

https://qiita.com/s2hap/items/c85f6acce7dd6953693f


## 結論

結論からいうと、このようになります。

### SuperCollider側

```supercollider
(
    ~dirt.addModule('myreverb', { |dirtEvent|
        dirtEvent.sendSynth('myreverb' ++ ~dirt.numChannels,
            [
                reverb: ~reverb,
                out: ~out
            ]
        )
    }, { ~reverb.notNil });

    SynthDef("myreverb" ++ ~dirt.numChannels, { |out, reverb|
        var signal, chain, in, z, y;
        signal = In.ar(out, ~dirt.numChannels);
        // SchroederReverb
        z = DelayN.ar(signal, 0.048);
        y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, 1, 15) }));
        32.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
        signal = signal + (0.2*reverb*y);
        ReplaceOut.ar(out, signal)
    }, [\ir]).add;
)
```

### TidalCycles側

```haskell
do
  let
    reverb = pF "reverb"
  d1
    $ s "superpiano"
    # n "<a2,a4,b4,af5>"
    # sustain 3
    # reverb 0.4
```

## 解説

TidalCyclesは、明示的に設定されていないエフェクト名は受け付けない仕様となっているので、まずTidalCycles側で`# reverb`を使える状態にします。今回は`# reverb 0`でリバーブなし、`#reverb 1`でリバーブ最大、のようにしたいので、基本的に引数としてとる値はFloatとなります。

よって以下のように定義してあげます

```haskell
let
  reverb = pF "reverb"
```

(もし引数にとるものがIntegerである場合は、この`pF`部分を`pI`に変更します。)

この時点で

```haskell
d1
  $ s "superpiano"
  # n "<a2,a4,b4,af5>"
  # sustain 3
  # reverb 0.4
```

のようにして実行しても特にエラーがでなくなっていると思います。(もちろんエフェクトの中身はまだ定義していないのでエフェクトはかかりません。)

次にエフェクトの定義ですが、こちらはSuperColliderで定義します。

エフェクト定義スクリプトは[default-effects-extra.scd](https://github.com/musikinformatik/SuperDirt/blob/develop/library/default-effects-extra.scd)に例がたくさん載っているのでこちらを参考にしながら定義していきます。

今回はSchroederReverbという古典的なアルゴリズムリバーブを実装していきたいのですが、[SuperCollider公式ページのリバーブレーションについての解説項目](https://doc.sccode.org/Tutorials/Mark_Polishook_tutorial/Japanese_version/13.html#リバーブレーション)が参考になりそうなので、こちらに手を加えていい感じなリバーブを作っていきます。

```supercollider
(
    ~dirt.addModule('myreverb', { |dirtEvent|
        dirtEvent.sendSynth('myreverb' ++ ~dirt.numChannels,
            [
                reverb: ~reverb,
                out: ~out
            ]
        )
    }, { ~reverb.notNil });

    SynthDef("myreverb" ++ ~dirt.numChannels, { |out, reverb|
        var signal, chain, in, z, y;
        signal = In.ar(out, ~dirt.numChannels);
        // SchroederReverb
        // リバーブのプリ・ディレイ・タイム
        z = DelayN.ar(signal, 0.048);
        // 並列に7つのcombによるディレイ
        y = Mix.ar(Array.fill(7,{ CombL.ar(z, 0.1, 1, 15) }));
        // 32つのallpassによるディレイの２つの並列したチェーン（合計64個）
        32.do({ y = AllpassN.ar(y, 0.050, [0.050.rand, 0.050.rand], 1) });
        // 0.2*reverbだけ原音に混ぜて返す
        signal = signal + (0.2*reverb*y);
        ReplaceOut.ar(out, signal)
    }, [\ir]).add;
)
```

以上でreverbエフェクトが定義されたので、実際に音を聴いてみましょう。

<iframe width="100%" height="450" src="https://www.youtube.com/embed/8rDiDhWpASM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# おわりに

意外と簡単にオリジナルエフェクトを追加できるので、表現の幅もさらに広がりそうな気がします。個人的には、このような「技術・機能的ハードルによるちょっとした制約」がその楽器を使うことのオリジナリティだと思っているので、思い通りに定義できないところも含めて楽しむのがいいかなと思っています。

また今回実装したリバーブにもいろいろ改善点があるので、より魅力的なリバーブなどのエフェクトができたら紹介していきます！