フィードバックについて
LocalOut/LocalInはCombNなどのディレイクラスにフィードバックを与える機能として使うというのが本来の使い方かなと思うのですが
sc140のNathaniel VirgoさんのコードがLocalOut/LocalInを音作りにカッコよく使っているのを見て
https://supercollider.github.io/sc-140
僕も音作りに積極的に使えないかなぁと考えLocalOut/LocalInについて研究・実験しています。
まだまだ全然使いこなしていないですが、いろいろやっていて発見したこともあるので書きます。
CombN
まずはCombNについてです。
CombNは受け取った信号をディレイさせます。
パラメータは下記のとおり
CombN.ar(input信号, maxdelaytime, delaytime, decaytime, mul, add)
下記のコードのような使い方になると思いますが
//サーバ起動
s.boot;
//コード
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, amp=0.7;
env = EnvGen.kr(Env.perc(0.01, 1.5), gate, doneAction:2);
sig = SinOsc.ar(freq, 0, 1) ;
sig = sig + CombN.ar(sig, 0.5, 0.15, 4.0);
Out.ar(0, sig!2 * amp * env);
}).play;
)
(原音sigにディレイ成分CombNを足して出力しています。)
普通のディレイエフェクトにあるようなフィードバックのパラメータが無いので、1度しか繰り返しません。
******** 訂正(2022.11.23)********
フィードバックのパラメータはありますので訂正します。
上記のdecaytimeの部分がそれです。
フィードバックのパラメータを持たないのはDelayNというクラスでした。。
文面からするとここはDelayNを使ったコード例が適していますが、このあともCombNで実験を行なったためCombNの例のまま書き残しておきます。
decaytimeの数値を大きくしても上記コード例ではEnv.percで音そのものが減衰しているので、フィードバックの効果は感じられないということもあり、改めてCombNやDelayNについては次回詳しく書きたいと思います。
**************************
LocalOutとLocalIn
LocalOut/LocalInはイメージ的には図のような信号の流れで、SynthDefの中にOutとInを持つことができる感じです。
LocalOut/LocalInはひとつのSynthDefに1個ずつだけ存在させることができます。
さて先ほどのコードにLocalOut/LocalInを入れてみます。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=0.9, amp=0.7;
env = EnvGen.kr(Env.perc(0.01, 1.5), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq, 0, 1) ;
sig = sig + CombN.ar(fbk, 0.5, 0.15, 4.0);
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
出力信号をOut.arの手前で受け取って
LocalOut.ar(sig);の信号は
LocalIn.ar()に入ってきます。
LocalIn.ar(1)の1は扱うチャンネル数です。
モノラル信号が入ってくればよいので1にしています。
上記コードを実行すると、(減衰する音なので)ちょっと聞き取りづらいかもしれないですが、ディレイにフィードバックが加わりました。
Pbindで鳴らしてやるほうがわかりやすいかもしれないです。
(
Pbind(
\instrument, \feedbackTest,
\midinote, Pseq([48, 60, 72, 60], inf),
\dur, 0.5,
\sustain, 0.45,
).play;
)
(今日のコードは全て同じ Pbindで鳴りますので、シーケンスさせながら各SynthDefを実行していくと差もわかりやすいし、エディットしながら聴き比べることもできます。)
上記コードのindexの部分でフィードバックの深さを変えることができますが、1以上にするとLocalOutと同じ音量の信号がどんどん足されていくことになるので(sig + CombNしてるので)どんどん音量が増していきます。
今はEnv.percで減衰しているのですぐに音は消えていきますが、adsrエンベロープに変えて実験する場合は長く再生していると爆音になることもあると思うので気をつけてくださいね。
*LocalOut/LocalInでフィードバックを作る場合はComNのディレイタイムは0.05s以下に設定することはできません。(コードのCombNの0.15の部分)
詳細は後述します。
SinOscの振幅にフィードバック
さてここで、LocalOut信号を(CombNを使わずに)サイン波のパラメータに直接LocalInしてやるとどうなるだろうかという実験をしてみました。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=1.5, amp=0.25;
env = EnvGen.kr(Env.perc(0.01, 1), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq, 0, 0.5 + fbk) ;
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
SinOscのアンプにフィードバック信号をプラス。いい感じの音色変化をしてくれました。indexのパラメータを変えることや、そもそものSinOscの音量0.5という数値を変えることで、違った音色になります。
ただこれ、indexを2以上にすると音量がグンと上がりますので数値を変えて実験するときはこれも気をつけてください。
LocalOutから出てLocalInに入れて、その信号をまたLocalOutから出して・・・を繰り返すので、パラメータいじりは慎重におこなわないとすぐに爆音が出てしまいます。
ちょっと好奇心でSinOscをLFSawに置き換えてみました。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=1.5, amp=0.25;
env = EnvGen.kr(Env.perc(0.01, 1), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = LFSaw.ar(freq, 0, 0.5 + fbk) ;
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
元のLFSawとは少しキャラの違った音質になりました。これももっといじっていくと面白くなりそうです。
SinOscのphaseにフィードバック
そして、今回発見があったのはこちら。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=3, amp=0.3;
env = EnvGen.kr(Env.perc(0.01, 1), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq, fbk, 0.5) ;
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
試しにphaseのパラメータにLocalInの信号をあててみたのですが、これで音色変化が起きました。
もう少しパラメータをいじって・・・
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=5, amp=0.3;
env = EnvGen.kr(Env.perc(0.01, 1), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq, fbk * SinOsc.ar(880), 0.5) ;
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
そしてさらにモジュレーションを加えたりもできて面白いです。
(下記はエンベロープをasrにして持続音にしたので、command+ピリオドするまで止まりません。)
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=3, amp=0.3;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq, fbk * SinOsc.ar(0.2+SinOsc.ar(4), 0, 1), 0.5) ;
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
phaseは初期位相なので再生開始時だけに参照するパラメータのはず、、最初は「なんでこれをモジュレーションできるの?」と思いましたが、その謎は(おそらく)解けました。
SinOscは、その中身は「サイン波のウェーブテーブルをOscクラスで鳴らしている」とヘルプに書かれていて、
つまり1サイクルのウェーブテーブル再生するたびにその開始位相を決めてるんですね。なので開始位相をモジュレーションできるんです。なのでウェーブテーブル実装ではないFSinOscで同じことをやっても音色変化は起きません。
…ということは自作ウェーブテーブルのOscでも、このLocalInによる位相モジュレーションが可能ということになります。
これは後日また実験してみようと思います。楽しみです。
そして実験を続けましたが、LocalInをSinOscの周波数に足していく実験はなかなかうまくいきませんでした。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=880, amp=0.5;
env = EnvGen.kr(Env.perc(0.01, 0.8), gate, doneAction:2);
fbk = LocalIn.ar(1) * index;
sig = SinOsc.ar(freq+fbk, 0, 0.5);
LocalOut.ar(sig);
Out.ar(0, sig!2 * amp * env);
}).play;
)
もちろん音色変化は起きるんですが、ピッチも一緒に変わってしまうんです。。
まぁfreqにプラスしてるんだから当たり前といえば当たり前なんですが・・・加える周波数が高い場合FMの原理で音色変化のみになりませんかね。。。(ということでindexを880とかにしてみてるんですけど。。)
このあたりはまた色々試してみます。
InFeedback
最後に注意点を書きます。
LocalOut/LocalInはフィードバックループで爆音になるのを防ぐために
LocalOutの信号が出力されてLocalInに入ってくるまで、プログラム上の1ブロックに相当する0.05秒の時間を要するつくりになっているそうです。(ヘルプドキュメントに記述があります)
なので最初のCombNのコードでディレイタイムを0.05以下にすることができないです。(0.05以下の値を入れると音が崩れます。)
ただそれだと困るのでInFeedbackというクラスがあり、これを使えば0.05以下でもOKです。
(それでも0.01以下とか短すぎるディレイタイムは音が崩れました。。次のコードは0.008にしていますが、このコードの場合これが限界でした。ただ、音色作りという点では0.008でじゅうぶん面白い効果が出せます。)
下記はフィードバック過大入力を起こす前にEnv.percで音が止まっている印象があります。これ以上リリースが長いとヤバイかもしれないですが、この音としてはフィードバックのいい効果が出ています。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=1, amp=0.5;
env = EnvGen.kr(Env.perc(0.01, 1), gate, doneAction:2);
fbk = InFeedback.ar(0, 1) * index;
sig = SinOsc.ar(freq, 0, 1);
sig = sig + CombN.ar(fbk, 0.8, 0.008, 1.0);
Out.ar(0, sig!2 * amp * env);
}).play;
)
あともうひとつ、原音sigは出さずに、さらにCombNのディレイタイムを揺らしてみたものです。ここまで来ると、面白い音が作れそうな気がしてきました。
*下記はエンベロープをasrにして持続音にしていますので「コマンド+ピリオド」するまで音は止まりません。
(
SynthDef(\feedbackTest,{
arg freq=220, gate=1;
var sig, env, fbk, index=0.8, amp=0.5;
env = EnvGen.kr(Env.asr(0.01, 1, 0.1), gate, doneAction:2);
fbk = InFeedback.ar(0, 1) * index;
sig = SinOsc.ar(freq, fbk, 0.5);
sig = CombN.ar(sig + fbk, 0.8, SinOsc.ar(0.5).range(0.02,1), 1.0);
Out.ar(0, sig!2 * amp * env);
}).play;
)
まだまだ、実験続けます!
今日はこのへんで。
<目次へ>
https://note.com/sc3/n/nb08177c4c011