tn8's SuperCollider site
tdfc

Audio Tutorial 3

3. External Control


Plugユニット・ジェネレータクラスでは、 エクスターナル・コントロール・ソースや、グラフィカルなスライダーやパラメータをコントロールする ためのプログラマチック処理から受け取る値をあなたのパッチに使用することができます。 簡単にいえば、例えばマウスのX座標値をSinオシレータの周波数にしたりすることができます。

Plugオブジェクトは入力された値を自動的にスムーズにしてくれます。 ソースの値が更新された時に引き起こされる人工的な値を除去するためのフィルタのようなものです。

ExternalControlSourceの詳しい使い方については PlugとExternalControlSourceのヘルプファイルを参照して下さい。ExternalControlSourceのヘルプファイルは Help>Other Classes>ExternalControl にあります。
ExternalControlSourceでは、マウスの座標の他MIDIやWacomのグラフィックタブレットといったものを 扱うことができます。

■3.1 マウスによるサウンドコントロールの方法

MouseXとMouseYクラスでは、マウスからの入力を利用することができます。
MouseXとMouseYクラスはclass ExternalControlSourceから継承します(間接的にマウスを通して)。
MouseXとMouseYクラスを使用するためには、Plugユニット・ジェネレータのなかにMouseXクラスやMouseYクラスをつくらなければなりません。

Plug.arは2つのアーギュメントをもちます。 1つめはコントロールソースで、下記の例ではMouseXオブジェクトにあたります。 2つめのアーギュメントは、マウスの値の出力をスムーズにするためのラグタイムです。下記の例ではデフォルトである0.1秒に設定しています。これはマウスが作動するのにちょうど良い時間なので普通はこのアーギュメントには数字を与えずデフォルト値を使用します。

MouseX.newは3つのアーギュメントをもちます。 最初の2つのアーギュメントではマウス出力の最小値と最大値を設定します、これらの値はそれぞれ、コンピュータ・スクリーンの左端と右端にマップされます。3つめのアーギュメントは「シンボル」で、マッピングの方法を指定することができ、'linear' か 'exponential'のどちらかを指定します。'linear'と'exponential'については後で述べます。

( //マウスのX座標値で周波数をコントロールします

{ SinOsc.ar( // サインオシレータをつくる。

Plug.kr( // コントロールをUGenのなかにつくる

MouseX.new( // コントロールソースを使用するために
//MouseXオブジェクトをつくります
200, // コンピュータのスクリーンの左端を200に設定
2000, // コンピュータのスクリーンの右端を2000に設定
'exponential' // exponentialマッピングをつかいます
)
// ラグタイムはデフォルトの0.1秒に設定
),
0, // 位相 0
0.1 // mul 0.1
)
}.scope
)

便宜上、MouseX.krメソッドはどのように自分がPlugオブジェクトに囲まれているかを知っているので、 上述の例よりも簡潔なコードを書くことができます。
The MouseX.krを利用した方法ではMouseX.newよりもう一つアーギュメントが必要になります、 それはPlugオブジェクトに与えていたラグタイムです。 下の例ではこのメソッドを用いています。

(
//マウスのX座標値で周波数をコントロールします
{
SinOsc.ar( // サインオシレータをつくる。


MouseX.kr( // Plug ugenに内包されたMouseXオブジェクトをつくる。

200, // コンピュータのスクリーンの左端を200に設定
2000, // コンピュータのスクリーンの右端を2000に設定
'exponential' // exponentialマッピングをつかいます
// ラグタイムはデフォルトの0.1秒に設定
),
0, // 位相 0
0.1 // mul 0.1
)
}.scope
)

同様にコントロール・ソースとしてMouseYを使うこともできます。名前から想像できる通り、マウスのY軸の値を利用します。

{ SinOsc.ar(MouseY.kr(200,2000,'exponential'), 0, 0.1) }.play;

一般的にexponentialマッピングは周波数のコントロールに適しています、 私達は音程を指数的に知覚するするからです、しかしlinearマッピングを使っても良いでしょう。

{ SinOsc.ar(MouseX.kr(200,2000,'exponential'), 0, 0.1) }.play;

{ SinOsc.ar(MouseX.kr(200,2000,'linear'), 0, 0.1) }.play;

