見出し画像

シンプルなシンセのシミュレーション(1)

今回(と次回)は、シンプルなシンセのシミュレーションをしようと思います。

たいていのシンセは、[音源]-[フィルター]-[アンプ]という構造をしてると思います。これにLFOやエンベロープ他、各シンセならではのパラメータを駆使して音作りを行なうわけですが、今回は、この最小単位のシンセをSuperColliderで作ってPbindでシーケンスさせることを目標にします。

各パートの役割は以下のような感じだと思います。

[音源] 波形を作る・読み込む
[フィルター] 音源に対してフィルターをかける
[アンプ] 音量を操作する

では早速、これをSuperColliderでシミュレートします。
「command + b」で、サーバブートしておき、コードを書いていきます。


//ex1
(
{
	var wg, amp=0.2;
	wg = Saw.ar(220)!2 * amp;
}.play;
)

今回は、音源にSawを使いました。
wgという変数(入れもの)にSaw.arを入れて、playしています。
220という数字は、中央ラの音(440Hz)のオクターブ下です。
そのまま鳴らすと爆音になる(音量がマックスの1になる)ので、ampに0.2を入れて、Saw.ar全体に掛け算しています(0.2の音量で鳴ってくれます)。

!2は、左右のチャンネルから鳴らしたいので、こう書いています。
(!2についての詳細は、03.フィルターの記事にて。)

コードを実行すると(command + Returnすると)音が出ますが、ここまでだとまだフィルターが入ってません。

※音を止めるには「command + ピリオド」です。

まだフィルターが入ってない


フィルターを追加します。
今回は、レゾナンス付きローパスフィルターRLPFを使います。
RLPF.arの、ひとつめのパラメータがインプットさせたい信号ですので、そこにwgを入れます。

//ex2
(
{
	var wg, amp=0.2, flt;
	wg = Saw.ar(220)!2 * amp;
	flt = RLPF.ar(wg, 2000);
}.play;
)

2000は、カットオフ周波数です。(2kHzです。)
お好みの周波数に合わせてOKです。

さて、このex2だと下図のように、信号の流れの最下流がフィルターになってるので、

最下流をアンプ(音量)にしたいです。
(アンプとフィルターが逆になっていても、今のところ出音としては同じですが、フィルターで音作りしたものに対してトータルで音量調整したいので、逆にしておきたい。。)

というわけで、コードに少し手を加えます。

//ex3
(
{
	var wg, amp=0.2, flt;
	wg = Saw.ar(220)!2;
	flt = RLPF.ar(wg, 2000) * amp;
}.play;
)

これで、ノコギリ波をインプットしたフィルターユニットに対して音量調整を行なうかたちになりました。

Pbindでフレーズを鳴らすのが目標なので、この音をSynthDefに入れます。
SynthDefに入れると、音色に名前をつけることができるのと、アウトプットを指定することができます。

さっきのコード(ex3)の以下の3行を、

(
{

var wg, amp=0.2, flt;
wg = Saw.ar(220)!2;
flt = RLPF.ar(wg, 2000) * amp;

}.play;
)

SyntheDefフォーマットの真ん中にコピペする。

(
SynthDef(\synth1, { ←名前をつけられる

[ここにペースト]

Out.ar(0, flt); ←アウトプットを指定できる(今回はデフォルトのゼロ)

}).play;
)


出来上がったSynthDefはこちら。(実行すると音が鳴ります。)

//ex4
(
SynthDef(\synth1, {	//synth1とう音色名にしました

	var wg, amp=0.2, flt;
	wg = Saw.ar(220)!2;
	flt = RLPF.ar(wg, 2000) * amp;

	Out.ar(0, flt);	//fltをデフォルトの0から出力
}).play;
)


次に、アンプエンベロープ(aEnv)を作っておきます。
Pbindで鳴らすには、このSynthDefが鍵盤ON/OFFの情報を受け取れるようになっている必要があるので。
今回はadsrエンベロープを使いました。
ampにアンプエンベロープaEnvを掛けることで、音量変化を実現します。

