liblo
URL: http://liblo.sourceforge.net/
ライセンス: GPL
概要
Open Sound Control を実装したライブラリ。C言語で書かれているので、C や C++ で書いた自作アプリケーションから使うのに便利。 UDP・TCP・UNIX ソケットなど、OSC パケットを伝送するための経路もいろいろとサポートされているので、自分に都合のよいものを選べる。クライアント側のプログラミングは実に簡単だが、サーバー側のプログラミングもシグナルハンドラの書き方に似ていて、理解しやすい。パケットを処理するタイミングも自分で決められる。POSIX 準拠のシステムならたいてい動くそうだ。
クライアントの例
これはもうとても簡単。まず冒頭でヘッダファイルを include しておく。
#include <lo/lo.h>
次に、相手となるアプリケーション毎に、lo_address
型の変数を作っておく。ここでは相手ホスト名が "target"で、UDP ポート
7770番に送る場合の例を示す。
lo_address t = lo_address_new("target", "7770");
後は、送りたい OSC
アドレスに対し、データの型を表わす文字列ととその値を引数にして関数lo_send()
を呼ぶだけ。
lo_send(t, "/foo", "i", 32);
lo_send(t, "/bar/buz", "f", 0.1);
lo_send(t, "/bar/baz", "siif", "hello world", 1, 2, 0.3);
サーバーの例
liblo を使った OSC サーバーでは、
- サーバー初期化
- 受付アドレスとそのハンドラの登録
- イベントループの開始
の三つが主な手順となる。イベントループの性質の違いにより、サーバーはスレッド版と非スレッド版の二種類が用意されている。イベントループ以外の部分はほとんど同じなので、まとめて解説しよう。
サーバー初期化
まず OSC メッセージを受け取る総合窓口を作る。一番簡単なのは UDP ポートを使ったサーバーで、非スレッド版では
lo_server OSCserver;
OSCserver = lo_server_new("7770", OSCerror);
のように書く。lo_server_new
の第一引数が UDP
ポートのポート番号となる。OSCerror
はエラーハンドラの関数ポインタだが、 解説は省く。
スレッド版では、
lo_server_thread OSCserver;
OSCserver = lo_server_thread_new("7770", OSCerror);
となる。型名と関数名に "_thread
" がついただけの違いだ。
受付アドレスとそのハンドラの登録
liblo では OSC アドレスとそれを処理する関数(ハンドラ)を登録しておくと、そのアドレスにメッセージが到着した際に登録した関数が呼び出される仕組みになっている。アドレス “/foo” で整数1個のデータを受け付ける場合には、
lo_server_add_method(OSCserver, "/foo", "i", fooHandler, NULL);
のように書く(非スレッド版)。このとき、fooHandler
の方は
static int fooHandler(const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data)
{
printf("%d\n", argv[0]->i);
return 0;
}
というように書く。注意するのは値の取り出し方で、上の例では3行目。変数argv
は lo_arg
型のポインタになっているが、 lo_arg
は各種データ型の union になっているので、このような表記になっている。プログラムを書く際にはここでちょっと悩むことになるかもしれない。この型は
lo_osc_types.h 内で定義されているのでそれを読むのが早い。
スレッド版では、lo_server_add_method
を lo_server_thread_add_method
にすればよい。
イベントループの開始
ハンドラの登録が終わったら、イベントループをコールしてやれば、UDP ポートを監視し、到着した OSC メッセージを順次ハンドラに渡してくれる。スレッド版の場合は、別スレッドでイベントループが走り、独立に処理される。こちらの方が便利ではあるが、なんらかの事情でこれを避けたい場合は、自分でイベントループを書く必要が出てくる。 (考えられる事情としては、OSCメッセージの処理タイミングを厳密に管理したい、スレッドセーフにするのが困難、など)
まずスレッド版だが、
lo_server_thread_start(st);
を呼ぶだけ。この関数を呼んだ後は、本スレッド側で引き続き本来の処理を行えばよい。
次に非スレッド版だが、一番簡単な例は、lo_server_recv_noblock()
を使うやり方で、
while(lo_server_recv_noblock(OSCserver, 0)!=0);
という行を OSC
メッセージを処理してもよい場所に挟めばよい。ただし、到着している OSC
メッセージをすべて処理し終わるまでループを続けるので、 切れ目なく大量に
OSC メッセージを受け取るような場合には注意が必要だ。なお、このやり方だと OSC
メッセージが到着していない場合はすぐに次の処理に手順が移る。ここでメッセージの到着を待ってもよい場合は、代わりに lo_server_recv()
を呼べばよい。
サンプル:sendosc
liblo を使ったサンプルとして、コマンドラインから OSC
メッセージを送るツールを以下に掲載する。以下に添付したソースコードを sendosc.c
というファイルに貼り付け、下記の要領でコンパイルする。
$ gcc -o sendosc sendosc.c `pkg-config --cflags --libs liblo`
#include <stdio.h>
#include <stdlib.h>
#include <lo/lo.h>
int main(int argc, char **argv)
{
int i;
lo_address target;
lo_message message;
if(argc < 4) {
fprintf(stderr, "sendosc host port path args...\n");
exit(1);
}
target = lo_address_new(argv[1], argv[2]);
if(target == NULL) {
fprintf(stderr, "failed to open %s:%s\n", argv[1], argv[2]);
exit(1);
}
message = lo_message_new();
for(i=4; i<argc; i++) {
switch(argv[i][0]) {
case 'i':
i++;
lo_message_add_int32(message, atoi(argv[i]));
break;
case 'f':
i++;
lo_message_add_float(message, (float)atof(argv[i]));
break;
case 'd':
i++;
lo_message_add_double(message, atof(argv[i]));
break;
case 's':
i++;
lo_message_add_string(message, argv[i]);
break;
default:
break;
}
}
lo_send_message(target, argv[3], message);
return 0;
}
使い方
コマンドラインから、
$ sendosc ホスト名 ポート番号 型1 値1 型2 値2 ...
とする。例えば
$ sendosc target 7770 s "hello world" i 1 i 2 f 0.3
のように書く。
なお、このプログラムはあくまでもサンプルであり、ホスト名や型のチェックなどはしていない。セキュリティーホールの原因にもなりうるので、このプログラムは不特定の人が使えるような状況で使うべきではない。