10.SuperColliderのパンニング・ステレオ(マルチチャンネル)
SuperColliderはプログラミング言語であり、めちゃめちゃフレキシブルに音作りできるツール(ソフトシンセ)です。
ただテキストベースなのでちょっとハードルが高そうに見えます。そこでプログラミングに接したことのない人でも「SuperCollider入門」として読んでいけるような記事を目指しています。扱い方さえわかればシンプルなんです。そして奥深さも味わえます。
途中途中で用語等を挟んでいますので、ぜひ(目次)に戻って[01.基本的なこと色々]から読んでみてほしいです。
今回はパンニング・ステレオについてです。
SuperColliderが生成する波形は基本的にはモノラルです(ステレオ素材のサンプル波形を読み込んで使う場合は別です)が、それをステレオに広げる方法について書きます。
Pan2クラス
これまでは!2を使ってデュプリケートすることで左チャンネルの信号を右チャンネルにも複製して、左右の真ん中から聞こえるようにしてきました。
Pan2クラスを使うと、そのまま真ん中に定位可能ですし、左右どちらにも、また「ちょっと左」などの位置にも定位させることができます。
まずは前回使ったSynthDefから!2を抜いたコードが下記です。
また、サーバにaddせずplayメソッドを使っていますので、実行すると(シーケンスせずとも)この音がそのまま鳴ります。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
sig = LFPulse.ar(freq);
Out.ar(0, sig * env * 0.3)
}).play;
)
音を止めるには、いつも通りコマンド&ピリオドです。
次にPan2クラスを使ってこの音を中央に定位させます。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, 0);
Out.ar(0, sig * env * 0.3)
}).play;
)
varのところに新たにoscという箱(入れ物)を作って、LFPulse.ar(freq)を入れました。
そしてそのoscをPan2にあてがってsigという箱に入れてます。
※「=という記号は数学でいうイコールとは違い、"=の右のものを、=の左にある箱に入れる"という意味」ということをぜひ思い出してください。
※箱の名前oscillatorの略としてoscとしましたが、小文字で始まっていれば箱の名前は何でもよいです。ちなみにsigはsignalの略です。
Pan2.arは("どの信号を", "どの位置に")というパラメータを持っていますので、"どの信号を"のところにoscと設定しました。
oscの中身はLFPulse.ar(freq)です。
"どの位置に"のパラメータは次のように指定できます。
-1で左
0で中央
1で右
です。
なので”-0.5”などとすると、”中央から少し左寄り”に定位させることができます。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, -0.5);
Out.ar(0, sig * env * 0.3)
}).play;
)
Pan2をcommand+dで調べると、"どの位置に"パラメータはpos(positionの略)と書かれてありますので、ここではposと呼ぶことにします。
posパラメータの範囲は-1〜1なので、振幅-1〜1のサイン派などでモジュレートすることも可能です。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, SinOsc.kr(2));
Out.ar(0, sig * env * 0.3)
}).play;
)
モジュレーションについて詳しく書く回を持ちたいと思ってますので、詳しい情報はそのときに。ここではそんなこともできるのか、という感じでコードを実行してみていただけると嬉しいです。「なるほど」と思っていただいた方は、いろいろと実験をしてみてはいかがでしょうか。
下記コードではパルス波でモジュレートしています。周期も少し速く10にしてみました。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, Pulse.kr(10));
Out.ar(0, sig * env * 0.3)
}).play;
)
今、実験でやってみたのですが、このSynthDefをaddして前回のPbindでシーケンスさせるとなかなか面白い感じになりました。
※SynthDefとPbindを同時に(カッコ)で囲んでいるので、(カッコ)内を一度だけcommand+enterすることでSynthDef().addとPbind().playが同時に実行されます。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, Pulse.kr(10));
Out.ar(0, sig * env * 0.3)
}).add;
Pbind(
\instrument, \synth1,
\midinote, Pseq([
56, 67, 70, 72, 44, 70, 75, 77,
79, 67, 70, 72, 84, 70, 77, 75
], inf),
\sustain, Pseq([
0.5, 0.2, 0.5, 0.7, 0.5, 0.2, 0.5, 0.5,
0.5, 0.2, 0.5, 0.2, 2.0, 0.2, 0.5, 0.5
], inf),
\dur, 0.5,
).play(TempoClock.new(110/60));
)
マルチチャンネル
さて、モノラル音源をパンニングさせるのではなくLRで違う音を鳴らしたい場合もあります。LRのピッチを少しだけ変えてコーラス効果を持たせるやり方は常套手段ですが、そういった方法について書きます。
「01.基本的なこと色々」のときのコードを使います。
{ SinOsc.ar(220, 0, 0.3) }.play;
これだと鳴るのは左チャンネルだけです。
下記のようにすると左右チャンネルから鳴ります。
{ SinOsc.ar([220, 220], 0, 0.3) }.play;
[角カッコ]内にカンマで区切ってパラメータ記入すると「隣のチャンネルにパラメータを与えた」ことになるんです。
s.meter;
またはcommand+mでメーターを表示して実行するとわかりますが、
{ SinOsc.ar(220, 0, 0.3) }.play;
の実行だと、出力先はoutput0のみです。
{ SinOsc.ar([220, 220], 0, 0.3) }.play;
の実行だと、output0と1から出力されています。
つまり隣のチャンネルにも220という値を与えたことになります。
そして、隣のチャンネルにパラメータを与えられるということは・・・
{ SinOsc.ar([220, 222], 0, 0.3) }.play;
このように別の数値を入れてやることもできます(ピッチ220の隣のチャンネルにピッチ222を与えました)。そうすると、デチューンされたサイン波が左右いっぱいの音場に鳴ります。
このとき、0や0.3といったパラメータはそのまま隣のチャンネルに採用されます。
ですが「こんなに左右に広げたくない」っていう時もありますね。
そういう場合はSplayクラスで調整できます。
Splay.arは、(2ch以上の信号, 広がり具合(0〜1))というパラメータを持っています。
なので
{ Splay.ar(SinOsc.ar([220, 222], 0, 0.3), 0.5) }.play;
このように書き、上記の0.5というところを変えることで左右の広がり具合を調節できます。
0がセンター、1が広がりマックスです。
ところでひとつミニ知識を。
このコード、SynthDefで書くと
SynthDef(\name, {Out.ar(0, SinOsc.ar([220, 222], 0, 0.3))}).play;
というようにシンセの名前と出力先を指定してやることができました。
そこで、下記のように書くとどうなるでしょう。
Out.arの出力先を0から1に変更しています。
SynthDef(\name, {Out.ar(1, SinOsc.ar([220, 222], 0, 0.3))}).play;
先ほどは「隣のチャンネルにパラメータを与えられる」と書きましたが、正確に言うと「出力チャンネルナンバーが1個大きいチャンネルにパラメータを与えられる」なんです。(まぁ、隣です。右隣。)
なので上記コードだと
220Hzの信号がoutput1から
222Hzの信号がoutput2から
出力されることになります。
ですがoutput2ってどこでしょう?
答え(1)
多チャンネルのオーディオインターフェイスを使っている方は出音を確認できます。output2は3つ目のアウトプットです。
SuperColliderをマルチアウト化するには下記のコードを実行します。
s.options.numOutputBusChannels = 8;
(上記は8パラアウトのオーディオインターフェイスの場合です。)
そのうえで、先ほどのコード(Out.arでoutputを1に指定したコード)を実行してみてください。
(もし反映されなかったらServerメニューからReboot Serverを選択してください。)
答え(2)
MACやPCの音声出力や2chのオーディオインターフェイスを使っている方、
output2は仮想アウト(仮想バス)です。
ソフトウェア内の仮想アウトに出力されています。
出力されているかどうかはscopeで確認できます。
s.scope(4);
これを実行すると4chのスコープウィンドウが現れます。
そして先ほどのこのコード
SynthDef(\name, {Out.ar(1, SinOsc.ar([220, 222], 0, 0.3))}).play;
を実行すると、2番目と3番目にサイン波が流れるのがわかります。
若干ピッチが違っているのも、わかりますでしょうか。わかりづらい場合はどちらかの周波数を660Hzとかに変更して実行すると見た目ではっきりと確認できます。
さて、ミニ知識のコーナーが長くなって申し訳ないですが、SynthDefの出力先を0に戻して、ピッチの[角カッコ]内の数値を3つにしたらどんなふうに出力されるでしょうか。
SynthDef(\name, {Out.ar(0, SinOsc.ar([220, 222, 660], 0, 0.3))}).play;
output0から220Hzのサイン波
output1から222Hzのサイン波
output2から660Hzのサイン波
通常は
output0がLチャンネル
output1がRチャンネル
という環境の人がほとんどだと思うので、マルチチャンネルのオーディオインターフェイスを持っていなければ3つ目の660Hzのサイン波は聞こえないです。でも確かに仮想outputで鳴っています。
こういったoutput2以降に送った信号は、例えばエフェクトをかけたあとoutput0に戻すようなことも可能です。
DAWやミキサーなどで言うBUSやAUXとしての使い方ができるんです。
そういったことを書く回も後々設けたいと考えています。
※1024の仮想オーディオBUSが使えます。
※マルチチャンネルアウトのオーディオインターフェイスを使っている方、例えば8パラアウトのI/Oの方は9チャンネル目以降が仮想オーディオBUSです。
さて。
ちょっと別方向への話の展開になってしまうのですが、上記のように[220, 222, 660]のサイン波は、Mixクラスを使うと全部の周波数のサイン波を聴くことが可能です。ただしMixというクラス名のとおり、3つのサイン波がモノラルミックスされた状態です。
SynthDef(\name, {Out.ar(0, Mix.new(SinOsc.ar([220, 222, 660], 0, 0.1)))}).play;
Mixクラスの使い方は
Mix.new(ミックスしたい信号)
という感じで簡単です。
中に入れた信号全部をmonoミックスします。
中に入れる信号の数に制限はありませんが気をつけたいのは音量です。Mixクラスは中に入れたものを単純に足し算するので、0.3のボリュームのサイン波3個だと0.3+0.3+0.3で0.9、もうすぐ1.0を超えてしまいそう。
1.0を超えると音は歪みますし、耳やスピーカーを炒める恐れがあります。
多数の信号をミックスする実験を行なう場合は音量が1.0を超えないよう(耳を痛めないよう)じゅうぶんに気をつけてください。
さてmonoミックスしてしまうので上記のコードだと全部左チャンネルに寄ってしまいましたね。Mix.new()!2というかたちにします。さらに.newは省略できるので・・・
SynthDef(\name, {Out.ar(0, Mix(SinOsc.ar([220, 222, 660], 0, 0.1))!2)}).play;
見事に加算合成されたサイン波が両チャンネルで鳴りました。
!2でなくともPan2.ar(Mix(), 0)という形でもいいですね。
SynthDef(\name, {Out.ar(0, Pan2.ar(Mix(SinOsc.ar([220, 222, 660], 0, 0.1)), 0))}).play;
だいぶコードが長くなってきたのでホントは変数を使って見やすく書いたほうがいいのですが、、勢いでここまでやってしまいました。
SC3のコードは、こんな感じでOutの中にPan2、その中にMix、またその中にSinOscというふうに、入れ子になって長くなっていくことが多いと思います。
なので変数をうまく使って見やすく書くことをオススメします。
一応、整理した形のコードを書いておきます。
(
SynthDef(\name, {
var sig;
sig = SinOsc.ar([220, 222, 660], 0, 0.1);
sig = Mix(sig);
sig = Pan2.ar(sig, 0);
Out.ar(0, sig)
}).play;
)
コードの整理の方法は人それぞれなので、これが正解というのはありません。
でも上記の書き方だと意味がわからない人がいるかもしれないので解説しておきます。
僕も最初はこの書き方を理解するのに時間かかったので。。
では解説しやすいように行番号をコメントします。
コードの内容は変わっていません。
(
SynthDef(\name, {//1
var sig;//2
sig = SinOsc.ar([220, 222, 660], 0, 0.1);//3
sig = Mix(sig);//4
sig = Pan2.ar(sig, 0);//5
Out.ar(0, sig)//6
}).play;//7
)
varでsigという箱を作りました。(プログラミング言語的な言い方だと「sigという変数を宣言しました」)
変数(sigという箱)の利用ルールをふたつ、まず念頭に置いておきます。
・=の右側にあるものを、=の左側にある変数(sigという箱)に入れる
・変数(sigという箱)に入れられるのはひとつだけ
では解説です。
まず2行目でsigという空箱を作りました。
3行目で「SinOsc.ar([220, 222, 660], 0, 0.1)」を空箱sigに入れました。
4行目。
まずは=の右側だけを見ます。
Mix(sig)のsigに今入っているのはSinOsc.ar([220, 222, 660], 0, 0.1)です。
その状態のものを=の左側のsigに入れます。
sigの中にはひとつしかモノが入らない(新しく何かモノを入れると、それまで入っていたものを捨てて入れる、つまり上書きされる)ので、それまで何が入っていようと、、、
4行目の状態はMix(SinOsc.ar([220, 222, 660], 0, 0.1))をsigに入れた状態。
その状態のsigを・・・
5行目でさらに使います。Pan2.ar(sig, 0)に入れてます。
ということは5行目ではPan2.ar(Mix(SinOsc.ar([220, 222, 660], 0, 0.1)), 0)がsigに入っている状態。
6行目でそのsigをOutしています。
以上、解説でした。
(図説・・・解りづらかったでしょうか。。どうでしょう。)
このsig(変数)を使い回すコードの書き方で便利なのは、単純に変数が1個で済むということと、エディタ上で例えば4行目だけミュートとか5行目だけミュートなどをサッとできること、です。
ミュートはコメントアウトで行います。
コードの4行目にカーソルがある状態でcommand+/(コマンド&スラッシュ)するとその行全部がコメントアウトされます。もう一度command+/するとミュート解除です。
ミュートして実行、聴き比べがすぐにできるのでとても便利です。
なので本当は今回の冒頭にてPan2を紹介したときのコード
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env, osc;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
osc = LFPulse.ar(freq);
sig = Pan2.ar(osc, 0);
Out.ar(0, sig * env * 0.3)
}).play;
)
これもoscを使わずにsigとenvだけの変数で書きたかったのですが
まだ解説していなかったのでoscという変数(箱)を使いました。
こっちのほうがパッと見た目にはわかりやすいと思いましたので。
だけどsig = Pan2.ar(osc, 0);の行をミュートして試したくなったり、Pan2を他のクラスに入れ替えてためしたくなったりしたときに、それ以外の行にもエディットを入れる必要が出てきたりしますので、(実際にググってみるとSC3のコードはこのような変数を上書きしていく書き方が多いということもありますし)やっぱり下記のような方法に慣れていくのがよいのではないかなと思います。
(
SynthDef(\synth1, {
arg freq=440, gate=1;
var sig, env;
env = EnvGen.kr(Env.adsr(0.01,0.6,0.5,0.2), gate, doneAction:2);
sig = LFPulse.ar(freq);
sig = Pan2.ar(sig, 0);
Out.ar(0, sig * env * 0.3)
}).play;
)
さて。今日は[角カッコ]内にカンマで区切ってパラメータ記入すると「隣のチャンネルにパラメータを与えられる」ということで、左右で異なるピッチのサイン波を作りましたが、[角カッコ]に入れるのはピッチだけでなく、何でもよいです。
{ SinOsc.ar(220, 0, [0.3, 0.3]) }.play;
このようにも書くことができますし(左右とも0.3にしてますが、0.3と0.2とか左右で音量差をつけるなども全然OK)、フィルターのカットオフやLFPulseのパルス幅とか、どのパラメータでも左右チャンネルで差をつけて設定できます。
また、下記のように生成する音自体を左右に分けるのもOK。
{ [SinOsc.ar(440, 0, 0.3), Saw.ar(220, 0.3)] }.play;
Mixクラスでモノラルミックスも可能です。
{ Mix([SinOsc.ar(440, 0, 0.3), Saw.ar(220, 0.3)])!2 }.play;
ただ、これは下記と全く同じ意味です。
{ SinOsc.ar(440, 0, 0.3) + Saw.ar(220, 0.3)!2 }.play;
このように波形同士を足したり、または掛け合わせたり、そういうこともできます。これについてもモジュレーションの回にて詳細を。
************ 2022.04.24追記 ************
上記コードを修正しました。
修正前は下記のコードでした。
{ Mix( SinOsc.ar(440, 0, 0.3) + Saw.ar(220, 0.3) )!2 }.play;
MixはMix([ , ])の[角カッコ]内に列挙したものをミックスしてくれるものなので、Mixの中で足し算するのはいい例ではなかったです。すみません。
上記の修正されたコードは
SinOsc.arはモノラル、
Saw.arはモノラルをデュプリケートした2ch信号、
の足し算ですが、1chの信号と2chの信号を足し算した場合は1chの方の信号(この場合SinOsc.ar)が自動的にデュプリケートされて2chの信号として出力されます。
************* 追記ここまで *************
では今回はこんなところで終わります。
次回は、あらかじめレコーディングされたwav波形をSuperColliderで鳴らすということについて書きたいと思います。
今日のまとめ
・Pan2クラスでモノラル信号をステレオ音場のどこにでも定位させることができる
・定位位置はモジュレーション可能
・[角カッコ]内にカンマで区切ってパラメータ記入すると隣のチャンネルにパラメータを与えることができる
・Splayクラスで左右の広がりを調整できる
・s.meterでレベルメーターウィンドウ表示
・s.scopeでスコープウィンドウ表示
・Mixクラスで複数の信号をモノラルミックスできる
・特定の行をコメントアウトするときのショートカットは、
command+/(コマンド&スラッシュ)
<目次>にも今回のリンクを作っておきます。https://note.com/sc3/n/nb08177c4c01
この記事が気に入ったらサポートをしてみませんか?