見出し画像

ディレイ系エフェクトとPfx

前回はDelayNをCombNと勘違いしたまま書き進めてしまい、お詫びを追記することになってしまいました。。(投稿したあとに「あれ?」と気づいて焦りました。。)
今日はAllpassNも含めてディレイ系クラスについて書きます。
また、Pfxの役割についても触れます。

CombN, CombL, CombC


CombN.ar(インプット信号, maxdelaytime, delaytime, decaytime, mul, add)

delaytimeは文字どおりディレイタイム。単位は秒です。
maxdelaytimeは、このクラスがバッファを確保するための数値で、たいていはdelaytimeと同じでよいですが、delaytimeをモジュレートするなどの場合はモジュレートされて最大になるディレイタイムを書いておきます。
最初から大きめの数値を入れておくのもアリですが、あまり大きすぎると音がプツプツ途切れてしまいます。(詳細不明です。)

decaytimeがいわゆるフィードバック。単位は秒です。
設定した秒数の長さまで音が(小さくなりながら)繰り返されます。

muladdはたいていのUGen(ユニットジェネレーター)についているパラメータで、意味は全て共通です。mulは振幅(音量。デフォルトで1.0)、addは振幅にプラスマイナスする値でデフォルトは0。

CombNはもちろんディレイエフェクトとして使うことができますが、名前から察するに本来はコムフィルターとして用意されたクラスではなかろうかと思います。

コムフィルターは一言でざっくり言うと
波形同士の干渉を利用して元波形に独特な倍音を生む効果で、出音はフランジャーみたいな感じです。

(まずcommand+bでサーバ起動します。)
(メニューからBootSeverを選択してもOKです。)


LFTriでドレミを鳴らしたこちらのサンプルに・・・

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, amp=0.3;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	sig = LFTri.ar(freq) * env;
	Out.ar(0, sig!2 * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;

// Pbindを~seqという変数に入れて、~seqに対して.playしています。
// シーケンスを止めるにはコマンド+ピリオドです。
)

コムフィルターをあてたのがこちら↓。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, amp=0.3;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	sig = LFTri.ar(freq) * env;
	sig = CombN.ar(sig, 1/500, 1/500, 0.5);
	Out.ar(0, sig!2 * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)

1/500秒というのは特に意味ありません。他の値でも大丈夫です。
ただこのくらい小さい値が効き目あります。

このディレイタイムを一定ではなくLFOなどで揺らしてあげるとまさにフランジャーサウンドになります。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, mod, amp=0.2;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	mod = SinOsc.ar([0.5, 0.55]).range(1/500, 5/500);
	sig = LFTri.ar(freq) * env;
	sig = sig + CombN.ar(sig, 5/500, mod, 0.5);
	Out.ar(0, sig * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)

左右のLFO周期を違う数値([0.5, 0.55])にしています。
それと↓この行は、
sig = sig + CombN.ar(sig, 5/500, mod, 0.5)
原音sigに、エフェクト音CombNを混ぜています。

CombNをコムフィルターで使うにはディレイタイムを極小にしているのが特徴的ですが、このディレイタイムを大きくすれば普通のディレイエフェクトとしても機能します。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	sig = sig + CombN.ar(sig, 2, [0.38,0.6], 4, 0.35);
	Out.ar(0, sig * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)

左右のディレイタイムを違う数値([0.38,0.6])にしています。
これもCombNだけだとディレイ音のみになってしまうので、原音のsigにCombNをプラスしてます。また、CombNのmulを使ってエフェクト音を少し下げています(0.35)。

ですが次の例のように、短い音にディレイをかけたい場合、このままだとエンベロープのdoneAction:2が効いてしまって、ディレイの効果を感じることができません。
ディレイ音が来る前にdoneAction:2が音を止めてしまうからです。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 0.4), gate, doneAction:2);
	sig = sig + CombN.ar(sig, 0.6, [0.38,0.6], 4, 0.3);
	Out.ar(0, sig * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)


次の例のようにdoneAction:2をつけなければ出音的にはOKなのですが

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 0.4), gate);
	sig = sig + CombN.ar(sig, 0.6, [0.38,0.6], 4, 0.3);
	Out.ar(0, sig * amp);
}).add;

