openframeworks_多媒體開發函式庫_OSC_網路傳輸_Send and Receive the Parameters

OSC 遠端網路傳輸控制技術探討講座

全名 : Open Sound Control

時常出現的工作職務

人機互動軟體研發工程師
多媒體開發工程師
互動(軟體) / 交互式 工程師

擅長做特別硬體的架設及軟硬體整合
(Sensor(感測周邊元件)、體感裝置(Kinect、leapmotion)、生物傳感器(Biosensor)....etc)

擁有圖學方面深層知識
要經常外派,要到處跑跑走(出外探險)
要懂電路學
要和眾多硬體零件搏鬥並熟知規格細項
要具備「人機互動軟體設計」  ----> 聲訊、視訊、物理光學.....網路
要有嵌入式系統設計概念



參考 link:
https://www.youtube.com/watch?v=evPQvNZbqck&feature=youtu.be
http://www.ofcourse.io/index.html
https://www.104.com.tw/job/?jobno=4b1u9
https://www.104.com.tw/jobbank/custjob/index.php?r=cust&j=4e62456f586e4b6a585a3d233e30386193a3c446d363840693e443c1f1f1f1f619483c5e3361j01

一些工作場合


MTF London: Rebecca Fiebrink

The Machine Learning Pipeline



Plyaing sound according to facial expression [openFrameworks + wekinator + Max MSP]





 黃怡靜同學 Openframeworks 介紹


這次我要跟大家分享主要用在
和 電腦、聲音合成器、體感裝置或是其他多媒體裝置做遠端溝通控制的
一個協定,OSC

分兩小節作介紹

(1)OSC protocol specifics

(2)Implementing sending and receiving OSC messages




(1)OSC protocol specifics

OSC它主要原理是藉由低延遲開放網路協定
(open sound control networking protocol intended for low-latency delivery)

分作傳送端及接收端
之間藉由傳遞  OSC messages 作溝通
且OSC messages  主要可乘載資料量不超過  1KB



多數OSC 是使用UDP網路協定去實作的
所以基本上UDP又被稱作不可靠傳輸
不需要做error檢查  更不用經過交握

(Based on UDP , so no "hand-shaking" required and no error checking !!!!)

OSC 協定 是最為適當被用來作為多個應用程式之間
多參數傳遞之媒介

至於剛剛所說的這些應用程式(Apps)可以是在同一台電腦上
或是同一網域下的不同台電腦裝置喔!!!

OSC message 的架構:

主要可分為
part1.  Address (name of parameter)            Ex:    /move/x
part2.  No , one or more Arguments              Ex:   (int , float , string , boolean)


OSC message主要包含了Address 和 一些參數

Address 是一個包含多個藉由forward slash symbol 去開始與修除的 tokens的字串
就像是URL的感覺,功能主要是去辨別區分OSC messages的標題
好讓接收端知道該如何抉擇要讓這些不同messages各自去執行特定指令功能

若不懂捨麼是forward slash symbol  ---->  / 這個斜線符號  啦!!!!
參考:https://www.computerhope.com/jargon/f/forwards.htm

此外OSC messages 還可以有一或多個參數
型態就跟一般寫程式時常看到那些整數、浮點、布林一樣

歸總來說

Address  就是 指令或參數的名字(稱呼)

Arguments  就是參數的value




(2)Implementing sending and receiving OSC messages

在openframeworks之中

主要是靠ofxOsc插件中
以下幾個類別去實作這些上述所講的功能

ofxOsc core Addon


  1. ofxOscSender
  2. ofxOscReceiver
  3. ofxOscMessage



Step1.
使用openframeworks 開啟一個新配置專案
Addons 選用: ofxOsc




開啟新的專案後基本上
架構又分為三部分
ofApp.h


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

 public:
  void setup();
  void update();
  void draw();

  void keyPressed(int key);
  void keyReleased(int key);
  void mouseMoved(int x, int y );
  void mouseDragged(int x, int y, int button);
  void mousePressed(int x, int y, int button);
  void mouseReleased(int x, int y, int button);
  void mouseEntered(int x, int y);
  void mouseExited(int x, int y);
  void windowResized(int w, int h);
  void dragEvent(ofDragInfo dragInfo);
  void gotMessage(ofMessage msg);
  
};

ofApp.cpp



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){

}

//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){

}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}


main.cpp



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include "ofMain.h"
#include "ofApp.h"

//========================================================================
int main( ){
 ofSetupOpenGL(1024,768,OF_WINDOW);   // <-------- setup the GL context

 // this kicks off the running of my app
 // can be OF_WINDOW or OF_FULLSCREEN
 // pass in width and height too:
 ofRunApp(new ofApp());

}


 Step2.

到ofApp.h  引用所需標頭
以及 宣告所需OSC傳輸/接收之類別型態

(PS:目前示範在同一台電腦 , 你可以試試在別台電腦下運行)


再切換至ofApp.cpp 檔案



