Expand description
§tcnet
A Rust implementation of the TCNet UDP protocol — a network protocol used by professional DJ / VJ gear (Pioneer / ProDJ Link adjacent) for synchronising playback state, mixer state, beat-grid information and waveform previews between networked nodes.
This crate covers protocol version 3.6 (the value emitted in every outgoing
ManagementHeader) and supports both roles:
- Passive observer — discover foreign DJ controller nodes broadcasting on the
network and read their state through
DjControllerView. Useful for VJ tools, visualisers, lighting controllers, analytics, etc. - Active broadcaster — present this process as a virtual DJ node via
ActiveDJNode: announce up to eight layers of playback, push Time / Status / Metrics / Meta / Mixer packets, and serve pre-built waveform / beat-grid / cue / artwork responses on request.
§Specification
The packet layouts, message-type IDs and field meanings all follow the official spec, available here:
https://www.tc-supply.com/_files/ugd/b1c714_0b351a4099c14e738f0cd7fcea623265.pdf
Citations elsewhere in these docs link back to that PDF.
§Quick start
use std::thread::sleep;
use std::time::Duration;
use tcnet::{ApplicationConfig, TCNetClient};
let config = ApplicationConfig::default();
let mut client = TCNetClient::new(config);
// Wait for a foreign DJ controller to be discovered, then read its state.
loop {
for node in client.active_nodes() {
if node.has_dj_controller {
if let Some(mut view) = client.get_controller_view(node.address) {
for (i, layer) in view.get_layers().iter().enumerate() {
println!("L{}: {:?} @ {:.1} BPM", i + 1, layer.state, layer.bpm.as_f32());
}
}
}
}
sleep(Duration::from_secs(1));
}§Architecture
┌──────────────────────────────┐
│ TCNetClient │
│ • spawns tokio runtime │
│ • binds UDP sockets │
│ • runs OptIn discovery │
network │ • dispatches packets │
────────── │ │
60000 ── broadcast ◀──────────┤ │
60001 ── time ◀──────────►│ Dispatcher │◀── ActiveDJNode (broadcast role)
60002 ── broadcast ◀──────────┤ │
port ── unicast ◀──────────┤ │
│ │
│ per-foreign-node triple │
│ buffer ──────────────────► │── DjControllerView (read role)
└──────────────────────────────┘§Module layout
protocol— wire-format types: every packet payload struct, plus helper types (LayerId,LayerState,Bpm,Speed, …).view— read-only consumer view of a discovered foreign node.
Most users only need the types re-exported at the crate root.
§Implementation status
This crate covers the parts of TCNet v3.6 needed to observe and impersonate a Pioneer-style DJ controller on the network. It is not a full reference implementation of every message type defined in the spec.
Implemented:
- Discovery —
OptInbroadcasts every second on UDP 60000, listening for peerOptIn/OptOut, 10-second timeout for stale nodes. - Foreign-node observation —
Status,Metrics,Meta,MixerandTimepackets are decoded and merged intoLayerSnapshot/MixerSnapshot, published through a triple buffer toDjControllerView. - On-demand requests from a
DjControllerView—SmallWaveform,BigWaveform,BeatGrid(with multi-packet reassembly) andLowResArtworkFile, each with a 5-second timeout. - Active broadcasting via
ActiveDJNode— periodicTime(20 ms),Status(1 s) and per-layerMetrics(50 ms while playing) emission, plus on-demand replies to peerRequestDatapackets from a pre-built response cache (SmallWaveform,BigWaveform,BeatGrid,Cue,Artwork,Mixer,Metrics,Meta).
Partial / not implemented:
- No
OptOutis emitted when anActiveDJNodeis dropped — peers currently rely on the 10-second silence timeout to drop us. IncomingOptOutfrom peers is handled. TimeSync(message type 10) — struct defined for parsing, but the handshake is not performed; this crate relies on local system time and the per-packet microsecond timestamp.ErrorNotification(message type 13) — struct defined, never sent or surfaced to the user when received.Control/Text/Keyboard/AppSpecific(message types 101 / 128 / 132 / 30 / 213) — structs defined but neither emitted nor surfaced; incoming packets are deserialised and dropped.- Authentication (
NodeOptions::NEED_AUTHENTICATION) — no handshake implemented; this crate always operates as an unauthenticated peer. LayerStatusandAutoMasterModeare placeholder enums with a single variant — the spec leaves these mostly unspecified at v3.6.- Master-election arbitration —
NodeTypeis reported faithfully but theAuto/Master/Slaveelection logic is not driven by this crate. MixerDataround-trip — most fields are surfaced throughMixerSnapshot, but a handful of less-common send-FX / send-return bytes are read from the wire without a dedicated snapshot field.
PRs welcome.
Re-exports§
pub use active_node::ActiveDJNode;pub use active_node::TrackMeta;pub use protocol::AsciiString;pub use protocol::BeatGridEntry;pub use protocol::BeatGridHeader;pub use protocol::BigWaveformData;pub use protocol::Bpm;pub use protocol::LayerId;pub use protocol::LayerState;pub use protocol::NodeOptions;pub use protocol::NodeType;pub use protocol::RequestDataType;pub use protocol::SmallWaveformData;pub use protocol::SmpteMode;pub use protocol::Speed;pub use view::DjControllerView;pub use view::WaveformRequester;
Modules§
- active_
node - Active broadcaster role: present this process as a TCNet DJ controller node.
- protocol
- Wire-format types for the TCNet UDP protocol.
- view
- Read-only consumer view of a foreign TCNet DJ controller node.
Macros§
- into_
ascii - Build an
AsciiString<N>from a string literal at compile time.
Structs§
- Application
Config - Identity and bind configuration used by a
TCNetClientwhen it announces itself to peers. - Channel
Snapshot - Decoded snapshot of one mixer channel — a fader strip on the physical mixer.
- DjController
State - Combined snapshot of one foreign DJ controller: all eight
LayerSnapshots plus oneMixerSnapshot. - Foreign
Node Info - Snapshot of a foreign node discovered on the network through TCNet OptIn
broadcasts. Returned by
TCNetClient::active_nodes. - Layer
Snapshot - Decoded snapshot of one layer’s state.
- Mixer
Snapshot - Decoded snapshot of a foreign mixer’s state.
- TCNet
Client - Entry point to the TCNet network.
- Timeout
Error - Returned when a
DjControllerViewrequest for waveform / beat-grid / artwork data does not receive a response within 5 s (or the underlying request channel has been closed).