マトリックスオペレータの詳細

この章の目的は、マトリックスオペレータがどのようなもので、どのように動作するのかについての詳細を説明することです。マトリックスデータは、一般的に、そのデータが何を意味しているかを考慮しない生データであると考えられます。これは、様々な種類のデータに対して、特別な情報を必要とせずにシンプルな基本的操作を適用することを可能にします。このため、ほとんどの MOP は汎用として使用されます。前の章で示した jit.scalebias の例は、その用語や型、プレーン数の制限などから、ビデオに特化したものと考えられます。しかし、基本的に、これは入力されるマトリックスの各プレーンに対して、積と和の演算を行っているにすぎません。

この章では、MOP のインプットとアウトプットの設定の方法、インプット、アウトプットに対するアトリビュートの制限やリンクについての詳細を述べ、 matrix_calc メソッドの中で処理しなければならない内容について、また、必要に応じてデフォルトの動作をオーバーライドし、 MOP を Max 環境から見えるようにする方法について述べます。

MOP Jitter クラスの定義

マトリックスオペレータクイックスタートで述べたように、 jit_object_new 関数によってMOP のために jit_mop のインスタンスを作り、jit_class_addadornment 関数によって、それをあなたの Jitter クラスにアドーンメントとして追加しなければなりません。jit_mop オブジェクトは、次のような情報を保持しています。それは、オブジェクトがインプットとアウトプットをいくつ持つか、どのような型、プレーン数、ディメンションをサポートするか、インプットが入力されるマトリックスに対してどのように反応しなければならないかということです。この情報は、MOP Max ラッパークラスがそうであるように、実際にインプットとアウトプットのために追加されるマトリックスを保守する Jitter オブジェクトのラッパーにだけ関連します。C、Java、JavaScript? によって使われる場合、MOP によって強制される制限に従うようにマトリックスを渡すことは、プログラマの責任になります。次の例は、jit_mop オブジェクトをインスタンス化して、追加するものです。

// 1インプット、1アウトプットを持つ jit_mop の新しいインスタンスの生成 mop = jit_object_new(_jit_sym_jit_mop,1,1); // jit_mop オブジェクトを、アドーンメントとしてクラスに追加 jit_class_addadornment(_jit_your_class,mop);

jit_mop_io オブジェクト

jit_mop のそれぞれのインスタンスは、いくつかのインプットとアウトプットを持っています。これは、コンストラクタに渡されるインプットとアウトプットの数を表す引数によって指定されたものです。これらのインプット、アウトプットそれぞれに対して、インプットやアウトプットに特有の情報を記録するための、jit_mop_io のインスタンスが存在します。この情報には、型、プレーン数、ディメンションの制限などがあります。次のように、getinputgetoutput メソッドを、整数値によるインデックスを引数として呼び出すことによって、インプット、またはアウトプットオブジェクトにアクセスすることができます。

input = jit_object_method(mop,_jit_sym_getinput,1); output = jit_object_method(mop,_jit_sym_getoutput,1);

一度これらのインプット、またはアウトプットへの参照を取得すると、 jit_mop_io のアトリビュートに対して、値の問い合わせやセットを行うことができるようになります。アトリビュートは、一般的に、次のように設定されます。

types : 許可される型のシンボルのリスト、これらのうちの最初のものがデフォルトになります。
mindim
maxdim
: 許可される個々のディメンションのサイズの最小値と最大値。
mindimcount
maxdimcount
: 許可されるディメンションの数の最小値と最大値。
minplanecount
maxplanecount
: 許可されるプレーンの数の最小値と最大値。
typelink : I/0 が、最も左の入力マトリックスに合わせて、その型を変えなければならないかどうかを決定するフラグ。
dimlink : I/0 が、最も左の入力マトリックスに合わせて、そのディメンションを変えなければならないかどうかを決定するフラグ。
planelink : I/0 が、最も左の入力マトリックスに合わせて、そのプレーン数を変えなければならないかどうかを決定するフラグ。

インプット/アウトプットアトリビュートの制限

デフォルトでは、すべての型、ディメンション、プレーン数、リンクが有効(イネーブル)になっています。あなたの MOP に対して特定の制限を設けたい場合や、特別なインプットやアウトプットに対してデフォルトと異なるリンク動作を持たせたい場合、対応するアトリビュートをセットすることができます。例えば、プレーン数を常に4つのプレーンにセットしたい場合、次のように、minplanecount およびmaxplanecount アトリビュートを4にセットします。

output = jit_object_method(mop,_jit_sym_getoutput,1); jit_attr_setlong(output,_jit_sym_minplanecount,4); jit_attr_setlong(output,_jit_sym_maxplanecount,4);

