とりあえずosc4nodeとSC3


そろそろブラウザからもSCにOSCメッセージ送りたいですよね?それならosc4node使いましょう!すごい簡単にOSCメッセージの送受信が実装できます、素晴らしい!!

準備

Node.jsの環境構築などはググれば沢山ありますので、割愛。
osc4nodeについては
hideyukisaitoさんのblog
osc4node – GitHub

使う

SC側

とりあえず、OSCメッセージの送受信を実装しましょう。

/*
*	osc4node test
*/
/*
*	osc setting
*/
(
	//receive osc messages from node
	~oscReceive = NetAddr("127.0.0.1", 57120);
	o = OSCresponder(nil, '/sc3/synth', {
		arg t, r, msg;
		//msg[1]にシンセ名を送っているため、ここで分岐
		switch(msg[1],
			//kickの場合
			\osc4node_kick, { s.sendMsg("/s_new",msg[1],s.nextNodeID,0,0 ); },
			//sinの場合
			\osc4node_sinosc, {
				//シンセ作成、価変更、解放の分岐
				switch(msg[2],
					\play, { s.sendMsg("/s_new",msg[1],x = s.nextNodeID,0,0); },
					\set, { s.sendMsg("/n_set", x, "freq", msg[3]); },
					\free, { s.sendMsg("/n_free", x); }
				);
			}
		);
		[t,msg].postln;
	}).add;

	// send osc messages 2 node
	~oscSend = NetAddr("127.0.0.1", 57124);
	//osc4node_kick1のSendReply.krにて一旦サーバ側へOSCメッセージ送信。ここでメッセージを受信する
	OSCresponder(nil, '/scServer/4node', { 
		arg t, r, msg;
		//ここでnode側にメッセージ送信
		~oscSend.sendMsg('/sc3/2node', msg[3]);
		[t,msg].postln;		
	}).add;
	
	//check
	NetAddr.localAddr;
)

/*
*	synth def
*/
(
	SynthDef("osc4node_kick",{
		arg pan = 0, outBus = 0, clip_amp = 1.0, white_amp=0.0, filter_freq=1.0;
		var env_mul, env_freq, signal, outSend;

		env_mul = Env.new([0.5, 1, 0.5, 0], [0.005, 0.06, 0.26], [-4, -2, -4]);
		env_mul = EnvGen.ar(env_mul ,doneAction:2);
		env_freq = Env.new([110, 59, 29], [0.005, 0.29], [-4, -5]);
		env_freq = EnvGen.ar(env_freq).midicps;
		
		signal = SinOsc.ar(env_freq, 0, env_mul);
		signal = signal + PinkNoise.ar(white_amp);
		signal = LPF.ar(signal, env_freq*filter_freq, env_mul);
		signal = signal + SinOsc.ar(env_freq,0.5,env_mul);
		outSend = signal * clip_amp;
		outSend.clip2(1);
		//一旦scサーバに値を送る
		SendReply.kr(Impulse.kr(20), "/scServer/4node",[outSend.()]);

		//outSend.poll(Impulse.ar(20),"/sc3/4node",1);
		//pollで/trにugenの出力値を送ったりもできるけれど、受信後の処理が面倒かも

		Out.ar(outBus,
			Pan2.ar(outSend, pan)
		);
	}).store;

	SynthDef("osc4node_sinosc",{
		arg pan = 0, outBus = 0, freq = 440;
		var signal;
		signal = SinOsc.ar(freq,0,0.4);
		Out.ar(outBus,
			Pan2.ar(signal,pan);
		);
	}).store;
)
/*
*	osc clear ?
*/
OSCresponder.initClass;
Nodeサーバ側

osc4nodeの詳しい使い方は、@hideyukisaitoさんに直接聞くか、READMEとexampleを参照しましょう。socket.ioのバージョンによってちょっと書き方が違うっぽいです。
express使っていますので、app.jsとかです。

/**
 * osc4node test by ksy.
 */

var express = require('express'),
	socketIO = require('socket.io'),
	osc = require('osc4node');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});

