SC3 こーどりーでぃんぐ2

前回に続いて、snare,hatを読んでみよう。わからないところはさらっとすっ飛ばしていますよ。

snare 08091500Acid309 by_otophiliaより

SynthDef("snare", {	
	arg outBus=0, amp=0.8;
	var env0, env1, env2, env1m, oscs, noise, out;
	
	env0 = EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.03, 0.10], [-4, -2, -4]));
	env1 = EnvGen.ar(Env.new([110, 60, 49], [0.005, 0.1], [-4, -5]));
	env1m = env1.midicps;
	env2 = EnvGen.ar(Env.new([1, 0.4, 0], [0.05, 0.13], [-2, -2]), doneAction:2);
		
	oscs = LFPulse.ar(env1m, 0, 0.5, 1, -0.5) + LFPulse.ar(env1m * 1.6, 0, 0.5, 0.5, -0.25);
	oscs = LPF.ar(oscs, env1m*1.2, env0);
	oscs = oscs + SinOsc.ar(env1m, 0.8, env0);
		
	noise = WhiteNoise.ar(0.2);
	noise = HPF.ar(noise, 200, 2);
	noise = BPF.ar(noise, 6900, 0.6, 3) + noise;
	noise = noise * env2;
		
	out = oscs + noise;
	out = out.clip2(1) * amp;
			
	Out.ar(outBus, out.dup);
}).send(s);

snareを読む

env0 = EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.03, 0.10], [-4, -2, -4]));

通常のenv。アタック音用

env1 = EnvGen.ar(Env.new([110, 60, 49], [0.005, 0.1], [-4, -5]));
env1m = env1.midicps;

通常のenvだけど、midi値へ変換。この時点でfreq用だとわかる。

env2 = EnvGen.ar(Env.new([1, 0.4, 0], [0.05, 0.13], [-2, -2]), doneAction:2);

残響ノイズ用。こちらの方がevn0よりも値の減少が緩やかに設計されている。env0,env2ともにplotして確認してみるとわかりやすい。値が0になるタイミングは同じなので、env0,env2どちらにdoneAction:2つけても良いと思われるが、感覚的にはenv2が終了した時点でシンセ解放がしっくりくる。

oscs = LFPulse.ar(env1m, 0, 0.5, 1, -0.5) + LFPulse.ar(env1m * 1.6, 0, 0.5, 0.5, -0.25);
oscs = LPF.ar(oscs, env1m*1.2, env0);
oscs = oscs + SinOsc.ar(env1m, 0.8, env0);

メイン波形、アタック音。freqをずらしたものを足し合わせることで厚みを出す。他の引数も異なるが、mul,addなので微調整だろう。ローパスのカットオフは*1.2でごっそり削っている。最後にサイン波を足しさらに厚みを増す。相変わらず位相をずらしている。理由は不明。

noise = WhiteNoise.ar(0.2);
noise = HPF.ar(noise, 200, 2);
noise = BPF.ar(noise, 6900, 0.6, 3) + noise;
noise = noise * env2;

こちらは残響ノイズ波形。ハイパスかましたノイズをさらにバンドパスして一部強調?それに元のハイパスかましたノイズを足している。最後に*env2しているため、終了後にシンセが解放される。なぜこう作っているのかさっぱりわからないが、これだけでスネアっぽさがでるのだから凄い。

out = oscs + noise;
out = out.clip2(1) * amp;

clip2で多少歪ませた後に出力値を調整している、細かいがこの順番は大事だろう。アタック音がoscs、残響ノイズがnoise。どちらか一方削って実行してみると理解しやすい。アタック音と残響音を分けてつくり、最後に足し合わせる手法は真似したい。シンセを作成するうえで基本的なことだが、なぜかSCやっていると忘れがち。

hat 08091500Acid309 by_otophiliaより

