私が調べた範囲では汎用的にオシレータシンクを行う方法が見当たりませんでした。ここでは場当たり的な実装をいくつか紹介します。
正確にHard syncできるのはphaseを入力できるgeneratorを使う方法です。次点でPlaybufですが位相リセット時にクリックノイズが乗ります。EnvGenは波系が歪んでいます。LoopBufはBufferのループ回数にばらつきが出ます。
Deterministic generatorでsync
phase
を入力できるgeneratorは周波数を0にして LFSaw
などで位相を回すことでsyncできます。
次の例の slave
は上のどれかを使う必要があります。
{ var freq = MouseY.kr(30, 3000, 1); var ratio = MouseX.kr(1, 100, 1); var master = LFPulse.ar(freq); var syncPhase = LFSaw.ar(freq, 0, pi * ratio); var slave = SinOsc.ar(0, syncPhase % 2pi, 1); var osc = master.range(0, 1) * slave; OffsetOut.ar(0, Pan2.ar(osc, 0, 0.5)) }.play;
EnvGenでsync
EnvGen
もゲートを使ってsyncできます。ゲートについてはヘルプに解説があります。IDEでは EnvGen
にカーソルを合わせてCtrl+Dでヘルプを開けます。
{ var freq = MouseY.kr(30, 3000, 1); var ratio = MouseX.kr(1, 100, 1); var gate = LFPulse.ar(freq, 0, 0.5, 2, -1); var levels = [0] ++ [1, 0.9, 0, -0.9, -1] ++ [0]; var waveSize = levels.size - 3; var times = [0] ++ { 1 / (ratio * freq) / waveSize }.dup(waveSize) ++ [0]; var env = Env(levels, times, \lin, levels.size - 2, 0); var envGen = EnvGen.ar(env, gate); OffsetOut.ar(0, Pan2.ar(envGen, 0, 0.5)) }.play;
levels
と times
の前後に [0]
を加えている理由を説明します。まずは Env
のループで使われる引数の releaseNode
と loopNode
の挙動を確認します。それぞれ4番目と5番目の引数です。以下の例を順に再生してみてください。
Env([1, 0, -0.1, 0], [1, 1, 1], \lin, 2, 0).test(5).plot; // (1) Env([1, 0, -0.1, 0], [1, 1, 1], \lin, 2, 1).test(5).plot; // (2) Env([1, 0, -0.1, 0], [1, 1, 1], \lin, 3, 0).test(5).plot; // (3)
(1)と(2)を再生すると、ループ開始点の値は levels[loopNode + 1]
、ループ終了点の値は levels[releaseNode]
であることがわかります。
(3)を再生するとループしません。 releaseNode
の値に levels.size - 1
を指定するとリリース後のエンベロープが無くなってしまうのでループしないようです。
つまり [0]
を前に加えるのはループ開始点を正しく設定するため、後に加えるのは Env
をループさせるため、ということです。
この方法の元ネタは DemandEnvGen
のヘルプにあった例です。あわせて参照してみてください。
Bufferでsync
Buffer
を用意して PlayBuf
をトリガすることでsyncできます。
~buffer = Buffer.alloc(s, 512).sine1(1.0, true, false, true); { var freq = MouseY.kr(30, 3000, 1); var rate = ~buffer.numFrames / SampleRate.ir * freq * MouseX.kr(1, 32, 1); var trigger = Impulse.ar(freq) - 0.5; var loopBuf = PlayBuf.ar( ~buffer.numChannels, ~buffer.bufnum, rate, trigger, 0, 1 ); var master = LFPulse.ar(freq).range(0, 1); OffsetOut.ar(0, Pan2.ar(master * loopBuf, 0, 0.5)) }.play; // ~buffer.free;
LoopBuf
でもsyncできます。 LoopBuf
の利点はループ開始点を指定できる点です。
~buffer = Buffer.alloc(s, 512).sine1(1.0, true, false, true); { var freq = MouseY.kr(30, 3000, 1); var rate = ~buffer.numFrames / SampleRate.ir * freq * MouseX.kr(1, 32, 1); var gate = LFPulse.ar(freq); var loopBuf = LoopBuf.ar( ~buffer.numChannels, ~buffer.bufnum, rate, gate, 0, 0, ~buffer.numFrames - 1, 4 ); OffsetOut.ar(0, Pan2.ar(loopBuf, 0, 0.5)) }.play; // ~buffer.free;