上述の二つの例の違いをすぐにはっきりと知覚することは難しいでしょう。
しかしexponentialマッピングとlinearマッピングを同時に聞いてみると、 すぐに二つの違いに気付くことができるでしょう。 二つのオシレータはスクリーンの端では同じ音程ですが、スクリーンのまん中程では exponentialマッピングの方がlinerよりも低い周波数になっているのが解るでしょう。 次の例では、1チャンネル(左チャンネル)にlinearマッピング、2チャンネル(右チャンネル)にexponentialマッピングを使用しています。

(
{
[// マルチチャンネルのために一つの配列を使います(後で説明します)
SinOsc.ar(MouseX.kr(200,2000,'linear'), 0, 0.2) ,
SinOsc.ar(MouseX.kr(200,2000,'exponential'), 0, 0.2)
]
}.scope;
)

3.2 MIDIによるサウンド・コントロール

ExternalControlSourceはサブクラスをもっており、MIDIによるコントロールをすることができます。 今回の例ではMIDIキーボードをつなぎ、そのMIDIチャンネルが1にセットしてあると仮定しています。

モジュレーション・ホイールのようなMIDIコントローラを使用する場合は、MIDIControllerクラスを使用します。MIDIControllerクラスは、前号でとりあげたMouseXとMouseYよりも2つほど多いアーギュメントをとります。それはMIDIチャンネルとMIDIコントロールナンバーです。ちなみにMouseXとMouseYのアーギュメントは最小値、最大値、マッピング方法、ラグタイムでしたね。

では、モジュレーション・ホイールでSinオシレータのピッチをコントロールしてみましょう。

(
Synth.play({
SinOsc.ar(
MIDIController.kr(
1, // MIDIチャンネル 1
1, // MIDIコントロールナンバー 1 (モジュレーションホイール)
200, // 最小のアウトプット値
2000, // 最小のアウトプット値
'exponential' // マッピング方法
// / ラグタイムはデフォルトの0.1秒に設定
),
0, //サインオシレータの位相
0.1 // サインオシレータの振幅
)
})
)

つづいてピッチベンドを使用してみましょう。
MIDIデータとして受け取ったピッチベンドデータをSinオシレータのピッチベンドにしてみます。

(
Synth.play({
var note, bend, freq;


bend = MIDIPitchBend.kr(
1, //MIDIチャンネル1
-2, //ベンドダウン 2 秒
2 //ベンドアップ 2 秒
//マップ デフォルトのlinear
//ラグタイム デフォルトの0.1

);

note = 60 + bend; // ピッチベンド分を中央のCであるMIDIノートナンバー(*60)に足す

freq = note.midicps; // MIDIノートナンバーを周波数に変換

SinOsc.ar(
freq, //サインオシレータの周波数
0, //サインオシレータの位相
0.1 //サインオシレータの振幅
)
})
)

MIDIMostRecentNoteは、MIDIキーボードの最新のノートナンバーを受け取ります。 MIDIMostRecentNoteのアーギュメントは適当なデフォルト値を持っているので、MIDIチャンネルだけを記述してあげれば問題はないでしょう。

(
Synth.play({
var note, bend, freq;


bend = MIDIPitchBend.kr(
1, //MIDIチャンネル1
-2, //ベンドダウン 2 秒
2 //ベンドアップ 2 秒
//マップ デフォルトのlinear
//ラグタイム デフォルトの0.1
);


note = MIDIMostRecentNote.kr(1) // MIDIチャンネルだけを指定。

+ bend; // ピッチベンド分を中央のCであるMIDIノートナンバー(*60)に足す

freq = note.midicps; // MIDIノートナンバーを周波数に変換

SinOsc.ar(
freq, //サインオシレータの周波数
0, //サインオシレータの位相
0.1 //サインオシレータの振幅
)
})
)

次の例題は、MIDIチャンネルプレッシャーやアフタータッチで振幅をコントロールした例です。

(
Synth.play({
var note, bend, freq, amp;

amp = MIDITouch.kr(1, 0.1, 0.8);

bend = MIDIPitchBend.kr(
1, //MIDIチャンネル1
-2, //ベンドダウン 2 秒
2 //ベンドアップ 2 秒
//マップ デフォルトのlinear
//ラグタイム デフォルトの0.1
);

note = MIDIMostRecentNote.kr(1) // MIDIチャンネルだけを指定。
+ bend; // ピッチベンド分を中央のCであるMIDIノートナンバー(*60)に足す
freq = note.midicps; // MIDIノートナンバーを周波数に変換

SinOsc.ar(
freq, //サインオシレータの周波数
0, //サインオシレータの位相
0.1 //サインオシレータの振幅
)
})
)

