Max 5 API Reference

アトムとメッセージ

Max オブジェクトは、メッセージを受け取ると、オブジェクトのクラスを使ってメッセージセレクタ("int"、"bang"、"set" など)を検索し、それに結合された C の関数(メソッド)を呼び出します。この結合は、初期化ルーチンの中で class_addmethod() を使って作成したものです。検索に失敗すると、"object doues'nt understand message" というエラーメッセージが Max ウィンドウに表示されます。

メッセージセレクタは文字列ではなく、シンボル( t_symbol )と呼ばれる特別なデータ構造体になっています。シンボルは文字列と値を保持しますが、より重要な点は、Max の中のすべてのシンボルがユニークであるということです。これにより、2つのシンボルが同じであるかどうかの比較は、2つの文字列に含まれている文字の比較ではなく、ポインタの比較によって行なうことができます。

「データ」、あるいはメッセージのアーギュメント部分が存在する場合、これらは アトム( t_atom)の配列の形で送信されます。 アトム(atom)は、整数、浮動小数点数、シンボル、あるいは他のオブジェクトへのポインタを持つことができる構造体で、タグによって識別されます。メッセージの送信や受信には、シンボルとアトムを用います。

シンボルとアトムの使用例として、ここでは、アウトレットからメッセージを送信する方法を示します。仮に、"green 43 crazy 8.34" というメッセージを送信したいとします。このメッセージには、"green" というセレクタと3つのアトムによる配列が含まれます。

まず、インスタンス生成ルーチン(new instance ルーチン)の中で、outlet_new によって汎用のアウトレットを作成します。

        x->m_outlet = outlet_new((t_object *)x, NULL);

第2引数の NULL は、アウトレットが任意のメッセージを送信することができるということを示しています。第2引数が "int" や "set" のような文字列になっている場合、このアウトレットは指定されたメッセージだけを送信できます。intout() が、実際には単にoutlet_new(x,"int") ではないかと疑問に思うことは正しい考えであると言えます。

これで、汎用アウトレットを作ることができたので、メソッドの中でこのアウトレットに対して outlet_anything() を呼び出します。 しかし、最初のステップはまず、セレクタ "green" にアトムの配列を加えたものによってメッセージを組み立てることです。アトムに整数や浮動小数点数を割り当てることは、比較的簡単ですが、シンボルを割り当てるためには、 gensym() を使って文字列をシンボルに変換する必要があります。 gensym() 関数はシンボルへのポインタを返しますが、このポインタは、与えられた文字列に対してユニークなものであることが保証されています。このことは、この文字列がユニークであるかどうかを確認するために、他のシンボルと比較されるということを意味します。シンボルがすでに存在するものである場合、 gensym() はそのシンボルへのポインタを返します。そうでない場合、新しいシンボルを作成し、テーブルに格納します。これにより、次に同じ文字列を送られた場合には、この文字列を探し出すことができます。

    void myobject_bang(t_object *x)
    {
        t_atom argv[3];

        atom_setlong(argv, 43);
        atom_setsym(argv + 1, gensym("crazy"));
        atom_setfloat(argv + 2, 8.34);

        outlet_anything(x->m_outlet, gensym("green"), 3, argv);
    }

上の outlet_anything() の呼び出しでは、gensym("green")はメッセージセレクタを表しています。 outlet_anything() 関数は、このアウトレットにインレットが接続されているそれぞれのオブジェクトで、メッセージ "green" を見つけだそうとします。 outlet_anything() がこのメッセージを見つけると、これを実行し、受け取ったアトムの配列をオブジェクトに渡します。

シンボル green に符合するものが見つけられない場合、もう1つ別の動作を行ないます。これはオブジェクトが一般的なメッセージの取り扱いを行なうことを許可します。オブジェクトは、シンボル "anything" にバインドされた特別なメソッドを定義することができ、このメソッドはセレクタと符合するものが見つからない場合に呼び出されます。anything メソッドについては後で触れますが、まず、 class_addmethod() に戻って、受信した最後のアーギュメントについて説明する必要があるでしょう。