於setup區塊針對  sender  及  receiver 作設定
指定sender目前位於捨麼主機位置 哪個port
指定receiver只能接收偵聽來自哪個特定port的封包
(port必須一致!!!)

在  ofxOscSender 類別 所宣告的變數sender中
我們呼叫到了  setup 的 method  參數兩個分別填上
hostname  及 port
這裡的  hotname 也可用 IP Address去寫


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class ofxOscSender
{
public:
 ofxOscSender();
 ofxOscSender(const ofxOscSender & mom);
 ofxOscSender & operator=(const ofxOscSender & mom);

 /// send messages to hostname and port
 void setup( const std::string &hostname, int port );

 /// send the given message
 void sendMessage( ofxOscMessage& message, bool wrapInBundle = true );
 /// send the given bundle
 void sendBundle( ofxOscBundle& bundle );
 /// creates a message using an ofParameter
 void sendParameter( const ofAbstractParameter & parameter);

 /// disables broadcast capabilities, usually call this before setup
 void disableBroadcast();

 /// enabled broadcast capabilities (usually no need to call this, enabled by default)
 void enableBroadcast();

private:
 void setup(osc::UdpTransmitSocket * socket);
 void shutdown();
  
 // helper methods for constructing messages
 void appendBundle( ofxOscBundle& bundle, osc::OutboundPacketStream& p );
 void appendMessage( ofxOscMessage& message, osc::OutboundPacketStream& p );
    void appendParameter( ofxOscBundle & bundle, const ofAbstractParameter & parameter, const std::string &address);
    void appendParameter( ofxOscMessage & msg, const ofAbstractParameter & parameter, const std::string &address);

  std::unique_ptr<osc::UdpTransmitSocket> socket;
  bool broadcast;
  std::string hostname;
  int port;

};

在  ofxOscReceiver 類別中  我們也同樣呼叫了
setup  的  method
參數只填區別辨識跟誰傳message的對象 port


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class ofxOscReceiver : public osc::OscPacketListener
{
public:
 ofxOscReceiver();
 ofxOscReceiver(const ofxOscReceiver & mom);
 ofxOscReceiver & operator=(const ofxOscReceiver & mom);

 /// listen_port is the port to listen for messages on
 void setup( int listen_port );

 /// returns true if there are any messages waiting for collection
 bool hasWaitingMessages();
 /// take the next message on the queue of received messages, copy its details into message, and
 /// remove it from the queue. return false if there are no more messages to be got, otherwise
 /// return true
 OF_DEPRECATED_MSG("Pass a reference instead of a pointer", bool getNextMessage( ofxOscMessage* msg));
 bool getNextMessage( ofxOscMessage& msg );

 bool getParameter(ofAbstractParameter & parameter);

 /// disables port reuse reuse which allows to use the same port by several sockets
 void disableReuse();

 /// enabled broadcast capabilities (usually no need to call this, enabled by default)
 void enableReuse();

protected:
 /// process an incoming osc message and add it to the queue
 virtual void ProcessMessage( const osc::ReceivedMessage &m, const osc::IpEndpointName& remoteEndpoint );

private:
 void setup(osc::UdpListeningReceiveSocket * socket);
 // shutdown the listener
 void shutdown();

 // start the listening thread
#ifdef TARGET_WIN32
 static DWORD WINAPI startThread( void* ofxOscReceiverInstance );
#else
 static void* startThread( void* ofxOscReceiverInstance );
#endif

 // socket to listen on
 std::unique_ptr<osc::UdpListeningReceiveSocket, std::function<void(osc::UdpListeningReceiveSocket*)>> listen_socket;

 std::thread listen_thread;
 ofThreadChannel<ofxOscMessage> messagesChannel;

 bool allowReuse;
 int listen_port;

};

這裡由於我要顯示傳送端 跟 接收段各自更新的時間

所以回到剛剛的
ofApp.h

再多加宣告兩個 float 變數





這裡我們在  ofApp.cpp  中

void ofApp::update(){

}

撰寫ˋ以下Code

首先在程式每次更新之時
我們先取得 傳送端的程式執行經過時間(浮點)耗時存取
(使用ofGetElapsedTimef()  method )

之後每次都會在更新一個畫面的同時
生成 OSC message
設定其Address  和  float型態之參數
傳送傳送端當下之耗時
給接收端

當接收端一直是有資料要等待接收的時候(為真 , 回傳true時)
生成OSC message 暫存
接收端就會持續取得下一個message

對每次來的osc message 進行address的檢查
若符合就設定 timeReceived 時候的 value給他的參數





void ofApp::draw(){

}



我們要將時間結果顯示於螢幕上

傳遞端耗時
接收端耗時
傳遞端耗時  -  接收端耗時




會發現在
傳遞端耗時  -  接收端耗時
顯示並非 總是 0
主要是因為  接收段目前是用第二個執行緒去執行
所以會有時間延遲差異喔!!








留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題