ChucKによる1bit音楽

ChucK
http://chuck.cs.princeton.edu/
長じてminiAudicleが装備され、使いやすくなった。


pdやscと比較してChucKで注目した点

  • サンプル単位の演算
  • 物理モデル音源
  • 制御構造

特にサンプル単位の演算というのはpdのfexpr~以来で、何に使うんだという話だが、
例えばカオス式のようなものを簡単に鳴らすことができるだろう。
カオス式はscでもStandardNなどUGenとして用意されているが、式自体をいじる方法がついにわからない。
もっとも、目ぼしいところは全てUGenで網羅しているのだろうが。


scのヘルプからStandardN (standard map chaotic generator) の説明をそのまま臨書してみる。

Step s => dac;

1.0 => float r; // iteration rate in samples
1.0 => float k; // perturbation amount
.5 => float x; // initial value of x
.5 => float y; // initial value of y

while( 1 )
{
r::samp => now;
    (k * Math.sin(x) + y) % (2*pi) => y;
    (x + y) % (2*pi) => x;
    x => s.next;
} 

これがそのまま鳴ったので少々驚く。


物理モデリングについては出来合いの音源が豊富にある。
ということで、では音響合成についてどこまで深く突っ込めるのかが主な関心となる。
以下はアナログ風キックの例。

SinOsc s  => dac;
ADSR a => blackhole;

a.set(1::samp, 0::ms, 1, 1000::ms);

a.keyOn();
1::samp => now;

a.keyOff();
now + 1000::ms  => time later;

while( now < later ) {
    a.value() => s.gain;
    Math.pow(a.value(), 50) * 3000 + 50 => s.freq;
    1::ms => now;
}

この記述に至るまでかなり難航したが、これでまともなのかもよくわからない。かつ、この手の用例はまだ少ない。
examplesではサウンドファイルでドラムパターンを鳴らすというようなことをやっているが、ではそのサウンドファイルはChucKではどうやって作れるのか。
音作りに関する機能の網羅性が明らかになれば制作環境としてもっと伸びると思う。


制御構造についてはあくまでpdやscとの比較だが、普通にわかりやすい。よりアルゴリズム作曲に適しているかも知れない。

1bit音楽へ

ChucKはサンプル単位の演算が可能である。2サンプルごとに振幅の+1と-1を切り替えてみる。

Step s => dac;
1 => int val;

while(1) {
-1 *=> val => s.next;
2::samp => now;
}

サンプリング周波数44100Hzの環境では、これは11025Hzの矩形波となる。


デジタルオーディオはサンプリング周波数の半分の周波数まで再生できる。
1サンプルごとに切り替えると再生の上限である22050Hzだが、これは私の可聴域を超えるので聴こえない。


切り替え間隔のサンプル数を増加させれば周波数は下がる。切り替えごとにサンプル数をインクリメントしてみる。

Step s => dac;
int val, len;
1 => val;

while(1) {
-1 *=> val => s.next;
len::samp => now;
len++;
}

急激に下がるのでアナログシンセのキック風の音になる。次は切り替え1000回ごとにインクリメントする。

Step s => dac;
int val, len;
1 => val;

while(1) {
int cnt;
len++;

	while(cnt<1000) {
	-1 *=> val => s.next;
	len::samp => now;
	cnt++;
	}
}

明らかな下降音階が聴こえる。これはサンプリング周波数の下方倍音列だろうか。
次は切り替え回数ではなく、一定時間でインクリメントする方法を考える。1/4秒間隔。

Step s => dac;
int val, len;
1 => val;

while(1) {
int cnt;
len++;

11025/len => float d;

	while(cnt < d) {
	-1 *=> val => s.next;
	len::samp => now;
	cnt++;
	}
}

切り替えサンプル数を数列で与えてみる。

Step s => dac;
int val, idx;
1 => val;
[1,2,3,4,5,6,7] @=> int len[];

while(1) {
-1 *=> val => s.next;
len[idx]::samp => now;
(idx+1)%len.cap() => idx;
}

数列が短い場合、この場合は一つの音色として聴こえる。
恐らく数列の合計が基音の周波数として認識されるが、ここに一つ数字を加えても単純に周波数が下がるわけではない。

Step s => dac;
int val, idx;
1 => val;
[1,2,3,4,5,6,7,8] @=> int len[];

while(1) {
-1 *=> val => s.next;
len[idx]::samp => now;
(idx+1)%len.cap() => idx;
}

数列の長さが偶数か奇数かによって、矩形波ひいては基音の聴こえ方が変わるのだろう。
次は一定時間ごとに数列を延ばしてみる。基音の変化が明らかである。

Step s => dac;
int val, len, rate;
1 => val;
2 => len;
44100/2 => rate;

while(1) {
int cnt, total;

	int n;
	while(n < len) {
	n +=> total;
	n++;
	}
	Math.min(total, rate)$int => total;
	<<<total>>>;
	
	while(cnt < rate/total) {
	
		int m;
		while(m < len) {
		-1 *=> val => s.next;
		m::samp => now;
		m++;
		}
	cnt++;
	}
len++;
}

ところでここでは+1か-1かの二つの振幅しか扱っていないので、原理としては1bitオーディオということになる。
サンプル数を乱数にしてみる。

Step s => dac;
1 => int val;

while(1) {
Math.rand2(1, 10) => int x;
	-1 *=> val => s.next;
	x::samp => now;
}

単なるノイズに聴こえる。値の範囲を変えることで帯域が変わるが、ノイズには変わりない。
当然ながら、ここに反復を導入すると個々のピッチが明らかになってくる。

Step s => dac;
1 => int val;

while(1) {
Math.rand2(1, 10) => int x;
	int i;
	while(i < 20) {
	-1 *=> val => s.next;
	x::samp => now;
	i++;
	}
}

1bit音楽というものがあるとすれば*1、単に+1と-1を切り替えるサンプル数の列として表すことができるだろう。
振幅の要素を欠いた純粋な持続の列である。


単純な算術的操作によって興味深い構造を作る方法はいくつか考えられる。
二つの異なる周期の同時使用は最も単純な例の一つだろう。

cyc(83,101,11,67,3);
	
fun void cyc (int ai, int am, int bi, int bm, int mul)
{
Step s => dac;
1 => int val;
int a, b;

	while(1) {
		int i;
		while(i < b*mul) {
		-1 *=> val => s.next;
		a::samp => now;
		i++;
		}
	(a+ai)%am => a;
	(b+bi)%bm => b;
	}
}

異なる周期によるインクリメントの例。

Step s => dac;
1 => int val;
int a, b, c;

while(1) {
	int d;
	while(d < 1000) {
	-1 *=> val => s.next;
	(a+1)%211 => a;
	a::samp => now;

	-1 *=> val => s.next;
	(b+1)%163 => b;
	b::samp => now;

	-1 *=> val => s.next;
	(c+70)%71 => c;
	c::samp => now;

	d++;
	}
5000::samp => now;
}

(原文2009年4月、加筆転載)

*1:有名な試みとしてはhttp://www.onebitmusic.com

謎の楽器

キー入力が使えるということがわかったので、鍵盤楽器のようなものを作ってみた。

(

// 音源
SynthDef("wave", {|note,amp|
f = note.lag(0.02).midicps;
g = amp.lag(0.2);
o = SinOsc.ar(f)!2*g;
Out.ar(0,o/2);
}).store;

// 音源はじめる
s.sendMsg(\s_new, "wave", 800);

// GUI
~wd = JSCWindow( "keystroke", Rect( 0, 0, 100, 100 ) );
~wd.view.canFocus = true;
~wd.view.focus;
~wd.front;

~t0 = JSCTextField( ~wd, Rect( 10, 10, 100, 20 ));
~t0.stringColor = Color.black;
~t0.value = "notenum";

~t1 = JSCTextField( ~wd, Rect( 10, 40, 100, 20 ));
~t1.stringColor = Color.black;
~t1.value = "unicode";

// 終了処理
~quit = {
	Task({
		s.sendMsg(\n_set, 800, \amp, 0);
		0.5.wait;
		s.sendMsg(\n_free, 800);
		"quit".postln;
	}).play;
};

// 配列など
~note = 60;
~mode = [1,2,3,4,5];
~key = [49,50,51,52,53,54,55,56,57,48];
~keyval = [4,3,2,1,0,5,6,7,8,9];
~amp = 0;

// キー操作
~wd.view.keyDownAction = {
	|view, char, modifiers, unicode, keycode|

	k = unicode;
	v = 0;
	b = 0;

	~t1.valueAction = k;

	~key.size.do{|i|	// 配列indexをさがす
		if( k == ~key[i], {
			v = ~keyval[i];
			b = 1;
		});
	};

	if( b == 1, {		// 数字キーで音程を上下
		n = ~mode[v%~mode.size];
		if( v<5, {	// '1~5'は下行、'6~0'は上行
			n = n*(-1);
		});
		~note = ~note + n;
		~t0.valueAction = ~note;
		s.sendMsg(\n_set, 800, \note, ~note);
		~note = max(min(~note,96),36);
	});

	switch( k,
		13, {		// ENTERで音を出す・止める
			~amp = ~amp+1%2;
			s.sendMsg(\n_set, 800, \amp, ~amp);
		},
		27, {		// ESCで終了処理
			~wd.close;
			~quit.();
		}
	);
};

)                                                    

とりあえず最低限の機能しかないのだが、これの画期的なところは「直前の音に音程を足し引きして演奏する」というもので、演奏に使うキーは11個、使える音程は半音〜4度の5種類しかないが、幅広い音域がカバーでき、派手な跳躍を無視すれば世の中の大概のメロディーは弾けるんじゃねえのと思われる。演奏している様がレジ打ちに似ていることからレジ式と名付けたい(一個のレジスタに対してひたすら加算しているとも言う)。


もちろん、

~mode = [0.5, 1, 2.5, 4.5, 6.5];

上のような謎の音階を設定しても気持ち悪い楽しいかも知れない。

