Max ラッパークラス

Jitter オブジェクトを Max パッチャーに対して公開するために、Max 「ラッパー」クラスを定義する必要があります。これは、シンプルなクラスの場合、主として少数のユーティリティ関数を使って容易に行うことができます。この関数は、Jitter クラスを受け取り、デフォルトの機能によって適切なラッパークラスを作るものです。しかし、特別な動作を行わせる場合には、それ以上の関与が必要になることがあります。例えば、インレットやアウトレットの追加、MSP との一体化、マトリックス情報の Max のリストへの変換などを行なう場合がこれにあたります。ここで示す、最初の Max ラッパークラスは、シンプルで基本的な Jitter クラスを含んでいるだけで、それ以上の複雑な追加要素を持たないものです。

一般に、Jitter クラスはMax パッチャーについての考慮を行わずに設計し、パッチャーとの通信のために必要なあらゆるロジックは Max ラッパークラスでメンテナンスするほうが望ましいでしょう。これが難しいような状況では、通常、Jitter クラスに Max ラッパーによってのみ呼び出される特別なメソッドを作るか、先の章で説明するように Jitter のオブジェクト通知メカニズムを利用することによってこれに対処します。次のものは、前章で示した、最小限の Jitter クラスのための、最小限の Max ラッパークラスです。

