jit.leapmotion リープモーションのステレオ赤外線カメラ画像取得できるExternalオブジェクト #maxmsp #jitter

スクリーンショット 2014-11-04 21.43.12
Jitter Externalの勉強がてらLeap Motionのステレオ赤外線カメラ画像を取得できるExternalオブジェクトを書いてみました。Mac OSX Yosemite環境にて開発してるので、それ以外で動くかは謎です。

ダウンロード

Cycling74 Max Tool。パッケージにまとめてあるので、Application/Max6.1/packages 以下に落としたファイルを置けば使えると思います。

注意点

スクリーンショット 2014-10-31 19.49.15

  • Leap Motionをインストールするとメニューバーに出てくるLeap Motionコントロールパネルにて、イメージを許可するにチェックをしないと動かないです。
  • 解像度は640×240で固定。Leap Motion SDKの仕様で、動作が重くなると自動的に640×120に変更されてしまう事があるのですが、その場合は縦に引き伸ばしています。

Max External開発 simplemaxオブジェクトのサンプルコードを読み解く #maxmsp

スクリーンショット 2014-10-25 2.08.50
MaxでもopenFrameworksみたいにCやC++の資産を活用したい!aka.leapmotionを参考にLeapMotion v2.のオブジェクトも開発してるので、そのうち公開したい!ということで、久しぶりにMax External勉強してます。

今回は、MaxSDKをダウンロードすると付いて来るXcodeプロジェクトサンプルから、simplemaxのコードを読んでみます。ちなみに Yosemite / Xcode6でやってます。

インクルード

#include "ext.h"	// standard Max include, always required
#include "ext_obex.h"	// required for new style Max object

この2つは大抵のオブジェクトでインクルードします。C言語の入門書におまじないって書いてあるやつですね。

Maxオブジェクトの構造体定義

////////////////////////// object struct
typedef struct _simplemax 
{
	t_object	ob;	// the object itself (must be first)
} t_simplemax;

プライベート変数やプロキシを利用する場合は、構造体メンバとして定義する必要があるそうです。MaxAPI Document参照。

関数プロトタイプ宣言

///////////////////////// function prototypes
//// standard set
void *simplemax_new(t_symbol *s, long argc, t_atom *argv);
void simplemax_free(t_simplemax *x);
void simplemax_assist(t_simplemax *x, void *b, long m, long a, char *s);

(Max側から呼び出したい)登録したいメソッドは、ここでプロトタイプ宣言しといてmain関数で登録する。maxsimpleがインスタンス化する時に必要なnewと解放する(消す)時に必要なfreeは必須。assist関数は、必ず必要なものでは無いようです。

クラスの登録

//////////////////////// global class pointer variable
void *simplemax_class;


int C74_EXPORT main(void)
{	
	// object initialization, NEW STYLE
	t_class *c;
	
	c = class_new("simplemax", (method)simplemax_new, (method)simplemax_free, (long)sizeof(t_simplemax), 
				  0L /* leave NULL!! */, A_GIMME, 0);
	
	/* you CAN'T call this from the patcher */
        class_addmethod(c, (method)simplemax_assist,"assist",A_CANT, 0);  
	
	class_register(CLASS_BOX, c); /* CLASS_NOBOX */
	simplemax_class = c;

	post("I am the simplemax object");
	return 0;
}

C74_EXPORTは、おまじないでMaxからこのmain関数が呼べるようになるマクロのようです。t_class *cのポインタにclass_newでクラスを生成しています。simplemax_new / simplemax_free は、プロトタイプ宣言してあるのでここで関数ポインタを渡してクラスメソッドとして登録していますね。さらにそのclassインスタンスにsimplemax_assistメソッドを登録しています。このメソッドは、inletやoutletにマウスポインタを合わせるとポップアップで説明がでてくるタイプのメソッド。class_registerでCLASS_BOXへクラスの登録をすることでMaxからsimplemaxクラスが探せるようになります。このmain関数で一度作成し登録したクラスは、グローバル変数のsimplemax_classにポインタを保存しておくみたい。たぶんMax起動時に一度だけこのMain関数が呼ばれてクラスだけ登録しておいて、simplemaxがインスタンス化される場合は使いまわしている。