pdをやりなおす(5)

乱数

#N canvas 0 0 667 306 12; #X obj 71 123 random 100; #X obj 71 50 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X floatatom 71 162 5 0 0 0 - - -; #X obj 71 85 metro 200; #X obj 244 85 samphold~; #X obj 304 45 phasor~ 5; #X obj 244 45 noise~; #X obj 244 123 *~ 500; #X obj 245 162 osc~; #X obj 245 203 *~; #X obj 245 247 dac~; #X obj 307 162 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 449 85 random 500; #X obj 545 50 delay; #X obj 449 50 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X floatatom 449 123 5 0 0 0 - - -; #X obj 545 123 line~; #X obj 449 203 *~; #X obj 449 247 dac~; #X msg 545 85 1 \, 0 50; #X obj 448 162 osc~ 100; #X connect 0 0 2 0; #X connect 1 0 3 0; #X connect 3 0 0 0; #X connect 4 0 7 0; #X connect 5 0 4 1; #X connect 6 0 4 0; #X connect 7 0 8 0; #X connect 8 0 9 0; #X connect 9 0 10 0; #X connect 9 0 10 1; #X connect 11 0 9 1; #X connect 12 0 13 0; #X connect 12 0 15 0; #X connect 13 0 12 0; #X connect 13 0 19 0; #X connect 14 0 12 0; #X connect 16 0 17 1; #X connect 17 0 18 0; #X connect 17 0 18 1; #X connect 19 0 16 0; #X connect 20 0 17 0;


randomはbangを受けて、0から引数-1の範囲の整数で乱数を返す。
中央はsamphold~を使ってランダムな音声信号を得る例。
右はrandomとdelayをたすき掛けにしてランダムなリズムを得る例。

定位(パン)

#N canvas 0 0 467 264 12; #N canvas 243 54 453 449 pan 0; #X obj 290 268 cos~; #X obj 271 307 *~; #X obj 235 268 inlet~; #X obj 157 268 cos~; #X obj 138 307 *~; #X obj 102 268 inlet~; #X obj 138 352 outlet~; #X obj 270 352 outlet~; #X obj 290 141 /~ 8; #X obj 290 106 +~ 1; #X obj 289 69 inlet~; #X obj 289 232 +~ 0.75; #X connect 0 0 1 1; #X connect 1 0 7 0; #X connect 2 0 1 0; #X connect 3 0 4 1; #X connect 4 0 6 0; #X connect 5 0 4 0; #X connect 8 0 3 0; #X connect 8 0 11 0; #X connect 9 0 8 0; #X connect 10 0 9 0; #X connect 11 0 0 0; #X restore 72 144 pd pan; #X obj 72 190 dac~; #X obj 164 38 osc~ 0.1; #X obj 71 38 osc~ 100; #X obj 71 101 *~; #X obj 89 70 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #N canvas 243 54 586 549 lin_panning 0; #X obj 277 90 sig~; #X obj 174 337 snapshot~; #X obj 63 123 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 280 24 hsl 128 15 0 1 0 0 empty empty pan -2 -8 0 12 -262144 -1 -1 6400 1; #X obj 174 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 6400 1; #X obj 353 395 expr sqrt($f1*$f1+$f2*$f2); #X floatatom 353 430 4 0 0 0 power - -; #X obj 63 162 metro 10; #X floatatom 277 56 5 0 0 0 - - -; #X obj 277 337 snapshot~; #X obj 277 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 6300 1; #X obj 278 162 expr~ 1-$v1; #X connect 0 0 11 0; #X connect 0 0 1 0; #X connect 1 0 4 0; #X connect 1 0 5 0; #X connect 2 0 7 0; #X connect 3 0 8 0; #X connect 5 0 6 0; #X connect 7 0 1 0; #X connect 7 0 9 0; #X connect 8 0 0 0; #X connect 9 0 10 0; #X connect 9 0 5 1; #X connect 11 0 9 0; #X restore 268 91 pd lin_panning; #N canvas 243 54 586 549 cos_panning 0; #X obj 175 291 cos~; #X obj 277 253 +~ 0.75; #X obj 277 90 sig~; #X obj 174 337 snapshot~; #X obj 63 123 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 280 24 hsl 128 15 0 1 0 0 empty empty pan -2 -8 0 12 -262144 -1 -1 12700 1; #X obj 174 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 0 1; #X obj 353 395 expr sqrt($f1*$f1+$f2*$f2); #X floatatom 353 430 4 0 0 0 power - -; #X obj 63 162 metro 10; #X floatatom 277 56 5 0 0 0 - - -; #X obj 278 162 /~ 4; #X obj 277 291 cos~; #X obj 276 337 snapshot~; #X obj 276 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 12700 1; #X connect 0 0 3 0; #X connect 1 0 12 0; #X connect 2 0 11 0; #X connect 3 0 6 0; #X connect 3 0 7 0; #X connect 4 0 9 0; #X connect 5 0 10 0; #X connect 7 0 8 0; #X connect 9 0 3 0; #X connect 9 0 13 0; #X connect 10 0 2 0; #X connect 11 0 0 0; #X connect 11 0 1 0; #X connect 12 0 13 0; #X connect 13 0 14 0; #X connect 13 0 7 1; #X restore 268 124 pd cos_panning; #N canvas 243 54 586 549 cos_panning_1 0; #X obj 175 291 cos~; #X obj 277 253 +~ 0.75; #X obj 277 90 sig~; #X obj 174 337 snapshot~; #X obj 63 123 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 280 24 hsl 128 15 -1 1 0 0 empty empty pan -2 -8 0 12 -262144 -1 -1 12700 1; #X obj 174 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 0 1; #X obj 353 395 expr sqrt($f1*$f1+$f2*$f2); #X floatatom 353 430 4 0 0 0 power - -; #X obj 63 162 metro 10; #X floatatom 277 56 5 0 0 0 - - -; #X obj 277 291 cos~; #X obj 276 337 snapshot~; #X obj 276 378 vsl 15 128 0 1 0 0 empty empty empty 0 -9 0 10 -262144 -1 -1 12700 1; #X obj 278 125 +~ 1; #X obj 278 162 /~ 8; #X connect 0 0 3 0; #X connect 1 0 11 0; #X connect 2 0 14 0; #X connect 3 0 6 0; #X connect 3 0 7 0; #X connect 4 0 9 0; #X connect 5 0 10 0; #X connect 7 0 8 0; #X connect 9 0 3 0; #X connect 9 0 12 0; #X connect 10 0 2 0; #X connect 11 0 12 0; #X connect 12 0 13 0; #X connect 12 0 7 1; #X connect 14 0 15 0; #X connect 15 0 0 0; #X connect 15 0 1 0; #X restore 268 158 pd cos_panning_1; #X connect 0 0 1 0; #X connect 0 1 1 1; #X connect 2 0 0 2; #X connect 3 0 4 0; #X connect 4 0 0 0; #X connect 4 0 0 1; #X connect 5 0 4 1;


左右のチャンネルの音量を調節して、音の左右の位置を制御することを定位と言う。


「pd lin_panning」は単純な線形定位の例。左右の振幅の合計が1となる。
ところがこの場合では、音を中央に定位した場合、全体の音量が約0.7倍まで落ちる。
(「pan」を0.5にすると「power」の値は0.707になる)
これを音で聴くと多少奥に引っ込んだ感じになるだろう。


「pd cos_panning」はコサインを使った定位の例。
この場合は定位に関係なく音量が一定に保たれる。


「pd cos_panning_1」は先程の例を双極信号(-1〜+1)で制御するようにしたもの。
1が右、0が中央、-1が左となる。
これを書き換えて音声信号を使って定位できるようにしたものが「pd pan」。

スペクトルアナライザ

#N canvas 0 0 522 306 12; #N canvas 0 0 450 300 (subpatch) 0; #X array spec 256 float 0; #X coords 0 256 255 0 200 140 1; #X restore 30 80 graph; #N canvas 306 80 640 428 fft 0; #X obj 292 138 rfft~; #X obj 291 269 rifft~; #N canvas 0 0 450 300 pol2rec 0; #X obj 88 70 inlet~; #X obj 225 70 inlet~; #X obj 88 219 outlet~; #X obj 257 219 outlet~; #X obj 87 155 expr~ $v1*cos($v2); #X obj 258 155 expr~ $v1*sin($v2); #X connect 0 0 4 0; #X connect 0 0 5 0; #X connect 1 0 4 1; #X connect 1 0 5 1; #X connect 4 0 2 0; #X connect 5 0 3 0; #X restore 292 224 pd pol2rec; #N canvas 0 0 450 300 rec2pol 0; #X obj 30 62 inlet~; #X obj 29 147 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 241 62 inlet~; #X obj 268 147 expr~ atan2($v2 \, $v1); #X obj 30 211 outlet~; #X obj 267 211 outlet~; #X connect 0 0 1 0; #X connect 0 0 3 0; #X connect 1 0 4 0; #X connect 2 0 1 1; #X connect 2 0 3 1; #X connect 3 0 5 0; #X restore 291 183 pd rec2pol; #X obj 146 228 tabwrite~ spec; #X obj 146 179 metro 500; #X obj 293 42 inlet~; #X obj 146 138 loadbang; #X obj 291 343 outlet~; #X obj 293 102 *~; #X obj 311 72 tabreceive~ hann; #X obj 413 344 block~ 1024 2; #X obj 292 306 /~ 2048; #X connect 0 0 3 0; #X connect 0 1 3 1; #X connect 1 0 12 0; #X connect 2 0 1 0; #X connect 2 1 1 1; #X connect 3 0 4 0; #X connect 3 0 2 0; #X connect 3 1 2 1; #X connect 5 0 4 0; #X connect 6 0 9 0; #X connect 7 0 5 0; #X connect 9 0 0 0; #X connect 10 0 9 1; #X connect 12 0 8 0; #X restore 295 216 pd fft; #X obj 295 253 dac~; #X obj 295 165 *~; #X obj 313 138 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 361 165 *~; #X obj 379 138 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 299 39 hsl 128 15 0 11025 0 0 empty empty empty -2 -8 0 10 -262144 -1 -1 2200 1; #X floatatom 296 67 0 0 0 0 freq - -; #X obj 296 105 osc~; #X obj 362 105 phasor~; #X obj 437 165 *~; #X obj 455 138 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 438 105 noise~; #N canvas 0 0 493 435 window 0; #X obj 127 235 osc~; #X obj 127 166 samplerate~; #X obj 127 268 *~ -0.5; #X obj 127 301 +~ 0.5; #X obj 107 343 tabwrite~ hann; #N canvas 0 0 452 302 (subpatch) 0; #X array hann 1024 float 0; #X coords 0 1 1023 -1 100 100 1; #X restore 296 71 graph; #X msg 168 206 0; #X obj 127 202 /; #X msg 278 235 \; hann resize \$1; #X obj 107 66 loadbang; #X obj 107 102 t b b b b; #X msg 236 206 1024; #X obj 236 239 / 2; #X obj 279 318 block~ 1024 2; #X connect 0 0 2 0; #X connect 1 0 7 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 6 0 0 1; #X connect 7 0 0 0; #X connect 9 0 10 0; #X connect 10 0 4 0; #X connect 10 1 1 0; #X connect 10 2 6 0; #X connect 10 3 11 0; #X connect 11 0 8 0; #X connect 11 0 12 0; #X connect 12 0 7 1; #X restore 382 216 pd window; #X connect 1 0 2 0; #X connect 1 0 2 1; #X connect 3 0 1 0; #X connect 4 0 3 1; #X connect 5 0 1 0; #X connect 6 0 5 1; #X connect 7 0 8 0; #X connect 8 0 9 0; #X connect 8 0 10 0; #X connect 9 0 3 0; #X connect 10 0 5 0; #X connect 11 0 1 0; #X connect 12 0 11 1; #X connect 13 0 11 0;