typedef struct _max_jit_foo { t_objectob; void*obex; } t_max_jit_foo; void *class_max_jit_foo; void main() { void *p,*q; // Jitter クラスの初期化 jit_foo_init(); // Writing Max Externals で説明されている Max クラスの生成 setup(&class_max_jit_foo, (method) max_jit_foo_new, (method) max_jit_foo_free, (short)sizeof(t_max_jit_foo), 0L, A_GIMME, 0); // 追加情報を格納するためのバイトオフセットの指定 p = max_jit_classex_setup(calcoffset(t_max_jit_foo, obex)); // クラスレジストリからの Jitter クラスの探索 q = jit_class_findbyname(gensym("jit_foo")); // Jitter オブジェクト用の標準メソッドで、Jitter クラスをラップする max_jit_classex_standard_wrap(p, q, 0); // インレット/アウトレット、アシスタンスメソッドの追加 addmess((method)max_jit_foo_assist, "assist", A_CANT,0); } void max_jit_foo_assist(t_max_jit_foo *x, void *b, long m, long a, char *s) { // インレット/アウトレット、アシスタンスを持たない } void max_jit_foo_free(t_max_jit_foo *x) { // 内部の Jitter オブジェクトインスタンスを探索して、解放 jit_object_free(max_jit_obex_jitob_get(x)); // obex エントリによって結びつけられたリソースを解放 max_jit_obex_free(x); } void *max_jit_foo_new(t_symbol *s, long argc, t_atom *argv) { t_max_jit_foo *x; long attrstart; void *o; // max ラッパークラス、および Jitter クラスに基づいて // ラッパーオブジェクトインスタンスを生成 if (x = (t_max_jit_foo *)max_jit_obex_new(class_max_jit_foo, gensym("jit_foo"))) { // 汎用アウトレットの追加 (右端) max_jit_obex_dumpout_set(x, outlet_new(x,0L)); // 必要なら、ノーマルの arg をゲット attrstart = max_jit_attr_args_offset(argc,argv); // Jitter オブジェクトのインスタンス化 if (o = jit_object_new(gensym("jit_foo"))) { // 内部の Jitter オブジェクトインスタンスをセット max_jit_obex_jitob_set(x,o); // アトリビュートのアーギュメントを処理 max_jit_attr_args(x,argc,argv); } else { // インスタンス化失敗, クリーンアップしてエラーを報告 freeobject((void *)x); x = NULL; error("jit.foo: out of memory"); } } return (x); }

オブジェクト構造体

まず最初に行なわなければならないことは 、あなたの Max クラスオブジェクト構造体の定義です。通常どおり、オブジェクト構造体の最初のエントリは、標準の Max オブジェクトでは t_object 型、UI オブジェクトでは t_box 型、MSP オブジェクトでは t_pxobject 型、MSP の UI オブジェクトでは t_pxbox 型でなければなりません。Max/MSP のオブジェクト型の違いに関するより詳しい情報は、Max/MSP 開発者ドキュメントを参照して下さい。Jitter オブジェクトは、これらの内のどのオブジェクト型の中へでもラップすることができます。

さらに、効果的に Jitter クラスをラップするために必要な追加情報やリソースを指すポインタを定義する必要があります。これは、通常、”obex” データと呼ばれます。ここには、Jitter によって、アトリビュート情報、汎用の「ダンプアウト (dumpout)」、内部的な Jitter オブジェクトのインスタンス、インレット/アウトレットのためのマトリックス操作リソース( Matrix Operator resources)、および、シンプルな Max オブジェクトでは必要とされないその 他の補助的なオブジェクト情報などが保存されます。

Max のバージョン 4.5 現在、このような普通の Max オブジェクトのための追加オブジェクト情報を利用する機能がまだ存在しています。これを書いている時点では、この情報は Pattr 開発者ドキュメントの中で、オブジェクトアトリビュートの定義に関連して示されています。これはオブジェクトのパッチャーアトリビュートセットによって保存、操作されます。

あなたの Max クラスの定義 (Defining Your Max Class)

あなたのエクスターナルの main 関数で行なわれるMax クラスの登録では、通常 your_object_name_init というような名前をつけた Jitter クラスの登録関数を呼び出すことから始めなければなりません。その後、Max オブジェクトでsetup 関数によって行う場合と同様に、Max クラスのコンストラクタ、デストラクタ、オブジェクト構造体のサイズ、型指定のあるアーギュメントの定義を始めます。

あなたのラッパークラスが obex データを見つけられるようにするために、各々のインスタンス内でobexへのポインタが置かれる位置のバイトオフセットを指定し、あなたの Max クラスの中でこのポインタを保存するリソースを割当てる必要があります。この操作は、max_jit_classex_setupによって行うことができます。その後、 jit_class_findbyname によって Jitter クラスを探し出し、それを max_jit_classex_standard_wrap 関数によってラップしなければなりません。

max_jit_classex_standard_wrap 関数は、Jitter クラスで定義されている、型を指定されたメソッドをすべて追加し、同時に、透過的な(すなわち、プライベートな)アトリビュートのためのゲッター、びセッターメソッドや、getattributes、getstate、summary、importattrs、exportattrs などのような Jitter オブジェクトに共通なメソッドすべてを追加します。 Jitter クラスをラップしてしまうと、Max オブジェクトに対して、インレット/アウトレット、アシスタンスメソッド、あるいは何か特定のメソッドなど、どのようなメソッドでも追加することができるようになります。Jitter オブジェクトのように、derer や usurp ラッパーを持つメソッドを追加することもできます。そして、これらは、単に従来の addmess 関数を使うのではなく、max_addmethod_defer_lowmax_addmethod_usurp_low 関数によって追加されなければなりません。

コンストラクタ (Constructor)

Max オブジェクトのコンストラクタの内部には、普通の Max エクスターナルを作る場合と多少の違いがあります。あなたのオブジェクトがアトリビュートアーギュメントに応答する場合、コンストラクタは、型を持つ atom アーギュメントの変数を取るように定義しなければなりません。これはA_GIMME という型記号で指定します。あなたのMaxオブジェクトにメモリ割当てを行う場合、従来のnewobject関数の代わりに、max_jit_obex_new 関数を使います。

あなたは、Jitterクラスの名前を、max_jit_obex_new 関数に渡す必要があり、これがあなたのobex データのメモリ割当てや初期化を行います。成功した場合、続いて汎用の「ダンプアウト」アウトレットの追加を行わなければなりません。このアウトレットは、アトリビュートの問い合わせや、情報を提供するためのメソッドに対する応答に使用されます。 max_jit_object_dumpout_set 関数によって行われる *jit_qt_movie* の framedump メソッドによるフレームナンバや、read メソッドのサクセスコードのようなものがこれにあたりますオブジェクトが、max_jit_mop_setup_simple を呼び出すマトリックスオペレータであれば、max_jit_mop_setup_simple が内部で max_jit_object_dumpout_set を呼び出すため、明示的に max_jit_object_dumpout_set を呼び出す必要はありません。

この後、jit_object_new によってJitter オブジェクトのメモリ割当てを行い、これを max_jit_obex_jitob_set によってあなたの obex データの中に保存します。この jitter オブジェクトのインスタンスは、いつでも max_jit_obex_jitob_get 関数によって見つけることができる点に注意して下さい。必要であれば、あなたの Jitter オブジェクトのメモリ割当てを行う前に、非アトリビュートアーギュメントを見ることができ(これらのアーギュメントの場所までの位置は max_jit_attr_args_offset によって返されます)、これをあなたの jitter オブジェクトのコンストラクタで利用することができます。一般的に、Max および Jitter オブジェクトインスタンスのメモリ割当ての後に、Max オブジェクトのインスタンスを渡される max_jit_attr_args によってアトリビュートアーギュメントの処理が行われます。

アトリビュートアーギュメントを、何とかしてあなたの Jitter オブジェクトコンストラクタで使いたいという場合には、あなた自身でアトリビュートアーギュメントを解析する必要があります。Jitter オブジェクトに対するメモリ割当てができない場合(メモリ不足で実行した場合や、 Jitter は存在するがオーソライズされていない場合など)、Max ラッパーオブジェクトをクリーンアップし、NULLを返すことが重要です。

デストラクタ

あなたの Max オブジェクトのデストラクタの中には、jit_object_free による内部の Jitter オブジェクトの解放、および max_jit_obex_free による追加の obex データの解放をつけ加える必要があります。マトリックスオペレータは、通常、マトリックスの入力、および出力に割り当てられたリソースを解放するために、max_jit_mop_free の呼び出しを必要とします。

オブジェクトが jit_object_attach によって登録済みオブジェクトに接続され、通知を受けるようになっている場合、デストラクタの中で jit_object_detach を使って登録済みオブジェクトから切り離し、解放されてしまったオブジェクトのメモリに対して登録済みオブジェクトが通知を行おうとすることによる無効なメモリアクセスを防がなければなりません。オブジェクトの登録と通知に関しては、後の章でより詳しく説明します。

ダンプアウト

アトリビュートのゲッターや、summary、 getattributes といったいくつかの標準的なメソッドが呼び出された場合、Max ラッパーオブジェクトによって、「ダンプアウト( dumpout)」という汎用アウトレットが自動的に用いられます。このアウトレットは、必要ならば他の Max メソッドで利用することも可能です。これを行うためには、outlet_anything とよく似た動作をする max_jit_obex_dumpout 関数によってアクセスする方法が最も簡単ですが、この関数は最初の引数として、アウトレットへのポインタではなく、Max オブジェクトへのポインタを取ります。あなたのコンストラクタの中でセットされた、アウトレットへのポインタは、max_jit_obex_dumpout_get 関数によって問い合わせることができ、標準のアウトレット呼び出しによって使用できます。

しかし、その場合でも、出力のルーティングを行なうことができるように、ダンプアウトアウトレットからの出力は、単なる bang や int、float ではなくシンボルで始まるメッセージであることが推奨されます。そのため、outlet_anything を使うことは最も理にかなっています。

追加のインレット/アウトレット

あなたの Max エクスターナルにインレットやアウトレットを追加する場合に、いくつか注意しなければならないことがあります。

第1に、オブジェクトがマトリックスオペレータである場合、マトリックスインレット、およびアウトレットは、高レベルの max_jit_mop_setup_simple によって、あるいは、低レベルの max_jit_mop_inputs max_jit_mop_outputs の呼び出しによって追加されなければならないという点です。これらのマトリックスオペレータ関数については、「マトリックスオペレータ」の章で述べています。

第2に、オブジェクトが MSP オブジェクトである場合、すべてのシグナルインレット、アウトレットは左端に置き、非シグナルインレット、アウトレットはシグナルインレット、びアウトレットの右側に置かなければならない(すなわち、混在することができない)という点です。

最後に、追加されるインレットにプロキシ(詳細は Max/MSP デベロッパドキュメントに述べられています)を使用して、どのインレットでメッセージを受信したかがオブジェクトにわかるようにしておかなければならないという点です。これは、max_jit_obex_proxy_new によって行うことができます。インレットナンバは0から始まり、左端のインレットに対してはプロキシを作る必要はありません。トリガメッセージがどのインレットで受信されたかを知る必要があるメソッドの中では、max_jit_obex_inletnumber_get 関数を使うことができます。

Max ラッパーアトリビュート (Max Wrapper Attributes)

時には、内部の Jitter クラスではなく、Max ラッパークラス固有のアトリビュートを追加する必要がある場合があるかもしれません。Max ラッパークラスのためののアトリビュートオブジェクトは、前の章で述べた Jitter クラスの場合と同じ方法で定義されます。しかし、これらのアトリビュートは jit_class_addattr 関数によってではなく、その代わりに max_jit_classex_addattr 関数によって Max クラスに追加されます。この関数は max_jit_classex_setup 関数によって返される classex ポインタを取ります。アトリビュートフラグ、および、カスタムゲッター、セッターメソッドは、Jitter クラスで行われた場合と全く同様に定義されます。