Signal routing is how devices communicate in WAVE Synapse. A signal route connects a source device output to a destination device input, carrying video, audio, data, or mixed signals across your facility.
The routing matrix is a grid visualization where rows represent source devices and columns represent destination devices. Each cell in the grid shows the current state of the connection between that source-destination pair.
| State | Meaning |
|---|---|
active | A live route exists between the devices |
inactive | No route, but one could be created |
incompatible | Devices cannot be connected (protocol/capability mismatch with no bridge available) |
needs_bridge | Devices use different protocols; an MXL bridge is required |
locked | Route is locked to a production and cannot be modified |
Retrieve the full routing matrix via the API:
const response = await fetch('/api/ncp/routing/matrix', {
headers: { 'Authorization': `Bearer ${accessToken}` },
});
const { data: matrix } = await response.json();
// matrix.sources = NCPDevice[] (row headers)
// matrix.destinations = NCPDevice[] (column headers)
// matrix.cells = RoutingMatrixCell[][] (2D grid)
Each cell contains:
interface RoutingMatrixCell {
source_device_id: string;
dest_device_id: string;
state: 'active' | 'inactive' | 'incompatible' | 'needs_bridge' | 'locked';
route_id: string | null; // ID of active route, if any
bridge_type: string | null; // e.g., 'ndi_to_srt' if bridging required
signal_type: SignalType | null; // 'video' | 'audio' | 'data' | 'mixed'
}
When source and destination devices share the same protocol, Synapse creates a direct route with no bridging overhead. This is the simplest and lowest-latency routing path.
Examples of direct routes:
| Source Protocol | Destination Protocol | Signal Type | Use Case |
|---|---|---|---|
| NDI | NDI | video | Camera to switcher on same network |
| Dante | Dante | audio | Microphone to mixer via Dante |
| SRT | SRT | video | Encoder to decoder over WAN |
| WebRTC | WebRTC | mixed | Browser source to browser output |
When the source and destination use different protocols, Synapse inserts an MXL (Media Cross-Link) bridge. The route’s bridge_type field indicates the conversion being performed.
Common bridge scenarios:
| Source | Destination | Bridge Type | Added Latency |
|---|---|---|---|
| NDI | SRT | ndi_to_srt | ~5-15ms |
| SRT | NDI | srt_to_ndi | ~5-15ms |
| NDI | Dante (audio) | ndi_to_dante | ~2-5ms |
| Dante | NDI (audio) | dante_to_ndi | ~2-5ms |
| WebRTC | SRT | webrtc_to_srt | ~10-20ms |
Use the route validation endpoint to check whether bridging is required before creating a route:
const response = await fetch('/api/ncp/routing/validate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify({
source: {
device_id: 'ndi-camera-uuid',
port: 'output_1',
protocol: 'ndi',
},
dest: {
device_id: 'srt-decoder-uuid',
port: 'input_1',
protocol: 'srt',
},
}),
});
const { data: validation } = await response.json();
// validation.compatible = true
// validation.requires_bridge = true
// validation.bridge_type = 'ndi_to_srt'
// validation.requires_vlan_routing = false
// validation.estimated_latency_ms = 12
When routing audio (especially with Dante devices that support many channels), you can specify an audio_channel_map that maps source audio channels to destination channels:
const response = await fetch('/api/ncp/routing', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`,
},
body: JSON.stringify({
source_device_id: 'dante-mic-uuid',
source_port: 'tx_1',
source_protocol: 'dante',
dest_device_id: 'dante-mixer-uuid',
dest_port: 'rx_1',
dest_protocol: 'dante',
signal_type: 'audio',
audio_channel_map: {
'1': '3', // Source channel 1 -> Destination channel 3
'2': '4', // Source channel 2 -> Destination channel 4
},
}),
});
The audio_channel_map is stored as JSON on the route and included in show profile snapshots for reproducible configurations.
Before a route is created, the system validates:
If validation fails, the POST /api/ncp/routing endpoint returns a 422 Unprocessable Entity response with a detailed error message.
Active routes continuously track quality metrics:
| Metric | Unit | Description |
|---|---|---|
latency_ms | milliseconds | End-to-end signal delay |
bitrate_kbps | kbps | Current throughput |
packet_loss_rate | ratio (0-1) | Percentage of lost packets |
jitter_ms | milliseconds | Variation in packet arrival time |
These metrics are updated by the route-health-monitor Inngest function and surfaced in the routing matrix UI.
During a live production, routes can be locked to prevent accidental changes. A locked route:
locked state in the routing matrixproduction_id for audit purposes// List all active routes
const response = await fetch('/api/ncp/routing?status=active', {
headers: { 'Authorization': `Bearer ${accessToken}` },
});
// Filter by source device
const response = await fetch(`/api/ncp/routing?source_device_id=${deviceId}`, {
headers: { 'Authorization': `Bearer ${accessToken}` },
});
// Filter by signal type
const response = await fetch('/api/ncp/routing?signal_type=audio', {
headers: { 'Authorization': `Bearer ${accessToken}` },
});
// Filter by production
const response = await fetch(`/api/ncp/routing?production_id=${productionId}`, {
headers: { 'Authorization': `Bearer ${accessToken}` },
});