現実の物音には様々な高さ(周波数)の音が同時に含まれている。
周波数ごとの音量の分布を示したものを周波数スペクトルと呼ぶ。


rfft~、rifft~を使ったスペクトルアナライザの例だが、非常に大雑把なので、例えば単一の周波数しか持たないはずのosc~のスペクトルが山型に滲む。


phasor~のスペクトルは複数のピークが等間隔に並んでいるのが見える。
要はこれらのピークが倍音で、基準の周波数の整数倍になっている。
倍音成分が強いとピッチを明確に聴き取ることができる。


一方、noise~のスペクトルは周波数の全域に一様に分布している。
こうなるとピッチの感覚はなくなる。

ボコーダ

#N canvas 0 0 416 387 12; #N canvas 0 123 640 428 fft 0; #X obj 182 130 rfft~; #X obj 179 302 rifft~; #N canvas 0 0 450 300 pol2rec 0; #X obj 88 70 inlet~; #X obj 225 70 inlet~; #X obj 88 219 outlet~; #X obj 257 219 outlet~; #X obj 87 155 expr~ $v1*cos($v2); #X obj 258 155 expr~ $v1*sin($v2); #X connect 0 0 4 0; #X connect 0 0 5 0; #X connect 1 0 4 1; #X connect 1 0 5 1; #X connect 4 0 2 0; #X connect 5 0 3 0; #X restore 180 257 pd pol2rec; #N canvas 0 0 450 300 rec2pol 0; #X obj 30 62 inlet~; #X obj 29 147 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 241 62 inlet~; #X obj 268 147 expr~ atan2($v2 \, $v1); #X obj 30 211 outlet~; #X obj 267 211 outlet~; #X connect 0 0 1 0; #X connect 0 0 3 0; #X connect 1 0 4 0; #X connect 2 0 1 1; #X connect 2 0 3 1; #X connect 3 0 5 0; #X restore 181 175 pd rec2pol; #X obj 183 34 inlet~; #X obj 179 376 outlet~; #X obj 183 94 *~; #X obj 397 34 tabreceive~ hann; #X obj 293 130 rfft~; #N canvas 0 0 450 300 rec2pol 0; #X obj 30 62 inlet~; #X obj 29 147 expr~ sqrt($v1*$v1+$v2*$v2); #X obj 241 62 inlet~; #X obj 268 147 expr~ atan2($v2 \, $v1); #X obj 30 211 outlet~; #X obj 267 211 outlet~; #X connect 0 0 1 0; #X connect 0 0 3 0; #X connect 1 0 4 0; #X connect 2 0 1 1; #X connect 2 0 3 1; #X connect 3 0 5 0; #X restore 292 175 pd rec2pol; #X obj 294 34 inlet~; #X obj 294 94 *~; #X obj 180 216 *~; #X obj 180 339 /~ 2048; #X obj 299 376 block~ 1024 4; #X connect 0 0 3 0; #X connect 0 1 3 1; #X connect 1 0 13 0; #X connect 2 0 1 0; #X connect 2 1 1 1; #X connect 3 0 12 0; #X connect 4 0 6 0; #X connect 6 0 0 0; #X connect 7 0 6 1; #X connect 7 0 11 1; #X connect 8 0 9 0; #X connect 8 1 9 1; #X connect 9 0 12 1; #X connect 9 1 2 1; #X connect 10 0 11 0; #X connect 11 0 8 0; #X connect 12 0 2 0; #X connect 13 0 5 0; #X restore 175 270 pd fft; #X obj 175 308 dac~; #N canvas 0 0 493 435 window 0; #X obj 127 235 osc~; #X obj 127 166 samplerate~; #X obj 127 268 *~ -0.5; #X obj 127 301 +~ 0.5; #X obj 107 343 tabwrite~ hann; #N canvas 0 0 452 302 (subpatch) 0; #X array hann 1024 float 0; #X coords 0 1 1023 -1 100 100 1; #X restore 296 71 graph; #X msg 168 206 0; #X obj 127 202 /; #X msg 278 235 \; hann resize \$1; #X obj 107 66 loadbang; #X msg 236 206 1024; #X obj 107 102 t b b b b; #X obj 236 239 / 1; #X obj 279 343 block~ 1024 1; #X connect 0 0 2 0; #X connect 1 0 7 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 6 0 0 1; #X connect 7 0 0 0; #X connect 9 0 11 0; #X connect 10 0 8 0; #X connect 10 0 12 0; #X connect 11 0 4 0; #X connect 11 1 1 0; #X connect 11 2 6 0; #X connect 11 3 10 0; #X connect 12 0 7 1; #X restore 247 270 pd window; #X obj 45 189 readsf~; #X obj 45 77 openpanel; #X msg 45 151 open \$1; #X obj 45 115 symbol; #X obj 127 77 t b b; #X msg 127 151 1; #N canvas 0 0 450 300 chord 0; #X obj 116 129 phasor~; #X obj 116 97 mtof; #X obj 189 132 phasor~; #X obj 189 100 mtof; #X obj 260 132 phasor~; #X obj 260 100 mtof; #X obj 331 132 phasor~; #X obj 331 100 mtof; #X obj 189 68 + 4; #X obj 260 68 + 3; #X obj 331 68 + 4; #X obj 188 224 hip~ 5000; #X obj 188 192 *~ 0.2; #X obj 116 17 inlet; #X obj 188 261 outlet~; #X connect 0 0 12 0; #X connect 1 0 0 0; #X connect 2 0 12 0; #X connect 3 0 2 0; #X connect 4 0 12 0; #X connect 5 0 4 0; #X connect 6 0 12 0; #X connect 7 0 6 0; #X connect 8 0 3 0; #X connect 8 0 9 0; #X connect 9 0 5 0; #X connect 9 0 10 0; #X connect 10 0 7 0; #X connect 11 0 14 0; #X connect 12 0 11 0; #X connect 13 0 1 0; #X connect 13 0 8 0; #X restore 215 78 pd chord; #X obj 215 44 nbx 3 18 -1e+037 1e+037 0 1 empty empty midi_note_num 0 -8 0 12 -262144 -1 -1 45 256; #X msg 126 189 0; #X obj 45 44 bng 20 250 50 0 empty empty open 0 -8 0 12 -262144 -1 -1; #X obj 127 44 bng 20 250 50 0 empty empty play 0 -8 0 12 -262144 -1 -1; #X connect 0 0 1 0; #X connect 0 0 1 1; #X connect 3 0 0 0; #X connect 3 1 11 0; #X connect 4 0 6 0; #X connect 5 0 3 0; #X connect 6 0 5 0; #X connect 7 0 8 0; #X connect 7 1 6 0; #X connect 8 0 3 0; #X connect 9 0 0 1; #X connect 10 0 9 0; #X connect 11 0 3 0; #X connect 12 0 4 0; #X connect 13 0 7 0;


rfft~など、FFTに関するオブジェクトを使ってボコーダを作った例。
要するに二つの音源のスペクトルを掛け算して、一方の音をもう一方の音でフィルタしている。
「open」で適当な音声ファイルを開いて「play」で再生してみる。

pdをやりなおす(4)

ディレイ

#N canvas 0 0 450 300 12; #X obj 150 104 *~; #X obj 168 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 149 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 228 24 pd wow; #X obj 228 104 *~; #X obj 246 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 151 209 delread~ hoge 1000; #X obj 150 160 delwrite~ hoge 1000; #X obj 151 255 dac~; #X connect 0 0 7 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 4 1; #X connect 6 0 8 0; #X connect 6 0 8 1;


adc~はオーディオ入力。PCに接続したマイクなどから音を入力する。
「pd wow」はテスト音源。トグルをクリックして音源を選択する。


ディレイへの入力はdelwrite~、ディレイからの出力はdelread~を使う。
第一の引数はディレイの名前。第二の引数は、delwrite~の場合はディレイの最大時間、delread~はディレイタイムをミリ秒で指定する。
この場合は1秒遅れて音が出る。

