Max 5 API Reference
Max オブジェクトは、C言語でかかれています。そして、Max の API は C に基づいています。C++ を使用することは可能ですが、APIレベルではサポートしていません。Max オブジェクトを C で書く場合には、5つの基本的な作業が必要になります。
1) ヘッダファイル(通常、ext.h および ext_obex.h)を正確にインクルードします。
2) オブジェクトの C 構造体を宣言します。
3) main から呼び出される初期化ルーチンを書き、これにクラスを定義します。
4) クラスの新しいインスタンスを生成する new instance ルーチンを書きます。このルーチンはMax上でオブジェクトが作られたとき、あるいはオブジェクトボックスにオブジェクト名が入力されたときに実行されます。
5) メソッド(あるいはメッセージハンドラ)を書きます。 これにより、オブジェクトが行なう処理を実装します。
それでは、これらをそれぞれ詳しく見ていきましょう。ここでの例は、simplemax example プロジェクトを引用しています。そのため、このファイルを開いておくとよりわかりやすいでしょう。
基本的な Max API のほとんどは ext.h および ext_obex.h というファイルに収められています。これらは、基本的にすべてのオブジェクトで必要なものです。より特殊なオブジェクトでは、これ以外にも特定のファイルをインクルードする必要があります。
このヘッダファイルはクロスプラットフォームです(プラットフォームに依存しません)。
#include "ext.h" // 常に、これを最初にインクルードしなければなりません。これに続けて、ext_obex.h や 他のヘッダファイルを指定します。.
typedef struct _simp { t_object s_obj; // t_object ヘッダ long s_value; // 任意のデータフィールド } t_simp;
この構造体宣言は、関数のプロトタイプ宣言の中で使用されます。そのため、関数のプロトタイプ宣言の前にこれを宣言しておく必要があります。
初期化ルーチンは main 関数の中で呼び出されなければなりません。これは、Max があなたのオブジェクトを最初にロードした際に呼び出されます。初期化ルーチンでは、1つ以上のクラスを定義します。クラスの定義は次のように構成されます。
1) Max に対し、あなたのオブジェクトの構造体のサイズを知らせ、インスタンスの生成、消滅の方法を指定します。
2) オブジェクトの動作を実装するためのメソッドを定義します。
3) 場合によっては、アトリビュートの定義を行ないます。アトリビュートはオブジェクトの持つデータを記述するためのものです。
4) オブジェクトのクラスをネームスペース(名前空間)に登録します。
次は、simp というクラスの初期化ルーチンの例です。
static t_class *s_simp_class; // main() 関数で設定される、このオブジェクトのクラス定義へのグローバルポインタ。 int main() { t_class *c; c = class_new("simp", (method)simp_new, (method)NULL, sizeof(t_simp), 0L, 0); class_addmethod(c, (method)simp_int, "int", A_LONG, 0); class_addmethod(c, (method)simp_bang, "bang", 0); class_register(CLASS_BOX,c); s_simp_class = c; return 0; }
class_new() は インスタンス生成ルーチン(new instance ルーチン(後述))、消滅関数(free 関数(このケースでは NULL を渡しているため存在しません))、構造体のサイズ、現在では使用されていない引数、インスタンスを生成するときに書き込まれるアーギュメントの記述(このケースでは、アーギュメントがないため 0 を渡しています)を引数として用い、クラスを作成します。
class_addmethod() は、Cの関数をテキストシンボルにバインドします。ここでは int と bang とい2つのメソッドが定義されています。
class_register() は、クラスを CLASS_BOX というネームスペースに登録します。これはユーザがオブジェクトボックスにオブジェクト名を入力した際に、このクラスの検索が行なわれることを意味します。
最後に、作成したクラスをグローバルポインタに割り当て、新しいインスタンスを生成する際にこれを利用することができるようにします。
より複雑なクラスでは、さらに多くのメソッドを宣言します。多くの場合、特定の API 機能を実装するためにメソッドを宣言することになります。UI オブジェクトを作成する場合は特にそうです。
標準的な インスタンス生成ルーチン(new instanceルーチン)では、あなたのクラスのインスタンスを生成するためにメモリを割り当て、このインスタンスの初期化を行ないます。このルーチンは、新しく生成されたインスタンスへのポインタを返します。
次は、simp オブジェクトのインスタンス生成(new instance)ルーチンの例です。
void *simp_new() { t_simp *x = (t_simp *)object_alloc(s_simp_class); x->s_value = 0; return x; }
1行目では、初期化ルーチンの中で定義した s_simp_class というグローバル変数を用いて、クラスの新しいインスタンスを生成しています。基本的に、インスタンスはクラスによって定義されたサイズのメモリブロックであり、このブロックへのポインタを使ってメッセージを正しく送ることができます。
次の行では、独自に設定したデータを初期化しています。より複雑なオブジェクトでは、インレットやアウトレットの作成など、ここで数多くの処理を行ないます。デフォルトでは、作成されたオブジェクトには1つのインレットがあり、アウトレットはありません。
そして、最後の行では新しく生成されたインスタンスへのポインタを返しています。
ここまでで、オブジェクトの実際の動作を定義する準備ができました。オブジェクトの動作はC の関数として書かれ、オブジェクトにメッセージが送信された際に呼び出されます。ここで示している簡単な例では、2つの関数しか書いていません。simp_int はオブジェクトが数値を受信した場合に呼び出されます。 この関数は受信した数値を s_value というフィールドに格納します。simp_bang はオブジェクトが bang を受信した際に呼び出されます。この関数は受信した値を Max ウィンドウに表示します。このようなわけで、このオブジェクトはほとんど役には立ちません。
あなたがCで書く 関数は、メッセージが必要とするアーギュメントを伴って宣言されます。すべての関数は、最初の引数としてオブジェクト自身へのポインタを渡されます。int メッセージを取り扱う関数の場合、2番目に long として1つの引数が渡されます。bang メッセージを取り扱う関数の場合、追加の引数は渡されません。
次は、int メソッドです。
void simp_int(t_simp *x, long n) { x->s_value = n; }
これは、アーギュメントとして渡された値を、インスタンス内にある内部の記憶領域に保存するだけのものです。
次は、bang メソッドです。
void simp_bang(t_simp *x) { post("value is %ld",x->s_value); }
post() 関数は printf() と同じようなものですが、テキストを Max ウィンドウに表示させます。post() はデバッグの際に非常に役に立ちます。特に、デバッガが示す値を見るためにユーザとのインタラクションやリアルタイム演算を止めることができないような場合には有効です。
さらに、オブジェクトに浮動小数点数が送られた際に呼び出される float メッセージを追加することができます。以下のコードをオブジェクトの初期化ルーチンに追加して下さい。
class_addmethod(c, (method)simp_float, "float", A_FLOAT, 0);
その後、浮動小数点数を受信した場合のメソッドを次のように書いて下さい。
void simp_float(t_simp *x, double f) { post("got a float and it is %.2f", f); }