アプリにOSCプロトコルを実装する
Open Sound Controlプロトコルを利用して、複数のアプリケーションをネットワークで接続する方法をご紹介します。アプリケーション間のインタラクションデータの送受信
レベル:中級
プラットフォーム:Windows, macOS, Linux, iOS, Android
クラス: OSCSender,OSCReceiver,OSCReceiver::Listener,OSCReceiver::ListenerWithOSCAddress,OSCMessage,OSCBundle
スタート
このチュートリアルにはいくつかのデモ・プロジェクトがあります。これらのプロジェクトのダウンロード・リンクは、チュートリアルの関連セクションに記載されています。
各セクションのこのステップでヘルプが必要な場合は、以下を参照してください。Tutorial: Projucer Part 1: Getting started with the Projucer.
デモ・プロジェクト
このチュートリアルで提供されるデモ・プロジェクトは、OSCとのインタラクションに必要な様々なアプリケーションを紹介します。要約すると、これらのアプリケーションは以下の通りです:
- OSCセンダー:センダーアプリケーションにはロータリーノブがあり、他のインスタンスに情報を送信する。
- OSCレシーバー:レシーバー・アプリケーションは、センダー・インスタンスに接続し、情報を受信して処理・表示する。
- OSCモニター:モニター・アプリケーションも送信者インスタンスに接続するが、より正確にやりとりをモニターし、ログに記録する。
このチュートリアルが正しく機能するためには、送信側と受信側/モニター側のアプリケーションを同時に1つだけ開く必要があります。
ここで紹介するコードは、JUCE ExamplesのOSCデモアプリとほぼ同様です。
OSCトランスミッター
このセクションのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
完成すると、OSCセンダー・アプリケーションは1つのロータリー・ノブを表示し、起動時にそれを操作できるようになる:

OSCトランスミッターの実装
まずは送信者アプリケーションの実装から始めよう。
の中でMainContentComponentクラスでは、まずこのアプリケーションのプライベート・メンバー変数を次のように宣言する:
juce::Slider rotaryKnob; // [1]
juce::OSCSender sender; // [2]
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
追加Sliderオブジェクトを使用してユーザーのインタラクションをキャプチャします。[1]そしてOSCSenderオブジェクトを後でレシーバーに接続する[2]。
クラスのコンストラクタで、スライダー・パラメータを設定し、以下のようにネットワークへの接続を試みる:
MainContentComponent()
{
rotaryKnob.setRange (0.0, 1.0);
rotaryKnob.setSliderStyle (juce::Slider::RotaryVerticalDrag);
rotaryKnob.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 150, 25);
rotaryKnob.setBounds (10, 10, 180, 180);
addAndMakeVisible (rotaryKnob); // [3]
// specify here where to send OSC messages to: host URL and UDP port number
if (! sender.connect ("127.0.0.1", 9001)) // [4]
showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
setSize (200, 200);
}
- [3]スライダーの範囲、スタイル、境界を設定しComponentをこのクラスに加える。
- [4]ここではローカルホストまたはIPアドレス "127.0.0.1 "をUDPポート番号9001で呼び出す。
connect()関数を使用する。OSCSenderオブジェクトを呼び出します。接続に失敗した場合は、プライベート関数showConnectionErrorMessage()エラーメッセージをユーザーに表示するために、後で宣言する。
このチュートリアルでは、後でレシーバーで指定する任意のポート番号で、開発マシンのローカル・ネットワークに接続する。
次にSlider::onValueChangeコールバックSliderオブジェクトを作成し、OSCメッセージをレシーバー・アプリケーションに送信する:
rotaryKnob.onValueChange = [this]
{
// create and send an OSC message with an address and a float value:
if (! sender.send ("/juce/rotaryknob", (float) rotaryKnob.getValue()))
showConnectionErrorMessage ("Error: could not send OSC message.");
};
ラムダ関数の中でsend()関数を使用する。OSCSenderオブジェクトのアドレスとロータリーノブの値[5].ここで"/juce/rotaryknob "として提供されるアドレスは、後で受信機でメッセージを分類することを可能にする。メッセージの送信に失敗した場合は、同じヘルパー関数showConnectionErrorMessage()をクリックするとエラーが表示されます。
この機能を実装するには、関数[AlertWindow::showMessageBoxAsync()](https://docs.juce.com/master/classAlertWindow.html#abf9c608d6e03ddf828fa9629cff1418b "Shows a dialog box that just has a message and a single button to get rid of it."):
void showConnectionErrorMessage (const juce::String& messageText)
{
juce::AlertWindow::showMessageBoxAsync (juce::AlertWindow::WarningIcon,
"Connection error",
messageText,
"OK");
}
これで送信側の実装は完了した。
この修正版のソースコードはOSCSenderTutorial_02.hファイルにある。
OSCレシーバー
このセクションのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
完成すると、OSCレシーバー・アプリケーションは、起動時に操作できない1つのロータリー・ノブを表示する:

OSCレシーバーの実装
レシーバーを実装するにはSliderオブジェクトMainContentComponentクラスで、送り手のロータリー・ノブで行った変更を反映させる:
//==============================================================================
juce::Slider rotaryKnob;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainContentComponent)
};
を宣言する代わりにOSCSenderオブジェクトをメンバーとして継承する。OSCReceiverのサブクラスとしてMainContentComponentを実装する。[1]:
class MainContentComponent : public juce::Component,
private juce::OSCReceiver, // [1]
private juce::OSCReceiver::ListenerWithOSCAddress // [2]
{
public:
また、メッセージ受信時にコールバックを受け取るために、OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>を継承する必要があります。[2]。
クラスのコンストラクタで、同じパラメータをSliderオブジェクトを作成する:
MainContentComponent()
{
rotaryKnob.setRange (0.0, 1.0);
rotaryKnob.setSliderStyle (juce::Slider::RotaryVerticalDrag);
rotaryKnob.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 150, 25);
rotaryKnob.setBounds (10, 10, 180, 180);
rotaryKnob.setInterceptsMouseClicks (false, false);
addAndMakeVisible (rotaryKnob);
// specify here on which UDP port number to receive incoming OSC messages
if (! connect (9001)) // [3]
showConnectionErrorMessage ("Error: could not connect to UDP port 9001.");
// tell the component to listen for OSC messages matching this address:
addListener (this, "/juce/rotaryknob"); // [4]
setSize (200, 200);
}
のサブクラスとしてOSCReceiverメッセージを受信するには、正しいポート番号に直接接続してください。[3]をリッスンするアドレスを提供し、このクラスを自分自身のリスナーとして登録する。[4]。
ポート番号とOSCアドレスが送信側アプリケーションと一致していることを確認してください。
(1)の場合OSCMessageオブジェクトを受信するとoscMessageReceived()コールバック関数が呼び出される:
void oscMessageReceived (const juce::OSCMessage& message) override
{
if (message.size() == 1 && message[0].isFloat32()) // [5]
rotaryKnob.setValue (juce::jlimit (0.0f, 10.0f, message[0].getFloat32())); // [6]
}
この関数をオーバーライドして、まずメッセージのサイズと値の型をチェックする。[5].次に、ロータリー・ノブの値を設定します。Slider範囲[6]。
接続に失敗した場合に備えて、送信側アプリケーションと同じエラー・ロギング機能を実装する:
void showConnectionErrorMessage (const juce::String& messageText)
{
juce::AlertWindow::showMessageBoxAsync (juce::AlertWindow::WarningIcon,
"Connection error",
messageText,
"OK");
}
これで、両方のアプリケーションを起動したときに、送信側ノブを動かして受信側ノブをコントロールできるはずです。
この修正版のソースコードはOSCReceiverTutorial_02.hファイルにある。
OSCモニター
このセクションのデモ・プロジェクトのダウンロードはこちらから:PIP|ZIP.プロジェクトを解凍し、最初のヘッダーファイルをProjucerで開く。
完了すると、OSCモニターアプリケーションはやりとりを記録するウィンドウを表示します。送信者インスタンスなしで最初に起動した場合、モニター・アプリケーションは接続できず、ウィンドウは次のように表示されます:

