Max 5 API Reference
オーディオシグナルを扱う MSP オブジェクトは、通常の Max オブジェクトにいくつかの機能を追加したものです。
ここでは、これらの追加機能について詳しく説明します。simplemsp~ というサンプルプロジェクトのソースを参照して下さい。simplemsp~ は単にシグナル値に数値を加算するだけのものです。これは、MSP の +~ オブジェクトにアーギュメントとして 1 を与えた場合と同じものです。,
次に、基本的な処理を列挙します。
1) 追加のヘッダファイル
ext.h および ext_obex.h をインクルードした後に、 z_dsp.h をインクルードします。
#include "z_dsp.h"
2) C 構造体の宣言
C 構造体の宣言は t_object ではなく、t_pxobject で始めなければなりません。
typedef struct _mydspobject { t_pxobject m_obj; // 構造体の他のフィールド } t_mydspobject;
3)初期化ルーチン
class_new()によってクラスを作成する際、消滅関数(free 関数)を作成しなければなりません。何らかの特別なことを行なわない場合には、この目的のために定義されているdsp_free(), を使います。独自の消滅関数を書く場合、最初に行なわなければならないことは dsp_free(). の呼び出しです。これは、オーディオ処理がオンの状態でオブジェクトが開放された際のクラッシュを避けるために重要です。
c = class_new("mydspobject", (method)mydspobject_new, (method)dsp_free, sizeof(t_mydspobject), NULL, 0);
class_new()によってクラスを作成した後、class_dspinit(), を呼び出さなければなりません。これは、すべてのシグナルオブジェクトによって使用される内部的なメッセージを扱うための標準のメソッドを追加します。
class_dspinit(c);
シグナルオブジェクトには "dsp" というシンボルにバインドされたメソッドが必要です。このメソッドが実行する内容の詳細については後述しますが、クラスの初期化を行なう際に次の行を追加しておかなければなりません。
class_addmethod(c, (method)mydspobject_dsp, "dsp", A_CANT, 0);
4) インスタンス生成ルーチン(new instance ルーチン)
インスタンス生成ルーチン( new instance ルーチン)では、dsp_setup()を呼び出さなければなりません。この関数によって新しく割り当てられたオブジェクトへのポインタ、そして、オブジェクトが持つシグナルインレットの数を渡します。オブジェクトがインレットを持たない場合、インレットの数として 0 を渡すことも可能です。simplemsp~ オブジェクトは(ここで例示しているオブジェクト)はシグナルインレットを1つ持ちます。
dsp_setup((t_pxobject *)x, 1);
dsp_setup() はシグナルインレットを1つ(プロキシとして)作成するため、これをあなた自身で作る必要はありません。
オブジェクトがオーディオシグナルのアウトレットを持つ場合、これらはインスタンス生成ルーチンの中で outlet_new()によって作成する必要があります。しかし、これらに直接アクセスすることはありません。そのため、通常のアウトレットではアウトレットへのポインタを格納する必要がありますが、この場合には必要ありません。次は、2つのシグナルアウトレットを作成している例です。
outlet_new((t_object *)x, "signal"); outlet_new((t_object *)x, "signal");
5) dsp メソッドとパフォームルーチン
dsp メソッドは、オブジェクトが引数と共に定義したシグナル処理関数を指定します。オブジェクトの dsp メソッドは MSP のシグナルコンパイラが処理のシーケンス(これはDSP チェインと呼ばれています)を構築した際に呼び出され、オーディオサンプルのセットごとに処理を行ないます。この操作の流れは、関数(パフォームルーチン と呼ばれます)へのポインタ、そしてその後に、関数に対する引数が続くという形で構成されています。
dsp メソッドは次のように宣言されます。
void mydspobject_dsp(t_mydspobject *x, t_signal **sp, short *count);
エントリを DSP チェインに追加するために、 dsp メソッドは dsp_add(). を使用します。 dsp メソッドはシグナル ( t_signal へのポインタ )の配列 を渡されますが、これには、オブジェクトのパフォームルーチンが入出力として使用する実際のサンプルメモリへのポインタが含まれています。シグナルの配列 は、入力(左から右の順)で始まり、その後に出力が続きます。例えば、オブジェクトが2つの入力(インスタンス生成ルーチンが dsp_setup(x, 2)を呼び出しているため) と3つの出力(オブジェクト生成関数が3つのシグナルアウトレットを作っているため)を持つ場合、シグナルの配列 sp には次のような5個の要素が含まれます。
sp[0] // 左の入力 sp[1] // 右の入力 sp[2] // 左の出力 sp[3] // 中央の出力 sp[4] // 右の出力
t_signal データ構造体( z_dsp.h で定義されています) は2つの重要な要素を含んでいます。その2つとは、シグナルベクタのサイズである s_n フィールド、およびシグナルデータに含まれる32 ビット浮動小数点数の配列へのポインタである s_vec です。オブジェクトが受信する t_signal はすべて同じサイズになっています。このサイズはグローバルな MSPシグナルベクタサイズと同じである必要はありません。その理由は、パッチの中で、このオブジェクトが独自のベクタサイズを定義したpoly~ オブジェクトの内部に含まれるかも知れないためです。したがって、オブジェクトのdsp メソッドに渡されたシグナルの s_n フィールドを使用することは重要なことです。
パフォームルーチンに対し、 dsp_add()を介してアーギュメントを渡すためには、様々な方法があります。ベクタの演算を行なっている間に内部の状態を保存しないような、シンプルなユニットジェネ レータの場合、入力、出力、およびベクタサイズを渡すだけで十分です。フィルタやランプジェネレータのように、ベクタの演算を行なっている間に内部の状態 を保存する必要があるオブジェクトでは、この内部状態を格納するためのスペースを持つデータ構造体であるオブジェクト自身へのポインタを渡します。plus1~ オブジェクトは内部状態を保存する必要がないオブジェクトです。ここでは、パフォームルーチンに対し、入力、出力、およびベクタサイズを渡しています。plus1~ のdsp メソッドを次に示します。
void plus1_dsp(t_plus1 *x, t_signal **sp, short *count) { dsp_add(plus1_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); }
dsp_add() の最初の引数はパフォームルーチンです。その後にはDSPチェインにコピーするアーギュメントがいくつか追加されます。
パフォームルーチンは従来の感覚で捕えられるような「メソッド」とは異なっています。このルーチンはオーディオドライバのコールバックの中で呼び出される もので、通常、ユーザがノンリアルタイム・オーディオドライバを使用する場合を除いて、高い優先度を持つスレッドに置かれます。パフォームルーチン内のス レッド保護は最小限に押さえられています。したがって、clock を用いることはできますが、qelem や outlet を使用することはできません。そのため、パフォームルーチンの設計方法は、他のMaxメソッドと多少異なっています。パフォームルーチンは、DSPチェイ ンの中の一部分へのポインタを受け取り、チェイン上で次にパフォームルーチンが実行される位置を返すという仕様になっています。この次の実行位置は、dsp_add() で呼び出す際にパフォームルーチンに対して指定した引数の個数によって決まります。例えば、3つの引数を渡した場合、w + 4 を返す必要があります。
次に plus1 のパフォームルーチンを示します。
t_int *plus1_perform(t_int *w) { t_float *in, *out; int n; in = (t_float *)w[1]; // 入力されるシグナルベクタを取得します。 out = (t_float *)w[2]; // 出力するシグナルベクタを取得します。 n = (int)w[3]; // ベクタサイズ while (n--) // すべてのサンプルに対して演算を実行します。 *out++ = *in++ + 1.; return w + 4; // DSPチェインの次の位置を返さなければなりません。 }
6) 消滅関数(Free 関数)
このクラスのための消滅関数(free 関数)は dsp_free() をそのまま使用するか、あるいは、次に示すように関数の中で dsp_free() を呼び出す形で書かなければなりません。
void mydspobject_free(t_mydspobject *x) { dsp_free((t_pxobject *)x); // ここで、他の開放処理を行なうことができます。 }