Pparで複数Pbindを同時再生
SuperColliderで曲の構成を作るとき、僕はPseq/Pparを駆使して入力します。
SuperColliderでの曲の記述方法はたくさんあると思いますが、今回は僕が普段やっている方法を書き記しておきます。参考になれば幸いです。
題材としてこんな曲を作りました。
単純な繰り返しフレーズなので曲と言えるかどうか、という感じですが。。。
構成は以下の画像のような感じで、
4つのパート、という構成です。
(それと、この画像には含まれていませんが、リズムパートもあります。リズムパートは単純な4小節フレーズの繰り返しです。)
画像の青い箇所がフレーズで、緑色の箇所はそのフレーズの繰り返し部分です。
(青い箇所だけSuperColliderに入力すれば、あとはその繰り返し再生でOK!)
・aパート
4小節のフレーズです。これを32小節ずっと繰り返しています。
・aパートM
フレーズはaパートと全く同じです。同じフレーズの音色違い、というトラックです。(モノラル音色にしたので、Mと付けました。)
・cパート
前半は4小説のフレーズの繰り返し、17小節目からの後半も、4小節のフレーズを繰り返しています。
・bass
ベースも、基本的にはcパートと同じく「1小節目からのフレーズ」の繰り返しと「17小節目からのフレーズ」の繰り返し、なんですが、細かい音符を入れてしまったため、後半は全部微妙なフレーズの違いがあるため青色になってます。
コード
ではコードの紹介です。解説は後ほど。
(最初にSuperColliderのサーバをブートしておきます。)
[ex1]
(
// エフェクト
~rvb = Bus.audio(s, 2);
SynthDef(\hall, {
var eff, src;
src = In.ar(~rvb, 2);
eff = NHHall.ar(src, stereo:0.8, modDepth:0.5);
Out.ar(0, eff);
}).add;
~sn_rvb = Bus.audio(s, 2);
SynthDef(\sn_tail, {
var eff, src;
src = In.ar(~sn_rvb, 2);
eff = FreeVerb.ar(src, mix:1, room:0.8);
Out.ar(0, eff);
}).add;
~dly = Bus.audio(s, 2);
SynthDef(\apdly, {
var eff, src;
src = In.ar(~dly, 2);
eff = AllpassN.ar(src, 1.0, [1/5*2, 1/5*3], 3);
Out.ar(0, eff);
}).add;
// 音源
SynthDef(\synth1, {
arg freq=440, t_gate=1;
var sig, sig1, sig2, env1, env2, amp=0.1, send=0.15;
env1 = EnvGen.kr(Env.perc(0.01, 0.25), t_gate, doneAction:2);
env2 = EnvGen.kr(Env.perc(0.005, 0.08), t_gate, doneAction:2);
sig1 = LFSaw.ar(freq*2);
sig1 = RLPF.ar(sig1, 1400, 0.9);
sig1 = Pan2.ar(sig1, 0) * env1;
sig2 = LFPulse.ar(freq * 4, 0, 0.5, 0.1) *env2;
sig = sig1 + sig2;
Out.ar(~rvb, sig * send);
Out.ar(0, sig * amp);
}).add;
SynthDef(\synthM, {
arg freq=440, gate=1;
var sig, env, amp=0.1, send=0.04;
env = EnvGen.kr(Env.asr(0.01, 1.0, 0.1), gate, doneAction:2);
sig = LFPulse.ar(Lag.kr(freq, 0.07));
sig = LPF.ar(sig, 1000) * env;
sig = Pan2.ar(sig, SinOsc.kr(0.2, pi));
Out.ar(~rvb, sig * send);
Out.ar(0, sig * amp);
}).add;
SynthDef(\bass, {
arg freq=440, gate=1;
var sig, env, amp=0.22;
env = EnvGen.kr(Env.adsr(0.05, 2.5, 0.45, 0.1), gate, doneAction:2);
sig = LFPulse.ar(freq/2, 0, [0.45, 0.55]);
sig = RLPF.ar(sig, 1200 * env, 0.7);
// sig = RLPF.ar(sig,1000 * env, 0.7);
Out.ar(0, sig * env * amp);
}).add;
SynthDef(\synth2, {
arg freq=440, gate=1;
var sig, env, mod, amp=0.35, send=0.09;
env = EnvGen.kr(Env.asr(0.08, 1.0, 0.3), gate, doneAction:2);
mod = SinOsc.kr(9).range(0.1, 7);
sig = LFPulse.ar([freq+0.7+ mod, freq-0.2, freq+0.2, freq-0.7+ mod]);
sig = Splay.ar(sig, 1, 0.5) * env;
Out.ar(~dly, sig * send);
Out.ar(0, sig * amp);
}).add;
// リズム音源
SynthDef(\bd, {
arg freq=110, t_gate=1;
var sig, env, amp=0.6;
env = EnvGen.kr(Env.perc(0.001, 0.18, 1, -5.0), t_gate, doneAction:2);
sig = SinOsc.ar(freq*env*1.2, 0, env);
sig = sig + VarSaw.ar(freq*env*1.5, 0, 0.65, env);
sig = Compander.ar(sig, sig, 0.8, 0.1, 0.1).clip(-0.5, 0.5);
sig = Pan2.ar(sig, 0);
Out.ar(0, sig * amp);
}).add;
SynthDef(\hh1, {
arg vel=1, t_gate=1;
var sig, env, amp=0.1;
env = EnvGen.kr(Env.perc(0.01, 0.03, 1, -4.0), t_gate, doneAction:2);
sig = WhiteNoise.ar(env);
sig = RLPF.ar(sig, 9000, 0.1, 0.3);
sig = Pan2.ar(sig, 0.35);
Out.ar(0, sig * amp * vel);
}).add;
SynthDef(\hh2, {
arg vel=1, t_gate=1;
var sig, env, amp=0.15;
env = EnvGen.kr(Env.perc(0.015, 0.07, 1, -4.0), t_gate, doneAction:2);
sig = WhiteNoise.ar(env);
sig = RLPF.ar(sig, 9000, 0.2, 0.2);
sig = Pan2.ar(sig, -0.35);
Out.ar(0, sig * amp * vel);
}).add;
SynthDef(\sn, {
arg freq=220, t_gate=1;
var sig, env, amp=0.28;
env = EnvGen.kr(Env.perc(0.003, 0.07, 1, -3.5), t_gate, doneAction:2);
sig = WhiteNoise.ar(env) + BrownNoise.ar(env);
sig = sig + SinOsc.ar(freq/2, 0, env);
sig = RLPF.ar(sig, 5300, 0.8);
sig = Pan2.ar(sig, 0);
Out.ar(~sn_rvb, sig * 0.15);
Out.ar(0, sig * amp);
}).add;
// シーケンス(音符)
~ap_a = Pbind(
\instrument, \synth1,
\midinote, Pseq([
Pseq([57, 52, 60, 57, 64, 60, 57, 60],2),
Pseq([57, 52, 59, 57, 64, 59, 57, 59],2)
], 1).trace,
\dur, 0.5,
);
~apM_a = Pmono(
\synthM,
\midinote, Pseq([
Pseq([57, 52, 60, 57, 64, 60, 57, 60],2),
Pseq([57, 52, 59, 57, 64, 59, 57, 59],2)
], 1),
\dur, 0.5,
);
~bss_a = Pbind(
\instrument, \bass,
\midinote, Pseq([41, 38, 40, 43], 1),
\dur, Pseq([4, 4, 6, 2], 1),
\sustain, Pkey(\dur)
);
~bss_a2 = Pbind(
\instrument, \bass,
\midinote, Pseq([41, 38, 40, 35, 36], 1),
\dur, Pseq([4, 4, 7, 0.5, 0.5], 1),
\sustain, Pkey(\dur)
);
~bss_b = Pbind(
\instrument, \bass,
\midinote, Pseq([38, 40, 33], 1),
\dur, Pseq([4, 4, 8], 1),
\sustain, Pkey(\dur)
);
~bss_b2 = Pbind(
\instrument, \bass,
\midinote, Pseq([38, 40, 33, 45, 43, 41, 40, 36], 1),
\dur, Pseq([4, 4, 6, 0.5, 0.25, 0.25, 0.5, 0.5], 1),
\sustain, Pkey(\dur)
);
~bss_b3 = Pbind(
\instrument, \bass,
\midinote, Pseq([38, 40, 33, 33, 40, 35, 36], 1),
\dur, Pseq([4, 4, 5.5, 1, 0.5, 0.5, 0.5], 1),
\sustain, Pkey(\dur)
);
~bss_b4 = Pbind(
\instrument, \bass,
\midinote, Pseq([38, 40, 33, 43], 1),
\dur, Pseq([4, 4, 6, 2], 1),
\sustain, Pkey(\dur)
);
~cpart1 = Pseq([
\, [52,57,60], \, \, \, \, \, \,
\, [50,57,59], \, \, \, \, \, \
], 1);
~cpart2 = Pseq([
\, [52,57,60], \, \, \, \, \, \,
\, [50,52,59], \, \, \, \, \, \
], 1);
~cp_a = Pbind(
\instrument, \synth2,
\midinote, Pseq([~cpart1], 1),
\dur, 1.0,
\sustain, 1.0,
);
~cp_b = Pbind(
\instrument, \synth2,
\midinote, Pseq([~cpart2], 1),
\dur, 1.0,
\sustain, 1.0,
);
~rb = Pbind(
\instrument, \bd,
\midinote, Pseq([
40, \, \, \, 36,
40, \, \, 40, \, \
], 2),
\dur, Pseq([
Pseq([1.0], 3), Pseq([0.5], 2),
Pseq([1.0], 2), Pseq([0.5], 4)
], 2)
);
~rh1 = Pbind(
\instrument, \hh1,
\midinote, Pseq([60, 60, \, 60], 4),
\vel, Pseq([0.5, 1, \, 1], 4),
\dur, 1.0
);
~rh2 = Pbind(
\instrument, \hh2,
\midinote, Pseq([\, 60, 60, 60], 4),
\vel, Pseq([\, 1, 0.45, 1], 4),
\dur, Pseq([0.5, 1.5, 0.5, 1.5], 4)
);
~rs = Pbind(
\instrument, \sn,
\midinote, Pseq([\, \, 44, \], 2),
\dur, 2.0
);
// シーケンスプレイ(テンポ150で2回繰り返し)
~s01 = Ppar([~ap_a, ~bss_a, ~cp_a, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s05 = Ppar([~ap_a, ~bss_a, ~cp_a, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s09 = Ppar([~ap_a, ~bss_a, ~cp_a, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s13 = Ppar([~ap_a, ~bss_a2, ~cp_a, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s17 = Ppar([~ap_a, ~bss_b, ~cp_b, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s21 = Ppar([~ap_a, ~bss_b2, ~cp_b, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
~s25 = Ppar([~ap_a, ~bss_b3, ~cp_b, ~apM_a, ~rb, ~rhs1, ~rh2, ~rs],1);
~s29 = Ppar([~ap_a, ~bss_b4, ~cp_b, ~apM_a, ~rb, ~rh1, ~rh2, ~rs],1);
x = Pseq([~s01, ~s05, ~s09, ~s13, ~s17, ~s21, ~s25, ~s29], 2);
x = Pfx(x, \sn_tail);
x = Pfx(x, \apdly);
x = Pfx(x, \hall);
x.play(TempoClock(150/60));
)
サーバをブートしたあとにこのコードを実行すると、先ほどのリンク先のYouTubeの音源と同じ音が鳴ります。
(YouTubeの音は、このコードをs.recordで録音したものです。)
SynthDef
音色のSynthDefを簡単に解説します。
\synth1
LFSawとLFPulseをミックスした音色です。
それぞれに別々のエンベロープをあてています。
ダイレクトアウトの他に~rvbバスにも信号を送っています。
\synthM
LFPulseの音色ですが、前回ポルタメントを覚えたのでLagを使いました。
SinOsc.kr(0.2, pi)で、ゆっくりとパンニングさせています。中央から始まって左に寄っていきます。次に右まで寄ったらまた中央に戻り、これを繰り返します。piのところを0にすると、中央から始まってまず右に寄っていきます。
ダイレクトアウトの他に~rvbバスにも信号を送っています。
\bass
LFPulseのパルス幅を[0.45, 0.55]というふうに左右で少し変えています。チューンではない方法で左右の広がりが欲しい時に。
\synth2
4つデチューンさせて、さらにSinOscでビブラートをかけています。
ダイレクトアウトの他に~dlyバスにも信号を送っています。
\bd
SinOscとVarSawを混ぜて作りました。
freqを使っているのでPbindでプレイする音程で、音色が少し変わってきます。
t_gateという書き方については深く触れたことがなかったと思うので、(ちょっと長くなりそうなので)次の記事で書くことにします。
\hh1と\hh2
ampにvelを掛け算することで、velをベロシティー(0〜1)に見立てています。
\sn
WhiteNoiseとBrownNoiseを混ぜて作りました。
SinOscは少し低音を足したくて追加しました。
ダイレクトアウトの他に~sn_rvbバスにも信号を送っています。
Pbind
そして、これらを鳴らすPbindのコードですが・・・
この曲をSuperColliderに入力する方法としてはふたつあると僕は考えています。
1。各トラックのPbindを作って、それを全トラック同時に再生する。
2。複数トラック同時再生できるPbindを、ある程度まとまった小節ごと(例えば4小節分とか)に作って、それをPseqで繋げて再生する。
曲によっては前者でもOKと思ったりするのですが、曲構成が複雑になるほど、僕は後者の方が断然いいと思います。(その理由は後述。)
[ex1]も後者の方法です。
解説順として、前者からの方がやりやすそうなので、まずは前者の方法から紹介します。
1。各トラックのPbindを作って、それを全トラック同時に再生する
先ほどの図のa / a(M) / c / bassの4トラックを、トラックごとにPbindを作って、それらを全部同時に再生しよう、というのがその方法です。
先ほどの[ex1]の
// シーケンス(音符)
から下の書き方が変わってきます。
こちらです。
[ex2]
// シーケンス
(
// aパート(ずっと8分で鳴ってるやつ)
~apart = Pbind(
\instrument, \synth1,
\midinote, Pseq([
Pseq([57, 52, 60, 57, 64, 60, 57, 60],2),
Pseq([57, 52, 59, 57, 64, 59, 57, 59],2)
], 8),
\dur, 0.5,
);
// aパートと全く同じものをユニゾン
~apart_M = Pmono(
\synthM,
\midinote, Pseq([
Pseq([57, 52, 60, 57, 64, 60, 57, 60],2),
Pseq([57, 52, 59, 57, 64, 59, 57, 59],2)
], 8),
\dur, 0.5,
);
// bassパート、ベースになっているフレーズを入力(とりあえず前後半どちらも4小節の繰り返しで入力しておいた)
~bass = Pbind(
\instrument, \bass,
\midinote, Pseq([
Pseq([41, 38, 40, 43], 4),
Pseq([38, 40, 33], 4)
], 1),
\dur, Pseq([
Pseq([4, 4, 6, 2], 4),
Pseq([4, 4, 8], 4)
], 1),
\sustain, Pkey(\dur)
);
// cパートの和音、休符が多いので、見やすいように~cpart1と~cpart2という箱の中にフレーズを入れた。
~cpart1 = Pseq([
\, [52,57,60], \, \, \, \, \, \,
\, [50,57,59], \, \, \, \, \, \
], 4);
~cpart2 = Pseq([
\, [52,57,60], \, \, \, \, \, \,
\, [50,52,59], \, \, \, \, \, \
], 4);
~cpart = Pbind(
\instrument, \synth2,
\midinote, Pseq([~cpart1, ~cpart2], 1),
\dur, 1.0,
\sustain, 1.0,
);
// ここから下はリズムパート、バスドラム、ハイハット1と2、スネア。
~rb = Pbind(
\instrument, \bd,
\midinote, Pseq([
40, \, \, \, 36,
40, \, \, 40, \, \
], 16),
\dur, Pseq([
Pseq([1.0], 3), Pseq([0.5], 2),
Pseq([1.0], 2), Pseq([0.5], 4)
], 16)
);
~rh1 = Pbind(
\instrument, \hh1,
\midinote, Pseq([60, 60, \, 60], 32),
\vel, Pseq([0.5, 1, \, 1], 32),
\dur, 1.0
);
~rh2 = Pbind(
\instrument, \hh2,
\midinote, Pseq([\, 60, 60, 60], 32),
\vel, Pseq([\, 1, 0.45, 1], 32),
\dur, Pseq([0.5, 1.5, 0.5, 1.5], 32)
);
~rs = Pbind(
\instrument, \sn,
\midinote, Pseq([\, \, 44, \], 16),
\dur, 2.0
);
// 全パートのシーケンスを(Pparで)並列にならべて、エフェクトをつけてプレイ!
a = Ppar([~apart, ~bass, ~cpart, ~apart_M, ~rb, ~rh1, ~rh2, ~rs],2);
a = Pfx(a, \sn_tail);
a = Pfx(a, \apdly);
a = Pfx(a, \hall);
a.play(TempoClock(150/60));
)
各トラックのPbindを、
~apart
~apart_M
~bass
~cpart
に入れています。
各Pbindとも、32小節分ずつになってます。
図には入ってないリズムトラックも鳴らしたいのでバスドラ、ハイハット1、ハイハット2、スネア、のPbindたちも下記の変数にそれぞれ入れてます。
~rb
~rh1
~rh2
~rs
あとは、これをPparで並べて再生するだけです。
Pparは
Ppar([並列に鳴らしたいPbindをカンマ(,)で区切って入力], 繰り返す回数)
という書き方です。
Ppar([並列に鳴らしたいPbindをカンマ(,)で区切って入力], 繰り返す回数).play;
と書くことで、複数のPbindを同時に再生できます。
ぶっちゃけ、下記のようにすればPbindの同時再生を実現出来ますが・・・
(
~apart.play(TempoClock(150/60));
~cpart.play(TempoClock(150/60));
~bass.play(TempoClock(150/60));
)
今回はPfxも使いたかったので、Pparを使ってます。
さて、[ex2]を実行することで32小節ひとまわし分を(2回繰り返して)聴くことができました。
ですがまだ~bassが単純なループになっていて、~bassのトラックに関しては図の青色の部分を全て入力できていません。
この、「1。各トラックのPbindを作って、それを全トラック同時に再生する」の方法の弱いところは、例えば曲の後半部分をエディットしたとき、その確認をするには曲の最初から聞かないといけないというところです。
これ、32小節ならまだガマンできても、もっと長い曲だとなかなかのストレスです。
そういうわけで、この曲のこの方法での入力は、僕はここで断念しました。。
ただ、ループさせながら少しずつフレーズや音色を変化させていくような音楽を作る場合はこの方法も有効だと思います。
2。複数トラック同時再生できるPbindを、ある程度まとまった小節ごと(例えば4小節分とか)に作って、それをPseqで繋げて再生する。
さてそこで次の方法の出番です。
まず図のように、4小節という短い単位でPparを組みます。(このPparをプレイすると、4つのパートとリズムパートが4小節分鳴る。)8個のPparを作っておいて、そして、それらをPseqで繋げてプレイすると1曲になる、という方法です。
この方法だと、例えば◯◯小節目以降をプレイ確認したい場合、下記のようにすればいいので、途中からのプレイ確認もストレス無く行なうことができます。
例えばこんな感じに。
[ex3]
(
x = Pseq([~s25, ~s29], 1);
x = Pfx(x, \sn_tail);
x = Pfx(x, \apdly);
x = Pfx(x, \hall);
x.play(TempoClock(150/60));
)
この方法だと、曲の後ろの方の確認もすぐにでき、ストレスがありません。こうやって出来上がったのが[ex1]です。
簡単に言えば、
方法「1。」は、「Pseqで繋げたものを、Pparで同時再生する」
方法「2。」は、「Pparで短い小節を同時再生できるようにしたものをPseqで繋げる」
という、順番の違いです。
SuperColliderの醍醐味は、Pwhiteなどのランダム性のあるクラスを使ったりして予想外の曲展開を楽しむところ(それも少ない行のコードで!)にあるとは思うのですが、僕はけっこうフレーズをきっちり入力したい人なので、こんなふうにPseqやPparを使ってひとつひとつ音符を入力します。
cパートの休符の並びとかを見ると、もっと効率的な入力方法あるだろー!と思いますが、、こんな使い方してる人もいます、ってことで、この記事が何かのヒントになれば嬉しいです。