~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

Node Treeを表示しながら上記のコードを実行するとわかるとおり、

s.plotTree;

いつまでもsynthが残ってしまってソフト的には健全ではない状態です。(音が鳴っていないのに処理負荷がかかる状態)

そこでリバーブ用のSynthDefを作ってそこにLFTriの信号を送る作戦にします。(そうすると音源側のdoneAction:2の影響をエフェクトが受けない。)


SynthDefから別のSynthDefに信号を送るには、バスを利用します。
バスとエフェクト」の回で書いた方法です。
今回はバスに変数を使わず、バスナンバー(50)を使いました。
*SuperColliderは1024個のオーディオバスを持っているので、明らかに空いてるバス(←ステレオ出力してるなら、バス2以降は全部空き)なら何番でもOKです。

実際にコードにしたのが下記です。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 0.4), gate, doneAction:2);
	Out.ar(50, sig!2 * amp);
}).add;

SynthDef(\effect1, {
	var sig, eff;
	sig = In.ar(50, 2);
	eff = sig + CombN.ar(sig, 0.6, [0.38,0.6], 4, 0.3);
	Out.ar(0, eff);
}).add;

~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

Pfx(~seq, \effect1).play;
)

短い音でもディレイ音が途切れずに再生できました。


Pfx


Pfxについても「バスとエフェクト」の回に書いていますが、今日はPfxについて少しだけ深掘りを。

s.plotTreeで表示させたNode Treeを見ながら実行してみてほしいのですが、例えば・・・

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	Out.ar(50, sig!2 * amp);
}).play;
)

上記だけの実行だと音は出ないですよね。
当然、バス50から出力させているので。(Node Tree上に白いボックスができるので、再生されているようではあるが、音は聞こえない状態)

この音が再生中、すぐにディレイの方のSynthDefを実行したら?
バス50から出ている音を受け取って無事に音が聞こえるのか?
・・・しかしそれでも音は出ないです。
このとき(\synth1のSynthDefを実行したあと\effect1のSynthDefを実行したとき)Node Treeを見ると、こんな感じになっています。

effect1が上で、その下に
synth1の白ボックスがありますね。

SuperColliderって、このNodeTreeの上から下に信号が流れるようになってるんです。
なのでeffect1が上だと音出ないんです。。

("あとに実行した白いボックス"が上に積まれていくようになっています。なので・・・)

音を出すには、effect1を先に実行しておいてsynth1を実行する、という順序が必要で。
そうするとNode Treeで見てもsynth1が上になっていて、信号が下に流れたときにeffect1が受けられるようになっていることを確認できます。

これでエフェクトを通した音が出ることはわかりました。で、
今回の「ドレミ」のシーケンスはPbindで鳴らしたいので、この実行順をどうするか、ということなんですが(コードで先にeffect1を書いてもダメなんですよね・・・)、、、そこでPfxの出番というわけです!

Pfxは、Pfx内に書かれたエフェクト名のSynthDefがNode Treeで下になるようにうまいことやってくれる、という機能を持ってるんです。


実はPfxを使わずとも他にも手はあります。
Groupを作ってその中にエフェクトを入れたり、addToHead/addToTailというコマンドを使う方法もあります。(それはまた別の機会に。)
より簡単な方法としては、、、上記の例だとバス50が常に受け入れOK状態であればよいので、「エフェクトのSynthDefだけaddではなくplayしておく」という方法です。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, amp=0.3;
	sig = LFTri.ar(freq) * EnvGen.kr(Env.perc(0.01, 0.4), gate, doneAction:2);
	Out.ar(50, sig!2 * amp);
}).add;

SynthDef(\effect1, {
	var sig, eff;
	sig = In.ar(50, 2);
	eff = sig + CombN.ar(sig, 0.6, [0.38,0.6], 4, 0.3);
	Out.ar(0, eff);
}).play;

~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], 3),
	\dur, 0.5,
);

~seq.play;
)