センダー・インスタンスの実行中に起動すると、モニター・アプリケーションはそのインスタンスに接続できるようになり、ウィンドウは次のように表示されます:

OSCモニター実施
を表示する別のタイプのレシーバーを実装してみよう。OSCMessageオブジェクトをテキストとして扱う。
の中でMainContentComponentクラスを継承していることがわかります。OSCReceiver::Listenerクラス[1].を継承していないことに注意してほしい。OSCReceiver::ListenerWithOSCAddressアドレスに関係なく送信されたすべてのメッセージを受信したいからだ:
class MainContentComponent : public juce::Component,
private juce::OSCReceiver::Listener // [1]
{
public:
OSCLogListBoxとOSCReceiverオブジェクトをプライベート・メンバー変数として使用します。[2]を定義し、ポート番号を格納する一時的なint変数を定義する。[3]以下の通りである:
OSCLogListBox oscLogListBox; // [2]
juce::OSCReceiver oscReceiver;
int currentPortNumber = -1; // [3]
コンストラクターで、対応するパラメーターを以下のように設定する:
MainContentComponent()
{
oscLogListBox.setBounds (0, 60, 700, 340); // [4]
addAndMakeVisible (oscLogListBox);
oscReceiver.addListener (this); // [5]
oscReceiver.registerFormatErrorHandler ([this] (const char* data, int dataSize) // [6]
{
oscLogListBox.addInvalidOSCPacket (data, dataSize);
});
setSize (700, 400);
}
- [4]OSCLogListBoxに境界を設定してComponentが見える。
- [5]このクラスをOSCReceiverオブジェクトでコールバックを受け取ります。
- [6]: カスタムエラーハンドラフォーマットをOSCReceiverをラムダ関数で返します。ここでは、解析に失敗したデータへのポインタとそのサイズをOSCLogListBoxオブジェクトに渡している。
モニター・アプリケーションのユーザー・インタフェース上でボタンがクリックされると、次のステップで実装する対応する関数を呼び出します。
ユーザーが "connect "ボタンをクリックした場合、アプリケーションの状態に応じて接続/切断を行う。[7].さらに、「接続」ボタンが押されるたびにupdateConnectionStatusLabel()ヘルパー関数[8]この通りである:
void connectButtonClicked()
{
if (! isConnected()) // [7]
connect();
else
disconnect();
updateConnectionStatusLabel(); // [8]
}
ステータス・ラベルを更新するには、以下のように接続状態に応じてテキストと色を変更するだけです:
void updateConnectionStatusLabel()
{
juce::String text = "Status: ";
if (isConnected())
text += "Connected to UDP port " + juce::String (currentPortNumber);
else
text += "Disconnected";
auto textColour = isConnected() ? juce::Colours::green : juce::Colours::red;
接続状態を判断するために、ポート番号がデフォルト値の-1から変更されたかどうかをチェックする:
bool isConnected() const
{
return currentPortNumber != -1;
}
そうでなければ、ユーザーが "clear "ボタンを押したとき、単純にOSCLogListBoxにclear命令を送り、スクリーン・ログをリセットする:
void clearButtonClicked()
{
oscLogListBox.clear();
}
送信側アプリケーションに接続するためにconnect()関数である:
void connect()
{
auto portToConnect = portNumberField.getText().getIntValue(); // [9]
if (! isValidOscPort (portToConnect)) // [10]
{
handleInvalidPortNumberEntered();
return;
}
if (oscReceiver.connect (portToConnect)) // [11]
{
currentPortNumber = portToConnect;
connectButton.setButtonText ("Disconnect");
}
else // [12]
{
handleConnectError (portToConnect);
}
}
- [9]ポート番号のテキストをint値に変換し、一時変数に格納する。
- [10]ポート番号が無効な場合は、次のようなエラーメッセージを表示します。
handleInvalidPortNumberEntered()ヘルパー関数。 - [11]そうでなければ、ポート番号が有効であることを意味し、接続を試みることができる。接続に成功したら、一時的なポート番号変数を正しい値に更新し、「接続」ボタンのテキストを変更する。
- [12]接続に失敗した場合は、対応する
handleConnectError()ヘルパー関数でエラーを表示する。
ポート番号が有効であることを確認するために、以下の範囲に該当するかどうかをチェックしてください。1 ..65535包括的だ:
bool isValidOscPort (int port) const
{
return port > 0 && port < 65536;
}
ネットワークから切断するには、切断が成功したかどうかを確認し、成功した場合はポート番号を-1にリセットし、「接続」ボタンのテキストを変更する。[13].それ以外の場合はhandleDisconnectError()ヘルパー関数[14]:
void disconnect()
{
if (oscReceiver.disconnect()) // [13]
{
currentPortNumber = -1;
connectButton.setButtonText ("Connect");
}
else
{
handleDisconnectError(); // [14]
}
}
(1)の場合OSCMessageを受信すると、以下のコールバック関数が呼び出され、メッセージの内容がOSCLogListBoxに転送される:
void oscMessageReceived (const juce::OSCMessage& message) override
{
oscLogListBox.addOSCMessage (message);
}
もしOSCBundleを受信すると、別のコールバック関数が呼び出され、バンドルの内容をOSCLogListBoxに転送する:
void oscBundleReceived (const juce::OSCBundle& bundle) override
{
oscLogListBox.addOSCBundle (bundle);
}
エラーの種類ごとにメッセージボックスを表示し、実装を完了する:
//==============================================================================
void handleConnectError (int failedPort)
{
juce::AlertWindow::showMessageBoxAsync (juce::AlertWindow::WarningIcon,
"OSC Connection error",
"Error: could not connect to port " + juce::String (failedPort),
"OK");
}
//==============================================================================
void handleDisconnectError()
{
juce::AlertWindow::showMessageBoxAsync (juce::AlertWindow::WarningIcon,
"Unknown error",
"An unknown error occured while trying to disconnect from UDP port.",
"OK");
}
//==============================================================================
void handleInvalidPortNumberEntered()
{
juce::AlertWindow::showMessageBoxAsync (juce::AlertWindow::WarningIcon,
"Invalid port number",
"Error: you have entered an invalid UDP port number.",
"OK");
}
これで、送信側のロータリー・ノブが操作されると、モニター・アプリケーションは受信したすべてのメッセージをログに記録し、ウィンドウは次のようになります:

この修正版のソースコードはOSCMonitorTutorial_02.hファイルにある。
以下のような他のGUIコンポーネントを使用して、異なるOSCメッセージを扱うように送信側と受信側のアプリケーションを修正する。ToggleButtonまたはComboBoxオブジェクトがある。
概要
このチュートリアルでは、アプリケーション・インスタンス間で情報を送信するためのOSCプロトコルの実装方法を学びました。特に
- 送信側アプリケーションのロータリーノブを使ってOSCメッセージを送信。
- レシーバー・アプリケーションに接続し、対応するメッセージを処理する。
- モニター・アプリケーションを使用して、OSCとのやりとりを冗長な方法で表示。