アトムにアクセスするためには、atom_setlong(), atom_getlong() といった関数を使用することができます。あるいは、 t_atom 構造体に直接アクセスすることも可能です。アクセサ関数はコードをより美しく保ち、先々t_atom 構造体に変更が加えられた場合でもソースコードを修正せずにすむものであるため、アクセサ関数の使用を推奨します。

アーギュメント型指定子

シンプルな例では、int メソッドが次のように定義されているのを見たことと思います。
        class_addmethod(c, (method)simp_int, A_LONG, 0);

class_addmethod() に渡されている A_LONG, 0 という引数は、すでに作成された C 関数が受け取ることができるアーギュメントの型を指定するものです。 A_LONG は C 関数が long 整数型のアーギュメントを受け取ることができるということを意味します。0 はアーギュメント指定子リストの最後を表すものであるため、int メッセージの場合、1つの long 整数アーギュメントが存在します。

他のオプションとして、double 用の A_FLOAT、シンボル用の A_SYM、そして、アトムの生のリストを渡すA_GIMME があります。A_GIMME は、そもそも最初に Max メッセージを送信するために用いられたものです。これらのアーギュメント型指定子は、Max の中の 「型を持った - typed」メソッド と呼ばれるものを定義します。型を持ったメソッドでは、Max はメッセージに含まれる個々のアトムの型をチェックし、受信側オブジェクトが与えられたセレクタで前提としているものと一致するかどうかを確認します。

アトムをアーギュメント型指定子のフォーマットに強制的に変換することができない場合には、Max ウィンドウに " bad arguments error " が表示されます。

使用できる型指定子の数には制限があり、通常、複数の A_FLOAT 指定子を置くことは避けなければなりません。これは、スタックに浮動小数点数を渡した場合に予測ができないという、元々のコンパイラの実装上の性質によるものです。アーギュメントが 4 個を超える場合や、複数の浮動小数点数アーギュメントが存在する場合には、 A_GIMME を使用して下さい。

メッセージにアーギュメントがない場合に、デフォルトの値を設定してから C 関数に送るように指定することもできます。A_DEFLONG は long 型のアーギュメントがない場合、代わりに 0 を設定します。 A_DEFFLOAT は float 型アーギュメントがない場合、代わりに 0.0 を設定します。 また、A_DEFSYM はsymbol 型のアーギュメントがない場合に、空のシンボル(これは、gensym("") と等価です)を設定します。

GIMME 関数の書き方

A_GIMME を使用するメソッドは、次のように宣言します。
    void myobject_message(t_myobject *x, t_symbol *s, long argc, t_atom *argv);

symbol アーギュメント s はメッセージセレクタです。 通常、これは不要なものと思われるかもしれません。しかし、後で述べる "anything" メソッドにとって役立ちます。

argc はargv 配列に含まれるアトムの数を表します。メッセージがアーギュメントなしで送信された場合、これは 0 になる可能性もあります。 argv はアーギュメントが保持しているアトムの配列です。

型を持ったメッセージでは、アトムは A_SYMA_FLOAT、あるいは A_LONGという型になります。これは、すべてのアーギュメントを単に表示するでけのメソッドの例です。

    void myobject_printargs(t_myobject *x, t_symbol *s, long argc, t_atom *argv)
    {
        long i;
        t_atom *ap;

        post("message selector is %s",s->s_name);
        post("there are %ld arguments",argc);
        for (i = 0, ap = argv; i < argc; i++, ap++) { // 次のアトムを取得するために、ap は毎回インクリメントします。 
            switch (atom_gettype(ap)) {
                case A_LONG:
                    post("%ld: %ld",i+1,atom_getlong(ap));
                    break;
                case A_FLOAT:
                    post("%ld: %.2f",i+1,atom_getfloat(ap));
                    break;
                case A_SYM:
                    post("%ld: %s",i+1, atom_getsym(ap)->s_name);
                    break;
                default:
                    post("%ld: unknown atom type (%ld)", i+1, atom_gettype(ap));
                    break;
            }
        }
    }

アーギュメントは、どのような方法で解釈することも可能です。しかし、アーギュメントは他のオブジェクトに渡される可能性もあるため、これを変更することはできません。

Copyright © 2008, Cycling '74