jit.scalebias の例では、内部的にこれらのアトリビュートを設定するためのユーティリティ関数 jit_mop_single_planecount を呼び出すのではなくminplanecount maxplanecount アトリビュートを使って planecount を設定しています。同様なことは、型やディメンションを制限するために行うこともできます。リンクに関しては、jit.convolve の場合のように、右からのインプットを最も左のインプットのサイズに適応させたくないならば、次のようにdimlink アトリビュートをオフにすれば良いでしょう。

input2 = jit_object_method(mop,_jit_sym_getinput,2); jit_attr_setlong(input2,_jit_sym_dimlink,0);

同様なことは、型やプレーン数のリンクに関しても行うことができます。jit_mop_input_nolinkjit_mop_output_nolink というユーティリティ関数は、これらのリンクに関するアトリビュートを false(ゼロ)に設定します。

ioproc 関数

右側のマトリックス入力の場合、通常、入力されたデータは MOP Max ラッパークラスによってコピーされます。入力されるマトリックスをMOP Max ラッパークラスで受け取ると、ioprocと呼ばれる関数が呼び出され、デフォルトの ioproc が現在のアトリビュート(これは左側のインプットとリンクされているかも知れません)を使ってデータをコピーします。デフォルトの ioproc は、下の jit_mop_ioproc_copy_adapt 関数の中で挙げられているような記号を渡される関数を伴った ioprocメソッドの呼出しによってオーバーライドすることができます。jit_mop_ioproc_copy_adapt 関数は、何らかの制限との衝突を起こさない限り、常にインレットに入力されるマトリックスのアトリビュートに適応します。jit.concat 用の SDK プロジェクトは、このjit_mop_ioproc_copy_adapt 関数の使用法を示しています。

