Kinectを買ったらサンプルがWPFアプリケーションばかりで使いにくいシロモノでした。Kinectに限らず得られたデータを他のアプリに渡したいというときは山ほどあるかと思いますが、個人的にはOpen Sound Control (略してosc)というデータ通信のライブラリが便利だと思います。oscについては田所先生のページが分かりやすいです。要は自分でソケットプログラム書かなくても、結構いろんなプラットフォーム間でデータやりとりできるよ、というものです。
ということでC#でググってみるといくつか見つかるのだけど、そのなかでもMake Controller Kit .NETというのが便利そう。ドキュメント内にサンプルがありますが、シンプルで良さそうです。
ということで実装。
Sender側
今回はudpを投げるようにします。サンプルではUSBも楽ちんそうです。
MakeControllerOsc.dllを作る。
1. ダウンロード
まずmakingthings.comのサイトに飛び、下記ページの「.NET C# v0.2」のリンクからdotnet.zipをダウンロードします。
http://www.makingthings.com/resources/downloads
2. 設定
展開したフォルダからdotnet.slnをVisual Studio express等で開き、ソリューションエクスプローラーで【MakeControllerOsc】というプロジェクトを右クリックし、【スタートアッププロジェクト】に設定します。また、DebugビルドになっているのでReleaseビルドに変えます。上のプルダウンのメニューから見つけてね。
4. ビルド
ソリューションのビルドでdotnet\MakeControllerOsc\bin\Release\MakeControllerOsc.dllといった形でdllができます。
あとはこれを自分のC#のプログラムで使いまわせます。
OscSenderアプリを作る(C# WPF)
今Kinectを使ってる関係でC# WPFです。.NETならおk。
1. まず空の【WPFアプリケーション】を作成
2. 作成したMakeControllerOsc.dllをプロジェクトの.csprojファイルや他のソースファイルがあるところにコピー。
3. 【ソリューションエクスプローラー】の【参照設定】を右クリックで、【参照の追加】->【参照】でコピーしてきたMakeControllerOsc.dllを設定
これでusing MakingThingsとかすればOSCが使えるようになります。
4. 【ツールボックス】からButtonを選んで、ウインドウに配置、それをダブルクリックすると、クリックアクションのコードが書けるように。
今回はクリックタイミングにしたけどお好きな時にどうぞ。
MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; //!< MakeController using MakingThings; namespace OscSenderExample { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { private UdpPacket udpPacket; private Osc oscUdp; OscMessage oscMsg; public MainWindow() { //!< osc udp udpPacket = new UdpPacket(); udpPacket.RemoteHostName = "127.0.0.1"; udpPacket.RemotePort = 8000; udpPacket.LocalPort = 9000; udpPacket.Open(); oscUdp = new Osc(udpPacket); InitializeComponent(); } private void Send_Click(object sender, RoutedEventArgs e) { Console.WriteLine("send data"); //パスはアプリの仕様で適当に、今回はウインドウの幅を送ります。 oscMsg = Osc.StringToOscMessage("/event/click " + Width.ToString()); oscUdp.Send(oscMsg); } } }
MainWindow.xaml
<Window x:Class="OscSenderExample.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> <Button Content="Send" Height="23" HorizontalAlignment="Left" Margin="224,136,0,0" Name="Send" VerticalAlignment="Top" Width="75" Click="Send_Click" /> </Grid> </Window>
Receiver側
osc対応してるなら何でも良いんだけど今回はopenFrameworksを使います。oFならaddonsExamplesの中にoscReceiveExampleとかあるので、それを参考にすれば瞬殺かと。
testApp.h
#include "ofMain.h" #include "ofxOsc.h" #define PORT 8000 #define NUM_MSG_STRINGS 60 class testApp : public ofBaseApp{ public: // //中略 // ofxOscReceiver receiver;
testApp.cpp
//-------------------------------------------------------------- void testApp::setup(){ // listen on the given port cout << "listening for osc messages on port " << PORT << "\n"; receiver.setup( PORT ); } //-------------------------------------------------------------- void testApp::update(){ // check for waiting messages while( receiver.hasWaitingMessages() ) { // get the next message ofxOscMessage m; receiver.getNextMessage( &m ); if(m.getAddress() == "/event/click") { printf("val=%d\n", m.getArgAsInt32(0)); } } } // //略 //
番外編(スレッドでOSCを送る)
例えばリアルタイムにどんどんデータを送る際には下記のようにバックグラウンドで送るというのもありかと思います。
MainWindow.xaml.cs
//!< thread private readonly BackgroundWorker sendWorker = new BackgroundWorker(); public MainWindow() { //!< udp udpPacket = new UdpPacket(); udpPacket.RemoteHostName = "127.0.0.1"; udpPacket.RemotePort = 8000; udpPacket.LocalPort = 9000; udpPacket.Open(); oscUdp = new Osc(udpPacket); //!< thread sendWorker.DoWork += send_DoWork; } // //中略 // //例えばkinectのフレームアップデートの際に private void kinect_allFramesReady(object sender, AllFramesReadyEventArgs e) { using (DepthImageFrame depthFrame = e.OpenDepthImageFrame()) { depthImg.Source = depthFrame.ToBitmapSource(); } if (!sendWorker.IsBusy) { //Console.WriteLine("not busy"); audioBeamAngle = audioManager.getAudioBeamAngle(); audioSourceAngle = audioManager.getAudioSourceAngle(); audioConfidence = audioManager.getAudioConfidence(); sendWorker.RunWorkerAsync(); } } private void send_DoWork(object sender, DoWorkEventArgs e) { //ここに上記のoscをsendするプログラムを書く }
oF上でデータがいろいろと扱えると、夢がいっきに広がりますね。