メソッドの実装

void *simplemax_new(t_symbol *s, long argc, t_atom *argv)
{
	t_simplemax *x = NULL;
        long i;
    
	// object instantiation, NEW STYLE
	if ((x = (t_simplemax *)object_alloc(simplemax_class))) {
        object_post((t_object *)x, "a new %s object was instantiated: %p", s->s_name, x);
        object_post((t_object *)x, "it has %ld arguments", argc);
        
        for (i = 0; i < argc; i++) {
            if ((argv + i)->a_type == A_LONG) {
                object_post((t_object *)x, "arg %ld: long (%ld)", i, atom_getlong(argv+i));
            } else if ((argv + i)->a_type == A_FLOAT) {
                object_post((t_object *)x, "arg %ld: float (%f)", i, atom_getfloat(argv+i));
            } else if ((argv + i)->a_type == A_SYM) {
                object_post((t_object *)x, "arg %ld: symbol (%s)", i, atom_getsym(argv+i)->s_name);
            } else {
                object_error((t_object *)x, "forbidden argument");
            }
        }
	}
	return (x);
}

void simplemax_free(t_simplemax *x)
{
	;
}

void simplemax_assist(t_simplemax *x, void *b, long m, long a, char *s)
{
	if (m == ASSIST_INLET) { // inlet
		sprintf(s, "I am inlet %ld", a);
	} 
	else {	// outlet
		sprintf(s, "I am outlet %ld", a); 			
	}
}

simplemax_newはオブジェクトがインスタンス化される場合に呼ばれる。このサンプルは、渡された引数をひたすらMaxWindowにポストしてくれてる。argvにアーギュメントがargc個入っていて、t_atom型はa_typeメンバをみると型が書いてあるよう。atom型はゲット関数が用意されている。メンバ変数などの初期化は、ここに書けば良さそう。

simplemax_freeの方は特に何もしてないけど、インスタンス化したオブジェクト自体は裏で勝手に開放してくれているのかな?。自分でインスタンス化したオブジェクトがあればここで開放したい。simplemax_assistは、アシスト表示を使うときのテンプレートのようです。

メソッド追加(余談)

void simplemax_bang(t_simplemax*x)
{
    post("bang");
}

class_addmethod(c,(method)simplemax_bang,"bang", A_SYM,0);

このsimplemaxオブジェクトは、どんなメッセージにも反応しないサンプルとしてはかなり駄目な子です。そこでメソッドを定義して、クラスに登録してみたらちゃんと動きました。トップ絵はbangを送って動作させてる様子です。C言語だけどオブジェクト思考の匂いがする。

SVGの背景を透過するサンプル jit.mgraphics × jit.gl.pix #maxmsp #jitter #gen

スクリーンショット 2014-10-17 11.57.59jit.mgraphicsで描画したSVGに透過色を持たせたい場合には、jit.gl.pixで特定の色を透過させる処理を書くと良さそうです。上の画像は、カメラのキャプチャにSVGの描画を載せたサンプル。棚がとっ散らかってますが、お気にせず・・・

jit.mgraphicsで背景黒にして描画

スクリーンショット 2014-10-17 11.56.39svgでピンクの丸を描画してます。ちなみにillustratorでSVGをつくりました。(左側に、ちょっと、余分なコードも入ってますがお気にせず。)

jit.gl.pix 黒を透過

スクリーンショット 2014-10-17 12.06.54厳密に言うと、黒に近いほど透過されるコードに成ってますが、とりあえず目的に適うのでオッケーです。

Code(シェーダ部分)