#N canvas 0 0 450 300 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 49 160 delwrite~ hoge 1000; #X obj 222 246 dac~; #X obj 278 24 delread~ hoge 0; #X obj 278 63 delread~ hoge 300; #X obj 278 104 delread~ hoge 600; #X obj 278 145 delread~ hoge 900; #X obj 221 201 /~ 2; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 8 0 12 0; #X connect 9 0 12 0; #X connect 10 0 12 0; #X connect 11 0 12 0; #X connect 12 0 7 0; #X connect 12 0 7 1;


異なるディレイタイムのdelread~を重ねるとエコーになる。

#N canvas 0 0 450 300 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 50 219 delwrite~ hoge 1000; #X obj 258 68 dac~; #X obj 238 24 delread~ hoge 300; #X obj 239 158 *~; #X obj 257 119 nbx 5 24 0 1 0 1 empty empty feedback 0 -8 0 12 -262144 -1 -1 0.7 256; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 8 0 7 1; #X connect 8 0 9 0; #X connect 9 0 6 0; #X connect 10 0 9 1;


フィードバックディレイで同様の効果を作る。
delread~の出力を再度delwrite~に返すことでフィードバックループになる。
feedbackの数値は減衰率。1以上になると信号が消えなくなり、音が歪む。

#N canvas 0 0 450 300 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 50 219 delwrite~ hoge 1000; #X obj 258 113 dac~; #X obj 238 67 delread~ hoge 10; #X obj 238 155 *~ 0.95; #X obj 50 161 /~ 2; #X connect 0 0 10 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 10 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 8 0 7 1; #X connect 8 0 9 0; #X connect 9 0 6 0; #X connect 10 0 6 0;


フィードバックディレイのディレイタイムを極めて短時間(10ms)にしたもの。
ディレイというよりは金属的な反響音になる。


(さらにディレイタイムを短くしていくと初歩的なローパスフィルタになるのだが、これを試すにはfexpr~を使う必要がある。fexpr~については省略)

#N canvas 0 0 450 300 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 49 160 delwrite~ hoge 1000; #X obj 246 208 dac~; #X obj 245 160 vd~ hoge; #X obj 248 43 hsl 128 15 0 1000 0 0 empty empty delay_time -2 -8 0 12 -262144 -1 -1 10300 1; #X floatatom 268 80 0 0 0 0 - - -; #X obj 245 121 lop~ 1; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 8 0 7 1; #X connect 9 0 10 0; #X connect 9 0 11 0; #X connect 11 0 8 0;


vd~はdelread~と同じくディレイの出力だが、ディレイタイムを変えることができる。
ここで、適当に「delay_time」を動かすと、単にディレイタイムが変わるだけでなく、スクラッチのような効果が起こる。
vd~の手前にlop~があるためだが、何故だろうか。

#N canvas 0 0 450 300 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 49 160 delwrite~ hoge 1000; #X obj 246 208 dac~; #X obj 245 160 vd~ hoge; #X obj 244 116 *~ 1000; #X obj 244 76 phasor~; #X obj 244 30 nbx 5 24 -1e+037 1e+037 0 1 empty empty freq 0 -8 0 12 -262144 -1 -1 0.5 256; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 8 0 7 1; #X connect 9 0 8 0; #X connect 10 0 9 0; #X connect 11 0 10 0;


ディレイタイムの指定にphasor~を使っている。「freq」の数値をいろいろ変えてみる。
ディレイタイムの動く速度が変わることでピッチ(音の高さ)が変化する。


(ここでディレイタイムとピッチとの関係を考えるとなかなか難しいが、とりあえずは考えないことにする)


さて、この例ではディレイタイムが0に戻る瞬間に波形が途切れてノイズが発生する。
これを回避するには、例えばディレイタイムが戻る時に一瞬音量を落とすことが考えられる。

#N canvas 0 0 553 327 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 49 160 delwrite~ hoge 1000; #X obj 245 160 vd~ hoge; #X obj 244 116 *~ 1000; #X obj 244 70 phasor~; #X obj 244 30 nbx 5 24 -1e+037 1e+037 0 1 empty empty freq 0 -8 0 12 -262144 -1 -1 0.5 256; #X obj 246 242 dac~; #X obj 246 201 *~; #X obj 364 116 cos~; #X obj 365 160 -~ 1; #X obj 366 242 expr~ pow($v1 \, 0.4); #X obj 365 201 /~ -2; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 7 0 12 0; #X connect 8 0 7 0; #X connect 9 0 8 0; #X connect 9 0 13 0; #X connect 10 0 9 0; #X connect 12 0 11 0; #X connect 12 0 11 1; #X connect 13 0 14 0; #X connect 14 0 16 0; #X connect 15 0 12 1; #X connect 16 0 15 0;


phasor~の出力を利用してノイズ回避のために窓かけという処理をしている。
「cos~」「-~ 1」「/~ -2」「expr~ pow($v1, 0.4)」の部分がこれにあたる。
簡単に言えば、短いフェードイン・アウトを生成している。

#N canvas 0 0 553 327 12; #X obj 49 104 *~; #X obj 67 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 48 24 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 127 24 pd wow; #X obj 127 104 *~; #X obj 145 68 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 49 160 delwrite~ hoge 1000; #X obj 245 160 vd~ hoge; #X obj 244 67 phasor~; #X obj 244 30 nbx 5 24 -1e+037 1e+037 0 1 empty empty freq 0 -8 0 12 -262144 -1 -1 80 256; #X obj 246 201 *~; #X obj 366 243 expr~ pow($v1 \, 0.4); #X obj 247 289 dac~; #X obj 246 243 *~ 0.7; #X obj 244 116 *~ 50; #X obj 364 116 cos~; #X obj 365 160 -~ 1; #X obj 365 201 /~ -2; #X connect 0 0 6 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 6 0; #X connect 5 0 4 1; #X connect 7 0 10 0; #X connect 8 0 14 0; #X connect 8 0 15 0; #X connect 9 0 8 0; #X connect 10 0 13 0; #X connect 11 0 10 1; #X connect 13 0 6 0; #X connect 13 0 12 0; #X connect 13 0 12 1; #X connect 14 0 7 0; #X connect 15 0 16 0; #X connect 16 0 17 0; #X connect 17 0 11 0;


先程のパッチにフィードバックを加えただけだが、非常に大雑把なピッチシフターのような効果になる。
これをさらに改良すると本格的なピッチシフターを作ることができるだろう。

配列テーブルへの音声読み込み

#N canvas 0 0 858 574 12; #X obj 72 298 osc~ 1000; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 100 float 0; #X coords 0 1 99 -1 200 140 1; #X restore 516 61 graph; #X obj 517 343 loadbang; #X msg 517 378 \; array1 ylabel -4 -1 0 1; #X obj 72 99 metro 500; #X obj 73 383 *~; #X obj 72 53 tgl 15 0 empty empty start 17 7 0 12 -262144 -1 -1 1 1 ; #X obj 72 163 tabwrite~ array1; #X obj 303 53 catch~ hoge; #X obj 91 343 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #X obj 73 432 throw~ hoge; #X obj 182 383 *~; #X obj 200 343 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 182 432 throw~ hoge; #X obj 181 298 phasor~ 1000; #X obj 298 383 *~; #X obj 316 343 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 298 432 throw~ hoge; #X obj 297 298 noise~; #X obj 302 149 *~; #X obj 320 108 nbx 5 20 -1e+037 1e+037 0 1 empty empty amp 0 -8 0 12 -262144 -1 -1 1 256; #X obj 178 99 r~ moge; #X obj 303 194 s~ moge; #X connect 0 0 5 0; #X connect 2 0 3 0; #X connect 4 0 7 0; #X connect 5 0 10 0; #X connect 6 0 4 0; #X connect 8 0 19 0; #X connect 9 0 5 1; #X connect 11 0 13 0; #X connect 12 0 11 1; #X connect 14 0 11 0; #X connect 15 0 17 0; #X connect 16 0 15 1; #X connect 18 0 15 0; #X connect 19 0 22 0; #X connect 20 0 19 1; #X connect 21 0 7 0;


最初にmetroを開始し、osc~、phasor~、noise~から音源を選ぶ。
tabwrite~はbangを受け取ることで、入力された音声信号を配列テーブルに読み込む。
テーブルには音源の波形が表示される。音源によって波の形は全く異なる。


また、ampの数値をいろいろ試してみる。
音声信号にampの数値を乗算しているが、数値によって波形が縦方向に伸び縮みする。
波形の横軸は時間、縦軸は振幅(振れ幅)を意味するが、要するに振幅が大きいと音量が大きくなる。
テーブルからはみ出した信号、つまり-1〜+1を超える値は、出音としては正しく再生されない。

#N canvas 0 0 655 615 12; #X obj 44 106 *~; #X obj 62 70 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 43 26 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 122 26 pd wow; #X obj 122 106 *~; #X obj 140 70 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 44100 float 0; #X coords 0 1 44099 -1 200 140 1; #X restore 360 39 graph; #X obj 44 162 tabwrite~ array1; #X obj 201 89 bng 50 250 50 0 empty empty get 0 -9 0 12 -262144 -1 -1; #X obj 239 563 dac~; #X obj 238 508 tabplay~ array1; #X msg 47 274 bang; #X obj 455 279 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 455 360 random 10; #X obj 455 403 * 4410; #X obj 455 315 metro 100; #X msg 455 447 \$1 44100; #X msg 238 274 0 4410; #X connect 0 0 7 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 10 0 9 0; #X connect 10 0 9 1; #X connect 11 0 10 0; #X connect 12 0 15 0; #X connect 13 0 14 0; #X connect 14 0 16 0; #X connect 15 0 13 0; #X connect 16 0 10 0; #X connect 17 0 10 0;


「get」をクリックしてテーブルに音声信号を読み込む。
tabplay~はテーブルのデータを音として再生する。
bangを受け取った場合はテーブルの最初から最後まで再生する。


次に「0 4410」をクリックすると、テーブルの0番目から4410個の値を再生する。
これはほとんどのPCで0.1秒分に相当する。
オーディオデバイスのサンプリング周波数が44.1kHzという場合、1秒間で44100個の値を再生する。


