iPhoneでOSC(Open Sound Control)の送受信をやる

「Making Things Talk」見るとArduinoでOSC(Open Sound Control)とかMIDIが使えそうなことが分かった。
iPhoneでPCじゃないスタインドアローンな装置と通信出来たら夢が広がりそうだったので、その仕込みとしてiPhone側を実装してみる。


Cocoa用のライブラリを探すと
ObjCOSC
Liblo
vvosc
あたりが使えそう。


vvoscは全部Objective-Cで書いてあってcocoaで使いやすそうなんだけど、ライセンスがCreative Commons Attribution-Noncommercial-Shareなので商用で売りたい人には向かない。
すでに素敵なAppがいっぱいStoreにあるから売るつもりも無いけど。
Libloは使いやすそうだけどC言語だから、Objective-CラッパーなObjCOSCを使うことにする。Libloの使い方はこのへん見れば分かると思います。


でも、ObjCOSCのサイトが404で見当たらない。しかたないのでここから拝借。
mrmrのソースの中にもあったけど。


送信は

OSCPort* oscPort = [[OSCPort oscPortToAddress:ip portNumber:port retain];
[oscPort sendTo:"/osc/1/xy" types:"ff", 0.5, 0.5];

とかやるだけ。簡単。


受信はちょっと面倒でコールバック関数を指定する。

OSCInPort *portIn = [[OSCInPort alloc] initPort:myPort];
OSCcontainer fooContainer = [portIn newContainerNamed:"osc"];
OSCcontainer barContainer = [portIn newContainerNamed:"1" under:fooContainer];

[portIn newMethodNamed:"hoge" under:[portIn topLevelContainer] callback:FooBarCallback context:nil];
[portIn newMethodNamed:"str" under:barContainer callback:FooBarCallback context:nil];
[portIn newMethodNamed:"xy" under:barContainer callback:BarCallback context:self];

OSCcontainerはoscのprefixが/hoge/fugaみたいなときに、hogeをOSCcontainerに指定しておく。
prefixが単に/hogeなときはOSCInPortのtopLevelContainerを使える。


最後の行のように

context:self

としておけばコールバックの引数に自分を渡せるので、そこからメソッドを呼び出せる。
もりもりThreadが動いてる状態だと色々波乱がありそうだけど、さくっと始める最初の1歩なのでとりあえずこのままで。


動作をテストする相手を調達しなくちゃいけいけど貧乏なのでiPhoneは1台しかない。よってmacでやる。
prefixとか気にせずにすぐに使えるOSCulatorが便利なんだけどシェアウェアだし、別途MIDI送受信できるアプリを使うのが大げさで面倒なので、max/mspでさくっと作ってしまう。


maxは数年ほど放置したままだったので久しぶり。maxでoscを使うにはCNMATのエクスターナルが必要だったけど、max4.6からudpsend/udpreceiveがあるのが分かったのでそれを使って5分で完成。


こんな感じ。
maxmsposcsenddemo


早速wifiでLANに繋いでテストしてみる。
iPhoneからの送信は全く問題ない。multipleな値もちゃんと送信できてる。
けど、iPhone側の受信でintとfloatの値がちゃんと取れない。ものすごい値になる。
どうやらエンディアンの違いで逆になってるみたい。


intとfloatのときだけ

int intValue = *((const int*)myArgs);
intValue = CFSwapInt32BigToHost(intValue);
float floatValue = *((const float*)myArgs);
floatValue = CFConvertFloat32SwappedToHost(*(CFSwappedFloat32 *)&floatValue);

とかやって戻しておく。


TCPだとNSInputStream/NSOutputStream使ってちょっと面倒なんだけど、oscは送りっぱなしのUDPなのでシンプル。
でも、iPhonemacのIPとポートを相互に指定するのが面倒。この先複数のoscサーバ/oscクライアント間で送受信することを考えると最初の設定が面倒すぎる。
NSHostでもいいけどLANに繋がってるIPが全部出ちゃうので、ここはBonjour使ってスマートに相手を見つけるのがかっこいい。
続きはこのあたりの方法をまとめます。