本来の使い方とは外れているのでしょうけれども、大量の音を処理する必要があり、最近Max/Mspを触っています。
Max/MspもTouchDesignerなどと同じようにパッチを繋いでプログラミングするものです。
これがTouchdesignerともまた異なり、一つのinletにたくさんの線を繋ぐことができます。
で、慣れている人なら標準の機能を使うだけで自由自在にできるのでしょうが、やはりそこはテキストベースのプログラミングに慣れた私としては不自由です。
そんな人のためにjsによる機能実装もあるのですが、external objectという拡張機能を使うと、C++でプログラミングして独自パッチを作成することが可能です。
その開発時のメモを書いておこうと思います。
Max SDKのダウンロード
こちらhttps://cycling74.com/downloads/sdkからSDKをダウンロードできます。
早速ファイルを解凍してみましょう。sourceフォルダがサンプルコードなど、開発時に役に立つコードがあります。
私は極力シンプルなものをベースに作ろうと思い、basic/simplemax を元に開発をはじめました。
*.vcxproj, *.xcodeprojの両ファイルがあるのでWindows, Mac共に開発をはじめやすいです。
実際の作り方について
では、早速コードを書いていきましょう。
- 宣言部
- ここはおまじないですね。
#include "ext.h" // standard Max include, always required #include "ext_obex.h" // required for new style Max object
- クラス 定義部 – 必要な情報をセットする
- 次にパッチで使われるstructを定義します。
typedef struct _simplemax_edit { t_object ob;// オブジェクト自身 long a; void* m_proxy; t_object *m_outlet; } t_simplemax_edit;
- 関数の定義
- 基本的に.cの1ファイルで書かれるようなので、実装は下に書くとして、ここで関数の定義をしておきます。
///////////////////////// function prototypes - ここで定義する void *simplemax_edit_new(t_symbol *s, long argc, t_atom *argv); // 初期化。必須 void simplemax_edit_free(t_simplemax_edit *x); // 解放処理。何も書かれていない場合が多い? void simplemax_edit_assist(t_simplemax_edit *x, void *b, long m, long a, char *s); // 接続部に表示するヒントを設定する void simplemax_edit_in1(t_simplemax_edit* x, long n); // intの処理 void simplemax_edit_ft2(t_simplemax_edit* x, float a); // floatの処理 void simplemax_edit_in2(t_simplemax_edit* x, long n); // intの処理 void myobject_bang(t_simplemax_edit* x); // bangの処理
- グローバル変数定義
- ここも実質おまじないです。自身のインスタンスを定義していると思います。
//////////////////////// global class pointer variable void *simplemax_edit_class;
- main関数 – エントリポイント
- サンプルです。
class_addmethodで関数を紐付け、post関数はMaxのコンソールに文字を表示します。void ext_main(void *r) { t_class *c; // 初期化 c = class_new("simplemax_edit", (method)simplemax_edit_new, (method)simplemax_edit_free, (long)sizeof(t_simplemax_edit), 0L /* leave NULL!! */, A_GIMME, 0); /* you CAN'T call this from the patcher */ // 関数の紐付け処理 class_addmethod(c, (method)simplemax_edit_assist, "assist", A_CANT, 0); class_addmethod(c, (method)simplemax_edit_in1, "in1", A_LONG, 0); class_addmethod(c, (method)simplemax_edit_ft2, "ft2", A_FLOAT, 0); class_addmethod(c, (method)myobject_bang, "bang", 0); // おまじない class_register(CLASS_BOX, c); /* CLASS_NOBOX */ simplemax_edit_class = c; // max consoleに表示するにはpost関数を使う。 post("I am the simplemax_editaaa object"); }
- 実装部
- その他定義した関数のサンプルコードです。ここは、floatの値が入った時の処理です。
void simplemax_edit_ft2(t_simplemax_edit* x, float a){ post("float"); }
これはlong(int)の処理です。
void simplemax_edit_in1(t_simplemax_edit* x, long n){ post("long"); }
bangの処理です。
void myobject_bang(t_simplemax_edit* x) { // t_atom argv[3]; // // atom_setlong(argv, 43); // atom_setsym(argv + 1, gensym("crazy")); // atom_setfloat(argv + 2, 8.34); // // outlet_anything(x->o_outlet, gensym("green"), 3, argv); // インレットの違いによる処理の切り分け switch (proxy_getinlet( (t_object*)x)) { case 0: post("bang received in left inlet"); outlet_int(x->m_outlet, 1); break; case 1: post("bang received in right inlet"); outlet_int(x->m_outlet, 2); break; default: break; } }
これはパッチのinlet, outletに近づけた時に出るコードヒントです。
void simplemax_edit_assist(t_simplemax_edit *x, void *b, long m, long a, char *s) { if (m == ASSIST_INLET) { // inlet sprintf(s, "I am inlet %ld", a); } else { // outlet sprintf(s, "I am outlet %ld", a); } }
インスタンスの解放処理です。何にもしていないけど。
void simplemax_edit_free(t_simplemax_edit *x){;}
インスタンス化している関数です。
ここでinlet, outletを定義し、Attriuteの処理などを行います。void *simplemax_edit_new(t_symbol *s, long argc, t_atom *argv) { t_simplemax_edit *x = NULL; long i; if ((x = (t_simplemax_edit *)object_alloc(simplemax_edit_class))) { // inlet, outletを追加 // intin(x, 1); // floatin(x, 2); object_post((t_object *)x, "a new %s object was instantiated: %p", s->s_name, x); object_post((t_object *)x, "it has %ld arguments", argc); for (i = 0; i < argc; i++) { if ((argv + i)->a_type == A_LONG) { object_post((t_object *)x, "arg %ld: long (%ld)", i, atom_getlong(argv+i)); } else if ((argv + i)->a_type == A_FLOAT) { object_post((t_object *)x, "arg %ld: float (%f)", i, atom_getfloat(argv+i)); } else if ((argv + i)->a_type == A_SYM) { object_post((t_object *)x, "arg %ld: symbol (%s)", i, atom_getsym(argv+i)->s_name); } else { object_error((t_object *)x, "forbidden argument"); } } } x->m_proxy = proxy_new((t_object*)x, 1, &x->a); x->m_outlet = outlet_new((t_object*)x, NULL); return (x); }
いったんはここまで。Attributeやメッセージの処理は引き続きのせていきます。