最後に、metroを開始するとテーブル上の開始位置をランダムにして再生する。

#N canvas 0 0 601 585 12; #X obj 48 367 *~; #X obj 66 331 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 47 287 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 126 287 pd wow; #X obj 126 367 *~; #X obj 144 331 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 44100 float 0; #X coords 0 1 44099 -1 200 140 1; #X restore 198 39 graph; #X obj 48 423 tabwrite~ array1; #X obj 205 350 bng 50 250 50 0 empty empty get 0 -9 0 12 -262144 -1 -1; #X obj 349 300 phasor~; #X obj 349 245 nbx 5 24 -1e+037 1e+037 0 1 empty empty freq 0 -8 0 12 -262144 -1 -1 10 256; #X obj 352 506 dac~; #X obj 351 454 tabread4~ array1; #X obj 413 347 nbx 5 24 0 44100 0 1 empty empty length 0 -8 0 12 -262144 -1 -1 4410 256; #X obj 414 405 nbx 5 24 0 44100 0 1 empty empty locate 0 -8 0 12 -262144 -1 -1 0 256; #X obj 349 354 *~; #X obj 350 405 +~; #X connect 0 0 7 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 9 0 15 0; #X connect 10 0 9 0; #X connect 12 0 11 0; #X connect 12 0 11 1; #X connect 13 0 15 1; #X connect 14 0 16 1; #X connect 15 0 16 0; #X connect 16 0 12 0;


tabread4~は音声信号を使ってテーブルのデータを再生する。
ここではphasor~を使っている。「freq」「length」「locate」の数値を変えるとどうなるか。


「length」はphasor~によって再生する音の長さ、「locate」は再生の開始位置と考えられる。
「freq」と「length」の両方によってピッチが変わるが、それぞれの数値の関係を考えると難しいので、とりあえず考えないでおく。


諸々の改良を施したのが次の例。

#N canvas 0 0 822 610 12; #X obj 44 106 *~; #X obj 62 70 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 43 26 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 122 26 pd wow; #X obj 122 106 *~; #X obj 140 70 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 44100 float 0; #X coords 0 1 44099 -1 200 140 1; #X restore 461 34 graph; #X obj 44 162 tabwrite~ array1; #X obj 201 89 bng 50 250 50 0 empty empty get 0 -9 0 12 -262144 -1 -1; #X obj 45 356 *~; #X obj 46 512 *~; #X obj 47 557 dac~; #X obj 45 396 +~; #X obj 73 319 s~ ph; #X obj 91 396 r~ loc; #X obj 64 472 r~ window; #X obj 411 297 noise~; #X obj 412 494 samphold~; #X obj 413 397 +~; #X obj 412 348 *~; #X obj 477 350 hsl 128 15 0 44100 0 0 empty empty random -2 -8 0 10 -261682 -1 -1 0 1; #X obj 478 399 hsl 128 15 0 44100 0 0 empty empty locate -2 -8 0 10 -261682 -1 -1 0 1; #X obj 471 454 r~ ph; #X obj 643 340 cos~; #X obj 644 381 -~ 1; #X obj 644 422 /~ -2; #X obj 642 301 r~ ph; #X obj 644 466 s~ window; #X obj 45 436 tabread4~ array1; #X obj 412 534 s~ loc; #X text 641 266 // window; #X text 411 258 // locate; #X obj 44 282 phasor~; #X obj 214 259 nbx 5 24 -1e+037 1e+037 0 1 empty empty freq 0 -8 0 12 -261682 -1 -1 50 256; #X obj 214 494 samphold~; #X obj 273 454 r~ ph; #X obj 259 360 hsl 128 15 -8 8 0 1 empty empty pitch -2 -8 0 10 -261682 -1 -1 7700 1; #X obj 214 308 expr 44100/$f1; #X obj 214 454 *; #X obj 256 401 t b f; #X obj 166 308 s fq; #X obj 44 247 r fq; #X obj 214 534 s~ len; #X obj 92 356 r~ len; #X text 211 210 // length; #X connect 0 0 7 0; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 7 0; #X connect 5 0 4 1; #X connect 8 0 7 0; #X connect 9 0 12 0; #X connect 10 0 11 0; #X connect 10 0 11 1; #X connect 12 0 28 0; #X connect 14 0 12 1; #X connect 15 0 10 1; #X connect 16 0 19 0; #X connect 17 0 29 0; #X connect 18 0 17 0; #X connect 19 0 18 0; #X connect 20 0 19 1; #X connect 21 0 18 1; #X connect 22 0 17 1; #X connect 23 0 24 0; #X connect 24 0 25 0; #X connect 25 0 27 0; #X connect 26 0 23 0; #X connect 28 0 10 0; #X connect 32 0 9 0; #X connect 32 0 13 0; #X connect 33 0 37 0; #X connect 33 0 40 0; #X connect 34 0 42 0; #X connect 35 0 34 1; #X connect 36 0 39 0; #X connect 37 0 38 0; #X connect 38 0 34 0; #X connect 39 0 38 0; #X connect 39 1 38 1; #X connect 41 0 32 0; #X connect 43 0 9 1;


非常に簡単なグラニュラー再生の例。
完全とは言えないが、phasor~の周波数によってピッチが変わらないようにしてある。
samphold~は、出力する値をphasor~が0に戻る時だけ更新する。
「random」で開始位置がランダムに変化する変化幅を変えられる。

音声ファイル

#N canvas 0 0 889 557 12; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 220480 float 0; #X coords 0 1 220479 -1 200 140 1; #X restore 523 52 graph; #X obj 523 354 soundfiler; #X obj 523 288 openpanel; #X obj 523 251 bng 24 250 50 0 empty empty open 0 -10 0 12 -262144 -1 -1; #X obj 190 227 writesf~; #X msg 364 171 open test.wav; #X msg 306 171 start; #X msg 190 171 stop; #X obj 318 61 t b b b; #X obj 318 25 bng 24 250 50 0 empty empty rec 0 -10 0 12 -262144 -1 -1; #X obj 235 432 readsf~; #X msg 331 378 open test.wav; #X obj 286 302 bng 24 250 50 0 empty empty play 0 -10 0 12 -262144 -1 -1; #X msg 286 378 1; #X obj 286 339 t b b; #X msg 235 378 0; #X obj 236 475 dac~; #X obj 26 150 +~; #X obj 26 107 *~; #X obj 44 71 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1 ; #X obj 25 27 adc~; #N canvas 0 0 482 383 wow 0; #X obj 67 203 phasor~; #X obj 69 77 samphold~; #X obj 68 34 noise~; #X obj 68 160 expr~ $v1*100+150; #X obj 130 34 phasor~ 5; #X obj 68 120 lop~ 2; #X obj 251 144 osc~ 2; #X obj 251 185 expr~ $v1*400+500; #X obj 229 242 vcf~ 10; #X obj 230 327 outlet~; #X obj 229 285 *~ 2; #X connect 0 0 8 0; #X connect 1 0 5 0; #X connect 2 0 1 0; #X connect 3 0 0 0; #X connect 4 0 1 1; #X connect 5 0 3 0; #X connect 6 0 7 0; #X connect 7 0 8 1; #X connect 8 0 10 0; #X connect 10 0 9 0; #X restore 104 27 pd wow; #X obj 104 107 *~; #X obj 122 71 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 26 204 dac~; #X floatatom 523 387 0 0 0 0 - - -; #X obj 707 288 tabplay~ array1; #X obj 707 251 bng 24 250 50 0 empty empty play 0 -10 0 12 -262144 -1 -1; #X obj 707 321 dac~; #X msg 523 420 \; array1 resize \$1; #X obj 190 138 bng 15 250 50 0 empty empty done 17 7 0 10 -262144 -1 -1; #X obj 190 100 delay 5000; #X msg 523 321 read -resize \$1 array1; #X text 197 377 stop; #X floatatom 257 25 5 0 0 2 length - -; #X connect 1 0 25 0; #X connect 2 0 32 0; #X connect 3 0 2 0; #X connect 5 0 4 0; #X connect 6 0 4 0; #X connect 7 0 4 0; #X connect 8 0 31 0; #X connect 8 1 6 0; #X connect 8 2 5 0; #X connect 9 0 8 0; #X connect 10 0 16 0; #X connect 10 0 16 1; #X connect 11 0 10 0; #X connect 12 0 14 0; #X connect 13 0 10 0; #X connect 14 0 13 0; #X connect 14 1 11 0; #X connect 15 0 10 0; #X connect 17 0 24 0; #X connect 17 0 24 1; #X connect 17 0 4 0; #X connect 18 0 17 0; #X connect 19 0 18 1; #X connect 20 0 18 0; #X connect 21 0 22 0; #X connect 22 0 17 1; #X connect 23 0 22 1; #X connect 25 0 29 0; #X connect 26 0 28 0; #X connect 26 0 28 1; #X connect 27 0 26 0; #X connect 30 0 7 0; #X connect 31 0 30 0; #X connect 32 0 1 0; #X connect 34 0 31 1;


writesf~は入力された音を録音して音声ファイルに保存する。
最初に「open」でファイルを作成し、「start」で録音を始める。
「stop」で録音を停止し、ファイルを完成する。
ここではdelayによって録音する長さを決められる。


readsf~は音声ファイルの再生を行なう。
「open」でファイルを開き、1と0でファイルを再生・中止する。


soundfilerはテーブルに音声ファイルを読み込む。
ここでは音声ファイルの長さに従い、「;array1 resize $1」でテーブルの長さを調整している。


tabplay~はテーブルのデータを無加工で再生する。

外部との連携(OpenSound Control:OSC)

OSCを使って、様々なアプリケーションの間で通信、連携することができる。
対応ライブラリの一覧 http://opensoundcontrol.org/implementations
対応アプリケーションは多岐に亘るが、今回はpdとSuperCollider(sc)との連携例を考える。


