【SuperCollider】SuperColliderで擬似立体音響

# はじめに

SC3pluginsに[atk-sc3](https://github.com/ambisonictoolkit/atk-sc3)を追加することで、SuperColliderでも立体音響を実現できるみたいです。

ちなみにこの内容はGithubにもアップロード済みなので、早く動かしたい方はそちらを参照してください

https://github.com/mynkit/binaural-TidalCycles

# ATKのインストール

SuperColliderのエディタで以下を実行。

```supercollider
Quarks.install("https://github.com/ambisonictoolkit/atk-sc3.git");
```

インストールが完了したら、一度SuperColliderを再起動します。再起動したくない場合はReCompileする必要があるみたいです。(参考: [When using the Atk command, “ERROR: Class not defined” is displayed](https://scsynth.org/t/solved-when-using-the-atk-command-error-class-not-defined-is-displayed/5648/6?u=mynkit))

その後、依存パッケージをインストール。

```supercollider
Atk.downloadKernels;
Atk.downloadMatrices;
Atk.downloadSounds;
```

# SuperCollider擬似立体音響

こちらのコードを参考に実装します。

https://depts.washington.edu/dxscdoc/Help/Guides/ATK-SynthDef-Examples.html

```supercollider
// SuperColliderは先に起動しておく

(
// First we will define our decoder
// stereo decoder
var decoder = FoaDecoderMatrix.newStereo((131/2).degrad, 0.5);

// next we define a synth using FoaPanB, and decoder using FoaDecode
SynthDef(\foaEncode1, {
    var src, theta, phi, foa, out;

    // our source: pink noise
    src = PinkNoise.ar(-6.dbamp);

    // theta is our angle on the X-Y plane and phi is our elevation
    // use a MouseX to control theta in real time, from pi to -pi
    // use a MouseY to control theta in real time, from pi/2 to -pi/2
    theta = MouseX.kr(pi, -pi);
	phi = MouseY.kr(pi/2, -pi/2);

    // Encode into our foa signal
    foa = FoaPanB.ar(src, theta, phi);

    // decode our signal using our decoder defined above
    out = FoaDecode.ar(foa, decoder);

    Out.ar(0, out);
}).add;
)

// 再生後、マウスカーソルを移動させることにより音像の位置が変化します
a = Synth(\foaEncode1);
```

最初の`FoaDecoderMatrix.newStereo`の引数は、自分の耳に合うように調整できます。

[ドキュメント](https://depts.washington.edu/dxscdoc/Help/Classes/FoaDecoderMatrix.html#*newStereo)を見ると、`FoaDecoderMatrix.newStereo`の第一引数はスピーカーの角度、第二引数はマイクの指向性パターンを指定できます。デフォルト引数である0.5はCardioidといい、これは正面の音がよく聞こえる状態です。数値を上げていくとこの指向性がより強くなります。

正直上下方向の変化はわかりづらいですが、水平方向の変化はわかりやすいと思います。

<img src="https://www.soundhouse.co.jp/images/shop/prod_img/r/rode_nt5a_3.jpg" width="90%">

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(出典: [カーディオイド:Cardioidとは](https://www.g200kg.com/jp/docs/dic/cardioid.html#:~:text=Cardioid%20%E3%82%AB%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA%E3%82%A4%E3%83%89,-%E8%BE%9E%E5%85%B8%E5%86%85%E6%A4%9C%E7%B4%A2&text=%E3%83%9E%E3%82%A4%E3%82%AF%E3%81%AE%E6%8C%87%E5%90%91%E6%80%A7%E3%81%AE,%E3%81%AF%E3%82%AB%E3%83%BC%E3%83%87%E3%82%A3%E3%82%AA%E3%82%A4%E3%83%89%E7%89%B9%E6%80%A7%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82))



# TidalCyclesで利用する

せっかくなので、TidalCyclesでも使用できるようにエフェクト化してしまいましょう。TidalCyclesにおけるエフェクトの追加方法は以下の記事にまとめてありますのでご確認ください。

[【TidalCycles】オリジナルエフェクトの追加方法](https://qiita.com/s2hap/items/e83780c35881eadad84a)


早速ですが、結果は以下のようになりました。

```supercollider:startup.scd
// SuperDirtの起動後以下を実行

(
var decoder = FoaDecoderMatrix.newStereo((100).degrad, (3-sqrt(3))/2);

~dirt.addModule('HRTF', { |dirtEvent|
	dirtEvent.sendSynth('HRTF' ++ ~dirt.numChannels,
		[
			theta: ~theta,
			phi: ~phi,
			out: ~out
		]
	)
}, { ~theta.notNil or: { ~phi.notNil }});
SynthDef("HRTF" ++ ~dirt.numChannels, { |out, theta, phi|
	var signal, in, left, right, foa;
	in = In.ar(out, ~dirt.numChannels);
	left = in[0];
	right = in[1];
    // panエフェクトの効果を打ち消すためにモノラルぽくする
	in = [(left+right), (left+right)];
	// theta is our angle on the X-Y plane and phi is our elevation
	theta = (theta-1) * pi;
	phi = phi * (-pi/2);
	// Encode into our foa signal
    foa = FoaPanB.ar(in, theta, phi);
	// decode our signal using our decoder defined above
    signal = FoaDecode.ar(foa, decoder);
	ReplaceOut.ar(out, signal)
}, [\ir]).add;
)
```


```haskell:BootTidal.hs
-- 下記をBootTidal.hsに追記してTidalCyclesを再起動させます。

:{
theta = pF "theta"
phi = pF "phi"
:}

```

FoaDecoderMatrix.newStereoの引数は自分の耳に合わせて調整しました。


theta, phiはそれぞれ-1~1, -1~1の範囲で使える引数(厳密にはそれ以外での範囲でも動きますが)で、特定の数値における挙動は以下のようになります。

- **theta**
  - 0で正面、1で背面
  - 0.5で真右、-0.5で真左
- **phi**
  - 0で水平
  - 1で真上、-1で真下
    - そのためこのときはthetaをどの値にしても横方向では中央に鳴ります


早速TidalCyclesで動かしてみましょう

```haskell:live.tidal
-- 通常のpan
d1
  $ s "hc*16" # gain 1
  # pan (range 0 1 sine)

-- 擬似立体音響
d1
  $ s "hc*16" # gain 1
  # theta (range 1 (-1) sine)
  # phi (range 1 (-1) (slow 3 $ sine))
```

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

全く音楽的なものではないですが、通常のpanと比べると音の立体感というか、広がり方はだいぶ違うと思います。
`# pan 1`の音と`# theta 0.5`の音を比較してみてもわかりやすいかもしれません。

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

thetaを用いたほうがより真右にある感じが強調されると思います。


ATKパッケージのおかげで簡単に立体音響的なことが実現できることがわかったので、いろいろ活用できそうです。