Initial commit of my folder
This commit is contained in:
605
backend/node_modules/node-osc/docs/GUIDE.md
generated
vendored
Normal file
605
backend/node_modules/node-osc/docs/GUIDE.md
generated
vendored
Normal file
@ -0,0 +1,605 @@
|
||||
# node-osc Guide
|
||||
|
||||
This guide provides best practices, patterns, and detailed information for using node-osc effectively.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Events](#events)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Type System](#type-system)
|
||||
- [Best Practices](#best-practices)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Events
|
||||
|
||||
The `Server` class extends `EventEmitter` and emits several events for different scenarios.
|
||||
|
||||
### Server Events
|
||||
|
||||
#### `listening`
|
||||
|
||||
Emitted when the server starts listening for messages.
|
||||
|
||||
```javascript
|
||||
server.on('listening', () => {
|
||||
console.log('Server is ready to receive messages');
|
||||
});
|
||||
```
|
||||
|
||||
#### `message`
|
||||
|
||||
Emitted when an OSC message is received.
|
||||
|
||||
**Parameters:**
|
||||
- `msg` (Array): The message as an array where the first element is the address and subsequent elements are arguments
|
||||
- `rinfo` (Object): Remote address information
|
||||
- `address` (string): The sender's IP address
|
||||
- `port` (number): The sender's port number
|
||||
|
||||
```javascript
|
||||
server.on('message', (msg, rinfo) => {
|
||||
const [address, ...args] = msg;
|
||||
console.log(`Received ${address} from ${rinfo.address}:${rinfo.port}`);
|
||||
console.log('Arguments:', args);
|
||||
});
|
||||
```
|
||||
|
||||
#### `bundle`
|
||||
|
||||
Emitted when an OSC bundle is received.
|
||||
|
||||
**Parameters:**
|
||||
- `bundle` (Object): The bundle object
|
||||
- `timetag` (number): The bundle's timetag
|
||||
- `elements` (Array): Array of messages or nested bundles
|
||||
- `rinfo` (Object): Remote address information
|
||||
|
||||
```javascript
|
||||
server.on('bundle', (bundle, rinfo) => {
|
||||
console.log(`Received bundle with timetag ${bundle.timetag}`);
|
||||
bundle.elements.forEach((element) => {
|
||||
console.log('Element:', element);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### Address-Specific Events
|
||||
|
||||
The server also emits events for each message address received. This allows you to listen for specific OSC addresses without filtering in your code.
|
||||
|
||||
```javascript
|
||||
// Listen specifically for messages to /note
|
||||
server.on('/note', (msg, rinfo) => {
|
||||
const [address, pitch, velocity] = msg;
|
||||
console.log(`Note: ${pitch}, Velocity: ${velocity}`);
|
||||
});
|
||||
|
||||
// Listen for /oscillator/frequency
|
||||
server.on('/oscillator/frequency', (msg) => {
|
||||
const [address, freq] = msg;
|
||||
console.log(`Frequency set to ${freq} Hz`);
|
||||
});
|
||||
```
|
||||
|
||||
#### `error`
|
||||
|
||||
Emitted when there's an error decoding an incoming message or a socket error.
|
||||
|
||||
**Parameters:**
|
||||
- `error` (Error): The error object
|
||||
- `rinfo` (Object): Remote address information (for decode errors)
|
||||
|
||||
```javascript
|
||||
server.on('error', (error, rinfo) => {
|
||||
if (rinfo) {
|
||||
console.error(`Error from ${rinfo.address}:${rinfo.port}: ${error.message}`);
|
||||
} else {
|
||||
console.error('Socket error:', error.message);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Client Events
|
||||
|
||||
#### `error`
|
||||
|
||||
Emitted when a socket error occurs.
|
||||
|
||||
```javascript
|
||||
client.on('error', (error) => {
|
||||
console.error('Client error:', error.message);
|
||||
});
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Proper error handling is essential for robust OSC applications.
|
||||
|
||||
### Client Errors
|
||||
|
||||
#### Sending on Closed Socket
|
||||
|
||||
If you try to send a message after closing the client, a `ReferenceError` will be thrown:
|
||||
|
||||
```javascript
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
await client.close();
|
||||
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
} catch (err) {
|
||||
console.error(err.message); // "Cannot send message on closed socket."
|
||||
console.error(err.code); // "ERR_SOCKET_DGRAM_NOT_RUNNING"
|
||||
}
|
||||
```
|
||||
|
||||
**Prevention:** Always ensure the client is open before sending:
|
||||
|
||||
```javascript
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
} finally {
|
||||
await client.close(); // Close after sending
|
||||
}
|
||||
```
|
||||
|
||||
#### Invalid Message Format
|
||||
|
||||
Passing an invalid message format will throw a `TypeError`:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
await client.send(12345); // Not a valid message format
|
||||
} catch (err) {
|
||||
console.error(err.message); // "That Message Just Doesn't Seem Right"
|
||||
}
|
||||
```
|
||||
|
||||
### Server Errors
|
||||
|
||||
#### Decoding Errors
|
||||
|
||||
When the server receives malformed OSC data, it emits an `'error'` event rather than throwing:
|
||||
|
||||
```javascript
|
||||
server.on('error', (err, rinfo) => {
|
||||
console.error(`Decode error from ${rinfo.address}: ${err.message}`);
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling Patterns
|
||||
|
||||
#### With Callbacks
|
||||
|
||||
```javascript
|
||||
client.send('/test', 123, (err) => {
|
||||
if (err) {
|
||||
console.error('Send failed:', err);
|
||||
return;
|
||||
}
|
||||
console.log('Message sent successfully');
|
||||
});
|
||||
```
|
||||
|
||||
#### With Async/Await
|
||||
|
||||
```javascript
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
console.log('Message sent successfully');
|
||||
} catch (err) {
|
||||
console.error('Send failed:', err);
|
||||
}
|
||||
```
|
||||
|
||||
#### Always Close Resources
|
||||
|
||||
Use try/finally to ensure resources are cleaned up even if errors occur:
|
||||
|
||||
```javascript
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
await client.send('/test', 456);
|
||||
} catch (err) {
|
||||
console.error('Error sending:', err);
|
||||
} finally {
|
||||
await client.close(); // Always executes
|
||||
}
|
||||
```
|
||||
|
||||
## Type System
|
||||
|
||||
OSC supports several data types. node-osc automatically detects types for common JavaScript values:
|
||||
|
||||
| JavaScript Type | OSC Type | Description |
|
||||
|----------------|----------|-------------|
|
||||
| Integer number | `integer` | Whole numbers (e.g., 42, -10, 0) |
|
||||
| Float number | `float` | Decimal numbers (e.g., 3.14, -0.5) |
|
||||
| String | `string` | Text values (e.g., "hello") |
|
||||
| Boolean | `boolean` | true or false |
|
||||
| Buffer | `blob` | Binary data |
|
||||
| MIDI object/Buffer | `midi` | MIDI messages (4 bytes: port, status, data1, data2) |
|
||||
|
||||
### Automatic Type Detection
|
||||
|
||||
```javascript
|
||||
const msg = new Message('/test');
|
||||
msg.append(42); // → integer
|
||||
msg.append(3.14); // → float
|
||||
msg.append('hello'); // → string
|
||||
msg.append(true); // → boolean
|
||||
```
|
||||
|
||||
### Explicit Type Control
|
||||
|
||||
For advanced use cases, you can explicitly specify types:
|
||||
|
||||
```javascript
|
||||
const msg = new Message('/test');
|
||||
|
||||
// Force a whole number to be sent as float
|
||||
msg.append({ type: 'float', value: 42 });
|
||||
|
||||
// Use shorthand type notation
|
||||
msg.append({ type: 'f', value: 42 }); // 'f' = float
|
||||
msg.append({ type: 'i', value: 3.14 }); // 'i' = integer (truncates)
|
||||
msg.append({ type: 's', value: 'text' }); // 's' = string
|
||||
msg.append({ type: 'b', value: Buffer.from('data') }); // 'b' = blob
|
||||
msg.append({ type: 'm', value: { port: 0, status: 144, data1: 60, data2: 127 } }); // 'm' = MIDI
|
||||
```
|
||||
|
||||
### Supported Type Tags
|
||||
|
||||
- `'i'` or `'integer'` - 32-bit integer
|
||||
- `'f'` or `'float'` - 32-bit float
|
||||
- `'s'` or `'string'` - OSC string
|
||||
- `'b'` or `'blob'` - Binary blob
|
||||
- `'m'` or `'midi'` - MIDI message (4 bytes)
|
||||
- `'boolean'` - Boolean value (true/false)
|
||||
- `'T'` - True
|
||||
- `'F'` - False
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Use Async/Await for Cleaner Code
|
||||
|
||||
Prefer async/await over callbacks for more readable code:
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Clean and readable
|
||||
async function sendMessages() {
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
try {
|
||||
await client.send('/test', 1);
|
||||
await client.send('/test', 2);
|
||||
await client.send('/test', 3);
|
||||
} finally {
|
||||
await client.close();
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Less ideal - Callback pyramid
|
||||
function sendMessages() {
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
client.send('/test', 1, (err) => {
|
||||
if (err) return console.error(err);
|
||||
client.send('/test', 2, (err) => {
|
||||
if (err) return console.error(err);
|
||||
client.send('/test', 3, (err) => {
|
||||
if (err) return console.error(err);
|
||||
client.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Always Close Resources
|
||||
|
||||
Always close clients and servers when done to prevent resource leaks:
|
||||
|
||||
```javascript
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
} finally {
|
||||
await client.close(); // Always close
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Use Address-Specific Event Listeners
|
||||
|
||||
For better code organization, use address-specific event listeners:
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Clear and organized
|
||||
server.on('/note', (msg) => {
|
||||
handleNote(msg);
|
||||
});
|
||||
|
||||
server.on('/control', (msg) => {
|
||||
handleControl(msg);
|
||||
});
|
||||
|
||||
// ❌ Less ideal - Manual routing
|
||||
server.on('message', (msg) => {
|
||||
const [address] = msg;
|
||||
if (address === '/note') handleNote(msg);
|
||||
else if (address === '/control') handleControl(msg);
|
||||
});
|
||||
```
|
||||
|
||||
### 4. Handle Errors Gracefully
|
||||
|
||||
Always implement error handling for both clients and servers:
|
||||
|
||||
```javascript
|
||||
// Client
|
||||
try {
|
||||
await client.send('/test', 123);
|
||||
} catch (err) {
|
||||
console.error('Failed to send:', err.message);
|
||||
}
|
||||
|
||||
// Server
|
||||
server.on('error', (err, rinfo) => {
|
||||
console.error(`Server error from ${rinfo?.address}:`, err.message);
|
||||
});
|
||||
```
|
||||
|
||||
### 5. Use Bundles for Related Messages
|
||||
|
||||
When sending multiple related messages, use bundles for atomic operations:
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Atomic operation
|
||||
const bundle = new Bundle(
|
||||
['/synth/freq', 440],
|
||||
['/synth/amp', 0.5],
|
||||
['/synth/gate', 1]
|
||||
);
|
||||
await client.send(bundle);
|
||||
|
||||
// ❌ Less ideal - Separate messages (not atomic)
|
||||
await client.send('/synth/freq', 440);
|
||||
await client.send('/synth/amp', 0.5);
|
||||
await client.send('/synth/gate', 1);
|
||||
```
|
||||
|
||||
### 6. Listen on All Interfaces for Network Access
|
||||
|
||||
If you need to receive messages from other machines:
|
||||
|
||||
```javascript
|
||||
// Listen on all interfaces (accessible from network)
|
||||
const server = new Server(3333, '0.0.0.0');
|
||||
|
||||
// Only localhost (default, more secure)
|
||||
const server = new Server(3333, '127.0.0.1');
|
||||
```
|
||||
|
||||
### 7. Use Descriptive OSC Addresses
|
||||
|
||||
Follow OSC naming conventions with hierarchical addresses:
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Hierarchical and descriptive
|
||||
await client.send('/synth/oscillator/1/frequency', 440);
|
||||
await client.send('/mixer/channel/3/volume', 0.8);
|
||||
|
||||
// ❌ Less ideal - Flat and unclear
|
||||
await client.send('/freq1', 440);
|
||||
await client.send('/vol3', 0.8);
|
||||
```
|
||||
|
||||
### 8. Validate Input Data
|
||||
|
||||
Validate data before sending to avoid runtime errors:
|
||||
|
||||
```javascript
|
||||
function sendNote(pitch, velocity) {
|
||||
if (typeof pitch !== 'number' || pitch < 0 || pitch > 127) {
|
||||
throw new Error('Invalid pitch: must be 0-127');
|
||||
}
|
||||
if (typeof velocity !== 'number' || velocity < 0 || velocity > 127) {
|
||||
throw new Error('Invalid velocity: must be 0-127');
|
||||
}
|
||||
return client.send('/note', pitch, velocity);
|
||||
}
|
||||
```
|
||||
|
||||
### 9. Wait for Server Ready
|
||||
|
||||
Always wait for the server to be listening before sending messages:
|
||||
|
||||
```javascript
|
||||
import { once } from 'node:events';
|
||||
|
||||
const server = new Server(3333, '0.0.0.0');
|
||||
|
||||
// Wait for server to be ready
|
||||
await once(server, 'listening');
|
||||
|
||||
// Now safe to send messages
|
||||
console.log('Server ready!');
|
||||
```
|
||||
|
||||
### 10. Use Parallel Sends When Appropriate
|
||||
|
||||
When sending multiple independent messages, use `Promise.all` for better performance:
|
||||
|
||||
```javascript
|
||||
// Send multiple messages in parallel
|
||||
await Promise.all([
|
||||
client.send('/track/1/volume', 0.8),
|
||||
client.send('/track/2/volume', 0.6),
|
||||
client.send('/track/3/volume', 0.9)
|
||||
]);
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Messages Not Being Received
|
||||
|
||||
**Possible causes and solutions:**
|
||||
|
||||
1. **Firewall blocking UDP traffic**
|
||||
- Check your firewall settings
|
||||
- Ensure the UDP port is open
|
||||
- Try with localhost first (`127.0.0.1`)
|
||||
|
||||
2. **Wrong host binding**
|
||||
- Server: Use `'0.0.0.0'` to listen on all interfaces
|
||||
- Server: Use `'127.0.0.1'` for localhost only
|
||||
- Client: Match the server's IP address
|
||||
|
||||
3. **Port mismatch**
|
||||
- Ensure client and server use the same port number
|
||||
- Check if another process is using the port
|
||||
|
||||
4. **Network connectivity**
|
||||
- Test with localhost first (`127.0.0.1`)
|
||||
- Verify network connectivity between machines
|
||||
- Check if devices are on the same network
|
||||
|
||||
### "Cannot send message on closed socket"
|
||||
|
||||
This error occurs when trying to send after closing the client:
|
||||
|
||||
```javascript
|
||||
// ❌ Wrong - Sending after close
|
||||
await client.close();
|
||||
await client.send('/test', 123); // Error!
|
||||
|
||||
// ✅ Correct - Send before close
|
||||
await client.send('/test', 123);
|
||||
await client.close();
|
||||
```
|
||||
|
||||
### Server Not Listening
|
||||
|
||||
Ensure you wait for the server to start before sending messages:
|
||||
|
||||
```javascript
|
||||
const server = new Server(3333, '0.0.0.0');
|
||||
|
||||
// Wait for server to be ready
|
||||
await new Promise(resolve => server.on('listening', resolve));
|
||||
|
||||
// Now safe to send messages
|
||||
console.log('Server ready!');
|
||||
```
|
||||
|
||||
### Messages Lost or Dropped
|
||||
|
||||
UDP is unreliable by design and messages can be lost:
|
||||
|
||||
**Solutions:**
|
||||
1. Use TCP-based OSC if reliability is critical (requires custom implementation)
|
||||
2. Implement acknowledgment messages
|
||||
3. Add retry logic for critical messages
|
||||
4. Use bundles to ensure related messages arrive together
|
||||
|
||||
### High CPU Usage
|
||||
|
||||
If you're seeing high CPU usage:
|
||||
|
||||
1. **Check for infinite loops** in event handlers
|
||||
2. **Rate limit** message sending if sending many messages
|
||||
3. **Use bundles** instead of many individual messages
|
||||
4. **Close unused connections** to free resources
|
||||
|
||||
### Memory Leaks
|
||||
|
||||
To prevent memory leaks:
|
||||
|
||||
1. **Always close** clients and servers when done
|
||||
2. **Remove event listeners** when no longer needed
|
||||
3. **Avoid** creating new clients/servers in loops
|
||||
4. **Reuse** client/server instances when possible
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Proper cleanup
|
||||
const server = new Server(3333);
|
||||
const handler = (msg) => console.log(msg);
|
||||
server.on('message', handler);
|
||||
|
||||
// Later, clean up
|
||||
server.removeListener('message', handler);
|
||||
await server.close();
|
||||
```
|
||||
|
||||
## Advanced Topics
|
||||
|
||||
### Custom Transports
|
||||
|
||||
The `encode` and `decode` functions allow you to use OSC over custom transports:
|
||||
|
||||
```javascript
|
||||
import { encode, decode, Message } from 'node-osc';
|
||||
import WebSocket from 'ws';
|
||||
|
||||
// WebSocket server
|
||||
const wss = new WebSocket.Server({ port: 8080 });
|
||||
wss.on('connection', (ws) => {
|
||||
ws.on('message', (data) => {
|
||||
const oscMsg = decode(data);
|
||||
console.log('Received:', oscMsg);
|
||||
});
|
||||
});
|
||||
|
||||
// WebSocket client
|
||||
const ws = new WebSocket('ws://localhost:8080');
|
||||
const message = new Message('/test', 123);
|
||||
ws.send(encode(message));
|
||||
```
|
||||
|
||||
### Timetags in Bundles
|
||||
|
||||
OSC bundles support timetags for scheduling:
|
||||
|
||||
```javascript
|
||||
// Immediate execution (timetag = 0)
|
||||
const bundle = new Bundle(['/test', 1], ['/test', 2]);
|
||||
|
||||
// Scheduled execution (timetag in OSC time)
|
||||
const futureTime = Date.now() / 1000 + 5; // 5 seconds from now
|
||||
const scheduledBundle = new Bundle(futureTime, ['/test', 1]);
|
||||
```
|
||||
|
||||
**Note:** The server receives the timetag but does not automatically schedule execution. You must implement scheduling logic if needed.
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
For high-throughput applications:
|
||||
|
||||
1. **Reuse client instances** instead of creating new ones
|
||||
2. **Use bundles** to send multiple messages together
|
||||
3. **Batch messages** and send periodically rather than immediately
|
||||
4. **Use binary blobs** for large data instead of many arguments
|
||||
5. **Profile your code** to identify bottlenecks
|
||||
|
||||
```javascript
|
||||
// ✅ Good - Reuse client
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
await client.send('/test', i);
|
||||
}
|
||||
await client.close();
|
||||
|
||||
// ❌ Bad - Creating many clients
|
||||
for (let i = 0; i < 1000; i++) {
|
||||
const client = new Client('127.0.0.1', 3333);
|
||||
await client.send('/test', i);
|
||||
await client.close();
|
||||
}
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
- [API Documentation](./API.md) - Complete API reference
|
||||
- [OSC Specification](http://opensoundcontrol.org/spec-1_0) - Official OSC 1.0 specification
|
||||
- [Examples](../examples/) - Working code examples
|
||||
- [Main README](../README.md) - Quick start guide
|
||||
Reference in New Issue
Block a user