pdでは拡張オブジェクトを用いてOSCメッセージを扱うことができる。
pd-extendedでは拡張オブジェクトをそのまま使うことができる。
OSCに関するオブジェクトは、sendOSC、dumpOSC、OSCrouteの3種。


始めにpdからscへの送信、


SuperCollider側(予め実行しておく)

OSCresponder(nil, '/boo', { |t,r,msg|
msg.postln;
s.sendMsg(\s_new, "boo", -1, 0, 0, \freq, msg[1], \amp, msg[2]);
}).add;

SynthDef("boo", {|freq=1000,amp=1|
var
a = EnvGen.ar(Env.perc(0,1),doneAction:2);
b = SinOsc.ar(freq,0,a*amp);
Out.ar(0,b!2);
}).load(s);


pd側

#N canvas 0 0 852 623 12; #X obj 49 287 sendOSC; #X msg 152 245 disconnect; #X msg 49 37 connect localhost 57120; #X msg 133 204 send /boo \$1 \$2; #X msg 133 86 100; #X msg 144 120 800; #X msg 200 86 1; #X msg 211 120 0.5; #X obj 133 165 pack f 0.5; #X obj 48 548 sendOSC; #X msg 106 506 disconnect; #X msg 85 415 send /s_new boo -1 0 0 freq 300 amp 0.7; #X msg 48 362 connect localhost 57110; #X msg 86 453 send /s_new boo -1 0 0 freq 350 amp 0.2; #X text 243 36 <-- to SCLang; #X text 236 361 <-- to SCSynth; #X obj 528 120 dumpOSC 8000; #X msg 512 307 1 \, 0 1000; #X obj 512 354 line~; #X obj 441 307 osc~; #X obj 442 354 *~; #X obj 443 398 *~; #X obj 559 450 dac~; #X obj 528 192 OSCroute /moo /noo; #X obj 460 192 print; #X obj 441 263 unpack f f; #X msg 730 307 1 \, 0 1000; #X obj 730 354 line~; #X obj 659 307 osc~; #X obj 660 354 *~; #X obj 661 398 *~; #X obj 659 263 unpack f f; #X text 641 120 <-- from SC; #X connect 1 0 0 0; #X connect 2 0 0 0; #X connect 3 0 0 0; #X connect 4 0 8 0; #X connect 5 0 8 0; #X connect 6 0 8 1; #X connect 7 0 8 1; #X connect 8 0 3 0; #X connect 10 0 9 0; #X connect 11 0 9 0; #X connect 12 0 9 0; #X connect 13 0 9 0; #X connect 16 0 24 0; #X connect 16 0 23 0; #X connect 17 0 18 0; #X connect 18 0 21 1; #X connect 19 0 20 0; #X connect 20 0 21 0; #X connect 21 0 22 0; #X connect 23 0 25 0; #X connect 23 1 31 0; #X connect 25 0 19 0; #X connect 25 0 17 0; #X connect 25 1 20 1; #X connect 26 0 27 0; #X connect 27 0 30 1; #X connect 28 0 29 0; #X connect 29 0 30 0; #X connect 30 0 22 1; #X connect 31 0 28 0; #X connect 31 0 26 0; #X connect 31 1 29 1;


sendOSCオブジェクトは、pdからOSCを送信する。
「connect localhost 57120」をクリックしてSCLangに接続する。
(SCLangのポート番号は通常57120)


次にpackの上の「100」「800」をクリックするとscから音が出る。
また「1」「0.5」で音量が変わる。
「send /boo $1 $2」のsend以降がOSCメッセージの内容になる。
sc側のsynthにOSCメッセージとして周波数と音量を送っている。


送られたOSCメッセージはscのコンソールに表示される。
sc側はOSCresponderでメッセージを受信している。
msg[n]で値を取り出している。受けた値を用いてs.sendMsgでSCSynthにメッセージを送信。


「disconnect」をクリックして接続を解除する。


scの場合、SCSynthで直接メッセージを受けることもできる。
(SCLangとSCSynthとの通信にもOSCが使われている)
「connect localhost 57110」をクリックしてSCSynthに接続する。
(SCSynthのポート番号は通常57110)
メッセージの書式はscのコマンドに合わせる必要がある。


次にscからpdへの送信。scで以下を実行する。

~pd = NetAddr("localhost", 8000);

OSCresponder(nil, '/foo', { |t,r,msg|
msg.postln;
~pd.sendMsg("/moo",msg[3],msg[5]);
~pd.sendMsg("/noo",msg[4],msg[5]);
}).add;

SynthDef("foo", {
var
a = Impulse.kr(1);
b = { Latch.kr(LFNoise0.kr(1,300,400),a) };
c = Latch.kr(LFNoise0.kr(1),a);
SendReply.kr(a,'/foo',[b.(),b.(),c]);
}).play;

dumpOSCオブジェクトでOSCメッセージを受信する。引数はpdのポート番号(通常は8000)。
OSCrouteオブジェクトでメッセージを振り分けている。
routeと同様に先頭の値で出力先を決定。


sc側では、まずNetAddrで送信先アドレスとポート番号を指定し、sendMsgで送信している。
synthを用いてメッセージを送出するには、まずSendReplyでOSCresponderに値を送り
OSCresponderから外部へ送信する、という2段階になる。

pdをやりなおす(3)

制御オブジェクト

#N canvas 0 0 450 300 12; #X obj 200 71 key; #X floatatom 119 127 5 0 0 0 - - -; #X obj 200 131 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 200 169 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X floatatom 200 207 5 0 0 0 - - -; #X obj 278 127 > 53; #X obj 278 169 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X connect 0 0 1 0; #X connect 0 0 2 0; #X connect 0 0 5 0; #X connect 2 0 3 0; #X connect 3 0 4 0; #X connect 5 0 6 0;

keyはキーボードからの入力を文字コードで出力する。
文字コードを左側のナンバーボックスで見ることができる。


次に、中央の列にbangがある。
bangは数値等の値を受けて、その内容に関係なくbangとして出力する。


bangの下にトグルが続いているが、もし何かの値を受けてスイッチしたい場合は、このように手前にbangを置く必要がある。
トグルはintと同様に数値を格納している(トグルの下のナンバーボックスを確認)。


右側の「> 53」は比較演算子である。
この場合、53より大きい数値を受けると1(真)、そうでなければ0(偽)を返す。
キー入力では、例えば0〜5のキーで偽となる。


比較演算子は他にもいくつかある。

#N canvas 0 0 450 300 12; #X obj 205 108 ==; #X obj 261 49 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 205 146 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 187 185 *~; #X obj 261 146 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 243 185 *~; #X obj 204 230 dac~; #X obj 261 108 !=; #X obj 118 146 osc~ 440; #X connect 0 0 2 0; #X connect 1 0 0 0; #X connect 1 0 7 0; #X connect 2 0 3 1; #X connect 3 0 6 0; #X connect 4 0 5 1; #X connect 5 0 6 1; #X connect 7 0 4 0; #X connect 8 0 3 0; #X connect 8 0 5 0;


比較演算子を使ったゲートの例。
一番上のトグルを繰り返しクリックすると左右のチャンネルに切り替わる。
「==」は二つの値が等しい場合、「!=」は二つの値が異なる場合に1を返す。
ここでは引数を省略しているので、それぞれ「== 0」「!= 0」の意味になる。

#N canvas 0 0 586 427 12; #X obj 290 56 hradio 15 1 0 4 empty empty empty 0 -8 0 10 -262144 -1 -1 0; #X obj 158 248 *~; #X obj 108 119 noise~; #X obj 108 172 lop~ 500; #X obj 272 248 *~; #X obj 389 248 *~; #X obj 222 172 hip~ 500; #X obj 339 172 bp~ 500 10; #X obj 249 340 dac~; #X obj 273 296 /~ 4; #X obj 176 208 == 1; #X obj 290 208 == 2; #X obj 407 208 == 3; #X floatatom 378 103 5 0 0 0 - - -; #X connect 0 0 10 0; #X connect 0 0 11 0; #X connect 0 0 12 0; #X connect 0 0 13 0; #X connect 1 0 9 0; #X connect 2 0 3 0; #X connect 2 0 6 0; #X connect 2 0 7 0; #X connect 3 0 1 0; #X connect 4 0 9 0; #X connect 5 0 9 0; #X connect 6 0 4 0; #X connect 7 0 5 0; #X connect 9 0 8 0; #X connect 9 0 8 1; #X connect 10 0 1 1; #X connect 11 0 4 1; #X connect 12 0 5 1;


同じく比較演算子によるゲートの例。
一番上にあるのはラジオボタン。プロパティからボタンの個数を設定できる。
ボタンは整数を出力する。ボタン操作で3種のフィルタを切り替えて聴き比べられる。

#N canvas 0 0 450 300 12; #X obj 155 70 key; #X obj 155 121 select 49 50 51; #X obj 155 170 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 155 208 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 189 170 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 189 208 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 223 170 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 223 208 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X floatatom 257 170 5 0 0 0 - - -; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 1 1 4 0; #X connect 1 2 6 0; #X connect 1 3 8 0; #X connect 2 0 3 0; #X connect 4 0 5 0; #X connect 6 0 7 0;


selectは引数にある値と同じ値を受けた場合に、対応するアウトレットからbangを出力する。
右端のアウトレットは、引数に含まれない値を受けた場合に、これを返す。
この例では1,2,3のキーをタイプすることでトグルを操作できる。

#N canvas 0 0 450 300 12; #X obj 94 179 print; #X floatatom 94 87 5 0 0 0 - - -; #X obj 147 87 key; #X obj 149 179 s hoge; #X obj 253 87 r hoge; #X obj 253 134 unpack f i; #X obj 94 134 pack f i; #X floatatom 253 179 5 0 0 0 - - -; #X floatatom 320 179 5 0 0 0 - - -; #X connect 1 0 6 0; #X connect 2 0 6 1; #X connect 4 0 5 0; #X connect 5 0 7 0; #X connect 5 1 8 0; #X connect 6 0 0 0; #X connect 6 0 3 0;