app.configure('production', function(){
  app.use(express.errorHandler()); 
});

// Routes

app.get('/', function(req, res){
  res.render('index', {
    title: 'osc4node for sc3'
  });
});

app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

// osc4node setting
var oscserver = new osc.Server(57124,'127.0.0.1');
var oscclient = new osc.Client('127.0.0.1',57120);

//socket.io
var io = socketIO.listen(app);

////connection
io.sockets.on('connection', function(socket){
	console.log("connection");
	socket.on('message', function(data){
		console.log(data);
		io.sockets.emit('message', { value :data.value });
	});

	//oscmessage send 2 sc
	socket.on('oscmessage', function(data){
		io.sockets.emit('oscmessage', { address: data.address,message: data.message });
		var mes = new osc.Message(data.address,data.message);
		oscserver.send(mes, oscclient);
	});

	//oscmessage receive from sc
	oscserver.on('oscmessage', function(msg, rinfo){
		io.sockets.emit('oscreceive',{
			address: msg._address,
			typetag: msg._typetag,
			value:	msg._args[0].value,
			args: msg.argments
		});
	});
	
	//disconnect
	socket.on('disconnect', function(){
		console.log("disconnection");
	});
});
Nodeクライアント側

./public/javascript/xx.jsとか。かなり適当。スライダー使うため、途中でjQueryに切り替えてます。。。これに合わせて、view/*.ejsも作成します。

$(function(){
//自分の環境に
var socket = io.connect('xxx.xxx.xxx.xxx:3000');

socket.on('connect', function(msg){
	console.log("client: connection");
	document.getElementById("connectMsg").innerHTML = "your sessisonID is:" + socket.socket.transport.sessid;
});

socket.on('message', function(msg){
	document.getElementById("receiveMsg").innerHTML = msg.value;
});
socket.on('oscmessage', function(msg){
	document.getElementById("receiveMsg").innerHTML = "address from client:" + msg.address + "| message:" +  msg.message;
});

// receive message from sc
socket.on('oscreceive', function(msg){
	osc_address = msg.address;
	switch(osc_address){
	case "/sc3/test":
		$('#receiveFromSc').html("address from sc:" + msg.address + " | value: " + msg.value);
		break;
	case "/sc3/2node":
		$('#receiveFromSc').html("address from sc:" + msg.address + " | value: " + msg.value);
		break;
	default:
		break;
	}
});

//send osc message 2 SC
$('#playKick').click(function(){
	socket.send('message', { message: "test send"});
	socket.emit('oscmessage', { address: "/sc3/synth",message: [ "osc4node_kick","play"] });
});
$('#playSin').click(function(){
	socket.emit('oscmessage', { address: "/sc3/synth",message: [ "osc4node_sinosc","play"] });
});
$('#freeSin').click(function(){
	socket.emit('oscmessage', { address: "/sc3/synth",message: [ "osc4node_sinosc","free"] });
});
function discon(){
	socket.emit('message', { value: "client: discon" });
	socket.disconnect();
};

//UIs
$(".slider").slider({
	max:880,
	min:440,
	step:10,
	value:440,
	orientation:'vertical',
	slide: function( event, ui ){
		$("#slideAmount").val(ui.value);
		socket.emit('oscmessage', { address: "/sc3/synth",message: [ "osc4node_sinosc","set", ui.value] });
	}
});
$("#slideAmount").val($(".slider").slider("value"));
});

いじょう

とりあえず暫定で。
canvasとかを使用していかしたGUI作ってから載せようと思いましたが、面倒なので全然いじっていません。exampleにいかしたGUIサンプルがあります!
これでブラウザからoscの送受信がさくっとできますので、あとはweb audio apiかなんかでストリーミングできれば面白いかとー。

たとえば、

  • ライブ会場とかでお客さんにiPhoneとか使ってパラメータいじってもらうにはクライアントソフトが必要でした。しかし、これならブラウザがあればおkですから準備不要ですね。
  • 数人でライブする場合、リアルタイムで値が反映されますので状況把握しやすいかと。
  • SCならlinuxでも動きますので、サーバ建ててオンラインで色々できそうですね!

SC3 こーどりーでぃんぐ2

前回に続いて、snare,hatを読んでみよう。わからないところはさらっとすっ飛ばしていますよ。

snare 08091500Acid309 by_otophiliaより

SynthDef("snare", {	
	arg outBus=0, amp=0.8;
	var env0, env1, env2, env1m, oscs, noise, out;
	
	env0 = EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.03, 0.10], [-4, -2, -4]));
	env1 = EnvGen.ar(Env.new([110, 60, 49], [0.005, 0.1], [-4, -5]));
	env1m = env1.midicps;
	env2 = EnvGen.ar(Env.new([1, 0.4, 0], [0.05, 0.13], [-2, -2]), doneAction:2);
		
	oscs = LFPulse.ar(env1m, 0, 0.5, 1, -0.5) + LFPulse.ar(env1m * 1.6, 0, 0.5, 0.5, -0.25);
	oscs = LPF.ar(oscs, env1m*1.2, env0);
	oscs = oscs + SinOsc.ar(env1m, 0.8, env0);
		
	noise = WhiteNoise.ar(0.2);
	noise = HPF.ar(noise, 200, 2);
	noise = BPF.ar(noise, 6900, 0.6, 3) + noise;
	noise = noise * env2;
		
	out = oscs + noise;
	out = out.clip2(1) * amp;
			
	Out.ar(outBus, out.dup);
}).send(s);

snareを読む

env0 = EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.03, 0.10], [-4, -2, -4]));

通常のenv。アタック音用

env1 = EnvGen.ar(Env.new([110, 60, 49], [0.005, 0.1], [-4, -5]));
env1m = env1.midicps;

通常のenvだけど、midi値へ変換。この時点でfreq用だとわかる。

env2 = EnvGen.ar(Env.new([1, 0.4, 0], [0.05, 0.13], [-2, -2]), doneAction:2);

残響ノイズ用。こちらの方がevn0よりも値の減少が緩やかに設計されている。env0,env2ともにplotして確認してみるとわかりやすい。値が0になるタイミングは同じなので、env0,env2どちらにdoneAction:2つけても良いと思われるが、感覚的にはenv2が終了した時点でシンセ解放がしっくりくる。

oscs = LFPulse.ar(env1m, 0, 0.5, 1, -0.5) + LFPulse.ar(env1m * 1.6, 0, 0.5, 0.5, -0.25);
oscs = LPF.ar(oscs, env1m*1.2, env0);
oscs = oscs + SinOsc.ar(env1m, 0.8, env0);

メイン波形、アタック音。freqをずらしたものを足し合わせることで厚みを出す。他の引数も異なるが、mul,addなので微調整だろう。ローパスのカットオフは*1.2でごっそり削っている。最後にサイン波を足しさらに厚みを増す。相変わらず位相をずらしている。理由は不明。

noise = WhiteNoise.ar(0.2);
noise = HPF.ar(noise, 200, 2);
noise = BPF.ar(noise, 6900, 0.6, 3) + noise;
noise = noise * env2;

こちらは残響ノイズ波形。ハイパスかましたノイズをさらにバンドパスして一部強調?それに元のハイパスかましたノイズを足している。最後に*env2しているため、終了後にシンセが解放される。なぜこう作っているのかさっぱりわからないが、これだけでスネアっぽさがでるのだから凄い。

out = oscs + noise;
out = out.clip2(1) * amp;

clip2で多少歪ませた後に出力値を調整している、細かいがこの順番は大事だろう。アタック音がoscs、残響ノイズがnoise。どちらか一方削って実行してみると理解しやすい。アタック音と残響音を分けてつくり、最後に足し合わせる手法は真似したい。シンセを作成するうえで基本的なことだが、なぜかSCやっていると忘れがち。

hat 08091500Acid309 by_otophiliaより

SynthDef("hat", {	
	arg outBus=0, amp=0.3;
	var env1, env2, out, oscs1, noise, n, n2;
	
	n = 5;
	thisThread.randSeed = 4;
	
	env1 = EnvGen.ar(Env.new([0, 1.0, 0], [0.001, 0.2], [0, -12]));
	env2 = EnvGen.ar(Env.new([0, 1.0, 0.05, 0], [0.002, 0.05, 0.03], [0, -4, -4]), doneAction:2);
	
	oscs1 = Mix.fill(n, {|i|
		SinOsc.ar(
			( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps,
			SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12),
			1/n
		)
	});
		
	oscs1 = BHiPass.ar(oscs1, 1000, 2, env1);
	n2 = 8;
	noise = WhiteNoise.ar;
	noise = Mix.fill(n2, {|i|
		var freq;
		freq = (i.linlin(0, n-1, 40, 50) + rand2(4.0) ).midicps.reciprocal;
		CombN.ar(noise, 0.04, freq, 0.1)
	}) * (1/n) + noise;
	noise = BPF.ar(noise, 6000, 0.9, 0.5, noise);
	noise = BLowShelf.ar(noise, 3000, 0.5, -6);
	noise = BHiPass.ar(noise, 1000, 1.5, env2);
	
	out = noise + oscs1;
	out = out.softclip;
	out = out * amp;
	
	Out.ar(outBus, out.dup);
}).send(s);

hatを読む

n = 5;

メイン波形でMixを使用しており、その重ねる回数を定義。このシンセではsin波を5回重ねている。

thisThread.randSeed = 4;

ランダムシード定義。thisThreadは調べていないが、このコード内のスレッドって意味かと思われ。

env1 = EnvGen.ar(Env.new([0, 1.0, 0], [0.001, 0.2], [0, -12]));
env2 = EnvGen.ar(Env.new([0, 1.0, 0.05, 0], [0.002, 0.05, 0.03], [0, -4, -4]), doneAction:2);

いつものenv

oscs1 = Mix.fill(n, {|i|
	SinOsc.ar(
		( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps,
		SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12),
		1/n
	)
});

ハットのメイン波形。まず、Mixは複数チャンネル(配列)を一つのチャンネルに落とし込む。波形を足し合わせているという認識でおk。
freqをちょっと詳しくみてみよう。サイン波を引数ごと分けると以下となる。

freq: ( i.linlin(0, n-1, 42, 74) + rand2(4.0) ).midicps
phase: SinOsc.ar( (i.linlin(0, n-1, 78, 80) + rand2(4.0) ).midicps, 0.0, 12)
mul: 1/n

まだわかりにくい。linlinが原因かな。linlinはUGenのヘルプ参照するよーに。linlin(inMin, inMax, outMin, outMax, clip)だ
freqのrand2とmisicpsを外して.postlnを付けて値を出力し、それらを配列にいれてplotしてみる。次にmidicpsをつけて、最後にrand2をつけてplot。

[42,50,58,66,74].plot;
[92.498605677909,146.8323839587,233.08188075904,369.99442271163,587.32953583482].plot;
[98.263516145654,125.48249990264,257.11842831899,335.51069863238,690.38826095162].plot;

本来linlinで42-72まで値が線形に出力されるが、midi値に変換しており、さらにrand2加えているので結構がたついたplotが確認できる。きれいに並べるよりも多少汚した方がリアルになるとの考えだろうか。

oscs1 = BHiPass.ar(oscs1, 1000, 2, env1);

最後にフィルターかましてアタック音の完成

n2 = 8;
noise = WhiteNoise.ar;
noise = Mix.fill(n2, {|i|
	var freq;
	freq = (i.linlin(0, n-1, 40, 50) + rand2(4.0) ).midicps.reciprocal;
	CombN.ar(noise, 0.04, freq, 0.1)
}) * (1/n) + noise;
noise = BPF.ar(noise, 6000, 0.9, 0.5, noise);
noise = BLowShelf.ar(noise, 3000, 0.5, -6);
noise = BHiPass.ar(noise, 1000, 1.5, env2);

ノイズも基本的にメイン波形と同じ作り方。が、freqを逆数にしていたり、よくわからないフィルターかましたりしている。音響工学に基づいて作成しているのだろうか。とりあえず、変数freqをpostlnしてplotしてみよう。

[0.025,0.023529411764706,0.022222222222222,0.021052631578947,0.02,0.02,0.02,0.02].plot;
[0.025102866412683,0.024835818361551,0.021720987467888,0.021087916441834,0.019894677979736,0.021583178205406,0.020957042037079,0.019790472120955].plot;
[0.012250345692431,0.011950968549663,0.0085616529455822,0.0079047203612541,0.0067071490182452,0.0084175066708027,0.0077706562061599,0.0066053916708074].plot;

こちらのノイズも謎である。

out = noise + oscs1;
out = out.softclip;
out = out * amp;

snareと同じくアタックと残響用のノイズをわけている。こちらもnoiseを外したり、oscsを外したりして確認してみるとよくわかる。softclipはまんまソフトなクリップという認識で良いかと。

いじょう

ノイズを制するものはパーカッションを制す。
(やはりフィルターがよくわかっていない、誰か分かりやすい解説をしてくれるまで待ってます!)

SC3 こーどりーでぃんぐ1

とりあえずキック部分のメモ書きです。snare,clapもほぼ同様かな。hatは結構複雑。
短いコードの中にも多くの学ぶべきことが詰まっています。supercolliderにおいては人によって書き方が全然違いますが、基本的な部分さえ押さえておけばある程度は読めるはずです。さらに、自分なりの書き方に落とし込めばコーディング力向上間違い無しです!

08091500Acid309 by_otophiliaより

SynthDef("kick", {	
	arg outBus=0;
	var env0, env1, env1m, out;
	
	env0 =  EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.06, 0.26], [-4, -2, -4]), doneAction:2);
	env1 = EnvGen.ar(Env.new([110, 59, 29], [0.005, 0.29], [-4, -5]));
	env1m = env1.midicps;
		
	out = LFPulse.ar(env1m, 0, 0.5, 1, -0.5);
	out = out + WhiteNoise.ar(1);
	out = LPF.ar(out, env1m*1.5, env0);
	out = out + SinOsc.ar(env1m, 0.5, env0);
		
	out = out * 1.2;
	out = out.clip2(1);
		
	Out.ar(outBus, out.dup);
}).send(s);

読む

env0 =  EnvGen.ar(Env.new([0.5, 1, 0.5, 0], [0.005, 0.06, 0.26], [-4, -2, -4]), doneAction:2);

なんてことないenv定義。ただし、こいつは波形のmulとして使用される。そのため、このenvが終了した時点でシンセを解放してやればいい。という理由でdoneAction:2を加えている。

env1 = EnvGen.ar(Env.new([110, 59, 29], [0.005, 0.29], [-4, -5]));
env1m = env1.midicps;

こちらも同じくnewで定義しているなんてことないenv。次の行にてenv1をmidi値へ変換しているが、波形のfreqとして使用されるため。

out = LFPulse.ar(env1m, 0, 0.5, 1, -0.5);

freqとして、先ほどのenv1をmidi値にしたものを使用。バスドラムなど破裂音の周波数は高から低へ瞬間的に遷移するという、正しい理解に基づいた音作り。これは凄い!

out = out + WhiteNoise.ar(1);

ここでホワイトノイズをうっすらと加える。ちょっと汚したい時によくやる手法であり覚えておきたい。フィルターで余計な部分をカットしないとダメなので、忘れないように。

out = LPF.ar(out, env1m*1.5, env0);

ローパス。カットオフ周波数にenv1をmidi値にしたもの*1.5を使用している。このようにして、ノイズの余計な部分をカットする。さらに、mulにenv0を使用して、もとのLFPulseとそろえている。元の音から外れた余計なノイズが混じらないようにだろう。

out = out + SinOsc.ar(env1m, 0.5, env0);

厚みを増すためにサイン波を加えている。ここでも周波数にenv1m、mulにenv0を使用しそろえることで余計な音がかぶらないようにしている。位相をずらしているがなぜだろう?

out = out * 1.2;
out = out.clip2(1);

ここも面白い。出力を*1.2と無理矢理あげておいてからclip2(1)で出力を1に押さえることで歪ませている。これは使いたい手法。

Out.ar(outBus, out.dup);

dupは配列を返す。デフォ値が2であり、ステレオ出力させたい時によく使う。SC140において有用。

こんな感じでテンプレ化

シンセ定義した場合はすぐに音が確認できるように。パラメータの数はあまり増やさないように注意。

(
	SynthDef("sc3read_otophilia_kick",{
		arg pan = 0, outBus = 0, clip_amp = 1.0, white_amp=0.0, filter_freq=1.0,sin_phase=0.0;
		var env_mul, env_freq, signal, outSend;

		env_mul = Env.new([0.5, 1, 0.5, 0], [0.005, 0.06, 0.26], [-4, -2, -4]);
		//env_mul.plot;
		env_mul = EnvGen.ar(env_mul ,doneAction:2);
		env_freq = Env.new([110, 59, 29], [0.005, 0.29], [-4, -5]);
		//env_freq.plot;
		env_freq = EnvGen.ar(env_freq).midicps;
		
		//メイン波形。これに色々加えていったり削ったりする。
		signal = SinOsc.ar(env_freq, 0, env_mul);
		//ノイズをどの程度加えると良いか確認してみましょう
		signal = signal + PinkNoise.ar(white_amp);
		//cutoff周波数もいじってみましょう
		signal = LPF.ar(signal, env_freq*filter_freq, env_mul);
		//厚みを増したい場合には別波形を足す。ノイズ系でもいいかも。位相もいじってみましょう!
		signal = signal + SinOsc.ar(env_freq,sin_phase,env_mul);
		
		//どの程度クリップさせると良いか確認してみましょう
		outSend = signal * clip_amp;
		outSend.clip2(1).scope;

		Out.ar(outBus,
			Pan2.ar(outSend, pan)
		);
	},metadata: (specs: (clip_amp: [0, 2, \lin],white_amp: [0, 3, \lin], filter_freq: [0, 4, \lin], sin_phase: [0, 1, \lin]))
	).store;
SynthDescLib.global[\sc3read_otophilia_kick].makeWindow;
)

いじょう

メモだけど、アウトプット、アウトプット。

Unity3d + kinect

はじめに

ゲーム開発環境なら余裕でkinect接続できるよね?ってことでunityのフォーラム見ていたら、win,macともにOpenNIがいけるようです。ちょっとごちゃっているので簡単にまとめました。
macOSX10.6ですよ。

準備

libtool,libusb-develが必要なようです。
macportで入れましょう。

port installed | grep libusb-devel
 (すでにインストールされていた場合は削除しましょう。sudo port unistall libusb-devel)
sudo port libtool
sudo port libusb-devel +universal

+universal付け忘れるなよ!とのことです。

おとす

色々DLします。リンク変わっていたらごめんなさい(2011/09/01修正)

ここまではmacでOpenNI使う場合と同じです。unityで使用する場合はunitywrapperもDLします。

いれる

インストールします。

OpenNI

解凍したディレクトリにいって

sudo ./install.sh

します。

SensorKinect

同じく解凍したディレクトリ配下のBinディレクトリにいき、SensorKinect-Bin-MacOSX-xxxを解凍。解凍先のディレクトリにいって

sudo ./install.sh

します。

NITE

これも同じ。ライセンスの入力を求められます。http://www.openni.org/downloadfiles/opennimodules のOpenNI Compliant Middleware Binariesの部分の太字をコピペしましょう。

sudo ./install.sh
Please enter your PrimeSense license key: 
UnityWrapper

こいつは解凍するだけ

確認

とりあえず、kinectが動作するか確認しておきましょう。NITEディレクトリ配下のSample/Bin/以下をターミナルから適当に実行してみましょう。

Unity

ようやく環境が整ったのでUnity側に移りましょう。UnityのOpen Projectから先ほど解凍したUnityWrapperディレクトリを開きましょう。http://planetmatt.com/integrate-kinect-unity3d-on-mac/ を参考にサンプルを動かしてみましょう。

サンプルを試す
  1. WalkRunIdleBlendを開きます。
  2. もともとHiearchyにあるsoldierはなんか見えないので削除
  3. ProjectのSoldierをHiearchyにD&D
  4. ちっちゃいのでSoldierのScaleを200,200,200に変更
  5. テクスチャが欲しいので、Projectのsoldier_blueをSceneのSoldierにD&D
  6. ついでに、bridgegroundstones_layersもCubeにD&D
  7. CubeのInspectorのMono Behaviourをいじります
    1. ScriptをNiteにします
    2. Right_HandをR_Handにします
    3. Right_ElbowをR_Elbowにします
    4. 以下それっぽいものをアサインします(Left_Elbowをアサインしないとなぜか落ちます)
    5. カメラにSmooth Followスクリプトも追加しますか。main cameraを選択して、Component>Camera-Control>Smooth Follow。適当にtargetとか変更。
  8. playボタンで起動します
    1. New User Foundとコンソールに表示されるまでkinectの前で待ちます
    2. Soldierがそれっぽく動きます

いじょう

動いた!ま、そんだけです。game objectがまだ全然分かっていないのでなにも出来ませんが、パーティクルかなにかに右手アサインしてOSCでSCに値返してエフェクタ制御とか、そんな感じですかね。

SuperCollider + Unity3d

はじめに

Unityというゲーム開発環境があります。UbuntuのUIじゃないですよ。機能制限はありますが、フリーです。iOSのゲーム開発にも使用されているようで、日本語の情報も結構あります。UnityではC#スクリプト書けるとのことでしたので誰かOSC使えるようにしてるだろ、とググったら引っかかりましたので、ちょっといじってみました。

プロジェクトサンプルをゲット

http://forum.unity3d.com/threads/16882-MIDI-or-OSC-for-Unity-Indie-users
でプロジェクトサンプルをUPしてくれている方がいるので、使わさせてもらいましょう。MacOSX、Unity3.3.04fの環境では、UDPPacketIO.csを

using System.IO.Ports;
using System.IO;

に変更しないとダメです。

試してみる

Unity側

HiearchyのOSCEmptyオブジェクトをクリック。
右枠にInspectorが開くので、OSCReciverのRemote IP,portを自分の環境に。
Unity側の再生ボタンを押します。

SC側

"/1/toggle1"に適当に数値送ります。

確認する
  • オブジェクトが動く
  • Unityのコンソールにメッセージ名と値が流れる

いじる

詳しく調べる気はないので、handler.SetAddressHandler("メッセージ名", 関数名);でOSCメッセージ名、関数を指定、OSCメッセージ値を関数内で変数に代入、Update関数でGameObjectのパラメータを更新させる。
という処理を丸パクリして、追加したゲームオブジェクトをいじってみる。うーむ、楽です。

いじょう

3D映像をSCから制御したい場合、oF、P5でコーディングするよりも、blender、Unity使うと圧倒的に楽かと。相変わらずSCはよく分かりませんが、Unity側をもうちょっといじってみようかと思います。
OSCなので、SCからだけじゃなくてMax/PDからも制御できますよ。

SuperColliderのクラスメモ

単なるメモ
特に難しいことはないかと

クラスファイル側

/* 0. はじめに  */
MyClassPre{
}

