CによるMaxオブジェクト開発における
pattr認識の追加

によるノーマル Maxオブジェクトの開発者は、開発するオブジェクトに対して、簡単にpattr互換性を追加することができます。最初のステップは、次のような #include 文をソースコードに追加することです。

#include "ext_obex.h"

"ext_obex.h" ヘッダは、多くの型と関数を定義しています。以下のサンプルではこのヘッダが存在することが前提にになっており、pattr 互換のためにはこれが必須です。

オブションとして、commonsyms.c(c74support/max-includes/common/)というソースコードファイルをあなたのプロジェクトに追加して下さい。加えて、main() の中に common_symbols_init() の呼び出しを追加することによって、有用なシンボルの多くを初期化します(リストは、ヘッダファイル commonsyms.h を参照して下さい)。このドキュメントでは、common シンボル が利用できることを前提にしています。

実際に pattr 互換性を持たせるためには、A_CANT を扱うメソッドとして2つの特別な関数を実装するだけです。

従来の Max オブジェクトインターフェイスを用いると次のようになります。

addmess((method)myobj_getvalueof, "getvalueof", A_CANT, 0); addmess((method)myobj_setvalueof, "setvalueof", A_CANT, 0);

obes オブジェクトインターフェイスを用いると次のようになります(詳細は後述します)。

class_addmethod(myobj_class, (method)myobj_getvalueof, "getvalueof", A_CANT, 0); class_addmethod(myobj_class, (method)myobj_setvalueof, "setvalueof", A_CANT, 0);

使用するインターフェイスに関わらず、getvalueof() およびsetvalueof() メソッドは、次のようなプロトタイプでなければなりません。

t_max_err myobj_setvalueof(t_myobj *x, long ac, t_atom *av); t_max_err myobj_getvalueof(t_myobj *x, long *ac, t_atom **av);

setvalueof() メソッドはオブジェクトのステート(状態)をセットするために用いられ、getvalueof() はその問い合わせを行います。オブジェクトのステートが1つの 浮動小数点の値で定義される非常にシンプルな実装例は次のようになります。

t_max_err myobj_setvalueof(t_myobj *x, long ac, t_atom *av) { if (ac && av) { // float の値の受取りをシミュレートします。 myobj_float(x, atom_getfloat(av)); } return MAX_ERR_NONE; } t_max_err myobj_getvalueof(t_myobj *x, long *ac, t_atom **av) { if (ac && av) { if (*ac && *av) { // 渡されるメモリ。これを使用します。 } else { *av = (t_atom *)getbytes(sizeof(t_atom)); } *ac = 1; // our data is a single floating point value atom_setfloat(*av, x->objvalue); } return MAX_ERR_NONE; }

当然のことながら、オブジェクトは複数の値を持つかもしれません。これは、3つの整数値から成る値(red、green、blue)をサポートするように変更された、同じメソッドの例です。

t_max_err myobj_setvalueof(t_myobj *x, long ac, t_atom *av) { if (ac && av && ac >= 3) { myobj_red(x, atom_getlong(av)); myobj_green(x, atom_getlong(av + 1)); myobj_blue(x, atom_getlong(av + 2)); } return MAX_ERR_NONE; } t_max_err myobj_getvalueof(t_myobj *x, long *ac, t_atom **av) { if (ac && av) { if (*ac && *av) { // 渡されるメモリ。これを使用します。 } else { *av = (t_atom *)getbytes(sizeof(t_atom) * 3); } *ac = 3; // 3つの整数値 atom_setfloat(*av, x->red); atom_setfloat(*av + 1, x->green); atom_setfloat(*av + 2, x->blue); } return MAX_ERR_NONE; }

重要:上記の2つのsetvalueof() の例では、メモリ割り当てを行なうために getbytes() 関数を使用しています。他のメモリアロケーション関数を用いた場合、ほとんどは不安定になるかクラッシュを引き起こします。この点に注意して下さい。

最後に、pattr が、いつ getvalueof() メソッドを呼び出さなければならないかを認識できるように、あらゆるオブジェクトのステートの変更を pattr に対して通知する必要があります。pattr はこの目的のために、Max 4.5 で導入されている一般化されたサーバ/クライアント通知メカニズムを使用します。このメカニズムの詳細は、(セクション D の Obex リファレンスには、より詳しく述べられていますが)このドキュメントの範囲を超えていますし、pattr の実装上、特に問題にはありません。

オブジェクトのステートの変更を pattr に知らせるためには、ステートの変更が生じた直後、あるいは適当な処理を行なった後で、 object_notify() 関数を呼び出す必要があるだけです。しかし、あなたのオブジェクトが UI オブジェクトがどうかによって、関数を呼び出す方法は異なります。

オブジェクトが UI オブジェクトの場合、関数は単純に次のように呼び出されます。

object_notify(x, _sym_modified, NULL);

object_notify(x, _sym_modified, NULL);
   
x あなたのオブジェクトへのポインタ
_sym_modified commonsyms.c で定義されています。gensym("modified") と同じものです。

シンボル_sym_modified は明確な obex API の一部ではありませんが、あなたのプロジェクトに commonsyms.c ソースコードを追加し、main() の中で common_symbols_init() の呼び出しを行なうことによって、他の多くの有用な定義済みシンボルと同様に使うことができます。この SDK にあるサンプルでは、開発者がこれを行なっていることを前提としています。

オブジェクトが UI オブジェクトではない場合、関数は次のように呼び出されます。

object_notify(x->b, _sym_modified, NULL);
   
x->b あなたのオブジェクトを含む box へのポインタ
_sym_modified commonsyms.c で定義されています。gensym("modified")と同じものです。

この場合、あなたの new() メソッドの中で、オブジェクトが入れられている box へのポインタを格納する必要があります。

値は t_symbol * gensym("#B") の s_thing に置かれます。次はその例です。

void *myobj_new(void) { t_myobj *x = NULL; if (x = (t_myobj *)newobject(myobj_class)) { x->b = (t_box *)gensym("#B")->s_thing; ... } return x; }

これが pattr にあなたのオブジェクトのgetvalueof() メソッドを呼び出させます。例えば、myobj という UI オブジェクトを仮定した場合、関数 myobj_float() は次のようになるでしょう。

void myobj_float(t_myobj *x, double d) { x->objvalue = d; object_notify(x, _sym_modified, NULL); outlet_float(x->floatout, d); }

object_notify() の呼び出しが、確実に、オブジェクトのステートの変更を生じさせるコードの後で起こるようにしておく必要があります。そうでない場合、オブジェクトが完全にアップデートされる前に、オブジェクトの getvalueof() メソッドが呼び出されてしまう可能性があります。

従来の Max オブジェクトに、上記のテクニックを使って pattr のサポートを追加する例は、SDKに同梱されているmyobj というサンプルソースコードを参照して下さい。