packは複数の値をまとめ、unpackはまとめられた値を分解する。
引数で値の形式を指定する。


まずナンバーボックスをドラッグすると、コンソールに数値が表示される。
表示はふたつの数値の組になっている。ふたつ目の数値はキー入力に従う。
右インレットはcoldなので、左インレットに入力があった時だけ出力することがわかる。


また、packの出力は「s hoge」から「r hoge」に流れている(それぞれsend、receiveの省略形)。
unpackのそれぞれのアウトレットから分解された値が出力される。

#N canvas 0 0 450 300 12; #X obj 185 121 pack i 1000; #X obj 185 170 line; #X floatatom 185 221 5 0 0 0 - - -; #X obj 185 71 key; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 3 0 0 0;


packとlineの組み合わせ例。
lineは数値の組を受け取って動作するが、ここではpackから直接受けている。
キー入力するとその文字コードの値まで、1秒をかけて移動する。

#N canvas 0 0 450 300 12; #X obj 163 132 pack i f; #X obj 163 175 route 49 50; #X obj 163 49 key; #X floatatom 113 94 5 0 0 0 - - -; #X floatatom 163 224 5 0 0 0 - - -; #X floatatom 212 224 5 0 0 0 - - -; #X floatatom 267 224 5 0 0 0 - - -; #X floatatom 228 49 5 0 0 0 - - -; #X obj 228 89 t b f; #X connect 0 0 1 0; #X connect 1 0 4 0; #X connect 1 1 5 0; #X connect 1 2 6 0; #X connect 2 0 0 0; #X connect 2 0 3 0; #X connect 7 0 8 0; #X connect 8 0 0 0; #X connect 8 1 0 1;


routeも値の組を受け取って動作する。
最初の値に従って出力するアウトレットを変え、2番目以降の値を振り分ける。
ここでは1か2のキーで出力先を変えている。
「t b f」の上のナンバーボックスをドラッグすると、対応するアウトレットから出力する。

#N canvas 0 0 450 300 12; #X floatatom 303 194 5 0 0 0 - - -; #X obj 303 111 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X floatatom 372 194 5 0 0 0 - - -; #X obj 372 111 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X floatatom 48 194 5 0 0 0 - - -; #X floatatom 48 73 5 0 0 0 - - -; #X floatatom 122 194 5 0 0 0 - - -; #X floatatom 122 73 5 0 0 0 - - -; #X obj 48 111 s xx; #X obj 48 152 r xx; #X obj 122 111 s xx; #X obj 122 152 r xx; #X obj 303 152 v yy; #X obj 372 152 v yy; #X floatatom 223 73 5 0 0 0 - - -; #X obj 223 111 v yy; #X connect 1 0 12 0; #X connect 3 0 13 0; #X connect 5 0 8 0; #X connect 7 0 10 0; #X connect 9 0 4 0; #X connect 11 0 6 0; #X connect 12 0 0 0; #X connect 13 0 2 0; #X connect 14 0 15 0;


数値等の値を扱うsendはsend~と異なり、同じ名前のものを複数作れる。


次に、value(vは省略形)はいわばプログラムにおける変数の役割をする。
send、receiveと似ているが、bangを受けた時だけ値を出力する。
また単体で格納も呼び出しも行なう。

#N canvas 0 0 450 300 12; #X obj 155 208 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 206 71 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 262 209 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 262 157 *; #X obj 155 156 spigot; #X obj 194 123 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X connect 1 0 3 0; #X connect 1 0 4 0; #X connect 3 0 2 0; #X connect 4 0 0 0; #X connect 5 0 4 1;


bangは特殊な値であり、例えばこのように、0を掛け算して遮断する、ということができない。
spigotは右インレットの値に従ってbangを遮断することができる。
トグルのオン・オフでbangの流れが変わるのを確認する。

#N canvas 0 0 450 300 12; #X floatatom 167 70 0 0 0 0 - - -; #X obj 72 70 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 100 118 delay 1000; #X obj 72 174 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X floatatom 345 70 0 0 0 0 - - -; #X obj 246 70 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 278 118 delay 1000; #X obj 246 174 timer; #X floatatom 246 212 5 0 0 0 - - -; #X connect 0 0 2 1; #X connect 1 0 2 0; #X connect 1 0 3 0; #X connect 2 0 3 0; #X connect 4 0 6 1; #X connect 5 0 6 0; #X connect 5 0 7 0; #X connect 6 0 7 1; #X connect 7 0 8 0;


delayは値を受け取った時点から、引数で指定された時間後にbangを遅れて返す。
時間指定は右インレットから変更できる。
ここでは、まず最初にトグルに直接bangが入り、次にdelayから再びbangが入る。
従って一定時間だけトグルがオンになる仕組みになっている。


timerはふたつのbangの時間間隔を測る。

#N canvas 0 0 450 300 12; #X floatatom 175 75 0 0 0 0 - - -; #X obj 115 122 pipe 1000; #X floatatom 115 75 5 0 0 0 - - -; #X floatatom 115 171 5 0 0 0 - - -; #X obj 261 121 until; #X obj 261 164 i; #X obj 313 164 + 1; #X msg 261 74 5; #X floatatom 261 217 5 0 0 0 - - -; #X connect 0 0 1 1; #X connect 1 0 3 0; #X connect 2 0 1 0; #X connect 4 0 5 0; #X connect 5 0 6 0; #X connect 5 0 8 0; #X connect 6 0 5 1; #X connect 7 0 4 0;


pipeは入力された値を、指定した時間後に遅れて返す。
時間指定は右インレットから変更できる。
ナンバーボックスをドラッグすると、同じ動きがそのまま遅延して出力されるのが確認できる。


untilは入力された数だけ繰り返しbangを出力する。繰り返しは一瞬で行なわれる。
もし引数も停止機構もなしにbangを与えると一瞬で膨大なbangが生成され、PCがフリーズするので注意。

#N canvas 0 0 450 300 12; #X obj 158 140 openpanel; #X symbolatom 158 184 0 0 0 0 - - -; #X obj 158 97 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X msg 214 95 symbol C:/; #X connect 0 0 1 0; #X connect 2 0 0 0; #X connect 3 0 0 0;


openpanelは「ファイルを開く」ダイアログを表示、選択したファイルのパスを出力する。
soundfilerにパスを与えて音声ファイルを読み込む、などの用法がある。

#N canvas 0 0 450 300 12; #X msg 233 127 \; pd dsp \$1; #X obj 233 78 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 130 78 noise~; #X obj 132 176 dac~; #X obj 131 123 /~ 10; #X connect 1 0 0 0; #X connect 2 0 4 0; #X connect 4 0 3 0; #X connect 4 0 3 1;


メッセージボックスを使った小技。トグル操作でpdのオーディオ処理を開始・中止できる。
「; pd dsp $1」のうち$1が変数であり、トグルの値が代入される。


このようにpdでは$記号が変数の印として随所に用いられる。変数の詳細は以下を参照。
File>Open>doc>2.control.examples>14.dollarsigns.pd


pdのexampleファイルには、これと同様のメッセージをloadbangで叩いて、パッチを開くと同時にオーディオを開始するものもある。
ただし自作のパッチにおいて、バグ取りが完全でない場合は使うべきではないと考える。


プログラムとは、単に上から実行するだけでなく、同じ処理を繰り返したり、条件によって処理を変えたりする(制御構造)。
こうした機能を持つものを仮に制御オブジェクトと呼ぶなら、pdのオブジェクトの大部分が制御オブジェクトと言える。
(音を出すオブジェクトはosc~、phasor~、noise~の3種のみ)
今回はその一部の、最も基本的なものだけを示したが、ほとんどの処理は以上で作ることができると考える。

pdをやりなおす(2)

効率的なプログラミング

#N canvas 0 0 450 300 12; #N canvas 0 0 450 300 hoge 0; #X obj 97 46 inlet; #X obj 232 468 dac~; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X connect 0 0 8 0; #X connect 2 0 1 0; #X connect 2 0 1 1; #X connect 3 0 6 0; #X connect 3 0 2 1; #X connect 4 0 2 0; #X connect 5 0 3 0; #X connect 6 0 7 1; #X connect 7 0 10 0; #X connect 8 0 9 0; #X connect 8 0 11 0; #X connect 8 0 5 0; #X connect 9 0 7 0; #X connect 10 0 4 0; #X connect 11 0 10 1; #X restore 193 149 pd hoge; #X floatatom 193 104 5 0 0 0 - - -; #X connect 1 0 0 0;


ナンバーボックスと「pd hoge」しか見えないが、実は先程のパッチと同じ機能を持つ。
「pd hoge」を右クリック>Openすると中のサブパッチを見ることができる。


パッチの一部をサブパッチとして小分けにするとプログラミングがしやすくなる。
サブパッチはpdオブジェクトで作成する。引数のhogeは適当な名前である。
サブパッチへの入力はinlet、inlet~、出力はoutlet、outlet~で行なう。

#N canvas 0 0 450 300 12; #N canvas 0 0 1024 692 out 0; #X obj 173 339 dac~; #X obj 174 287 *~; #X obj 191 241 lop~ 5; #X obj 114 241 inlet~; #X obj 191 198 inlet; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X restore 174 181 pd out; #X obj 263 51 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #N canvas 0 0 560 558 hoge 0; #X obj 97 46 inlet; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X obj 232 468 outlet~; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1; #X restore 173 133 pd hoge; #X floatatom 173 88 5 0 0 0 - - -; #X connect 1 0 0 1; #X connect 2 0 0 0; #X connect 3 0 2 0;


音量制御の部分を「pd out」にまとめた。