3.3 グラフィックスライダーを用いてサウンドをコントロールする方法

この章ではグラフィックスライダーについて学びます。

ここに二つのスライダーをともなったウィンドウのサンプルがあります。 これらはグラフィカル・ユーザー・インターフェイス・ビルダで作られたものです。 このようなウィンドウを作るには、GUIメニューからNew GUI Windowを選びます。

すると空のウィンドウが現れますので、GUIメニューからEdit GUIを選びます。 するとパレットが現れます。このパレットの中のSliderView itemをクリックして、空のウィンドウにドラッグします。 ひとつのスライダービューがウィンドウに表示されます。 次にコマンド+dをタイプしてスライダービューを複製します。 二つのスライダーができたら、ウィンドウの右下をドラッグして、丁度よいウィンドサイズになるように整えます。

次にメニューからGenerate Code to Clipboardを選択します。 そして、テキストエディタなどにペーストします。すると以下のようなテキストがペーストされます。

{ var w;
w = GUIWindow.new("panel", Rect.newBy( 128, 64, 170, 90 ));
SliderView.new( w, Rect.newBy( 13, 19, 128, 20 ), "SliderView", 0, 0, 1, 0, 'linear');
SliderView.new( w, Rect.newBy( 13, 43, 128, 20 ), "SliderView", 0, 0, 1, 0, 'linear');
}

//////////////////////////////////////////////////////////////////////////////

(
var f;
f = { var w;
w = GUIWindow.new("panel", Rect.newBy( 128, 64, 170, 90 ));
SliderView.new( w, Rect.newBy( 13, 19, 128, 20 ), "SliderView", 0, 0, 1, 0, 'linear');
SliderView.new( w, Rect.newBy( 13, 43, 128, 20 ), "SliderView", 0, 0, 1, 0, 'linear');
};
f.value;
)

サウンドコントロールに使うために、上記のコードをすこし変えてみましょう。

上記のスライダーに与えられたパラメーター、0, 0, 1, 0, 'linear' は順に、 カレント値、最小値、最大値、ステップ値とマッピング方法です。

これらを次のように変えてみましょう。
1つめのスライダーは周波数をコントロールするのに使いますので、 カレント値を500に、最小値を200に、最大値を8000にして、マッピングは'exponential'にします。

2つめのスライダーは振幅のコントロールに使いましょう、 カレント値を0.1にして、他の値はそのままにしておきます。

(
var w; // ウィンドウをつくるための変数を用意してあげます。
w = GUIWindow.new("panel", Rect.newBy( 128, 64, 170, 90 ));
SliderView.new( w, Rect.newBy( 13, 19, 128, 20 ), "SliderView", 500, 200, 8000, 0, 'exponential'); SliderView.new( w, Rect.newBy( 13, 43, 128, 20 ), "SliderView", 0.1, 0, 1, 0, 'linear');

Synth.play({
SinOsc.ar(
// w.at(0)は、ウィンドウ(w)の最初のビューである、
// 一番目のスライダーの値を得ます。w.at(1)は二番目のスライダーの値を得ます。

w.at(0).kr, // サイン・オシレータの周波数
0, // サイン・オシレータの位相
w.at(1).kr // サイン・オシレータの振幅
)
});
w.close; // プログラムが終了した時に、wウィンドウを閉じます。
)

しかし、上述のようにインディックスで指定する他にも、変数を利用することができます。 w.at(0).krのようにインディックス番号で指定するよりも、変数をもちいた方が分かりやすいでしょう。

(
var w, freqSlider, ampSlider; // 変数の宣言
w = GUIWindow.new("panel", Rect.newBy( 128, 64, 170, 90 ));
freqSlider =
SliderView.new( w, Rect.newBy( 13, 19, 128, 20 ),
"SliderView", 500, 200, 8000, 0, 'exponential');
ampSlider =
SliderView.new( w, Rect.newBy( 13, 43, 128, 20 ),
"SliderView", 0.1, 0, 1, 0, 'linear');

Synth.play({
SinOsc.ar(
// freqSliderは最初のビューである、一番目のスライダーの値を参照します。
freqSlider.kr, // サイン・オシレータの周波数
0, // サイン・オシレータの位相
ampSlider.kr // サイン・オシレータの振幅
)
});
w.close; // プログラムが終了した時に、ウィンドウを閉じます
)