Visual Studio C++プロジェクトでOSCを送受信する手順
PARTYでエンジニアをしている梶原です。
世にあるC++系のフレームワークやSDKを使う際、VSC++でそのまま自分で書いたほうがシンプルなことがよくあります。
その際、他のアプリケーションへ通信などで情報の共有を行うことが多いのですが、今回はOSCを使って(よくあるシチュエーションですね)送受信するための段取りを記載していきます。
使うライブラリ
色々ありますが、汎用性の高いoscpackというのを使います。
作業順に解説していきます。
ライブラリのダウンロード
上記サイトの"Download snapshot"から、最新版(2013年ですが)を落としてきます。
oscpack_1_1_0.zipというのがそれですね。
ライブラリの配置
DLして解凍したフォルダを、使いたいプロジェクトのフォルダに配置します。
後ほどVSでこのフォルダのパスを指定しますので、実はどこにあっても大丈夫ではあります。
ただ、後でプロジェクトごとパッケージにしてアーカイブする際など、まとめておいたほうが便利なことがあるので、今回はプロジェクト内に配置していきます。
今回はExampleのプロジェクトも用意しておくので、そちらの例に従ってフォルダ名やパスなどを記載していきます。(Visual Studio Community 2019で作業しています)
ご自分の環境に合わせて適宜変更してください。
自分のプロジェクトのディレクトリを"MySolutionDir"として、解凍したフォルダを、
MySolutionDir/ThirdParty/
に配置します。
ThirdPartyというフォルダの下にしたのに大きな理由はありません。
僕がよくこういう配置にしてるだけなので、別にMySolutionDir直下でも良いでしょう。
プロジェクトの設定をする
こういったC++外部ライブラリの場合、主にやることは下記になります。
●header includeパスの指定
・ライブラリで提供されている関数がまとまっているヘッダーファイルたちのフォルダのパスを指定します。こうすることで、"#include xxxx.h"みたいなのを書いて、関数を読み込めるようになります。
●libraryパスの指定
・ヘッダーで関数を呼べるようになっても、その実装の中身(ヘッダーはあくまでインターフェースなので、実際に動作する中身はライブラリの中に入っています)を呼び出せないと意味がありません。したがって、ライブラリが置かれているパスをここで指定します。
●使うlibrary名の指定
・ライブラリのパスを指定しても、提供されているライブラリがめちゃくちゃ多い場合があります。使いたいライブラリだけ名指しで使うことがほとんどなので、ここで使いたいライブラリ名を指定します。
今回使うoscpackの場合、少し古いのもあるのでいくつか上記以外の設定が必要です。
まず、library(.libとか.dllとかのファイル)がありません。
したがって、内包しているファイルをプロジェクトと一緒にビルドしないといけません。
これをするために、oscpack_1_1_0に入っているファイルたちをビルドターゲットとしてプロジェクトに参照させる必要があります。
ファイルをプロジェクトへ追加する
方法は簡単で、Solutionに”oscpack”というフィルタ(フォルダみたいなもの)を作成し、その下に"ip"と”osc”というフィルタも追加します。
そして、そこにoscpack_1_1_0の中のipフォルダ直下のファイルと、ip/win32の中のファイルをドラッグしていきます。
下記動画を参照してみてください。
それでは、それ以外のいつもやるような設定をやっていきましょう。
Solution ExplorerのSolution名を右クリックし、一番下に出る"Properties"を選択すると、いろいろな設定ができる画面が立ち上がります。
Header include pathを指定する
Properties画面の"C/C++ → General"で出てくる画面の一番上の箇所に記載していきます。
"Additional Include Directories"という項目です。
そのまま入力もできるのですが、この一番右の矢印をクリックし、"edit"を選択すると、入力用の画面が出るので、そこでやると読みやすいです。
ちなみに、このウインドウの一番上にある"configure" / "Platform"は、どのビルドターゲットに対してこの設定をするか?を選べる欄でして、たとえば"Debug / Release"で別々のライブラリが提供されていたりする場合なんかに重宝します。
今回のoscpackでは、どちらでも共通で使えるので
・configure : All configuration
・Platform: x64
にしておきましょう。
さて、"Additional Include Directories"に追加するのは下記のパスです。
・$(SolutionDir)ThirdParty\oscpack_1_1_0
・$(SolutionDir)ThirdParty\oscpack_1_1_0\ip
・$(SolutionDir)ThirdParty\oscpack_1_1_0\ip\win32
・$(SolutionDir)ThirdParty\oscpack_1_1_0\osc
この$(SolutionDir)というのは、現在のプロジェクトのディレクトリのパスを自動で保管してくれる便利なマクロです。
なので、別にフルパスを書いてしまっても問題ありません。
ここまでの流れ、動画を参照してみてください。
library Pathを指定する
今回のoscpackにはlibraryが含まれていないので、この作業は不要です。
使うライブラリを指定
Properties画面の"Linker → Input"で出てくる画面の一番上の箇所に記載していきます。
"Additional Dependencies"という項目です。
oscpackでは、Win標準の通信ライブラリを使用するので、この標準の通信ライブラリを「使うよー」ってことで指定しておく必要があります。
・ws2_32.lib
・winmm.lib
の2つですね。
Preprocesser(マクロ)の設定
こちらはすこし特殊ですが、oscpackがwin32用以外にも提供されているので、win32であるとわかるように設定をしてあげる必要があります。
Properties画面の"C/C++→ Preprocesser"で出てくる画面の一番上の箇所に記載していきます。
__WIN32__
というのを記載します。
古いライブラリにありがちなエラーを無視する設定をする
この段階でビルドすると、C4996という警告が出てビルドができないかと思います。
これは、古いライブラリによくあるやつで、無視する設定をしてあげればビルドが通ります。
"C/C++ → Advanced" の、"Disable Specific Warnings"という項目に、
4996
と書いてあげれば大丈夫です。
以上で基本の設定は終了です。
では、実際に使ってみましょう。
oscpackを使って送信する
使い方は簡単です。
よくある例として、あるIDを持ったオブジェクトの、(x, y, z)の座標を送信する場合を想定してコードを書いてみます。
・送信先IPAddress: 127.0.0.1
・送信先Port: 8000
・address: "/position"
として、
id: int
x: float, y: float, z: float
の値を送信します。
//---------------------------------
// OSC
//---------------------------------
#include "OscReceivedElements.h"
#include "OscPacketListener.h"
#include "OscOutboundPacketStream.h"
#include "UdpSocket.h"
#include "IpEndpointName.h"
void sendOSCMessage(int id, float x, float y, float z)
{
// Set IPAddress and Port
const std::string ipAddress = "127.0.0.1";
const int port = 8000;
UdpTransmitSocket transmitSocket(IpEndpointName(ipAddress.c_str(), port));
//Buffer
char buffer[6144];
osc::OutboundPacketStream p(buffer, 6144);
p << osc::BeginBundleImmediate
//Head
<< osc::BeginMessage("/position") << id << x << y << z << osc::EndMessage
<< osc::EndBundle;
transmitSocket.Send(p.Data(), p.Size());
}
これだけです!
コードを簡単に解説すると、
・oscpackで必要なヘッダーをincludeし、
・ipaddress / portを設定してUdpTransmitSocket型のインスタンスを生成。
・このインスタンスに、osc::OutboudPacketStream型のデータ(これが送信するメッセージになる)を入れてSendする
書き方にちょっと癖がありますが、とてもシンプルですね。
oscpackを使って受信する
受信も同じように簡単ですが、受信に関してはそれ用のクラスを作ってしまったほうが使い勝手がよいのでここに丸っと記載しておきます。
//---------------------------------
// OSC
//---------------------------------
#include "OscReceivedElements.h"
#include "OscPacketListener.h"
#include "OscOutboundPacketStream.h"
#include "UdpSocket.h"
#include "IpEndpointName.h"
class OSCReceiveHandler : public osc::OscPacketListener {
private:
public:
OSCReceiveHandler(){}
~OSCReceiveHandler(){}
protected:
bool isOSCReceiveBegan = false;
virtual void ProcessMessage(const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint)
{
(void)remoteEndpoint;
try{
if (strcmp(m.AddressPattern(), "/position") == 0){
// if address is "/position"
} else {
}
}
catch (osc::Exception& e){
std::cout << "OSC Receive: Error while parsing message: "
<< m.AddressPattern() << ": " << e.what() << "\\n";
}
}
};
このOSCReceiveHandlerクラスで受信が可能です。
ぜひ、参考にしていただけると嬉しいです!
______________________________________
PARTYでは未来の体験を社会にインストールすべく、好奇心旺盛なエンジニアを募集してます!
この記事が気に入ったらサポートをしてみませんか?