#N canvas 0 0 450 300 12; #N canvas 0 0 1024 692 out 0; #X obj 173 339 dac~; #X obj 174 287 *~; #X obj 191 241 lop~ 5; #X obj 191 198 inlet; #X obj 175 158 receive~ moge; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 2 0; #X connect 4 0 1 0; #X restore 230 169 pd out; #X obj 230 52 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #N canvas 0 0 560 558 hoge 0; #X obj 97 46 inlet; #X obj 233 416 *~; #X obj 251 181 line~; #X obj 98 367 osc~; #X msg 251 142 1 \, 0 400; #X obj 115 242 expr~ pow($v1 \, 8); #X obj 96 289 *~; #X floatatom 97 93 5 0 0 0 - - -; #X obj 97 196 osc~; #X obj 97 329 *~; #X obj 150 289 * 8; #X obj 232 468 send~ moge; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1; #X restore 155 169 pd hoge; #X floatatom 155 124 5 0 0 0 - - -; #X connect 1 0 0 0; #X connect 3 0 2 0;


ふたつのサブパッチは結線していないが、先程の例と同じ機能を持つ。
send~、receive~を使って信号を渡している。これらはサブパッチに限らずどこでも使える。
引数に同じ名前を持つsend~とreceive~で信号が流れる。
同じ名前を持つsend~が複数あるとエラーとなる。


次の例は予め三つのpdファイルを作成しておく。


(hoge.pd)

#N canvas 0 0 449 595 12; #X obj 107 56 inlet; #X obj 243 426 *~; #X obj 261 191 line~; #X obj 108 377 osc~; #X msg 261 152 1 \, 0 400; #X obj 125 252 expr~ pow($v1 \, 8); #X obj 106 299 *~; #X floatatom 107 103 5 0 0 0 - - -; #X obj 107 206 osc~; #X obj 107 339 *~; #X obj 160 299 * 8; #X obj 243 476 outlet~; #X connect 0 0 7 0; #X connect 1 0 11 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 10 0; #X connect 7 0 4 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 10 0 9 1;


(out.pd)

#N canvas 0 0 450 300 12; #X obj 160 233 dac~; #X obj 161 181 *~; #X obj 178 135 lop~ 5; #X obj 178 92 inlet; #X obj 162 52 inlet~; #X connect 1 0 0 0; #X connect 1 0 0 1; #X connect 2 0 1 1; #X connect 3 0 2 0; #X connect 4 0 1 0; #X coords 0 0 1 1 85 60 0;


(main.pd)

#N canvas 0 0 450 300 12; #X obj 219 52 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X floatatom 156 84 5 0 0 0 - - -; #X obj 156 129 hoge; #X obj 201 172 out; #X connect 0 0 3 1; #X connect 1 0 2 0; #X connect 2 0 3 0;


main.pdを開くと、やはり先程と同じ機能のパッチであるが、
先程までサブパッチとしていたものを外部パッチとして呼び出している。
外部パッチとして独立させることで、同じ機能を他のパッチでも使い回せるようになる。


(mohe.pd)

#N canvas 0 0 449 595 12; #X obj 107 56 inlet; #X obj 243 426 *~; #X obj 261 191 line~; #X obj 108 377 osc~; #X msg 261 152 1 \, 0 400; #X obj 125 252 expr~ pow($v1 \, 8); #X obj 106 299 *~; #X floatatom 107 103 5 0 0 0 - - -; #X obj 107 206 osc~; #X obj 107 339 *~; #X obj 243 476 outlet~; #X obj 160 299 * \$1; #X connect 0 0 7 0; #X connect 1 0 10 0; #X connect 2 0 5 0; #X connect 2 0 1 1; #X connect 3 0 1 0; #X connect 4 0 2 0; #X connect 5 0 6 1; #X connect 6 0 9 0; #X connect 7 0 8 0; #X connect 7 0 4 0; #X connect 7 0 11 0; #X connect 8 0 6 0; #X connect 9 0 3 0; #X connect 11 0 9 1;


(main.pd)

#N canvas 0 0 450 300 12; #X obj 223 58 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X floatatom 160 95 5 0 0 0 - - -; #X obj 160 140 mohe 20; #X obj 205 183 out; #X connect 0 0 3 1; #X connect 1 0 2 0; #X connect 2 0 3 0;


外部パッチに引数を与えることで、同じパッチに異なるふるまいをさせることができる。
mohe.pdに「* $1」とあるが、この$1で引数を受け取っている。
引数が複数の場合は、$2、$3と続く。

カウンタ

1ずつ加算する処理(インクリメント)はプログラミングでは多用されるが、
pdでは以下のようにして行なう。いわゆるカウンタと呼ばれる。

#N canvas 0 0 450 300 12; #X obj 173 89 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X obj 173 134 i; #X obj 221 134 + 1; #X floatatom 173 180 5 0 0 0 - - -; #X connect 0 0 1 0; #X connect 1 0 2 0; #X connect 1 0 3 0; #X connect 2 0 1 1;


一番上のスイッチはbangと呼ぶ。クリックや、数値等の値などを受け取ることでbangメッセージを発する。
bangは様々なオブジェクトが動作するきっかけに使われる。


iオブジェクトは整数を扱うintの省略形表記である。pdのオブジェクトにはこうした省略形がいくつかある。


この奇妙なパッチをよく観察してみる。
intの出力は+に入力され1を加算される。+の出力はintの右インレットに戻される。
intの値はbangをクリックするたびに1ずつ加算する。


このpd独特の仕組みを理解するには以下の例を見てみる。

#N canvas 0 0 450 300 12; #X obj 85 149 i; #X floatatom 85 190 5 0 0 0 - - -; #X floatatom 60 106 5 0 0 0 - - -; #X floatatom 103 106 5 0 0 0 - - -; #X obj 196 149 i; #X floatatom 196 190 5 0 0 0 - - -; #X floatatom 214 106 5 0 0 0 - - -; #X obj 181 106 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; #X floatatom 324 190 5 0 0 0 - - -; #X floatatom 276 84 5 0 0 0 - - -; #X floatatom 324 84 5 0 0 0 - - -; #X obj 324 118 t b i; #X obj 324 149 +; #X text 90 231 [1]; #X text 203 231 [2]; #X text 313 231 [3]; #X connect 0 0 1 0; #X connect 2 0 0 0; #X connect 3 0 0 1; #X connect 4 0 5 0; #X connect 6 0 4 1; #X connect 7 0 4 0; #X connect 9 0 12 0; #X connect 10 0 11 0; #X connect 11 0 12 0; #X connect 11 1 12 1; #X connect 12 0 8 0;


数値等の値を扱ういくつかのオブジェクトには、インレットの優先順位がある。
その場合、左をhot、右をcoldと呼ぶ
[1]の例では左のナンバーの値はすぐにintから出力される一方、右のナンバーの値は出力されない。
しかしintの中には右のナンバーの値が格納されている。


[2]の例では、左インレットにbangを入力することで、右インレットから格納された値が、intから出力される。
ふたつのインレットを使って、いかなる時も足し算ができるようにするには[3]のようにしなければならない。


tはtrigger、bはbangの省略形。
このtriggerオブジェクトは右側のアウトレットから順に、引数で指定された形式(ここではintとbang)で出力する。
(なおpdでの数値等の出力順は必ず右から左である)
つまり最初にtriggerに入力された数値、次にbangを出力する。
bangを受け取った+は即座に足し算の結果を返す。
先程のカウンタも、インクリメントの結果をbangに応じて出力していた。


ふたたびカウンタに戻る。

#N canvas 0 0 450 300 12; #X obj 178 137 i; #X obj 226 137 + 1; #X floatatom 178 231 5 0 0 0 - - -; #X obj 178 58 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 178 97 metro 100; #X connect 0 0 1 0; #X connect 0 0 2 0; #X connect 1 0 0 1; #X connect 3 0 4 0; #X connect 4 0 0 0;


metroは引数で指定された時間間隔でbangを繰り返し出力する。
「metro 100」は100ms(0.1秒)間隔でbangを出力する。
一番上のトグルをオンにすることで動作が始まる。

#N canvas 0 0 450 300 12; #X obj 190 133 i; #X obj 238 133 + 1; #X floatatom 190 227 5 0 0 0 - - -; #X obj 190 54 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 190 93 metro 100; #X obj 190 180 % 16; #X connect 0 0 1 0; #X connect 0 0 5 0; #X connect 1 0 0 1; #X connect 3 0 4 0; #X connect 4 0 0 0; #X connect 5 0 2 0;


カウンタの出力に%をつなげる。
%とはモジュロ演算と呼ばれるもので、その結果、数値は一定の範囲での繰り返しになる。

#N canvas 0 0 667 300 12; #X floatatom 69 248 5 0 0 0 - - -; #X obj 324 106 vsl 15 101 0 1 0 0 empty empty empty 0 -9 0 10 -262130 -1 -1 0 1; #X obj 201 111 * 1000; #X obj 201 151 mohe 20; #X obj 69 198 tabread array1; #N canvas 0 0 450 300 (subpatch) 0; #X array array1 16 float 3; #A 0 0.435721 0.0785732 0.707154 0.178575 0.378578 0.100002 0.0500014 0.0642873 0.0357155 0.557152 0.814299 0.107145 0.0642876 0.0285727 0.257148 0.164289; #X coords 0 1 16 0 200 140 1; #X restore 404 64 graph; #X obj 69 104 i; #X obj 117 104 + 1; #X obj 69 25 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; #X obj 69 64 metro 100; #X obj 69 151 % 16; #X obj 306 229 out; #X connect 1 0 11 1; #X connect 2 0 3 0; #X connect 3 0 11 0; #X connect 4 0 0 0; #X connect 4 0 2 0; #X connect 6 0 7 0; #X connect 6 0 10 0; #X connect 7 0 6 1; #X connect 8 0 9 0; #X connect 9 0 6 0; #X connect 10 0 4 0;


これまでの例を総合して簡単なシーケンサを作成。
array1と書かれたテーブルは配列オブジェクトである。


先程のカウンタの値をインデックスとしてtabreadに与えることで、配列の値を読み出している。
配列テーブルは複数のスライダの集まりのように操作できる。
線をドラッグするとフレーズが変化する(ドラッグには少々コツが要る)。