
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言語だけどオブジェクト思考の匂いがする。