node-osc 11.1.1 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitattributes +11 -0
- package/.github/workflows/bump-version.yml +2 -0
- package/.github/workflows/nodejs.yml +3 -0
- package/LICENSE +201 -165
- package/README.md +135 -42
- package/dist/lib/Bundle.js +66 -0
- package/dist/lib/Client.js +137 -22
- package/dist/lib/Message.js +87 -1
- package/dist/lib/Server.js +117 -6
- package/dist/lib/index.js +3 -0
- package/dist/lib/internal/decode.js +4 -4
- package/dist/lib/{internal/osc.js → osc.js} +70 -5
- package/dist/test/lib/osc.js +395 -0
- package/dist/test/test-client.js +152 -0
- package/dist/test/test-e2e.js +9 -3
- package/dist/test/test-encode-decode.js +849 -0
- package/dist/test/test-error-handling.js +116 -0
- package/dist/test/test-osc-internal.js +399 -41
- package/dist/test/test-promises.js +250 -0
- package/dist/test/test-types.js +42 -0
- package/dist/test/util.js +15 -8
- package/docs/API.md +477 -0
- package/docs/GUIDE.md +605 -0
- package/examples/README.md +119 -0
- package/examples/async-await.mjs +57 -0
- package/examples/bundle-example.mjs +92 -0
- package/examples/client.js +22 -5
- package/examples/error-handling.mjs +152 -0
- package/examples/esm.mjs +21 -0
- package/examples/server.js +16 -0
- package/jsdoc.json +16 -0
- package/lib/Bundle.mjs +66 -0
- package/lib/Client.mjs +137 -22
- package/lib/Message.mjs +87 -1
- package/lib/Server.mjs +117 -6
- package/lib/index.mjs +1 -0
- package/lib/internal/decode.mjs +4 -4
- package/lib/{internal/osc.mjs → osc.mjs} +71 -4
- package/package.json +12 -10
- package/rollup.config.mjs +48 -41
- package/scripts/generate-docs.mjs +229 -0
- package/test/fixtures/types/test-cjs-types.ts +19 -0
- package/test/fixtures/types/test-esm-types.ts +35 -0
- package/test/fixtures/types/tsconfig-cjs.test.json +17 -0
- package/test/fixtures/types/tsconfig-esm.test.json +17 -0
- package/test/test-bundle.mjs +0 -1
- package/test/test-client.mjs +152 -0
- package/test/test-e2e.mjs +9 -3
- package/test/test-encode-decode.mjs +847 -0
- package/test/test-error-handling.mjs +115 -0
- package/test/test-osc-internal.mjs +400 -42
- package/test/test-promises.mjs +249 -0
- package/test/test-types.mjs +39 -0
- package/test/util.mjs +15 -8
- package/tsconfig.json +45 -0
- package/types/Bundle.d.mts +70 -0
- package/types/Bundle.d.mts.map +1 -0
- package/types/Client.d.mts +101 -0
- package/types/Client.d.mts.map +1 -0
- package/types/Message.d.mts +84 -0
- package/types/Message.d.mts.map +1 -0
- package/types/Server.d.mts +98 -0
- package/types/Server.d.mts.map +1 -0
- package/types/index.d.mts +6 -0
- package/types/index.d.mts.map +1 -0
- package/types/internal/decode.d.mts +4 -0
- package/types/internal/decode.d.mts.map +1 -0
- package/types/osc.d.mts +66 -0
- package/types/osc.d.mts.map +1 -0
package/docs/GUIDE.md
ADDED
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
# node-osc Guide
|
|
2
|
+
|
|
3
|
+
This guide provides best practices, patterns, and detailed information for using node-osc effectively.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Events](#events)
|
|
8
|
+
- [Error Handling](#error-handling)
|
|
9
|
+
- [Type System](#type-system)
|
|
10
|
+
- [Best Practices](#best-practices)
|
|
11
|
+
- [Troubleshooting](#troubleshooting)
|
|
12
|
+
|
|
13
|
+
## Events
|
|
14
|
+
|
|
15
|
+
The `Server` class extends `EventEmitter` and emits several events for different scenarios.
|
|
16
|
+
|
|
17
|
+
### Server Events
|
|
18
|
+
|
|
19
|
+
#### `listening`
|
|
20
|
+
|
|
21
|
+
Emitted when the server starts listening for messages.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
server.on('listening', () => {
|
|
25
|
+
console.log('Server is ready to receive messages');
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
#### `message`
|
|
30
|
+
|
|
31
|
+
Emitted when an OSC message is received.
|
|
32
|
+
|
|
33
|
+
**Parameters:**
|
|
34
|
+
- `msg` (Array): The message as an array where the first element is the address and subsequent elements are arguments
|
|
35
|
+
- `rinfo` (Object): Remote address information
|
|
36
|
+
- `address` (string): The sender's IP address
|
|
37
|
+
- `port` (number): The sender's port number
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
server.on('message', (msg, rinfo) => {
|
|
41
|
+
const [address, ...args] = msg;
|
|
42
|
+
console.log(`Received ${address} from ${rinfo.address}:${rinfo.port}`);
|
|
43
|
+
console.log('Arguments:', args);
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
#### `bundle`
|
|
48
|
+
|
|
49
|
+
Emitted when an OSC bundle is received.
|
|
50
|
+
|
|
51
|
+
**Parameters:**
|
|
52
|
+
- `bundle` (Object): The bundle object
|
|
53
|
+
- `timetag` (number): The bundle's timetag
|
|
54
|
+
- `elements` (Array): Array of messages or nested bundles
|
|
55
|
+
- `rinfo` (Object): Remote address information
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
server.on('bundle', (bundle, rinfo) => {
|
|
59
|
+
console.log(`Received bundle with timetag ${bundle.timetag}`);
|
|
60
|
+
bundle.elements.forEach((element) => {
|
|
61
|
+
console.log('Element:', element);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Address-Specific Events
|
|
67
|
+
|
|
68
|
+
The server also emits events for each message address received. This allows you to listen for specific OSC addresses without filtering in your code.
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
// Listen specifically for messages to /note
|
|
72
|
+
server.on('/note', (msg, rinfo) => {
|
|
73
|
+
const [address, pitch, velocity] = msg;
|
|
74
|
+
console.log(`Note: ${pitch}, Velocity: ${velocity}`);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Listen for /oscillator/frequency
|
|
78
|
+
server.on('/oscillator/frequency', (msg) => {
|
|
79
|
+
const [address, freq] = msg;
|
|
80
|
+
console.log(`Frequency set to ${freq} Hz`);
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### `error`
|
|
85
|
+
|
|
86
|
+
Emitted when there's an error decoding an incoming message or a socket error.
|
|
87
|
+
|
|
88
|
+
**Parameters:**
|
|
89
|
+
- `error` (Error): The error object
|
|
90
|
+
- `rinfo` (Object): Remote address information (for decode errors)
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
server.on('error', (error, rinfo) => {
|
|
94
|
+
if (rinfo) {
|
|
95
|
+
console.error(`Error from ${rinfo.address}:${rinfo.port}: ${error.message}`);
|
|
96
|
+
} else {
|
|
97
|
+
console.error('Socket error:', error.message);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Client Events
|
|
103
|
+
|
|
104
|
+
#### `error`
|
|
105
|
+
|
|
106
|
+
Emitted when a socket error occurs.
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
client.on('error', (error) => {
|
|
110
|
+
console.error('Client error:', error.message);
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Error Handling
|
|
115
|
+
|
|
116
|
+
Proper error handling is essential for robust OSC applications.
|
|
117
|
+
|
|
118
|
+
### Client Errors
|
|
119
|
+
|
|
120
|
+
#### Sending on Closed Socket
|
|
121
|
+
|
|
122
|
+
If you try to send a message after closing the client, a `ReferenceError` will be thrown:
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
const client = new Client('127.0.0.1', 3333);
|
|
126
|
+
await client.close();
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
await client.send('/test', 123);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error(err.message); // "Cannot send message on closed socket."
|
|
132
|
+
console.error(err.code); // "ERR_SOCKET_DGRAM_NOT_RUNNING"
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Prevention:** Always ensure the client is open before sending:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const client = new Client('127.0.0.1', 3333);
|
|
140
|
+
try {
|
|
141
|
+
await client.send('/test', 123);
|
|
142
|
+
} finally {
|
|
143
|
+
await client.close(); // Close after sending
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Invalid Message Format
|
|
148
|
+
|
|
149
|
+
Passing an invalid message format will throw a `TypeError`:
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
try {
|
|
153
|
+
await client.send(12345); // Not a valid message format
|
|
154
|
+
} catch (err) {
|
|
155
|
+
console.error(err.message); // "That Message Just Doesn't Seem Right"
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Server Errors
|
|
160
|
+
|
|
161
|
+
#### Decoding Errors
|
|
162
|
+
|
|
163
|
+
When the server receives malformed OSC data, it emits an `'error'` event rather than throwing:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
server.on('error', (err, rinfo) => {
|
|
167
|
+
console.error(`Decode error from ${rinfo.address}: ${err.message}`);
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Error Handling Patterns
|
|
172
|
+
|
|
173
|
+
#### With Callbacks
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
client.send('/test', 123, (err) => {
|
|
177
|
+
if (err) {
|
|
178
|
+
console.error('Send failed:', err);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
console.log('Message sent successfully');
|
|
182
|
+
});
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### With Async/Await
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
try {
|
|
189
|
+
await client.send('/test', 123);
|
|
190
|
+
console.log('Message sent successfully');
|
|
191
|
+
} catch (err) {
|
|
192
|
+
console.error('Send failed:', err);
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Always Close Resources
|
|
197
|
+
|
|
198
|
+
Use try/finally to ensure resources are cleaned up even if errors occur:
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
const client = new Client('127.0.0.1', 3333);
|
|
202
|
+
try {
|
|
203
|
+
await client.send('/test', 123);
|
|
204
|
+
await client.send('/test', 456);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
console.error('Error sending:', err);
|
|
207
|
+
} finally {
|
|
208
|
+
await client.close(); // Always executes
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Type System
|
|
213
|
+
|
|
214
|
+
OSC supports several data types. node-osc automatically detects types for common JavaScript values:
|
|
215
|
+
|
|
216
|
+
| JavaScript Type | OSC Type | Description |
|
|
217
|
+
|----------------|----------|-------------|
|
|
218
|
+
| Integer number | `integer` | Whole numbers (e.g., 42, -10, 0) |
|
|
219
|
+
| Float number | `float` | Decimal numbers (e.g., 3.14, -0.5) |
|
|
220
|
+
| String | `string` | Text values (e.g., "hello") |
|
|
221
|
+
| Boolean | `boolean` | true or false |
|
|
222
|
+
| Buffer | `blob` | Binary data |
|
|
223
|
+
| MIDI object/Buffer | `midi` | MIDI messages (4 bytes: port, status, data1, data2) |
|
|
224
|
+
|
|
225
|
+
### Automatic Type Detection
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
const msg = new Message('/test');
|
|
229
|
+
msg.append(42); // → integer
|
|
230
|
+
msg.append(3.14); // → float
|
|
231
|
+
msg.append('hello'); // → string
|
|
232
|
+
msg.append(true); // → boolean
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Explicit Type Control
|
|
236
|
+
|
|
237
|
+
For advanced use cases, you can explicitly specify types:
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
const msg = new Message('/test');
|
|
241
|
+
|
|
242
|
+
// Force a whole number to be sent as float
|
|
243
|
+
msg.append({ type: 'float', value: 42 });
|
|
244
|
+
|
|
245
|
+
// Use shorthand type notation
|
|
246
|
+
msg.append({ type: 'f', value: 42 }); // 'f' = float
|
|
247
|
+
msg.append({ type: 'i', value: 3.14 }); // 'i' = integer (truncates)
|
|
248
|
+
msg.append({ type: 's', value: 'text' }); // 's' = string
|
|
249
|
+
msg.append({ type: 'b', value: Buffer.from('data') }); // 'b' = blob
|
|
250
|
+
msg.append({ type: 'm', value: { port: 0, status: 144, data1: 60, data2: 127 } }); // 'm' = MIDI
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Supported Type Tags
|
|
254
|
+
|
|
255
|
+
- `'i'` or `'integer'` - 32-bit integer
|
|
256
|
+
- `'f'` or `'float'` - 32-bit float
|
|
257
|
+
- `'s'` or `'string'` - OSC string
|
|
258
|
+
- `'b'` or `'blob'` - Binary blob
|
|
259
|
+
- `'m'` or `'midi'` - MIDI message (4 bytes)
|
|
260
|
+
- `'boolean'` - Boolean value (true/false)
|
|
261
|
+
- `'T'` - True
|
|
262
|
+
- `'F'` - False
|
|
263
|
+
|
|
264
|
+
## Best Practices
|
|
265
|
+
|
|
266
|
+
### 1. Use Async/Await for Cleaner Code
|
|
267
|
+
|
|
268
|
+
Prefer async/await over callbacks for more readable code:
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
// ✅ Good - Clean and readable
|
|
272
|
+
async function sendMessages() {
|
|
273
|
+
const client = new Client('127.0.0.1', 3333);
|
|
274
|
+
try {
|
|
275
|
+
await client.send('/test', 1);
|
|
276
|
+
await client.send('/test', 2);
|
|
277
|
+
await client.send('/test', 3);
|
|
278
|
+
} finally {
|
|
279
|
+
await client.close();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ❌ Less ideal - Callback pyramid
|
|
284
|
+
function sendMessages() {
|
|
285
|
+
const client = new Client('127.0.0.1', 3333);
|
|
286
|
+
client.send('/test', 1, (err) => {
|
|
287
|
+
if (err) return console.error(err);
|
|
288
|
+
client.send('/test', 2, (err) => {
|
|
289
|
+
if (err) return console.error(err);
|
|
290
|
+
client.send('/test', 3, (err) => {
|
|
291
|
+
if (err) return console.error(err);
|
|
292
|
+
client.close();
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### 2. Always Close Resources
|
|
300
|
+
|
|
301
|
+
Always close clients and servers when done to prevent resource leaks:
|
|
302
|
+
|
|
303
|
+
```javascript
|
|
304
|
+
const client = new Client('127.0.0.1', 3333);
|
|
305
|
+
try {
|
|
306
|
+
await client.send('/test', 123);
|
|
307
|
+
} finally {
|
|
308
|
+
await client.close(); // Always close
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 3. Use Address-Specific Event Listeners
|
|
313
|
+
|
|
314
|
+
For better code organization, use address-specific event listeners:
|
|
315
|
+
|
|
316
|
+
```javascript
|
|
317
|
+
// ✅ Good - Clear and organized
|
|
318
|
+
server.on('/note', (msg) => {
|
|
319
|
+
handleNote(msg);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
server.on('/control', (msg) => {
|
|
323
|
+
handleControl(msg);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
// ❌ Less ideal - Manual routing
|
|
327
|
+
server.on('message', (msg) => {
|
|
328
|
+
const [address] = msg;
|
|
329
|
+
if (address === '/note') handleNote(msg);
|
|
330
|
+
else if (address === '/control') handleControl(msg);
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 4. Handle Errors Gracefully
|
|
335
|
+
|
|
336
|
+
Always implement error handling for both clients and servers:
|
|
337
|
+
|
|
338
|
+
```javascript
|
|
339
|
+
// Client
|
|
340
|
+
try {
|
|
341
|
+
await client.send('/test', 123);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.error('Failed to send:', err.message);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Server
|
|
347
|
+
server.on('error', (err, rinfo) => {
|
|
348
|
+
console.error(`Server error from ${rinfo?.address}:`, err.message);
|
|
349
|
+
});
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### 5. Use Bundles for Related Messages
|
|
353
|
+
|
|
354
|
+
When sending multiple related messages, use bundles for atomic operations:
|
|
355
|
+
|
|
356
|
+
```javascript
|
|
357
|
+
// ✅ Good - Atomic operation
|
|
358
|
+
const bundle = new Bundle(
|
|
359
|
+
['/synth/freq', 440],
|
|
360
|
+
['/synth/amp', 0.5],
|
|
361
|
+
['/synth/gate', 1]
|
|
362
|
+
);
|
|
363
|
+
await client.send(bundle);
|
|
364
|
+
|
|
365
|
+
// ❌ Less ideal - Separate messages (not atomic)
|
|
366
|
+
await client.send('/synth/freq', 440);
|
|
367
|
+
await client.send('/synth/amp', 0.5);
|
|
368
|
+
await client.send('/synth/gate', 1);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 6. Listen on All Interfaces for Network Access
|
|
372
|
+
|
|
373
|
+
If you need to receive messages from other machines:
|
|
374
|
+
|
|
375
|
+
```javascript
|
|
376
|
+
// Listen on all interfaces (accessible from network)
|
|
377
|
+
const server = new Server(3333, '0.0.0.0');
|
|
378
|
+
|
|
379
|
+
// Only localhost (default, more secure)
|
|
380
|
+
const server = new Server(3333, '127.0.0.1');
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### 7. Use Descriptive OSC Addresses
|
|
384
|
+
|
|
385
|
+
Follow OSC naming conventions with hierarchical addresses:
|
|
386
|
+
|
|
387
|
+
```javascript
|
|
388
|
+
// ✅ Good - Hierarchical and descriptive
|
|
389
|
+
await client.send('/synth/oscillator/1/frequency', 440);
|
|
390
|
+
await client.send('/mixer/channel/3/volume', 0.8);
|
|
391
|
+
|
|
392
|
+
// ❌ Less ideal - Flat and unclear
|
|
393
|
+
await client.send('/freq1', 440);
|
|
394
|
+
await client.send('/vol3', 0.8);
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### 8. Validate Input Data
|
|
398
|
+
|
|
399
|
+
Validate data before sending to avoid runtime errors:
|
|
400
|
+
|
|
401
|
+
```javascript
|
|
402
|
+
function sendNote(pitch, velocity) {
|
|
403
|
+
if (typeof pitch !== 'number' || pitch < 0 || pitch > 127) {
|
|
404
|
+
throw new Error('Invalid pitch: must be 0-127');
|
|
405
|
+
}
|
|
406
|
+
if (typeof velocity !== 'number' || velocity < 0 || velocity > 127) {
|
|
407
|
+
throw new Error('Invalid velocity: must be 0-127');
|
|
408
|
+
}
|
|
409
|
+
return client.send('/note', pitch, velocity);
|
|
410
|
+
}
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### 9. Wait for Server Ready
|
|
414
|
+
|
|
415
|
+
Always wait for the server to be listening before sending messages:
|
|
416
|
+
|
|
417
|
+
```javascript
|
|
418
|
+
import { once } from 'node:events';
|
|
419
|
+
|
|
420
|
+
const server = new Server(3333, '0.0.0.0');
|
|
421
|
+
|
|
422
|
+
// Wait for server to be ready
|
|
423
|
+
await once(server, 'listening');
|
|
424
|
+
|
|
425
|
+
// Now safe to send messages
|
|
426
|
+
console.log('Server ready!');
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
### 10. Use Parallel Sends When Appropriate
|
|
430
|
+
|
|
431
|
+
When sending multiple independent messages, use `Promise.all` for better performance:
|
|
432
|
+
|
|
433
|
+
```javascript
|
|
434
|
+
// Send multiple messages in parallel
|
|
435
|
+
await Promise.all([
|
|
436
|
+
client.send('/track/1/volume', 0.8),
|
|
437
|
+
client.send('/track/2/volume', 0.6),
|
|
438
|
+
client.send('/track/3/volume', 0.9)
|
|
439
|
+
]);
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Troubleshooting
|
|
443
|
+
|
|
444
|
+
### Messages Not Being Received
|
|
445
|
+
|
|
446
|
+
**Possible causes and solutions:**
|
|
447
|
+
|
|
448
|
+
1. **Firewall blocking UDP traffic**
|
|
449
|
+
- Check your firewall settings
|
|
450
|
+
- Ensure the UDP port is open
|
|
451
|
+
- Try with localhost first (`127.0.0.1`)
|
|
452
|
+
|
|
453
|
+
2. **Wrong host binding**
|
|
454
|
+
- Server: Use `'0.0.0.0'` to listen on all interfaces
|
|
455
|
+
- Server: Use `'127.0.0.1'` for localhost only
|
|
456
|
+
- Client: Match the server's IP address
|
|
457
|
+
|
|
458
|
+
3. **Port mismatch**
|
|
459
|
+
- Ensure client and server use the same port number
|
|
460
|
+
- Check if another process is using the port
|
|
461
|
+
|
|
462
|
+
4. **Network connectivity**
|
|
463
|
+
- Test with localhost first (`127.0.0.1`)
|
|
464
|
+
- Verify network connectivity between machines
|
|
465
|
+
- Check if devices are on the same network
|
|
466
|
+
|
|
467
|
+
### "Cannot send message on closed socket"
|
|
468
|
+
|
|
469
|
+
This error occurs when trying to send after closing the client:
|
|
470
|
+
|
|
471
|
+
```javascript
|
|
472
|
+
// ❌ Wrong - Sending after close
|
|
473
|
+
await client.close();
|
|
474
|
+
await client.send('/test', 123); // Error!
|
|
475
|
+
|
|
476
|
+
// ✅ Correct - Send before close
|
|
477
|
+
await client.send('/test', 123);
|
|
478
|
+
await client.close();
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Server Not Listening
|
|
482
|
+
|
|
483
|
+
Ensure you wait for the server to start before sending messages:
|
|
484
|
+
|
|
485
|
+
```javascript
|
|
486
|
+
const server = new Server(3333, '0.0.0.0');
|
|
487
|
+
|
|
488
|
+
// Wait for server to be ready
|
|
489
|
+
await new Promise(resolve => server.on('listening', resolve));
|
|
490
|
+
|
|
491
|
+
// Now safe to send messages
|
|
492
|
+
console.log('Server ready!');
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### Messages Lost or Dropped
|
|
496
|
+
|
|
497
|
+
UDP is unreliable by design and messages can be lost:
|
|
498
|
+
|
|
499
|
+
**Solutions:**
|
|
500
|
+
1. Use TCP-based OSC if reliability is critical (requires custom implementation)
|
|
501
|
+
2. Implement acknowledgment messages
|
|
502
|
+
3. Add retry logic for critical messages
|
|
503
|
+
4. Use bundles to ensure related messages arrive together
|
|
504
|
+
|
|
505
|
+
### High CPU Usage
|
|
506
|
+
|
|
507
|
+
If you're seeing high CPU usage:
|
|
508
|
+
|
|
509
|
+
1. **Check for infinite loops** in event handlers
|
|
510
|
+
2. **Rate limit** message sending if sending many messages
|
|
511
|
+
3. **Use bundles** instead of many individual messages
|
|
512
|
+
4. **Close unused connections** to free resources
|
|
513
|
+
|
|
514
|
+
### Memory Leaks
|
|
515
|
+
|
|
516
|
+
To prevent memory leaks:
|
|
517
|
+
|
|
518
|
+
1. **Always close** clients and servers when done
|
|
519
|
+
2. **Remove event listeners** when no longer needed
|
|
520
|
+
3. **Avoid** creating new clients/servers in loops
|
|
521
|
+
4. **Reuse** client/server instances when possible
|
|
522
|
+
|
|
523
|
+
```javascript
|
|
524
|
+
// ✅ Good - Proper cleanup
|
|
525
|
+
const server = new Server(3333);
|
|
526
|
+
const handler = (msg) => console.log(msg);
|
|
527
|
+
server.on('message', handler);
|
|
528
|
+
|
|
529
|
+
// Later, clean up
|
|
530
|
+
server.removeListener('message', handler);
|
|
531
|
+
await server.close();
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
## Advanced Topics
|
|
535
|
+
|
|
536
|
+
### Custom Transports
|
|
537
|
+
|
|
538
|
+
The `encode` and `decode` functions allow you to use OSC over custom transports:
|
|
539
|
+
|
|
540
|
+
```javascript
|
|
541
|
+
import { encode, decode, Message } from 'node-osc';
|
|
542
|
+
import WebSocket from 'ws';
|
|
543
|
+
|
|
544
|
+
// WebSocket server
|
|
545
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
|
546
|
+
wss.on('connection', (ws) => {
|
|
547
|
+
ws.on('message', (data) => {
|
|
548
|
+
const oscMsg = decode(data);
|
|
549
|
+
console.log('Received:', oscMsg);
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
// WebSocket client
|
|
554
|
+
const ws = new WebSocket('ws://localhost:8080');
|
|
555
|
+
const message = new Message('/test', 123);
|
|
556
|
+
ws.send(encode(message));
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
### Timetags in Bundles
|
|
560
|
+
|
|
561
|
+
OSC bundles support timetags for scheduling:
|
|
562
|
+
|
|
563
|
+
```javascript
|
|
564
|
+
// Immediate execution (timetag = 0)
|
|
565
|
+
const bundle = new Bundle(['/test', 1], ['/test', 2]);
|
|
566
|
+
|
|
567
|
+
// Scheduled execution (timetag in OSC time)
|
|
568
|
+
const futureTime = Date.now() / 1000 + 5; // 5 seconds from now
|
|
569
|
+
const scheduledBundle = new Bundle(futureTime, ['/test', 1]);
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Note:** The server receives the timetag but does not automatically schedule execution. You must implement scheduling logic if needed.
|
|
573
|
+
|
|
574
|
+
### Performance Optimization
|
|
575
|
+
|
|
576
|
+
For high-throughput applications:
|
|
577
|
+
|
|
578
|
+
1. **Reuse client instances** instead of creating new ones
|
|
579
|
+
2. **Use bundles** to send multiple messages together
|
|
580
|
+
3. **Batch messages** and send periodically rather than immediately
|
|
581
|
+
4. **Use binary blobs** for large data instead of many arguments
|
|
582
|
+
5. **Profile your code** to identify bottlenecks
|
|
583
|
+
|
|
584
|
+
```javascript
|
|
585
|
+
// ✅ Good - Reuse client
|
|
586
|
+
const client = new Client('127.0.0.1', 3333);
|
|
587
|
+
for (let i = 0; i < 1000; i++) {
|
|
588
|
+
await client.send('/test', i);
|
|
589
|
+
}
|
|
590
|
+
await client.close();
|
|
591
|
+
|
|
592
|
+
// ❌ Bad - Creating many clients
|
|
593
|
+
for (let i = 0; i < 1000; i++) {
|
|
594
|
+
const client = new Client('127.0.0.1', 3333);
|
|
595
|
+
await client.send('/test', i);
|
|
596
|
+
await client.close();
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
## Further Reading
|
|
601
|
+
|
|
602
|
+
- [API Documentation](./API.md) - Complete API reference
|
|
603
|
+
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Official OSC 1.0 specification
|
|
604
|
+
- [Examples](../examples/) - Working code examples
|
|
605
|
+
- [Main README](../README.md) - Quick start guide
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# node-osc Examples
|
|
2
|
+
|
|
3
|
+
This directory contains working examples demonstrating various ways to use the node-osc library.
|
|
4
|
+
|
|
5
|
+
## Running the Examples
|
|
6
|
+
|
|
7
|
+
All examples can be run directly with Node.js. Some examples require a server and client running simultaneously.
|
|
8
|
+
|
|
9
|
+
### Server/Client Examples
|
|
10
|
+
|
|
11
|
+
Run the server in one terminal:
|
|
12
|
+
```bash
|
|
13
|
+
node examples/server.js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Run the client in another terminal:
|
|
17
|
+
```bash
|
|
18
|
+
node examples/client.js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Standalone Examples
|
|
22
|
+
|
|
23
|
+
These examples run both client and server in the same process:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Callback-based example
|
|
27
|
+
node examples/esm.mjs
|
|
28
|
+
|
|
29
|
+
# Async/await example (recommended)
|
|
30
|
+
node examples/async-await.mjs
|
|
31
|
+
|
|
32
|
+
# Bundle example
|
|
33
|
+
node examples/bundle-example.mjs
|
|
34
|
+
|
|
35
|
+
# Error handling example
|
|
36
|
+
node examples/error-handling.mjs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Example Files
|
|
40
|
+
|
|
41
|
+
### [server.js](./server.js)
|
|
42
|
+
**CommonJS Server Example**
|
|
43
|
+
|
|
44
|
+
Demonstrates:
|
|
45
|
+
- Creating an OSC server with CommonJS
|
|
46
|
+
- Listening for messages
|
|
47
|
+
- Displaying remote client information
|
|
48
|
+
- Closing the server after receiving a message
|
|
49
|
+
|
|
50
|
+
### [client.js](./client.js)
|
|
51
|
+
**CommonJS Client Example**
|
|
52
|
+
|
|
53
|
+
Demonstrates:
|
|
54
|
+
- Creating an OSC client with CommonJS
|
|
55
|
+
- Building messages with the Message class
|
|
56
|
+
- Sending messages with callbacks
|
|
57
|
+
|
|
58
|
+
### [esm.mjs](./esm.mjs)
|
|
59
|
+
**ESM with Callbacks Example**
|
|
60
|
+
|
|
61
|
+
Demonstrates:
|
|
62
|
+
- Using node-osc with ES modules
|
|
63
|
+
- Callback-based API
|
|
64
|
+
- Server event listeners
|
|
65
|
+
- Client sending with callbacks
|
|
66
|
+
|
|
67
|
+
### [async-await.mjs](./async-await.mjs)
|
|
68
|
+
**ESM with Async/Await Example** (Recommended Pattern)
|
|
69
|
+
|
|
70
|
+
Demonstrates:
|
|
71
|
+
- Modern async/await patterns
|
|
72
|
+
- Using `node:events.once()` to wait for server ready
|
|
73
|
+
- Sending multiple messages in parallel with `Promise.all()`
|
|
74
|
+
- Clean shutdown of both client and server
|
|
75
|
+
- Complete end-to-end workflow
|
|
76
|
+
|
|
77
|
+
Expected output:
|
|
78
|
+
```
|
|
79
|
+
OSC server listening on port 3333
|
|
80
|
+
Sent /hello
|
|
81
|
+
Sent counters
|
|
82
|
+
Received: /hello [ 'world' ]
|
|
83
|
+
Received: /counter [ 1 ]
|
|
84
|
+
Received: /counter [ 2 ]
|
|
85
|
+
Received: /counter [ 3 ]
|
|
86
|
+
Client and server closed
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### [bundle-example.mjs](./bundle-example.mjs)
|
|
90
|
+
**OSC Bundles Example**
|
|
91
|
+
|
|
92
|
+
Demonstrates:
|
|
93
|
+
- Creating OSC bundles with multiple messages
|
|
94
|
+
- Sending bundles atomically
|
|
95
|
+
- Receiving and processing bundles
|
|
96
|
+
- Using timetags
|
|
97
|
+
|
|
98
|
+
### [error-handling.mjs](./error-handling.mjs)
|
|
99
|
+
**Error Handling Example**
|
|
100
|
+
|
|
101
|
+
Demonstrates:
|
|
102
|
+
- Proper error handling with try/catch
|
|
103
|
+
- Server error events
|
|
104
|
+
- Resource cleanup with finally blocks
|
|
105
|
+
- Handling closed socket errors
|
|
106
|
+
|
|
107
|
+
## Tips
|
|
108
|
+
|
|
109
|
+
1. **Always close resources**: Use try/finally or ensure you call `.close()` on clients and servers
|
|
110
|
+
2. **Wait for server ready**: Use the 'listening' event before sending messages
|
|
111
|
+
3. **Use async/await**: The async/await pattern is cleaner than callbacks for most use cases
|
|
112
|
+
4. **Handle errors**: Always implement error handling in production code
|
|
113
|
+
5. **Test locally first**: Start with `127.0.0.1` before trying network communication
|
|
114
|
+
|
|
115
|
+
## Further Reading
|
|
116
|
+
|
|
117
|
+
- [Main README](../README.md) - Quick start guide
|
|
118
|
+
- [API Documentation](../docs/API.md) - Complete API reference
|
|
119
|
+
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Learn about the OSC protocol
|