| Crates.io | bbx_net |
| lib.rs | bbx_net |
| version | 0.4.3 |
| created_at | 2026-01-14 00:44:01.699447+00 |
| updated_at | 2026-01-15 17:28:49.575761+00 |
| description | Network audio control for art installations and live performance: OSC and WebSocket protocols |
| homepage | |
| repository | https://github.com/blackboxaudio/bbx_audio |
| max_upload_size | |
| id | 2041841 |
| size | 247,414 |
Network audio control for art installations and live performance: OSC and WebSocket protocols.
This crate provides lock-free, realtime-safe network message passing for distributed audio systems. It supports:
Enable protocols via feature flags:
[dependencies]
bbx_net = { version = "0.4", features = ["osc"] } # OSC only
bbx_net = { version = "0.4", features = ["websocket"] } # WebSocket only
bbx_net = { version = "0.4", features = ["full"] } # Both protocols
Create a buffer pair for communication between network and audio threads:
use bbx_net::{net_buffer, NetMessage, NodeId, hash_param_name};
// Create buffer (network thread → audio thread)
let (mut producer, mut consumer) = net_buffer(256);
// Network thread: send parameter changes
let msg = NetMessage::param_change("gain", 0.5, NodeId::default());
producer.try_send(msg);
// Audio thread: receive messages (realtime-safe, no allocation)
let gain_hash = hash_param_name("gain");
let events = consumer.drain_into_stack();
for msg in events {
if msg.param_hash == gain_hash {
if let Some(value) = msg.payload.value() {
// Apply gain change with value
}
}
}
The OSC server listens for UDP packets and forwards them to the audio thread.
use bbx_net::{net_buffer, osc::{OscServer, OscServerConfig}};
let (producer, consumer) = net_buffer(256);
let config = OscServerConfig {
bind_addr: "0.0.0.0:9000".parse().unwrap(),
..Default::default()
};
let server = OscServer::new(config, producer);
let handle = server.spawn(); // Runs in background thread
Messages must follow one of these patterns:
| Address | Description |
|---|---|
/blocks/param/gain |
Broadcast gain parameter to all blocks |
/blocks/param/freq |
Broadcast frequency parameter to all blocks |
/block/<uuid>/param/gain |
Target specific block by UUID |
/blocks/param/freq, /blocks/param/cutoff, /blocks/param/gainThe WebSocket server supports room-based authentication and bidirectional communication.
use bbx_net::{
net_buffer,
websocket::{WsServer, WsServerConfig, ServerCommand},
};
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (producer, consumer) = net_buffer(256);
let (command_tx, command_rx) = mpsc::channel(256);
let config = WsServerConfig {
bind_addr: "0.0.0.0:8080".parse().unwrap(),
..Default::default()
};
let server = WsServer::new(config, producer, command_rx);
// Create a room
let (response_tx, response_rx) = tokio::sync::oneshot::channel();
command_tx.send(ServerCommand::CreateRoom { response: response_tx }).await.unwrap();
let room_code = response_rx.await.unwrap();
println!("Room code: {room_code}");
// Run server
server.run().await.unwrap();
}
The @bbx-audio/net package provides a TypeScript/JavaScript client for web and Node.js applications.
npm install @bbx-audio/net
# or
yarn add @bbx-audio/net
import { BbxClient } from '@bbx-audio/net'
const client = new BbxClient({
url: 'ws://localhost:8080',
roomCode: '123456',
})
client.on('connected', () => console.log('Connected!'))
client.on('update', (msg) => console.log(`${msg.param} = ${msg.value}`))
await client.connect()
client.setParam('freq', 0.5)
See the full documentation for the complete API reference.
Client → Server:
// Join a room
{"type": "join", "room_code": "ABC123"}
// Send parameter change
{"type": "param", "param": "freq", "value": 0.5}
// Send trigger event
{"type": "trigger", "name": "note_on"}
// Request current state
{"type": "sync"}
// Ping for latency measurement
{"type": "ping", "client_time": 1234567890}
// Leave room
{"type": "leave"}
Server → Client:
// Welcome after joining
{"type": "welcome", "node_id": "uuid-string", "server_time": 1234567890}
// Current parameter state
{"type": "state", "params": [{"name": "freq", "value": 0.5, "min": 0.0, "max": 1.0}]}
// Parameter update (broadcast)
{"type": "update", "param": "freq", "value": 0.7}
// Pong response
{"type": "pong", "client_time": 1234567890, "server_time": 1234567891}
// Error
{"type": "error", "code": "INVALID_ROOM", "message": "Invalid room code"}
// Room closed
{"type": "closed"}
The core message type for network communication:
pub struct NetMessage {
pub message_type: NetMessageType, // ParameterChange, Trigger, etc.
pub param_hash: u32, // FNV-1a hash of parameter name
pub payload: NetPayload, // Message payload data
pub node_id: NodeId, // Source node identifier
pub timestamp: SyncedTimestamp, // Synchronized timestamp
}
Convenience constructors:
// Parameter change (most common)
let msg = NetMessage::param_change("gain", 0.5, node_id);
// Trigger event (no value)
let msg = NetMessage::trigger("note_on", node_id);
// Spatial trigger with coordinates
let msg = NetMessage::trigger_with_coordinates("tap", 0.5, 0.75, node_id);
Type-safe payload data for network messages:
| Variant | Description | Use Case |
|---|---|---|
None |
No data | Triggers, ping/pong |
Value(f32) |
Single float | Parameter changes |
Coordinates { x, y } |
2D position | Spatial triggers |
Helper methods for extracting data:
// Extract float value (returns Option<f32>)
if let Some(value) = msg.payload.value() {
oscillator.set_frequency(value * 1000.0);
}
// Extract coordinates (returns Option<(f32, f32)>)
if let Some((x, y)) = msg.payload.coordinates() {
// Handle spatial trigger
}
Parameters are identified by FNV-1a hash for efficient matching:
use bbx_net::hash_param_name;
let freq_hash = hash_param_name("freq");
let gain_hash = hash_param_name("gain");
// In audio thread:
if msg.param_hash == freq_hash {
// Handle frequency change
}
The NetBufferConsumer provides realtime-safe methods:
drain_into_stack() — Returns up to 64 messages using stack allocation (no heap)try_pop() — Pop a single messageis_empty() / len() — Check buffer stateSee bbx_sandbox for complete examples:
14_osc_synth.rs — TouchOSC-controlled synthesizer15_ws_synth.rs — WebSocket-controlled synthesizer# Run OSC example
cargo run --example 14_osc_synth -p bbx_sandbox
# Run WebSocket example
cargo run --example 15_ws_synth -p bbx_sandbox
┌─────────────┐ ┌──────────────────┐ ┌─────────────┐
│ TouchOSC │────▶│ OscServer │ │ │
│ (Phone) │ UDP │ (UDP recv) │ │ │
└─────────────┘ └────────┬─────────┘ │ │
│ │ Audio │
▼ │ Thread │
┌──────────────────┐ │ │
│ NetBufferProducer│────▶│ NetBuffer │
└──────────────────┘ │ Consumer │
▲ │ │
┌─────────────┐ ┌────────┴─────────┐ │ │
│ Browser │────▶│ WsServer │ │ │
│ (Phone) │ WS │ (Tokio async) │ │ │
└─────────────┘ └──────────────────┘ └─────────────┘