第3回UBオブジェクト開発講座は、サンプルとして提供されているmaximumオブジェクトを題材に、そのソース・コードの概要を説明します。これは、以前に公開した「Maxオブジェクト開発技法」から主要部を抜き出し、MaxMSP UB SDK用にアップデートしたものです。
日本語コメントを付けたソース・コードは、こちらからダウンロードしてください。
maximum-jcommented.zip
以下は要点のみをザザっと俯瞰しています。
■処理の概要
Maxオブジェクトのソース・コードは、大きく3つの部分から構成されています。
(1) メイン関数 main()【必須】
Max起動時やパッチを開いた時など、オブジェクトが読み込まれた時に呼び出され、クラスとして必要な初期化などの設定を行います。具体的には、以下の2つの処理が必須ですが、必要に応じてその他の処理を行っても構いません。
・クラスの設定を行う。setup()【必須】
・メッセージ処理関数を登録する。【ほぼ必須】
(2) メッセージ処理関数 maximum_bang()など【ほぼ必須】
オブジェクトにメッセージが送られた時に対応する関数が呼び出されます。メッセージ処理関数の引数は、その登録によって異なります。
(3) オブジェクト生成関数 maximum_new()【必須】
オブジェクト生成時に呼び出され、インスタンスとして必要な初期化などの設定を行います。具体的には、以下の3つの処理が必須ですが、必要に応じてその他の処理を行っても構いません。
・オブジェクトを生成する。newobject()【必須】
・アーギュメントの処理、インレット、アウトレットの作成などを行う。
(右から左への順序で。第1インレットはnewobject()が自動的に作成する。)
・生成したオブジェクトへのポインタを関数の戻り値として返す。【必須】
なお、maximumでは必要がありませんが、オブジェクト生成関数において、メモリの確保やデバイスのオープンなどを行った場合は、オブジェクト消滅関数を登録(setup()の3番目の引数で指定)し、その関数でメモリの解放やデバイスのクローズなどの処理を行うようにします。
■メッセージ処理関数の登録
メッセージ処理関数を登録するには、以下の関数を用います。これらはext_proto.hに定義されています。
void addbang(method f); // bangメッセージ処理関数の登録
void addint(method f); // 整数メッセージ処理関数の登録
void addfloat(method f); // 実数メッセージ処理関数の登録
void addinx(method f, short n); // 第nインレットでの整数メッセージ処理関数の登録
void addftx(method f, short n); // 第nインレットでの実数メッセージ処理関数の登録
fはメッセージ処理関数へのポインタ、nはインレット番号(0始まり、第2インレットは1)
void addmess(method f, char *s, short type, ...); // 任意のメッセージ処理関数の登録
sはメッセージ文字列、typeはアーギュメントのデータ・タイプで、アーギュメントの数に応じて並べ、最後は0で終えます。詳しくは、次の「メッセージ処理関数への引数」最後の部分を参照してください。
オブジェクトがメッセージを受け取る時に、登録されたメッセージ処理関数に従って、メッセージやアーギュメントがチェックされ、一致しなければ自動的にエラーとなります。
■メッセージ処理関数への引数
メッセージ処理関数の種類によって、以下のように受け取る引数は異なります。
bangメッセージ処理関数は、次の引数を受け取ります。
void maximum_bang(t_maximum *x)
xはオブジェクト構造体へのポインタを表します。
bangメッセージはアーギュメントを持ちませんから、これ以外の引数はありません。
整数メッセージ処理関数は、次の引数を受け取ります。
void maximum_int(t_maximum *x, long n)
xはオブジェクト構造体へのポインタ、nは送られた整数を表します。
リスト・メッセージ処理関数は、次の引数を受け取ります。
void maximum_list(t_maximum *x, t_symbol *s, short ac, t_atom *av)
sはメッセージのシンボル(list)、acはリストの要素数、avはリストの最初の要素へのポインタを表します。
「jack n m」というメッセージの処理関数は以下のようになります。ここでは、nとmは整数で、nは省略不可、mは省略可能とします。
void maximum_jack(t_maximum *x, long n, long m)
xはオブジェクト構造体へのポインタ、nとmは送られた整数を表します。
なお、このメッセージ処理関数を登録するには、以下のように記述します。
addmess((method)maximum_jack, "jack", A_LONG, A_DEFLONG, 0);
■データ・タイプ
Maxが用いるデータの種類を表すデータ・タイプは、ext_mess.hに以下のように定義されています。
#define A_NOTHING 0 // 無(データはないことを示す)
#define A_LONG 1 // 整数
#define A_FLOAT 2 // 実数
#define A_SYM 3 // シンボル
#define A_OBJ 4 // オブジェクト、アーギュメントの配列
#define A_DEFLONG 5 // デフォルト値が0である整数
#define A_DEFFLOAT 6 // デフォルト値が0.0である実数
#define A_DEFSYM 7 // デフォルト値が""であるシンボル
#define A_GIMME 8 // アーギュメントの配列、t_atomの配列であることをチェック
#define A_CANT 9 // タイプチェック不能/不要であるアーギュメント
#define A_SEMI 10 // ;(セミコロン)
#define A_COMMA 11 // .(コンマ)
#define A_DOLLAR 12 // $(ドルマーク)
#define A_DOLLSYM 13 // $(ドルマークのシンボル)
#define A_GIMMEBACK 14 // アーギュメントの配列、関数でチェックして戻す
■データ構造
Maxのデータ(整数、実数、シンボル)はすべてt_atom(Atom)構造体で表され、データの種類を表すデータ・タイプと実際のデータ値の2つの要素から構成されています。データ値は共用体として、異なるタイプのデータが納められるようになっています。
union word // データ値の共用体
{
long w_long;
float w_float;
struct symbol *w_sym;
struct object *w_obj;
};
typedef struct atom // データの構造体
{
short a_type; // データ・タイプ
union word a_w; // データ値
} t_atom, Atom;
例:t_atom *data の場合、
データの値を参照するには、データ・タイプを確認した上で、データの値を取り出します。例えば、データが整数である場合は、以下のようになります。データ・タイプが明確であれば、データ・タイプの確認は省略しても構いません。
if (data->a_type == A_LONG)
value = data->a_w.w_long;
データに値を代入する場合は、データ・タイプを設定した上で、実際の値を代入します。例えば、整数を代入するには以下のようになります。
data->a_type = A_LONG;
data->a_w.w_long = 100;