/* 1. 変数 */

	/*	
	変数の宣言はいつもどおりvar
	<でゲッター、ゲッターはオブジェクトの外側から変数の値を取得できるようにする
	>でセッター、セッターはオブジェクトの外側から変数の値を設定できるようにする
	*/
	
MyClassVar {
	var <>x;
	var y;
}

/* 2. メソッド */
MyClassMethod {
	var <>x;
	/* 
	*付きでクラスメソッド
	クラスメソッドとは、インスタンスを生成しなくても直接クラスから呼び出せるメソッド
	*/
	*new{
		^super.new //ここでのsuperはobject。help->class browserのsuperボタンを押してもわかる
	}
	
	/*
	*なしでインスタンスメソッド
	インスタンスメソッドとは、インスタンスから呼び出すメソッド
	*/
	postMethod {
		^x.postln;
	}
	
	/*
	argで引数
	^で戻り値
	*/
	postMethod2{
		arg a,b;
		var c = a+b;
		^c.postln;
	}
}

/* 3. ちょっと書いてみる */
//Objectは勝手に継承
SynthPlusFilter{
	var <>mysyn,<>myfilter;
	var >freq=440,>q=1.0,>mul=1.0,>add=0.0;
	*new{
		arg mysyn,myfilter;
		^super.newCopyArgs(mysyn,myfilter);
	}
	play{
		play{myfilter.ar(mysyn.ar(),freq,q,mul,add);}
	}
}

