Max 5 API Reference

Jitterのネットワーク仕様

この補遺では、jit.net.send オブジェクトによって送信されるデータのフォーマットについて説明します。

このオブジェクトは、アトリビュートで指定された IP と ポートによって、ホストと TCP コネクションを確立しようとします。したがって、何らかのデータを受信しようとするオブジェクトは、自分をホストとして設定し、入力される TCP コネクションで待ち受けていなければなりません。

一度接続が確立されると、データを受信することができるようになります。データはチャンクのストリームとして送信されます。最初に受信するものはチャンクヘッダです。これには 32 ビットのチャンク ID と 次に受信するチャンクの データサイズを 32 ビット整数で表した値が含まれます。チャンク ID は、パケットの種類によって、次に示すような4文字によるシンボルの中の1つになります。

#define JIT_MATRIX_PACKET_ID  'JMTX'
#define JIT_MATRIX_LATENCY_PACKET_ID  'JMLP'
#define JIT_MESSAGE_PACKET_ID  'JMMP'

このチャンクヘッダは、次のようなC構造体で表すことができます。

typedef struct _jit_net_packet_header
{
   long id;
   long size; //size of packet to come
} t_jit_net_packet_header;

チャンクがマトリックスパケットである場合、次に受信するデータは 288 バイトのヘッダで、その内容は次のようなものになります。

id

'JMTX'

Size

288 (32-bit int, size of this header)

Planecount

32-bit int

Type

32-bit int, 0 for char, 1 for long, 2 for float32, 3 for float64

Dimcount

32-bit int

Dim

Array of 32 32-bit ints

Dimstride

Array of 32 32-bit ints

Datasize

32-bit int, size of the data buffer to come

Time

64-bit double precision float

このチャンクは、次のような C 構造体で表すことができます。

typedef struct _jit_net_packet_matrix 
{
   long   id;
   long   size;
   long   planecount;
   long   type;         //0=char,1=long,2=float32,3=float64
   long   dimcount;
   long   dim[JIT_MATRIX_MAX_DIMCOUNT];
   long   dimstride[JIT_MATRIX_MAX_DIMCOUNT];
   long   datasize;
   double   time;

} t_jit_net_packet_matrix;

このヘッダの次に受け取るデータはマトリックスデータになります。マトリックスデータのサイズは上記のヘッダの中で渡されます。データを使用する場合には、くれぐれもヘッダで受信した dimstride に注意して下さい。

上記のヘッダの time フィールドには、送信側コンピュータからの送信時間が設定されます。jit.net.send はサーバに対し、応答としてサーバ自身のタイミングデータを送り返すことを要求します。このデータは伝達時間の遅れ(レイテンシ)を推定するために使用されます。jit.net.send が要求するレイテンシチャンクの詳細なデータは次のようなものです。

id

'JMLP'

client_time_original

64ビットの double, 受信したマトリックスヘッダパケットの time 値

server_time_before_data

64ビットの double、パケットヘッダを受信したときのサーバの time 値

server_time_after_data

64ビットの double,、パケットの処理が終了したときのサーバの time 値。これが使用されます。

このチャンクは次のような C 構造体で表現できます。

typedef struct _jit_net_packet_latency
{
   long id;
   double client_time_original;
   double server_time_before_data; 
   double server_time_after_data;
} t_jit_net_packet_latency;

サーバがデータを受信したときの時刻(server time before)とサーバが処理を終了した時刻(server time after)の差は、サーバがデータを受信し、送信を開始するまでにかかる時間を表しています。jit.net.send はこの情報の送信や要求をミリ秒単位で行ないます。このタイミング情報が送信側コンピュータで受信されると、サーバは現在の時刻に注目し、往復にかかった時間を計算して、この往復時間の 1/2 に、サーバの処理時間の 1/2 を加算してレイテンシを推定します。A から B までの移動に要する時間と B から A までの移動に要する時間が同じであれば、この推定は正確な値になりますが、ネットワークのトポロジには非常に複雑なこともあり、多くの場合 A から B への移動にかかる時間が B から A への移動にかかる時間と等しくないという状況が生じます。2つのコンピュータ間が小さな LAN で直接に接続されているようなシンプルな環境では、推定値はかなり正確なものになります

送信されるパケットの型の最後はメッセージパケットです。メッセージパケットのサイズは、最初のヘッダパケットの中で送信されます。メッセージパケットでは、標準の A_GIMME メッセージ(t_symbol *s, long ac, t_atom *av)が連続して送信されますが、先頭には、32 ビット整数で表されるメッセージ全体のサイズのバイト数が置かれ、その次にはもう1つの 32 ビット整数によって、atom の引数の数が続きます。その後に、先頭のシンボルが存在する場合には、そのシンボルを最初としてメッセージの atom 自身が送信されます。それぞれの atom はメモリの中で次のような形をとります。まず最初に atom の型を表す文字(char)が置かれます。これが "s"であれば、atom の型は シンボルに、"l"であれば long に、"f" であれば float になります。long と float の atom では、続く 4 バイトがその atom の値になります。シンボルの atom では、シンボル文字列の最後に NULL 文字が追加されます。次の C の関数は、データポインタとして渡された連続したメッセージの仕分けを行なうものです。

void gimme_deserialize(char *data, t_symbol **s, long *ac, t_atom **av)
{
   char *curr = data;
   float *currf;
   long *currl,i;
   long datasize = BE_I32(*((long *)curr));
   curr += sizeof(long);   
   *ac = BE_I32(*(long *)(curr));
   curr += sizeof(long);
   *av = (t_atom *)sysmem_newptr(sizeof(t_atom)*(*ac));

   if (*curr == ATOM_SERIALIZATION_SYMBOL_CODE)
   {
      curr++;
      *s = gensym(curr);
      while (*(++curr) != '\0') ;
      curr++;
   }
   else
      *s = 0L;
   for (i=0;i<*ac;i++)
      switch (*curr++)
      {
         case ATOM_SERIALIZATION_SYMBOL_CODE:
            (*av)[i].a_type = A_SYM;
            (*av)[i].a_w.w_sym = gensym(curr);
            while (*(++curr) != '\0') ;
            curr++;
            break;
         case ATOM_SERIALIZATION_FLOAT_CODE:
            (*av)[i].a_type = A_FLOAT;
            (*av)[i].a_w.w_float = BE_F32(*((float *)curr));
            curr += sizeof(float);
            break;
         case ATOM_SERIALIZATION_LONG_CODE:
            (*av)[i].a_type = A_LONG;
            (*av)[i].a_w.w_long = BE_I32(*((long *)curr));
            curr += sizeof(long);
            break;
      }
}

Copyright © 2008, Cycling '74