Skip to content

Commit

Permalink
Merge pull request #10 from msawired/OSC-support
Browse files Browse the repository at this point in the history
OSC support
  • Loading branch information
msawired authored Sep 16, 2024
2 parents 26e0b80 + b903185 commit 4fa8e0b
Show file tree
Hide file tree
Showing 13 changed files with 1,380 additions and 12 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@

/oscServer/node_modules
/node_modules
.release-it.json
13 changes: 13 additions & 0 deletions .release-it.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"git": {
"tagName": "${version}",
"commitMessage": "Version Bump ${version}",
"requireCleanWorkingDir": true,
"requireUpstream": false,
"commit": true,
"pushTags": true
},
"npm": {
"publish": false
}
}
17 changes: 17 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Server",
"skipFiles": [
"<node_internals>/**"
],
"program": "${workspaceFolder}/oscServer/oscServer.js --verbose"
}
]
}
64 changes: 64 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This is a helper library that allows generative art project to provide a UI to p

OP Configurator communicates with the UI elements via postMessage() in browsers. This also allows configurator to be implemented across frames, making it suitable for use with iframes in code editors. Library can also be overridden to use the same mapping to other interfaces, such as physical buttons, knobs, sliders, etc.

OPC also supports OSC [Open Sound Control](https://en.wikipedia.org/wiki/Open_Sound_Control) protocol and can connect to a websocket server to send/receive OSC data. See [OSC Support](#osc-support).

## Installation
On OpenProcessing, just toggle on the "OP Configurator 3000" in the default libraries list. On an HTML project, you can manually load it as below:
```html
Expand Down Expand Up @@ -247,3 +249,65 @@ Deletes a variable and removes its UI component from the interface.
```javascript
OPC.delete('myVariable');
```


# OSC Support
You can connect OPC to a websocket server to communicate with other devices via OSC protocol. An example websocket server that also opens a UDP port is provided in the oscServer folder. An example OPC control interface (for [TouchOSC](https://hexler.net/touchosc)) is provided in the example folder.


![OSC Recording](./docs/OSC_recording.gif)

## Example Installation and Setup

### Step 1: Clone the Repository

First, clone this repository to your local machine, and install the necessary dependencies for the server.

```sh
git clone https://github.com/msawired/OPC.git
cd OPC/oscServer
npm install
```

### Step 2: Run the OSC Server

Start the OSC server using the following command:

```sh
node oscServer.js

```

The server will start and listen for incoming OSC messages on the specified port. You can also define the ports and output every message received via:

```sh
node oscServer.js 57120 --verbose
```

### Step 3: Connect via TouchOSC

1. Install and open the [TouchOSC](https://hexler.net/touchosc) app on your device.
2. (Optional) Open the example in the "/example/TouchOSC example.tosc"
2. Go to the "Connections" tab and add a new OSC connection.
3. Set the Host as "127.0.0.1"
4. Set the Send Port to the port number specified in the `oscServer.js` file (default is 57120).
5. Save the connection settings.
![TouchOSC setup screenshot](./docs/touchOSC_screenshot.png)
2. Open the example layout provided in the `example` folder of the repository.
3. Press play to connect layout to the server.

Your TouchOSC app should now be able to send and receive OSC messages to and from the OSC server.

### Step 4: Connect OPC to the OSC Server

Add the code below to your sketch to connect OPC to your server:
```js
OPC.setOSC('ws://localhost', 8081);
```
Once connected, any changes in the variable values will be sent as an OSC message to TouchOS and vice versa.

### Troubleshooting

- Ensure that your device and the machine running the OSC server are on the same network.
- Verify that the IP address and port number are correctly configured in TouchOSC.
- Check the console output of the OSC server for any error messages.
Binary file added docs/OSC_recording.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/OSC_recording.webm
Binary file not shown.
Binary file added docs/touchOSC_screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions example/TouchOSC example.tosc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
x��Zms�H�+�O~ry1�VMH�j����2d붶��p*��!���A^�!%$gr�Chx����~z\.��GD(����In�!�
l��;��m\��C[��7t�6:�^4:���L�j���T�4͞�j:���^WvzN�q�V!�h\ϧw���!" #�˫�3�|�V:���`�7��Mhݻ$�|��HɵG�EHW�����p�.�x�ED��Dt���M@> ����'">"sh�Vќ�:> A�%�t��?�ÿ��K�t�XK�}���h^���\� h1�(P � �{$��>Pt�/sj�
����]��`�3��7����y�t�'�D��X�l�UZ0 �%���V�`� +�+��Sl���bI��)�L��eAd-
�uHrKS)"FD���P\���|�u�]��_J�Y�yh�0%��Z`ώ�7R���ֹjw6R5tz��T}���o��K��D])�c�!��+���D9�
"8ԊU���5� ܺwyS�Y�S�
�**�R{���Y��z� HQ�Oj{�֑�w�P(��Us���׷*��x�j����~�J_ �5*=A4 |Z����0,Iv�G�"�W�!���=ҥD)t�PKȇ�*�|]2P�'�d�8�%e?����I�.���>�xz������9���ֹ�=HvW7���1L�O�%0�)��g�/�l���!c�yH�����O��>~�!��^�5�7��6�M��f���������r:
���29��L�muf��l47�US'O�/�HZ� 7z�������?��(x31Fף�5\�^ү�*����ħTī���ˇ��TS�Y>�Y~Ov�Y��;ØNޔ��<b,����`�wK���2�Vʀ����=��`���- �I �Q����y�+�'ƌ��$�CP���,����'fŵ�ȉC;�|�6�($2�����|:|�O� N���*��2ؐA%�Y"$�jv� F��R��E<���[|j;����p�� 'yn�-���;��$�V6Mz�:�N�˩wٯw�O�Fm��S�ҷ8Z�욖����N�!Y�(�}˸�y4>��G�@�6N�WݲW��^ڮt�v��Ty�r�_�=h"O=�*�쓴��В�=��_�lz<��
���n�ᰖD��=�,�4Y*&�RG���E- ��n�oq�Ϗ�o�*�){�D_�>|���Hd�� ��rF�����#y��Die;���:q��8I��IN���IN��8���ts��EN��w��H��|�_㊔M
Expand Down
7 changes: 7 additions & 0 deletions node_modules/.package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

128 changes: 116 additions & 12 deletions opc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,105 @@ class OPC {
constructor() {
this.options = {};
this.collapsed = false;
this.osc = null;
}

static setOSC(server, port = null) {
// you can set the OSC server and port here
// this will be used to send OSC messages when a variable changes
// This will also be used to receive OSC messages to update the variables

//load the OSC library
if (typeof window.OSC === 'undefined') {
let script = document.createElement('script');
script.onload = function () {
console.log('OSC library loaded');
//initialize OSC
OPC.loadOSC(server, port);
};
script.onerror = function () {
console.log('OSC library failed to load');
};
script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/dist/osc-browser.min.js';
document.head.appendChild(script);
}
}

static loadOSC(server, port = null) {
//initialize OSC
OPC.osc = new osc.WebSocketPort({
url: `${server}` + (port ? `:${port}` : ''), // URL to your Web Socket server.
metadata: true
});
OPC.osc.on('open', function () {
console.log('OSC connection opened');
});
OPC.osc.on('close', function () {
console.log('OSC connection closed. Trying again in 5 seconds...');
//try again in 5 seconds
setTimeout(function () {
OPC.osc.open();
}, 5000);

});
OPC.osc.on('error', function (error) {
console.log('OSC connection error. Trying again in 5 seconds...');
//try again in 5 seconds
setTimeout(function () {
OPC.osc.open();
}, 5000);

});
OPC.osc.on('message', function (message) {
//set variables based on message
// console.log('OSC message:', message);
if (message.address) {
let variableName = message.address.slice(1); //remove the first slash
if (OPC.options[variableName]) {
if (message.args.length === 1) {
let value = message.args[0];
if (typeof message.args[0] === 'object') {
value = message.args[0].value;
if (message.args[0].type === 'f') {
value = parseFloat(value);
} else if (message.args[0].type === 'i') {
value = parseInt(value);
} else if (message.args[0].type === 's') {
value = value.split(',');
}
}
OPC._set(variableName, value);
}
}
}
});
OPC.osc.on('ready', function () {
console.log('OSC ready');
});
OPC.osc.open();
}
static oscSendMessage(varName, value) {
let type;
if (Array.isArray(value)) {
type = 's';
value = value.join(',');
} else if (typeof value === 'string') {
type = 's';
} else if (typeof value === 'number') {
type = 'f';
} else if (typeof value === 'boolean') {
type = 'i';
value = value ? 1 : 0;
}
OPC.osc.send({
address: `/${varName}`,
args: [
{ type, value }
]
});
}


static slider(variableNameOrConfig, value, min = 0, max = null, step = null) {
let variableName, label, description;
if (typeof variableNameOrConfig === 'object') {
Expand Down Expand Up @@ -201,53 +298,60 @@ class OPC {
return OPC.options[option.name].value;
},
set: function (value) {
OPC.options[option.name].value = value;
OPC.callParentFunction('OPC', OPC.options[option.name]);
if (typeof window.parameterChanged == 'function') {
window.parameterChanged(option.name, value);
OPC._set(option.name, value);
if (OPC.osc) {
OPC.oscSendMessage(option.name, value);
}
}
});
this.callParentFunction('OPC', option);
return true;
}

static set (variableName, value) {
static _set(variableName, value) {
OPC.options[variableName].value = value;
OPC.callParentFunction('OPC', OPC.options[variableName]);
if (typeof window.parameterChanged == 'function') {
window.parameterChanged(variableName, value);
}
}

static set(variableName, value) {
window[variableName] = value;
}

static buttonPressed (variableName, value) {
static buttonPressed(variableName, value) {
OPC.options[variableName].value = value;
if (typeof window.buttonPressed == 'function') {
window.buttonPressed(variableName, value);
}

}
static buttonReleased (variableName, value) {
static buttonReleased(variableName, value) {
OPC.options[variableName].value = value;
if (typeof window.buttonReleased == 'function') {
window.buttonReleased(variableName, value);
}

}

static collapse () {
static collapse() {
OPC.collapsed = true;
OPC.callParentFunction('OPC_collapsed', OPC.collapsed);
}
static expand () {
static expand() {
OPC.collapsed = false;
OPC.callParentFunction('OPC_collapsed', OPC.collapsed);
}
static delete (variableName) {
if (OPC.options[variableName]){
static delete(variableName) {
if (OPC.options[variableName]) {
delete OPC.options[variableName];
delete window.variableName;
}
OPC.callParentFunction('OPC_delete', variableName);
}

static callParentFunction (functionName, arg = {}) {
static callParentFunction(functionName, arg = {}) {
// console.log(arg);
try {
//try sending as is
Expand Down
Loading

0 comments on commit 4fa8e0b

Please sign in to comment.