/* 4. もうちょい書いてみる */
SubSynthDef{
	var <>defName,<>mysyn1,<>mysyn1Param,<>myfilter,<>myfilterParam,<>myenv;
	*new{
		arg defName,mysyn1,myfilter,myenv;
		^super.newCopyArgs(defName,mysyn1,myfilter,myenv);
	}
	def{
		arg bus=0, pan=0;
		SynthDef(defName,{
			var signal,envelope,filter,out;
			mysyn1Param.postln;
			signal=mysyn1.ar(mysyn1Param);
			filter=myfilter.ar(signal,myfilterParam);
			envelope=EnvGen.ar(myenv,doneAction:2);
			Out.ar(bus,
				Pan2.ar(envelope*filter,pan);
			);
		}).store;
	}
	play{
		Synth(defName).play;
	}
}

rtf側

/*0. はじめに*/
/*
	.scのファイル名でクラスファイルを作る
	保存する場所は
*/
Platform.userExtensionDir;
//保存したらmenu->Lang->compile library(もしくはpostウィンドウでcmd+K)して、下が実行できればおk
MyClassPre

/*1. 変数*/
(
	var test =MyClassVar.new;
	test.x = 1;		//test.x_(1);も同じ意味
	test.x.postln;
	//test.y = 2;	//NG
	//test.y;	//NG
)

