はじめに
ピンポンディレイとは、音が一度鳴ったあとのディレイ音が左右のpanに交互に振れていくようなディレイのことです。
以前TidalCyclesのstut'を用いてピンポンディレイを作った記事を書いたので、そちらもよければご確認ください。
最初に、
- 通常のディレイ
- TidalCyclesのエフェクト(stut')を用いたピンポンディレイ
- SuperDirtで自作したピンポンディレイ
それぞれの音を聞いていただければと思います。
stut'ピンポンディレイの課題
CPU使用率が高い
さきほどの動画で、SuperCollider右下の緑文字の値を見ていただくと(一番左の平均CPU使用率)、通常2%台であるのに対してstut'でのピンポンディレイのときだけ稀に3%台になっています。
通常のディレイが、一度遅延した音を再度フィードバックさせて遅延音を重ねているというシンプルな機構になっているのに対し、stut'は全ての遅延音を再生させているということもありその分CPUに負荷がかかります。
今回のようなそこまで再生するものが多くないときは問題ないですが、ディレイをかける対象が増えたりすると処理落ちしたりします。
意図通り動作しないことがある
例えば、TidalCyclesで以下のコードを実行してみてください。
do
resetCycles
d1
$ seqP [
(1, 3, (
((# delay 0.5).(# delayt (1/8)).(# delayfb 0.7).(# lock 1))
$ s "notes:2 [~ notes:2]"
# gain 0.6
))
]
seqPを用いて、1サイクル目から3サイクル目の手前(つまり2サイクル目)まで再生しています。当然ディレイがかかっているので3サイクル目以降も音がしばらく残ります。
(ちなみにresetCyclesは0サイクル目までリセットする関数ですが、遅延があって使いづらいのでこちらのresetCyclesTo関数を使ったほうが良いです。)
続いてこちらのコードを実行してみてください。
do
resetCycles
d1
$ seqP [
(1, 3, (
stut' 2 (1/8) ((|* gain 0.9).(|> pan (1)))
$ stut' 8 (1/4) ((|* gain 0.8).(|> pan (0)))
$ s "notes:2 [~ notes:2]"
# gain 0.6
))
]
同様に1,2サイクル目のときのみ再生させてますが。こちらは2サイクル目再生が終わると再生がストップし、3サイクル以降は音が残りません。
前述したように、stut'は全ての遅延音を再生させているだけなので、サイクル指定した場合その範囲外の音は再生されない、ということになっているようです。
使い方によってはこれが良いときもあると思いますが、指定していないサイクル以降も音が残って欲しい場面も多いはずです。
globalEffectsを定義する
そこで、通常のdelayエフェクトと同様な使い勝手のピンポンディレイを作ると良さそうです。
通常のdelayはSuperDirtのsynths/core-synths-global.scdで定義されています。
(ちなみにパラメータはdelaytime, delayfeedback, delaySend, delayAmp, lock
などでdelay
パラメータが存在しませんが、delayAmpがdelayパラメータにあたります。ここで定義されています。)
また、globalEffectsの追加方法はhacks/adding-a-compressor.scdあたりを参考にすれば出来そうです。
このコードたちを参考にピンポンディレイを自作してみましょう。
コード
SuperDirt.start;
したあとに以下のコードをSuperCollider 上で実行すれば、ピンポンディレイが定義されるはずです。(※ちなみに、自分の好みとしてlock=1でしたdelayコマンドを使うことがないので、lock=1の前提でlockはパラメータに含めてません。lockを有効化したい方はご自身で試してみてください!)
// ここの値は https://github.com/musikinformatik/SuperDirt/blob/develop/classes/DirtOrbit.sc#L59-L66 を参考にした
(
~dirt.orbits.do { |x|
x.globalEffects = [
GlobalDirtEffect(\dirt_delay, [\delaytime, \delayfeedback, \delaySend, \delayAmp, \lock, \cps]),
GlobalDirtEffect(\pingpong_delay, [\pingpongt, \pingpongfb, \pingpong, \cps]), // 追加
GlobalDirtEffect(\dirt_reverb, [\size, \room, \dry]),
GlobalDirtEffect(\dirt_leslie, [\leslie, \lrate, \lsize]),
GlobalDirtEffect(\dirt_rms, [\rmsReplyRate, \rmsPeakLag]).alwaysRun_(true),
GlobalDirtEffect(\dirt_monitor, [\limitertype]).alwaysRun_(true)
]
};
SynthDef("pingpong_delay" ++ ~dirt.numChannels, { |dryBus, effectBus, gate = 1, pingpongt, pingpongfb, pingpong, cps = 1|
var signal = In.ar(dryBus, ~dirt.numChannels);
var maxDelayTime = 4;
var delaytime, delayfeedback, delayAmp, decayTime, left, right, dry;
delayAmp = pingpong;
delaytime = pingpongt;
delayfeedback = pingpongfb;
delayfeedback = delayfeedback.clip(0, 0.99);
delaytime = delaytime * reciprocal(cps);
delaytime = delaytime.clip(0, maxDelayTime); // just to be sure
decayTime = log2(-60.dbamp) / log2(delayfeedback) * delaytime;
decayTime = decayTime.clip(0, 20);
// ピンポンディレイのロジック部分
dry = signal;
right = CombL.ar(dry, maxDelayTime*2, delaytime.lag(0.01)*2, decayTime);
left = DelayN.ar(dry + right, maxDelayTime, delaytime.lag(0.01));
signal = NumChannels.ar(signal + [left*delayAmp, right*delayAmp], ~dirt.numChannels);
signal = signal * EnvGen.kr(Env.asr, gate, doneAction:2);
DirtPause.ar(signal, graceTime:4);
Out.ar(effectBus, signal);
}, [\ir, \ir]).add;
// 再生されているシンセを解放する(これによってglobalEffectsの追加・変更が適用される)
Server.default.freeAll;
)
あとは、BootTidal.hsに下記を追記して、各パラメータをTidalCyclesでも使えるようにしてください。
:{
pingpong = pF "pingpong"
pingpongt = pF "pingpongt"
pingpongfb = pF "pingpongfb"
:}
(BootTidal.hsを直接いじるのが好みでない方は、TidalCyclesのプロジェクトディレクトリ直下にBootTidal.hsをコピペしてから編集したり、.tidalファイルから直接実行してみたりしてください。BootTidalについては以下の記事も参考にしてみてください)
以上でピンポンディレイが定義されたので、試しに.tidalファイルで以下を実行してみましょう。
setcps (120/60/4)
d1
$ ((# pingpong 0.7).(# pingpongt (3/16)).(# pingpongfb 0.7))
$ slow 2
$ s "notes:2 [~ ~ ~ [~ notes:2]] notes:2 ~" # up "0 [0 0 0 -4] -2 0"
# pan 0.5
# gain 1.0
無事最初の動画のような音が再生されたかと思います。