----------begin_max5_patcher----------
757.3oc0WssahCCD84juBK+LKxNAnj8s86.UgLItgoqwNxwbYaU+2WamKjvF
pnkVDqPJANy3wGe73gwuFFfWoNvKwnehVfBBdMLHvC4.Bp+c.dC6PpfU5cCK
46UqdFOpxjgev3geFLiyEiKfCn8fLSsuwimTRSI7B24EMZLoFVs0H3FyeJ3U
StK.KyEKcAbqliGgvXzi0NK2tAjV28LfdDrJHdznZzBlIcMHyWp4olpPGSlZ
mVTzL+qjI9ePFSZCuihR1FOSv+RCLQC4gLOlcA+C5Lb2ofqq0nZQxFEPv2w0
kfR1glAXVQQG3fNCworOq7AZ1nVHPVAQagz7cPy3SZQYZ6B0XWkN4xwxCymg
qL9VXyjeTEhnIN0GQmG6E.5CtWwSiNJD1s97TknZ5WfHiSF04AsuiBU5u4d4
gz.pJ3RPVn4kbogYpYbq4L9SrsByxgyH5a+IVJ+rCdv8p.btFxTRGI5MRGby
zs.Q8IAtmGWLdOjrhAFboccrsbES61AVI3cR0rI+Jknuo1Mc6YHlD1vLbCTQ
1HRaPgMEZPZ5MQbIyFi0koZkPzKTUV1MfkLadQJeOjYV6iUWoz5NTzrEfa0n
LHmWZ5iYX4k8Q5UTnaBa2hC8veuhD8KTTtGdAoyW003YxHFrNQaUgyVYXfpC
csLPEBJweXHwexX5zSKP79EINsPQTDtA+svvluL5qWI2.G9FDw3OqHNm30sI
d4axrqTDo2HQTvk41iO2eIizod8bR7UJjjajPtimhHUe9FTyIeV0Lt5zL0Kl
O7v0IlzjajXBRD8aPEIeVUzVYbpsYApukAxUJh2HMzt5tXQ7BO4RtjJfwj49
WWYt1jgjoi8rH.449KZOyb1GV+JUa0oMIK0+gksEudrz1+fAjssvsnsVxoNt
Fxx52xTUWFktdVp5N7h1s+nbldoTN5+OJ6bjdWP4nKkySteT4jOhLeev4ANW
8NbN5KfynGCOh3eVWa4zaq5n8I2R8jan9u2N8b2L0NOuE9W.Vv2VrA
-----------end_max5_patcher-----------

こういう微妙なコードをBlogに書いとくと、後で再利用しやすいのが良いですね。Max7だと、コードスニペット機能が充実するそうなので、楽しみですな〜。

Cycling74 Max レッスン生徒さんご紹介 その1 TY0004さん #maxmsp

TY0004さん アイコン

130810

Cycling 74 Maxを使い始めたきっかけを教えて下さい

作品発表のための、表現力を高めたかったためです。

今回Maxによる、お子様向けの展示をつくったのは、どうしてですか?

環境・健康問題について、体を使ったゲームを楽しみながら、慣れ親しんで貰いたかったからです。

TY0004さんによる作品

kidz_01

kidz_04

※著者解説
綿毛やバイキンが降ってくるのを、触ったり避けたりする事を通して、環境や健康への認識を育む作品。TY0004さんにとって初のMaxによる作品制作でした。OpenNI / Xtionを使ったボーン認識や、jit.physを使った物理シュミレーション、jit.gl.*による描画など、少し高度な内容も含まれていましたが、明確に目的が決まっていたので制作は順調に進みました。

実際展示してみてどうでしたか?

ゲーム自体、とても楽しんでもらえました。体を動かし、中には転んでしまう程にたっぷりと満喫してもらうことが出来ました。(環境・健康の)難解な話だけであれば子供達の関心は向かなかったと思います。

Maxレッスンについての感想を教えて下さい

自身のやりたい事に合わせながら、とても丁寧に進めて下さっており、個人的な相談にも親身に耳を傾けて下さります。

今後、さらに勉強していきたい技術は、ありますか?

今後は、Maxだけではなくプログラミング全般も学んでいきたいです。

Maxレッスンについての詳細は、こちらのページを御覧ください!

maxurlのサンプルを試す #cycling74 #maxmsp #curl

スクリーンショット 2014-09-11 02.43.36
最近のアップデートで登場したcURLのMaxラッパーであるmaxurl。ついにMaxがインターネットと繋がる!と期待が高まり、勉強がてらサンプルコードを弄ってみました。