t_jit_err jit_mop_ioproc_copy_adapt(void *mop,void *mop_io, void *matrix) { void *m; // 転送先(デスティネーション)のマトリックス t_jit_matrix_info info; // mop_io から転送先のマトリックスを調べる if (matrix&&(m=jit_object_method(mop_io,_jit_sym_getmatrix))) { // 入力されるマトリックスの情報を取得 jit_object_method(matrix,_jit_sym_getinfo,&info); //mop_io アトリビュートに基づくマトリックス情報の制限 jit_object_method(mop_io,_jit_sym_restrict_type,&info); jit_object_method(mop_io,_jit_sym_restrict_dim,&info); jit_object_method(mop_io,_jit_sym_restrict_planecount,&info); // 転送先マトリックスの情報をセット jit_object_method(m,_jit_sym_setinfo,&info); // frommatrix メソッドでデータをコピー jit_object_method(m,_jit_sym_frommatrix,matrix,NULL); } return JIT_ERR_NONE; }

可変のインプット/アウトプット

あなたの jit_mop を作る際に、インプットやアウトプットに負の引数を与えることによって、可変インプット/アウトプット MOP の指定を行うことができます。可変のインプットやアウトプットを使う場合、あなたのクラス定義の中には、個々のインプット、アウトプットのための jit_mop_io が存在しないため、typedimplanecount、リンク用アトリビュートをセットすることができません。そのため、何かデフォルトの動作が必要な場合は、これを他の方法によって行わなければなりません。例えば、MOP Max ラッパークラスの jit_matrix メソッドをオーバーライドする、あるいは、MOP ラッパークラスの標準の jit_matrix メソッドの中から呼び出される mproc の定義を行うという方法です。SDK の中の、jit.packjit.unpackjit.scissorsjit.glue オブジェクトは可変のインプットとアウトプットを持ったMOP の例です。jit_matrixmproc およびMOP Maxラッパークラスの他のデフォルトのメソッドのオーバーライドに関するより詳しい情報は、この章の後の方で述べられています。

クラスアドーンメントとしての jit_mop の追加

jit_mop オブジェクトのすべてのインプットとアウトプットの設定を終えた後、jit_mop オブジェクトを、jit_class_addadornment 関数によってあなたの Jitter クラスに追加しなければなりません。アドーンメントは jitter クラスからの問い合わせを受けることができます。これは、下に示すような、Jitter クラスのポインタとアドーンメントオブジェクトのクラス名を渡された jit_class_adornment_get の呼出しによっていつでも行うことができます。

// jit_mop オブジェクトをアドーンメントとしてクラスに追加 jit_class_addadornment(_jit_your_class,mop); // jit_mopアドーンメントを調べる mop = jit_class_adornment_get(_jit_your_class,_jit_sym_jit_mop);

マトリックス演算メソッド

MOP Jitter クラスのエントリポイントは matrix_calc メソッドです。このメソッドは、インプットのためのマトリックスのリストとアウトプットのためのマトリックスのリストを渡されます。マトリックスのコピーや、マトリックスの適応を行う動作は、matrix_calc の仕事ではありません。このメソッドは、単にマトリックスが有効で、互換性があることを確認し、そうであるならばこれを処理するだけです。ある種のオブジェクトは、アウトプットマトリックスの dimtypeplanecount、を修正する場合があります(SDK プロジェクトの jit.thin がその例にあたります)。しかし、コピーや、jit_mop_io オブジェクトで定義された MOP/IOの制限に対する適応を実行することは、呼出し側(すなわち、matrix_calc メソッドを呼び出す Max ラッパークラス、または C、Java、Javascript コード)の責任になります。

インプットとアウトプットのリストへのアクセス

あなたの matrix_calc メソッドに引数として渡されるインプットとアウトプットのリストは Jitter オブジェクトです。また、それぞれのインプットとアウトプットへのポインタは、整数による引数とともに getindexメソッドを呼び出すことによって取得されます。 引数には0から始まるリストのインデックスで指定します。この戻り値が NULL でないことを確認しておかなければなりません。次はその例です。

// 該当するインプットとアウトプットのリストから、0番のインデックスを持つインプットと // アウトプットを取得 (get the zeroth index input and output from) in_matrix= jit_object_method(inputs,_jit_sym_getindex,0); out_matrix= jit_object_method(outputs,_jit_sym_getindex,0); // オブジェクト自身、及びインプットとアウトプットのマトリックスが有効であれば処理を行い、 // そうでない場合はエラーを返す if (x&&in_matrix&&out_matrix) { // ... process data ... } else { return JIT_ERR_INVALID_PTR; }

jit_matrix は、jit_matrix のインスタンスを返すgetindex メソッドを持っているため、技術的には、インプットとアウトプットのためのリストを渡す代わりに、jit_matrixのインスタンスを引数として渡すことも可能です。これは、ダイナミックバインディング(動的バインディング)の例にあたります。matrix_calc メソッドの内部の、もう1つのダイナミックバインディングの例は、リストの要素が jit_matrix のインスタンスではなく、jit_mop_io のインスタンスである場合です。しかし、jit_mop_io オブジェクトは jit_matrixの「装飾する」クラスであるため、Jitter がダイナミックバインディングを使用することによって、すべての対応するメソッドは、jit_mop_io によって参照される jit_matrix に渡されます。事実、jit_matrix の標準インターフェイスに対応するあらゆる Jitter オブジェクトは、インプットまたはアウトプットとして渡すことが可能です。このことが理解しづらいのであれば、根底となっている実装についてあまり深く考えず、単に jit_matrix のインスタンスが渡されているとみなすことができます。結局のところ、同じような動作をするのであれば、異なったものでも構わないということです。

マトリックスのロックとアンロック

マトリックスに対する処理を行なう場合、マトリックスのデータやアトリビュートが処理の最中に変更されないように、事前にマトリックスを「ロック」しておく必要があります。マトリックスのロックは、 jit_matrix インスタンスの lock メソッド に整数の引数 1 (真)を与えて呼び出すことで実行できます。処理が終わった後で元に戻すために、現在のロックのステート(状態)を保存しておかなければなりません。ロック処理は、マトリックスのオブジェクトが NULL でないことを確認した後、最初に行なわなければならないことです。次はその例です。

//インプットとアウトプットのマトリックスのロック in_savelock = (long) jit_object_method(in_matrix, _jit_sym_lock,1); out_savelock = (long) jit_object_method(out_matrix, _jit_sym_lock,1); // ... データの処理... out: // マトリックスのロックステートを以前の値に復元 jit_object_method(out_matrix,_jit_sym_lock,out_savelock); jit_object_method(in_matrix,_jit_sym_lock,in_savelock);

マトリックス情報の取得

マトリックスをロックした時点で、あなたは、すでにマトリックスについてのいくつかの情報を取得しています。これは、 t_jit_matrix_info 構造体のインスタンスへのポインタを伴う getinfo メソッドによって行われています。この、 t_jit_matrix_info 構造体は、マトリックスに共通した様々な アトリビュートと、マトリックスデータのデータ構成を保持していますが、これらの情報を一回の呼出しによって得る方法は、個々のアトリビュートに対する問い合わせを行うよりも便利です。 この情報は、通常、matrix_calc メソッドの実行に必要な条件との互換性を確認する目的でテストされます(このメソッドは、C、Java、Javascript から呼び出されるかもしれないため、MOP Max ラッパーがすでにこれらの条件を強制していると仮定することはできません)。また、型、プレーン数、ディメンションに基づいた、適切なポインタ演算を行うために使用されます。さらに、より高次のディメンションは緊密にパックされていないため、ディメンション間のバイトストライドに基づく適切なポインタ演算を行うためにも使用されます。t_jit_matrix_info 構造体のリストを次に示します。

typedef struct _jit_matrix_info { long size; // in bytes (0xFFFFFFFF=UNKNOWN) t_symbol *type; // primitive type longf lags; // matrix flags: my data?, handle? long dimcount; // # of dimensions long dim[JIT_MATRIX_MAX_DIMCOUNT]; // dimension sizes longd imstride[JIT_MATRIX_MAX_DIMCOUNT]; // in bytes long planecount; // # of planes } t_jit_matrix_info;

次は t_jit_matrix_info 構造体に書き込むための getinfo メソッドの呼出し例です。

// fill out matrix info structs for input and output jit_object_method(in_matrix,_jit_sym_getinfo,&in_minfo); jit_object_method(out_matrix,_jit_sym_getinfo,&out_minfo);

データポインタの取得

t_jit_matrix_info 構造体はメタ・データですが、実際のマトリックスデータへはデータポインタを取得することによってアクセスできます。マトリックスの getdata メソッドにポインタへのポインタを渡して呼び出すことによってこれを行います。このポインタはどのような型でも可能ですが、あなたのマトリックスの型や dimstride に基づいてバイト幅のポインタ演算を行う必要があるため、通常 char (または byte )のポインタになります。 このポインタが有効なものであるかどうかを、データを処理する前に確認しておくことは重要です。これは次に示すようになります。

// マトリックスデータポインタの取得 jit_object_method(in_matrix,_jit_sym_getdata,&in_bp); jit_object_method(out_matrix,_jit_sym_getdata,&out_bp); // データポインタが無効な場合、エラーをセットしてクリーンアップ if (!in_bp) { err=JIT_ERR_INVALID_INPUT; goto out;} if (!out_bp) { err=JIT_ERR_INVALID_OUTPUT; goto out;}

データの処理

データ処理のコードを matrix_calc の内部に組み込むことは可能ですが、潜在的にマルチプロセッサ処理の能力を持つ、再帰によって N 次元処理を行うことができる他のルーチンを利用する方法が一般的です。N次元再帰処理関数(通常、myobject_calculate_ndim)については、次のセクションで述べています。この calculate_ndim 関数に対しては、あなたのオブジェクトへのポインタ、および演算する上で考慮しなければならない全体のディメンション数、ディメンションサイズを、必要なマトリックス情報構造体と個々のインプット、アウトプットへのデータポインタと一緒に渡さなければなりません。次のコードで示すように、このメソッドを直接呼び出すことが可能です。

// カレントのスレッドで calculate_ndim 関数を直接呼び出す jit_scalebias_calculate_ndim(x, dimcount, dim, planecount, &out_minfo, out_bp);

また、このメソッドを Jitter 1.5 で提供されている並列処理ユーティリティ関数を使って呼び出すこともできます。これにより、マルチプロセッサが利用可能な時には、大きなマトリックスの処理を自動的にマルチプロセッサに対して横断的に送ることができます。この図は、並列処理ユーティリティのデータ転送と演算を示しています。

並列処理は、マトリックスを小さなマトリックスに分解し、それぞれがオリジナルのインプットとアウトプットの部分領域を参照することによって行われます。新しいオブジェクトは作られず、単に t_jit_matrix_info 構造体とオフセットデータポインタを追加しています。jitter 1.5 はこの目的のために処理スレッドのプールを保守します。これによって、スレッドを生成するオーバーヘッドは生じず、スレッド同期のための小さなオーバーヘッドが生じるだけで済みます。

あなたのオブジェクトがある種の空間的操作(コンボリューション - 畳み込み演算、ローテーション - 回転)、スケーリング - 拡大縮小など)を行なう場合、並列処理ユーティリティを使用するマトリックスをあらかじめ分割しておく必要があります。あるいは、並列処理の使用を避けて、現在のスレッドから直接呼び出します。これは注意しておくべき重要な点です。 jit.scalebias の例では、一度に1つのピクセルを処理する(すなわち、点ごとの操作を行なう)だけなので、本質的に並列処理が可能です。そのため、この例では下に示すように、マルチプロセッサを利用しています。

// 演算:マルチプロセッサが実装されている場合、マルチスレッドでclculate_ndim 関数を // 呼び出すために、並列処理ユーティリティ関数を使用しています。 jit_parallel_ndim_simplecalc2( (method)jit_scalebias_calculate_ndim, x, dimcount, dim, planecount, &in_minfo, in_bp, &out_minfo, out_bp, 0 /* フラグ1 */, 0 /* フラグ2 */);

重要な注:あなたのオブジェクトが点ごとの処理を行なうものかどうかわからない場合、あるいは、アルゴリズムを並列に処理する方法を完全に理解していない場合、並列処理ユーティリティを使用すべきではありません。この場合、単純に、関数を直接呼び出さなければなりません。

N次元マトリックスの処理

マトリックスオペレータ・クイックスタートの章では、jit.scalebiasオブジェクトを例にとり、2D スライスの中の N 次元データを処理するための再帰的関数を定義する方法について説明しました。この例では、4 つのプレーンの char データに制限されていましたが、多くの Jitter オブジェクトはどのような型、プレーン数でも動作します。すべての型、およびプレーン数をサポートするためには、いくつかのケースの扱いを知っておく必要があります。それは、どのようにデータをステップスルー(順次読み出し)するか、そして、データをどのような型として解釈するかといったことで、これにより適切な演算を行うことが可能になります。この問題に対するアプローチにはいくつかの方法があり、また、いくつかの最適化に関して行うべき決定があります。これらのケースの扱いは全て、多少厄介なものかも知れません。そのため、最初にオブジェクトを開発する時には、1つの型、および1つのプレーン数に対象を絞っておき、そこで演算の適切な定義を完了した上でのみ、あらゆる型を処理できるようにコードを強化することを試みたり、特定のケースにおける最適化を考えたりするという方法が、理にかなっていると言えるでしょう。C におけるマクロの使用や、C++ のテンプレートは、良いコードの再利用を調べる上で役に立つかも知れません。コードの最適化について言うと、通常、最適化を試し、実行するための適切な単位要素は、可能な限り条件分岐を避けるために「最も内部のループ」になります。この関数は問題解決のための心臓部となるもので、あなたはこれを自分自身のカスタムオブジェクトに追加することになります。このデータを処理するための「王道」は存在しないため、再帰的なN次元処理関数のためのコードリストについては、これ以上言及しません。しかし、良いサンプルを含むSDK プロジェクトとして次のようなものがあります。jit.clip はプレーンに関してそれぞれ独立に作用し、点ごとの演算を行います(いくつかの指定範囲のための制限値を持ちます)。jit.rgb2lumaはプレーンに関してそれぞれ独立に作用し、点ごとの演算を行います(輝度の処理のために RGB カラーをサポートします)。また、 jit.transposeは、プレーンに関してそれぞれ独立に作用し、空間処理(行は列になります)を行います。N次元マトリックス処理に関する、より以上のアイデアを得るためには、入手できる 2D シグナル処理や画像処理に関する書籍のうちの1つを読んでみることを推奨します。これらのコンセプトはほとんど、容易に、より高次のディメンションに対して一般化できます。

MOP Max ラッパークラスの定義

MOP Max ラッパークラスは通常、多くのデフォルトの動作を持っています。これは、 jit_mop Jitter クラスアドーンメントとユーザが指定したフラグに基づいて max_jit_classex_mop_wrap 関数によってセットアップされます。すべてのデフォルト動作、あるいは特定の機能をオーバーライドすることができます。デフォルト動作をすべてオーバーライドしたい場合には、max_jit_classex_mop_wrap 関数の呼出しを行なう際に、 MAX_JIT_MOP_FLAGS_OWN_ALL を使うことができます。jit_mop アドーンメントを利用する必要がある場合には、jit_class_adornment_get メソッドを Jitter クラス上で呼出すことによって、jit_mop を調べることができます。jit_mop_io の入力と出力は、この章で既に述べた MOP Jitter クラス定義の中でセットされる場合と同様に、問い合わせを受け付け、アトリビュートの調査に応じることができます。次は、 jit.scalbias オブジェクトの jit_mop アドーンメントを調べる方法の例です。

// Jitter クラス名を調べます。 jclass = jit_class_findbyname(gensym("jit_scalebias")); // jit_mop アドーンメントを調べます。 mop = jit_class_adornment_get(jclass,_jit_sym_jit_mop);

jit_matrix メソッドのオーバーライド (Overriding the jit_matrix Method)

デフォルトでは、jit_matrix メソッドが自動的に追加され、マトリックスのコピーや入力されるデータに基づく演算を行ないます。最も一般的な MOP では、デフォルトの jit_matrix メソッドをそのまま使用します。しかし、デフォルトの MOP メソッドをオーバーライドし、特別な動作を行なわせる必要があるインスタンスがあります。これには、SDKの jit.opの例のように入力されるデータを記録する場合、jit.pack jit.str.opの例のように標準のコピーや適合以外の動作を行なう場合、あるいは、jit.noiseの例のように jit.matrix を全く動作しないようにしたい場合などがあります。デフォルトの jit_matrix メソッドが定義されないようにするためには、max_jit.classex_mop_wrap 関数を呼び出す際に MAX_JIT_MOP_FLAGS_OWN_JIT_MATRIX というフラグを使用します。独自のjit_matrix メソッドを定義するためには、jit_matrix シンボルにバインドされた A_GIMME によるメソッドを main 関数に追加します。次は、jit.opからの例です。

// 独自の jit_matrix メソッドを main() に追加します。 addmess((method)max_jit_op_jit_matrix, "jit_matrix", A_GIMME, 0); void max_jit_op_jit_matrix(t_max_jit_op *x, t_symbol *s, short argc,t_atom *argv) { if (max_jit_obex_inletnumber_get(x)) { // 右の入力でマトリックスを受信すると、floatint に // 優先して、これを記録します。 x->last = OP_LAST_MATRIX; } // デフォルトのjit_matrix メソッドに処理を渡します。 max_jit_mop_jit_matrix(x,s,argc,argv); }

jit.pack jit.str.op の例はやや複雑で、デフォルトの jit_matrix メソッドが実行するタスクの種類をよく表しているものでもあります。

bang および outputmatrix メソッドのオーバーライド

一般的に、MOP の Max ラッパークラスには、bang および outputmatrix メソッドがあります。デフォルトでは、通常、この2つのメソッドは同じもので、どちらも最も最後に計算されたマトリックスを出力します。SDK の jit.3m の例のような、マトリックスの出力を行なわない特定のオブジェクトでは、たいていの場合、これらのメッセージを独自の bang メソッド、場合によっては outputmatrix メソッドでオーバーライドしています。これらのメソッドは、max_jit_classex_mop_wrap 関数を呼び出す際に、MAX_JIT_MOP_FLAGS_OWN_BANG およびMAX_JIT_MOP_FLAGS_OWN_OUTPUTMATRIX フラグを使用することによってオーバーライドできます。このフラグは通常、2ついっしょに渡されます。

name, type, dim, および planecount アトリビュートのオーバーライド

左端以外の、インプットとアウトプットそれぞれには、デフォルトで、入出力されるマトリックスに関する問合せ、設定を行なうことができるアトリビュートが追加されます。このアトリビュートには、name (名前)、type(型)、dim (ディメンション)、planecount(プレーン数)があります。このアトリビュートには、name (名前)、type(型)、dim (ディメンション)、planecount(プレーン数)があります。非常に特殊な処理を実行する場合、デフォルトのアトリビュートの動作をオーバーライドする必要が生じるかもしれませんが、SDK のサンプルで、これを行なっているものはありません。nametypedimplanecount アトリビュートの追加が行なわれないようにするためには、max_jit_classex_mop_wrap 関数の呼び出しの際に、 MAX_JIT_MOP_FLAGS_OWN_NAMEMAX_JIT_MOP_FLAGS_OWN_TYPE MAX_JIT_MOP_FLAGS_OWN_DIMMAX_JIT_MOP_FLAGS_OWN_PLANECOUNT というフラグを使用します。独自のアトリビュートの定義を行なうには、オーバーライドしたいアトリビュート名を使って、Max ラッパークラスでアトリビュートを定義する場合と同じ方法で行います。

clear および notify メソッドのオーバーライド

clear および notify メソッドは、デフォルトで追加されます。デフォルトの clear メソッドは、それぞれの入力、出力マトリックスを消去します。デフォルトの notify メソッド、 max_jit_mop_notify は、MOP によって維持されているマトリックスに変更があった場合、常に呼び出されます。このデフォルトの notify(通知)に加えて、何らかの通知が必要である場合、入出力されるマトリックスに関する必要なメンテナンスを行なうことができるようにmax_jit_mop_notify 関数を呼び出すことが重要です。この例は SDK の jit.notify で示しています。この2つのメソッドは、max_jit_classex_mop_wrap 関数を呼び出す際に、それぞれ MAX_JIT_MOP_FLAGS_OWN_CLEARMAX_JIT_MOP_FLAGS_OWN_NOTIFY というフラグを使用することによってオーバーライドできます。オブジェクトの登録と通知に関する詳細は後の章で述べます。次に jit.notify notify メソッドの例を示しておきます。

// s はサーバ名、msg はメッセージ、ob はサーバへのオブジェクトポインタ、 // data 与えられたメッセージに対してサーバから提供される追加データです。 void max_jit_notify_notify(t_max_jit_notify *x, t_symbol *s, t_symbol *msg, void *ob, void *data) { if (msg==gensym("splat")) { post("notify: server=%s message=%s",s->s_name,msg->s_name); if (!data) { error("splat message NULL pointer"); return; } // 右端のアウトレットを使って出力を行ないます。ここで、data t_atom[3] // へのポインタであることは、たまたま知っていただけです。 max_jit_obex_dumpout(x,msg,3,(t_atom *)data); } else { // デフォルトの Max MOP notifyメソッドに処理を渡します。 max_jit_mop_notify(x,s,msg); } }

adapt および outputmode アトリビュートのオーバーライド

adapt および outputmode アトリビュートは、MOP Max ラッパーにデフォルトで追加されます。adapt アトリビュートは、入力マトリックスへの適応を行なうかどうかを決定します。また、outputmode アトリビュートは、アウトプットで新しい出力マトリックスの演算を行なうか、あるいは最後に演算を行なったマトリックスを出力するか(freeze)、もしくは入力マトリックスをそのまま出力するか(bypass)を決定します。max_jit_classex_mop_wrap の呼び出しの際に、 MAX_JIT_MOP_FLAGS_OWN_ADAPT、および MAX_JIT_MOP_FLAGS_OWN_OUTPUTMODE というフラグを使うと、adapt および outputmode のデフォルトのアトリビュートが追加されないようにすることができます。独自のアトリビュートの定義を行なうには、オーバーライドしたいアトリビュート名を使って、Max ラッパークラスでアトリビュートを定義する場合と同じ方法で行います。

mproc メソッドの定義

様々な処理において、デフォルトの Jit_matrix メソッドやアトリビュートの完全なオーバーライドが必要になることはありません。単に Jitter クラスの matrix_calc メソッドやアウトレット用関数を呼び出す方法をオーバーライドする必要があるだけであれば、デフォルトの処理の代わりに呼び出される mproc メソッドを定義することによって行なうことができます。jit.3m SDK プロジェクトは Jitter クラスの matrix_calcメソッドを呼び出した後、デフォルトの jit_matirix メッセージを出力するのではなく、その代わりに Jitter クラスのアトリビュートの問合せを行い、max メッセージを出力します。

void max_jit_3m_mproc(t_max_jit_3m *x, void *mop) { t_jit_err err; // 内部の Jitter オブジェクトの matrix_calc メソッドを呼び出します。 if (err=(t_jit_err) jit_object_method( max_jit_obex_jitob_get(x), _jit_sym_matrix_calc, jit_object_method(mop,_jit_sym_getinputlist), jit_object_method(mop,_jit_sym_getoutputlist))) { // エラーがあれば報告します。 jit_error_code(x,err); } else { // Jitterクラスの問合せを行ない、アウトレットの呼び出しを行ないます。 max_jit_3m_bang(x); } }

Max クラスのコンストラクタ/デストラクタ

マトリックスオペレータ・クイックスタート で述べたように、Max クラスのコンストラクタの中では、MOP のインプット、アウトプットで必要となるマトリックスを、対応するマトリックスインレット、アウトレットに割り当て、マトリックスのアーギュメントと他の MOP の設定を処理しなければなりません。さらに、デストラクタでは、MOPリソースの開放を行なわなければなりません。通常、標準の関数 max_jit_mop_setup_simplemax_jit_mop_free を使ってこれらをすべて処理することができます。しかし、いくつかのインスタンスでは、特別な処理が必要になる場合もあります。

可変な入出力

max_jit_mop_setup_simple 関数は、必要なプロキシインレット、アウトレットや内部のマトリックスを定義するために、 max_jit_mop_inputs max_jit_mop_outputs を呼び出します。 デフォルトの動作の例として、これらの関数のリストを示しておきます。さらに、SDK プロジェクトにある jit.scissorsjit.gluejit.packjit.unpack をより詳しく調べてみることをお勧めします。

t_jit_err max_jit_mop_inputs(void *x) { void *mop,*p,*m; long i,incount; t_jit_matrix_info info; t_symbol *name; // オブジェクトの MOP アドーンメントを調べます。 if (x&&(mop=max_jit_obex_adornment_get(x,_jit_sym_jit_mop))) { incount= jit_attr_getlong(mop,_jit_sym_inputcount); // プロキシインレットの追加、および左端のインレットを除くすべての // インプット用のマトリックスの追加を行ないます。 for (i=2;i<=incount;i++) { max_jit_obex_proxy_new(x,(incount+1)-i); // 右から左へ if (p=jit_object_method(mop,_jit_sym_getinput,i)) { jit_matrix_info_default(&info); max_jit_mop_restrict_info(x,p,&info); name = jit_symbol_unique(); m = jit_object_new(_jit_sym_jit_matrix,&info); m = jit_object_register(m,name); jit_attr_setsym(p,_jit_sym_matrixname,name); jit_object_method(p,_jit_sym_matrix,m); jit_object_attach(name, x); } } return JIT_ERR_NONE; } return JIT_ERR_INVALID_PTR; } t_jit_err max_jit_mop_outputs(void *x) { void *mop,*p,*m; long i,outcount; t_jit_matrix_info info; t_symbol *name; if (x&&(mop=max_jit_obex_adornment_get(x,_jit_sym_jit_mop))) { outcount = jit_attr_getlong(mop,_jit_sym_outputcount); // アウトレットの追加、およびすべてのアウトプット用のマトリックスの追加を行ないます。 for (i=1;i<=outcount;i++) { max_jit_mop_matrixout_new(x,(outcount)-i);// 右から左へ if (p=jit_object_method(mop,_jit_sym_getoutput,i)) { jit_matrix_info_default(&info); max_jit_mop_restrict_info(x,p,&info); name = jit_symbol_unique(); m = jit_object_new(_jit_sym_jit_matrix,&info); m = jit_object_register(m,name); jit_attr_setsym(p,_jit_sym_matrixname,name); jit_object_method(p,_jit_sym_matrix,m); jit_object_attach(name, x); } } return JIT_ERR_NONE; } return JIT_ERR_INVALID_PTR; }

マトリックスアーギュメント(引数)

max_jit_mop_setup_simple は任意のマトリックスアーギュメントを取得するために max_jit_mop_matrix_args 関数を呼び出します。そして、マトリックスが存在していれば、リンクされたインプット、アウトプットを送り、adapt アトリビュートを無効にします。次のリストはデフォルトの動作を示しています。

t_jit_err max_jit_mop_matrix_args(void *x, long argc, t_atom *argv) { void *mop,*p,*m; long incount,outcount,attrstart,i,j; t_jit_matrix_info info,info2; if (!(mop=max_jit_obex_adornment_get(x,_jit_sym_jit_mop))) return JIT_ERR_GENERIC; incount= jit_attr_getlong(mop,_jit_sym_inputcount); outcount = jit_attr_getlong(mop,_jit_sym_outputcount); jit_matrix_info_default(&info); attrstart = max_jit_attr_args_offset(argc,argv); if (attrstart&&argv) { jit_atom_arg_getlong(&info.planecount, 0, attrstart, argv); jit_atom_arg_getsym(&info.type, 1, attrstart, argv); i=2; j=0; while (i<attrstart) { //ディメンション jit_atom_arg_getlong(&(info.dim[j]), i, attrstart, argv); i++; j++; } if (j) info.dimcount=j; jit_attr_setlong(mop,_jit_sym_adapt,0); //adaptオフ } jit_attr_setlong(mop,_jit_sym_outputmode,1); for (i=2;i<=incount;i++) { if ((p=jit_object_method(mop,_jit_sym_getinput,i)) && (m=jit_object_method(p,_jit_sym_getmatrix))) { jit_object_method(m,_jit_sym_getinfo,&info2); if (jit_attr_getlong(p,_jit_sym_typelink)) { info2.type = info.type; } if (jit_attr_getlong(p,_jit_sym_planelink)) { info2.planecount = info.planecount; } if (jit_attr_getlong(p,_jit_sym_dimlink)) { info2.dimcount = info.dimcount; for (j=0;j<info2.dimcount;j++) { info2.dim[j] = info.dim[j]; } } max_jit_mop_restrict_info(x,p,&info2); jit_object_method(m,_jit_sym_setinfo,&info2); } } for (i=1;i<=outcount;i++) { if ((p=jit_object_method(mop,_jit_sym_getoutput,i)) && (m=jit_object_method(p,_jit_sym_getmatrix))) { jit_object_method(m,_jit_sym_getinfo,&info2); if (jit_attr_getlong(p,_jit_sym_typelink)) { info2.type = info.type; } if (jit_attr_getlong(p,_jit_sym_planelink)) { info2.planecount = info.planecount; } if (jit_attr_getlong(p,_jit_sym_dimlink)) { info2.dimcount = info.dimcount; for (j=0;j<info2.dimcount;j++) { info2.dim[j] = info.dim[j]; } } max_jit_mop_restrict_info(x,p,&info2); jit_object_method(m,_jit_sym_setinfo,&info2); } } return JIT_ERR_NONE; }