この方法だとPfxは使わず普通のPbindでもOKです。一度実行するとエフェクトのSynthDefだけずっとplayしっぱなしになってしまうので、、
*上記コードは「ド・レ・ミ」の繰り返しを3回でやめていますが、effect1という白いボックがNode Tree上に表示されたままです。(コマンド+ピリオドで消えます。)
さっと試すくらいのコードならこれもいいですかね。


補完


話をCombNに戻します。
CombLやCombCとの違いは何でしょうか。
delaytimeを変調するとき線形補間されるのがCombLで、曲線で補間されるのがCombC、です。なのでただディレイとして使う場合はN, L, Cのどれを使っても同じ。
前述したフランジャーサウンドのコード例の場合(今回の3つ目のコード)、ディレイタイムを変調してるので、コード内のCombNの部分をCombCに置き換えると明らかに音が良くなります。CombNの方が少しノイジーに感じるはずです。
CPU負荷はN<L<Cという順に高まるので、どんな使い方をするかでN, L, Cを選択するのがいいと思います。


AllpassN, AllpassL, AllpassC


オールパスフィルターは「高周波になるほど位相がずれていく」という処理をするフィルターです。面白そうですね。
使い方はCombNと同じ様な感じで使うことができ、
持っているパラメータも下記のとおりCombNと同じです。

AllpassN.ar(インプット信号, maxdelaytime, delaytime, decaytime, mul, add)

今日の2個目のコードのCombNの部分をAllpassNに置き換えました。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, amp=0.3;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	sig = LFTri.ar(freq) * env;
	sig = sig + AllpassN.ar(sig, 1/500, 1/500, 0.5);
	Out.ar(0, sig!2 * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)

ただし位相がずれた音になるだけなので、ただAllpassNを通すだけだと音色変化はありません。このエフェクトの効果を感じるには、原音とミックスして出力させてあげる必要があります。
上記はそのようにしています。
(sig + AllpassN.ar の部分)
(試しに "sig + " だけを削除して実行してみると、AllpassNだけでは原音と音色に差がなく聞こえると思います。)

少し不思議な音色変化ですが、CombN同様面白い音作りができそうです。
今までディレイとしてしか使ったことがなかったのでもっと研究してみたい。。
CombNでフランジャーぽくなったコードをAllpassLに変えてみました。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, mod, amp=0.2;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	mod = SinOsc.ar([0.5, 0.55]).range(1/500, 5/500);
	sig = LFTri.ar(freq) * env;
	sig = sig + AllpassL.ar(sig, 5/500, mod, 0.5);
	Out.ar(0, sig * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)

Combのときよりも音像が奥まったように聴こえます。


DelayN, DelayL, DelayC


DelayN.ar(インプット信号, maxdelaytime, delaytime, mul, add)

パラメータにdecaytimeが無いということでもわかるとおり、DelayNは信号をただただ送らせて再生するだけです。
地味ではありますが、出番はあります。
例えばリバーブの初期反射を模すときに使ったり、片チャンネルだけ少し送らせてみたり。

delaytimeを変調できるので、強烈な音色にもなり得ます。
(これもComebCと同じく曲線補間されるDelayCが一番なめらかな音です。)
前回紹介したようにLocalIn / LocalOutを使えばフィードバックを作ることも可能です。

(
SynthDef(\synth1, {
	arg freq=440, gate=1;
	var sig, env, mod, fbk, amp=0.2;
	env = EnvGen.kr(Env.perc(0.01, 2), gate, doneAction:2);
	fbk = LocalIn.ar(2);
	mod = SinOsc.ar([2, 3]).range(0.1, 0.3);
	sig = LFTri.ar(freq) * env;
	sig = sig + DelayC.ar(fbk, 1, mod, 0.5);
	LocalOut.ar(sig);
	Out.ar(0, sig * amp);
}).add;


~seq = Pbind(
	\instrument, \synth1,
	\midinote, Pseq([60, 62, 64, \], inf),
	\dur, 0.5,
);

~seq.play;
)


*補足*
原音を直接エフェクトに通さず、Auxセンドのように信号を送る方法についての記事をこちらに書きました。


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




この記事が気に入ったらサポートをしてみませんか?