OSC 是各软件和设备之间互传数据时常用的协议,多款 PC 软件和手机 App 都支持,一般基于 UDP 进行传输。
但有时会遇到不支持 UDP 的情况,大多是因为网络安全原因而禁止,比如用基于浏览器的 p5.js 或 H5 向 Processing/MaxMSP/Unity 传输数据,还比如我正在开发中的微信小程序 BugOSC(类似 TouchOSC 的控制器),就都无法使用 UDP 来传送 OSC 数据。
所幸这些基于 Web 的平台还支持另一种网络传输协议:WebSocket。
我写了一些例子,用来演示如何通过 WebSocket 来传输 OSC 数据。
有两个关键点:
WebSocket 传输
数据按 OSC 打包与解析
用 WebSocket 可以传输自定义格式数据,之所以要按 OSC 标准格式打包,就是为了兼容各软件既有的功能。
WebSocket 客户端作为发送端,开发语言使用 p5.js;
WebSocket 服务端作为接收端,分别用 Processing、MaxMSP、Unity 来演示(Pure Data、VVVV 等附上资料,陆续补充)。
传输的数据遵循标准 OSC 格式打包成二进制形式。
http://opensoundcontrol.org/introduction-osc
OSC 发送端 - p5.js WebSocket client
p5.js 是基于浏览器的 JavaScript 库,用它来写一个简单的发送程序(其实收发皆可),作为WebSocket 客户端。
这里用到业界良心 osc.js 库,它支持 UDP (NodeJS, 无浏览器), WebSocket, TCP and Serial port(串口),也能单独对数据进行 OSC 打包和解析。
//p5.js sketch:
function setup() {
createCanvas(400, 400);
oscWebSocket = new osc.WebSocketPort({
url: "ws://localhost:12345",
metadata: true
});
oscWebSocket.on("ready", onSocketOpen);
oscWebSocket.on("message", onSocketMessage);
}
function onSendClick() {
const msg = input.value();
input.value('');
// send the OSC message to server.
//(osc.js will convert it to binary packet)
oscWebSocket.send({
address: "/p5js/sayhi",
args: [{
type: "s",
value: msg
}]
});
}
OSC 接收端 - Processing
WebSocket of Processing:
Processing 社区有一个不错的 WebSocket lib:processing WebSocket library。
可惜它只支持传输 String
字符串数据,不支持二进制类型。
大部分时候字符串够用,但如果传输大量数据,二进制更高效一些。
幸运的是有一个 fork 增加了二进制支持,但是年久失修有 Bug,以及仅有 Java 源代码,没有 Processing JAR 包。
基于此 fork 我增加了一版 Bug 修复版,并编译了 JAR 包(用 Processing 3),可直接下载使用。
github.com/avantcontra/processing_websockets
OSC 数据解析:
使用 oscP5 。
oscP5 的解析函数是 protected
,无法直接使用。
这里不再 hack 它,而是直接起一个本地 localhost 的 oscP5 UDP,把 WebSocket 收到的二进制数据包再次转发给自己,oscP5 接收到后会内部将其解包,得到正确的数据。
// Processing sketch:
void setup(){
//WebSocket
ws = new WebsocketServer(this, 12345, "/");
//use OSC (UDP) to parse the data
oscP5 = new OscP5(this,12346);
oscHost = new NetAddress("127.0.0.1", 12346);
}
// The data receiving handler, support byte[]
void webSocketServerEvent(byte[] buf, int offset, int length){
//send to OSC (UDP) to parse the data
OscP5.flush(buf, oscHost);
}
OSC 接收端 - MaxMSP
现在已有几款 Max 与浏览器通信的lib:
xebra.js from MiraWeb
wsserver
2018年新出的 Max8,增加了 Node for Max 功能,可以用 NodeJS 给 Max 写扩展,这里选用此方案。
所以这个例子中的 WebSocket 与 OSC 相关功能,实际上是用 NodeJS 实现的。
仍然用前文 p5.js 部分用到的 osc.js 即可,OSC 数据格式处理及 WebSocket 传输都用此库。
webSocketPort = new osc.WebSocketPort({
socket: ws
});
// receive message
ws.on("message", function incoming(message) {
const msgParsed = osc.readPacket(message, { metadata: true });
console.log("received parsed : ", msgParsed);
maxAPI.outlet('message', msgParsed);
});
// send message
maxAPI.addHandler("send", (...args) => {
if (webSocketPort && isConnected) {
webSocketPort.send({
address: "/max/midi",
args: [{
type: "i",
value: args[0]
}]
});
}
});
OSC 接收端 - Unity
Unity WebSocket:
websocket-sharp 是 WebSocket 的 C# 实现。
我编译了 websocket-sharp.dll
,添加到 Unity 工程目录 Assets/
下即可。
如果这个 dll
你无法使用,可以在自己环境下自行编译。
OSC 数据解析:
市面上有几款 Unity OSC 库,但大部分跟 oscP5 一样,把 OSC 数据打包解析和 UDP 传输合在一起,在这个案例下反而不方便使用。
这时目光转向了 VVVV ...
同是 C# 生,而 VVVV 当然也支持 OSC。
VVVVUnityOSC 这一款想什么来什么的库,就是把 VVVV SDK 中对 OSC 的支持,移植到 Unity 上,并且使用非常方便:
打包: new OSCMessage("/unity/midi", 87).BinaryData
解析: OSCPacket.Unpack(oscPacketRawData)
public class WebSocketOSC : MonoBehaviour
{
public WebSocketServer wssv;
void Start()
{
wssv = new WebSocketServer("ws://127.0.0.1:12345");
wssv.AddWebSocketService("/");
wssv.Start();
}
}
public class Echo : WebSocketBehavior
{
//receive message
protected override void OnMessage(MessageEventArgs e)
{
if (e.IsBinary)
{
OSCPacket msg = OSCPacket.Unpack(e.RawData);
Debug.Log(string.Format("recv OSC addr: {0}, value: {1}", msg.Address, msg.Values[0]));
}
}
protected override void OnOpen()
{
//send message
Send(new OSCMessage("/unity/midi", 87).BinaryData);
}
}
其他 OSC 接收端
另几款常见的互动开发软件,先补充一些资料在这里,欢迎PR!!?
Pure Data:
Websocket-Server-in-a-Patch
"websocket-server in a patch.pd" single Pd file contains a reusable and fully documented websocket server, meant as an easy way to experiment with Pure Data web-browsers communications through websockets, without the need of other software (like node.js or python) or any specific external to compile (just one deken library is required).
pd-ws
Communicate between Pure Data & web browser over a websocket.
VVVV:
C# websocket-sharp
VVVV Websocket (Network Server)
VVVV Venode 这个更像是用 NodeJS 写了一个 Bridge,桥接中转方案。
Bridge 桥接中转方案
当然还可以用桥接的方案:
p5js BRIDGE receivers
用 NodeJS/Python/Whatever 等写一个中转,一方面接收浏览器发来的 WebSocket 数据,另一方面把数据用基于 UDP 的标准 OSC 库发出去,而其他接收端,不用做任何更改,直接用既有的 UDP OSC 接收即可。
这个方案开发简单,但使用时需要多启动一个第三方的 Bridge 程序,各有利弊。
而上文中的 Processing 方案,其实也可以看作是一个中转,只是把收到的数据自己转给了自己,内部消化。
更多
所有例子的源代码都在这里(点击阅读原文):
github.com/avantcontra/osc-websocket-example
Welcome PR!?
Cheers~
文章封面用 Max 生成。
Contra
website: floatbug.com/contra
微信公众号/知乎专栏:浮生开方