SynthDef("hat", {	
	arg outBus=0, amp=0.3;
	var env1, env2, out, oscs1, noise, n, n2;
	
	n = 5;
	thisThread.randSeed = 4;
	
	env1 = EnvGen.ar(Env.new([0, 1.0, 0], [0.001, 0.2], [0, -12]));
	env2 = EnvGen.ar(Env.new([0, 1.0, 0.05, 0], [0.002, 0.05, 0.03], [0, -4, -4]), doneAction:2);
	
	oscs1 = Mix.fill(n, {|i|
		SinOsc.ar(
			( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps,
			SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12),
			1/n
		)
	});
		
	oscs1 = BHiPass.ar(oscs1, 1000, 2, env1);
	n2 = 8;
	noise = WhiteNoise.ar;
	noise = Mix.fill(n2, {|i|
		var freq;
		freq = (i.linlin(0, n-1, 40, 50) + rand2(4.0) ).midicps.reciprocal;
		CombN.ar(noise, 0.04, freq, 0.1)
	}) * (1/n) + noise;
	noise = BPF.ar(noise, 6000, 0.9, 0.5, noise);
	noise = BLowShelf.ar(noise, 3000, 0.5, -6);
	noise = BHiPass.ar(noise, 1000, 1.5, env2);
	
	out = noise + oscs1;
	out = out.softclip;
	out = out * amp;
	
	Out.ar(outBus, out.dup);
}).send(s);

hatを読む

n = 5;

メイン波形でMixを使用しており、その重ねる回数を定義。このシンセではsin波を5回重ねている。

thisThread.randSeed = 4;

ランダムシード定義。thisThreadは調べていないが、このコード内のスレッドって意味かと思われ。

env1 = EnvGen.ar(Env.new([0, 1.0, 0], [0.001, 0.2], [0, -12]));
env2 = EnvGen.ar(Env.new([0, 1.0, 0.05, 0], [0.002, 0.05, 0.03], [0, -4, -4]), doneAction:2);

いつものenv

oscs1 = Mix.fill(n, {|i|
	SinOsc.ar(
		( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps,
		SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12),
		1/n
	)
});

ハットのメイン波形。まず、Mixは複数チャンネル(配列)を一つのチャンネルに落とし込む。波形を足し合わせているという認識でおk。
freqをちょっと詳しくみてみよう。サイン波を引数ごと分けると以下となる。

freq: ( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps
phase: SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12)
mul: 1/n

まだわかりにくい。linlinが原因かな。linlinはUGenのヘルプ参照するよーに。linlin(inMin, inMax, outMin, outMax, clip)だ
freqのrand2とmisicpsを外して.postlnを付けて値を出力し、それらを配列にいれてplotしてみる。次にmidicpsをつけて、最後にrand2をつけてplot。

[42,50,58,66,74].plot;
[92.498605677909,146.8323839587,233.08188075904,369.99442271163,587.32953583482].plot;
[98.263516145654,125.48249990264,257.11842831899,335.51069863238,690.38826095162].plot;

本来linlinで42-72まで値が線形に出力されるが、midi値に変換しており、さらにrand2加えているので結構がたついたplotが確認できる。きれいに並べるよりも多少汚した方がリアルになるとの考えだろうか。

oscs1 = BHiPass.ar(oscs1, 1000, 2, env1);

最後にフィルターかましてアタック音の完成

n2 = 8;
noise = WhiteNoise.ar;
noise = Mix.fill(n2, {|i|
	var freq;
	freq = (i.linlin(0, n-1, 40, 50) + rand2(4.0) ).midicps.reciprocal;
	CombN.ar(noise, 0.04, freq, 0.1)
}) * (1/n) + noise;
noise = BPF.ar(noise, 6000, 0.9, 0.5, noise);
noise = BLowShelf.ar(noise, 3000, 0.5, -6);
noise = BHiPass.ar(noise, 1000, 1.5, env2);

ノイズも基本的にメイン波形と同じ作り方。が、freqを逆数にしていたり、よくわからないフィルターかましたりしている。音響工学に基づいて作成しているのだろうか。とりあえず、変数freqをpostlnしてplotしてみよう。

[0.025,0.023529411764706,0.022222222222222,0.021052631578947,0.02,0.02,0.02,0.02].plot;
[0.025102866412683,0.024835818361551,0.021720987467888,0.021087916441834,0.019894677979736,0.021583178205406,0.020957042037079,0.019790472120955].plot;
[0.012250345692431,0.011950968549663,0.0085616529455822,0.0079047203612541,0.0067071490182452,0.0084175066708027,0.0077706562061599,0.0066053916708074].plot;

こちらのノイズも謎である。

out = noise + oscs1;
out = out.softclip;
out = out * amp;

snareと同じくアタックと残響用のノイズをわけている。こちらもnoiseを外したり、oscsを外したりして確認してみるとよくわかる。softclipはまんまソフトなクリップという認識で良いかと。

いじょう

ノイズを制するものはパーカッションを制す。
(やはりフィルターがよくわかっていない、誰か分かりやすい解説をしてくれるまで待ってます!)