/*2. メソッド*/
(
	var test = MyClassMethod.new;
	test.x_(10);
	test.postMethod;
	test.postMethod2(1,2);
	//var ngTest = MyClassMethod.postMethod;
	//test.new;
)

/* 3. ちょっと書いてみる */
//継承とか色々あるけれど、上記だけで簡単なクラスは書ける。特に意味はないクラスだけど
//newは省略できる
(
	var test1 = SynthPlusFilter(WhiteNoise,RLPF);
	var test2 = SynthPlusFilter(SinOsc,RHPF);
	test1.freq_(220).mul_(0.5).play;
	test2.freq_(1000).play;
	test1.mysyn.postln;	test2.mysyn;
	
	//以下と同じこと
	//{RLPF.ar(WhiteNoise.ar(),220,mul:0.5)}.play;
	//{RHPF.ar(SinOsc.ar(),1000)}.play;
)

/* 4. もうちょっと書いてみる */
//さらに意味はないクラスだけど、ちょっとすっきり
(
	var test1 = SubSynthDef('subtest1',
		SinOsc,[440,0.8,0],
		RLPF,[220,1.0,1.0,0.0],
		Env.perc(0.01,0.4)
	);
	var test2 = SubSynthDef('subtest2',
		Blip,[440,110,0.5,0],
		RHPF,[1000,1.0,1.0,0.0],
		Env.perc(0.1,1.0)
	);
	test1.def();
	test2.def();
	test1.play;
	test2.play;
)

以上

継承などをうまくつかっていくと表のコードはすっきりきれいに早く書けます。
でも、クラスライブラリ充実しているから別に自分で書かなくても良いと思います。ライブコーディングやる場合、あらかじめ自分でクラス書いておくと、かなり早くなりそうですね。