aka.iphone2はOSC (OpenSound Control) というプロトコル(下位レイヤーはUDP/IP)を使って通信しています。Maxではお馴染みの方法で、udpsendやudpreceiveで一発OKですね。
でも、Maxとは違ってiPhoneではOSCが標準じゃないから、自分でケアしなければなりません。もちろん、スクラッチから書くのは大変なので、私が利用したのはChandrasekhar “Sekhar” Ramakrishnan氏が開発したObjCOSCというObjective-C用のOSCライブラリ。
それで、ObjCOSCを使ったOSCの送信はとっても簡単。Objective-Cのコードはちょっとクセがあるけど、なんとなく分かるでしょ?
OSCPort* port = [OSCPort oscPortToAddress: address portNumber: portNumber];
[port sendTo:"/foo" types:"f", 0.2061f];
受信もコールバック関数を登録しておくだけ。
OSCInPort* portIn = [[OSCInPort alloc] initPort: inportno];
[portIn newMethodNamed: "foo" under: [portIn topLevelContainer]
callback:fooCallback context: myController];
だけど、受信にはハマりまくりました。コールバック関数が呼び出されるところまでは問題ないんだけど、その後がダメダメです。全然うまくいかなかったので、現在公開しているaka.iphone2は送信だけなのね。
ハマった箇所は2つあって、ひとつはエンディアン問題。OSCは数値をビッグ・エンディアンで扱っているのに対して、iPhone(あるいはIntel Macなど)はリトル・エンディアンみたいです。ObjCOSCのサンプル・コード(main.c)なら、PrintOSCArgs()で整数と実数をエンディアン変換する必要があったわけです。変換する部分だけ抜き出しておきます。
int intValue;
intValue = CFSwapInt32BigToHost(intValue);
float floatValue;
CFSwappedFloat32 swappedFloatValue;
swappedFloatValue.v = floatValue;
floatValue = CFConvertFloat32SwappedToHost(swappedFloatValue);
ただ、送信は変換ナシに扱えてるんだから、受信だけ変換が必要ってのはイマイチなんじゃない?って思いますけどね。シンボル(文字列)の受信は問題ないです。
もうひとつのポイントは、受信のコールバック関数から他のインスタンスのメソッドを呼び出すには、メイン・スレッドから行なう必要があるみたいってこと。これをやるのがperformSelectorOnMainThreadね。myControllerというインスタンスがあるとして、そのtest:メソッド(パラメータはNSNumber)をメイン・スレッドから呼び出すのは、こんな感じ。
[myController performSelectorOnMainThread:@selector(test:)
withObject:[NSNumber numberWithFloat:floatValue] waitUntilDone:NO];
以下のように、直接呼び出すとiPhoneがフリーズします(笑)。こっちがフツーなんですが…
[myController test:[NSNumber numberWithFloat:floatValue]];
このあたりは、永野哲久くんがヘルプ&ヒントをくれました。多謝多謝。彼はObjective-Cでオーディオ・ビジュアル系の作品を作っていて(大雑把ですまない〜)、こーゆー人がIAMASに在学しているのは有り難いです。どっちが先生で、どっちが生徒だ?って気もしますけどね(笑)。彼の記事にトラックバックを入れておきます。