//ex5
(
SynthDef(\synth1, {

	var wg, amp=0.2, flt, aEnv;
	aEnv = EnvGen.kr(Env.adsr(0.15, 1.0, 0.6, 0.15), 1, doneAction:2);

	wg = Saw.ar(220)!2;
	flt = RLPF.ar(wg, 2000) * amp * aEnv;

	Out.ar(0, flt);
}).play;
)

doneActionの左側の1は、「鍵盤ON」の意味です。
ここを0にすると「鍵盤OFF」となり、実行しても音は出ません。
(doneAction:2についてはこちらをご覧ください。乱暴な言い方をしてしまえば、エンベロープを作る時はほぼほぼ必ずここにdoneAction:2と書いておく必要があります。次回の記事で例外が出てくる見込みです。そのときにまた説明追記しておこうと思います。)


それではいよいよ、Pbindでシーケンスさせるための最後の工程です。
「鍵盤ON/OFF」のgateと「音程」のfreqをargに登録します。
これでSynthDefの外から(Pbindから)鍵盤ON/OFFと音程をコントロールすることができるようになります。

//ex6
(
SynthDef(\synth1, {
	arg gate=1, freq;
	var wg, amp=0.2, flt, aEnv;
	aEnv = EnvGen.kr(Env.adsr(0.15, 1.0, 0.7, 0.15), gate, doneAction:2);

	wg = Saw.ar(freq)!2;
	flt = RLPF.ar(wg, 2000) * amp * aEnv;

	Out.ar(0, flt);

}).add;
)
//gateのデフォルト値は1(鍵盤ON)を設定しておく必要があります。

そして、今までSynthDefに対して.playしていたのを、.addに置き換えてコードを実行します。
これで、サーバにこのSynthDefが置かれます。

Pbindを実行します。

//ex7
(
Pbind(
	\instrument, \synth1,
	\midinote, Pseq([59, 66, 69, 66, 73, 59, 64, 66], inf),
	\dur, 0.5,
	\sustain, 0.1
).play(TempoClock(140/60));
)

サーバに置かれたsynth1という音色に対してmidinoteを指示しています。(各midinoteの鍵盤を弾いている(ON/OFFしている)イメージです。)



フィルターにレゾナンスを設定することもできます。

RLPF.ar(wg, 2000, 1)

こんな感じで、RLPF.arに三つ目のパラメータを入力できます。
1がレゾナンスなしで
0がレゾナンスMAXです。
(ex6では、パラメータを入れなかったので、デフォルトの1で処理されていました。)


実際のシンセならば、フィルターにもエンベロープをあてたり、ピッチにLFO(ビブラート)をあてたりしたいところです。
次回、そのあたりの機能を追加していこうと思います。


ちなみに、ex6のコードは、下記のように書いても全く同じ意味で同じ信号の流れです。

//ex8
(
SynthDef(\synth1, {
	arg gate=1, freq;
	var wg, amp=0.2, flt, aEnv;
	aEnv = EnvGen.kr(Env.adsr(0.15, 1.0, 0.7, 0.15), gate, doneAction:2);

	wg = Saw.ar(freq)!2;
	wg = RLPF.ar(wg, 2000) * amp * aEnv;

	Out.ar(0, wg);

}).add;
)

wg = RLPF.ar(wg, 2000) * amp * aEnv;
Out.ar(0, wg);

この部分が違いです。fltという変数を使っていません。

SuperColliderのコード上の「=(イコール)」は「=を挟んで左と右が同等」という意味ではなく「左側の入れ物に、右のものを入れる」という意味なので。(このあたりピンと来ない人は、こちらの記事をぜひ読んでみてください)

今回は音源・フィルター・アンプ、の役割をわかりやすくコードにしたかったので、ex6のようにwg/flt/ampが文字で見えるかたちにしました。


<目次へ>
https://note.com/sc3/n/nb08177c4c011


いいなと思ったら応援しよう!