まずmaxurlのヘルプパッチを見るのがオススメです。タブになっていて、いくつものサンプルがいじれます。上記画像はヘルプパッチの良いねボタンサンプル。良いねボタンを押すとサーバー上のいいねカウントが1つあがる。

更に詳しい説明が書いてあったのは→Use MaxURL to Create a Realtime Instagram Collage

MaxでWeb APIを扱う2つの方法

どうやら、Maxがインターネッツとのリクエスト・レスポンスを扱う方法は二種類あるようです。

  1. maxurlとdictを使うパターン。JSONでクエリを生成し、それ以外はMaxらしいパッチングで行う方法
  2. jsオブジェクト(javascript)を使うパターン。XMLHttpRequestで行う方法

それぞれの方法についてCycling74のフォーラムにいくつかサンプルパッチがあったので試してみました。

MaxURL Weather Report

スクリーンショット 2014-09-11 02.12.47

Maxから天候を取得してみようというサンプル。このサンプルは、maxurlとdictでAPIを使う方法。

天候取得クエリ

{
     "http_method" : "get",
     "url" : "api.openweathermap.org/data/2.5/weather?q=Tokyo,jp",
     "response_dict" : "data1",
     "parse_type" : "json"
}

元々のサンプルはロンドンの天気を取得していたのですが、東京の天気は、”London,uk” → “Tokyo,jp”と変更したら取得できました。

MaxURL SoundCloud

スクリーンショット 2014-09-11 02.19.12

MaxからSoundCloudのAPIを使うサンプル。SoundCloud Developerの登録が必要です。例えば “livetune”と検索すると、ヒットしたURLがumenuに登録されます。このサンプルは、jsオブジェクトでAPIを使う方法。

ニコニコ横断検索API

サンプルにも慣れて、そろそろ自分で一から試してみようとAPIを探してみると、検索キーワードに関連したタグを返してくる”タグ検索API”というのを発見。将来的に”あのタグで待ってる。“に組み込んだら面白いかなと思って試してみました。

{
	"http_method" : "POST",
	"url" : "http://api.search.nicovideo.jp/api/tag/",
	"post_data" : 	{
		"query" : "Free!",
		"service" : [ "tag_video" ],
		"from" : 0,
		"size" : 5,
		"issuer" : "apiguide",
		"reason" : "ma10"
	}
,
	"response_dict" : "data4"
}

こんなクエリーを投げると

スクリーンショット 2014-09-11 02.34.46
レスポンスが帰ってきた!しかし、応答のBodyに対して文字列がエスケープ?のバックスラッシュが付きまくっている!!!Maxで消す方法がわからなくて、データを利用できなかった。

取得できたタグを見ると”Free!のためなら死ねる!”とか面白いですが、タグとして抽象度が低いのは使いづらいかも?!maxurlの使い方は未知数なので、今後も情報収集していきたいです!!!!!

jit.phys を2次元物理エンジンBox2d的に利用 #maxmsp #jitter

スクリーンショット 2014-09-04 17.15.30

2次元物理エンジン

Max6から導入されたjit.phys、デモなどをみると大抵3次元で利用されていますが、設定をすれば2次元でも利用できます。2次元物理エンジンで有名なBox2dみたいな使い方も可能というわけです。AZUMA HITOMIさんのVJでタイコマシーンの上に積み上がるボールは、これを使っています。

設定方法

スクリーンショット 2014-09-04 17.20.21
jit.phys.world のアトリビュート @remove_plane 3 と書くだけでZ軸を無視できます。

大量のオブジェクトを配置

スクリーンショット 2014-09-04 17.20.32
たくさんのオブジェクトを出したい場合は、jit.phys.multipleとjit.gl.multipleを使います。jit.phys.multipleが物理モデルの計算を担当し、表示をjit.gl.multipeが担当します。詳しくは、後述のコードで確認ください。

パフォーマンスは500個くらいまでなら60fpsで動いておりました。またオブジェクトのスケールを小さくしすぎると、物理計算が正しく行われなくなってしまうようでした。この辺りは注意して利用したいです。

デバッグ表示

スクリーンショット 2014-09-04 17.21.53

