Sonic Piで、自作Synthをつくる

## Sonic Piにおける楽器の定義

Sonic Piでは、音響合成にSuperColliderのサーバー「scsynth」を用いている。Sonic Piのパッケージの中で定義されたSuperColliderの楽器の定義(Synthdef)を読み込んでいる。Sonic Piでは、あらかじめ多くの楽器(Synths)が定義されていて、use_synthで呼び出すだけで簡単に演奏が可能となっている。

例えば、FM合成を用いた楽器「fm」を演奏するには以下のようにする。

```rb
use_synth :fm
play 60
sleep 0.5
```

Sonic Piに内包されている楽器の定義は、以下のフォルダ内に格納されている。

```
Sonic Pi.app/etc/synthdefs/designs/sonic_pi/synths
```

中を覗いてみると、記述は一般的なSuperColliderの言語(sclang)ではなく、Clojureを用いた[OverTone](http://overtone.github.io/)の書式で書かれている。OverToneで楽器定義をするには、かなりの修練が必要となる。

しかし、Sonic Piでは普通のSuperColliderの楽器定義 (SynthDef) の書式で書いた楽器を読み込む機能が用意されている。ここでは、その方法で楽器を定義して読み込んでみる。

## SuperColliderでSonic Piの楽器を定義

### Hello World (Sin波)

まずは、Hello World的な意味合いで、440HzのSin波を生成する楽器を定義してみたい。SuperColliderで以下のように「piTest」という名前の楽器を定義している。

```c
SynthDef("piTest", {
	//出力先のバスを引数で設定
	arg out_bus = 0;
	//440HzのSin波生成
	var mix = SinOsc.ar(440).dup;
	//Envelope生成
	var env = EnvGen.ar(Env.perc(0.0, 1.0, 1.0), doneAction: 2);
	//Envelope適用
	mix = mix * env;
	Out.ar(out_bus, mix);
}
//ファイルの出力先を指定
).writeDefFile("/Users/tado/Desktop/my-synths");
```

この例では、ユーザー名「tado」のデスクトップに「my-synths」というフォルダを作成してその中に定義ファイルを生成している。witeDefFileの出力先の指定は、使用している環境にあわせて変更する。

これを、Sonic Piから読み込んで演奏してみる。ここでも読み込み先のフルパスは環境にあわせて変更する必要がある。

```rb
#Synth定義を読み込む(フォルダの場所を指定)
load_synthdefs "/Users/tado/Desktop/my-synths"
#Synth名を指定
use_synth :piTest

play 60
sleep 1.0
```

ここで注意しなければならないのは、Sonic Piで使用する楽器はエンベロープを用いて必ず一定時間で停止する楽器のデザインにしておく必要があるところだ。もしそうでない場合は、どんどん音が重なっていき最後にはSonic Piが停止してしまう。

### 音程と音量を指定できるように

先程の状態では、音程と音量をSonic Piから変化させることができない。少し改造して、音程と音量を受け取れるようにしてみる。

```c
SynthDef("piTest", {
  //音程と音量を引数を指定
  arg note = 52, amp = 1, out_bus = 0;
  //音程(MIDI番号)から周波数へ
  var freq = note.midicps;
  //周波数をSin波に適用
  var mix = SinOsc.ar(freq).dup;
  var env = EnvGen.ar(Env.perc(0.0, 1.0, 1.0), doneAction: 2);
  mix = mix * env;
  Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
```

Sonic Piから音程と音量をランダムに指定してみる。

```rb
load_synthdefs "/Users/tado/Desktop/my-synths"
use_synth :piTest

loop do
  play rrand_i(40, 80), amp: rrand(0.5, 0.8)
  sleep 0.5
end
```

### エンベロープと定位(Pan)も可変に

次に、エンベロープと左右の定位もSonic Piから設定できるようにしてみる。ここでも引数を追加して、対応するSuperColliderのプログラムを追加することで実現できる。

```c
SynthDef("piTest", {
	arg out_bus = 0, note = 52, amp = 1, attack = 0, release = 1, pan=0;
	var freq = note.midicps;
	var mix = SinOsc.ar(freq);
	var env = EnvGen.ar(Env.perc(attack, release, 1.0), doneAction: 2);
	//Panを適用
	mix = Pan2.ar(mix * env, pan);
	Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
```

Sonic Pi側のプログラムも変更してみる。

```rb
load_synthdefs "/Users/tado/Desktop/my-synths"
use_synth :piTest

loop do
  play rrand_i(40, 80), amp: rrand(0.5, 0.8), pan: rrand(-0.8, 0.8), release: 2.0
  sleep 0.5
end
```

## より複雑な楽器のデザインへ

ここから先は、工夫次第でいろいろな発展が可能だ。SuperCollierの様々なユニットジェネレイター(Ugens)を駆使して、複雑な楽器を定義して、それをSonic Piから読み出すことで、表現の可能性が大きく拡がる。

例えば、Moog風シンセサイザーの楽器を定義して、演奏してみる。

SuperCollider側

```c
SynthDef("piTest", {
	arg out_bus = 0, note = 52, amp = 1, attack = 0, release = 1, detune = 1.01, pan=0;
	var freq = note.midicps;
	var mix =  MoogFF.ar(
		Pulse.ar([freq,freq*0.5], 0.8),
		freq*2.5,
		128
	);
	var env = EnvGen.ar(Env.perc(attack, release, amp), doneAction: 2);
	mix = Pan2.ar(mix * env, pan);
	Out.ar(out_bus, mix);
}
).writeDefFile("/Users/tado/Desktop/my-synths");
```

Sonic Pi側

```rb
load_synthdefs "/Users/tado/Desktop/my-synths"
use_synth :piTest

live_loop :live do
  with_fx :flanger do
    use_random_seed 8
    32.times do
      3.times do
        ns = (scale :c1, :mixolydian, num_octaves: 8)
        play ns.choose, attack: 0.0, release: 2.0, amp: 1.5
      end
      sleep 0.25
    end
  end
end
```