Continue reading

BPMに同期した映像の再生 #maxmsp #jitter

BPMに同期した映像の再生

スクリーンショット 2014-08-12 00.56.26
transportで指定したBPMとjit.qt.movieを同期させて、映像を再生する方法です。phasor~が指定したBar/Beatsでtransportに同期して0~1の信号を出すことができるので、その値をframecountに掛ける事で現在のフレームへジャンプさせて同期再生をする事ができます。例えば、2小節でjit.qt.movieの再生を繰り返したい場合は、phasor~のfrequencyアトリビュートに”2 0 0″と指定します。

Continue reading

jit.gl.pix @gen によるシェーダ切り替えで画面が一瞬止まる問題について #jitter #maxmsp

スクリーンショット 2014-08-07 02.45.33

シェーダ切り替え

jit.gl.pixにて@genによるシェーダの切り替えを行うと一瞬画面が止まってしまう事案が発生いたしました。この原因は2つ考えられるかなと思いまして

  1. jit.gl.pixが内部的に.jitgen → .jxs / GLSLに変換してシェーダを使うため、SSDやHDDとファイルのRead/Write処理が同期的に行われて止まる
  2. GPU上へシェーダを転送・コンパイルする時に止まる

検証はできてないですが、1の方が原因としては重たそう。というのも、jit.qt.movieでreadするとファイル読み込みで画面が止まってしまう現象と同じなためです。jit.qt.movieではasyncread使うことで再生中の動画を止めずに切り替える事が可能ですね。ただ、実はこれも問題があり、大量のjit.qt.movieに対してasyncreadを指示すると画面止まります。内部的にどうやってるかコード読んでみたい。(関係ないけど、Windows版のMaxの非同期読込もサポートしてほしいな・・・。)余談はさておき、jit.gl.pixには非同期読み込みをするメッセージを送れません!

対処方法

BiLmv_hCUAEVOcq.jpg-largeパッチをロードしたタイミングで使いたいシェーダの数だけjit.gl.pixをインスタンス化しておく。使おうと思った時にはメモリ上に載っているので画面が止まる事は無さそうです。スマートなやり方ではないですが、この方法しかないかな・・・?使いたいシェーダの数が増えるとメンテナンスが大変になりそうなので、jsオブジェクトなどを使ってメタプログラミングでオブジェクトの自動生成をすれば、メンテナンスも楽そうです。やりすぎるとGPUのメモリが足りなくなる事案も発生しそうですが。

ちなみに上記画像は遠藤一郎氏のライブペインティングでプログラムしたときに使ったパッチのキャプチャです。音声ファイルを400個近く読み込む必要があって、ライブ中にファイル読込で止まるのが嫌だったので、起動時に全部メモリ上にロードしておくプログラムをjavascriptで書きました。

jit.gl.pix における2次元平面のアンチエイリアス処理 #maxmsp #jitter

jit.gl.pixでトランジションエフェクト

あのタグの次期バージョンに入れるトランジションエフェクトを開発しています。例えば、中央から丸が出てきて画面全体に広がるようなエフェクトをつくろうとすると、丸を書いて2つのテクスチャを割当るだけで簡単そうです。しかし、単純につくるとエッジがギザギザしてしまいます。そこでjit.gl.pix(フラグメントシェーダ)におけるアンチエイリアスを実装してみます。

アンチエイリアス処理有り

スクリーンショット 2014-08-10 03.08.23

アンチエイリアス処理無し

スクリーンショット 2014-08-10 03.13.37中央の円のエッジがギザギザしており、VJなどで大画面に表示するともっと気になるかなと思います。

円トランジションjit.gl.pix

スクリーンショット 2014-08-10 03.25.40現在のピクセルの半径rと、トランジションする円の半径rとの距離をとって、その距離が短い場合に(0~0.005の距離)smoothstepで補間をしています。補完した値でmixする事でエッジをなだらかに繋ぐ事が可能です。ここを単純に書くと大なり小なりで比較して、0 or 1ピクセルの色を指定して、switchやif文でピクセル値を切り替えて出力したくなりますが、それではエッジがギザギザになってしまいます。